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

方法介绍

1.apply

apply()方法指定this值参数(参数以数组或者类数组对象的形式存在)的情况下调用某个函数,意思是它可以改变一个函数的执行环境,语法:

fn.apply(thisArg[, argsArray])

参数介绍:

  • 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

 2.call

call和apply作用一样,不同的是两者的参数传入不一样,apply第一个参数和call没有区别,但是第二个参数就不同了,apply是以数组或者类数组的形式传入的,而call是将这个数组展开,一个一个的形式传入的,说的比较绕,下面我们看一下代码示例就能理解了。语法:(注意:中括号表示可选,不是数组的意思)

fn.call(thisArg[,arg1,arg2...]);
  • 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

 3.bind

bind() 方法会创建一个新函数,当这个新函数被调用时,它的 this 值是传递给 bind() 的第一个参数, 它的参数是 bind() 的其他参数和其原本的参数。(这里需要注意bind返回的是一个函数,bind自身不会立即执行这个函数;而apply和call立即执行这个函数,返回执行后的结果)语法:

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

代码示例:

function fn(a, b, c) {
  console.log(this);
}

var fn1 = fn.bind({a:123});
fn(); // 输出:window
fn1(); // 输出:{a:123}

 手写实现

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
}

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
}

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
    }
  }

你可能感兴趣的:(JavaScript,javascript,前端,开发语言)