简体   繁体   English

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

[英]JavaScript better way to modify function prototype

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 Function prototype... 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 : 我想要做的是实现函数而不是对象,但是修改函数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.

Note that A.prototype is not A.__proto__ but A.prototype is (new A).__proto__ . 请注意A.prototype不是A.__proto__ 但是 A.prototype(new A).__proto__

And it works perfectly by modifying __proto__ , what a shame. 它通过修改__proto__完美地工作,真是太可惜了。 I read that Firefox has integrated a "Object.setPrototypeOf()" but it is only experimental. 我读过Firefox集成了一个“Object.setPrototypeOf()”但它只是实验性的。

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. 执行您希望执行的操作的唯一方法是更改​​要返回的函数的__proto__属性。 However that is not a bad thing. 然而,这不是一件坏事。 In fact ES6 Harmony is going to standardize it as the Object.setPrototypeOf function. 事实上,ES6 Harmony将把它标准化为Object.setPrototypeOf函数。

I would however advise you against mutating the [[Prototype]] of an object because it makes your program very slow . 但是我会建议你不要改变一个对象的[[Prototype]]因为它会让你的程序变得很慢 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 . 例如, obj.func(a, b, c)擅长于obj和的实例obj On the other hand func(obj, a, b, c) doesn't specialize on any argument (ie obj can be any value). 另一方面, func(obj, a, b, c)并不专注于任何参数(即obj可以是任何值)。

Following this example you could rewrite add and remove as follows: 按照此示例,您可以重写addremove ,如下所示:

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. 现在,您可以在所需的任何功能上使用addremove功能。 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? 假设你已经有一个名为add的函数:你做什么? 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. 实际上,这正是本机JavaScript API通常所做的事情。 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. 您不需要像call这样的方法并apply “unspecialize”专用函数。
  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.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) . 现在您可以使用Circle.area(notCircleInstance)而不是Circle.prototype.area.call(notCircleInstance) It's a win-win situation. 这是一个双赢的局面。 Generalization is always better than specialization. 泛化总是比专业化更好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM