this指向一直是一个头疼的问题,这篇文章整理了 this 指向面试题相关的内容。
this 指向的绑定规则一共有 5 种:
函数独立调用,无其他绑定规则。 非严格模式下,this指向window,严格模式下,this指向undefined。
1.1 非严格模式
var foo = 123;
function print(){
this.foo = 234;
console.log(this); // Window{...}
console.log(foo); // 234
}
print();
1.2 严格模式
"use strict";
var foo = 123;
function print(){
console.log('print this is ', this); // print this is undefined
console.log(window.foo); // Uncaught TypeError: Cannot read property 'foo' of undefined
console.log(this.foo); // 123
}
console.log('global this is ', this); // global this is Window{...}
print();
满足XXX.fn()格式,fn的this指向XXX。如果存在链式调用,this永远指向最后调用它的那个对象。
2.1 隐式绑定
var a = 1;
function foo() {
console.log(this.a);
}
// 对象简写,等同于 {a:2, foo: foo}
var obj = {a: 2, foo}
foo(); // 1
obj.foo(); // 2
2.2 隐式绑定的丢失
!!! 隐式绑定丢失后、this的指向会启动默认绑定。
隐式绑定丢失的两种情况:
(1)使用另一个变量作为函数别名,之后使用别名执行函数。
var a = 1
var obj = {
a: 2,
foo() {
console.log(this.a)
}
}
var foo = obj.foo;
obj.foo(); // 2
foo(); // 1
(2)将函数作为参数传递时。
function foo() {
console.log(this.a)
}
function doFoo(fn) {
console.log('print this is', this)
fn()
}
var obj = { a: 1, foo }
var a = 2
doFoo(obj.foo) // print this is Window{...} 2
通过call、apply、bind 函数改变this 指向,但是使用起来也有差别:
call()、apply()函数会立即执行,
bind()不会立即执行,会返回一个新函数,
call()、apply()的区别在于 call接收若干个参数、apply接收数组。
3.1 三种方式调用
function foo () {
console.log(this.a)
}
var obj = { a: 1 }
var a = 2
foo() // 2
foo.call(obj) // 1
foo.apply(obj) // 1
foo.bind(obj) // 不会立即执行,所以没有返回值
3.2 隐式绑定丢失可通过call、apply找回
function foo() {
console.log(this.a)
}
function doFoo(fn) {
console.log('print this is', this)
fn.call(this)
}
var obj = { a: 1, foo }
var a = 2
doFoo(obj.foo) // print this is {a: 1, foo} 1
3.3 注意call位置
function foo () {
console.log(this.a)
}
var obj = { a: 1 }
var a = 2
foo() // 默认绑定 2
foo.call(obj) // 显式绑定 1
foo().call(obj) // foo()函数执行 显式绑定,打印1, 对foo()执行的返回值执行call,foo返回值为undefined,执行call()会报错Uncaught TypeError: Cannot read property 'call' of undefined
3.4 外层this与内层this
function foo () {
console.log(this.a)
return function() {
console.log(this.a)
}
}
var obj = { a: 1 }
var a = 2
foo.call(obj)() // 1 2
// foo.call(obj): 第一层函数foo通过call将this指向obj,打印1;第二层函数为匿名函数,默认绑定,打印2。
3.5 显式绑定扩展
(1)apply 数组求最大值、最小值
const arr = [1,11,33]
Math.max.apply(Math, arr)
Math.min.apply(Math, arr)
(2)类数组转为数组,采用Array.prototype.slice.call(arguments)或[].slice.call(arguments)将类数组转化为数组。
(3)数组高阶函数,我们会经常用到forEach、map等,但这些数组高阶方法,它们还有第二个参数thisArg,每一个回调函数都是显式绑定在thisArg上的。
const obj = {a: 2}
const arr = [1, 2]
arr.forEach(function (val, key){
console.log(`${key}: ${val} --- ${this.a}`)
}, obj)
// 0:1 --- 2
// 1:2 --- 2
使用new 构建函数分为以下几个步骤:
4.1 new 绑定
function User(name, age) {
this.name = name;
this.age = age;
}
var name = 'Tonny';
var age = 12;
var zc = new User('Tom', 18);
console.log(zc.name) // Tom
箭头函数没有自己的this,所以它查找到外层的this 值作为自己的 this值,且指向函数定义时的this而非执行时。
箭头函数不能用作构造函数;没有arguments 对象,要用rest参数;没有原型对象prototype;不能使用call、apply、bind改变this指向。
name = 'Jonny'
const obj = {
name: 'Tom',
intro: () => {
console.log('My name is ' + this.name)
}
}
obj.intro() // Jonny
最后:综合题
var name = 'window'
var user1 = {
name: 'user1',
foo1: function () {
console.log(this.name)
},
foo2: () => console.log(this.name),
foo3: function () {
return function () {
console.log(this.name)
}
},
foo4: function () {
return () => {
console.log(this.name)
}
}
}
var user2 = { name: 'user2' }
user1.foo1()
user1.foo1.call(user2)
user1.foo2()
user1.foo2.call(user2)
user1.foo3()()
user1.foo3.call(user2)()
user1.foo3().call(user2)
user1.foo4()()
user1.foo4.call(user2)()
user1.foo4().call(user2)
综合题答案分析:
user1.foo1() 隐式绑定 user1
user1.foo1.call(user2) 显示绑定 user2
user1.foo2() 箭头函数 window
user1.foo2.call(user2) 箭头函数call 改变无效 window
user1.foo3()() 默认绑定 window
user1.foo3.call(user2)() 第一层user1.foo3.call(user2)使用call将user1.foo3的this指向user2,第二层匿名函数默认绑定,打印 window
user1.foo3().call(user2) 第一层 user1.foo3() 隐式绑定,第二层匿名函数使用call将this指向user2,打印 user2
user1.foo4()() 第一层 user1.foo3() 隐式绑定,第二层匿名函数 默认绑定,打印 user1
user1.foo4.call(user2)() 第一层user1.foo4.call(user2)使用call将user1.foo4的this指向user2,第二层箭头函数 向上查找,打印 user2
user1.foo4().call(user2) 第一层 user1.foo4() 隐式绑定,第二层箭头函数使用call将this指向user2无效 向上查找,打印 user1
希望以上内容对大家有帮助
~~旨在分享