3

obj.prototype.__proto__ = events.EventEmitter.prototype

我有时看过上面的代码,我在谷歌上搜索了一下,他们说这一行将所有 EventEmitter 属性复制到 obj. 而且我还看到这样的代码:

obj.__proto__ = events.EventEmitter.prototype

所以我想知道它们是否相同?


在这篇文章中看到了第一个用法,作者给出了例子:

var events = require('events');
function Door(colour) {
  this.colour = colour;
  events.EventEmitter.call(this);
  this.open = function()
  {
    this.emit('open');
  }
}
Door.prototype.__proto__ = events.EventEmitter.prototype;
var frontDoor = new Door('brown');
  frontDoor.on('open', function() {
  console.log('ring ring ring');
});

frontDoor.open();

他解释说:

此行:Door.prototype.__proto__ = events.EventEmitter.prototype;将所有 EventEmitter 属性复制到 Door 对象。

至于第二种方式,我是在 hexo 的源码中看到的,在 init.js 中有代码:

    var hexo = global.hexo = {
    get base_dir(){return baseDir},
    get public_dir(){return baseDir + 'public/'},
    get source_dir(){return baseDir + 'source/'},
    get theme_dir(){return baseDir + 'themes/' + config.theme + '/'},
    get plugin_dir(){return baseDir + 'node_modules/'},
    get script_dir(){return baseDir + 'scripts/'},
    get scaffold_dir(){return baseDir + 'scaffolds/'},
    get core_dir(){return path.dirname(dirname) + '/'},
    get lib_dir(){return dirname + '/'},
    get version(){return version},
    get env(){return env},
    get safe(){return safe},
    get debug(){return debug},
    get config(){return config},
    get extend(){return extend},
    get render(){return render},
    get util(){return util},
    get call(){return call},
    get i18n(){return i18n.i18n},
    get route(){return route},
    get db(){return db}
  };

  hexo.cache = {};

  // Inherits EventEmitter
  hexo.__proto__ = EventEmitter.prototype;

  // Emit "exit" event when process about to exit
  process.on('exit', function(){
    hexo.emit('exit');
  });
4

3 回答 3

3

说法不一样。

而不是obj.prototype.__proto__ = events.EventEmitter.prototype,我希望看到类似Constructor.prototype.__proto__ = events.EventEmitter.prototype, (Constructor任何类型的构造函数,因此可以有任何名称。它们通常大写。)因为该prototype属性通常仅在函数上可用,并且没有任何特殊定义为常规(非功能)对象的属性时的含义。

换句话说,obj在给出的第一行代码中应该是一个(构造函数)函数才有意义,而且很少看到函数具有这样的通用变量名obj.

如果您分享找到确切第一条语句的来源,这可能会解决问题。


第二个例子是最简单的。不涉及构造函数。hexo是使用对象文字创建的普通对象。作者希望EventEmitter方法可以通过该hexo方法获得,所以他赋值EventEmitter.prototype给了__proto__属性,这实际上改变了 的原型hexo

第一个代码示例稍微复杂一些。在这里,作者希望确保由该Door函数构造的任何对象都将提供对 EventEmitter 方法的访问。由 Door 函数构造的任何对象都将Door.prototype作为其原型。这个特殊的原型现在有 EventEmitter 作为它的原型,所以 EventEmitter 函数可以通过原型链的两个步骤来访问。

“将所有 EventEmitter 属性复制到 Door 对象。” - 这个特定的评论具有误导性。不复制任何属性。唯一发生的就是这个。

door = new Door
door.on("open", function() { console.log("door has opened")})

的原型door是现在Door.prototype。如果在尝试访问某个属性(在本例中on)时未找到该属性,则 JS 引擎将查看此原型。door- -的原型Door.prototype也没有on定义,所以JS引擎会查看是否Door.prototype有原型。它确实,以events.EventEmitter.prototype. 这个对象确实on定义了一个属性。

希望这能让事情更清楚一些。Javascript 原型继承非常棘手。

另请参阅关于设置 something.prototype.__proto__ 的困惑

于 2013-08-07T13:06:09.360 回答
2

prototype属性通常在构造函数上找到,即创建新对象的函数。构造函数的prototype是用作新实例化对象的原型的对象。

对象的__proto__属性指向在对象第一次实例化时用作原型的对象。它是非标准的,因此无法保证您的 JavaScript 引擎会支持它。

在您的示例中,您可以看到它们指的是Door.prototypeand hexo.__proto__。这里的关键区别在于它Door是一个构造函数,而hexo它是一个对象的实例。

然而,Door.prototype是一个对象的实例,所以要得到它的原型,你需要使用__proto__.

在这两种情况下,赋值的 RHS 都是构造函数,所以指的是prototype.

总之,如果您想要构造函数使用的原型,请使用prototype. 如果你想要一个实例化对象的原型,你可以使用__proto__.

事实上,你最好只使用Object.getPrototypeOf.

资源

于 2013-08-07T12:53:05.600 回答
1

JavaScript 中的一切都是对象。并且每个对象,它可能是一个函数,{}, new Object(),在 JavaScript 中都有一个称为 的内部属性[[Prototype]]

[[Prototype]]这正是 JavaScript 中原型继承的原因。这个内部属性通过__proto__. 这是一个非标准。并非所有 JS 环境都支持这一点。

我们使用构造函数创建的对象如何获得它的[[Prototype]]?

只有[[Class]]is的对象才能Function获得属性prototype。这意味着每个函数在被 JS 引擎执行时声明,它都会创建一个对象。它[[Class]]被设置为Function并且一个属性prototype被附加到这个函数对象。

默认情况下,此原型是一个具有一个属性constructor指向函数对象的对象。

当上述函数作为 new 运算符的一部分被调用时new constructorFn(),JS 引擎创建一个对象,其[[Class]]属性设置为Object,属性设置为构造函数的[[Prototype]]指向的对象。[[Prototype]]

由于这个新创建的对象属于 class Object,因此它不具有该prototype属性。

简而言之,__proto__存在于每个对象中。prototype默认情况下,仅存[[Class]]在于Function. 这意味着只有函数(通过函数语句、函数表达式、Function构造函数创建)才会具有此属性。

于 2013-08-07T13:33:47.067 回答