面试官:手写一个call、apply、bind之this铺垫(一)

203 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第2天,点击查看活动详情

相信学过前端的大佬们,都知道call、apply、bind是干啥的吧?不会吧、不会真的有人不知道吧(手动狗头,阴阳怪气一秒钟)

众所周知,这三种方法都是改变我们函数中的this指向的,为了唤醒回忆,那我在这里先举三个例子帮大家复习一下

//定义一个对象,对象中挂载一些属性
let obj = {
  name: "夹心啊",
  age: 18,
  type: "美女",
};

var name = "Alice";

function foo(num1, num2) {
  console.log(this.name);
  console.log(this.age);
  console.log(this.type);
  console.log(num1 + num2);
}
foo(1,2)

猜猜输出什么?是时候考验一下你的this指向有没有学好了!

看看结果

image.png 你答对了吗??

好,我们来分析一下输出结果,首先我们要明白的知识点是foo里面this指向是什么?好了,可能又有宝子们不太了解,那还能怎么办,继续讲解!

this绑定

默认绑定

  • this 所处得词法作用域在哪里生效了,this 就绑定了在哪里

  • 单独使用 this,它指向全局(Global)对象

  • 在严格模式下,全局对象无法进行默认绑定,所以导致 this 只能绑定在 undefined 上

隐式绑定规则

  • 当函数引用有上下文对象时, 隐式绑定就会把函数调用中的这个 this 绑定到这个上下文对象,即谁调用谁就是爹

隐式丢失

  • 当隐式绑定的函数丢失了绑定对象,就会应用默认绑定

显示绑定规则

  • apply bind call 可以强行指定函数的 this 指向

所以很显然,上面的foo函数是在全局下调用的,命中默认绑定,所以它的this指向的就是我们全局,而我们全局作用于下只定义了一个name,却没有age和type,所以当然输出的分别是Alice、Undefined、Undefined和传入相加的数字3咯。

那如果我们在obj内挂载一个foo属性让它等于外部的foo函数,再调用会输出什么呢?

obj.foo=foo
obj.foo(1,2)

image.png foo的this指向了obj,这显而易见了,是命中了隐式绑定规则谁调用谁是this的爹

难度升级👀 通过上面一番赋值操作,obj里面应该有个foo了且内部逻辑和外部foo函数一模一样

let obj = {
  name: "夹心啊",
  age: 18,
  type: "美女",
  foo:function(num1, num2) {
          console.log(this.name);
          console.log(this.age);
          console.log(this.type);
          console.log(num1 + num2);
    }
};

image.png 是这样毫无疑问吧。

那如果我将obj中的foo再赋给一个变量,调用该变量了呢

var test=obj.foo
//调用foo发生什么?
test(1,2)

看看结果 image.png

嗯?怎么会这样???为什么foo中的this指向了全局??你现在肯定是满脸黑人问号,这个foo不是在obj里面吗,为什么还指向了全局??

停停停,咱们的foo是在obj里面没错,但是宝子,问题来了!你没调用啊!你只是把foo这个函数又从obj里面拿到了外面在全局下调用了,属于典型的隐式丢失了。

举个例子,foo是个手电筒,obj调用foo相当于你晚上在家里把手电筒打开了,手电筒只能照亮你家,照不到外面。但是你出去散步,你把手电筒从家里带出来了,你在外面把手电筒打开,手电筒还能照亮你家吗?当然不能啦!!它现在只能照亮野外的路。

那明白了上面几点,那我们使用call、apply、bind这样来搞一下,分别输出什么呢?

foo.call(obj,1,2)

foo.apply(obj,[1,2])

var newFoo=foo.bind(obj)
newFoo(1,2)

看看结果

image.png

image.png

image.png

好家伙!谁看了不惊呼一句好家伙,三个都集体输出了obj对象的属性,说明call、apply、bind这三个方法集体改变了foo()里面原本的this指向,把它强行掰弯到了obj的身上了

接下来是个小扩展

箭头函数中的this

通常来说箭头函数是没有自己的this的,它的this继承上下文的this

定义在全局作用域下的箭头函数

image.png 毫无疑问依旧输出全局作用域下的name即Alice

定义在作用域中的箭头函数

那如果我在普通函数中定义一个计时器,计数器回调为箭头函数

var objfun = function () {
  this.name = "夹心啊";
  this.age = 18;
  this.type = "美女";
  setTimeout(() => {
    console.log(this.name);
  });
};
objfun()

调用objfun()定时器会输出什么呢

image.png 显然箭头函数集成到了objfun内部的this输出了"夹心啊"

讲明白了this的原理,下一篇我们来看看如何实现call、apply、bind吧

下一篇:面试官:手写一个call、apply、bind我瞧瞧之call(二)