我深入了解了这种特殊行为,并认为我找到了很好的解释。
在介绍为什么您不能使用别名之前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');
$与windowas一起调用,this并且如果getElementById期望this实现document,它将失败。
再次解决此问题,您可以执行
$.apply(document, ['someElement']);
那么为什么它可以在Internet Explorer中工作呢?
我不知道getElementByIdIE中的内部实现,但是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解释器将windowobject设置为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。:)