У меня есть другое мнение о this
из других ответов, которое, я надеюсь, будет полезным.
Один из способов взглянуть на JavaScript - увидеть, что есть только 1 способ вызова функции 1 . Это
functionObject.call(objectForThis, arg0, arg1, arg2, ...);
Всегда предоставляется некоторое значение для objectForThis
.
Все остальное - синтаксический сахар для functionObject.call
Итак, все остальное можно описать тем, как оно переводится в functionObject.call
.
Если вы просто вызываете функцию, тогда this
- это «глобальный объект», который в браузере является окном
function foo() {
console.log(this);
}
foo(); // this is the window object
Другими словами,
foo();
был фактически переведен в
foo.call(window);
Обратите внимание, что если вы используете строгий режим, this
будет undefined
'use strict';
function foo() {
console.log(this);
}
foo(); // this is the window object
что означает
Другими словами,
foo();
был фактически переведен в
foo.call(undefined);
В JavaScript есть операторы типа +
и -
и *
. Существует также точка оператора, который .
Оператор .
при использовании с функцией справа и объектом слева фактически означает «передать объект как this
для работы.
Пример * * один тысяча шестьдесят-один
const bar = {
name: 'bar',
foo() {
console.log(this);
},
};
bar.foo(); // this is bar
Другими словами bar.foo()
переводится в const temp = bar.foo; temp.call(bar);
Обратите внимание, что не имеет значения, как была создана функция (в основном ...). Все это даст одинаковые результаты
const bar = {
name: 'bar',
fn1() { console.log(this); },
fn2: function() { console.log(this); },
fn3: otherFunction,
};
function otherFunction() { console.log(this) };
bar.fn1(); // this is bar
bar.fn2(); // this is bar
bar.fn3(); // this is bar
Опять же, это всего лишь синтаксический сахар для
{ const temp = bar.fn1; temp.call(bar); }
{ const temp = bar.fn2; temp.call(bar); }
{ const temp = bar.fn3; temp.call(bar); }
Еще одна морщина - это прототип цепи. При использовании a.b
JavaScript сначала ищет объект, на который ссылается a
, для свойства b
. Если b
не найден в объекте, тогда JavaScript будет искать в прототипе объекта, чтобы найти b
.
Существуют различные способы определения прототипа объекта, наиболее распространенным в 2019 году является ключевое слово class
. Для целей this
хотя это не имеет значения. Важно то, что, когда он смотрит в объекте a
свойство b
, если он находит свойство b
объекта или в цепочке его прототипов, если b
становится функцией, то применяются те же правила, что и выше. Ссылка на функцию b
будет вызываться с использованием метода call
и передачей a
как objectForThis, как показано в верхней части этого ответа.
Теперь. Давайте представим, что мы создаем функцию, которая явно устанавливает this
перед вызовом другой функции, а затем вызываем ее с помощью оператора .
(точка)
function foo() {
console.log(this);
}
function bar() {
const objectForThis = {name: 'moo'}
foo.call(objectForThis); // explicitly passing objectForThis
}
const obj = {
bar,
};
obj.bar();
После перевода использовать call
, obj.bar()
становится const temp = obj.bar; temp.call(obj);
. Когда мы входим в функцию bar
, мы вызываем foo
, но мы явно передаем другой объект для objectForThis, поэтому, когда мы достигаем foo this
, этот внутренний объект.
Это то, что эффективно выполняют функции bind
и =>
. Они более синтаксические сахара. Они эффективно создают новую невидимую функцию в точности как bar
выше, которая явно устанавливает this
, прежде чем она вызовет любую указанную функцию. В случае bind this
устанавливается на то, что вы передаете bind
.
function foo() {
console.log(this);
}
const bar = foo.bind({name: 'moo'});
// bind created a new invisible function that calls foo with the bound object.
bar();
// the objectForThis we are passing to bar here is ignored because
// the invisible function that bind created will call foo with with
// the object we bound above
bar.call({name: 'other'});
Обратите внимание, что если бы functionObject.bind
не существовало, мы могли бы сделать свое собственное, как это
function bind(fn, objectForThis) {
return function(...args) {
return fn.call(objectForthis, ...args);
};
}
и тогда мы могли бы назвать это так
function foo() {
console.log(this);
}
const bar = bind(foo, {name:'abc'});
Функции стрелок, оператор =>
являются синтаксическим сахаром для связывания
const a = () => {console.log(this)};
совпадает с
const tempFn = function() {console.log(this)};
const a = tempFn.bind(this);
Так же, как bind
, создается новая невидимая функция, которая вызывает данную функцию со связанным значением для objectForThis
, но в отличие от bind
объект, который должен быть связан, является неявным. Это то, что this
происходит, когда используется оператор =>
.
Итак, как и правила выше
const a = () => { console.log(this); } // this is the global object
'use strict';
const a = () => { console.log(this); } // this is undefined
function foo() {
return () => { console.log(this); }
}
const obj = {
foo,
};
const b = obj.foo();
b();
obj.foo()
переводится в const temp = obj.foo; temp.call(obj);
, что означает, что оператор стрелки внутри foo
свяжет obj
с новой невидимой функцией и вернет ту новую невидимую функцию, которая назначена b
. b()
будет работать как всегда, как b.call(window)
или b.call(undefined)
, вызывая новую невидимую функцию, созданную foo
. Эта невидимая функция игнорирует this
, переданные в нее, и передает obj
как objectForThis` в функцию стрелки.
Код выше переводится как
function foo() {
function tempFn() {
console.log(this);
}
return tempFn.bind(this);
}
const obj = {
foo,
};
const b = obj.foo();
b.call(window or undefined if strict mode);
1 apply
- другая функция, аналогичная call
functionName.apply(objectForThis, arrayOfArgs);
Но с ES6 концептуально вы можете даже перевести это в
functionName.call(objectForThis, ...arrayOfArgs);