我深入了解这种特殊行为,我想我找到了一个很好的解释。
在我开始讨论为什么不能使用 alias 之前document.getElementById
,我将尝试解释 JavaScript 函数/对象是如何工作的。
每当您调用 JavaScript 函数时,JavaScript 解释器都会确定一个范围并将其传递给该函数。
考虑以下功能:
function sum(a, b)
{
return a + b;
}
sum(10, 20); // returns 30;
此函数在 Window 范围内声明,当您调用它时this
, sum 函数内部的值将是全局Window
对象。
对于 'sum' 函数,'this' 的值无关紧要,因为它没有使用它。
考虑以下功能:
function Person(birthDate)
{
this.birthDate = birthDate;
this.getAge = function() { return new Date().getFullYear() - this.birthDate.getFullYear(); };
}
var dave = new Person(new Date(1909, 1, 1));
dave.getAge(); //returns 100.
当您调用 dave.getAge 函数时,JavaScript 解释器看到您正在对dave
对象调用 getAge 函数,因此它设置this
为dave
并调用该getAge
函数。getAge()
将正确返回100
。
您可能知道在 JavaScript 中您可以使用apply
方法指定范围。让我们试试看。
var dave = new Person(new Date(1909, 1, 1)); //Age 100 in 2009
var bob = new Person(new Date(1809, 1, 1)); //Age 200 in 2009
dave.getAge.apply(bob); //returns 200.
在上面的行中,您不是让 JavaScript 决定作用域,而是将作用域作为bob
对象手动传递。即使您“认为”您调用了对象,getAge
现在也会返回。200
getAge
dave
以上所有内容的意义何在?函数“松散地”附加到您的 JavaScript 对象上。例如你可以做
var dave = new Person(new Date(1909, 1, 1));
var bob = new Person(new Date(1809, 1, 1));
bob.getAge = function() { return -1; };
bob.getAge(); //returns -1
dave.getAge(); //returns 100
让我们进行下一步。
var dave = new Person(new Date(1909, 1, 1));
var ageMethod = dave.getAge;
dave.getAge(); //returns 100;
ageMethod(); //returns ?????
ageMethod
执行会报错!发生了什么?
如果您仔细阅读我的上述几点,您会注意到该dave.getAge
方法是使用dave
asthis
对象调用的,而 JavaScript 无法确定ageMethod
执行的“范围” 。所以它通过全局“Window”作为“this”。现在由于window
没有birthDate
属性,ageMethod
执行将失败。
如何解决这个问题?简单的,
ageMethod.apply(dave); //returns 100.
以上所有内容是否有意义?如果是这样,那么您将能够解释为什么您不能别名document.getElementById
:
var $ = document.getElementById;
$('someElement');
$
用window
as调用,this
如果getElementById
实现预期this
为document
,它将失败。
再次解决这个问题,你可以这样做
$.apply(document, ['someElement']);
那么为什么它可以在 Internet Explorer 中工作呢?
我不知道getElementById
在 IE 中的内部实现,但是 jQuery 源代码(inArray
方法实现)中的注释说在 IE 中,window == document
. 如果是这种情况,则别名document.getElementById
应该在 IE 中工作。
为了进一步说明这一点,我创建了一个精心设计的示例。看看Person
下面的功能。
function Person(birthDate)
{
var self = this;
this.birthDate = birthDate;
this.getAge = function()
{
//Let's make sure that getAge method was invoked
//with an object which was constructed from our Person function.
if(this.constructor == Person)
return new Date().getFullYear() - this.birthDate.getFullYear();
else
return -1;
};
//Smarter version of getAge function, it will always refer to the object
//it was created with.
this.getAgeSmarter = function()
{
return self.getAge();
};
//Smartest version of getAge function.
//It will try to use the most appropriate scope.
this.getAgeSmartest = function()
{
var scope = this.constructor == Person ? this : self;
return scope.getAge();
};
}
对于上述Person
函数,以下是各种getAge
方法的行为方式。
让我们使用Person
函数创建两个对象。
var yogi = new Person(new Date(1909, 1,1)); //Age is 100
var anotherYogi = new Person(new Date(1809, 1, 1)); //Age is 200
console.log(yogi.getAge()); //Output: 100.
直接, getAge 方法获取yogi
object asthis
并输出100
。
var ageAlias = yogi.getAge;
console.log(ageAlias()); //Output: -1
JavaScript 解释器将window
object设置为 asthis
并且我们的getAge
方法将返回-1
.
console.log(ageAlias.apply(yogi)); //Output: 100
如果我们设置了正确的范围,则可以使用ageAlias
方法。
console.log(ageAlias.apply(anotherYogi)); //Output: 200
如果我们传入其他 person 对象,它仍然会正确计算年龄。
var ageSmarterAlias = yogi.getAgeSmarter;
console.log(ageSmarterAlias()); //Output: 100
该ageSmarter
函数捕获了原始this
对象,因此现在您不必担心提供正确的范围。
console.log(ageSmarterAlias.apply(anotherYogi)); //Output: 100 !!!
问题ageSmarter
在于您永远无法将范围设置为其他对象。
var ageSmartestAlias = yogi.getAgeSmartest;
console.log(ageSmartestAlias()); //Output: 100
console.log(ageSmartestAlias.apply(document)); //Output: 100
ageSmartest
如果提供了无效范围,该函数将使用原始范围。
console.log(ageSmartestAlias.apply(anotherYogi)); //Output: 200
您仍然可以将另一个Person
对象传递给getAgeSmartest
. :)