jQuery对' this '变量做了一些神奇的事情吗?

Does jQuery do some magic with the `this` variable?

本文关键字:神奇 this 变量 jQuery      更新时间:2023-09-26

jQuery已经让我们习惯了以下的this模式:

$(selector).each(function () {
     // do something with `this`
     // `this` iterates over the DOM elements inside `$(selector)`
});

根据这个Crockford谈话(大约15分钟),不使用new运算符调用的函数中this的值是全局对象,除了ES5严格模式,在这种情况下它是undefined

jQuery做一些魔术与this得到它以外的东西,而不是全局对象?请指出源代码的具体行

jQuery使用100%非魔幻的Function.call方法来调用具有特定this的回调。

根据您的使用情况进行裁剪和改写:

jQuery.each = jQuery.fn.each = function( object, callback ) {
    for ( var i = 0, length = object.length ; i < length; i++ ) {
        if ( callback.call( object[ i ], i, object[ i ] ) === false ) {
            break;
        }
    }
    return object;
};

这是重要的部分:callback.call .

孩子,是时候满足this值了。this有一个名称,它准确地指出了它所引用的容器。this有几个可能的值

注意:因为它的名字,this是一个很难谈论的值。因此,每当在代码注释或代码解释中看到self(工作名)这个词时,它指的是this的值。

var o = {
    log : function () {
        console.log( this );
    }
};
o.log(); //will log the object o
function log () {
    console.log( this );
}
//the default enclosing object is the global object - window
//this is the behaviour you observed in Crockford's talk
log();
//the self value is not set in stone. it is evaluated when the function is called
var grass = {
    log : o.log,
    color : 'purple'
};
grass.log(); //will log the object grass
//this is different from languages where self is static. in such languages, doing
// grass.log() would've logged the original object, o, but since self is evaluated
// at runtime, it is set to the now containing object - grass

我们在日常生活中看到的一种现象是变异的self。当我看到一个泥鳅并大喊"这是蓝色的!"时,我并不是指我自己,尽管我的蓝色色调。相反,我宣布了this的新值是谁(通过指向它)。这样的事情可以在js中完成,这就是在jQuery.each中发生的事情。

是时候会见Function.prototype.applyFunction.prototype.call了。他们的行为最好通过一个例子来观察:

grass.speak = function ( message ) {
    //a philosophical conundrum: how does grass talk?
    console.log( this.color + ' ' + message );
};
grass.speak( ' fruit' ); //will log "blue fruit"
var ultraGrass = {
    color : 'maroon'
};
grass.speak.call( ultraGrass, ', hijacked!' ); //will log "maroon, hijacked!"

Function.prototype.call(从现在开始我就叫它call。明白了吗?call…call…没关系)接受两个形参:一个thisArg和一个变长形参列表。

callapply使用指定的self值(第一个参数)和参数列表(其余参数为call,第二个参数为apply)调用函数

Function.prototype.call( thisArg, arg0, arg1, arg2, ... );
Function.prototype.apply( thisArg, [arg0, arg1, arg2, ...] );

下面是一个更传统的例子:

function average () {
    console.log( this ); //to see what call and apply actually do
    for ( var i = 0, sum = 0, len = arguments.length; i < len; i++ ) {
        sum += arguments[ i ];
    }
    return sum / ( len || 1 ); //don't want to divide by zero!
}
average( 1, 2, 3, 4, 5 ); //will log the global object, and return 3
average.call( grass, 1, 2, 3, 4, 5 ); //will log the grass object, and return 3
average.apply( grass, [1, 2, 3, 4, 5] ); //will do the same as above
log.call( ultraGrass ); //will log ultraGrass
log.apply( ultraGrass ); //will also log ultraGrass

callapply之间的唯一区别是apply需要一个参数数组,而call使用第一个参数之后传递给它的所有参数作为参数。

返回到原来的jQuery代码:

callback.call( object[ i ], i, object[ i ] )

调用callback函数:

  1. this设置为object[ i ]
  2. 两个参数:iobject[ i ]

查看更多信息:

  1. 申请并调用MDN
  2. 在ES5规范
  3. 中应用和调用

这只是普通的javascript调用和应用

相关部分来自jQuery源码:

if ( isObj ) {
    for ( name in object ) {
        if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
            break;
        }
    }
} else {
    for ( ; i < length; ) {
        if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) {
            break;
        }
    }
}

callback指的是用.call调用的函数:

function asd(){
     alert( this.i ); // 3
}
asd.call( {i: 3} );