[英]Javascript closure and “this”
我的问题是......在TestObj中的CallMeLaterTestObj函数中,“this”是窗口对象而不是TestObj。 我如何重构这个,以便在CallMeLater函数中我不必包装调用function() { v.CallMeLaterTestObj(); }
function() { v.CallMeLaterTestObj(); }
在封闭或使用绑定函数,因为它必须新的浏览器的支持有限。 两个目标:
为每个单独的对象维护“值”的单独值,使它们不共享相同的值。
//模拟公共API,私有方法,私有变量,公共字段。
//问题的新部分
重写以包括绑定功能和原型表示法。 如何将Binding函数移动到所有新对象将获得的基础对象?
这是我可以接近使用这两个世界中最好的。 我不知道这种方法的缺陷是什么
var BaseObject = function () { _getBinding = function (method) { var _self = this; return function () { _self[method].apply(_self, arguments); }; }; return { CallInline: _getBinding } }(); var TestObj = function (value) { $.extend(this, BaseObject); // public var this._value = value; }; TestObj.prototype = function() { var privateVar = false; // these are private _giveMe = function () { return this._value; }, _callMeLaterTestObj = function () { console.log('I am ' + this.constructor.name + ' my value is ' + this._value); }; // public API return { GiveMe : _giveMe, CallMeLaterTestObj : _callMeLaterTestObj } }(); function CallMeLater(v, i) { setTimeout(v.CallInline('CallMeLaterTestObj'), 10); } var V1 = new TestObj(1); var V2 = new TestObj(2); var V3 = new TestObj(3); console.log('V1= ' + V1.GiveMe()); console.log('V2= ' + V2.GiveMe()); console.log('V3= ' + V3.GiveMe()); console.log('---'); V1.CallMeLaterTestObj(); console.log('---');
不,你不能。 这只是做到这一点的方式。 顺便说一句,你可以轻松地填充bind
方法,以便它也可以在旧版浏览器中使用。
另一种方法是将闭包移动到原型方法中,如果你知道你总是需要绑定实际的函数:
TestObj.prototype.getCallMeLaterTestObj = function () {
var that = this;
return function() {
console.log('I am ' + that.constructor.name + ' my value is ' + that._value);
};
};
setTimeout(v.getCallMeLaterTestObj(), 10);
顺便说一句,您的原型没有constructor
属性,因此日志将无法按预期工作。
您唯一的机会是完全避免使用this
关键字 :
TestObj = function() {
var privateVar = false; // these are private static
function TestObj(value) {
function giveMe() {
return value;
}
function callMeLaterTestObj() {
console.log('I am TestObj my value is ' + giveMe());
}
this._value = value;
this.giveMe = giveMe;
this.callMeLaterTestObj = callMeLaterTestObj;
/* you could do this as well:
return {
_value: value,
giveMe: giveMe,
callMeLaterTestObj: callMeLaterTestObj
}; */
}
return TestObj;
})();
var v = new TestObj;
setTimeout(v.callMeLater, 10);
但这并不是非常有效的内存,因为它根本不使用原型继承。
我想你要找的是这个:
function TestObj(value) {
var _value = value;
this.giveMe = function() {
return _value;
};
this.callMeLaterTestObj = function() {
console.log('I am ' + this.constructor.name + ' my value is ' + _value);
};
return this;
};
function callMeLater(v, i) {
setTimeout(function() {
v.callMeLaterTestObj();
}, 10);
}
var v1 = new TestObj(1);
var v2 = new TestObj(2);
var v3 = new TestObj(3);
console.log('V1= ' + v1.giveMe());
console.log('V2= ' + v2.giveMe());
console.log('V3= ' + v3.giveMe());
console.log('---');
callMeLater(v1, 1);
callMeLater(v2, 2);
callMeLater(v3, 3);
要访问constructor.name,您需要使用function name()
语法声明该函数,而不是使用var name = function()
语法。
要保留私有变量并公开公共API,请在函数中将公共变量公开this
属性。
一定要回到this
从构造函数,使其工作。
遵循CamelCase的命名约定(其中TestObj是一个)和lowerCamelCase的变量/方法/对象/等也是一个好习惯。有助于清楚地了解哪些变量是实例,哪些是类。
注意
关于在setTimeout的闭包中包装v.callMeLaterTestObj()
,这种技术完全是跨浏览器兼容的。 你不会有任何问题。
bind
方法比较新,虽然有许多库可以在旧版浏览器中为您填充。 我个人最喜欢的是下划线 。
笔记2
你不能在setTimeout中的对象上调用一个方法而不将它包装在某个闭包中,但是如果你愿意,你可以在不使用泛型bind
函数(由Underscore或jQuery和其他人提供)的情况下抽象类中的闭包。可以像这样在类中“滚动你自己”:
function TestObj(value) {
var _value = value;
var _self = this;
this.giveMe = function() {
return _value;
};
this.callMeLaterTestObj = function() {
console.log('I am ' + this.constructor.name + ' my value is ' + _value);
};
this.getBinding = function(method) {
var _self = this;
return function() {
_self[method].apply(_self, arguments);
};
};
return this;
};
function callMeLater(v, i) {
setTimeout(v.getBinding('callMeLaterTestObj'), 10);
}
var v1 = new TestObj(1);
var v2 = new TestObj(2);
var v3 = new TestObj(3);
console.log('V1= ' + v1.giveMe());
console.log('V2= ' + v2.giveMe());
console.log('V3= ' + v3.giveMe());
console.log('---');
callMeLater(v1, 1);
callMeLater(v2, 2);
callMeLater(v3, 3);
说明:
你需要,因为,当您通过方法来使用某种结合setTimeout
,通过引用传递它。 所以所有setTimeout看到的都是一个函数 - 而不是它所在的对象,这就是你失去了它的上下文的this
。
因为setTimeout因此将在默认范围内执行函数 - 即浏览器窗口 - 您需要一种方法通过引用,通过内联匿名函数或通过返回使用apply
方法“重置”的闭包来获取this
函数this
。
注3
如果你想拥有自己的bind方法,而不是包含一个为你提供它的库或者将它包含在每个类中,那么你可以使用Underscore中的这个,它遵循新浏览器中的本机方法:
function bind(func, context) {
var bound, args;
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
if (!_.isFunction(func)) throw new TypeError;
args = slice.call(arguments, 2);
return bound = function() {
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
ctor.prototype = func.prototype;
var self = new ctor;
var result = func.apply(self, args.concat(slice.call(arguments)));
if (Object(result) === result) return result;
return self;
};
};
然后像这样使用它:
function callMeLater(v, i) {
setTimeout(bind(v.callMeLaterTestObj, v), 10);
}
这适用于所有浏览器。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.