Этот вопрос уже имеет ответ здесь:
Я просто вхожу в использование prototypal JavaScript, и я испытываю затруднения при выяснении, как сохранить a this
ссылка на основной объект из прототипа функционирует, когда объем изменяется. Позвольте мне проиллюстрировать то, что я имею в виду (я использую jQuery здесь):
MyClass = function() {
this.element = $('#element');
this.myValue = 'something';
// some more code
}
MyClass.prototype.myfunc = function() {
// at this point, "this" refers to the instance of MyClass
this.element.click(function() {
// at this point, "this" refers to the DOM element
// but what if I want to access the original "this.myValue"?
});
}
new MyClass();
Я знаю, что могу сохранить ссылку на основной объект путем выполнения этого в начале myfunc
:
var myThis = this;
и затем использование myThis.myValue
получить доступ к свойству основного объекта. Но что происходит, когда у меня есть целый набор опытных функций на MyClass
? Сделайте я должен сохранить ссылку на this
в начале каждого? Кажется, что должен быть более чистый путь. И что относительно такой ситуации:
MyClass = function() {
this.elements $('.elements');
this.myValue = 'something';
this.elements.each(this.doSomething);
}
MyClass.prototype.doSomething = function() {
// operate on the element
}
new MyClass();
В этом случае я не могу создать ссылку на основной объект с var myThis = this;
потому что даже исходное значение this
в контексте doSomething
a jQuery
возразите и не a MyClass
объект.
Было предложено мне использовать глобальную переменную для содержания ссылки на оригинал this
, но это походит на действительно плохую идею мне. Я не хочу загрязнять глобальное пространство имен, и это кажется, что препятствовало бы тому, чтобы я инстанцировал двух различных MyClass
объекты без них вмешивающийся друг в друга.
Какие-либо предложения? Существует ли очевидный способ, чтобы сделать то, что я после? Или мой весь шаблон разработки испорчен?
Для сохранения контекста действительно полезен метод bind
, теперь он является частью недавно выпущенного ECMAScript 5th Edition Спецификация, реализация этой функции проста (всего 8 строк):
// The .bind method from Prototype.js
if (!Function.prototype.bind) { // check if native implementation available
Function.prototype.bind = function(){
var fn = this, args = Array.prototype.slice.call(arguments),
object = args.shift();
return function(){
return fn.apply(object,
args.concat(Array.prototype.slice.call(arguments)));
};
};
}
И вы можете использовать ее в своем примере следующим образом:
MyClass.prototype.myfunc = function() {
this.element.click((function() {
// ...
}).bind(this));
};
Другой пример:
var obj = {
test: 'obj test',
fx: function() {
alert(this.test + '\n' + Array.prototype.slice.call(arguments).join());
}
};
var test = "Global test";
var fx1 = obj.fx;
var fx2 = obj.fx.bind(obj, 1, 2, 3);
fx1(1,2);
fx2(4, 5);
В этом втором примере мы можем наблюдать больше о поведении связывания
.
По сути, он генерирует новую функцию, которая будет отвечать за вызов нашей функции, сохраняя контекст функции ( this
значение), который определяется как первый аргумент bind
.
Остальные аргументы просто передаются нашей функции.
Обратите внимание, что в этом примере функция fx1
вызывается без какого-либо контекста объекта ( obj.method ()
), как и простой вызов функции, в этом типе вызова ключевое слово this
внутри будет ссылаться на глобальный объект, оно будет предупреждать «глобальный тест».
Теперь fx2
- это новая функция, которую сгенерировал метод bind
, он вызовет нашу функцию, сохраняя контекст и правильно передавая аргументы, она выдаст предупреждение «obj test 1 , 2, 3, 4, 5 ", поскольку мы вызвали его, добавив два дополнительных аргумента, он уже связал первые три.
Вы можете установить область видимости, используя call() и apply() function
Поскольку вы используете jQuery, стоит отметить, что эта
уже поддерживается самой jQuery:
$("li").each(function(j,o){
$("span", o).each(function(x,y){
alert(o + " " + y);
});
});
В данном примере o
представляет li
, в то время как y
представляет ребенка span
. А с помощью $.click()
можно получить область видимости из объекта события :
$("li").click(function(e){
$("span", this).each(function(i,o){
alert(e.target + " " + o);
});
});
Где e.target
представляет li
, а o
представляет ребенка span
.
Для вашего последнего примера MyClass
вы можете сделать следующее:
var myThis=this;
this.elements.each(function() { myThis.doSomething.apply(myThis, arguments); });
В функции, которая передается в each
, this
относится к объекту jQuery, как вы уже знаете. Если внутри этой функции вы получаете функцию doSomething
из myThis
, а затем вызываете метод apply для этой функции с массивом аргументов (см. apply
function ] и arguments
переменная ), то this
будет установлено в myThis
в doSomething
.