这是JavaScript代码(或JSFiddle)。我想强制每个测试的实例使用其自己的self
变量引用。我该如何实现?
(function (w) {
var self;
w.Test = function (name) {
// how to redeclare `self` here to not use it by reference? This doesn't help:
// self = undefined;
// delete self;
self = this;
self.name = name;
};
w.Test.prototype.getName = function () {
return self.name;
}
w.Test.prototype.test = function () {
console.debug(self.name);
console.debug(self == this);
}
})(window);
var a = new Test("a");
var b = new Test("b");
console.log(a.getName() + b.getName());
// expected: ab
// actual: bb
a.test();
b.test();
// expected: a > true > b > true
// actual: b > false > b > true
第二个调用将覆盖self
变量。如何获得预期的结果?我知道常见的解决方案是在每个Test方法中使用本地self
方法,但是有没有其他方法可以做到这一点呢?有什么办法可以重新声明闭包变量吗?
我想使用self
而不是this
,因为我的大多数方法都有一些异步函数调用,这些函数带有自己的上下文回调,因此在每种方法中我都需要单独的self
变量
答案 0 :(得分:2)
在javascript中,闭包是全局变量概念的概括-它是在多个作用域中可见的变量。实际上,全局变量可以看作是闭包本身-在全局范围内。调用全局变量闭包没有矛盾或混乱(尽管某些引擎可能以不同的方式实现它们,实际上它们可能使用完全相同的机制来实现)。
说明:
var a;
var b;
(function () {
var shared; // a shared variable behaving like a sub-global variable
a = function (x) {shared = x}
b = function () {return shared}
})()
function c {return shared};
a(100);
b(); // returns 100
c(); // errors out because it is not part of the shared scope.
函数创建作用域的实例,不幸的是,实例的技术名称称为闭包,因为闭包一词还指用于创建此类事物的基础算法,非正式地表示闭包捕获的变量(技术名称)因为它是封闭变量,但人们通常只说“封闭”)。另一方面,对于每个概念,OOP都有完全不同的词-类,实例,实例化,属性和方法。
由于函数创建了作用域(关闭)实例,因此可以通过多次调用该函数来拥有多个作用域实例:
function makeShared () {
var shared // this thing will have more than one copy/instance in RAM
return {
a: function (x) {shared = x},
b: function () {return shared}
}
}
var x = makeShared();
var y = makeShared();
x.a(100);
y.a(200);
x.b(); // returns 100
y.b(); // returns 200
如您所见,理论上闭包和对象在概念上相似。实际上,那里有一篇论文宣称它们是完全一样的东西。在OOP几乎为零的情况下,我们创建了一个对象系统( almost 只是因为我们返回了一个对象文字,但是如果js像Perl或PHP那样具有真实的maps / hashhes / associative数组,那么我们可以使用零OOP来实现)
那么我们如何得到您想要的?好吧,我们可以在js中使用称为 module pattern 的设计模式(不要与js模块混淆)。实际上,这就是我上面说明的makeShared
代码-我们放弃了js中的OOP功能,转而使用功能编程来发明自己的OOP系统。
您的代码在模块模式中将如下所示:
function newTest (name) { // in case you like the word "new"
var self = {};
self.name = name;
self.getName = function () {
return self.name;
}
self.test = function () {
console.debug(self.name);
console.debug(self == this);
}
return self;
};
var a = newTest("a"); // note: newTest instead of new Test
var b = newTest("b");
console.log(a.getName() + b.getName());
在javascript复兴的时代,人们开始认真地将其作为一种编程语言(距创建之初约10年),模块模式已成为该语言爱好者的最爱。与原型的东西不同,它看起来像一个类,整个定义包含在一对大括号{}
中。您甚至可以滚动自己的继承系统(使用从寄生继承到原型克隆的方法)。但这破坏了instanceof
的功能。对于大多数人来说,这是一个很好的权衡,因为大多数人认为需要意识到自身类型的代码是一种代码味道。但是图书馆经常需要做这样的事情。
我们可以将模块模式与构造函数结合起来,从而获得两全其美的效果。我们可以改为:
function Test (name) {
var self = this;
self.name = name;
self.getName = function () {
return self.name;
}
self.test = function () {
console.debug(self.name);
console.debug(self == this);
}
// constructors don't need to return anything
};
var a = new Test("a"); // note: new
var b = new Test("b");
console.log(a.getName() + b.getName());
这种编程风格的主要异议是您现在拥有getName
和test
的多个实例,因此它的内存效率不如使用原型的东西(这也适用于模块模式) )。但是有些人会认为这是一个很好的权衡。
您实际上根本不需要别名this
。您需要做的就是了解它的工作原理以及如何使用它(请阅读我对另一个问题的回答:How does the "this" keyword in Javascript act within an object literal?,我保证只要我还活着,就一直在更新答案)。
删除所有self
的内容,仅使用this
。对于异步回调,您只需要知道如何正确调用方法即可:
var a = new Test("a");
// either:
someAsyncThing(function() {
a.getName();
});
// or:
anotherAsyncThing(a.getName.bind(a));
// with promises:
promiseSomething.then(a.getName.bind(a));