【手写系列】自己手写实现apply、call、bind

我正在参与「启航计划」

办法介绍

1.apply

apply()办法指定this值参数(参数以数组或许类数组目标的方式存在)的情况下调用某个函数,意思是它能够改动一个函数的履行环境,语法:

fn.apply(thisArg[, argsArray])

【手写系列】自己手写实现apply、call、bind

参数介绍:

  • thisArg:在fn函数运行时指定的this值。需求留意的是,指定的this值并不一定是该函数履行时真正的this值,假如这个函数处于非严格方式下,则指定null或undefined时会主动指向全局目标(浏览器中就是window目标),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的主动包装目标。
  • argsArray: 一个数组或许类数组目标,其间的数组元素作为单独的参数传给fn函数。假如该参数为null或undefined,则表明不传入任何参数。从ECMAScript 5开端能够使用类数组目标。

代码示例:

function fn (a,b,c) {
    console.log(this.name)
    console.log(a, b, c)
}
let obj = {name: '李华'}
fn.apply(obj, [1,2,3])
// 输出结果:
//李华 
//1 2 3

【手写系列】自己手写实现apply、call、bind

2.call

call和apply效果相同,不同的是两者的参数传入不相同,apply第一个参数和call没有差异,但是第二个参数就不同了,apply是以数组或许类数组的方式传入的,而call是将这个数组打开,一个一个的方式传入的,说的比较绕,下面咱们看一下代码示例就能理解了。语法:(留意:中括号表明可选,不是数组的意思)

fn.call(thisArg[,arg1,arg2...]);

【手写系列】自己手写实现apply、call、bind

  • thisArg: 和apply第一个参数相同
  • arg1:作为第一个参数传给fn函数
  • arg2:作为第二个参数传给fn函数
  • ……依此类推

代码示例:

function fn (a,b,c) {
    console.log(this.name)
    console.log(a, b, c)
}
let obj = {name: '李华'}
fn.call(obj, 1, 2, 3)
// 输出  
//李华  
//1 2 3

【手写系列】自己手写实现apply、call、bind

3.bind

bind() 办法会创建一个新函数,当这个新函数被调用时,它的 this 值是传递给 bind() 的第一个参数, 它的参数是 bind() 的其他参数和其本来的参数。 (这儿需求留意bind回来的是一个函数,bind自身不会当即履行这个函数;而apply和call当即履行这个函数,回来履行后的结果) 语法:

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

【手写系列】自己手写实现apply、call、bind

  • thisArg 当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用 new 操作符调用绑定函数时,该参数无效。
  • arg1, arg2, … (可选)当绑定函数被调用时,这些参数加上绑定函数本身的参数会按照次序作为原函数运行时的参数。

代码示例:

function fn(a, b, c) {
  console.log(this);
}
var fn1 = fn.bind({a:123});
fn(); // 输出:window
fn1(); // 输出:{a:123}

【手写系列】自己手写实现apply、call、bind

手写实现

1.apply

Function.prototype.myApply = function (context, args) {
	// 1. 判别args的类型,假如不是Array的实例,抛出一个TypeError;
    if(!(args instanceof Array)){
        throw new TypeError(`args is not an array!`)
    }
    // 2. 确定要绑定的目标,即终究谁来调用函数,命名为new_this;若没有传入要绑定的目标, 默许绑定window目标
    const new_this = context || window
    // 3. 把办法作为目标的特点绑定给new_this,但要留意,也许原有特点就有func,为了防止冲突,这儿用symbol
    const func = Symbol('func')
    //因为这儿func是Symbol变量不再是字符串,所以不能再用new_this.func而是要用中括号获取特点
    //下面的this为调用咱们正在写的myApply函数的函数,比如:fn1.myApply(context, args);此刻this为fn1
    new_this[func] = this
    // 4. 履行当前函数,并获取回来值
    const res = new_this[func](...args)
    // 5. 删去咱们绑定的的Symbol(func)特点,防止污染new_this的特点
    delete new_this[func]
	// 6. 回来第3步得到的回来值
    return res
}

【手写系列】自己手写实现apply、call、bind

2.call

Function.prototype.myCall = function (context, ...args) {
    // if(typeof this !== 'function'){ //不需求判别类型,因为myCall界说在Function.prototype上
    //     throw new TypeError(`${this} is not a function!`)
    // }
    // 1. 确定要绑定的目标,即终究谁来调用函数,命名为new_this;若没有传入要绑定的目标, 默许绑定window目标
    const new_this = context || window
    // 2. 把办法作为目标的特点绑定给new_this,但要留意,也许原有特点就有func,为了防止冲突,这儿用symbol
    const func = Symbol('func')
    //因为这儿func是Symbol变量不再是字符串,所以不能再用new_this.func而是要用中括号获取特点
    new_this[func] = this
    // 3. 履行当前函数,并获取回来值
    const res = new_this[func](...args)
    // 4. 删去咱们绑定的的Symbol(func)特点,防止污染new_this的特点
    delete new_this[func]
	// 5. 回来第3步得到的回来值
    return res
}

【手写系列】自己手写实现apply、call、bind

3.bind

Function.prototype.myBind = function (context, ...args) {
    // 1. 确定要绑定的目标,即终究谁来调用函数,命名为new_this;若没有传入要绑定的目标, 默许绑定window目标
    context = context || window
    // 2. 把原函数(即this)用一个fn变量保存一下,这样更能看出它表明一个函数 
    let fn = this
    return function newFn (...fnArgs) {
      let res
      // 3.要考虑新函数是不是会当作结构函数
      if (this instanceof newFn) {
        // 假如是结构函数则调用new 并且兼并参数args,fnArgs
        res = new fn(...args, ...fnArgs)
      } else {
        // 当作普通函数调用 也能够用上面界说的myCall
        res = fn.call(context, ...args, ...fnArgs)
      }
      return res
    }
  }

【手写系列】自己手写实现apply、call、bind