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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21788187/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 21:41:54  来源:igfitidea点击:

JavaScript better way to modify function prototype

javascriptconstructorprototype

提问by Tot

I wish to create a constructor of constructors. Relating to this thread : JavaScript build a constructor of constructors, it seems the only solutions are :

我希望创建一个构造函数的构造函数。与此线程相关:JavaScript 构建构造函数的构造函数,似乎唯一的解决方案是:

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

But I don't want to modify the generic Functionprototype... and also :

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

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

But I don't want to have an object wrapper around the constructor itself.

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

The problem is that generally constructors creates new objects, inheriting methods from the constructor prototype. What I'm trying to do is to instanciates functions instead of objects, but the only way to modify a function prototype property is this to modify its __proto__property :

问题是通常构造函数创建新对象,从构造函数原型继承方法。我想要做的是实例化函数而不是对象,但修改函数原型属性的唯一方法是修改其__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.

Notethat A.prototypeis not A.__proto__butA.prototypeis (new A).__proto__.

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

And it works perfectly by modifying __proto__, what a shame. I read that Firefox has integrated a "Object.setPrototypeOf()" but it is only experimental.

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

Would it be another way to do what I wish to do ?

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

回答by Aadit M Shah

Indeed. The only way to do what you wish to do is to mutate the __proto__property of the function you are returning. However that is not a bad thing. In fact ES6 Harmony is going to standardize it as the Object.setPrototypeOffunction.

的确。做你想做的事情的唯一方法是改变__proto__你要返回的函数的属性。不过这也不是坏事。实际上 ES6 Harmony 将其标准化为Object.setPrototypeOf功能。

I would however advise you against mutating the [[Prototype]]of an object because it makes your program very slow. There is a faster alternative available:

但是,我建议您不要改变[[Prototype]]对象的 ,因为它会使您的程序非常慢。有一个更快的替代方案可用:

Don't Use the Prototype

不要使用原型

Traditionally the prototype is used to define functions that operate on a certain type of object. These functions, which specialize on a certain argument, are called methods.

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

For example, obj.func(a, b, c)specializes on objand the instances of obj. On the other hand func(obj, a, b, c)doesn't specialize on any argument (i.e. objcan be any value).

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

Following this example you could rewrite addand removeas follows:

在此之后例如,你可以重写addremove如下:

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

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

Now you can use addand removeon any function you want. You don't have to worry about inheritance at all.

现在您可以使用addremove任何您想要的功能。您根本不必担心继承问题。

The only problem is namespace conflicts. Suppose you already have a function named add: what do you do? The answer is pretty simple. You create a new namespace:

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

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

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

In fact this is exactly what native JavaScript APIs usually do. For example:

事实上,这正是原生 JavaScript API 通常所做的。例如:

  1. Object.create
  2. Object.getPrototypeOf
  3. Object.setPrototypeOf
  1. Object.create
  2. Object.getPrototypeOf
  3. Object.setPrototypeOf

So on and so forth.

等等等等。

The point is this: generalization is always better than specialization.We use prototypes to specialize. We use normal functions to generalize. There are a lot of advantages of generalization over specialization:

重点是:泛化总是比专业化好。我们使用原型来专业化。我们使用普通函数来概括。泛化相对于专业化有很多优点:

  1. You don't need methods like calland applyto "unspecialize"specialized functions.
  2. You don't have to worry about inheritance and prototype chains.
  3. Your code is cleaner and easier to understand.
  1. 你不需要像call和那样的方法apply“非专业化”专门的功能。
  2. 您不必担心继承和原型链。
  3. 您的代码更清晰、更易于理解。

This is the reason I always prefer generalization over specialization. The only reason I ever use prototypes is to created union types. For example:

这就是我总是喜欢泛化而不是专业化的原因。我使用原型的唯一原因是创建联合类型。例如:

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

Instead of adding methods to Circle.prototypeand Rectangle.prototypeI do the following instead:

取而代之的添加方法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);
};

Now you can use Circle.area(notCircleInstance)instead of Circle.prototype.area.call(notCircleInstance). It's a win-win situation. Generalization is always better than specialization.

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