阅读 153

面试官:call、apply和bind的原理?

前言

这里需要些作用域和闭包,this指向的知识。

正文

首先,我们需要知道他们的区别?

首先他们的第一个参数都是要绑定的this。我记忆方式是: call是打电话,所以我们需要一句一句的说,对应参数就是一个一个的传。apply是应用我想到了app,当我们填写个人信息就是把全部的信息一次性的填写上去,对应就是第二参数是一个数组。bind是绑定,跟一个人绑定起来,形成了一个新个体,第二个参数嘛,之前记住只有一个是数组其他的都是一个个传就可以了。这个是我的记忆方式,一般是不适用大家,你们大家看到这几个单词想到什么就使用这个场景进行记忆

call

从区别我们知道call是一个个传参的。

核心代码:

Function.prototype.myCall = function (ctx, ...arg) {   let context    if(ctx==null || ctx==undefined) {     context = window   }else {     context = Object(ctx)   }   let symbol = Symbol('特殊的标记')   context[symbol] = this   let result = context[symbol](...arg)   delete context[symbol]   return result } 复制代码

例子加核心代码:

Function.prototype.myCall = function (ctx, ...arg) {   let context    if(ctx==null || ctx==undefined) {     context = window   }else {     context = Object(ctx)   }   let symbol = Symbol('特殊的标记')   context[symbol] = this   let result = context[symbol](...arg)   delete context[symbol]   return result } function sbThis() {   console.log(this.name);   return this.name } var name = '我是window' const obj = {   name: '我是Obj' } sbThis.myCall(obj) // 我是Obj sbThis.myCall(null) // 我是window 复制代码

这里的疑问:

  1. 为什么需要对nullundefined做判断?

答: 传入null和undefined是为了不绑定this的指向,当使用bind,可以实现柯里化的效果。

  1. 为什么this就是指sbThis

隐式绑定,看段代码:

function sbThis() {   console.log(this.name);   return this.name } var name = '我是window' const obj = {   name: '我是Obj',   fn: sbThis } obj.fn() //我是Obj 复制代码

样子是类似的、就是通过obj调用的fn,所以fn中的this就是obj。前面的代码是sbThis通过调用myCall,所以在myCall中的this就是指向sbThis。

  1. 为什么使用 Symbol

答:产生出一个特殊的值,用来传入obj绑定一个特殊的属性

  1. 为什么要把this的绑定到 context

答:就是前面说的隐式绑定,跟前面的隐式绑定的例子一样产生一个属性,这个属性的this就是context,这里使用symbol就是以免与obj的属性产生冲突。所以我们在下面一行就是使用这个属性拿到返回值,然后就删除这个属性。

apply

apply和call是相似的就是第二个参数是一个数组:

Function.prototype.myApply = function (ctx) {   let context    if(ctx==null || ctx==undefined) {     context = window   }else {     context = Object(ctx)   }   let symbol = Symbol('特殊的标记')   context[symbol] = this   let result   let arg = arguments[1] // 注意点   if(arg) {  // 判断有没有传第二个参数     if(!Array.isArray(arg)) {  // 判断是不是数组       throw new TypeError('myApply 的第二个参数需要是数组类型')     }else {       let arr = Array.from(arg)       result = context[symbol](...arg)     }   }else {     result =  context[symbol]()   }   delete context[symbol]   return result } 复制代码

与call对比。多来一个对第二参数数组的判断

注意点:这里的第二参数是根据arguments中获取的,因为在使用myApply的时候可能是不会传第二个参数的。

bind

bind 和 call的区别就是返回一个函数,核心代码:

Function.prototype.myBind = function (ctx,...arg) {   const thisFn = this; // 存储源函数以及上方的params(函数参数)   let resultFn = function (...arg2) {     return thisFn.call(ctx,...arg,...arg2)   }   return resultFn } 复制代码

这里的注意点: 为什么需要把this赋值给thisFn?

答: 如果我们使用了this.call()的话,由于闭包我们把resultFn的函数给返回了出去,这里有一个赋值的操作,然后我们运行的话,this指向是看使用位置(别把this和作用域搞混了)。详情查看参考,例如:

Function.prototype.myBind = function (ctx,...arg) {   const thisFn = this; // 存储源函数以及上方的params(函数参数)   let resultFn = function (...arg2) {     console.log(this);  //window     return this.call(ctx,...arg,...arg2)   }   return resultFn } function sbThis(name) {   console.log(name);   console.log(this.name);   return this.name } var name = '我是window' const obj = {   name: '我是Obj',   fn: sbThis } let bindResult = sbThis.myBind(obj) bindResult('obj') 复制代码

这里就是我们执行bindResult,在全局的执行,这里的this就指向了window,所以我们需要存储一个thisFn用来指向sbThis函数。

记忆bind:

闭包加call的结合。

结论

先认识call、apply、bind的区别。
以区别的角度出发,填写对应的参数和返回值。
整体的全部原理都是以this的隐式绑定出发。

call:

  1. 本身this(被使用函数)绑定到要使用的对象的一个属性。(隐式绑定)

  2. 执行这个属性。

  3. 删除这个属性。

  4. 返回被使用函数的返回值。

apply:

  1. 以call的书写出发。

  2. 使用arguments来拿第二个参数

  3. 针对第二属性做判断。

  4. 是数组转成数组传入。

  5. 不是数组报错。

  6. 是就执行并传传参

  7. 没有第二参数直接执行属性。

bind:

  1. 返回一个函数用来控制call什么时候被使用(给出一个按钮一样)。

  2. 注意点返回出去的函数不能直接使用this,要使用bind绑定的那个this。

参考


作者:晓欲望
链接:https://juejin.cn/post/7015103366049038350


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐