objc_msgSend (obj, @selector (foo: bar: err :), var, var2 и & errVar);
Вы должны привязать этот метод к объекту документа. Посмотрите:
>>> $ = 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.s = document.getElementById;
>>> document.s('myid');
<div id="myid">
Давайте посмотрим, как насчет того, чтобы сделать абстракцию обязательной для каждого типа данных, а затем предоставить способы обобщения наших абстракций по параметрам типа? Подождите! Я только что заново изобрел CLU . Получу ли я премию Тьюринга?
Любой, кто интересуется ролью абстракции в программировании, должен изучить CLU .
интерпретатор 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
Пусть ' s сделать следующий шаг.
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
. Таким образом, он передал глобальное «Окно» как «это». Теперь, поскольку окно
не имеет свойства BirthDate
, выполнение ageMethod
завершится ошибкой.
Как это исправить? Просто,
ageMethod.apply(dave); //returns 100.
Все ли вышеперечисленное имело смысл? Если это так, то вы сможете объяснить, почему вы не можете использовать псевдоним document.getElementById
:
var $ = document.getElementById;
$('someElement');
$
вызывается с окном
как this
, и если реализация getElementById
ожидает , этот
будет документ
, он выйдет из строя.
Чтобы исправить это снова, вы можете сделать
$.apply(document, ['someElement']);
Так почему это работает в Internet Explorer?
Я не знаю внутренней реализации getElementById
в IE, но комментарий в источнике jQuery (реализация метода inArray
) говорит, что в IE window == документ
. В этом случае псевдоним 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
выше, здесь '
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
]. :)