[英]JavaScript function aliasing doesn't seem to work
I was just reading this question and wanted to try the alias method rather than the function-wrapper method, but I couldn't seem to get it to work in either Firefox 3 or 3.5beta4, or Google Chrome, both in their debug windows and in a test web page.我只是在读这个问题,想尝试别名方法而不是函数包装方法,但我似乎无法让它在 Firefox 3 或 3.5beta4 或 Google Chrome 中工作,无论是在他们的调试 windows 和在测试 web 页面中。
Firebug:萤火虫:
>>> window.myAlias = document.getElementById
function()
>>> myAlias('item1')
>>> window.myAlias('item1')
>>> document.getElementById('item1')
<div id="item1">
If I put it in a web page, the call to myAlias gives me this error:如果我把它放在 web 页面中,对 myAlias 的调用会给我这个错误:
uncaught exception: [Exception... "Illegal operation on WrappedNative prototype object" nsresult: "0x8057000c (NS_ERROR_XPC_BAD_OP_ON_WN_PROTO)" location: "JS frame :: file:///[...snip...]/test.html :: <TOP_LEVEL> :: line 7" data: no]
Chrome (with >>>'s inserted for clarity): Chrome(为清楚起见插入了>>>):
>>> window.myAlias = document.getElementById
function getElementById() { [native code] }
>>> window.myAlias('item1')
TypeError: Illegal invocation
>>> document.getElementById('item1')
<div id=?"item1">?
And in the test page, I get the same "Illegal invocation".在测试页面中,我得到了相同的“非法调用”。
Am I doing something wrong?难道我做错了什么? Can anyone else reproduce this?
其他人可以重现吗?
Also, oddly enough, I just tried and it works in IE8.另外,奇怪的是,我刚刚尝试过,它在 IE8 中工作。
I dug deep to understand this particular behavior and I think I have found a good explanation.我深入挖掘以了解这种特殊行为,我认为我找到了一个很好的解释。
Before I get in to why you are not able to alias document.getElementById
, I will try to explain how JavaScript functions/objects work.在我开始解释为什么你不能为
document.getElementById
取别名之前,我将尝试解释 JavaScript 函数/对象是如何工作的。
Whenever you invoke a JavaScript function, the JavaScript interpreter determines a scope and passes it to the function.每当您调用 JavaScript function 时,JavaScript 解释器都会确定 scope 并将其传递给 function。
Consider following function:考虑以下 function:
function sum(a, b)
{
return a + b;
}
sum(10, 20); // returns 30;
This function is declared in the Window scope and when you invoke it the value of this
inside the sum function will be the global Window
object.此 function 在 Window scope 中声明,当您调用它时,总和 function 中的
this
值将是全局Window
object。
For the 'sum' function, it doesn't matter what the value of 'this' is as it is not using it.对于“sum”function,“this”的值是多少并不重要,因为它没有使用它。
Consider following function:考虑以下 function:
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.
When you call dave.getAge function, the JavaScript interpreter sees that you are calling getAge function on the dave
object, so it sets this
to dave
and calls the getAge
function. getAge()
will correctly return 100
.当你调用 dave.getAge function 时,JavaScript 解释器看到你在
dave
object 上调用 getAge function,所以它将this
设置为dave
并调用getAge
getAge()
将正确返回100
You may know that in JavaScript you can specify the scope using the apply
method.您可能知道,在 JavaScript 中,您可以使用
apply
方法指定 scope。 Let's try that.让我们试试吧。
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.
In the above line, instead of letting JavaScript decide the scope, you are passing the scope manually as the bob
object. getAge
will now return 200
even though you 'thought' you called getAge
on the dave
object.在上面的行中,不是让 JavaScript 决定 scope,而是将 scope 作为
bob
object 手动传递getAge
现在将返回200
,即使您“认为”在dave
getAge
上调用了 getAge。
What's the point of all of the above?以上所有的意义何在? Functions are 'loosely' attached to your JavaScript objects.
函数“松散地”附加到您的 JavaScript 对象。 Eg you can do
例如你可以做
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
Let's take the next step.让我们进行下一步。
var dave = new Person(new Date(1909, 1, 1));
var ageMethod = dave.getAge;
dave.getAge(); //returns 100;
ageMethod(); //returns ?????
ageMethod
execution throws an error? ageMethod
执行抛出错误? What happened?发生了什么?
If you read my above points carefully, you would note that dave.getAge
method was called with dave
as this
object whereas JavaScript could not determine the 'scope' for ageMethod
execution.如果你仔细阅读我的上述几点,你会注意到
dave.getAge
方法是用dave
调用的,因为this
object 而 JavaScript 无法确定ageMethod
执行的“范围”。 So it passed global 'Window' as 'this'.所以它通过全局“Window”作为“this”。 Now as
window
doesn't have a birthDate
property, ageMethod
execution will fail.现在,由于
window
没有birthDate
属性, ageMethod
执行将失败。
How to fix this?如何解决这个问题? Simple,
简单的,
ageMethod.apply(dave); //returns 100.
Did all of the above make sense?以上所有内容是否有意义? If it does, then you will be able to explain why you are not able to alias
document.getElementById
:如果是这样,那么您将能够解释为什么您不能为
document.getElementById
添加别名:
var $ = document.getElementById;
$('someElement');
$
is called with window
as this
and if getElementById
implementation is expecting this
to be document
, it will fail. $
是用window
调用this
,如果getElementById
实现期望this
是document
,它将失败。
Again to fix this, you can do再次解决这个问题,你可以做
$.apply(document, ['someElement']);
So why does it work in Inte.net Explorer?那么为什么它在 Inte.net Explorer 中有效呢?
I don't know the internal implementation of getElementById
in IE, but a comment in jQuery source ( inArray
method implementation) says that in IE, window == document
.我不知道
getElementById
在 IE 中的内部实现,但 jQuery 源代码( inArray
方法实现)中的评论说在 IE 中, window == document
。 If that's the case, then aliasing document.getElementById
should work in IE.如果是这种情况,那么别名
document.getElementById
应该可以在 IE 中使用。
To illustrate this further, I have created an elaborate example.为了进一步说明这一点,我创建了一个精心设计的示例。 Have a look at the
Person
function below.看看下面的
Person
function。
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();
};
}
For the Person
function above, here's how the various getAge
methods will behave.对于上面的
Person
function,这里是各种getAge
方法的行为方式。
Let's create two objects using Person
function.让我们使用
Person
function 创建两个对象。
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.
Straight forward, getAge method gets yogi
object as this
and outputs 100
.直截了当, getAge 方法获取
yogi
this
并输出100
。
var ageAlias = yogi.getAge;
console.log(ageAlias()); //Output: -1
JavaScript interepreter sets window
object as this
and our getAge
method will return -1
. JavaScript 解释器将
window
object 设置为this
,我们的getAge
方法将返回-1
。
console.log(ageAlias.apply(yogi)); //Output: 100
If we set the correct scope, you can use ageAlias
method.如果我们设置正确的scope,就可以使用
ageAlias
方法。
console.log(ageAlias.apply(anotherYogi)); //Output: 200
If we pass in some other person object, it will still calculate age correctly.如果我们传入其他人 object,它仍然会正确计算年龄。
var ageSmarterAlias = yogi.getAgeSmarter;
console.log(ageSmarterAlias()); //Output: 100
The ageSmarter
function captured the original this
object so now you don't have to worry about supplying correct scope. ageSmarter
function 捕获了原始this
object,所以现在您不必担心提供正确的 scope。
console.log(ageSmarterAlias.apply(anotherYogi)); //Output: 100 !!!
The problem with ageSmarter
is that you can never set the scope to some other object. ageSmarter
的问题在于您永远无法将 scope 设置为其他一些 object。
var ageSmartestAlias = yogi.getAgeSmartest;
console.log(ageSmartestAlias()); //Output: 100
console.log(ageSmartestAlias.apply(document)); //Output: 100
The ageSmartest
function will use the original scope if an invalid scope is supplied.如果提供了无效的 scope,
ageSmartest
function 将使用原始 scope。
console.log(ageSmartestAlias.apply(anotherYogi)); //Output: 200
You will still be able to pass another Person
object to getAgeSmartest
.您仍然可以将另一个
Person
object 传递给getAgeSmartest
。 :) :)
You have to bind that method to the document object. Look:您必须将该方法绑定到文档 object。看:
>>> $ = document.getElementById
getElementById()
>>> $('bn_home')
[Exception... "Cannot modify properties of a WrappedNative" ... anonymous :: line 72 data: no]
>>> $.call(document, 'bn_home')
<body id="bn_home" onload="init();">
When you're doing a simple alias, the function is called on the global object, not on the document object. Use a technique called closures to fix this:当你做一个简单的别名时,function 是在全局 object 上调用的,而不是在文档 object 上调用的。使用一种称为闭包的技术来解决这个问题:
function makeAlias(object, name) {
var fn = object ? object[name] : null;
if (typeof fn == 'undefined') return function () {}
return function () {
return fn.apply(object, arguments)
}
}
$ = makeAlias(document, 'getElementById');
>>> $('bn_home')
<body id="bn_home" onload="init();">
This way you don't loose the reference to the original object.这样您就不会丢失对原始 object 的引用。
In 2012, there is the new bind
method from ES5 that allows us to do this in a fancier way: 2012 年,ES5 中出现了新的
bind
方法,它允许我们以更奇特的方式执行此操作:
>>> $ = document.getElementById.bind(document)
>>> $('bn_home')
<body id="bn_home" onload="init();">
This is a short answer.这是一个简短的回答。
The following makes a copy of (a reference to) the function. The problem is that now the function is on the window
object when it was designed to live on the document
object.以下是 function 的副本(参考)。问题是现在 function 在
window
object 上,而它被设计为存在于document
object 上。
window.myAlias = document.getElementById
The alternatives are备选方案是
or you can use two aliases.或者您可以使用两个别名。
window.d = document // A renamed reference to the object window.d.myAlias = window.d.getElementById
Another short answer, just for wrapping/aliasing console.log
and similar logging methods.另一个简短的答案,仅用于包装/别名
console.log
和类似的日志记录方法。 They all expect to be in the console
context.他们都希望在
console
上下文中。
This is usable when wrapping console.log
with some fallbacks, in case you or your users have run into trouble when using a browser that doesn't (always) support it .这在用一些回退包装
console.log
时很有用,以防您或您的用户在使用不(始终)支持它的浏览器时遇到麻烦。 This is not a full solution to that problem though, as it needs to be expanded checks and a fallback - your mileage may vary.不过,这并不是该问题的完整解决方案,因为它需要扩展检查和回退——您的情况可能会有所不同。
Example using warnings使用警告的示例
var warn = function(){ console.warn.apply(console, arguments); }
Then use it as usual然后照常使用
warn("I need to debug a number and an object", 9999, { "user" : "Joel" });
If you prefer to see your logging arguments wrapped in an array (I do, most of the time), substitute .apply(...)
with .call(...)
.如果您希望看到您的日志记录 arguments 包装在一个数组中(我这样做,大部分时间),
.apply(...)
替换为 .call( .call(...)
。
Should work with console.log()
, console.debug()
, console.info()
, console.warn()
, console.error()
.应该与
console.log()
、 console.debug()
、 console.info()
、 console.warn()
、 console.error()
一起使用。 See also console
on MDN .另请参阅MDN 上的
console
。
You actually can't "pure alias" a function on a predefined object. Therefore, the closest to aliasing you can get without wrapping is by staying within the same object:实际上,您不能在预定义的 object 上“纯别名”function。因此,在不换行的情况下最接近别名的方法是保持在相同的 object 中:
>>> document.s = document.getElementById;
>>> document.s('myid');
<div id="myid">
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.