【问题标题】:Using extend to add an object to a prototype使用 extend 将对象添加到原型
【发布时间】:2011-09-15 16:34:13
【问题描述】:

每当我尝试将对象BaseNet 添加到IPv4Address.prototype 时都会出错。错误:

TypeError: 无法读取未定义的属性“ipInt”

只是没有意义。当我将对象复制到原型时,它的行为就像实际上正在执行 getter。有没有办法复制这个并且不会出现这样的错误?

var _s = require('underscore')

var BaseNet = {
  get network(){ 
    return new IPv4Address((this.ipInt & this.netmask.ipInt) >>> 0);
  },
}

function IPv4Address (address){
  this.ipInt = address
  this.netmask = {}
  this.netmask.ipInt = 255
}

_s.extend(IPv4Address.prototype,BaseNet)

//also fails with generic extend:

function extend(destination, source) {
  for (var k in source) {
    if (source.hasOwnProperty(k)) {
      destination[k] = source[k];
    }
  }
  return destination;
}

extend(IPv4Address.prototype,BaseNet)

首次编辑

也许这是我自己问题的答案。 Resig 对此发表了一篇文章,这种“修改后的”扩展方法似乎可以满足我的要求。有人可以解释吗?这似乎是一个范围界定问题,但我不明白为什么它的行为就像有人在扩展操作期间实际调用了 getter。

http://ejohn.org/blog/javascript-getters-and-setters/

function extend(a,b) {
    for ( var i in b ) {
        var g = b.__lookupGetter__(i), s = b.__lookupSetter__(i);

        if ( g || s ) {
            if ( g )
                a.__defineGetter__(i, g);
            if ( s )
                a.__defineSetter__(i, s);
         } else
             a[i] = b[i];
    }
    return a;
}

二次编辑

所以我做了更多的实验,并想出了另外两种似乎可行的方法。一个使用之前发布的 extend 方法,另一个使用 cmets 中指出的 ECMA 规范中的 defineProperties 方法。这个比那个好吗?似乎“mixin”风格更适合我正在寻找的东西,但 Object.create 方式也适用。

var BaseNet = {}
Object.defineProperties(BaseNet, {
  "network":{
    enumerable:true,
    get: function (){return new IPv4Address((this.ipInt & this.netmask.ipInt) >>> 0)}
            }
    });

function IPv4Address (address){
  this.ipInt = address
  this.netmask = {}
  this.netmask.ipInt = 255
}
IPv4Address.prototype = Object.create(BaseNet)
var ip = new IPv4Address(12345)
ip.netmask

或者,您也可以这样做:

var BaseNet = {}
Object.defineProperties(BaseNet, {
  "network":{
    enumerable:true,
    get: function (){return new IPv4Address((this.ipInt & this.netmask.ipInt) >>> 0)}
            }
    });

function IPv4Address (address){
  this.ipInt = address
  this.netmask = {}
  this.netmask.ipInt = 255
}
extend(IPv4Address.prototype,BaseNet)
var ip = new IPv4Address(12345)
ip.netmask

【问题讨论】:

  • IPv4Address 的原型是一个空对象。 ipIntIPv4Address 实例的属性,在调用构造函数时设置。
  • 是的,没错,但我没有实例化对象。没有新的,或任何东西。我只是将一个对象 BaseNet 复制到一个未实例化的 function.prototype。
  • 哦,抱歉,我以为是反之亦然....您想将IPv4Address 添加到BaseNet....对不起。

标签: javascript node.js


【解决方案1】:

它不起作用,因为在扩展时,getter 被执行,此时this.netmask 根本不是实例(没有创建实例),但实际上undefined,所以访问this.netmask.ipInt 会抛出一个错误(您无法从undefinednull 访问任何东西,无论如何都会引发错误)。

看看底层的_.extend代码:

  _.extend = function(obj) {
    each(slice.call(arguments, 1), function(source) {
      for (var prop in source) {
        // source[prop] is fetched, so getter is executed
        if (source[prop] !== void 0) obj[prop] = source[prop];
      }
    });
    return obj;
  };

您可能希望自己迭代并使用for in 循环和Object.defineProperty 复制“未触及”的getter。


至于您的编辑:那些 __xxx__ 函数是获取 getter/setter 函数而不执行它的丑陋方式。通常,传递函数的工作方式类似于someFunction,不带括号。但是,如果您使用 someGetter 访问它,getter 会自动执行。

您发布的函数复制 getter/setter 而不执行它们:

function extend(a,b) {
    for ( var i in b ) { // iterate over all properties
        // get getter and setter functions
        var g = b.__lookupGetter__(i), s = b.__lookupSetter__(i);

        if ( g || s ) { // if there is a getter or setter
            if ( g )
                a.__defineGetter__(i, g); // copy getter to new object
            if ( s )
                a.__defineSetter__(i, s); // copy setter to new object
         } else // if there is not getter nor setter
             a[i] = b[i]; // just copy the value; nothing special
    }
    return a; // return the altered object
}

【讨论】:

  • 嗯,有道理,但是如果我访问 getter,它会调用它,我如何在不这样做的情况下复制它?我开始怀疑我是否应该在javascript中使用getter/setter。在 javascript 本身的眼中,它们就像是在邪恶土地上的朝圣者。
  • @Sneaky Wombat:嗯,使用__lookupGetter__,就像您发布的示例函数一样。它们很棒,但并非在所有地方都受支持,因此您可能更喜欢简单的功能。
  • 我想我正在尝试理解他的建议。关于使用 lookupGetter 等使用 for in w/o 来完成它。这是专门用于 nodejs,所以我并不关心浏览器。 :)
  • @Sneaky Wombat:基本上,__lookupGetter__('someGetter') 返回 getter 函数,就像使用普通函数一样,您可以使用 someFunction 获取函数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-12
  • 2012-02-19
  • 1970-01-01
  • 2010-12-08
  • 1970-01-01
  • 2020-11-04
相关资源
最近更新 更多