原生javascript实现call、apply和bind的方法

var context = {id: 12};
function fun (name, age) {
  console.log(this.id, name, age)
}

bind

bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。

 原生 bind
1. var a = fun.bind(context, 'bind');
2. a(1); // 12, 'bind', 1  :作为普通函数
3. new a('1'); // undefined, 'bind', '1'  :作为构造函数

以上例子可以看出

     bind作为普通函数使用时,改变的this的指向,指向context对象;fun函数执行了;传入参数不确定,可以在第一步bind传值,也可以在第二步执行函数fun传值。

     bind最为构造函数使用时,不同点是this指向实例的对象。

bind的实现代码

Function.prototype.mybind = function (context) {
  if (this && this.constructor !== Function) // 抛错
    throw new Error("Function.prototype.mybind - what is trying to be bound is not callable");
  // this =>绑定函数 fun
  var self = this;
// 获取mybind函数从第二个参数到最后一个参数 var arg = Array.prototype.slice.call(arguments, 1); function fbound() { // 普通函数: this => window 构造函数: this => 实例对象
// 获取mybind返回函数传入的函数 var args = Array.prototype.slice.call(arguments); self.apply(this instanceof self ? this : context, arg.concat(args)); } var FNOP = function () {}; FNOP.prototype = this.prototype; fbound.prototype = FNOP.prototype; return fbound; } var a = fun.mybind(context, 'mybind'); a('12') // a: 普通函数 var b = new a(12) // a: 构造函数 b: 实例对象

  兼容写法

Function.prototype.bind = Function.prototype.bind || function () {
    ……
};

call

  call() 方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。

原生 call
1. fun.call(context, 'call', 2) // 12, call, 2

  

call 的实现代码

Function.prototype.mycall = function (context) {
  if (this && this.constructor !== Function)
    throw new Error(this.name + ".mycall is not a function");
  context = context || window;
  context.fn = this;
  var arg = [];
  for (var i =1; i< arguments.length; i++) {
    arg.push('arguments[' + i + ']');
  }
  eval('context.fn(' + arg + ')');
  delete context.fn;
}

fun.mycall(context, 'mycall', 3);  // 12, 'mycall', 3

  

apply

apply与call的实现原理差不多,只是apply第二个参数是数组;

1. fun.apply(context, ['call', '4']) // 12, call, '4'

  

Function.prototype.myapply = function (context, arr) {
  if (this && this.constructor !== Function)
    throw new Error(this.name + ".myapply is not a function");
  context = context || window;
  context.fn = this;
  var arrs = [];
  if (arr && arr.constructor === Array) { // 判断第二个参数是数组类型
    for (var i =0; i <arr.length; i++) {
      arrs.push('arr[' + i + ']');
    }
    eval('context.fn(' + arrs + ')');
  } else if (!arr) { // 第二个参数不传
    delete context.fn();
  } else { // 第二个参数不是数组类型
    throw new Error ("CreateListFromArrayLike called on non-object");
  }
  delete context.fn;
}

fun.myapply(context, ['myapply', 4]);