this指向相关,看这篇就够了

this指向一直是一个头疼的问题,这篇文章整理了 this 指向面试题相关的内容。

this 指向的绑定规则一共有 5 种

  • 默认绑定
  • 隐式绑定
  • 显示绑定
  • new 绑定
  • ES6 新增箭头函数绑定

1、默认绑定

函数独立调用,无其他绑定规则。 非严格模式下,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();

2、隐式绑定

满足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

3、显示绑定

通过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

4、new 绑定

使用new 构建函数分为以下几个步骤:

  1. 创建一个简单的空JavaScript对象
  2. 为新创建的对象添加__proto__ 属性,该属性链接构造函数的原型对象
  3. 将新创建的对象作为this 的上下文
  4. 如果该函数没有返回对象,则返回this

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

5、ES6 箭头函数绑定

箭头函数没有自己的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

希望以上内容对大家有帮助

~~旨在分享

  • 20
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值