Nodejs中一个奇怪的javascript extend问题

Nodejs中一个奇怪的javascript extend问题

代码在这里:

http://jsbin.com/xabowoyihawe/1/edit?js,console

问题: 为什么noop.name可以取到值而noop.title是undefined?而name和title都是在noop.prototype中的

function noop() {   }
function _extend(source, obj) {
  for (var prop in source) {
    if(!obj.prototype[prop]) {
      obj.prototype[prop] = source[prop]
    }
  }
  return obj
}

noop.prototype[“name”] = ‘noop’

_extend({“title”:“sometitle”}, noop)

var n = new noop() console.log( noop.name, noop.title, noop.prototype.name, noop.prototype.title, noop.prototype) console.log( n.name, n.title)


3 回复

Node.js 中一个奇怪的 JavaScript extend 问题

背景

在 Node.js 中,我们经常需要扩展对象的功能。使用 extend 函数可以帮助我们将一个对象的属性复制到另一个对象的原型上。然而,在某些情况下,这种操作可能会导致一些意外的行为。

问题描述

在这个例子中,我们定义了一个名为 noop 的函数,并使用 _extend 函数尝试将一个包含 nametitle 属性的对象扩展到 noop 函数的原型上。尽管 nametitle 都被添加到了 noop.prototype 中,但当我们创建一个新的 noop 实例并访问这些属性时,只有 name 可以正确地获取到值,而 title 却是 undefined

示例代码

function noop() { }

function _extend(source, obj) {
  for (var prop in source) {
    if (!obj.prototype[prop]) {
      obj.prototype[prop] = source[prop];
    }
  }
  return obj;
}

// 直接设置 name 属性到 prototype
noop.prototype["name"] = 'noop';

// 使用 _extend 方法扩展 title 属性
_extend({ "title": "sometitle" }, noop);

// 创建新的 noop 实例
var n = new noop();

// 输出结果
console.log(noop.name, noop.title, noop.prototype.name, noop.prototype.title, noop.prototype);
console.log(n.name, n.title);

分析

让我们详细分析一下代码执行的步骤:

  1. 直接设置 name 属性:

    noop.prototype["name"] = 'noop';
    

    这行代码直接将 name 属性添加到 noop.prototype 上。

  2. 使用 _extend 方法扩展属性:

    _extend({ "title": "sometitle" }, noop);
    

    这里 _extend 函数遍历传入的对象,并将其属性添加到 noop.prototype 上。但是由于 noop.prototype 已经有一个 name 属性,因此 _extend 函数不会覆盖它。

  3. 创建新的 noop 实例:

    var n = new noop();
    

    当我们创建一个新的 noop 实例时,该实例的原型链指向 noop.prototype

  4. 输出结果:

    • noop.name'noop',因为我们在 noop.prototype 上直接设置了 name
    • noop.titleundefined,因为在 _extend 函数中,我们检查了 obj.prototype[prop] 是否存在,如果存在则不进行赋值。
    • n.name'noop',因为 n 继承自 noop.prototype
    • n.titleundefined,因为 _extend 函数没有覆盖已经存在的 name 属性。

解决方案

为了避免这种情况,可以在 _extend 函数中去掉对现有属性的检查:

function _extend(source, obj) {
  for (var prop in source) {
    obj.prototype[prop] = source[prop]; // 移除检查
  }
  return obj;
}

通过这种方式,_extend 函数会覆盖 noop.prototype 上已有的属性,从而确保所有属性都被正确地继承到新实例中。


noop.name,取到的是Function实例的name,和你后面的逻辑没有关系。 var noop = function(){} 或者 var noop = new Function(); 或者在你原来代码基础上,第2行直接输出 console.log(noop.name); 这样你应该能理解了吧?

从你提供的代码来看,问题出在 _extend 函数中。你只在 obj.prototype 上添加了属性,但没有直接修改 obj 对象本身。因此,当你创建 noop 的实例时,实例上并没有 title 属性,只有 name 属性。

示例代码修正

function noop() {}

function _extend(source, obj) {
  for (var prop in source) {
    obj[prop] = source[prop]; // 直接修改 obj 而不是 obj.prototype
  }
  return obj;
}

noop.prototype["name"] = 'noop';

_extend({ "title": "sometitle" }, noop);

var n = new noop();
console.log(noop.name, noop.title, noop.prototype.name, noop.prototype.title, noop.prototype);
console.log(n.name, n.title);

解释

  1. noop.prototype["name"] = 'noop': 这里你将 name 属性添加到了 noop 的原型对象上。
  2. _extend({"title":"sometitle"}, noop): 在 _extend 函数中,你原本只是修改了 obj.prototype,这会导致实例继承这些属性。为了使 noop 对象自身也包含这些属性,你需要直接修改 obj 而不是 obj.prototype
  3. 创建实例:当创建 noop 的实例时,实例会查找自身属性,如果找不到,则会在原型链上查找。

通过上述修改,n.title 将不再是 undefined,而是 "sometitle"

输出结果

  • noop.name'noop'
  • noop.title'sometitle'
  • n.name'noop'
  • n.title'sometitle'

这样,noop 和它的实例 n 都有了 nametitle 属性。

回到顶部