JavaScript更好的方式来修改函数原型 [英] JavaScript better way to modify function prototype
问题描述
我想创建一个构造函数的构造函数。
关于此主题: 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通常做的。例如:
-
Object.create
-
Object.getPrototypeOf
-
Object.setPrototypeOf
等等。
关键是:优于专业化。我们使用原型专业化。我们使用正常函数来推广。有很多泛化优于专业化的优点:
- 你不需要像
call
和应用
到unspecialize专用函数。 - 担心继承和原型链。
- 您的代码更干净,更容易理解。
这是我总是喜欢泛化而不是专业化的原因。我使用原型的唯一原因是创建联合类型。例如:
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:
Object.create
Object.getPrototypeOf
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:
- You don't need methods like
call
andapply
to "unspecialize" specialized functions. - You don't have to worry about inheritance and prototype chains.
- 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屋!