关于javascript:手写实现applycallbind

9次阅读

共计 2568 个字符,预计需要花费 7 分钟才能阅读完成。

本文 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。因而这里加了这个判断。

总结

通过上述的剖析,咱们来总结下这三个函数的相同点和不同点,来帮忙更好的了解。

办法 call apply bind
函数参数 一个参数列表 一个蕴含多个参数的数组 多个参数
函数作用 扭转函数运行时 this 指向 扭转函数运行时 this 指向 扭转函数运行时 this 指向
返回后果 间接执行 间接执行 期待执行函数
底层实现 通过eval 通过eval 调用apply
正文完
 0