那些年,被 this 折磨的前端小白


在刚开始学 JavaScript 时,最深恶痛绝的三个 JS怪👹:this妖原型精AJAX魔

最近在写总结回顾时竟意外发现这三大怪早已在不知不觉中被我消化吸收了,趁着热乎记下来✍~~~

有请今天的出场嘉宾🎤:this妖

this妖 的前世今生

假设现在有一对象🐘🐘

var person = {
    name: 'April-cl',
    age: 18,
    sayHi: function(){
      // 待补充
    }
}

我想要调用 person.sayHi(…),打印出「你好,我是 April-cl,今年 18 岁」

emmmmmmmmm 我要怎样写 sayHi 呢,这样吗?

sayHi: function(name, age){
    console.log('你好,我是 ${name},今年 ${age} 岁')
}

调用方式:person.sayHi(person.name, person.age)

这似乎可以,但是…看起来傻傻的😳

那要不,给 sayHi 传入一个对象?代码改成这样:

sayHi: function(self){
    console.log('你好,我是 ${self.name},今年 ${self.age} 岁')
}

调用方式:person.sayHi(person)

心里嘀嘀咕:可我就想把参数里的 person 省掉

💡 有了,给参数里的 person 穿上隐身衣。可是没有了实参,sayHi 声明的形参怎么办?

好办,给 self 也穿上隐身衣,用一个驻守的小妖精 this 来代替:

sayHi: function(){
    // this 就是 self
    console.log('你好,我是 ${this.name},今年 ${this.age} 岁')
}

调用方式:person.sayHi()

现在看着明了了,thissayHi 隐藏的第一个形参。在调用 person.sayHi() 时,这个 person 会「变成」 this

令小白闻风丧胆的 this妖 现身啦

俗话说知彼知己方能百战百胜,那就先要了解 this 的5种绑定方式

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

默认绑定

前置知识(系统丢给了你一本秘籍 📓 ):

在全局块级作用域

  • 所有 JavaScript 全局对象、函数以及变量均自动成为 window 对象的成员
  • 非严格模式下 this 指向 window
  • 用 let 或 const 声明不会被绑定到 window 上

好了,接下来打怪升级 ⬆

var a = 1
let b = 2
function foo () {
  console.log('foo a:' + this.a)
  console.log('foo b:' + this.b)
}
console.log('this a:' + this.a)
console.log('this b:' + this.b)
foo()

根据秘籍

afoo 变量会被绑定在 window 上,而 b 则不会(即 window.bundefined),而 this 指向 window,所以答案是

this a:1
this b:undefined
foo a:1
foo b:undefined

隐式绑定

回顾上文『this妖 的前世今生』,this 隐式绑定的对象其实就是穿了隐身衣的 person

✨ Get 新秘籍:当函数引用有上下文对象时, 如 obj.foo() 的调用方式, foo 内的 this 指向 obj

但是江湖险恶,若是有人披着层「马甲」诓骗 this妖,就会造成 this妖 和穿着隐身衣的正主形同陌路,请看题:

function foo () {
  console.log(this.a)
};
var obj = { a: 1, foo };
var a = 2;
var foo2 = obj.foo;
obj.foo();
foo2();

打开照妖镜(控制台),会发现此时结果

1
2

???

不难理解 obj.foo() 的结果是 1,但 foo2() 居然打印出的是window 下的 a 😮

这便是这层「马甲」的厉害之处了:使用另一个变量来给函数取别名造成丢失绑定对象(秘籍升级 ⬆︎ )

显式绑定

嘿嘿 😏 你有张良计,我有过墙梯 ~

拿出哆啦A梦的道具『非生物催眠喇叭📢』: call apply bind

function foo () {
  console.log(this.a)
};
var obj = { a: 1, foo };
var a = 2;
var foo2 = obj.foo;
obj.foo();
foo2();
foo2.call(obj)      // 上面代码与隐式绑定示例一致,新增此行代码

当当当,foo2.call(obj) 打印出的是 1。

这就是利用 callapply 或者 bind 方法改变函数内this的指向的显示绑定。

MDN Function.prototype.call()

MDN Function.prototype.apply()

MDN Function.prototype.bind()

new绑定

使用new来调用一个函数,会构造一个新对象并把这个新对象绑定到调用函数中的this

这里留坑,等关于 new 的博客写好了再来填

箭头函数绑定

实际上箭头函数里并没有 this,如果在箭头函数里看到 this,直接把它当作箭头函数外面的 this 即可。外面的 this 是什么,箭头函数里面的 this 就还是什么,因为箭头函数本身不支持 this。

参考文章:

this 的值到底是什么?一次说清楚

你怎么还没搞懂 this?

JS 里为什么会有 this


文章作者: April-cl
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 April-cl !
  目录