原型相关内容:
1.所有的函数都有一个prototype属性(即原型),原型默认是一个没有任何自身属性的空对象,原型对象也有自己的原型,向上到顶层是Object对象
2.可以在原型对象中添加新属性和方法,也可以令其指向一个自定义对象(如代码中Person.prototype=monkey)
3.当使用new操作符,并通过构造函数来实例化对象时,对象会自动拥有一个指向其原型的__proto__属性。经由此属性对原型的引用,对象可以通过原型链直接访问原型中的属性和方法
4.对象自身属性优先级高于其原型对象上的同名属性,通过hasOwnProperty()方法来区分
5.轻易不要扩展内建对象,会造成不可预料的隐患。如一定要扩展,要先检测是否有同名属性或方法
原型陷阱:当使用自定义的新原型对象覆盖原有原型对象时(参考本例中,如Person.prototye={language:false}),现有实例化的对象(tom)仍可访问被覆盖前的对象属性和方法(如hair),且无法访问新原型对象的内容(language)。但后续新实例化的对象无此问题,可正常访问新原型。
var monkey={
hair:true,
foods:'banana',
fingers:5
}
function Person(name){
this.name=name;
this.greet=function(){
console.log('Hi, my name is '+this.name);
}
}
Person.prototype=monkey;
Person.prototype.constructor=Person;
//令tom.constructor依旧指向Person,此处涉及原型陷阱问题,为什么这样做仍有疑问。但constructor意为构造函数,实际上就指向对象的构造函数
var tom=new Person('Tom');
tom.greet(); //Hi, my name is Tom
tom.hair; //true
for(let props in tom){
console.log(props); //name greet hair foods fingers
}
for(let props in tom){
if(tom.hasOwnProperty(props)){
console.log(props); //name greet
}
}
//另,由于tom是对象,无法迭代(not iterable),故无法用for...of迭代自有属性数据(属性值)
tom.constructor===Person; //true,如果上方对Person.prototype指向monkye后不重新对其constructor进行赋值,则此处为false
tom.constructor===Object; //true,由上一句而来,Person是对象
tom.constructor===monkey; //false
Person.prototype.isPrototypeOf(tom); //true
tom.__proto__===monkey; //true
//注:__proto__属性与prototype属性不同,__proto__属于某个实例对象,而prototype属于构造函数。二者都指向所属对象的原型
Object.getPrototypeOf(tom)===monkey; //true
tom.hasOwnProperty('hair'); //false
tom.hasOwnProperty('name'); //true
tom.propertyIsEnumerable('name'); //true,此方法对于所有非内建对象属性(自有属性)返回true
tom.propertyIsEnumerable('constructor'); //false,对于内建对象属性,大部分不可枚举(可枚举的尚不了解)
tom.propertyIsEnumerable('hair'); //false,原型链中的属性也是不可枚举的
monkey.propertyIsEnumerable('hair'); //true
hasOwnProperty
propertyIsEnumerable
isPrototypeOf
getPrototypeOf