首页 > 关于js对象实例以及原型的一点疑惑

关于js对象实例以及原型的一点疑惑

我写了如下两段代码:

function Person(){}
var p1 = new Person();

Person.prototype = {
    constructor: Person,
    name: "cxc",
    age: 20,
    sayName: function(){
        console.log(this.name);
    }
};

p1.sayName();

这一段的运行结果是:Uncaught TypeError: undefined is not a function
然后我又把它改写为下面的代码:

function Person(){}

Person.prototype = {
    constructor: Person,
    name: "cxc",
    age: 20,
    sayName: function(){
        console.log(this.name);
    }
};

var p1 = new Person();
p1.sayName();

运行结果却是没问题的,为什么会这样呢。
原型不是具有动态性吗,在它上面所做的任何修改不是都会反映到实例上吗。所以我就有些想不明白了,烦劳哪位大神能给解释一下。在此先行谢过了。


其实问题的核心只有一点
就算你重写构造函数的原型后,实例的指针仍然指向其当初构造函数的原型
你进行重写后
你新原型的constructor属性指向Person,但是实例p1指向的原型仍然为Object.prototype
p1[[prototype]]隐式属性仍然为Object.prototype,故不会存在相应的方法sayName()
你重写了构造函数的原型不等于实例也会和构造函数一起改变相应的指针
要知道关键字new才是进行一系列原型传递的信号,你有兴趣可以看看


让我来给你解释一下吧。
看看下面这段代码

function Person(){}
var proto1 = Person.prototype;
var p1 = new Person();

Person.prototype = {
    constructor: Person,
    name: "cxc",
    age: 20,
    sayName: function(){
        console.log(this.name);
    }
};

console.log(Person.prototype.isPrototypeOf(p1)); //输出false
console.log(proto1.isPrototypeOf(p1)); //输出true

MDN Reference: Object.prototype.isPrototypeOf()
The isPrototypeOf() method tests for an object in another object's prototype chain.

意思就是:Object.isPrototypeOf(obj)可以用来检测该原型是否处于obj对象的原型链上。

在上面这段代码中,首先我将一开始Person.prototype的引用指向proto1
最后我重新检验发现,Person.prototype已经不是p1的原型了,而proto1还是p1的原型。

其实罪魁祸首就是Person.prototype = {...}这句话,因为它声明了一个新的对象,让Person.prototype指向它。

要实现题主所需要的动态增加属性的功能可以这么写:

function Person(){}
var p1 = new Person();

Person.prototype.name = 'hi';
Person.prototype.sayName = function(){
    console.log(this.name);
}

p1.sayName();

这样做就不是重新声明一个原型对象,而是对原来的原型对象进行扩展。


你先要理解你所说的“修改”指的是什么?对于你第一个例子中希望达到的效果正确的写法应该是:

function Person(){}
var p1 = new Person();

Person.prototype.name = 'cxc';
Person.prototype.sayName = function(){
    console.log(this.name);
};

p1.sayName();

而题主所做的操作是:

Person.prototype = {
    constructor: Person,
    name: "cxc",
    age: 20,
    sayName: function(){
        console.log(this.name);
    }
};

这就相当于对 Person 的原型对象进行重新赋值,毫无疑问这是一种引用赋值,所以现在 Person 的原型对象已经指向了另外一片内存空间,而你之前建立的实例 p1 仍然指向的是原来的内存空间,所以肯定达不到题主想要的效果了。

对于原型对象 prototype 和 原型 __proto__ 的理解,强烈推荐 @nightire 前辈的好文:

《理解 JavaScript(四)》


prototype才没有楼主认为的“动态”功能呢。
不然楼主的反例该如何解释呢,对吧。

你的空函数创建后,创建了一个原型对象PPerson.prototype会指向P
原型对象P中,有constructor属性,指向其构造函数Person
还有一个不可枚举的内部属性[[prototype]],chrome和firefox下可以用__proto__属性来获取,该指针指向Object对象的原型对象。

此时,原型对象P中,并没有sayName方法,所以,var p1 = new Person();p1此时也不会拥有sayName方法。

【热门文章】
【热门文章】