我深入了解了这种特殊行为,并认为我找到了很好的解释。
在介绍为什么您不能使用别名之前document.getElementById
,我将尝试解释JavaScript函数/对象的工作方式。
每当您调用JavaScript函数时,JavaScript解释器都会确定作用域并将其传递给该函数。
考虑以下功能:
function sum(a, b)
{
return a + b;
}
sum(10, 20);
该函数在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();
当您调用dave.getAge函数时,JavaScript解释器会看到您正在对该dave
对象调用getAge函数,因此它将设置this
为dave
并调用该getAge
函数。getAge()
将正确返回100
。
您可能知道,在JavaScript中,您可以使用apply
方法指定范围。让我们尝试一下。
var dave = new Person(new Date(1909, 1, 1));
var bob = new Person(new Date(1809, 1, 1));
dave.getAge.apply(bob);
在上一行中,您不是将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();
dave.getAge();
让我们进行下一步。
var dave = new Person(new Date(1909, 1, 1));
var ageMethod = dave.getAge;
dave.getAge();
ageMethod();
ageMethod
执行抛出错误!发生了什么?
如果仔细阅读以上几点,您会注意到该dave.getAge
方法是dave
作为this
对象调用的,而JavaScript无法确定ageMethod
执行的“范围” 。因此它通过全局“窗口”作为“此”。现在,由于window
没有birthDate
属性,ageMethod
执行将失败。
如何解决这个问题?简单,
ageMethod.apply(dave);
以上所有都有意义吗?如果是这样,那么您将能够解释为什么您不能使用别名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()
{
if(this.constructor == Person)
return new Date().getFullYear() - this.birthDate.getFullYear();
else
return -1;
};
this.getAgeSmarter = function()
{
return self.getAge();
};
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));
var anotherYogi = new Person(new Date(1809, 1, 1));
console.log(yogi.getAge());
直截了当,getAge方法获取yogi
对象asthis
并输出100
。
var ageAlias = yogi.getAge;
console.log(ageAlias());
JavaScript解释器将window
object设置为this
,我们的getAge
方法将返回-1
。
console.log(ageAlias.apply(yogi));
如果我们设置了正确的范围,则可以使用ageAlias
方法。
console.log(ageAlias.apply(anotherYogi));
如果我们传递其他人对象,它仍然可以正确计算年龄。
var ageSmarterAlias = yogi.getAgeSmarter;
console.log(ageSmarterAlias());
该ageSmarter
函数捕获了原始this
对象,因此现在您不必担心提供正确的范围。
console.log(ageSmarterAlias.apply(anotherYogi));
问题ageSmarter
在于您永远无法将范围设置为其他对象。
var ageSmartestAlias = yogi.getAgeSmartest;
console.log(ageSmartestAlias());
console.log(ageSmartestAlias.apply(document));
ageSmartest
如果提供了无效的作用域,则该函数将使用原始作用域。
console.log(ageSmartestAlias.apply(anotherYogi));
您仍然可以将另一个Person
对象传递给getAgeSmartest
。:)