原型是JavaScript这门语言最重要的部分之一 ,理解原型以及原型机制对于学习这门语言来说事半功倍。这篇博客帮助你理解。
构造函数与实例
Person.prototype.lastName = 'chen';
function Person(){
firstName = 'Joe';
}
var person = new Person();
console.log(person.lastName);
//output:'chen';
prototype
在JS中,每个函数上面都会有一个属性 "prototype" ,它指向了一个名为原型对象的对象,这个对象为由函数创建的实例提供了一个原型模板,也就是说这个对象是实例的 原型,实例会继承原型对象的属性和方法,原型对象包含两个属性,一个是 "consturctor", 指向这个函数,是函数默认取得的属性,至于为什么要指向这个函数,下文有提到,其他的属性都是继承自Object。
consturctor
prototype上面的属性"constructor" 指向函数本身,但是它的作用其实是为实例指明自己的构造函数是谁(个人认为),当实例访问属性contructor时,其实实例是没有这个属性的,找不到就会去实例原型里也就是prototype指向的对象里找,而这个原型对象里的contructor指向的是构造函数,在原型链中constructor属性不一定真能代表构造函数,这一点要注意。
console.log(Person.prototype.constructor==Person);
//true
console.log(person.constructor==Person)
//true
__proto__
当用new运算符来调用构造函数构造出一个实例后,实例会有一个属性__proto__它指向了构造函数的原型对象,《JavaScript高级程序设计》里面有这样一段话描写这个属性
ECMA-262第五版中管这个指针叫[[ Prototype ]],虽然在脚本中没有标准的方式访问[[ Prototype ]],但Firefox,Safari,Chrome 在每一个对象上都支持一个属性__proto__;而在其他实现中,这个属性对脚本则是完全不可见的。
意思是JS中每个对象都会有一个__proto__属性,但是在不同的浏览器上面存在不同的实现方式,貌似最新版本的IE11也实现了对它的访问,
console.log(Person.prototype.__proto__ == Object.prototype)
//output:true
原型链
我们说到prototype是一个对象,那么它会不会也有一个__proto__属性,指向它的构造函数的原型对象呢?答案是肯定的。它指向的是Object的原型对象Object.prototype,而Object.prototype的__proto__指向的是 null ,这就是person的原型链的尽头。
console.log(Object.prototype.__proto__== null);
//output:true
值得注意的是在上面的例子中函数Person也是有一个__proto__属性的,因为函数本质上也是一个对象,它指向的是Function.prototype。Person.__proto__要和Person.prototype.__proto__区分开,前者指向的函数的原型,后者指向的是函数原型的原型。
Object上面也有__proto__属性,它指向的是Function.prototype。最难以理解的地方来咯,下图为Function和Object之间的关系,三条红色虚线最难以理解,下面我阐述一下我个人的理解,仅代表我个人观点,因为我是一个初学者。。。。
第一条红色的虚线标识Function的prototype和__proto__指向同一个地方,这样做,我认为是因为Function已经是顶级的构造器的,顶级的构造器哪里需要什么参考原型。
console.log(Function.prototype == Function.__proto__);
//output:true
第二条红色虚线标识Function.prototype.proto指向Object.prototype,这样做,是要符合所有对象的原型都是Object.prototype的原则,从名字上来看也可以知道"Object.prototype"应该是所有对象的祖先,也包括Function.prototype这个对象。
console.log(Function.prototype.__proto__ == Object.prototype)
//output:true
第三条红色虚线也是讲的这个道理,Object.prototype是所有对象的祖先,它是不需要有原型的,所有对象都会继承Object.prototype的属性和方法。
console.log(Object.prototype.__proto__);
//output:null
hasOwnProperty()和"in"
对象会从原型链上继承属性,判断属性是存在与实例上,还是原型链上,用hasOwnProperty()方法。'in'运算符则会判断属性是否可以访问,无论是在实例上还是在原型链上,
console.log(person.hasOwnProperty("firstName"));
//true
console.log(person.hasOwnProperty("lastName"));
//false
console.log("firstName" in person);
//true
console.log("lastName" in person);
//true
参考,掘金冴羽大佬的深入JS系列,红宝书,知乎藕粉海大佬的深入原型。
//欢迎在评论区留言讨论。