避免在JavaScript原型函数中需要声明“var me = this”的方法

4

目前,我通过声明一个构造函数(普通函数)来在JavaScript中创建对象,然后像下面这样向原型添加方法:

function Test(){
}
Test.prototype.test1 = function(){
    var me = this;
}

然而,我希望避免在每个函数的顶部声明var me = this。下面的方法似乎可行,但似乎非常低效:

$(document).ready(function(){
var n = 0;
(function(){

     function createTest(){
        var me;
        function Test(){
            this.n = n;
            this.testArr = [1, 2, 3, 4];
            n++;
        }

        Test.prototype.test1 = function(){
            me.test2();
        };
        Test.prototype.test2 = function(){
            alert(me.n);
            $.getJSON('test.php', {}, function(reply)
                //want to be able to use 'me' here
                me.newField = reply;
            });
        };

        var t = new Test();
        me = t;
        return t;
    }
    window['createTest'] = createTest;
})();

var t = createTest();
t.test1();
var t2 = createTest();
t2.test1();
t.test1();
});

这段代码输出的结果是预期的,但它看起来真的像它看起来那样低效吗(每次调用createTest()都重新声明Test对象)?

总之,这似乎有点hacky...是否有完全不同的方法来做到这一点更好呢?

编辑:我想要这样做的真正原因是,像test2中的回调函数将引用正确的this


2
你应该阅读这个(请原谅双关语):http://bonsaiden.github.com/JavaScript-Garden/#function.this - Bernhard Hofmann
非常有益的信息。非常感谢!哦,而且你的双关语已经被原谅了 :) - Hersheezy
3个回答

2
你可以将当前的this值绑定到一个函数并将其存储在某个地方。 (为了效率起见。)
if (!Function.prototype.bind) {
    // Most modern browsers will have this built-in but just in case.
    Function.prototype.bind = function (obj) {
        var slice = [].slice,
            args = slice.call(arguments, 1),
            self = this,
            nop = function () { },
            bound = function () {
                return self.apply(this instanceof nop ? this : (obj || {}),
                                    args.concat(slice.call(arguments)));
            };
        nop.prototype = self.prototype;
        bound.prototype = new nop();
        return bound;
    };
}

function Test(n) {
    this.n = n;
    this.callback = (function () {
        alert(this.n);
    }).bind(this)
}

Test.prototype.test1 = function () {
    this.test2();
}

Test.prototype.test2 = function () {
    doSomething(this.callback);
}

function doSomething(callback) {
    callback();
}

var t = new Test(2);
t.test1();

是的,但如果您在任何原型内部有一个回调函数,this将不会引用该对象。对于没有表述清楚,我感到抱歉。请查看编辑。 - Hersheezy
嗯,这是否意味着对于Test对象中的每个函数,我都需要在构造函数中添加this.func = (function(){...}).bind(this)?如果是这样的话,我认为只需在每个Test.prototype函数顶部声明var me = this会更加简洁清晰... - Hersheezy
2
@Hersheezy - 你不一定需要这样做。但如果由我决定,我会将“this”引用保留在需要它的函数中。这样你就可以避免使用工厂样式的对象创建。 - ChaosPandion
我觉得我开始理解你的意思了。我可以看到在原型函数内避免回调执行是一个有用的模式。至于你提到的“避免工厂样式对象创建”,我还需要再考虑一下……对这种模式不是特别熟悉…… - Hersheezy

2
我知道你的问题没有标记为jQuery,但是你在示例中使用了它,因此我的解决方案也利用了jQuery。
有时我会使用$.proxy函数来避免回调上下文。看看这个简单的jsfiddle示例。源代码如下。
function Test(){
    this.bind();
}

Test.prototype.bind = function(){
    $('input').bind('change', $.proxy(this.change, this)); 
    // you could use $.proxy on anonymous functions also (as in your $.getJSON example)
}
Test.prototype.change = function(event){ 
    // currentField must be set from e.target
    // because this is `Test` instance
    console.log(this instanceof Test);          // true
    console.log(event.target == $('input')[0]); // true
    this.currentField = event.target;           // set new field
};

function createTest(){
    return new Test();
}

$(function(){ // ready callback calls test factory
    var t1 = createTest();
});

非常酷!好主意,我应该用jQuery标记...我想我会试着玩一下,看看这个在实践中能满足我的需求有多少。 - Hersheezy

0
大多数情况下,我只需声明一个本地变量来引用此变量,在回调函数中需要引用此变量的任何位置:
function Foo() {
}

Foo.prototype.bar = function() {
  var that=this;
  setTimeout(function() {
     that.something="This goes to the right object";
  }, 5000);
}

或者,您可以像这样使用bind():

Function Foo() {
   this.bar = this.bar.bind(this);
   // ... repeated for each function ...
}

Foo.prototype.bar = function() {
}

这意味着每次创建新的Foo实例时,方法都会绑定到当前实例,因此您可以将它们用作setTimeout()等回调函数。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接