obj .__ proto__ = events.EventEmitter.prototype和obj.prototype .__ proto__ = events.EventEmitter.prototype之间有什么不同

时间:2013-08-07 12:46:21

标签: node.js

obj.prototype.__proto__ = events.EventEmitter.prototype

我有时会看到上面的代码,我会对它进行谷歌搜索,他们说这行将所有EventEmitter属性复制到obj。我也看到了这样的代码:

obj.__proto__ = events.EventEmitter.prototype

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


我在这个article中看到了第一个用法,其中作者给出了这个例子:

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');
  });

3 个答案:

答案 0 :(得分:3)

陈述不一样。

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

换句话说,给定的第一行代码中的obj应该是一个(构造函数)函数才有意义,并且看到一个函数具有像obj这样的通用变量名称是非常罕见的。

如果你分享你找到确切第一个陈述的来源,这可能会让事情变得清晰。


第二个例子是最简单的。没有涉及构造函数。 hexo是使用object-literal创建的普通对象。作者希望通过hexo方法提供EventEmitter方法,因此他将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原型继承非常棘手。

另见Confusion about setting something.prototype.__proto__

答案 1 :(得分:2)

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

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

在您的示例中,您可以看到它们引用了Door.prototypehexo.__proto__。这里的关键区别是Door是构造函数,而hexo是对象的实例。

但是,Door.prototype是对象的实例,因此要获取原型,您需要使用__proto__

在这两种情况下,赋值的RHS都是构造函数,因此引用prototype

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

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

Source

答案 2 :(得分:1)

JavaScript中的所有内容都是对象。 JavaScript中的每个对象(可能是一个函数{}new Object()都有一个名为[[Prototype]]的内部属性。

[[Prototype]]是JavaScript中原型继承的原因。此内部属性通过__proto__向程序员公开。这是非标准的。并非所有JS环境都支持此功能。

我们使用构造函数创建的对象如何获取其[[Prototype]]

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

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

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

由于这个新创建的对象属于类Object,因此它没有prototype属性。

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