day 07 JS call、apply、bind

118 阅读1分钟
  • call

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

var foo = {
    value: 1
};

function bar() {
    console.log(this.value);
}

bar.call(foo); // 1

模拟call的思路:

  1. 将函数设置成为对象的属性(利用this的隐式绑定规则)
  2. 执行该函数
  3. 删除该函数

以上个例子为例,就是:

// 第一步
foo.fn = bar
// 第二步
foo.fn()
// 第三步
delete foo.fn

整理一下代码:

Function.prototype.call2 = function (context) {
    var context = context || window;
    context.fn = this;

    var args = [];
    for(var i = 1, len = arguments.length; i < len; i++) {
        args.push('arguments[' + i + ']');
    }

    var result = eval('context.fn(' + args +')');  // 这句是真的骚,佩服

    delete context.fn
    return result;
}

// 测试一下
var value = 2;

var obj = {
    value: 1
}

function bar(name, age) {
    console.log(this.value);
    return {
        value: this.value,
        name: name,
        age: age
    }
}

bar.call2(null); // 2

console.log(bar.call2(obj, 'kevin', 18));
// 1
// Object {
//    value: 1,
//    name: 'kevin',
//    age: 18
// }

  • apply apply 的实现跟 call 类似,在这里直接给代码
Function.prototype.apply = function (context, arr) {
    var context = Object(context) || window;
    context.fn = this;

    var result;
    if (!arr) {
        result = context.fn();
    }
    else {
        var args = [];
        for (var i = 0, len = arr.length; i < len; i++) {
            args.push('arr[' + i + ']');
        }
        result = eval('context.fn(' + args + ')')
    }

    delete context.fn
    return result;
}

  • bind的模拟实现 bind函数的三个特点:

    1. 返回一个函数
    2. 可以传入参数
    3. 碰到new 关键字this的指向会失效
Function.prototype.bind2 = function (context) {
    // 先判断调用自己的是不是函数
    if (typeof this !== "function") {
      throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
    }
    
    // 记载下 当前函数
    var self = this;
    // 拿出除了this指向外的后面的形参
    var args = Array.prototype.slice.call(arguments, 1);

    var fNOP = function () {};

    var fBound = function () {
        // 这个时候的arguments是指bind返回的函数传入的参数
        var bindArgs = Array.prototype.slice.call(arguments);
        return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
    }

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();
    return fBound;
}

// 不处理new 的情况

Function.prototype.bind2 = function () {
    var _this = arguments[0];
    var args = Array.prototype.slice(arguments, 1);
    var fn = this;
    return function () {
        return n.apply(_this, args.concat(Array.prototype.slice(arguments)));
        // 之所以要用到return 是考虑到调用者需要返回什么值
    }
}

本文全文参考 github.com/mqyqingfeng… 以及 github.com/mqyqingfeng…