构造函数 构造函数也是函数,与普通函数唯一区别就是调用方式不同。任何函数只要使用new操作符调用就是构造函数,而不适用new操作符调用的函数就是普通函数。
理解原型 只要创建一个函数,就会按照特定的规则为这个函数创建一个prototype属性(指向原型对象),默认情况下,所有原型对象会自动获得一个名为constructor指向Person
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 function  Person ( ) {}   console .log(typeof  Person.prototype);    console .log(Person.prototype);            console .log(Person.prototype.__proto__===Object .prototype);    console .log(Person.prototype.__proto__.constructor===Object );    console .log(Person.prototype.__proto__.__proto__===null );    console .log(Person.prototype.__proto__);    let  person1=new  Person(),person2=new  Person();        console .log(person1!==Person);    console .log(person1!==Person.prototype);    console .log(Person.prototype!==Person);            console .log(person1.__proto__===Person.prototype);    console .log(person1.__proto__.constructor===Person);        console .log(person1.__proto__===person2.__proto__);        console .log(person1 instanceof  Person);    console .log(person1 instanceof  Object );    console .log(Person.prototype instanceof  Object ); 
 
Person构造函数,Person原型对象和Person现有实例的关系如上:Person.prototype指向原型对象,因此Person.prototye.constructor指回Person构造函数。原型对象包含constructor属性和其他后来添加的属性。Person的两个实例person1,person2有一个内部属性指回Person.prototype,而且两者和构造函数没有直接联系。person1.sayName()可以正常调用,这是由于对象属性查找机制的原因
虽然不是所有实现都对外暴露[[Prototype]],但可以使用isPrototypeOf()方法确定两个对象之间的关系,本质上isPrototypeOf()会在传入参数的[[Prototype]]指向调用它的对象时返回true
1 2 console .log(Person.prototype.isprototypeOf(person1));console .log(Person.prototype.isprototypeOf(person2));
 
Object类型有一个方法叫Object.getPrototypeOf(),返回参数的内部特性[[Prototype]]的值
1 2 console .log(Object .getPrototypeOf(person1)==Person.prototype);console .log(Object .getPrototypeOf(person1).name);
 
使用Object.getPrototypeOf()可以取得一个对象的原型
Object类型还有一个setPrototypeOf()方法,可以向实例的私有特性[[Prototype]]写入一个新值,这样就可以重写一个对象的原型继承关系
1 2 3 4 5 6 7 8 9 10 let  biped={       numLegs :2     };    let  person={        name :'Matt'     };    Object .setPrototypeOf(person,biped);    console .log(person.name);    console .log(person.numLegs);    console .log(Object .getPrototypeOf(person)===biped); 
 
Object.setPrototypeOf()可能会严重影响代码性能,会涉及所有访问了哪些修改过[[Prototype]]的对象的代码
可以通过Object.create()创建一个新对象,同时为其指定原型
1 2 3 4 5 6 7 8 let  biped={    numLegs :2  }; let  person=Object .create(biped);person.name='Matt' ; console .log(person.name);console .log(person.numLegs);console .log(Object .getPrototypeOf(person)===biped);
 
原型层级 通过对象访问属性时会按照这个属性名称开始搜索,搜索开始于对象实例本身,如果在实例上发现给定的名称,则返回该名称对应值,如果没有找到这个属性,则搜索会沿着指针进入原型对象,在原型对象找到属性后返回对应值。
比如foo不直接存在于myObject中而是存在于原型链上层时,myObject.foo=”bar”会出现三种情况:
如果在[[Prototype]]链上层存在名为foo的普通数据访问属性,并且writable:true,那就会直接在myObject中添加一个名为foo的新属性,它是屏蔽属性
 
如果在[[Prototype]]链上层存在foo,但是它是被标记为只读(writable:false),那么无法修改已有属性或者在myObject上创建屏蔽属性。如果运行在严格模式下,代码会抛出一个错误。否则,这条赋值语句会被忽略。不会发生屏蔽
 
如果在[[Prototype]]链上层存在foo并且它是一个setter,那就一定会调用这个setter,foo不会添加到myObject,也不会重新定义foo这个setter。
 
 
当属性被屏蔽时,可以使用delete删除实例上的这个属性。
1 2 3 4 5 6 7 8 9 10 11 function  Person ( ) {}Person.prototype.name='Nicholas' ; Person.prototype.age=29 ; let  person1=new  Person();let  person2=new  Person();person1.name="Greg" ; console .log(person1.name);console .log(person2.name);delete  person1.name;console .log(person1.name);
 
hasOwnProperty() hasOwnProperty()方法用于确定某个属性是在实例上还是原型对象上。这个方法继承自Object,会在属性存在于调用它的对象实例善时返回true
in操作符 in操作符会在可以通过对象访问指定属性时返回true,无论该属性是在实例上还是在原型上
因此如果要确定某个属性是否在原型上可以同时使用hasOwnProperty()和in操作符
1 2 3 function  hasPrototypeProperty (object,name ) {	return  !Object .hasOwnProperty(name)&&(name in  		object) } 
 
原型的问题 弱化了向构造函数传递初始化参数的能力,会导致所有实例默认取得属性相同的值,以及它的共享特性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function  Person ( ) {        Person.prototype={             constructor :Person,             name :'nic' ,             age :29 ,             job :"Software Engineer" ,             friends :['Coloey' ,'Amy' ],             sayName ( ) {                 console .log(this .name)             }         }     }     let  person1=new  Person();     let  person2=new  Person();     person1.friends.push('Van' );     console .log(person1.friends===person2.friends); 
 
不同实例应有不同的副本 。
原型链 原型链是ECMAScript的主要继承方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function  SuperType ( ) {      this .property=true ;   }   SuperType.prototype.getSuperValue=function ( ) {       return  this .property;   }   function  SubType ( ) {       this .subproperty=false ;   }      SubType.prototype=new  SuperType();   SubType.prototype.getSubValue=function ( ) {       return  this .subproperty;   }   let  instance=new  SubType();   console .log(instance.getSuperValue());   console .log(instance.property) 
 
SubType.prototype是SuperType的实例,因此SubType.prototype指向SuperType.prototype,注意,getSuperValue()方法还在SuperType.prototype对象上,而property是一个实例属性,SubType.prototype是SuperType的实例,因此property存在它上面,由于SubType.prototype的constructor属性被重写指向SuperType,所以instance.constructor也指向SuperType.
原型与继承关系 使用instanceOf操作符,如果一个实例的原型链出现过相应构造函数则instanceOf返回true
1 2 console .log(instance instanceof  Object );console .loh(instance instanceof  SubType);
 
使用**isPrototypeOf()**方法,原型链上的每个原型都可以调用这个方法,只要原型链上包含这个原型就返回true
1 2 3 console .log(Object .prototype.isPrototypeOf(instance));    console .log(SuperType.prototype.isPrototypeOf(instance));     console .log(SubType.prototype.isPrototypeOf(instance)); 
 
以对象字面量的方式创建原型方法会破坏之前的原型链:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function SuperType(){         this.property=true;     }     SuperType.prototype.getSuperValue=function(){         return this.property;     }     function SubType(){         this.subproperty=false;     }     //继承SuperType     SubType.prototype=new SuperType(); //通过对象字面量添加新方法,会导致上一行无效   SubType.prototype={         getSubValue(){             return this.subproperty;         },         someOtherMethod(){             return false;         }     }     let instance=new SubType();     console.log(instance.getSuperValue());//出错 
 
原型链的问题就是会在原型中包含的引用值会在实例间共享 。
1 2 3 4 5 6 7 8 9 10 11 function  SuperType ( ) {       this .color=["red" ,"blue" ,"green" ]    }    function  SubType ( ) {    }    SubType.prototype=new  SuperType();    let  intance1=new  SubType();    intance1.color.push("black" );    console .log(intance1.color);    let  instance2=new  SubType();    console .log(instance2.color); 
 
盗用构造函数 1 2 3 4 5 6 7 8 9 10 11 12 13 function  SuperType ( ) {        this .color=["red" ,"blue" ,"green" ]     }     function  SubType ( ) {                  SuperType.call(this );     }          let  intance1=new  SubType();     intance1.color.push("black" );     console .log(intance1.color);     let  instance2=new  SubType();     console .log(instance2.color); 
 
使用call(),SuperType构造函数在为SubType的实例创建的新对象的上下文中执行了,相当于新的SubType对象上运行了SuperType()函数的所有初始化代码
传递参数: 1 2 3 4 5 6 7 8 9 10 11 12 function  SuperType (arr ) {        this .color=["red" ,"blue" ,"green" ]         arr.forEach(item =>this .color.push(item));     }     function  SubType ( ) {                  SuperType.call(this ,['black' ]);     }     let  intance1=new  SubType();     console .log(intance1.color);     let  instance2=new  SubType();     console .log(instance2.color); 
 
问题: 也是使用构造函数模式自定义类型的问题:必须在构造函数中定义方法,因此函数不能重用,此外,子类也不能访问父类原型上定义的方法,因此所有类型只能使用构造函数模式。