【问题标题】:what is the different between obj.__proto__ = events.EventEmitter.prototype and obj.prototype.__proto__ = events.EventEmitter.prototypeobj.__proto__ = events.EventEmitter.prototype 和 obj.prototype.__proto__ = events.EventEmitter.prototype 有什么区别
【发布时间】:2013-08-07 12:46:20
【问题描述】:

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

【问题讨论】:

    标签: node.js


    【解决方案1】:

    语句不一样。

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

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

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


    第二个例子是最简单的。不涉及构造函数。 hexo 是一个使用对象文字创建的普通对象。作者希望通过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__

    【讨论】:

    • 我添加了相关代码,您的回复很有帮助,但是根据您的解释,我阅读的代码似乎没有意义,请您帮我检查一下吗?
    • @cinvro 感谢您提供实际代码。您可能已经认识到,第一段代码确实改变了作为 constructor 函数的原型属性的对象。两种处理原型的方式都有其独特的优点/目的。我将尝试进一步解释。我认为您发布了一个非常好的问题,因为它相对难以回答正确和准确。
    • 内置函数对象的原型和proto指向同一个对象。 Function.__proto__ === Function.prototype 结果为真。这是我所知道的唯一案例。
    • @Chandu 我认为这将是一个很好的面试问题! :)
    【解决方案2】:

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

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

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

    但是,Door.prototype 是一个对象的实例,所以要获得 its 原型,您需要使用__proto__

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

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

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

    Source

    【讨论】:

    • 丹,您认为您可以在我的回答中解决我的 cmets 问题吗? (我将它作为我的答案的一部分发布,因为它对于一个单纯的评论框来说非常冗长。)
    • 据我所知,obj.__proto === obj.prototype 的唯一情况就是这样,Function.__proto__ === Function.prototype。还有其他情况吗?
    【解决方案3】:

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

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

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

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

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

    当上面的函数作为new constructorFn()这样的new操作符的一部分被调用时,JS引擎会创建一个对象,它的[[Class]]属性设置为Object[[Prototype]]属性设置为@指向的对象987654338@的构造函数。

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

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

    【讨论】:

    • "只有函数(通过函数语句、函数表达式、函数构造函数 fn 创建)才会有这个属性。" - 不完全正确。我可以a = {prototype:"somevalue"},稍后再读出来就好了。它只是没有任何特殊含义。
    • 我也不认为函数对象的原型属性有任何“外部”。它只是一个常规属性,但一旦将函数用作构造函数,它就会具有特殊含义。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-29
    • 2017-04-16
    • 2015-09-29
    • 2016-10-29
    • 1970-01-01
    • 2012-03-16
    相关资源
    最近更新 更多