如何让JavaScript的Test类每个实例拥有独立的self闭包变量?
问题分析
你遇到的核心问题是:闭包中的self是整个IIFE作用域下的共享变量,所有Test实例都会修改同一个self引用——第二个实例创建时,self = this会把变量指向b,导致所有原型方法都使用这个最新的self,自然结果不符合预期。
而且你提到不想在每个方法里重复声明局部self,因为异步回调需要固定上下文,那我们可以让每个实例拥有自己独立的self,而不是依赖共享的闭包变量。
解决方案一:将self设为实例属性
把self绑定到当前实例的属性上,这样每个实例都会拥有自己的self,不会互相覆盖:
(function (w) { w.Test = function (name) { // 将self设为当前实例的专属属性,每个实例独立持有 this.self = this; this.self.name = name; }; w.Test.prototype.getName = function () { // 通过this访问当前实例的self return this.self.name; } w.Test.prototype.test = function () { console.debug(this.self.name); console.debug(this.self == this); } })(window); var a = new Test("a"); var b = new Test("b"); console.log(a.getName() + b.getName()); // 输出:ab a.test(); // 输出:a > true b.test(); // 输出:b > true
解决方案二:用ES6 Class语法(更优雅)
如果你的环境支持ES6+,用Class语法会更清晰,构造函数里的this天然属于每个实例,直接绑定self即可:
class Test { constructor(name) { this.self = this; this.name = name; // 异步回调场景:用箭头函数定义实例方法,自动绑定上下文 this.asyncCallback = () => { console.log("异步回调中的name:", this.self.name); }; } getName() { return this.self.name; } test() { console.debug(this.self.name); console.debug(this.self === this); } } const a = new Test("a"); const b = new Test("b"); console.log(a.getName() + b.getName()); // ab a.test(); // a true b.test(); // b true setTimeout(a.asyncCallback, 100); // 输出:异步回调中的name: a
解决方案三:绑定方法上下文(无需self)
如果不想额外维护self,可以在构造函数中把所有原型方法的上下文绑定到当前实例,这样即使在异步回调中调用方法,this也始终指向实例:
(function (w) { w.Test = function (name) { this.name = name; // 把原型方法的上下文绑定到当前实例 this.getName = this.getName.bind(this); this.test = this.test.bind(this); }; w.Test.prototype.getName = function () { return this.name; } w.Test.prototype.test = function () { console.debug(this.name); console.debug(this === this); // 必然为true } })(window);
关于你的疑问:能否重新声明闭包变量?
答案是不行。闭包中的self属于整个IIFE作用域,你无法在构造函数里重新声明一个同名变量让原型方法访问到——构造函数内部声明的let self = this是局部变量,原型方法不在这个作用域内,依然会访问闭包的共享self。所以必须让self属于每个实例,而不是共享的闭包变量。
内容的提问来源于stack exchange,提问作者Yurii Semeniuk
相关产品推荐
相关产品推荐

