前言
this指向也是面试当中必不可少的问题了,相信有些同学还是和我一样在面试中会被问倒。借此机会回顾一下this指向的相关知识,攻克这个难关,以后面试不会再被这个问题困扰。
this指向
首先我们要知道this绑定的五种方式
- new绑定
- 箭头函数绑定(本身没有,同上级作用域)
- 默认绑定
- 显示绑定(通过
call,apply、bind方法直接指定this) - 隐示绑定(当函数引用有上下文对象时, 如
obj.foo()的调用方式,foo内的this指向obj)
1. new绑定
通过new创建的构造函数this会指向构造函数本身,如果使用call之类更改了this指向,相应的实例this同样会更改
function Person (name, id) {
this.name = name
this.id = id
}
Person.prototype.foo = function () {
console.log(this.name, this.id);
return function () {
console.log(this);
}
}
window.name = 'xiaowang'
let p = new Person('xiaoli', 2)
let p2 = new Person('xiaozhao', 3)
p.foo()()
p.foo.call(p2)()
p.foo().call(p2)
答案
2. 箭头函数绑定
箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined。箭头函数作用域在创建的时候就已经决定了,不会改变。
也就是说箭头函数中的this是由外层作用域来决定的,同样的一个嵌套结构的箭头函数它的this实际是最外层的那个this,并不会因为层级深入改变。
Person.prototype.arrFoo = () => {
console.log(this.name, this.id);
return function () {
console.log(this);
}
}
p.arrFoo('arrow', 5)()
const obj1 = {
name: 'obj1',
foo: function () {
console.log(this.name)
return function () {
console.log(this.name)
}
}
}
const obj2 = {
name: 'obj2',
foo: function () {
console.log(this.name)
return () => {
console.log(this.name)
}
}
}
const obj3 = {
name: 'obj3',
foo: () => {
console.log(this.name)
return function () {
console.log(this.name)
}
}
}
const obj4 = {
name: 'obj4',
foo: () => {
console.log(this.name)
return () => {
console.log(this.name)
}
}
}
obj1.foo()()
obj2.foo()()
obj3.foo()()
obj4.foo()()
答案
obj1.foo()()输出了obj1和xiaowang 是因为obj1.foo()返回了一个匿名函数,它的执行环境是在全局下。obj2.foo()()则是因为return的箭头函数在创建时它的作用域就是obj2。
3. 默认绑定
在非严格模式下
this指向的是全局对象window,而在严格模式下会绑定到undefined,class中默认开启了严格模式。
var a = 1
function foo () {
var a = 2
function inner () {
console.log(this.a)
}
inner()
}
foo()
注意: 这里的答案是1并不是2,因为inner在函数内部调用,所以他的this指向实际还是window
4.显示绑定
通过
call()、apply()或者bind()方法直接指定this的绑定对象, 如foo.call(obj)。使用.call()或者.apply()的函数是会直接执行的;bind需要手动调用;call接收若干个参数,apply是一个数组。
var obj = {
a: 'obj',
foo: function () {
console.log('foo:', this.a)
return function () {
console.log('inner:', this.a)
}
}
}
var a = 'window'
var obj2 = { a: 'obj2' }
obj.foo()()
obj.foo.call(obj2)()
obj.foo().call(obj2)
答案
5. 隐式绑定
function foo () {
console.log(this.a)
};
function doFoo (fn) {
console.log(this)
fn()
}
var obj = { a: 1, foo };
var a = 2;
var obj2 = { a: 3, doFoo }
var foo2 = obj.foo;
obj.foo();
foo2();
obj2.doFoo(obj.foo)
答案
可能有同学问
foo2()为什么是2,这就涉及到隐式丢失的问题。因为foo2虽然等于obj.foo,但是最后调用实际是window,所以答案是2而不是1。doFoo中为什么也输出2呢?
因为把一个函数当成参数传递到另一个函数的时候,也会发生隐式丢失的问题,且与包裹着它的函数的this指向无关。在非严格模式下,会把该函数的this绑定到window上,严格模式下绑定到undefined。
结语
写的不好,有错误的地方还请各位大佬多多指教,感恩家人🙏