手写实现applycallbind

Posted 春天的风夏天的雨

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手写实现applycallbind相关的知识,希望对你有一定的参考价值。

本文github地址,欢迎star

apply、call、bind区别

这三个方法都是挂载 Funtion 原型上的方法,所以调用者必须是个函数。

  • Function.prototype.call()
  • Function.prototype.apply()
  • Function.prototype.bind()

这三个函数的使用语法:

func.call(thisArg, param1, param2, ...)
func.apply(thisArg, [param1, param2, ...])
func.bind(thisArg, param1, param2, ...)

他们共有的作用都可以改变函数运行时 this 的指向。

callapply 的区别在于传递参数的方式不同:

  • apply 的第 2 个参数为数组
  • call 则是从第 2 个至第 N 个都是给 func 的传参

bindcallapply 的区别在于函数是否立即执行:

  • callapply是在改变了函数的 this 指向之后立马执行
  • bind 会返回一个函数,虽然改变了 functhis 指向,但不是马上执行, 而是调用返回的函数才执行

apply、call、bind 使用场景

获取数组的最值

可以通过 apply 来获取 Math.max() 最大值和 Math.min() 最小值。当然也可以展开来获取数组的最值。

let arr = [1, 2, 3];
const max = Math.max.apply(null, arr); // es6 Math.max(...arr)
const min = Math.min.apply(null, arr); // es6 Math.min(...arr)
console.log(max); // 3
console.log(min); // 1

判断数据类型

可以通过 Object.toString.call 来判断所有的数据类型。

// 判断原生对象
const isPlainObject = val => Object.toString.call(val) === \'[object Object]\'
// 判断字符串
const isString = val => Object.toString.call(val) === \'[object String]\'

将类数组转为数组

类数组因为不是真正的数组,所有没有数组类型上自带的种种方法,所以要转为数组,才能调用数组的方法.

let arrayLike = {
    \'0\': \'a\',
    \'1\': \'b\',
    \'2\': \'c\',
    length: 3
};

// ES5的写法
let arr1 = [].slice.call(arrayLike); // [\'a\', \'b\', \'c\']

// ES6的写法
let arr2 = Array.from(arrayLike); // [\'a\', \'b\', \'c\']

继承

组合继承使用call来实现。

function Parent() {
    this.name = \'张三\';
    this.age = 18;
}

Parent3.prototype.getName = function() {
    return this.name;
}

function Child() {
    Parent3.call(this);
    this.address = \'beijing\';
}

Child.prototype = new Parent3();
Child.prototype.constructor = Child3;
var s = new Child();
console.log(s.getName()); // \'张三\'

手写实现

call的实现

Function.prototype.call = function (context, ...args) {
  var context = context || window;
  context.fn = this;
  var result = eval(\'context.fn(...args)\');
  delete context.fn
  return result;
}

apply的实现

apply 和 call 基本原理是差不多的,只是参数存在区。

Function.prototype.apply = function (context, args) {
  let context = context || window;
  context.fn = this;
  let result = eval(\'context.fn(...args)\');
  delete context.fn
  return result;
}c

bind的实现

bind 的实现思路基本和 apply 一样,但是在最后实现返回结果这里,bindapply 有着比较大的差异,bind 不需要直接执行,需要通过返回一个函数的方式将结果返回,之后再通过执行这个结果,得到想要的执行效果。

Function.prototype.bind = function (context, ...args) {
    if (typeof this !== "function") {
      throw new Error("this must be a function");
    }
    var self = this;
    var fbound = function () {
        self.apply(this instanceof self ? this : context, args.concat(Array.prototype.slice.call(arguments)));
    }
    if(this.prototype) {
      fbound.prototype = Object.create(this.prototype);
    }
    return fbound;
}
  • bind需要返回一个函数,但是不能丢失函数原型上的属性,因此fbound.prototype = Object.create(this.prototype);
  • this instanceof self当这个绑定函数被当做普通函数调用的时候,可以直接用context; 而返回的这个之后当做构造函数使用的时候,却是指向这个实例,所以this instanceof selftrue时,要用this。 因此这里加了这个判断。

总结

经过上述的分析,我们来总结下这三个函数的相同点和不同点,来帮助更好的理解。

方法callapplybind
函数参数一个参数列表一个包含多个参数的数组多个参数
函数作用改变函数运行时 this 指向改变函数运行时 this 指向改变函数运行时 this 指向
返回结果直接执行直接执行等待执行函数
底层实现通过eval通过eval调用apply

以上是关于手写实现applycallbind的主要内容,如果未能解决你的问题,请参考以下文章

手动实现applycallbind

前端面试题之手写promise

applycallbind有什么区别?

凡尘:关于Applycallbind的详解

Javascript中applycallbind

Javascript中applycallbind