二、js手写函数第二批


6、高阶函数

<script>
  // 1、map的用法(注意和Map不是同一个东西)
  let arr = [2, 3, 4, 5, 6]
  console.log(arr.map((item) => { return 2 * item }))

  // 2、reduce的用法,用于数组加减乘除的累积计算
  console.log('******************')
  console.log(arr.reduce((a, b) => { return a + b }))
  console.log(arr.reduce((a, b) => { return a * b }))
  console.log(arr.reduce((a, b) => { return a - b }))

  // 可以增加一个默认的初始值
  console.log(arr.reduce((a, b) => { return a + b }, 10))
  console.log(arr.reduce((a, b) => { return a * b }, 10))
  console.log(arr.reduce((a, b) => { return a - b }, 10))

  // 3、filter函数可以结合数组去重去看,很简单

  // 4、sort很有意思,多看看
  // 首先它改变原数组
  // 可以排序字母,按字母升序排序
  // 排序数字的时候把数组中所有的项转化为字符串,然后进行 unicode 编码比较,所以直接用会出错
  console.log([10, 20, 1, 2].sort());

  // 正确的升序
  console.log([10, 20, 1, 2].sort((a, b) => {
    return a - b;
  }))
  // 正确的降序
  console.log([10, 20, 1, 2].sort((a, b) => {
    return b - a;
  }))

  // 5、some的用法:遍历数组,当满足条件时,停止遍历,并返回true,如果都不存在则返回false
  console.log(arr.some((item) => {
    return item > 5;
  }))

  // 6、every的用法:和some一样,只是every找到第一个不符合条件的,返回false,一直没找到就返回true,
  console.log(arr.every((item) => {
    console.log(item)
    return item < 7
  }))

  // 7、find的用法:和some的规则一样,不过some返回true或者false,但是find直接返回找到的结果,找到第一个满足条件的就停止
  console.log(arr.find(item => {
    console.log(item)
    return item > 4
  }))

  // 8、findIndex的用法:和find一样,只不过findIndex返回的是符号条件的值的索引(从0开始)
  console.log('**********')
  console.log(arr.findIndex(item => {
    return item > 4
  }))
</script>

7、重写call

<script>
  Function.prototype.myCall = function (params) {
    //这里的写法很有意思,直接写在Function的原型里,我们知道Function是由function关键字定义的函数对象的原型
    //这样写完后,所有我们创建的函数都可以调用myCall这个函数,符合call的用法,function本身也是对象,所以函数对象
    //会逐层往下找,直到找到myCall方法

    let context = params || window;//首先this可能传入null,所以兼容,这里的参数在下方解释了
    context.fn = this;//注意这是一个普通函数,所以函数里的this是指向最后调用这个函数的对象的,call是被函数对象调用的,所以this一般指向调用它的函数
    const args = [];//装形参,并且这里args把形参的第一位去掉,因为第一位一般是对象
    //可以打印看一下形参arguments
    for (let i = 1, len = arguments.length; i < len; i++) {
      args.push('arguments[' + i + ']');
    }
    //eval函数会直接执行内部的代码,这里的args是['arguments[1]', 'arguments[2]'],解析成代码就是传进来的参数
    let result = eval('context.fn(' + args + ')')// 这里字符串和数组相加有点意思,会把数组的东西拿出来加成一个字符串,这里eval函数是同步执行代码
    delete context.fn;
    return result;
  }

const say = function (a, b) {
  console.log(this)
  console.log(a, b)
}

say.myCall({ a: 'qer', b: 'sdfd', name: 'nihaoa' }, 4, 5)

  //注意:需要解释的地方,我们在定义函数的时候只设置了一个参数params,但是实际调用的时候我们可以传入多个
  //这个时候,params只代表了我们传入的第一个参数,但是在形参arguments里是装着所有的参数的
  </script>

// 优化改造一下的
Function.prototype.myCall = function (params, ...args) {
  let context = params || window;
  context.fn = this;
  let result = context.fn(...args);
  delete context.fn;
  return result;
}

8、重写apply

<script>
  Function.prototype.myApply = function (obj, arr) {
    let context = obj || window;
    context.fn = this;
    debugger
    let result;
    if (!arr) {
      result = context.fn();
    } else {
      let args = [];
      // 这里和call不一样了,这里从0开始,因为参数是arr数组,就是从零开始的
      for (let i = 0, len = arr.length; i < len; i++) {
        args.push('arr[' + i + ']');
      }
      result = eval('context.fn(' + args + ')')
    }

    delete context.fn;
    return result;
  }

  const say = function (a, b) {
    console.log(this)
    console.log(a, b)
  }

  say.myApply({ a: 'qer', b: 'sdfd', name: 'nihaoa' }, [4, 7])
</script>

9、重写instanceof

<script>
  // 1、什么是instanceof
  // A instanceof B  就是检测对象A是不是另一个对象B的实例

  // 原理就是看对象B的原型对象(B.prototype)是否在对象A的隐式原型链(A.__pro__)上
  // 如果在,则返回true,如果不在则返回false,不过有一个特殊的情况
  // 当对象B(比如B本身就是实例化对象)的prototype为null将会报错(类似于空指针异常)

  const _instanceof = (target, Fn) => {
    if ((typeof target !== 'object' || typeof target !== 'function') || target == null)
      return false
    let proto = target.__proto__
    while (true) {
      if (proto === null) return false
      if (proto === Fn.prototype) return true
      proto = proto.__proto__
    }
  }
  function A() { }
  const a = new A()
  console.log(_instanceof(a, A)) // true
  console.log(_instanceof(1, A)) // false
</script>

10、原型链继承

<script>
  // 画好原型链自己看,可以看懂
  // 这里只需要说一点,就是对象在寻找字段或者方法的时候,首先会从自身找,
  // 自身找不到就会顺着隐式原型链去找
  function Animal() {
    this.colors = ['red', 'pink'];
  }
  Animal.prototype.getColor = function () {
    return this.colors;
  }
  function Dog() { }
  Dog.prototype = new Animal()
  let dog1 = new Dog()
  dog1.colors.push('brown')
  let dog2 = new Dog()
  console.log(dog2.colors)

  // 注意:需要注意的是原型链继承存在的两个问题
  // 问题1:原型中包含的引用类型属性将被所有实例共享;
  // 问题2:子类在实例化的时候不能给父类构造函数传参;
</script>

文章作者: 吴俊杰
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 吴俊杰 !
  目录