ms

155 阅读3分钟
  • new 模拟实现
  • bind 模拟实现
  • call 模拟实现
  • apply 模拟实现
  • 数组去重

new 模拟实现

  1. 创建一个新对象
  2. 这个新对象会执行[[prototype]]连接
  3. 这个新对象会绑定到函数调用的this
  4. 如果函数没有返回对象,new表达式中的函数调用会自动返回这个新对象
function create() {
    // 创建一个空对象
    var obj = new Object();
    // 获取外部传入的构造函数,同时删除
    var Constructor = Array.prototype.shift.call(arguments); 
    // 实现继承,obj可以访问到构造函数中的属性
    obj.__proto__ = Constructor.prototype;
    // 调用构造函数,并且改变this指向
    var ret = Constructor.apply(obj, arguments);
    // 优先返回构造函数返回的对象
    return ret instanceof Object ? ret : obj;
}

bind 模拟实现

bind()会创建一个新函数,当新函数被调用时,它的this会指向bind()的第一个参数。 bindapply/call的区别是,前者返回了一个绑定上下文的函数,后者是直接执行了函数。

Function.prototype.mybind = function(thisArg) {
    if (typeof this !== 'function') {
      throw TypeError("Bind must be called on a function");
    }
    // 拿到参数,为了传给调用者
    const args = Array.prototype.slice.call(arguments, 1),
      // 保存 this
      self = this,
      // 构建一个干净的函数,用于保存原函数的原型
      nop = function() {},
      // 绑定的函数
      bound = function() {
        // this instanceof nop, 判断是否使用 new 来调用 bound
        // 如果是 new 来调用的话,this的指向就是其实例,
        // 如果不是 new 调用的话,就改变 this 指向到指定的对象 o
        return self.apply(
          this instanceof nop ? this : thisArg,
          args.concat(Array.prototype.slice.call(arguments))
        );
      };

    // 箭头函数没有 prototype,箭头函数this永远指向它所在的作用域
    if (this.prototype) {
      nop.prototype = this.prototype;
    }
    // 修改绑定函数的原型指向
    bound.prototype = new nop();

    return bound;
  }
}

call 模拟实现

call可以接收一个参数列表

Function.prototype.myCall = function (thisArgs) {
    if (typeof this !== 'function') {
        throw new TypeError('类型错误')
    }

    const fn = Symbol('fn');

    const args = [...arguments].slice(1);

    thisArgs[fn] = this;

    var result = thisArgs[fn](...args);

    delete thisArgs[fn];

    return result;
};


function foo() {
    console.log(this.name);
    console.log([...arguments]);
}

var obj = {
    name: 'Lusen'
};

foo.myCall(obj, 1, 2);

apply 模拟实现

apply只能接收两个参数,第二个参数为数组

Function.prototype.myApply = function (thisArgs) {
    if (typeof this !== 'function') {
        throw new TypeError('类型错误')
    }

    const fn = Symbol('fn');

    const args = arguments[1];

    thisArgs[fn] = this;

    var result = thisArgs[fn](...args);

    delete thisArgs[fn];

    return result;
};


function foo() {
    console.log(this.name);
    console.log([...arguments]);
}

var obj = {
    name: 'Lusen'
};

foo.myApply(obj, [1, 2, 3]);

call/apply使用场景:

  1. 合并两个数组
var arr1 = [1, 2];
var arr2 = [3, 4];
Array.prototype.push.apply(arr1, arr2);
  1. 获取数组中最大值
var arr1 = [1, 2, 3, 4, 5];
Math.max.apply(Math, arr1); // 5
Math.max.call(Math, -2, 22, 11); //  22
// ES6
Math.max.call(Math, ...arr1) // 5
  1. 验证是否是数组
function isArray(obj) {
    return Object.prototype.toString.call(obj) === '[Object Array]';
}
  1. 类数组转化
function transData(obj) {
    return Array.prototype.slice.call(obj);
}
  1. 调用父构造函数实现继承
function Super() {
    this.color = ['red', 'yellow'];
}
function Sub() {
    Super.call(this);
}

数组去重

  1. 利用ES6提供的新的数据结构Set,它类似于数组,但成员值都是唯一的。(常用,效率高)
function unique(arr) {
    // return [...new Set(arr)];
    return Array.from(new Set(arr));
}
  1. for循环嵌套for,然后splice去重
function unique(arr) {
    if (!Array.isArray(arr)) {
        throw new TypeError('类型错误');
    }
    for (var i = 0; i < arr.length; i++) {
        for (var j = i + 1; j < arr.length; j++) {
            if (arr[i] == arr[j]) {
                arr.splice(j, 1);
                j--;
            }
        }
    }
}
  1. 利用indexOf判断
function unique(arr) {
    if (!Array.isArray(arr)) {
        throw new TypeError('类型错误');
    }
    let result = [];
    for (let i = 0; i < arr.length; i++) {
        if (result.indexOf(arr[i]) === -1) {
            result.push(arr[i]);
        }
    }
    return result;
}

  1. 利用includes判断
function unique(arr) {
    if (!Array.isArray(arr)) {
        throw new TypeError('类型错误');
    }
    let result = [];
    for (let i = 0; i < arr.length; i++) {
        if (!result.includes(arr[i])) {
            result.push(arr[i]);
        }
    }
    return result;
}

  1. 利用reduce + includes
function unique(arr) {
    if (!Array.isArray(arr)) {
        throw new TypeError('类型错误');
    }
    return arr.reduce((prev, cur) => {
        return prev.includes(cur) ? prev : [...prev, cur]
    }, []);
}

  1. 利用filter + indexOf
function unique(arr) {
    if (!Array.isArray(arr)) {
        throw new TypeError('类型错误');
    }
    return arr.filter((item, index) => {
        return arr.indexOf(item) === index;
    })
}
  1. for of + Object 利用对象的属性不可重复这一特性,效率比new Set(...)
function unique(arr) {
    if (!Array.isArray(arr)) {
        throw new TypeError('类型错误');
    }

    let result = [],
        obj = {};

    for (let i of arr) {
        if (!obj[i]) {
            result.push(i);
            obj[i] = 1;
        }
    }

    return result;
}