您可以使用 metaconstructor* 模式来解决这个问题。
function defineCtor(metaCtor) {
var proto = new metaCtor();
var ctor = proto.hasOwnProperty('constructor') ?
proto.constructor : new Function();
return (ctor.prototype = proto).constructor = ctor;
}
现在您有了一个构造构造函数(或更准确地说构造原型并返回构造函数)的函数。
var Widget = defineCtor(function() {
function doInternalStuff() {
// ...cant see me
}
// this function ends up on the prototype
this.getFoo = function() { return doInternalStuff(); };
});
// ...
var myWidget = new Widget();
解释
defineCtor
将单个匿名函数作为属性。它使用 调用函数new
,创建一个对象。它将对象分配为新构造函数的原型属性(空函数或生成的原型对象自己的constructor
属性),并返回该函数。
这为您的内部函数提供了一个闭包,解决了您的问题 1,并为您设置了构造函数/原型对,解决了问题 2。
比较
将该defineCtor
技术与以下两个示例进行比较。
本例使用原型,存在问题1:内部的东西没有封装。
function Widget(options) {
this.options = options;
}
Widget.prototype = {
getFoo: function() {
return doInternalStuff();
}
};
// How to encapsulate this?
function doInternalStuff() { /* ... */ }
此示例在构造函数中设置所有内容,并且存在问题 2:每次构造对象时,都会为每个属性实例化新的函数对象。
function Widget(options) {
this.options = options;
function doInternalStuff() { /* ... */ }
this.getFoo = function() {
return doInternalStuff();
};
}
此示例使用上述技术提供封装,同时仍利用原型:
var Widget = defineCtor(function() {
// ^
// This function runs once, constructing the prototype.
// In here, `this` refers to the prototype.
// The real constructor.
this.constructor = function(options) {
// In function properties, `this` is an object instance
// with the outer `this` in its prototype chain.
this.options = options;
};
function doInternalStuff() { /* ... */ }
this.getFoo = function() { return doInternalStuff(); };
});
// ...
var myWidget = new Widget();
这种方法有一些好处,其中一些好处比其他方法更明显。
它提供封装。您可以通过将第一个“比较”示例包装在立即调用的函数中来做到这一点,但这种方法在团队设置中可能更清晰,更容易“实施”。
它是可扩展的。metaCtor
您可以为您的“元构造函数”函数提供自己的原型,具有“扩展”、“混合”等函数属性。然后,在this.extends(BaseWidget)
. defineCtor
API 永远不需要改变来实现这些。
它“欺骗”了 Google Closure Compiler、Eclipse、jsdoc 等,让您认为您正在定义实际的构造函数而不是“元函数”。这在某些情况下可能很有用(代码以这些工具理解的方式“自我记录”)。
* 据我所知,“元构造函数”这个词完全是虚构的。