JS this的指向

参考优质博文:嗨,你真的懂this吗?

每次看到this都会痛苦面具。

首先我们想一下this的含义,我觉得应该是:本。
英文含义本身是:这个。
在不手动修改的前提下,基本遵循这个意思。

要想知道this指向何方,我们要先了解四种this的绑定:

  • 默认绑定
  • 隐式绑定
  • 显示绑定
  • new绑定

以下问题的前提是在浏览器环境下:

  1. 默认绑定
    最简单的情况,看一下代码
var name = 'snowt'

function sayName() {
  console.log(this.name)
  console.log(this)
}

sayName()
// snowt
// window

一般来说,函数独立调用,非严格模式下,this指向全局对象(浏览器中是windownode中是global),严格模式下,this指向undefined
如果是node环境下,会打印出undefinedglobal,因为全局变量并非挂载在global对象上

  1. 隐式绑定
    情况一般是一个具体的对象调用某个方法,即调用位置存在上下文对象
var name = 'snowt'

function sayName() {
  console.log(this.name)
  console.log(this) 
}

var person = {
  name: 'meiko',
  say: sayName
}

person.say()
// meiko
// { name: 'meiko', say: [Function: sayName] }

sayNamethis在调用时指向了person,没有明显地在代码中表示,但实际上this改绑了。
但是一般来说,被调用方法的this只会被绑定给最接近他的一层对象,看下面代码:

var name = 'snowt'

function sayName() {
  console.log(this.name)
  console.log(this)
}

var person = {
  name: 'meiko',
  say: sayName
}

var lover = {
  name: 'nana',
  friend: person
}

lover.friend.say()
// meiko
// { name: 'meiko', say: [Function: sayName] }

这里的函数调用跟lover没有半点关系,最终输出的还是personname
所以我们只关注最后一层。

  • 绑定丢失问题
    如果一个方法没有直接执行,而是赋值给了另一个变量,那么不管它之前有多少爹爹妈妈把他点出来,都不再好使,不再是隐式绑定。代码:
var name = 'snowt'

function sayName() {
  console.log(this.name)
  console.log(this)
}

var person = {
  name: 'meiko',
  say: sayName
}

var call = person.say
call()
// snowt
// window

这里变成了默认绑定,输出了snowt,跟person没有关系

另一种情况也是绑定丢失:

var name = 'snowt'

function sayName() {
  console.log(this.name)
  // console.log(this)
}

var person = {
  name: 'meiko',
  say: function () {
    setTimeout(function () {
      console.log(this.name);
    });
  }
}

var person2 = {
  name: 'tom',
  say: sayName,
}

person.say(); // snowt
setTimeout(person2.say, 100); // snowt
setTimeout(function () {
  person2.say() // tom
}, 200);

第一行输出snowt是因为setTimeout回调函数中,在非严格模式下,this执行默认绑定,指向全局对象window
第二行是因为相当于将person2.say赋值给了一个新变量,然后执行这个变量,这个this自然就和person2没关系了
第三行是因为进行了正常的隐式绑定,this指向person2

  1. 显示绑定
    显示绑定就是通过callapplybindthis进行改绑
var name = 'snowt'

function sayName() {
  console.log(this.name)
}

var person = {
  name: 'meiko',
  say: sayName
}

var say = person.say;

say.call(person) // meiko

硬绑定啦,this指向person
但实际上还是会出现绑定丢失的问题:

var name = 'snowt'

function sayName() {
  console.log(this.name)
}

var person = {
  name: 'meiko',
  say: sayName
}

var exer = function (fn) {
  fn()
}

exer.call(person, person.say) // snowt

传入的是person.say但输出的是全局对象的name,本质上还是那个问题,person.say相当于赋值给了一个新的变量,然后执行(没有涉及到this),自然就和person没关系了
解决方法就是fn执行的时候也进行显示绑定:

var name = 'snowt'

function sayName() {
  console.log(this.name)
}

var person = {
  name: 'meiko',
  say: sayName
}

var exer = function (fn) {
  fn.call(this)
}
exer.call(person, person.say) // meiko

不要让前面的硬绑定白干,fn.call(this)这里我们拿到的this指向person,直接拿来给fn绑对象即可

  1. new绑定
    new实际上把构造函数的this指向了创建的实例
function sayName(name) {
  this.name = name;
}

var say = new sayName('tom')

console.log(say.name); // tom
  1. 绑定优先级
    new绑定 > 显示绑定 > 隐式绑定 > 默认绑定

  2. 例外
    callbindapply传入的第一个参数是nullundefined时,会被忽略,然后执行默认绑定

function sayName() {
  console.log(this.name);
}

var name = 'jerry'

var obj = {
  name: 'meiko'
}

sayName.call(null) // jerry

this指向了全局对象,执行了默认绑定。

箭头函数比较特殊 下一篇整理好了。

留一个练习:

var number = 5;
var obj = {
    number: 3,
    fn: (function () {
        var number;
        this.number *= 2;
        number = number * 2;
        number = 3;
        return function () {
            var num = this.number;
            this.number *= 2;
            console.log(num);
            number *= 3;
            console.log(number);
        }
    })()
}
var myFun = obj.fn;
myFun.call(null);
obj.fn();
console.log(window.number);
// 10
// 9

// 3
// 27

// 20

涉及到闭包和立即执行函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值