Это еще один классический вопрос на собеседовании~/(ㄒoㄒ)/~~ также является одной из многих ям в ES 5. В ES 6 ошибок, вызванных этим, можно значительно избежать, но для обслуживания некоторых старых кодов это лучше всего узнать о разнице между этим указанием и вызовом, применением и привязкой.
Эта статья была впервые опубликована на моем личном сайте:cherryblog.site/
указатель на это
Фактически, в ES5 смысл этого всегда придерживается принципа:это всегда указывает на объект, который последний раз вызывал его, давай, трижды прочитай со мной:это всегда указывает на объект, который вызвал его последним, это всегда указывает на объект, который вызвал его последним, это всегда указывает на объект, который вызвал его последним. Запомните это предложение, это вы уже поняли половину.
Давайте рассмотрим самый простой пример:
пример 1:
var name = "windowsName";
function a() {
var name = "Cherry";
console.log(this.name); // windowsName
console.log("inner:" + this); // inner: Window
}
a();
console.log("outer:" + this) // outer: Window
Я думаю, все знают, почему в журнале имя окна, потому что, согласно предложению только что "это всегда указывает на объект, который последний раз вызывал его", смотрим на последний звонокa
Местоa();
, объект, который не вызывался ранее, является глобальным окном объекта, что эквивалентноwindow.a()
; Обратите внимание, что здесь мы не используем строгий режим, если бы использовался строгий режим, глобальный объект был быundefined
, то сообщит об ошибкеUncaught TypeError: Cannot read property 'name' of undefined
.
Посмотрите еще раз на этот пример:
Пример 2:
var name = "windowsName";
var a = {
name: "Cherry",
fn : function () {
console.log(this.name); // Cherry
}
}
a.fn();
В этом примере функция fn вызывается объектом a, поэтому напечатанное значение является значением имени в a. Стало немного яснее?
Делаем небольшое изменение:
Пример 3:
var name = "windowsName";
var a = {
name: "Cherry",
fn : function () {
console.log(this.name); // Cherry
}
}
window.a.fn();
Причина, по которой здесь напечатана Cherry, также связана с предложением только что "это всегда указывает на объект, который последний раз вызывал его", объект, который, наконец, вызвал его, по-прежнему является объектом a.
Давайте еще раз посмотрим на этот пример:
Пример 4:
var name = "windowsName";
var a = {
// name: "Cherry",
fn : function () {
console.log(this.name); // undefined
}
}
window.a.fn();
зачем печатать здесьundefined
Шерстяная ткань? Это связано с тем, что, как только что было сказано, объект а вызывает fn, то есть внутреннее this объекта fn является объектом а, а имя не определено в объекте а, поэтому журналthis.name
Значениеundefined
.
Этот пример по-прежнему иллюстрирует:это всегда указывает на объект, который последний раз вызывал его, потому что объект, который вызывает fn последним, является a, поэтому, даже если в a нет атрибута имени, он не будет продолжать поиск предыдущего объекта.this.name
, а напрямую выводитьundefined
.
Давайте посмотрим на более грубый пример:
Пример 5:
var name = "windowsName";
var a = {
name : null,
// name: "Cherry",
fn : function () {
console.log(this.name); // windowsName
}
}
var f = a.fn;
f();
Здесь у вас могут возникнуть вопросы, почему бы и нетCherry
, это потому, что хотя метод fn объекта a присваивается переменной f, он не вызывается, а потом прочитайте мне это предложение: "это всегда указывает на объект, который последний раз вызывал его", так как ф только сейчас не вызывали, так чтоfn()
В конце концов, он все еще вызывается окном. Итак, это указывает на окно.
Из приведенных выше пяти примеров видно, что точка this не определяется при создании, в es5 она всегдаэто всегда указывает на объект, который последний раз вызывал его.
Давайте посмотрим на другой пример:
Пример 6:
var name = "windowsName";
function fn() {
var name = 'Cherry';
innerFunction();
function innerFunction() {
console.log(this.name); // windowsName
}
}
fn()
Прочитав его, вы должны понять, почему (о゚▽゚)о.
Как изменить точку этого
Есть несколько способов изменить точку:
- Использование стрелочных функций ES6
- используется внутри функции
_this = this
- использовать
apply
,call
,bind
- new создает экземпляр объекта
Пример 7:
var name = "windowsName";
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
},100);
}
};
a.func2() // this.func1 is not a function
В случае неиспользования стрелочных функций будет сообщено об ошибке, т.к. последний вызовsetTimeout
Объектом является окно, но в окне нет функции func1.
Мы будем использовать этот пример в качестве демонстрации в разделе Изменить это, чтобы указать на.
стрелочная функция
Как мы все знаем, стрелочные функции ES6 могут избежать ловушки использования этого в ES5.this в стрелочной функции всегда ссылается на this, когда функция определена, а не когда она выполняется., стрелочные функции должны помнить это предложение: «В стрелочных функциях нет привязки this, и ее значение должно быть определено путем поиска в цепочке областей видимости. Если стрелочная функция содержится в нестрелочной функции, это привязывается к ближайший слой нестрелочных функций. this функции, в противном случае это не определено».
Пример 8:
var name = "windowsName";
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( () => {
this.func1()
},100);
}
};
a.func2() // Cherry
используется внутри функции_this = this
Если вы не используете ES6, то этот способ должен быть самым простым и безошибочным, сначала мы сохраняем объект, который вызывает эту функцию, в переменную_this
а затем используйте это как в функции_this
,так_this
не изменится.
Пример 9:
var name = "windowsName";
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
var _this = this;
setTimeout( function() {
_this.func1()
},100);
}
};
a.func2() // Cherry
В этом примере в func2 сначала установитеvar _this = this;
,здесьthis
это вызовfunc2
объект а, чтобы предотвратитьfunc2
Это в setTimeout является окном, потому что setTimeout в setTimeout вызывается окном. мы будемthis(指向变量 a)
присвоить переменной_this
, так что вfunc2
в мы используем_this
состоит в том, чтобы указать на объект a.
Используйте применить, позвонить, связать
Использование функций apply, call и bind также может изменить эту ситуацию.Принцип будет рассмотрен позже.Давайте посмотрим, как он реализован:
использовать применять
Пример 10:
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
}.apply(a),100);
}
};
a.func2() // Cherry
использовать вызов
Пример 11:
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
}.call(a),100);
}
};
a.func2() // Cherry
использовать привязку
Пример 12:
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
}.bind(a)(),100);
}
};
a.func2() // Cherry
Разница между применением, вызовом и привязкой
Мы только что представили, что применение, вызов и связывание могут изменить суть этого, но эти три функции немного отличаются.
существуетMDNОпределение применения выглядит следующим образом;
Метод apply() вызывает функцию с указанным значением this и аргументами, представленными в виде массива (или объекта, подобного массиву).
грамматика:
fun.apply(thisArg, [argsArray])
- thisArg: значение this, указанное при запуске функции fun. Следует отметить, что указанное значение this не обязательно является реальным значением this при выполнении функции.Если функция находится в нестрогом режиме, когда она указана как null или undefined, она автоматически укажет на глобальный объект ( объект окна в браузере). ), а значение this, которое является примитивным значением (число, строка, логическое значение), будет указывать на автоматический объект-оболочку для примитивного значения.
- argsArray: Массив или подобный массиву объект, где элементы массива будут переданы функции fun в качестве отдельных аргументов. Если значение этого параметра равно null или не определено, это означает, что параметры не нужно передавать. Массивоподобные объекты доступны начиная с ECMAScript 5. Информацию о совместимости браузеров см. в нижней части этой статьи.
Разница между применением и вызовом
Фактически, apply и call в основном похожи, единственная разница между ними заключается в передаваемых параметрах.
Синтаксис вызова:
fun.call(thisArg[, arg1[, arg2[, ...]]])
Таким образом, разница между apply и call заключается в том, что метод call принимает несколько списков параметров, а apply принимает массив, содержащий несколько параметров.
Пример 13:
var a ={
name : "Cherry",
fn : function (a,b) {
console.log( a + b)
}
}
var b = a.fn;
b.apply(a,[1,2]) // 3
Пример 14:
var a ={
name : "Cherry",
fn : function (a,b) {
console.log( a + b)
}
}
var b = a.fn;
b.call(a,1,2) // 3
Разница между связыванием, применением и вызовом
Давайте попробуем только что пример, используя bind
var a ={
name : "Cherry",
fn : function (a,b) {
console.log( a + b)
}
}
var b = a.fn;
b.bind(a,1,2)
Обнаружим, что выхода нет, почему так, давайте разберемсяMDNВ документации на него указано:
Метод bind() создает новую функцию, которая при вызове устанавливает ключевое слово this в предоставленное значение, а при вызове новой функции предоставляет заданную последовательность аргументов перед любым предоставленным.
Итак, мы видим, что bind — это создание новой функции, мы должны вызывать ее вручную:
var a ={
name : "Cherry",
fn : function (a,b) {
console.log( a + b)
}
}
var b = a.fn;
b.bind(a,1,2)() // 3
==================================== Обновление ============= ===================
Вызовы функций в JS
Увидел сообщение, что многие детские боты не понимают, почему innerFunction в примере 6 и this в примере 7 указывают на окно, поэтому добавлю вызовы функций в JS.
Пример 6:
var name = "windowsName";
function fn() {
var name = 'Cherry';
innerFunction();
function innerFunction() {
console.log(this.name); // windowsName
}
}
fn()
Пример 7:
var name = "windowsName";
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
},100);
}
};
a.func2() // this.func1 is not a function
Есть 4 способа вызвать функцию
- как вызов функции
- функция как вызов метода
- Используйте вызовы функций конструктора
- Вызов функции как метода функции (вызов, применение)
как вызов функции
Например, пример 1 выше:
пример 1:
var name = "windowsName";
function a() {
var name = "Cherry";
console.log(this.name); // windowsName
console.log("inner:" + this); // inner: Window
}
a();
console.log("outer:" + this) // outer: Window
Такая простейшая функция не принадлежит никакому объекту, но является функцией, при этом в нестрогом режиме JavaScript в браузере она по умолчанию принадлежит окну глобального объекта, а в строгом режиме не определена.
Но это глобальная функция, которая склонна к конфликтам имен, поэтому не рекомендуется использовать ее таким образом.
функция как вызов метода
Так что лучше использовать функции как методы объектов. Например пример 2:
Пример 2:
var name = "windowsName";
var a = {
name: "Cherry",
fn : function () {
console.log(this.name); // Cherry
}
}
a.fn();
определить объект здесьa
, объектa
Есть недвижимость(name
) и метод (fn
).
затем возразитеa
пройти через.
метод вызывает в нем метод fn.
И тогда мы продолжаем вспоминать фразу "это всегда указывает на объект, который последний раз вызывал его”, так что this в fn указывает на a.
Вызов функции с помощью конструктора
Если новое ключевое слово используется перед вызовом функции, вызывается конструктор.
Это выглядит так, как будто создается новая функция, но на самом деле функции JavaScript — это воссозданные объекты:
// 构造函数:
function myFunction(arg1, arg2) {
this.firstName = arg1;
this.lastName = arg2;
}
// This creates a new object
var a = new myFunction("Li","Cherry");
a.lastName; // 返回 "Cherry"
Это должен сказать еще один классический вопрос интервью: новый процесс, (ಥ_ಥ)
Вот простой взгляд на новый процесс:
Псевдокод означает:
var a = new myFunction("Li","Cherry");
new myFunction{
var obj = {};
obj.__proto__ = myFunction.prototype;
var result = myFunction.call(obj,"Li","Cherry");
return typeof result === 'obj'? result : obj;
}
- создать пустой объект obj;
- Укажите неявный прототип только что созданного пустого объекта на явный прототип его конструктора.
- используйте вызов, чтобы изменить указатель этого
- Если возвращаемое значение отсутствует или возвращается значение, не являющееся объектом, obj возвращается как новый объект; если возвращаемое значение является новым объектом, объект возвращается напрямую.
Итак, мы видим, что в процессе new мы используем call, чтобы изменить точку this.
Вызов функции как метода функции
В JavaScript функции являются объектами.
Функция JavaScript имеет свои свойства и методы.
call() и apply() являются предопределенными функциональными методами. Для вызова функции можно использовать два метода, первым параметром обоих методов должен быть сам объектВ строгом режиме JavaScript при вызове функции первый аргумент становится значением this , даже если аргумент не является объектом.
В нестрогом режиме JavaScript, если значение первого параметра равно null или не определено, вместо этого будет использоваться глобальный объект.
Теперь давайте посмотрим на пример 6:
Пример 6:
var name = "windowsName";
function fn() {
var name = 'Cherry';
innerFunction();
function innerFunction() {
console.log(this.name); // windowsName
}
}
fn()
Относится ли здесь вызов innerFunction() к первому вызывающему методу: как к вызову функции (она вызывается как функция, не смонтированная ни на каком объекте, поэтому для функций, не смонтированных ни на каком объекте, в нестрогом режиме это относится к окну)
Затем снова посмотрите на пример 7:
Пример 7:
var name = "windowsName";
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
},100 );
}
};
a.func2() // this.func1 is not a function
Это простое понимание можно понять как «this анонимной функции всегда указывает на окно', вы можете думать об этом таким образом, или это предложениеэто всегда указывает на объект, который последний раз вызывал его, то мы найдем объект, который, наконец, вызывает анонимную функцию, что очень смущает, потому что имя анонимной функции смеется и плачет, поэтому у нас нет возможности вызывать анонимную функцию другими объектами. Таким образом, this анонимной функции всегда указывает на окно.
Если вы хотите сейчас спросить, как определяются анонимные функции?Во-первых, анонимные функции, которые мы обычно пишем, являются самовыполняющимися, то есть добавляют анонимную функцию после анонимной функции.()
Пусть исполняется сама. Во-вторых, хотя анонимные функции не могут вызываться другими объектами, они могут вызываться другими функциями, такими как setTimeout в примере 7.