什么';这是JavaScript';s对象。原型行为?

什么';这是JavaScript';s对象。原型行为?,javascript,object,prototype,javascript-objects,Javascript,Object,Prototype,Javascript Objects,我遇到了一段奇怪的代码片段,我完全无法理解,它是: var obj = function() {}; obj.prototype.x = 5; var instance1 = new obj(); obj.prototype = {y: 6}; var instance2 = new obj(); console.log(instance1.x, instance1.y, instance2.x, instance2.y); // 5, undefined, undefined, 6

我遇到了一段奇怪的代码片段,我完全无法理解,它是:

var obj = function() {};
obj.prototype.x = 5;

var instance1 = new obj();

obj.prototype = {y: 6};

var instance2 = new obj();

console.log(instance1.x, instance1.y, instance2.x, instance2.y);
// 5, undefined, undefined, 6
现在,问题是:

  • 为什么此日志记录为
    5,未定义,未定义,6
    ,而不是
    未定义,6,未定义,6
  • 为什么替换原型并不像通常那样更改对象所有实例的原型
  • V8发动机在此代码中一步一步地执行什么操作
  • 编辑:如何更改所有实例的原型?

  • 感谢您的每一个解释。

    原型是一项与新的幕后功能相结合的功能。它适用于与new一起使用的该函数的所有实例。在第一个示例中,您将.x=5追加到原型中,并且您创建的实例的值为.x=5。稍后将原型修改为新对象。现在,这就是任何新实例中使用的原型。这就是为什么第一个实例只有.x=5,第二个实例只有.y=6的原因

    因此,首先,您的两行代码创建一个函数,
    obj
    ,并分配给它的原型
    {x:5}

    当您创建此对象的实例时,它似乎有一个内部引用,指向当它是
    new
    'd时存在的原型

    在此之后,将原型重新分配给
    {y:6}
    ,这不会影响对第一个原型的
    实例1
    内部引用

    然后,当您创建
    instance2
    时,它具有对第二个原型的内部引用,因此,记录它们将生成
    5,未定义,未定义,6

    #4

    您可以,而不是将原型重新指定给新对象:

    obj.prototype = {y: 6};
    
    改为修改原型:

    delete obj.prototype.x; // Setting to undefined should produce same behaviour
    obj.prototype.y = 6;
    
    这将产生输出:
    undefined,6,undefined,6


    我已经在Windows上的Chrome和Firefox最新版本上对此进行了测试。

    因为已经创建了
    实例1
    new
    关键字通过执行构造函数创建新对象,在您的例子中,构造函数是
    obj

    因为您在第一个实例初始化后更改了原型,所以您不再具有相同的构造函数状态(例如原型),并且无法生成相同的对象。但创建的模型仍然存在,参考了旧的原型

    当您再次使用
    obj
    构造函数时,您正在创建另一个对象,该对象可以非常粗略地转换为经典类型的继承术语,作为另一个类的实例

    编辑:#4 这把小提琴: 表明

    obj.prototype.y=6
    

    成功更改所有现有对象。因此,答案显然是不应该将新对象指定为原型,而应该修改当前原型。

    实例的原型不引用类,而是引用原型对象本身。当您尝试
    Object.getPrototypeOf()
    查看实例引用的原型对象时,这一点就会变得很清楚

    Object.getPrototypeOf(instance1)
    Object { x: 5, 1 more… }
    
    Object.getPrototypeOf(instance2)
    Object { y: 6 }
    
    该字段
    getPrototypeOf
    references应该是每个实例都存在的内部字段。在
    getPrototypeOf
    存在之前,您可以根据

    prototype
    属性的值用于初始化新创建对象的
    [[prototype]]
    内部属性,然后将函数对象作为新创建对象的构造函数调用

    很明显,
    prototype
    只是初始化
    [[prototype]]
    属性。当我们创建一个对象时,
    [[Prototype]]
    被设置为构造函数的
    Prototype
    对象,并建立原型链。在你的情况下,当你

    var obj = function() {};
    obj.prototype.x = 5;
    
    var instance1 = new obj();
    
    [[Prototype]]
    如下所示

    console.log(Object.getPrototypeOf(instance1));
    # { x: 5 }
    
    Object.setPrototypeOf(instance1, {
        y: 6
    });
    
    delete obj.prototype.x;
    obj.prototype.y = 6;
    
    console.log(Object.getPrototypeOf(instance1) === Object.getPrototypeOf(instance2));
    # true
    console.log(Object.getPrototypeOf(instance1) === obj.prototype);
    # true
    
    (是的,您可以使用
    对象.getPrototypeOf
    函数访问
    [[Prototype]]]

    因此,当JS引擎在
    instance1
    中查找
    x
    时,它发现值为
    5
    ,并且由于
    y
    未定义,因此它使用
    未定义的

    在第二种情况下

    obj.prototype = {y: 6};
    
    var instance2 = new obj();
    
    您正在更改
    obj
    prototype
    对象,以便使用此函数构造的新对象将使用分配给它的新对象。因此,
    [[Prototype]]
    对于
    instance2

    console.log(Object.getPrototypeOf(instance2));
    # { y: 6 }
    
    这就是为什么,
    instance2
    无法在其中找到
    x
    ,而
    y


    要回答最新的问题

    编辑:我如何更改所有实例的原型

    您可以使用
    object.setPrototypeOf
    更改旧对象的原型,如下所示

    console.log(Object.getPrototypeOf(instance1));
    # { x: 5 }
    
    Object.setPrototypeOf(instance1, {
        y: 6
    });
    
    delete obj.prototype.x;
    obj.prototype.y = 6;
    
    console.log(Object.getPrototypeOf(instance1) === Object.getPrototypeOf(instance2));
    # true
    console.log(Object.getPrototypeOf(instance1) === obj.prototype);
    # true
    
    因为这使得
    instance1
    [[Prototype]]
    instance2
    不同,所以我们只需更新构造函数的
    Prototype
    对象,如下所示

    console.log(Object.getPrototypeOf(instance1));
    # { x: 5 }
    
    Object.setPrototypeOf(instance1, {
        y: 6
    });
    
    delete obj.prototype.x;
    obj.prototype.y = 6;
    
    console.log(Object.getPrototypeOf(instance1) === Object.getPrototypeOf(instance2));
    # true
    console.log(Object.getPrototypeOf(instance1) === obj.prototype);
    # true
    
    现在,我们没有更改
    instance1
    instance2
    的内部属性。我们可以这样检查

    console.log(Object.getPrototypeOf(instance1));
    # { x: 5 }
    
    Object.setPrototypeOf(instance1, {
        y: 6
    });
    
    delete obj.prototype.x;
    obj.prototype.y = 6;
    
    console.log(Object.getPrototypeOf(instance1) === Object.getPrototypeOf(instance2));
    # true
    console.log(Object.getPrototypeOf(instance1) === obj.prototype);
    # true
    
    注意:惯例是将构造函数的首字母命名为大写字母

  • 为什么此日志记录为
    5
    未定义
    未定义
    6
    而不是
    未定义
    未定义
    6
  • 为什么替换原型并不像通常那样更改对象所有实例的原型
  • 从根本上说,这归结为一个事实,即对象引用是值,而不是数字,它告诉JavaScript引擎(在您的例子中是V8)对象在内存中的位置。复制值时,您只需执行以下操作:复制值。复制对象引用将生成t的副本