• 使用数组的slice方法将类数组转换为数组
    原理:slice将类数组通过下标操作放进了array里面

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    Array.prototype.slice = function(begin, end){
    end = (typeof end != 'undefined') ? end :this.length
    var i, cloned = [], size , len = this.length;
    // 处理不合法的开始索引
    var start = begin || 0;
    start = (start > 0) ? start : Math.max(len + start, 0);
    // 处理不合法的结束索引
    var upTo = (typeof end == 'number') ? Math.min(end, len): len;
    if (end < 0) {
    upTo = len + end;
    }

    // 实际上被切分的长度
    size = upTo - start;
    if (size > 0) {
    cloned = new Array(size)
    if (this.charAt) {
    for (i = 0; i < size; i++) {
    cloned[i] = this.charAt(start + i)
    }
    } else {
    for (i = 0; i < size; i++) {
    cloned[i] = this[start + i];
    }
    }
    return cloned;
    }
    }
    • call的模拟实现
      实现思路:

      1. 将函数设置为对象的属性: a.fn = fx
      2. 执行函数: a.fn()
      3. 删除函数: delete a.fn

        1
        2
        3
        4
        5
        6
        7
        8
        Function.prototype.call = function(context){
        // 获取调用call的函数,用this来实现
        context.fn = this;
        // 执行函数
        context.fn();
        // 删除函数
        delete context.fn
        }

        上面的函数中实现了基本的call方法,但是被执行的方法fn不能接受参数,下面进行改进。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        Function.prototype.call = function(context){
        context.fn = this;
        var args = [];
        // 去除第一个指定this的参数
        for (var i = 1; i < arguments.length; i++){
        argus.push('arguments[' + i + ']');
        }
        eval('context.fn('+ args +')'); // 需要将args中的元素一个个传递给fn,使用eval方法来实现。
        delete context.fn
        }

        上面的实现还需要注意以下三点:

      4. this的值可以传入为null或者undefined,此时采用的是默认绑定,指向的是全局对象,在浏览器中指向的是window对象
      5. this的参数可以是基本的数据类型,在原始的call方法中会采用Object函数进行对象化包装
      6. 函数是可以有返回值的

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        Function.prototype.call = function(context){
        context = context ? Object(context) : window;
        context.fn = this;
        var args = []
        for (var i = 1; i < arguments.length; i++) {
        args.push('arguments[' + i + ']')
        }
        var results = eval('context.fn('+ args +')');
        delete context.fn;
        return results;
        }

        以上是ES3的实现方式,下面我们来看看ES6的实现方式

        1
        2
        3
        4
        5
        6
        7
        8
        Function.prototype.call = function(context){
        context = context ? Object(context) : window;
        context.fn = this;
        let args = [...arguments].slice(1);
        let results = context.fn(...args);
        delete context.fn;
        return results;
        }

    以上我们实现了call的方法,下面我们来实现apply的方法,apply的实现和call类似

    • es3实现

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      Function.prototype.apply = function(context, arr) {
      context = context ? Object(context) : window;
      context.fn = this;

      var results;
      // 判断是否传入了第二个参数
      if (!arr) {
      context.fn();
      } else {
      var args = [];
      for (var i = 1; i < arguments.length; i++) {
      args.push('arguments['+ i +']')
      }
      eval('context.fn('+ args +')')
      }
      delete context.fn;
      return results;
      }
    • es6实现

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      Function.prototype.apply = function(context, arr) {
      context = context ? Object(context) : window;
      context.fn = this;
      let result;
      if (!arr) {
      context.fn();
      } else {
      result = context.fn(...arr);
      }
      delete context.fn;
      return result;
      }