JavaScript更好的方式来修改函数原型 [英] JavaScript better way to modify function prototype

查看:121
本文介绍了JavaScript更好的方式来修改函数原型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个构造函数的构造函数。
关于此主题: JavaScript构建构造函数的构造函数,似乎唯一的解决方案是:

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

但我不想修改泛型 Function prototype ... and:

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

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



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

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

var ConstBuilder = function(){
var constructor = function(){};
constructor.prototype = {};
//唯一的方法(?),但是已经过时了...
constructor .__ proto__ = constructorPrototype;
return constructor;
};

//不工作方式...
//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(); //错误:测试不是一个函数。

请注意 A.prototype 不是 A .__ proto __ A.prototype $ c>(new A).__ proto __ 。



通过修改 __ proto __ ,什么可耻。
我读到Firefox集成了一个Object.setPrototypeOf(),但它只是实验性的。



这是另一种方式来做我想做的do?

解决方案

确实。做你想做的事情的唯一方法是改变你要返回的函数的 __ proto __ 属性。但这不是一件坏事。事实上,ES6 Harmony会将其标准化为 Object.setPrototypeOf 函数。



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



不使用原型



传统上,原型用于定义对某种类型的对象进行操作的函数。



例如, obj.func(a,b,c)专门用于 obj 以及 obj 的实例。另一方面 func(obj,a,b,c)不专用于任何参数(即 obj 可以重写 add remove



如下所示:

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

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

现在您可以使用 add remove 。你根本不必担心继承。



唯一的问题是命名空间冲突。假设你已经有一个名为 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

等等。



关键是:优于专业化。我们使用原型专业化。我们使用正常函数来推广。有很多泛化优于专业化的优点:


  1. 你不需要像 call 应用unspecialize专用函数。

  2. 担心继承和原型链。

  3. 您的代码更干净,更容易理解。

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

  function Shape(constructor){
this.constructor = constructor;
}

函数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.prototype Rectangle.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)。这是一个双赢的局面。泛化总是比专业化更好。


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

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 Function prototype... and also :

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 :

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.

Note that A.prototype is not A.__proto__ but A.prototype is (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.

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

解决方案

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.setPrototypeOf function.

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:

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 obj and the instances of obj. On the other hand func(obj, a, b, c) doesn't specialize on any argument (i.e. obj can be any value).

Following this example you could rewrite add and remove as follows:

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

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

Now you can use add and remove on any function you want. You don't have to worry about inheritance at all.

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:

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:

  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 call and apply to "unspecialize" specialized functions.
  2. You don't have to worry about inheritance and prototype chains.
  3. Your code is cleaner and easier to understand.

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.prototype and Rectangle.prototype I do the following instead:

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.

这篇关于JavaScript更好的方式来修改函数原型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆