繁体   English   中英

Javascript关闭和“这个”

[英]Javascript closure and “this”

我的问题是......在TestObj中的CallMeLaterTestObj函数中,“this”是窗口对象而不是TestObj。 我如何重构这个,以便在CallMeLater函数中我不必包装调用function() { v.CallMeLaterTestObj(); } function() { v.CallMeLaterTestObj(); }在封闭或使用绑定函数,因为它必须新的浏览器的支持有限。 两个目标:

  • 在对象内的函数调用中保持“this”
  • 为每个单独的对象维护“值”的单独值,使它们不共享相同的值。

    //模拟公共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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM