Я как раз читал этот вопрос и хотел попробовать метод псевдонима, а не метод-оболочку функции, но я не мог заставить его работать ни в Firefox 3, ни в 3.5beta4, ни в Google Chrome, как в окнах отладки, так и в на тестовой веб-странице.
Firebug:
>>> window.myAlias = document.getElementById
function()
>>> myAlias('item1')
>>> window.myAlias('item1')
>>> document.getElementById('item1')
<div id="item1">
Если я помещаю его на веб-страницу, вызов 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 (для ясности вставлены символы >>>):
>>> window.myAlias = document.getElementById
function getElementById() { [native code] }
>>> window.myAlias('item1')
TypeError: Illegal invocation
>>> document.getElementById('item1')
<div id=?"item1">?
И на тестовой странице я получаю то же «Незаконный вызов».
Я делаю что-то неправильно? Может ли кто-нибудь еще воспроизвести это?
Также, как ни странно, я только что попробовал, и он работает в IE8.
Ответы:
Вы должны привязать этот метод к объекту документа. Посмотрите:
>>> $ = 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();">
источник
Я глубоко копнул, чтобы понять это конкретное поведение, и думаю, что нашел хорошее объяснение.
Прежде чем я
document.getElementById
расскажу, почему вы не можете использовать псевдоним , я попытаюсь объяснить, как работают функции / объекты JavaScript.Каждый раз, когда вы вызываете функцию JavaScript, интерпретатор JavaScript определяет область видимости и передает ее функции.
Рассмотрим следующую функцию:
function sum(a, b) { return a + b; } sum(10, 20); // returns 30;
Эта функция объявлена в области Window, и когда вы ее вызываете, значение
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 видит , что вы вызываете функцию СеЬАда на
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
Интерпретатор JavaScript устанавливает
window
объект как,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
. :)источник
Это короткий ответ.
Следующее делает копию (ссылку) на функцию. Проблема в том, что теперь функция находится на
window
объекте, когда она была спроектирована для жизни наdocument
объекте.window.myAlias = document.getElementById
Альтернативы
или вы можете использовать два псевдонима.
window.d = document // A renamed reference to the object window.d.myAlias = window.d.getElementById
источник
Еще один короткий ответ, только для обертывания / псевдонима
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
на MDN .приставка Firebug
источник
В дополнение к другим отличным ответам есть простой метод jQuery $ .proxy .
Вы можете использовать такой псевдоним:
myAlias = $.proxy(document, 'getElementById');
Или
myAlias = $.proxy(document.getElementById, document);
источник
$.proxy
тоже есть обертка.Ты на самом деле вы не можете использовать "чистый псевдоним" функции для предопределенного объекта. Следовательно, наиболее близким к алиасингу без обертывания является пребывание в одном объекте:
>>> document.s = document.getElementById; >>> document.s('myid'); <div id="myid">
источник