前端面试系列-JavaScript-call、applay、bind的区别及代码实现

开发者训练营官方
发布于 2021-3-25 17:01
浏览
0收藏

call 和 apply 的主要作用,是改变对象的执行上下文,并且是立即执行的。它们在参数上的写法略有区别;bind 也能改变对象的执行上下文,它与 call 和 apply 不同的是,返回值是一个函数,并且需要稍后再调用一下,才会执行。

一、call
call 的写法

Function.call(obj,[param1[,param2[,…[,paramN]]]])

 

var name = 'name'
var obj = {
 name: 'objName'
}
function getName(p1, p2) {
 console.log(p1, p2,this.name)
}
getName(1, 2) //1 2 "name"
getName.call(obj, 1, 2)//1 2 "objName"


需要注意以下几点:

  • 调用 call 的对象,必须是个函数 Function。
  • call 的第一个参数,是一个对象。 Function 的调用者,将会指向这个对象。如果不传,则默认为全局对象 window。
  • 第二个参数开始,可以接收任意个参数。每个参数会映射到相应位置的 Function 的参数上。但是如果将所有的参数作为数组传入,它们会作为一个整体映射到 Function 对应的第一个参数上,之后参数都为空。
    function func (a,b,c) {}
    
    func.call(obj, 1,2,3)
    // func 接收到的参数实际上是 1,2,3
    
    func.call(obj, [1,2,3])
    // func 接收到的参数实际上是 [1,2,3],undefined,undefined​

    call模拟实现
  • 明确是谁调用call:函数。
  • call接收的第一个参数是要改变的this指向(去执行这个函数)。若无指定,默认为window
  • call接收的后续参数就是作为调用call的那个函数所需的参数。
    function myCall(context) {
      //判断一下
      if (typeof this !== 'function'){
     throw new TypeError('error')
      }
      //this指向,谁去执行去这个函数
      context = context || window
      //要执行的函数
      context.fn = this
      //取出参数
      const args = [...arguments].slice(1)
      //执行函数
      const result = context.fn(...args)
      //删除fn
      delete context.fn
      return result
    }​

    验证一下
Function.prototype.myCall = myCall
getName.myCall(obj, 1, 2)//1 2 "objName"


二、applay
apply使用与call大体一致,只是接受参数的方法不同。call可以接收多个参数。apply接收的第一个参数是this,第二个参数是 所需参数所组成的数组。

Function.apply(obj[,argArray])


applay模拟实现

function myApply(context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error');
  }
  context = context || window;
  context.fn = this;
  var result;
  if (arguments[1]) {
    result = context.fn(...arguments[1]);
  } else {
    result = context.fn();
  }
  delete context.fn;
  return result;
}


三、bind

Function.bind(thisArg[, arg1[, arg2[, ...]]])


bind 方法 与 apply 和 call 比较类似,也能改变函数体内的 this 指向。不同的是,bind 方法的返回值是函数,并且需要稍后调用,才会执行。而 apply 和 call 则是立即调用。

bind模拟实现

function  myBind(context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
const _this = this
const args = [...arguments].slice(1)
// 返回函数
return function F() {
  // 1 判断是否用作构造函数
  if (this instanceof F) {
    return new _this(...args, ...arguments)
  }
  // 2 用作普通函数
  return _this.apply(context, args.concat(...arguments))
 }
}


验证一下:
普通函数

Function.prototype.myBind = myBind

var name = 'name'
var obj = {
 name: 'objName'
}

function test(p1, p2){
  this.a = p1
  this.b = p2
  console.log(this.name,p1, p2)
}
var f1 = test.myBind(obj, 1)
f1(2)//objName 1 2


构造函数

var name = 'name'
var obj = {
 name: 'objName'
}

function test(p1, p2){
  this.a = p1
  this.b = p2
  console.log(this.name,p1, p2)//undefined 1 2
}
var f1 = test.myBind(obj, 1)
var f= new f1(2)
console.log(f)//test {a: 1, b: 2}


应用场景
1.对象的继承

function superClass () {
    this.value = 123;
    this.print = function () {
        console.log(this.value);
    }
}

function subClass () {
    superClass.call(this);
    this.print();
}

subClass();
//123


2.借用方法
使类数组可以使用Array 原型链上的方法

let domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));


3.Math.max||Math.min

let arr = [1,2,3,4,5];
console.log(Math.max.apply(this,arr))//5
console.log(Math.min.call(this,...arr))//1


4.数组合并

let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];

Array.prototype.push.apply(arr1, arr2);
console.log(arr1); // [1, 2, 3, 4, 5, 6]

————————————————
版权声明:本文为博主「LYFlied」的原创文章

分类
收藏
回复
举报
回复
    相关推荐