简体   繁体   English

JavaScript function 别名似乎不起作用

[英]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实现期望thisdocument ,它将失败。

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备选方案是

  • to use a wrapper (already mentioned by Fabien Ménager)使用包装器(Fabien Ménager 已经提到)
  • 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

In addition to other great answers, there's simple jQuery method $.proxy .除了其他很好的答案之外,还有简单的 jQuery 方法$.proxy

You can alias like this:你可以像这样别名:

myAlias = $.proxy(document, 'getElementById');

Or要么

myAlias = $.proxy(document.getElementById, document);

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM