You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
functionPerson(){}letperson1=newPerson()person1.name='Charles'person1.age=27person1.job='Software Engineer'person1.sayName=function(){console.log(this.name)}person1.sayName()// Charles
在这个例子中,Person就是一个构造函数,我们使用 new 创建了一个实例对象person1,这是是用构造函数创建对象,很简单吧。
构造函数创建对象
在这个例子中,Person就是一个构造函数,我们使用 new 创建了一个实例对象person1,这是是用构造函数创建对象,很简单吧。
prototype
我们创建的每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,这个对象就是我们通过调用构造函数创建的那个对象实例的原型对象。
这个原型对象就管理着由特定类型的创建的所有实例共享的属性和方法。也就是说我们通过某个构造函数创建对象实例,它都会从它指向的原型那里继承属性和方法。
由上个例子:
我们在浏览器打印一下,可以看到,Person.prototype是一个对象,这个原型对象上有一个constructor属性指向构造函数Person,如图所示,这里是一个循环引用。
__poroto__和[[prototype]]
我们现在知道了构造函数上有一个prototype属性指向了原型对象,原型对象上的constructor属性指向构造函数,那另一个属性__proto__属性呢?它是代表什么意思?有什么作用呢?
我们来看下面这个例子:
我们可以看到,用Man构造函数实例化的child里面也有一个__proto__参数,而且它同样指向一个对象,我们发现它和Man函数的原型对象一模一样,__proto__是每个实例上都有的属性,但prototype是构造函数上的属性,这两个不一样,但 p.__proto__和 Parent.prototype 却指向同一个对象。
我们从上面知道,构造函数和原型对象之间通过prototype和constructor进行连接,所以构造函数,实例和原型对象之间的关系如下图:
现在我们知道可以通过__proto__访问到对象的内部 [[Prototype]]
[[Prototype]] 是对象的一个内部属性,外部代码无法直接访问。
注意点
__proto__属性在 ES6 时才被标准化,以确保 Web 浏览器的兼容性,但是不推荐使用,除了标准化的原因之外还有性能问题。为了更好的支持,推荐使用 Object.getPrototypeOf()。
如果要读取或修改对象的 [[Prototype]] 属性,建议使用如下方案,但是此时设置对象的 [[Prototype]] 依旧是一个缓慢的操作,如果性能是一个问题,就要避免这种操作。
如果确要新建一个对象并继承另一个对象的[[prototype]],可以使用Object.create()。
这时child上有一个指针__proto__ 指向man对象。
原型链
来看下面这个例子:
为什么grandSon上并没有定义age属性,却能打印出 1 ,因为原型链的搜索机制是,当以读取模式访问一个实例属性时,首先会在实例中搜索该属性。如果没有找到该属性,则会继续搜索实例的原型。在通过原型链实现继承的情况下,搜索过程就得以沿着原型链继续向上。就拿上面的例子来说,调用
grandSon.age 会经历三个搜索步骤:1)搜索实例;2)搜索 Son.prototype ;3)搜索 Parent.prototype ,最后一步才会找到该属性。在找不到属性或方法的情况下,搜索过程总是要一环一环地前行到原型链末端才会停下来。
继续上面的例子验证我们的想法:
参考文章:
《JavaScript 高级程序设计第三版》
重新认识构造函数、原型和原型链
下集预告
原型链和继承
The text was updated successfully, but these errors were encountered: