JavaScript 的原型链

来源:互联网 发布:hip2p 网络摄像机 编辑:程序博客网 时间:2024/05/05 21:29

假设 Foo / Bar 是构造函数:

function Foo(name) {  this.name = name}function Bar() {  this.setAge = (age) => this.age = age  this.showAge = () => console.log(this.age)}let foo = new Foo('foo')

总结一下,就是:

  1. __proto__ 属性属于对象,prototpye 属性属于构造函数。

    因此上例中,对象 foo 具有 __proto__ 属性,但没有 prototype 属性。Foo 和 Bar 具有 prototype属性。

     foo.__proto__  // 有值 foo.prototype  // undefined Foo.prototype  // 有值 Foo.__proto__  // ?? 有值还是 undefined,留个悬念
  2. 对象的 __proto__ 指向其构造函数的 prototype

     foo.__proto__ === Foo.prototype  // true

    __proto__ 总是指向 prototype,而 prototype 总是被 __proto__ 所指向。

  3. prototype 也是对象,因此它也有自己的 __proto__ 属性。根据上面所言,__proto__ 指向对象自身构造函数的 prototype 属性,而这里的 Foo.prototype 的构造函数是谁呢?因为 Foo.protype 是一个普通对象,Object,它是通过 new Object() 创造出来的,所以它的构造函数是 Object,所以它指向 Object.protype

     Foo.prototype.__proto__ === Object.prototype  // true

    又因为 foo.__proto__ = Foo.prototype,所以,可得

     foo.__proto__.__proto__ === Object.prototpye  // true

    Object.prototype也是对象,那么它的 __proto__ 又是多少呢,规定 Object.prototype.__proto__ = null (呃,有点特殊),因此

     foo.__proto__.__proto__.__proto__ === null  // true

    一般来说,一个普通的自定义的构造函数生成的对象,通过向上查找三级 __proto__ 就能得到 null 值。

  4. 如上所示,因为对象是通过 __proto__ 这条链条向上查找方法和变量,而 __proto__ 指向的是各级构造函数的 prototype 属性。因此,为构造函数添加原型方法时,这些方法必须绑定在 prototype属性上,这样才能被对象的 __proto__ 所查找到。

     function Foo(name) {     this.name = name } let foo = new Foo('foo') Foo.prototype.showName = function() {     console.log(this.name) } foo.showName() // 'foo'
  5. 上面说到,一般来说,一个普通的自定义的构造函数生成的对象,向上查找三级 __proto__ 就得到了 null,它们的链条只有三级。如果我们想实现继承关系,比如让 Foo 继承自 Bar,让 foo 能用上 Bar 中的方法,那要怎么办呢。方法就是手动修改这条默认的 __proto__ 链条。我们要让 Foo.prototype.__proto__ 不再指向默认的 Object.prototype,而是指向 Bar.prototype

     Foo.prototype.__proto__ = Bar.prototype

    那么就可以得到:

     foo.__proto__.__proto__ = Bar.prototype

    这样,foo 就可以用上 Bar 中的方法了:

     foo.setAge(18) foo.showAge()

    呃呃,但是实验之后,证明我还是太天真了,上面通过强行改变 prototype.__proto__ 的方法并不管用,原因我想可能是因为 __proto__ 这个值是一个内部值,并不是一个公开的值,从它的前缀是 2 个下划线可知,因此它可以被外部所读取,但不能被外部所修改,只能在内部被修改。为了达到相同的效果,该怎么做呢。前面我们得知:

     foo = new Foo('foo') // 得到 foo.__proto__ --> Foo.prototype

    那么:

     Foo.prototype = new Bar() // 得到 Foo.protype.__proto__ --> Bar.prototype Foo.protoype.__proto__ === Bar.prototype  // true
  6. 现在我们知道了,prototype 也有一个 __proto__ 属性,那它还包含什么值呢?我们在 chrome dev tool 中打印一下它。

     function Foo(name) {     this.name = name } Foo.prototype.showName = function() {     console.log(this.name) } > Foo.prototype < Object {showName: function, constructor: function}     showName: function ()     constructor: function Foo(name)     __proto__: Object

    除了我们已知的 __proto__ 和后来附加的原型方法 showName,还有一个属性,叫 constructor,从名字上看,它应该是表示构造函数,而它的值正是构造函数 function Foo(name){...}。所以得到:

     Foo.prototype.constructor === Foo  // true
原创粉丝点击