我希望创建一个构造函数的构造函数。 关于这个帖子: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()”但它只是实验性的。
这是另一种做我想做的事吗?
答案 0 :(得分:9)
事实上。执行您希望执行的操作的唯一方法是更改要返回的函数的__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
和apply
等"非专业化" 专业功能等方法。这就是我总是喜欢概括而不是专业化的原因。我使用原型的唯一原因是创建了联合类型。例如:
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.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)
。这是一个双赢的局面。泛化总是优于专业化。