JavaScript 中的 this 到底指向谁? 今天彻底把它征服!!!

概述

我们知道,JS 中的 this 指向问题,一直是一个经久不衰的重点和考点。那么它到底难不难呢?怎么说呢?它也难也不难,你要是不把它理清楚,随意变化一下就能把你绊倒;但是你要是把他理清楚了以后,无论它如何变换也难不倒你!那这个 this 里面到底有什么道道呢?今天我们就一起把它彻底征服!

this 到底指向谁之 this 绑定规则

咱们不弯弯绕绕了,直接开宗明义,this 的绑定规则一共有四种,如下

  1. 默认绑定
  2. 隐式绑定
  3. 显式绑定
  4. new 绑定

那这 4 种绑定分别是什么意思呢?文字略显苍白,接下来我们直接通过代码来看一下这 4 种绑定是什么样的情况,直接明了!

测试代码,后文用于说明这 4 种绑定方式的测试都基于本段代码

function Foo() {
  console.log(this)
}

var obj1 = {
  name: 'obj1',
  age: 13, 
  foo: Foo
}
var obj = {
  name: 'obj',
  obj1: obj1
}

function Person(name) {
  console.log(this) 
  this.name = name 
}

1. 默认绑定:指向window,严格模式下指向undefined

Foo() // window

2. 隐式绑定,指向调用对象(不管多少层调用,始终指向最后发起调用的对象,也就是最后一层)

obj1.foo()  // obj1
obj.obj1.foo()  // 指向 obj1 ,而不是 obj

3. new 绑定  :指向构造函数

var p = new Person()	// 16行输出 Person {}

4. 显式绑定 — call/apply/bind:绑定谁就是谁

Foo.call(obj) // obj
Foo.apply(obj1) // obj1
Foo.bind(obj)() // obj

绑定的优先级比较:默认绑定 < 隐式绑定 < apply/call < bind < new

// I)   new > bind
var bindFn = Foo.bind('abc')
new bindFn()  // 打印的是Foo{} 而不是 String:{ 'abc' },说明new > bind
// II)  bind > apply/call
bindFn.call(123)  // String:{ 'abc' } 而不是 Number { 123 },说明 bind > call 
// III) apply/call > 隐式绑定
obj1.foo.apply(111) // Number { 111 } 而不是 ojb1, 说明 apply/call > 隐式绑定
// IV)  隐式 > 默认 
obj1.foo() // ojb1 而不是 window ,说明 隐式 > 默认

this规则之外的特殊情况

var obj2 = {
  name: 'obj2'
}

1. 显式绑定的this传入null/undefined,使用默认绑定规则(非严格模式下)

Foo.call(null) 	// window

2. 间接函数引用,采用默认规则

(obj1.foo = obj2.foo)() // window

3. 要注意的是,在严格模式中,null 就是 null,undefined 就是 undefined

'use strict'
function Foo() {
  console.log(this)
}
Foo(null) // null
Foo(undefined) // undefined

箭头函数的用法和规则

1. 不会绑定this,arguments等属性

2. 不能作为构造函数来使用,因为其没有原型

3. 箭头函数没有自己的this,内部的this需按照作用域链一步步的向上查找

this指向练习

1. 例1

var name = 'window'
var person1 = {
  name: 'person1',
  foo1: function () {
    console.log(this.name)
  },
  foo2: () => console.log(this.name),
  // 注意这里有两个 this 要分析
  foo3: function () { 
    return function () { 
      console.log(this.name)
    }
  },
  // 这里也有两个 this 要分析(虽然只存在一个)
  foo4: function () {
    return () => {
      console.log(this.name)
    }
  }
}

var person2 = { name: 'person2' }

person1.foo1(); // person1
person1.foo1.call(person2); // person2

person1.foo2(); // window
person1.foo2.call(person2); // window

person1.foo3()(); // window
person1.foo3.call(person2)(); // window
person1.foo3().call(person2); // person2

person1.foo4()(); // person1
person1.foo4.call(person2)(); // person2
person1.foo4().call(person2); // person1

分析

  • 注意函数调用时,是普通函数还是箭头函数,是独立调用还是其他
  • person1.foo1() 隐式绑定指向 person1 ;
  • person1.foo1().call(person2) 显式绑定指向 person2;
  • person1.foo2() 箭头函数无自己的 this,到上层作用域查找指向 window;
  • person1.foo2.call(person2) 同上,注意,箭头函数无自己的 this,所以绑定无效!
  • person1.foo3()() 回调函数独立调用指向 window
  • person1.foo3.call(person2)() 同上
  • person1.foo3().call(person2) 显式绑定指向 person2
  • person1.foo4()() 箭头函数无自己的 this,到上层作用域查找指向 person1
  • person1.foo4.call(person2)() 箭头函数无自己的 this,到上层作用域查找指向 person2
  • person1.foo4().call(person2) 箭头函数绑定无效,到上层作用域查找指向 person1

2. 例2

var name = 'window'
function Person (name) {
  this.name = name
  this.foo1 = function () {
    console.log(this.name)
  },
  this.foo2 = () => console.log(this.name),
  this.foo3 = function () {
    return function () {
      console.log(this.name)
    }
  },
  this.foo4 = function () {
    return () => {
      console.log(this.name)
    }
  }
}
var person1 = new Person('person1')
var person2 = new Person('person2')

person1.foo1() // person1
person1.foo1.call(person2) // person2

person1.foo2() // person1
person1.foo2.call(person2) // person1

person1.foo3()() // window
person1.foo3.call(person2)() // window
person1.foo3().call(person2) // person2

person1.foo4()() // person1
person1.foo4.call(person2)() // person2
person1.foo4().call(person2) // person1

分析

  • person1.foo1() 隐式绑定指向 person1
  • person1.foo1.call(person2) 显式绑定指向 person2
  • person1.foo2() 上层作用域中查找指向 person1
  • person1.foo2.call(person2) 同上指向 person1
  • person1.foo3()() 默认绑定指向 window
  • person1.foo3.call(person2)() 同上指向 window
  • person1.foo3().call(person2) 显式绑定指向 person2
  • person1.foo4()() 上层作用域中查找指向 person1
  • person1.foo4.call(person2)() 上层作用域中查找指向 person2
  • person1.foo4().call(person2) 上层作用域中查找指向 person1

3. 例3

var name = 'window'
function Person (name) {
  this.name = name
  this.obj = {
    name: 'obj',
    foo1: function () {
      return function () {
        console.log(this.name)
      }
    },
    foo2: function () {
      return () => {
        console.log(this.name)
      }
    }
  }
}
var person1 = new Person('person1')
var person2 = new Person('person2')

person1.obj.foo1()() // window
person1.obj.foo1.call(person2)() // window
person1.obj.foo1().call(person2) // person2

person1.obj.foo2()() // obj
person1.obj.foo2.call(person2)() // person2
person1.obj.foo2().call(person2) // obj

分析

  • person1.obj.foo1()() 默认绑定指向window
  • person1.obj.foo1.call(person2)() 同上指向 window
  • person1.obj.foo1().call(person2) 显式绑定指向 person2
  • person1.obj.foo2()() 上层作用域中查找指向 obj
  • person1.obj.foo2.call(person2)() 上层作用域中查找指向 person2
  • person1.obj.foo2().call(person2) 上层作用域中查找指向 obj

4. 例4

var length = 10
function fn() {
  console.log(this.length)
}

var obj = {
  length: 5,
  method: function(fn) {
    fn()
    arguments[0]()
  }
}

obj.method(fn, 1)

// 10
// 2
  • 分析:第一次执行fn(),this指向window对象,输出10。第二次执行arguments[0],相当于arguments调用方法,this指向arguments,而这里传了两个参数,故输出arguments长度为2。

总结

本篇文章详细讲述了 JS 中 this 的指向问题,相信大家只要仔细阅读,并将例题琢磨透,以后再遇到 this 指向的问题一定会所向披靡,战无不胜!!亦菲,彦祖们,我们下次见~

  • 13
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值