Вопрос по closures, alias, function, javascript – Псевдоним функции JavaScript не работает

79

Я просто читалэтот вопрос и я хотел попробовать метод псевдонима, а не метод функции-обертки, но я не смог заставить его работать в Firefox 3 или 3.5beta4 или в Google Chrome, как в их окнах отладки, так и на тестовой веб-странице.

Firebug:

<code>>>> window.myAlias = document.getElementById
function()
>>> myAlias('item1')
>>> window.myAlias('item1')
>>> document.getElementById('item1')
<div id="item1">
</code>

Если я помещу это на веб-страницу, вызов myAlias выдаст мне эту ошибку:

<code>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]
</code>

Хром (со вставками для ясности):

<code>>>> window.myAlias = document.getElementById
function getElementById() { [native code] }
>>> window.myAlias('item1')
TypeError: Illegal invocation
>>> document.getElementById('item1')
<div id=?"item1">?
</code>

И на тестовой странице я получаю тот же «Незаконный вызов».

Я делаю что-то неправильно? Кто-нибудь еще может воспроизвести это?

Также, как ни странно, я только что попробовал, и это работает в IE8.

Я добавил комментарии к неправильным ответам наstackoverflow.com/questions/954417 и направил их сюда. Grant Wagner
в IE8 & # x2014; это может быть какая-то магическая функция или что-то в этом роде. Maciej Łebkowski
Для браузеров, которые поддерживают метод Function.prototype.bind: window.myAlias = document.getElementById.bind (document); Ищите документы MDN, там будет обязательная прокладка. Ben Fleming

Ваш Ответ

6   ответов
2

console.log и подобные методы регистрации. Все они ожидают быть вconsole контекст.

Это можно использовать при упаковкеconsole.log с некоторыми запасными вариантами, если вы или ваши пользователи столкнулись с проблемами, когдаиспользуя браузер, который его не поддерживает (всегда), Тем не менее, это не полное решение этой проблемы, поскольку необходимо расширить проверки и использовать запасной вариант - ваш пробег может варьироваться.

Пример использования предупреждений

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

Тогда используйте это как обычно

warn("I need to debug a number and an object", 9999, { "user" : "Joel" });

Если вы предпочитаете, чтобы ваши аргументы регистрации были заключены в массив (чаще всего), замените.apply(...) с.call(...).

Должен работать сconsole.log(), console.debug(), console.info(), console.warn(), console.error(), Смотрите такжеconsole on MDN.

-6

can't & quot; чистый псевдоним & quot; функция на предопределенном объекте. Таким образом, ближайший к псевдониму, который вы можете получить без переноса, находится в пределах одного и того же объекта:

>>> document.s = document.getElementById;
>>> document.s('myid');
<div id="myid">
Извините, я имел в виду getElementById. Вы правы. Я немного изменил ответ, чтобы отразить это ... Kev
Это немного неверно. Вы можете «чистый псевдоним»; функция при условии, что реализация функции использует «this». Так что это зависит.
О, Кев, это действительно OP Lolzzzz
1

существует простой метод jQuery$ .proxy.

Вы можете использовать псевдоним так:

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

Или же

myAlias = $.proxy(document.getElementById, document);
+1, потому что это реальное решение. Но, строго говоря, за кадром$.proxy это также обертка на самом деле.
3

Следующее делает копию (ссылку) на функцию. Проблема в том, что теперь функция находится наwindow объект, когда он был разработан, чтобы жить наdocument объект.

window.myAlias = document.getElementById
<,p>The alternatives are

to use a wrapper (already mentioned by Fabien Ménager)

or you can use two aliases.

window.d = document // A renamed reference to the object
window.d.myAlias = window.d.getElementById
184

чтобы понять это конкретное поведение, и я думаю, что нашел хорошее объяснение.

Прежде чем я пойду, почему вы не можете псевдонимdocument.getElementByIdЯ попытаюсь объяснить, как работают функции / объекты JavaScript.

Всякий раз, когда вы вызываете функцию JavaScript, интерпретатор JavaScript определяет область действия и передает ее функции.

Рассмотрим следующую функцию:

function sum(a, b)
{
    return a + b;
}

sum(10, 20); // returns 30;

Эта функция объявляется в области видимости окна, и когда вы вызываете ее, значениеthis внутри функции суммы будет глобальнаяWindow объект.

Для «суммы» функции, это не имеет значения, каково значение этого. как он не использует его.

Рассмотрим следующую функцию:

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.

Когда вы вызываете функцию dave.getAge, интерпретатор JavaScript видит, что вы вызываете функцию getAge дляdave объект, поэтому он устанавливаетthis вdave и называетgetAge функция.getAge() вернется правильно100.

Вы можете знать, что в JavaScript вы можете указать область, используяapply метод. Давайте попробуем это.

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.

В приведенной выше строке вместо того, чтобы позволить 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(); //returns -1
dave.getAge(); //returns 100

Давайте сделаем следующий шаг.

var dave = new Person(new Date(1909, 1, 1));
var ageMethod = dave.getAge;

dave.getAge(); //returns 100;
ageMethod(); //returns ?????

ageMethod выполнение выдает ошибку! Что случилось?

Если вы внимательно прочитаете мои пункты выше, вы заметите, чтоdave.getAge метод был вызван сdave какthis объект, тогда как JavaScript не может определить «область действия»; заageMethod выполнение. Так прошло глобальное «окно» как "это". Сейчас какwindow не имеетbirthDate имущество,ageMethod выполнение не удастся.

Как это исправить? Просто,

ageMethod.apply(dave); //returns 100.

Имеет ли все вышесказанное смысл? Если это так, то вы сможете объяснить, почему вы не можете псевдонимdocument.getElementById:

var $ = document.getElementById;

$('someElement'); 

$ называется сwindow как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()
    {
        //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();
    };

}

ДляPerson выше, вот как различныеgetAge методы будут себя вести.

Давайте создадим два объекта, используяPerson функция.

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.

Прямо вперед, метод getAge получаетyogi объект какthis и выводы100.

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

Наборы интерпретаторов JavaScriptwindow объект какthis и нашgetAge метод вернется-1.

console.log(ageAlias.apply(yogi)); //Output: 100

Если мы установим правильную область, вы можете использоватьageAlias метод.

console.log(ageAlias.apply(anotherYogi)); //Output: 200

Если мы передадим объект другого человека, он все равно будет правильно рассчитывать возраст.

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

ageSmarter функция захватила оригиналthis объект, так что теперь вам не нужно беспокоиться о предоставлении правильного объема.

console.log(ageSmarterAlias.apply(anotherYogi)); //Output: 100 !!!

Проблема сageSmarter является то, что вы никогда не можете установить область видимости для какого-либо другого объекта.

var ageSmartestAlias = yogi.getAgeSmartest;
console.log(ageSmartestAlias()); //Output: 100
console.log(ageSmartestAlias.apply(document)); //Output: 100

ageSmartest Функция будет использовать исходную область, если указана неверная область.

console.log(ageSmartestAlias.apply(anotherYogi)); //Output: 200

Вы все еще сможете пройти другойPerson ВозражатьgetAgeSmartest. :)

Отличный ответ. Я не вижу, как это все не по теме.
+1, почему IE работает. Остальное немного не по теме, но в целом полезно. :) Kev
Очень хороший ответ, действительно. Несколько более короткая версия написанаhere.
+1, потому что это реальное решение. Но, строго говоря, за кадром$.proxy это также обертка на самом деле.
35

>>> $ = 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();">

Когда вы делаете простой псевдоним, функция вызывается для глобального объекта, а не для объекта документа. Используйте метод, называемый замыканиями, чтобы исправить это:

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();">

Таким образом, вы не потеряете ссылку на исходный объект.

В 2012 году появился новыйbind метод от ES5, который позволяет нам сделать это более изящным способом:

>>> $ = document.getElementById.bind(document)
>>> $('bn_home')
<body id="bn_home" onload="init();">
Который действительно является оберткой, потому что он возвращает новую функцию. Я думаю, что ответ на вопрос Кева состоит в том, что это невозможно по причинам, которые вы описали выше. Метод, который вы описываете здесь, вероятно, лучший вариант.
На самом деле, вы должны представить это как ответ ... Kev
Спасибо за ваш комментарий, я не выглядел достаточно внимательно, чтобы понять это. Таким образом, синтаксис в ответах на другой вопрос является полностью поддельным? Интересно... Kev

Похожие вопросы