javascript - JavaScript 闭包变量在实例之间共享
问题描述
这是 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
变量。我怎样才能得到预期的结果?我知道常见的解决方案是self
在每个测试的方法中使用本地方法,但是有没有其他方法可以在没有这种重复的情况下做到这一点?有没有办法重新声明闭包变量?
我想使用self
,而不是this
因为我的大多数方法都有一些带有回调的异步函数调用,它有自己的上下文,并且在每个方法中我都需要单独的self
变量。
解决方案
在 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 几乎为零,我们创建了一个对象系统(几乎只是因为我们返回一个对象字面量,但如果 js 像 Perl 或 PHP 一样具有真正的映射/哈希/关联数组,我们可以用零 OOP 来实现)。
TLDR
那么我们怎样才能得到你想要的呢?好吧,我们可以在 js 中使用一种称为模块模式的设计模式(不要与 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
. 对于大多数人来说,这是一个很好的权衡,因为大多数人认为需要了解其自身类型的代码是一种代码气味。但是图书馆经常需要做这样的事情。
TLDR 2
我们可以将模块模式与构造函数结合起来,几乎是两全其美。我们可以这样做:
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
它不像使用原型东西那样内存效率高(这也适用于模块模式)。但有些人会认为这是一个很好的权衡。
TLDR 3
您实际上根本不需要别名this
。您需要做的就是了解它是如何工作的并与之共存(阅读我对另一个问题的回答:Javascript 中的“this”关键字如何在对象文字中起作用?,我保证只要答案保持更新我还活着)。
删除所有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));
推荐阅读
- ios - 调用“停止”功能时,如何逐渐减慢计时器的速度?
- python - Django rest框架不正确的嵌套序列化
- marklogic - 不显示在 MarkLogic 上创建的集合以在 Geoserver 中创建图层
- reactjs - 如何在通用 React / React Native 项目中用玩笑收集覆盖率
- python - 如何查看 Kaggle 笔记本的内存使用情况?
- javascript - 如何使具有不同单词的多个数组在游戏屏幕上移动?
- amazon-web-services - 带有光线的 AWS SageMaker RL:ray.tune.error.TuneError:未指定可训练
- sql - 如何将 PARAMETERS 传递给 MS ACCESS 中的另一个查询(没有 VBA)?
- python - 相当具体的数据框查询
- sql - 如何从 SQL SERVER 中获得三班倒的总小时数?