关于javascript:JS对象this.method()通过jQuery中断

JS Object this.method() breaks via jQuery

我肯定有一个简单的答案,但现在是星期五下午,我累了。:(

不知道该如何解释,所以我将继续并发布示例代码…

< BR>下面是一个简单的对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var Bob =

{ Stuff : ''

, init : function()
    {
        this.Stuff = arguments[0]
    }

, doSomething : function()
    {
        console.log( this.Stuff );
    }

}

它的用途是:

1
2
3
4
5
6
7
8
9
10
11
12
$j = jQuery.noConflict();
$j(document).ready( init );


function init()
{
    Bob.init('hello');

    Bob.doSomething();

    $j('#MyButton').click( Bob.doSomething );
}

除了最后一行,一切都正常。当jquery调用dosomething方法时,它将重写"this"并停止它的工作。

仅仅使用Stuff也不起作用。

那么,如何以允许jquery调用对象的方式引用对象自己的属性,以及允许对象使用调用的jquery对象?

也就是说,我希望能够这样做:

1
2
3
4
doSomething : function()
    {
        console.log( <CurrentObject>.Stuff + $j(<CallerElement>).attr('id') );
    }

(其中替换为适当的名称。)


这不是jquery的错误,它是javascript处理对象方式的一个组成部分。

与大多数其他面向对象语言不同,"this"在javascript中不绑定在每个方法级别上;相反,它完全由函数的调用方式决定:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Bob= {
    toString: function() { return 'Bob!'; },

    foo: function() { alert(this); }
};
Brian= {
    toString: function() { return 'Brian!'; },
};

Bob.foo(); // Bob!
Bob['foo'](); // Bob!

Brian.foo= Bob.foo;
Brian.foo(); // Brian! NOT Bob

var fn= Bob.foo;
fn(); // window NOT Bob

您在以下情况下所做的工作:

1
$j('#MyButton').click( Bob.doSomething );

就像上一个使用fn的例子:您将函数doSomething从bob中拉出,并将其作为纯函数传递给jquery的事件处理程序setter:它不再与bob或任何其他对象有任何连接,因此javascript将传入全局window对象。(这是javascript最糟糕的设计特性之一,因为您可能不会立即注意到window不是Bob,并开始访问它的属性,从而导致奇怪和混乱的交互和错误。)

要记住Bob,通常需要在Nickyt的答案中创建一个函数,将"Bob"的副本保存在一个闭包中,以便在回调时记住它,并用于调用real方法。然而,在ECMAScript第五版中,现在有了一种标准化的方法:

1
$j('#MyButton').click( Bob.doSomething.bind(Bob) );

(您也可以在bind调用中添加额外的参数,以便调用doSomething返回,这很方便。)

对于本机还不支持第五版bind()方法的浏览器(目前大多数浏览器都是这种方法),您可以黑客自己实现bind(原型库也这样做),比如:

1
2
3
4
5
6
7
8
9
10
11
12
if (!Object.bind) {
    Function.prototype.bind= function(owner) {
        var that= this;
        var args= Array.prototype.slice.call(arguments, 1);
        return function() {
            return that.apply(owner,
                args.length===0? arguments : arguments.length===0? args :
                args.concat(Array.prototype.slice.call(arguments, 0))
            );
        };
    };
}


变化

1
$('#MyButton').click( Bob.doSomething );

1
$('#MyButton').click( function() { Bob.doSomething() } );

如果您真的想避免匿名函数,还可以在Bob对象中添加一个私有字段var that = this,并在成员中的任何位置使用该字段而不是this

例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var Bob = new function() {
    // Private Fields
    var that = this;

    // Public Members  
    this.Stuff = '';

    this.init = function() {
                that.Stuff = arguments[0];
        }

    this.doSomething = function() {
                console.log( that.Stuff );
        }
}


this的身份是javascript中的一个常见问题。如果您试图创建一个到doSomething的快捷方式,它也会中断:

1
2
var do = Bob.doSomething;
do(); // this is no longer pointing to Bob!

不依赖this的身份是一种良好的做法。您可以通过多种方式来实现这一点,但最简单的方法是明确地引用Bob,而不是在doSomething的内部引用this。另一种方法是使用构造函数函数(但随后会丢失酷对象的文本语法):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var createBob = function() {
    var that = {};

    that.Stuff = '';
    that.init = function() {
        that.Stuff = arguments[0];
    };

   that.doSomething = function() {
       console.log( that.Stuff );
   };

   return that;  
}

var bob = createBob();


你可以尝试这样做:

1
2
3
$j('#MyButton').click(function(event) {
  Bob.doSomething(event, this);
});

现在,doSomething仍然有Bob作为其this的对象,使用函数参数,它将有event对象和它被触发的元素。