javascript - 定义一个函数,扩展函数原型(prototype),创建两个实例,原型(prototype)被修改?

标签 javascript function-prototypes

有人可以告诉我为什么结果是这样的,而不是我预期的那样。这让我抓狂!

var f = function(b){
    console.log(this.config);
    this.config.b = b;
}
f.prototype.config = {
    a: 'a',
    b: 'b'
};

var f1 = new f('bb');
var f2 = new f('bbb');

// logs
// { a: 'a', b: 'b' }
// { a: 'a', b: 'bb' }

// expected
// { a: 'a', b: 'b' }
// { a: 'a', b: 'b' }

最佳答案

被修改的不是原型(prototype),而是您放置在原型(prototype)上的 config 对象。这是正确的行为,创建新实例时不会复制原型(prototype)引用的对象。 f1.config === f2.config,它们指向相同对象。

原型(prototype)链用于get操作的方式是这样的:

  1. 你做一些事情来查找对象的属性。比如说,this.config
  2. 检查对象本身是否具有该名称的属性。如果是,则使用该属性的副本并返回其值。在您的情况下,该对象没有自己的 config,因此我们继续下一步。
  3. 检查对象的原型(prototype)以查看是否具有该属性。如果是,则使用该属性的副本并返回其值。在您的情况下,这是真的,因为原型(prototype)具有该属性。但只是为了完整性:
  4. 重复第 3 步,根据需要继续向上(向下?)原型(prototype)链。
  5. 如果该属性根本没有,则返回undefined

(set 操作的工作方式不同;set 操作总是更新或创建它所设置的对象的属性,永远不会进一步向下 [up?] 原型(prototype)链.)

所以在您的情况下,由于您的实例没有 config 属性,我们转到原型(prototype)。由于原型(prototype)确实 有一个config 属性,所以它被使用了。该属性的值是一个对象引用,因此如果您更改该对象(通过分配给其中一个它的属性),它就会发生变化,并且任何其他也使用该对象的东西都会看到变化。

另一种查看方式是绘制图表:

+------+       +------+
|  f1  |       |  f2  |
+------+       +------+
   |              |
   +------+-------+
          |
          v
    +--------------------+       +--------+
    | [[Proto]] assigned |       | config |
    | via `new f`        |------>| object |
    +--------------------+       +--------+
                                     |
                             +-------+-------+
                             |               |
                             V               v
                     +------------+     +------------+
                     | a property |     | b property |
                     +------------+     +------------+

Another way to think of it is to get the function and prototype out of the way entirely:

var pseudoProto = {};               // A stand-in for the prototype...
pseudoProto.config = {              // ...with `config` on it
    a: 'a',
    b: 'b'
};
var f1 = {};                        // A blank object...
f1.pseudo = pseudoProto;            // ...referencing `pseudoProto`
var f2 = {};                        // Another blank object...
f2.pseudo = pseudoProto;            // ...also referencing `pseudoProto`
f1.pseudo.config.b = "bb";          // Change the `b` property on `config`
console.log(f2.pseudo.config.b);    // Logs "bb", of course

以一种非常真实的方式,这就是通过 new f() 在幕后发生的事情。您不能直接访问指向原型(prototype)的 f1f2 实例的属性(规范将其称为 [[Proto]] 属性(property)),但它是真实的东西,而且确实存在。 [仅供引用:latest version ECMAScript 规范允许我们直接使用 [[Proto]] 属性做一些事情,比如使用特定的 [[Proto]] 创建原始对象(无需通过一个函数),但仍然不能让我们直接访问属性本身。]

很多时候您希望所有实例共享同一个对象(例如函数对象!),因此原型(prototype)是这些对象引用的正确位置;但是,如果您希望每个实例都有其自己的 对象副本,则需要在构造函数中创建该对象。在你的情况下:

var f = function(b){
    this.config = {
        a: 'a',
        b: b
    };
}

var f1 = new f('bb');
console.log(f1.config);
var f2 = new f('bbb');
console.log(f2.config);
// Logs
// { a: 'a', b: 'bb' }
// { a: 'a', b: 'bbb' }

(请注意,我移动了 console.log 语句,所以我们看到的是最后的结果,而不是中间状态。)

关于javascript - 定义一个函数,扩展函数原型(prototype),创建两个实例,原型(prototype)被修改?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5235828/

相关文章:

javascript - AngularJS - 在 POST 请求的正文中发送数据

javascript - 如何使用正则表达式允许空字符串或至少 6 个字符?

Javascript:原型(prototype)方法错误?

javascript - 多重继承问题

javascript - 填充半径是否有属性或解决方法?

javascript - 推文对象数组的 ShellSort

c - 使用此规范声明函数

javascript - js inside method in method 中this关键字的逻辑是什么?

c++ - 何时不将 C 函数原型(prototype)放在头文件中

javascript - JavaScript Stack Overflow 注释字符 "limiter"在某处可用吗?