bind Polyfill 详解javascript bind

前端面试经常会遇到的一个面试题:手写bind方法
这篇文章就ECMAScript中bind Polyfill来详解bind方法,详见MDN
首先,bind() call() apply()与javascript中this息息相关,他们有个共同的作用,就是对this对象进行显式绑定,至于他们的区别不在这里做深入讨论

bind()是干什么用的

bind() 方法会==创建一个新函数==,在bind()被调用时,新函数的this被指定为bind()的第一个参数,而其余参数将作为新函数得参数,供调用时使用。
const obj = {
    name: 'lily',
    getName: function() {
        return this.name
    }
}

let fn = obj.getName

console.log(fn())  // undefined  因为此时fn中this指向 [object Window]

let fn2 = fn.bind(obj, 1, 2, 3)

console.log(fn2()) // 'lily' 通过bind()将this显式绑定在obj上

bind()语法

fn.bind(obj, arg1, arg2 )

除了更改this指向外,bind()还有其他用途

预设参数

这里有另外一个点,bind第一个参数为null 或者undefined时,调用的函数this在非严格模式下指向window,严格模式下将会报错 TypeError: this is undefined

function preset() {
    return Array.prototype.slice.call(arguments)
}

let list1 = list(1, 2, 3) // [1, 2, 3]

let presetList = list.bind(null, 666)

let list2 = presetList() // [666]
let list3 = presetList(777, 888, 999) // [666, 777, 888, 999]

bind() Polifill

这种写法不可在构造函数new funcA.bind(thisArg, args)使用,因为new操作符也可改变this指向,并且优先级最高
if (!Function.prototype.bind) (function() { // 是否有bind
  let slice = Array.prototype.slice // slice 返回新数组
  
  Function.prototype.bind = function() {
    let thatFunc = this,  // 调用bind的方法
        thatArg = arguments[0],  // 目标对象
        args = slice.call(arguments, 1) // 预设参数
    
    if (typeof thatFunc !== 'function') {  // 调用bind()的是否是函数,因为bind只能被函数调用
      throw new TypeError('only Function can be bound')
    }
    
    return function(){
      let funcArgs = args.concat(slice.call(arguments))  // 拼接参数,因为bind返回的是一个函数,所以调用的时候有可能会传参数进来
      return thatFunc.apply(thatArg, funcArgs);
    }
  }
})()
下面写法可用于new funcA.bind(thisArg, args)
if (!Function.prototype.bind) (function(){ // 是否有bind
  var ArrayPrototypeSlice = Array.prototype.slice;  // slice 返回新数组
  
  Function.prototype.bind = function(otherThis) {
  
    if (typeof this !== 'function') { // 调用bind()的是否是函数,因为bind只能被函数调用
      throw new TypeError('only Function can be bound');
    }

    let baseArgs= ArrayPrototypeSlice .call(arguments, 1),  // 目标对象
        baseArgsLength = baseArgs.length, // 预设参数长度
        fToBind = this, // 调用bind的方法
        fNOP = function() {},  // 字面量创建一个函数
        fBound = function() {  // 
          baseArgs.length = baseArgsLength; // reset to default base arguments
          baseArgs.push.apply(baseArgs, arguments); // 拼接参数
          return fToBind.apply(
            // 是否被new 操作符调用,是的话就用新创建的this替换bind的this
            fNOP.prototype.isPrototypeOf(this) ? this : otherThis, baseArgs
          );
        };

    if (this.prototype) {
      fNOP.prototype = this.prototype; 
    }
    fBound.prototype = new fNOP(); //  防止new了函数后改变原型而导致原函数的原型也被修改

    return fBound;
  };
})();

你可能感兴趣的:(bind Polyfill 详解javascript bind)