0

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

  • 在对象内的函数调用中保留“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('---');
    
4

2 回答 2

1

不,你不能。这就是这样做的方法。顺便说一句,您可以轻松地填充该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);

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

于 2012-11-29T16:16:58.200 回答
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从构造函数返回以使其工作。

对类名(TestObj 是其中之一)遵循 CamelCase 的命名约定和对变量/方法/对象/等遵循 lowerCamelCase 的命名约定也是一种很好的做法。有助于清楚哪些变量是实例,哪些是类。

测试并查看此处预期的控制台输出

笔记

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

bind方法较新,尽管有许多库会在较旧的浏览器中为您填充该方法。我个人最喜欢的是下划线

笔记2

您不能在 setTimeout 中调用对象上的方法而不将其包装在某个地方的闭包中,但是如果您愿意,您可以在不使用泛型bind函数(如 Underscore 或 jQuery 等提供)的情况下抽象 Class 中的闭包,您可以像这样在类中“滚动你自己的”:

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方法的闭包来 'reset' this

注3

如果您想拥有自己的绑定方法,并且不包含为您提供它或将其包含在每个类中的库,那么您可以使用 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);    
}

这适用于所有浏览器。

于 2012-11-29T16:36:54.120 回答