likes
comments
collection
share

魔术师this的奇幻之旅:从默认绑定到主动操控

作者站长头像
站长
· 阅读数 54

前言

想象一下,this不是枯燥的编程概念,而是一位戴着面具的神秘嘉宾,在不同的派对(函数调用场景)中扮演着多重角色。它时而化身为王国的统治者(全局对象),时而变成某个家庭的一员(对象方法),甚至还能奇迹般地自我复制(构造函数)。接下来,让我们跟随这位善变的魔术师,展开一场奇妙的探索之旅吧!

第一幕:无名英雄——全局舞台(默认绑定)

在JavaScript的广阔天地里,this的默认身份是那个默默无闻却又无处不在的全球守护者。在浏览器的王国,它被称为window,而在Node.js的地下世界,则化身global。当你在一片混沌中(全局作用域)呼唤this,它便露出真身:

console.log(this); // 在浏览器中输出window,在Node.js中输出global

在严格模式('use strict';)下,全局作用域中的this会是undefined

'use strict'; 
console.log(this); // 输出undefined

在函数中无论嵌套多少层,this都指向全局:

var b = 2
function foo() {
  var b = 1

  function bar() {
    baz()
  }

  function baz() {
    console.log(this.b)
  }

  bar()
}

foo()  //2

总而言之,在默认绑定下,函数在哪个词法作用域里生效,this就指向哪里。

第二幕:家的温暖——对象方法的归属(隐式绑定)

this被邀请到某个对象的家中做客(作为对象的一个方法被调用),this不再漂泊,它找到了自己的根:

function foo() {
  console.log(this.a)
}
var obj = {
  a: 2,
  foo: foo //读到函数体(引用)
}
obj.foo() //2

稍微修改一下代码:

function foo() {
  console.log(this.a)
}
var obj = {
  a: 2,
  foo: foo()
}
obj.foo // undefined

为什么变成了undefined呢?需要注意的是,函数foo只是在obj里被调用了,他并没有被对象所拥有,所以可以得出结论:

在隐式绑定中,当函数被一个对象所拥有,再调用时,此时this才会指向该对象。

第三幕:迷失的向导——隐式丢失

然而,当this的家谱变得复杂,比如在链式调用或赋值操作中,它可能会失去方向,指向最后一个触碰它的对象,仿佛在众多家庭中迷失了自己:

function foo() {
  console.log(this.a)
}
var obj = {
  a: 2,
  foo: foo
}
var obj2 = {
  a: 3,
  obj: obj
}
obj2.obj.foo() //2

由此可见,当函数被多个对象链式调用时,this指向最终引用函数的对象。

第三幕:自我复制术——构造函数的奇迹(new 绑定)

如果说this还有什么隐藏技能,那一定是它的“分身术”了。当你在它的名字前念出“new”的咒语,它就会摇身一变,创造出一个全新的自己,也就是对象实例:

function foo(name) {
  this.name = name;
  console.log("I am " + this.name);
}

const Kkk = new foo("kkk"); // "I am kkk"

所以,使用new关键字调用函数(即构造函数)时,this指向新创建的对象实例。

第四幕:箭头函数——不变的初心

箭头函数,就像是那些坚守本心,不受外界影响的角色。无论你如何变换场景,它始终坚持自我,它的this值永远继承自外层上下文,不会在函数内部发生改变:

var obj = {
  name: 'TOM',
  show: function () {
    // console.log(this)

    var bar = () => {
      console.log(this.name)// 箭头函数外部的非箭头函数show的this
    }
    bar()
  }
}
obj.show() //TOM

箭头函数没有this这个概念,写在箭头函数中的this也是他外层函数的this

尾声:掌握主动权——callapplybind

最后,如果你想要在JavaScript的剧场里彻底掌握this的命运,.call(), .apply(), .bind()这三个道具绝对不能少。它们能让你在任何时候,任何地点,指定this想要扮演的角色:

1. call()

call() 方法允许你直接调用一个函数,并且指定 this 的值以及传递给函数的参数。参数列表是以逗号分隔的形式传递的:

function foo(n, m) {
  console.log(this.a, n, m)
}
var obj = {
  a: 2
}
// call 强行把this绑定在obj上
//第一个参数:你想设置为this的值
//后续参数:传递给被调用函数的参数,按顺序列出
foo.call(obj, 20, 30) // 2 20 30

2. apply()

apply() 方法与 call() 类似,也是改变函数内部 this 的值,并传入参数,但不同的是,apply() 接收两个参数:一个是 this 的值,另一个是一个数组或类数组对象,其中的数组元素将作为单独的参数传递给被调用的函数:

function foo(n, m) {
  console.log(this.a, n, m)
}
var obj = {
  a: 2
}
//第一个参数:你想设置为this的值
//第二个参数:一个数组或类数组对象,作为参数传入函数
foo.apply(obj, [20, 30]) // 2 20 30

3. bind()

.bind() 方法创建一个新的函数,该函数的this值会被永久绑定到.bind()的第一个参数。与前两者不同,.bind()不会立即调用函数,而是返回一个已绑定上下文的新函数供后续调用:

function foo(n, m) {
  console.log(this.a, n, m)
}
var obj = {
  a: 2
}
// 会返回一个函数体,接受的参数给bind和函数体都可以
var bar = foo.bind(obj, 20)
bar(30) // 2 20 30

结语

就这样,我们结束了这次关于this的奇妙探险。记住,每一次this的变换,都是JavaScript为你精心准备的魔术表演。只要掌握了规律,你也可以成为这场表演的主宰者!

转载自:https://juejin.cn/post/7377208418567929891
评论
请登录