使用extend将对象添加到原型中

Using extend to add an object to a prototype

本文关键字:原型 添加 对象 extend 使用      更新时间:2023-09-26

当我尝试将对象BaseNet添加到IPv4Address.prototype时,我得到一个错误。错误:

TypeError: Cannot read property 'ipInt' of undefined

就是没有意义。当我将对象复制到原型时,它的行为就像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方法,另一个使用评论中指出的ECMA规范中的defineProperties方法。一个比另一个好吗?似乎"mixin"风格更适合我所寻找的,但对象。Create way也可以。

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

它不工作,因为同时扩展,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 " unchanged "。


至于你的编辑:那些__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
}