Javascript关闭和“这个”

时间:2012-11-29 15:30:26

标签: javascript

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

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

    //模拟公共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('---');
    

2 个答案:

答案 0 :(得分:1)

不,你不能。这只是做到这一点的方式。顺便说一句,您可以轻松shim the bind method,以便它也可以在旧版浏览器中使用。

另一种方法是将闭包移动到原型方法中,如果你知道你总是需要绑定实际的函数:

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 keyword

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);

但这不是很节省内存,因为它根本不使用原型继承。

答案 1 :(得分:1)

我认为您正在寻找的是:

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的变量/方法/对象/等也是一个好习惯。有助于清楚哪些变量是实例,哪些是类

Test and see the console output expected here

注意

关于在setTimeout的闭包中包装v.callMeLaterTestObj(),这种技术完全兼容跨浏览器。你不会有任何问题。

bind方法较新,尽管有许多库会在旧版浏览器中为您提供内容。我个人最喜欢的是underscore

注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因此会在默认范围内执行函数 - 即浏览器窗口 - 你需要一种方法通过引用,通过内联匿名函数或通过返回一个使用的闭包来返回this要{重置'apply的{​​{1}}方法。

注3

如果您想拥有自己的绑定方法,并且不包含为您提供它的库或将其包含在每个类中,那么您可以使用Underscore中的这个,这与新浏览器中的本机方法一致:

this

然后像这样使用它:

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;
  };
};

这适用于所有浏览器。