91

我只是在读书这个问题我想尝试别名方法而不是函数捕捉器方法,但我似乎无法在Firefox 3或3.5beta4或Google Chrome的调试窗口和测试网页中使用它。

萤火虫:

>>>window.myAlias=文档.getElementById函数()>>>我的别名('item1')>>>window.myAlias('item1')>>>document.getElementById('item1')<div id=“item1”>

如果我将其放在网页中,调用myAlias会出现以下错误:

未捕获的异常:[异常…“WrappedNative原型对象上的非法操作”nsresult:“0x8057000c(NS_ERROR_XPC_BAD_OP_on_WN_PROTO)”location:“JS frame::file:///[…snip…]/test.html::<TOP_LEVEL>::line 7”data:no]

镀铬(为了清晰起见,已插入>>):

>>>window.myAlias=文档.getElementById函数getElementById(){[本机代码]}>>>window.myAlias('item1')TypeError:非法调用>>>document.getElementById('item1')<div id=?“项目1”>?

在测试页面中,我得到了相同的“非法调用”。

我做错什么了吗?其他人能复制这个吗?

而且,奇怪的是,我刚刚尝试过,它在IE8中也能工作。

  • 1
    在IE8中,它可能是某种魔法函数或其他东西。 评论 2009年6月17日14:42
  • 我在上对错误答案添加了评论stackoverflow.com/questions/954417并把他们带到了这里。 评论 2009年6月17日21:37
  • 2
    对于支持Function.prototype.bind方法的浏览器:window.myAlias=document.getElementById.bind(document);搜索MDN文档时,将出现绑定填充符。
    – 
    评论 2011年8月12日0:09

6个答案6

重置为默认值
194

我深入了解了这种特殊行为,我认为我找到了一个很好的解释。

在我了解你为什么不能化名之前文档.getElementById,我将尝试解释JavaScript函数/对象是如何工作的。

每当调用JavaScript函数时,JavaScript解释器都会确定一个范围并将其传递给函数。

考虑以下功能:

函数和(a,b){返回a+b;}总和(10,20);//返回30;

此函数在Window范围内声明,当您调用它时在sum函数中是全局窗口对象。

对于“sum”函数,“this”的值是多少并不重要,因为它没有使用它。


考虑以下功能:

功能人员(生日){this.birthDate=出生日期;this.getAge=函数(){return new Date().getFullYear()this.birthDate.getFullYear();};}var dave=新人员(新日期(1909年1月1日));dave.getAge()//返回100。

当您调用dave.getAge函数时,JavaScript解释器会看到您正在调用戴夫对象,因此它设置戴夫并调用getAge(获取年龄)功能。获取代理()将正确返回100.


您可能知道,在JavaScript中,可以使用应用方法。让我们试试看。

var dave=新人员(新日期(1909年1月1日))//2009年100岁var bob=新人员(新日期(1809,1,1))//2009年200岁dave.getAge.apply(鲍勃)//返回200。

在上面的行中,不是让JavaScript决定范围,而是手动将范围作为鲍勃对象。getAge(获取年龄)现在将返回200即使你“以为”你打过电话getAge(获取年龄)戴夫对象。


以上这些有什么意义?函数“松散”地附加到JavaScript对象。例如,你可以

var dave=新人员(新日期(1909年1月1日));var bob=新人员(新日期(1809,1,1));bob.getAge=函数(){return-1;};bob.getAge()//返回-1dave.getAge()//返回100

让我们迈出下一步。

var dave=新人员(新日期(1909年1月1日));var ageMethod=dave.getAge;dave.getAge()//返回100;年龄方法()//回报?????

age方法执行时抛出错误!怎么搞的?

如果你仔细阅读我的上述观点,你会注意到日期获取年龄方法是用调用的戴夫作为对象,而JavaScript无法确定的“范围”age方法执行。所以它将全局“Window”传递为“this”。现在作为窗口没有出生日期属性,年龄方法执行将失败。

如何解决这个问题?很简单,

ageMethod.apply(戴夫)//返回100。

以上所有内容都有意义吗?如果是这样,那么您将能够解释为什么不能使用别名文档.getElementById:

var$=document.getElementById;$(“omeElement”);

$用调用窗口作为如果按Id获取元素期望实现成为文件,它将失败。

再次解决这个问题,你可以

$.apply(文件,[“omeElement”]);

那么为什么它在Internet Explorer中工作呢?

我不知道按Id获取元素在IE中,但在jQuery源代码中有一条注释(inArray(阵列中)方法实现)表示,在IE中,window==文档。如果是这样,则使用别名文档.getElementById应该在IE工作。

为了进一步说明这一点,我创建了一个详细的例子。看看下面的函数。

功能人员(生日){var self=此;this.birthDate=出生日期;this.getAge=函数(){//让我们确保调用了getAge方法//使用从我们的Person函数构造的对象。if(this.constructor==个人)return new Date().getFullYear()-this.birthDate.getFulYear(();其他的返回-1;};//getAge函数的智能版本,它将始终引用对象//它是用创建的。this.getAgeSmarter=函数(){return self.getAge();};//getAge函数的最智能版本。//它将尝试使用最合适的范围。this.getAgeSmartest=函数(){var scope=this.constructor==个人?这:自我;return scope.getAge();};}

对于上面的函数,下面是各种getAge(获取年龄)方法将起作用。

让我们使用创建两个对象功能。

var yogi=新人员(新日期(1909年1月1日))//年龄为100岁var anotherYogi=新人员(新日期(1809,1,1))//年龄为200岁

console.log(yogi.getAge())//输出:100。

直接来说,getAge方法得到瑜伽士对象为和输出100.


var ageAlias=yogi.getAge;console.log(ageAlias())//输出:-1

JavaScript interpreter集合窗口对象为以及我们的获取年龄方法将返回-1.


console.log(ageAlias.apply(瑜伽))//输出:100

如果我们设置了正确的范围,您可以使用age别名方法。


console.log(ageAlias.apply(anotherYogi))//输出:200

如果我们传入其他人对象,它仍将正确计算年龄。

var ageSmarterAlias=yogi.getAgeSmarter;console.log(ageSmarterAlias())//输出:100

这个年龄更智能函数捕获了原始对象,所以现在您不必担心提供正确的范围。


console.log(ageSmarterAlias.apply(anotherYogi))//输出:100!!!

问题在于年龄更智能就是你永远不能将范围设置为其他对象。


var ageSmartestAlias=yogi.getAgeSmartest;console.log(ageSmartestAlias())//输出:100console.log(ageSmartestAlias.apply(文档))//输出:100

这个年龄最聪明如果提供的范围无效,函数将使用原始范围。


console.log(ageSmartestAlias.apply(另一个瑜伽))//输出:200

你仍然可以通过另一个对象到获取AgeSmartest. :)

6
  • 7
    +1解释IE为什么有效。其余部分有点离题,但总体上仍有帮助
    – Kev公司
    评论 2009年7月29日15:10
  • 44
    回答得很好。但我不认为这其中有什么是偏离主题的。 评论 2009年12月18日0:43
  • 答案确实很好。写了一个略短的版本在这里. 评论 2010年8月30日23:00
  • 9
    这是我在stackoverflow上看到的最好的答案之一。
    – 瓦贝克
    评论 2011年6月30日20:40
  • 为什么它在IE中有效?因为:stackoverflow.com/questions/6994139/…-所以这个方法实现实际上可能是{return window[id]},所以它可以在任何上下文中工作 评论 2015年2月11日14:11
43

您必须将该方法绑定到文档对象。看:

>>>$=文档.getElementById按ID获取元素()>>>$('b_nhome')[异常…“无法修改WrappedNative的属性”…匿名::第72行数据:否]>>>$.call(文档,'bn_home')<body id=“bn_home”onload=“init();”>

当你做一个简单的别名时,函数是在全局对象上调用的,而不是在文档对象上。使用一种称为闭包的技术来解决此问题:

函数makeAlias(对象、名称){var fn=对象?对象[名称]:空;if(typeoffn==“undefined”)返回函数(){}返回函数(){返回fn.apply(对象,参数)}}$=makeAlias(文档,'getElementById');>>>$('b_nhome')<body id=“bn_home”onload=“init();”>

这样就不会丢失对原始对象的引用。

2012年,新的绑定ES5中的方法,使我们能够以更奇特的方式实现这一点:

>>>$=document.getElementById.bind(文档)>>>$('b_nhome')<body id=“bn_home”onload=“init();”>
2
  • 4
    它实际上是一个包装器,因为它返回一个新函数。我认为Kev问题的答案是,由于上述原因,这是不可能的。你在这里描述的方法可能是最好的选择。
    – 激动的
    评论 2009年6月17日14:56
  • 谢谢你的评论,我还没有仔细观察到这一点。那么,另一个问题的答案中的语法完全是假的?有趣。。。
    – Kev公司
    评论 2009年6月17日16:16

这是一个简短的回答。

下面是函数的副本(引用)。问题是现在该函数位于窗口当它被设计为在文件对象。

window.myAlias=文档.getElementById

备选方案包括

  • 使用包装器(Fabien Ménager已经提到)
  • 或者可以使用两个别名。

    window.d=document//对象的重命名引用window.d.myAlias=window.d.getElementById
2

另一个简短的答案,仅用于包装/别名控制台.log以及类似的测井方法。他们都希望在慰问上下文。

这在包装时可用控制台日志如果您或您的用户遇到问题使用不(总是)支持它的浏览器。但这并不是解决这个问题的完整方案,因为它需要扩大检查和回退-您的里程数可能会有所不同。

使用警告的示例

var-warn=function(){console.warn.apply(console,arguments);}

然后照常使用

警告(“我需要调试一个数字和一个对象”,9999,{“用户”:“Joel”});

如果您喜欢将日志参数包装在数组中(大多数情况下是这样),请替换为.应用(…)具有.调用(…).

应该与合作控制台.log(),控制台.debug(),控制台.info(),控制台.warn(),控制台.error()。另请参阅慰问在MDN上.

1

除了其他很好的答案外,还有一个简单的jQuery方法$.代理.

您可以这样别名:

myAlias=$.proxy(文档,'getElementById');

或者

myAlias=$.proxy(document.getElementById,document);
1
  • +因为这是一个可行的解决方案。但是,严格地说,在幕后$.代理也是一个包装器。 评论 2012年8月17日9:33
-6

实际上你不能“纯别名”是预定义对象上的函数。因此,在不进行包裹的情况下,最接近别名的方法是停留在同一对象内:

>>>document.s=document.getElementById;>>>文档.s('myd');<div id=“myid”>
2
  • 5
    这有点不正确。如果函数实现使用了“this”,则可以对函数进行“纯别名”。这要视情况而定。 评论 2009年7月29日17:10
  • 对不起,我的意思是在getElementById的上下文中。你说得对。我稍微改变了答案以反映这一点。。。
    – Kev公司
    评论 2009年8月4日17:52

你的答案

单击“发布您的答案”,表示您同意我们的服务条款并确认您已阅读我们的隐私政策.

不是你想要的答案吗?浏览标记的其他问题问你自己的问题.