call、apply、bind

196 阅读2分钟

刚开始写技术笔记的时候,很浅显的写了一篇this的指向问题,现在看起来不能说错误百出,但也确实是没什么技术水平。今天分享自己对于call、apply、bind新的认识,并手写一个自己的call、apply、bind。

三个方法的语法大体一样:

fnction fn() {}

fn.call(thisArg, arg1, arg2, ...)

fn.apply(thisArg, [arg1,arg2,...])

fn.bind(thisArg, arg1, arg2, ...)

call和bind的参数一样,apply的参数是一个数组(a开头,Array),call和apply返回的是fn执行的结果,bind返回的是fn的拷贝并指定this值和参数(bind不执行,需要调用)。

thisArg是可选的,fn的this指向是thisArg对象。

非严格模式thisArg指定为null、undefined,fn的this指向就是window:

function fn() {

  console.log(this);//window

}

fn.call()

严格模式fn的this指向是undefined:

"use strict"

function fn() {

  console.log(this);//undefined

}

fn.call()

如果thisArg是数字、字符串、布尔值,this指向原始值的对象:

function fn() {

  console.log(this);//String||Number||Boolean

}

fn.call('' || 5 || true)

call、apply和bind是挂在Function对象上的方法,只有函数才能调用。

说真的,这三个方法开发业务的时候并不常用,有用也是用来装一装,面面试。三个方法最主要的就是借助别人的方法,减少重复代码,节省内存。比如fn1方法和fn2方法,fn2需要用到fn1的方法,这时候直接用fn1的方法而不是自己声明一个方法。bind方法比较不一样,bind返回的是一个函数,所以还可以用来做闭包等。

比如求一个数组的最大最小值:

var arr = [5, 6, 2, 8, 1];

console.log(Math.max.apply(Math, arr));//8

console.log(Math.min.apply(Math, arr));//1

实现一个call::

Function.prototype.myCall = function (context) {

  if(context === undefined || context === null){

    context = window;

  }else{

    context = Object(context);

  };

  context.fn = this;

  let result = context.fn([...arguments].slice(1));

  delete context.fn;

  return result;

};

这边需要注意的一个就是context,不能context = context || window;因为这样会把原始数值都绑定到window,但是上面也说了,String是绑定String的。还有就是直接context.fn也是不够严谨,可以使用symbol就绝对不会冲突,只能说fn这个需要用特殊的不会命名冲突的去实现,考虑兼容的话symbol好像也不是非常合适。

实现一个apply:

Function.prototype.myApply = function (context) {

  if(context === undefined || context === null){

    context = window;

  }else{

    context = Object(context);

  };

  context.fn = this;

  let result;

  if(arguments[1] instanceof Array){

    result = context.fn(...arguments[1]);

  }else{

    result = context.fn();

  };

  delete context.fn;

  return result;

};

跟call差不多,就是一个参数的判断不一样,其他的跟call的注意点一样。

实现一个bind:

Function.prototype.mybind = function (context) {

  let that = this;

  if(context === undefined || context === null){

    context = window;

  }else{

    context = Object(context);

  };

  let args = [...arguments].slice(1);

  return function Fn() {

    if(this instanceof Fn){

      return new that(...args, ...arguments)

    }else{

      return that.apply(context, args.concat(...arguments));

    }

  }

}

因为bind返回的是一个函数,所以思路是一样的,不同的是需要判断,bind之后是否是直接new这个函数,如果是new,那么this就是这个构造函数。

function Fn() {

  console.log(this);//Fn

}

new Fn();