深入了解原型

说原型之前先说说对象,好像在工作中,对象用的挺多的,原型基本上没有用。既然没有用那我还要不要学习呢?思考了很久,还是学一学,万一以后的工作用的着呢?领导常说,上一份工作是为下一份工作做准备的,所以在工作中要多学东西,不要因为暂时不用,而放弃学习,目光要放得长远。既然这样我就简单学一学原型。说原型之前先温习一下对象的相关知识

对象:属性值的集合,属性可以包括基本值,对象,或者函数

原型:在对象上面添加特定的属性,是定义属性的快捷方式,可以假想成 java 里面的类 (ps:Vue 实例就是在 vue 对象上面进行原型扩展

了解原型之前,先简单的介绍一下对象的创建

  • 1.对象字面量
var person = {
    name: "Nice,
    age: 23
}
复制代码

优点:代码量少,给人封装数据的感觉,也可以向函数传递大量可选参数;在实际开发中常用

    1. new 操作符后跟 Object 构造函数(几乎不用)
var person=new Object();
person.name="Nice;
person.age=23;
复制代码

  • 3.工厂模式(几乎不用)
function createPerson(name,age){
    var o=new Object();
    o.name=name;
    o.age=age;
    return o;
}
var person=createPerson("Nice",23);
复制代码

用一个函数把构造函数包裹起来,再在该函数体内返回改该构造函数

  • 4.构造函数模式(很少用)
function Person(name,age){
    this.name=name;
    this.age=age;
    this.sayName=function(){
        this.name;
    }
}
var person=new Person("Nice",23);
复制代码

构造函数始终都应该以一个大写字母开头,构造函数它本身就是一个函数,如果没有 new 关键字,他就和普通的函数调用一模一样; 调用构造函数经历四个步骤:

1.创建一个新对象

2.将构造函数的作用域赋给新对象(this就职向这个新对象)

3.执行构造函数中的代码

4.返回新的对象

构造函数创建对象很方便,但也有他的问题:每个方法都要在每个实例上都要重新创建创建一遍,不同的实例的同名函数是不相等;因此创建了大量的重复代码(当你 new 一个构造器对象,上面的构造函数就执行一遍,每次都会新建一个 function ,会新开辟一个内存空间,每次都是指向一个新的堆的对象 ,这样占用内存消耗非常的大);即重复造轮子,我们希望的是代码尽可能的复用,这时候我们就需要原型(实例,构造函数,原型对象之间的关系请看下面)

原型所定义的属性和功能会自动应用到对象的实例上(每个对象都有一份原型引用)

function Animal(name,age) {
  //加一个 this 条件判断,用 instanceof 来检查自己是否被 new 调用
  if (this instanceof Animal) {
    this.name = name
    this.age = age
  }eles {
    //以 new 递归调用自己来为对象创建正确的实例,目的保持没有 new 和有new 的表现行为一致
    return new Animal(name,age)
  }
}
复制代码

所有函数在初始化的时候都有一个 prototype 属性,该属性的初始值是一个空对象,只有函数在作为构造函数的时候,prototype 属性指向原型对象,这个对象包含所有实例共享的属性和方法

所有原型对象会自动获取一个 constructor 属性,指向构造函数

对象与函数原型之间的引用关系是在对象创建时建立的

由此可以看出,实例和构造函数之间没有什么直接的关系

function Person(){}
Person.prototype.dance=function(){}
function Ninja(){}
Ninja.prototype={dance:Person.prototype.dance}
复制代码

注意:Person.prototype 设置为一个对象字面量形式创建的新对象时,就切断了原来对象的联系(即 constructor 属性不在指向Person

如果 constructor 的值很重要可以向在新对象中设置:

造成这个原因是,实例和原型之间的松散链接关系,实例中的指针只指向原型,而不指向构造函数(可以看上面,原型,实例,构造函数直接的关系)

但是重设 constructor 属性,会导致它的[[Enumerable]] 的特性被设为 true,最好使用 Object.defineProperty()

Object.defineProperty(Person.prototype,'constructor',{
  enumerable:false,
  value:'',
  writable:true
})
复制代码

Object.defineProperty(): 这个方法接受三个参数,属性所在的对象,属性的名字,一个描述符对象;其中描述符对象的属性的一个或者多个值(Configurable,Enumerable,Writable,Valueget,set),一旦configurable 定义为不可配置的(false),就不能把它修改成可配置的,返回被传递的对象

Object.defineProperties():一次性可修改多个属性,第一个参数是属性对象,第二个参数是所要修改的数据属性组成的集合(即要修改的数据对象),返回被传递的对象

Object.getOwnPropertyDescriptor():读取属性描述符;第一个参数是属性所在的对象,第二个是要读取其描述符的属性名称,返回一个对象

构造函数内部的绑定操作符优先级永远都高于在原型上绑定的操作符优先级,在应用对象的一个属性时,优先检查该对象上本身是否拥有该属性,如果有则直接返回,否则继续在原型上面找,找不到就返回 undefined。一般情况不会轻易去修改原型对象上的属性,一旦修改就会出现各种问题;

判断属性是实例还是原型的几种常用方法

hasOwnProperty():如果返回 true,该属性存在实例当中

person.hasOwnProperty('name');//返回 true,name 属性在 person 实例当中
复制代码

in 操作符,只要返回 true,该属性就在对象中,也许是原型中,也许是实例当中

'name' in person //返回 true ,该属性存在
复制代码

for-in循环时,返回所有能够通过对象访问的,可枚举的属性

Object.keys():返回对象上所有可枚举的实例属性组成的字符串数组

function Person(name,age){
    this.name=name;
    this.age=age;
    this.sayName=function(){
        this.name;
    }
}
Person.prototype.job=function(){

}
var person=new Person();
console.log(Object.keys(person));//返回["name", "age", "sayName"]
复制代码

instanceof 操作符真正的含义:检查右边的构造函数原型是否存在于操作符左边的对象原型上的任何一个位置 object instanceof constructor

原型对象的问题

函数的原型是一个对象,所以有很多功能(属性或者方法)可以通过赋值的方法到达继承的目的,同时也可以定义新的方法;

因为原型对象上所有的属性和方法是共享的,而对于属于引用类型值的属性来说,会直接修改原型对象上的属性,造成 bug,引一发而动全身

解决原型对象的问题

  1. 构造函数和原型模式相结合:引用类型值的属性放在构造函数当中,其他共享的不会修改属性放在原型对象上

2.动态原型模式:根据函数是否在构造函数中,而选择性的添加到原型对象中去

3.寄生构造函数

长得和工厂模式一模一样,不同的是,通过 new 操作符来调用

function Person(name,age){
    var o=new Object();
    o.name=name;
    o.age=age;
    return o;
}
var friend=new Person();
复制代码

class 实现继承 class 底层的实现仍然是基于原型继承(ES6语法) 创建类

class Ninja {//创建类名
  constructor(name) {//定义构造函数。当使用关键字 new 调用类时。会调用这个构造函数
    this.name = name 
  }
  swingSword(){//定义一个Ninja 实例均可访问的方法
    return true
  }
}
复制代码

继承类

class Ninja extends Person {//extends 实现继承
 constructor(name.weapon) {
   super(name)
   this.weapon = weapon
 }
 wieldWeapon(){
   return true
 }
}
复制代码

期待我的续更吧,或许写的有点糟糕,我是初学者,如有错误之处,请多多请教(sunseekers_)。掘金谈技术,公众号谈生活(sunseekers)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 程序员如何迈过年薪30万的坎?

    作为一个写了十几年代码的老程序员,程序员要想拿到年薪30万,起码需要具备以下几个条件:

    程序员互动联盟
  • 快速在组合中查找重复和遗失的元素

    望月从良
  • 【本体】电机偏差值

    EdenChen
  • 阿里巴巴待遇这么好,为什么还有这么多人离职?

    不是每个阿里巴巴的职位待遇都是那么好,而且作为阿里巴巴几万人的大公司即使很低的比例离职也有相当的数量,这也属于正常的现象,正常来讲一个人离职主要有两种理由:

    程序员互动联盟
  • 工程师、程序员、码农有什么区别?

    正常来讲工程师范围更加广泛,包括程序员,现在有硬件工程师,实施工程师,安装工程师等等都属于工程师的范畴,一般称呼程序员高大上的称呼软件工程师。

    程序员互动联盟
  • 程序员是否应该拿年薪百万的工资?

    就目前国内程序员的薪资状态能达到百万级别的少的可怜,做技术开发做过几年之后很快就能达到天花板,就现在国内程序员水平年薪达到这一半就已经算是很不错的了,大部分的程...

    程序员互动联盟
  • 沟通学TCP握手,等于谋财害命

    TCP之所以有这个三次握手协议有很多历史原因。但是最主要的我认为是网络基础架构不稳定。因为不稳定,所以我们需要多次向对方表示或者等待对方反复确认“我爱你”...

    用户1564362
  • 想成为「不那么差」的程序员,离不开这个 buff

    一周前,我分享了篇文章《如何成为一位「不那么差」的程序员》,当时主要是从硬技能和软实力两方面分享经验,今天我想做一点补充:其实,做一名不那么差的程序员,也离不开...

    zhisheng
  • 程序员们,转变你的思维方式吧!

    笔者已经工作两年多了,在这里我想谈下关于程序员关于职业的思维方式。对于职业的思维方式,决定了一个人的选择和成就。建立一个正确的思维方式,有利于我们主动管理自己的...

    栋先生
  • 为什么程序员越来越排斥面试时做题?

    从业十几年经历的不是很多, 算起来也就是三四家,在编程行业跳槽比较频繁的行业,这算是一股清流了,呆的习惯了也就懒得动了,主要还是觉得在一个公司呆的时间长点,才真...

    程序员互动联盟

扫码关注云+社区

领取腾讯云代金券