JavaScript更好的方法来修改函数原型

时间:2014-02-14 19:41:48

标签: javascript constructor prototype

我希望创建一个构造函数的构造函数。 关于这个帖子:JavaScript build a constructor of constructors,似乎唯一的解决方案是:

Function.prototype.add = function(name, value) {
    this.prototype[name] = value;
};
Function.prototype.remove = function(name) {
    delete this.prototype[name];
};

但我不想修改通用Function原型......还有:

var A = new ConstBuilder().add('test', function() {
    console.log('test');
}).getConstructor();

但我不想在构造函数本身周围有一个对象包装器。

问题是通常构造函数创建新对象,从构造函数原型继承方法。我想要做的是实现函数而不是对象,但是修改函数prototype属性的唯一方法是修改它的__proto__属性:

var constructorPrototype = {
    add : function(name, value) {
        this.prototype[name] = value ;
    }
} ;

var ConstBuilder = function() {
    var constructor = function() {} ;
    constructor.prototype = {} ;
    // The only way (?), but quite deprecated...
    constructor.__proto__ = constructorPrototype ;
    return constructor ;
} ;

// Not working way...
//ConstBuilder.prototype = constructorPrototype ;

var A = new ConstBuilder() ;
A.add('test', function() {
    console.log('test') ;
}) ;

var a = new A() ;
a.test() ; // "test"

constructorPrototype.remove : function() {
    delete this.prototype[name] ;
} ;

A.remove('test') ;

a.test() ; // Error: test is not a function.

注意 A.prototype不是A.__proto__ 但是 A.prototype(new A).__proto__

通过修改__proto__,它完美无缺,真可惜。 我读到Firefox集成了一个“Object.setPrototypeOf()”但它只是实验性的。

这是另一种做我想做的事吗?

1 个答案:

答案 0 :(得分:9)

事实上。执行您希望执行的操作的唯一方法是更改​​要返回的函数的__proto__属性。然而,这不是一件坏事。事实上,ES6 Harmony将把它标准化为Object.setPrototypeOf函数。

但我建议你不要改变对象的[[Prototype]],因为它会使你的程序非常慢。有更快的替代选择:

不要使用原型

传统上,原型用于定义对特定类型的对象进行操作的函数。这些函数专门用于某个参数,称为方法。

例如,obj.func(a, b, c)专注于objobj的实例。另一方面,func(obj, a, b, c)并不专注于任何参数(即obj可以是任何值)。

按照此示例,您可以按如下方式重写addremove

function add(func, name, value) {
    func.prototype[name] = value;
}

function remove(func, name) {
    delete func.prototype[name];
}

现在,您可以对所需的任何功能使用addremove。你根本不必担心遗产。

唯一的问题是命名空间冲突。假设您已经有一个名为add的函数:您做什么?答案很简单。您创建一个新的命名空间:

Function.add = function (func, name, value) {
    func.prototype[name] = value;
};

Function.remove = function remove(func, name) {
    delete func.prototype[name];
};

实际上,这正是本机JavaScript API通常所做的事情。例如:

  1. Object.create
  2. Object.getPrototypeOf
  3. Object.setPrototypeOf
  4. 等等。

    关键在于:泛化总是优于专业化。我们使用原型来专门化。我们使用普通函数进行推广。泛化优于专业化有很多好处:

    1. 您不需要callapply"非专业化" 专业功能等方法。
    2. 您不必担心继承和原型链。
    3. 您的代码更清晰,更易于理解。
    4. 这就是我总是喜欢概括而不是专业化的原因。我使用原型的唯一原因是创建了联合类型。例如:

      function Shape(constructor) {
          this.constructor = constructor;
      }
      
      function Circle(x, y, r) {
          this.x = x;
          this.y = y;
          this.r = r;
      }
      
      function Rectangle(x1, y1, x2, y2) {
          this.x1 = x1;
          this.y1 = y1;
          this.x2 = x2;
          this.y2 = y2;
      }
      
      Circle.prototype = new Shape(Circle);
      Rectangle.prototype = new Shape(Rectangle);
      

      我没有向Circle.prototypeRectangle.prototype添加方法,而是执行以下操作:

      Circle.area = function (circle) {
        return Math.PI * circle.r * circle.r;
      };
      
      Rectangle.area = function (rectangle) {
        return Math.abs((rectangle.x2 - rectangle.x1) * (rectangle.y2 - rectangle.y1));
      };
      
      Shape.prototype.area = function () {
        return this.constructor.area(this);
      };
      

      现在,您可以使用Circle.area(notCircleInstance)代替Circle.prototype.area.call(notCircleInstance)。这是一个双赢的局面。泛化总是优于专业化。