中级前端需要掌握的28个javascript技巧

1.判断对象的数据类型
const isType = type => target=> `[object ${type}]` === Object.prototype.toString.call(target)
       const isArray = isType('Array')
       console.log(isArray([]));
  • 使用Object.prototype.toString配合闭包 通过传入不同的判断类型来返回不同的判断函数,一行代码,简洁优雅灵活(注意传入type参数时首字母大写)
  • 不推荐将这个函数用来检测可能会产生包装类型的基本数据上 因为call始终会将第一个参数进行装箱操作 导致基本类型和包装类型无法区分
  • 包装类型与基本类型 基本类型是直接存储变量的值保存在堆栈中
  • 封装类型需要通过引用指向实例
  • 声明方式的不同,基本类型无需通过new关键字来创建,而封装类型需new关键字
2. 循环实现数组 map 方法
const selfMap = function(fn,context){
          const arr = Array.prototype.slice.call(this)
          let mapArr = new Array()
          for(let i =0;i<arr.length;i++){
            //判断稀疏数组的情况
            if(!arr.hasOwnProperty(i)) continue; //是否有指定的键(索引)
            mapArr[i] = fn.call(context,arr[i],i,this)
          }
        }
        Array.prototype.selfMap = selfMap
        [1,2,3].selfMap(num => num * 2)

稀疏数组

  • 当一个数组中大部分元素为0,或者为同一值的数组时,可以使用稀疏数组来保存该数组。稀疏数组的处理方式是:记录数组一共有几行几列,有多少个不同值;

  • 稀疏数组可以简单的看作为是压缩,在开发中也会使用到。比如将数据序列化到磁盘上,减少数据量,在IO过程中提高效率

  • 使用方法:将selfMap注入到Array.prototype上

  • 值的一提的是 selfMap的第二个参数为第一个参数回调中的this指向,如果第一个参数为箭头函数 那设置的第二个参数this 会因为箭头函数的词法绑定而失效

  • 另外就是对稀疏数组的处理 通过hasOwnProperty来判断当前下标下的元素是否存在于数组中

3. 使用 reduce 实现数组 map 方法
const selfMap2 = function(fn,context){
      let arr = Array.prototype.slice.call(this)
      return arr.reduce((pre,cur,index)=>{
        return [...pre,fn.call(context,cur,index,this)]
      },[])
    }
4. 循环实现数组 filter 方法
const selfFilter = function(fn,context){
          let arr = Array.prototype.slice.call(this)
          let filterArr = []
          for(let i=0;i<arr.length;i++){
            if(!arr.hasOwnProperty(i))continue
            fn.call(context,arr[i],i,this)&&filterArr.push(arr[i])
          }
          return filterArr
        }
5. 使用 reduce 实现数组 filter 方法
   const selfFilter = function(fn,context){
        return this.reduce((pre,cur,index)=>{
            return fn.call(context,cur,index,this)?[...pre,cur]:[...pre]
        },[])
      }
6. 循环实现数组的 some 方法
const selfSome = function(fn,context){
            let arr = Array.prototype.slice.call(this)
            if(!arr.length) return false
            for(let i =0;i<arr.length;i++){
              if(!arr.hasOwnProperty(i)) continue
              let res = fn(context,arr[i],i,this)
              if(res)return true
            }
            return  false
        }

执行some方法的数组如果是一个空数组,最终始终返回false 而另一个every方法中的数组如果是一个空数组,则始终返回true

7. 循环实现数组的 reduce 方法
 Array.prototype.selfReduce = function(fn,initialValue){
     let arr = Array.prototype.slice.call(this)
     let res
     let startIndex
     if(initialValue === undefined){
       //找到第一个非空单元(真实)的元素和下标
       for(let i = 0;i<arr.length;i++){
         if(!arr.hasOwnProperty(i))continue
         startIndex = i
         res = arr[i]
         break
       }
     }else{
       res = initialValue
     }
     for(let i = ++startIndex||0;i<arr.length;i++){
       if(!arr.hasOwnProperty(i))continue
       res = fn.call(null,res,arr[i],i,this)
     }
     return res
   }
8. 使用 reduce 实现数组的 flat 方法

const selfFlat = function(depth = 1){
    let arr = Array.prototype.slice.call(this)
    if(depth === 0) return arr
    return arr.reduce((pre,cur)=>{
        if(Array.isArray(cur)){
            return [...pre,...selfFlat.call(cur,depth-1)]
        }else{
            return [...pre,cur]
        }
    },[])
}

  • 因为selfFlat是依赖this指向的 所以在reduce便利时需要指定selfFlat的this指向
    否则会默认指向window 从而发生错误
  • 原理通过reduce遍历数组 遇到数组的某个元素仍是数组时 通过ES6的扩展运算符对其进行降维(es5可以使用concat方法) 而这个数组内部可能还嵌套数组 所以需要递归调用selfFlat
    同时原生的flat方法支持一个depth参数表示降维的深度 默认为1 即给数组降一层维度
  • 传入 Inifity 会将传入的数组变成一个一维数组
  • 原理是每递归一次将 depth 参数减 1,如果 depth 参数为 0 时,直接返回原数组
    在这里插入图片描述
    在这里插入图片描述
9. 实现 ES6 的 class 语法
  function inherit(subType,superType){
    // 第一个参数Object.create//方法创建一个新对象,使用现有的对象来提供新对象的__proto__
    // 第二个参数是对象的属性描述符以及对应的属性名称  将要为对象添加或修改的属性的具体配置 。该参数是一个属性描述对象,它所描述的对象属性,会添加到实例对象,作为该对象自身的属性。


    subType.prototype = Object.create(superType.prototype,{
      constructor:{
        enumerable:false,//是否可遍历
        configurable:true, //是否可通过delete删除此属性
        writable:true, //能否修改属性的值
        value:subType//该属性对应的值 为对象添加属性
      }
    })

    // 为现有对象设置一个指定的对象原型  subType的原型为superType
    Object.setPrototypeOf(subType,superType)
  }
 
  • ES6的class内部是基于寄生组合式继承,它是目前最理想的继承方式 通过Object.create方法创造一个空对象,
  • 并将这个空对象继承Object.create方法的参数 再让子类(subType) 的原型对象等于这个空对象,就可以实现子类实例的原型等于这个空对象
  • 而这个空对象的原型又等于父类原型对象(superType.prototype)的继承关系
  • 而Object.create支持第二个参数,即给生成的空对象定义属性和属性描述符/访问器描述符,
  • 我们可以给这个空对象定义一个constructor属性更加符合默认的继承行为,同时它是不可枚举的内部属性(enumerable:false)
  • 而ES6的class允许子类继承父类的静态方法和静态属性 而普通的寄生组合式继承只能做到实例与实例之间的继承
  • 对于类与类之间的继承需要额外定义方法 这里使用Object.setPrototypeOf将superType设置为subType的原型 从而能够从父类中继承静态方法和静态属性

Object.create方法接受第二个参数。该参数是一个属性描述对象,它所描述的对象属性,会添加到实例对象,作为该对象自身的属性。

var o2;
//创建一个可写的,可枚举的,可配置的属性p
o2 = Object.create({}, {
  p: {
    value: 42, 
    writable: true,
    enumerable: true,
    configurable: true 
  } 
});

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值