在OO Javascript constructor pattern: neo-classical vs prototypal中,我了解到使用原型继承的构造函数可以比使用带有闭包的所谓neo-classical
模式的构造函数快10倍(或更多),正如Crockford在他的“好的部分”中提出的那样书和演示文稿。
由于这个原因,似乎更喜欢原型继承似乎是正确的事情。
问题有没有办法将原型继承与模块模式结合起来,以便在必要时允许私有变量?
我在想的是:
// makeClass method - By John Resig (MIT Licensed)
function makeClass(){
return function(args){
if ( this instanceof arguments.callee ) {
if ( typeof this.init == "function" )
this.init.apply( this, args.callee ? args : arguments );
} else
return new arguments.callee( arguments );
};
}
// =======================================================
var User = makeClass();
// convention; define an init method and attach to the prototype
User.prototype.init = function(first, last){
this.name = first + " " + last;
};
User.prototype.doWork = function (a,b,c) {/* ... */ };
User.prototype.method2= (function (a,b,c) {
// this code is run once per class
return function(a,b,c) {
// this code gets run with each call into the method
var _v2 = 0;
function inc() {
_v2++;
}
var dummy = function(a,b,c) {
/* ... */
inc();
WScript.echo("doOtherWork(" + this.name + ") v2= " + _v2);
return _v2;
};
var x = dummy(a,b,c);
this.method2 = dummy; // replace self
return x;
};
})();
这不太对。但它说明了这一点。
有没有办法做到这一点,是否值得?
答案 0 :(得分:5)
更喜欢原型继承似乎是正确的事情,一般来说
嗯......当然,这是用JavaScript做的更自然,本土感觉的事情。但是这么多JavaScript确实是错误的,这不一定是一种恭维!
当然,当性能不是问题时,获取每个方法自己的绑定副本的对象比共享其方法的对象更容易处理,因为您可以直接传递对object.method
的引用而不必创建一个closure-delegate或function.bind。
有没有办法将原型继承与模块模式结合起来,以便在必要时允许私有变量?
你想从私人变量得到什么?如果它通过封装是一种Java风格的安全性概念,我会放弃它并且只是以Python方式执行:在成员名称的开头添加下划线,并且任何想要从外部使用的人都将被适当地警告它没有支持,可能搞砸了。在同一页面上执行的JavaScript代码中绝不存在安全边界,这样可以保证您的私有真正私有。
如果你想要的是避免在调用方法时找到this
的正确副本,你可以在初始化器中手动绑定方法方法:
var Thing= makeClass();
Thing.prototype.init= function(a) {
this._a= a;
this.showA= this.showA.bind(this);
};
Thing.prototype.showA= function() {
alert(this._a);
};
thing= new Thing(3);
setTimeout(thing.showA, 1000); // will work as `thing` has its own bound copy of `showA`
(function.bind是future-JavaScript,你现在可以入侵Function.prototype,直到浏览器支持它。)
这自然会失去基于原型的对象的一些轻量级特性,但至少你仍然可以让它们共享不是方法的成员,以及永远不会被用作代理的方法,只要它很清楚你可以随时记住哪种方法可以用这种方法。
如果你只是想能够输入一个私有变量名而不必一直放this.
那么,是的,你必须用一个闭包来做。你的榜样世界可能会从初始化者那里得到一点清晰,而不是使用第一次自我写作:
var User= makeClass();
User.prototype.init= function(first, last){
this.name= first+' '+last;
this.method2= this._method2factory();
};
User.prototype._method2factory= function() {
var _v2= 0;
function inc() {
_v2++;
}
return function method2(a,b,c) {
/* ... */
inc();
WScript.echo('doOtherWork('+this.name+') v2= '+_v2);
return _v2;
};
};
但是,与仅撰写this._v2
和this._inc()
相比,我并不确定这会让您受益匪浅。
答案 1 :(得分:1)
我不完全确定我理解你的问题。但是从我认为我理解的方面出发......
function Foo () { /*constructor*/
var counter = 0; /* private variable */
this.incrementCounter=function () { /*privileged function */
counter++
}
this.getCounter=function () { /*privileged function */
return counter;
}
}
/*public functions. Note: this pattern destroys constructor property.
Lesson: Don't depend on the constructor property! */
Foo.prototype = {
toString: function () {
var string = "";
var count = this.getCounter();
while(count--) {
string+="*"
}
return string;
}
}
var bar = new Foo();
bar.incrementCounter();
bar.incrementCounter();
bar.toString(); /* in theory, this returns "**".. haven't tested code */
答案 2 :(得分:0)
你可以看看 https://github.com/riga/jclass
我认为这就是你要找的东西。
答案 3 :(得分:0)
就个人而言,我更喜欢以下语法:
var keyValueStore = (function() {
// Singleton private properties
var count = 0;
var kvs = function() {
// Instance private / privileged properties
count++;
};
kvs.prototype = {
// Instance public properties
'data' : {},
'get' : function(key) { return this.data[key]; },
'set' : function(key, value) { this.data[key] = value; },
'delete' : function(key) { delete this.data[key]; },
'getLength' : function() {
var l = 0;
for (p in this.data) l++;
return l;
}
};
return {
// Singleton public properties
'create' : function() { return new kvs(); },
'count' : function() { return count; }
};
})();
使用这种语法,您可以使用单个对象,可以使用原型继承创建实例,并可以在多个级别定义私有属性。
你这样使用它:
kvs = keyValueStore.create();
kvs.set('Tom', "Baker");
kvs.set('Daisy', "Hostess");
var profession_of_daisy = kvs.get('Daisy');
kvs.delete('Daisy');
console.log(keyValueStore.count());