У меня есть функция конструктора, которая регистрирует обработчик событий:
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', function () {
alert(this.data);
});
}
// Mock transport object
var transport = {
on: function(event, callback) {
setTimeout(callback, 1000);
}
};
// called as
var obj = new MyConstructor('foo', transport);
Однако я не могу получить доступ к свойству data
созданного объекта внутри обратного вызова. Похоже, что this
ссылается не на объект, который был создан, а на другой объект.
Я также пытался использовать метод объекта вместо анонимной функции:
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', this.alert);
}
MyConstructor.prototype.alert = function() {
alert(this.name);
};
но это приводит к тем же проблемам.
Как я могу получить доступ к правильному объекту?
this
.this
(она же "контекст") - это специальное ключевое слово внутри каждой функции, значение которого зависит только от того, как была вызвана функция, а не от того, как/когда/где она была определена. На него не влияют лексические диапазоны, как на другие переменные (за исключением стрелочных функций, см. ниже). Вот несколько примеров:
function foo() {
console.log(this);
}
// normal function call
foo(); // `this` will refer to `window`
// as object method
var obj = {bar: foo};
obj.bar(); // `this` will refer to `obj`
// as constructor function
new foo(); // `this` will refer to an object that inherits from `foo.prototype`
this
, загляните в MDN документацию.this
.this
.На самом деле вы хотите получить доступ не к this
в частности, а к объекту, на который он ссылается. Поэтому простым решением будет просто создать новую переменную, которая также будет ссылаться на этот объект. Переменная может иметь любое имя, но обычными являются self
и that
.
function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function() {
alert(self.data);
});
}
Поскольку self
является обычной переменной, она подчиняется правилам лексической области видимости и доступна внутри обратного вызова. Это также имеет то преимущество, что вы можете получить доступ к значению this
самого обратного вызова.
this
обратного вызова - часть 1Может показаться, что у вас нет контроля над значением this
, потому что его значение устанавливается автоматически, но на самом деле это не так.
У каждой функции есть метод .bind
[docs], который возвращает новую функцию с this
, привязанной к значению. Эта функция имеет точно такое же поведение, как и та, для которой вы вызвали .bind
, только this
было задано вами. Независимо от того, как и когда будет вызвана эта функция, this
всегда будет ссылаться на переданное значение.
function MyConstructor(data, transport) {
this.data = data;
var boundFunction = (function() { // parenthesis are not necessary
alert(this.data); // but might improve readability
}).bind(this); // <- here we are calling `.bind()`
transport.on('data', boundFunction);
}
В данном случае мы привязываем обратный вызов this
к значению MyConstructor
's this
.
Примечание: При привязке контекста для jQuery используйте jQuery.proxy
[docs] вместо этого. Это необходимо для того, чтобы вам не нужно было хранить ссылку на функцию при отвязывании обратного вызова события. jQuery обрабатывает это внутренне.
ECMAScript 6 вводит стрелочные функции, которые можно представить как лямбда-функции. Они не имеют собственной привязки this
. Вместо этого this
ищется в области видимости, как обычная переменная. Это означает, что вам не нужно вызывать .bind
. Это не единственная особенность их поведения, за дополнительной информацией обращайтесь к документации MDN.
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => alert(this.data));
}
this
обратного вызова - часть 2Некоторые функции/методы, принимающие обратные вызовы, также принимают значение, на которое должен ссылаться this
обратного вызова. По сути, это то же самое, что привязать его самостоятельно, но функция/метод делает это за вас. Array#map
[docs] является таким методом. Его сигнатура имеет вид:
array.map(callback[, thisArg])
Первый аргумент - обратный вызов, а второй - значение, на которое должно ссылаться this
. Вот надуманный пример:
var arr = [1, 2, 3];
var obj = {multiplier: 42};
var new_arr = arr.map(function(v) {
return v * this.multiplier;
}, obj); // <- here we are passing `obj` as second argument
Примечание: О том, можно ли передавать значение для this
, обычно говорится в документации к этой функции/методу. Например, jQuery's $.ajax
method [docs] описывает опцию под названием context
:
Этот объект становится контекстом всех обратных вызовов, связанных с Ajax.
Общая проблема: Использование методов объекта в качестве обратных вызовов/обработчиков событий
Другим распространенным проявлением этой проблемы является использование объектного метода в качестве обратного вызова/обработчика событий. Функции являются гражданами первого класса в JavaScript, и термин "метод" - это просто разговорный термин для функции, которая является значением свойства объекта. Но эта функция не имеет конкретной связи со своим "содержащим" объектом. Рассмотрим следующий пример:
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = function() {
console.log(this.data);
};
Функция this.method
назначена обработчиком события click, но если щелкнуть по document.body
, то значение logged будет undefined
, потому что внутри обработчика события this
ссылается на document.body
, а не на экземпляр Foo
.
Как уже упоминалось в начале, то, на что ссылается this
, зависит от того, как вызывается функция, а не от того, как она определена.
Если бы код выглядел следующим образом, было бы более очевидно, что функция не имеет неявной ссылки на объект:
function method() {
console.log(this.data);
}
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = method;
Решение такое же, как указано выше: Если есть возможность, используйте .bind
для явного связывания this
с определенным значением
document.body.onclick = this.method.bind(this);
или явно вызовите функцию как "метод" объекта, используя анонимную функцию как обратный вызов / обработчик события и присвойте объект (this
) другой переменной:
var self = this;
document.body.onclick = function() {
self.method();
};
или использовать функцию-стрелку:
document.body.onclick = () => this.method();
в
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', ( function () {
alert(this.data);
}).bind(this) );
}
// Mock transport object
var transport = {
on: function(event, callback) {
setTimeout(callback, 1000);
}
};
// called as
var obj = new MyConstructor('foo', transport);
в
Если вы с использованием <код>подчеркнуть.Яш</код> - http://underscorejs.org/#bind
transport.on('data', _.bind(function () {
alert(this.data);
}, this));
в
function MyConstructor(data, transport) {
var self = this;
this.data = data;
transport.on('data', function() {
alert(self.data);
});
}
в
Функция #3 стрелка
в
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => {
alert(this.data);
});
}
в
Все дело в "волшебном" синтаксисе вызова метода:
object.property();
Когда вы получаете свойство от объекта и вызываете его одним махом, объект будет контекстом для метода. Если вы вызываете тот же метод, но отдельными шагами, контекстом будет глобальная область видимости (окно):
var f = object.property;
f();
Когда вы получаете ссылку на метод, она больше не привязана к объекту, это просто ссылка на обычную функцию. То же самое происходит, когда вы получаете ссылку для использования в качестве обратного вызова:
this.saveNextLevelData(this.setAll);
Здесь вы привязываете контекст к функции:
this.saveNextLevelData(this.setAll.bind(this));
Если вы используете jQuery, вам следует использовать метод $.proxy
, так как bind
поддерживается не во всех браузерах:
this.saveNextLevelData($.proxy(this.setAll, this));
Термин "контекст" иногда используется для обозначения объекта, на который ссылается this. Его использование неуместно, поскольку он не соответствует ни семантически, ни технически ECMAScript's this.
"Контекст" означает обстоятельства, окружающие что-то, которые добавляют смысл, или некоторую предшествующую и последующую информацию, которая придает дополнительный смысл. Термин "контекст" используется в ECMAScript для обозначения контекста выполнения, который представляет собой все параметры, область видимости и это в пределах области видимости некоторого выполняющегося кода.
Это показано в ECMA-262 раздел 10.4.2:
Установить ThisBinding в то же значение, что и ThisBinding из вызывающего контекста выполнения
что явно указывает на то, что это является частью контекста выполнения.
Контекст выполнения предоставляет окружающую информацию, которая придает смысл выполняемому коду. Он включает гораздо больше информации, чем просто thisBinding.
Таким образом, значение this не является "контекстом", это всего лишь одна из частей контекста выполнения. По сути, это локальная переменная, которая может быть установлена вызовом на любой объект, а в строгом режиме - вообще на любое значение.
Вы должны знать о том, что "это" и сайта.
Как на мой взгляд можно реализовать "эта" в три стороны (Самовыдвижение/стрелка/функция bind метод)
Функция's этот сайта ведет себя немного по-разному в JavaScript по сравнению с другими языками.
Он также имеет некоторые отличия строгого режима, а не строгого режима.
В большинстве случаев, ценность эта определяется как функция называется.
Это может'т быть установлено задание в процессе выполнения, и он может быть разным каждый раз, когда функция вызывается.
В ES5, представил привязать() метод, чтобы установить значение функция's это независимо от того, как он's посетило,
и ES2015 введены функции стрелка, который Дон'т предоставить свои привязки (она сохраняет это значение включающего лексический контекст).
Метод Method1: самостоятельной используется для поддержания ссылку на оригинал, это даже как контекст меняется. Это'ы метод часто используется в обработчиках событий (особенно в укупорочных средств).
Ссылка : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function () {
alert(self.data);
});
}
Метода Method2: функция стрелка - стрелочной функции выражение является синтаксически компактная альтернатива регулярное выражение функции,
хотя и без его на это собственные привязки, доводы, супер, или новый.целевые ключевые слова.
Функция стрелка выражений плохо подходит в качестве методов, и они не могут быть использованы в качестве конструкторов.
Ссылка: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
function MyConstructor(data, transport) {
this.data = data;
transport.on('data',()=> {
alert(this.data);
});
}
Method3:привязка - привязки() метод создает новую функцию,которая
когда позвонил, его это ключевое слово задано указанное значение,
с заданной последовательностью аргументов, предшествующих любым при условии, когда новая функция называется.
Ссылка: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind
function MyConstructor(data, transport) {
this.data = data;
transport.on('data',(function() {
alert(this.data);
}).bind(this);
Во-первых, нужно иметь четкое представление о сфера
поведение " это " ключевое слово в контексте сфера
.
это
& сфера
:
there are two types of scope in javascript. They are :
1) Global Scope
2) Function Scope
короче, глобальный масштаб относится к объекту window.Переменные, объявленные в глобальной области видимости, доступны из любой точки мира.С другой стороны объем функция находится внутри функции.переменная, объявленная внутри функции не могут быть доступны, как правило, от внешнего мира.Это
сайта в глобальной области ссылается на объект window.это
внутри функции также относится к объекту window.Так что это всегда смотрите в окно, пока мы не найдем способ манипулировать "это", чтобы указать, в контексте нашего собственного выбора.
--------------------------------------------------------------------------------
- -
- Global Scope -
- ( globally "this" refers to window object) -
- -
- function outer_function(callback){ -
- -
- // outer function scope -
- // inside outer function"this" keyword refers to window object - -
- callback() // "this" inside callback also refers window object -
- } -
- -
- function callback_function(){ -
- -
- // function to be passed as callback -
- -
- // here "THIS" refers to window object also -
- -
- } -
- -
- outer_function(callback_function) -
- // invoke with callback -
--------------------------------------------------------------------------------
Различные способы манипулирования это
внутри функции обратного вызова:
Вот у меня есть функция конструктора под названием Человек. Он имеет свойство под названием " Имя " и четыре метода, называемого sayNameVersion1
,sayNameVersion2
,sayNameVersion3
,sayNameVersion4
. Все четыре из них одну конкретную задачу.Принять звонок и вызвать его.Обратный вызов имеет конкретную задачу, которая должна войти в свойства name экземпляра функции конструктора человека.
function Person(name){
this.name = name
this.sayNameVersion1 = function(callback){
callback.bind(this)()
}
this.sayNameVersion2 = function(callback){
callback()
}
this.sayNameVersion3 = function(callback){
callback.call(this)
}
this.sayNameVersion4 = function(callback){
callback.apply(this)
}
}
function niceCallback(){
// function to be used as callback
var parentObject = this
console.log(parentObject)
}
Теперь давайте's создавать экземпляр из человека конструктора и вызывать разных версий sayNameVersionX
( X относится к 1,2,3,4 ) способ с niceCallback
чтобы увидеть, как много способов, которыми мы можем манипулировать " это " внутри обратного вызова для обозначения "человек" экземпляра.
var p1 = new Person('zami') // create an instance of Person constructor
Что привязку сделать, это создать новую функцию с " это " набор ключевых слов к заданному значению.
sayNameVersion1
и sayNameVersion2
использовать привязку к манипуляции " это " функции обратного вызова.
this.sayNameVersion1 = function(callback){
callback.bind(this)()
}
this.sayNameVersion2 = function(callback){
callback()
}
первый привязать " это " с обратного вызова внутри самого метода.И для второго обратного вызова передается с объект привязан к нему.
p1.sayNameVersion1(niceCallback) // pass simply the callback and bind happens inside the sayNameVersion1 method
p1.sayNameVersion2(niceCallback.bind(p1)) // uses bind before passing callback
В первый аргумент
на "вызов" метод используется, как это внутри функции, которая вызывается с "вызов" прилагается к нему.
sayNameVersion3
использует "вызов" манипулировать " это " обратиться к человеку объекта, который мы создали, вместо объекта window.
this.sayNameVersion3 = function(callback){
callback.call(this)
}
и это называется следующим образом :
p1.sayNameVersion3(niceCallback)
Аналогично "вызов", первый аргумент "применить" относится к объекту, который будет обозначен " это " сайта.
sayNameVersion4
использует применить
манипулировать " это " обратиться к человеку объект
this.sayNameVersion4 = function(callback){
callback.apply(this)
}
и это называется, как следующие.Просто обратного вызова передается,
p1.sayNameVersion4(niceCallback)
Мы не можем привязать эту точку к функции setTimeout()
, так как он всегда выполняет с глобальный объект (окно), если вы хотите получить доступ к этой
связи в функции обратного вызова, а затем с помощью привязки () в функции обратного вызова мы можем добиться:
setTimeout(function(){
this.methodName();
}.bind(this), 2000);
Вопрос вращается вокруг того, как это
ключевое слово ведет себя в JavaScript. это
ведет себя по-разному, как показано ниже,
этот
, как правило, определяется функциями контекста выполнения.это
будет неопределено
как в строгом режиме, глобальный объект относится к "неопределенным" на месте "окна" объект.,
привязать ()и
применить()`это
— вместо Это
связан лексически (т. е. на основе исходного контекста)Как большинство ответов предложить, мы можем использовать функция Стрелка или `bind метод () или собственн ВАР. Я хотел бы процитировать пункт про лямбды (функции стрелы) из Руководство по стилю Гугле на JavaScript
предпочитают использовать кнопки со стрелками на.привязать(это), и особенно за аналоги.персонализация(Ф этого). Избежать написания слово const себя = это. Функции стрелок особенно полезны для обратных вызовов, которые иногда неожиданные дополнительные аргументы.
Google четко рекомендует использовать лямбда-выражения вместо привязки или константный себя = в`
Поэтому лучшим решением будет использовать лямбда-выражения, как показано ниже,
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => {
alert(this.data);
});
}
Ссылки:
В настоящее время существует и другой подход возможен, если классы используются в коде.
При поддержке полей это'можно сделать это следующим способом:
class someView {
onSomeInputKeyUp = (event) => {
console.log(this); // this refers to correct value
// ....
someInitMethod() {
//...
someInput.addEventListener('input', this.onSomeInputKeyUp)
Точно под капотом это's все старые добрые функции стрелки, которые связывают контекста, но в таком виде она выглядит гораздо более ясно, что прямой привязки.
Поскольку она's на этапе предложения 3 вам потребуется Бабеля и соответствующие плагин Бабель в процессе его, как сейчас(08/2018).
Другой подход, который *стандартным способом с дом2 чтобы связать " это " внутри слушателя события, что пусть у вас всегда удалить прослушиватель (среди прочих преимуществ), это handleEvent(evt) иметод
для EventListener` интерфейс:
var obj = {
handleEvent(e) {
// always true
console.log(this === obj);
}
};
document.body.addEventListener('click', obj);
Подробная информация об использовании handleEvent можно ознакомиться здесь: https://medium.com/@WebReflection/dom-handleevent-a-cross-platform-standard-since-year-2000-5bf17287fd38