引出问题

    function Person(name, age) {}     // Person构造函数是个空函数,将默认值都放在原型对象中
    
    Person.prototype.name = 'xiaoming'
    Person.prototype.age = 20
    Person.prototype.sayHi = function() {
        console.log(`hi, ${this.name}`)
    }
    
    const p1 = new Person()
    
    /*
        实例对象的内部指针指向的是构造函数的原型对象
        构造函数的prototype属性指向的也是这个原型对象
    
        实例对象和构造函数之间没有直接的联系
    */
    
    console.log(p1.__proto__ === Person.prototype)    //true
  
    console.log(Person.prototype.constructor)
    
    /*
        ƒ Person(name, age) {
        this.name = name
        this.age = age
        this.sayHi = function() {
            console.log(`hi, ${this.name}`)
        }
        
        构造函数的原型对象中的constructor指向的是构造函数
    }
    */
    
    // 尝试重写Person构造函数的原型对象
    Person.prototype = {
        name: 'alice',
        age: 12,
        sayLove: function() {
            console.log(`i love ${this.name}`)
        }
    }
    
    console.log(Person.prototype.constructor)    // ƒ Object() { [native code] }

重写了 prototype 之后发现它的constructor不再指向 Person,而是指向了Object构造函数

why ?

明确一点,在调用构造函数的时候,会为生成的新的实例对象添加一个指针指向构造函数的原型对象
那么在重写prototype的时候我们用对象字面量的方式创建了一个新的对象,而用这种方式创建就相当于调用了Object构造函数
不信可以试试

    const o1 = {}
    const o2 = new Object()
    
    console.log(o1.__proto__ === o2.__proto__)    // true

此时,在调用了Object构造函数创建一个新对象,并将这个新对象作为 Person 的 prototype 之后,
发现这个原型对象里面少了一个constructor属性;
当在一个实例对象中找不到该属性时,就会去这个实例对象的构造函数的原型对象中寻找,
这个原型对象的构造函数是 Object,所以就会去 Object构造函数的原型对象中寻找,
而我们前面说了,构造函数的原型对象中的constructor指向的是构造函数,
所以Object的原型对象中的constructor指向的还是Object,

那么如何避免这种情况呢?
如果constructor真的很重要,那么在重写原型对象的时候可以在对象中加上constructor属性,
这样的话就不会去新对象的原型对象中查找constructor属性了

Person.prototype = {
    constructor: Person,    // 注意这个值不是字符串
    name: 'alice',
    age: 12,
    sayLove: function() {
        console.log(`i love ${this.name}`)
    }
}

console.log(Person.prototype.constructor)
/*
    ƒ Person(name, age) {
    this.name = name
    this.age = age
    this.sayHi = function() {
        console.log(`hi, ${this.name}`)
    }
}
*/

仍然需要注意的是,在修改了构造函数的原型对象之后,即使给原型对象添加了constructor属性,但是之前通过构造函数生成的实例对象不会自动更新它们的原型对象的指针。请看下方的例子:

    //我们可以看一下Person构造函数的原型对象
    
    console.log(Person.prototype)    //    {name: "alice", age: 12, sayLove: ƒ, constructor: ƒ}
    
    //再来看一下p1的原型对象指针指向的对象
    
    console.log(p1.__proto__)    //    {name: "xiaoming", age: 20, sayHi: ƒ, constructor: ƒ}

所以在修改了原型对象之后,只有在修改之后生成的实例对象上会有新的属性和方法,之前的实例对象指向的仍然是之前的原型对象。


秦锋
26 声望0 粉丝

下一篇 »
Git入门