美文网首页
JS开发必须知道的41个技巧

JS开发必须知道的41个技巧

作者: 奈何缘浅wyj | 来源:发表于2021-02-08 14:39 被阅读0次

    JS开发必须知道的41个技巧

    前言

    JS是前端的核心,但有些使用技巧你还不一定知道;
    本文梳理了JS的41个技巧,帮助大家提高JS的使用技巧;

    Array

    1.数组交集

    普通数组

    const arr1 = [1, 2, 3, 4, 5 , 8 ,9],arr2 = [5, 6, 7, 8, 9];
    
    const intersection = arr1.filter(function (val) { return arr2.indexOf(val) > -1 })
    console.log(intersection) //[5, 8, 9]
    

    数组对象
    数组对象目前仅针对value值为简单的Number,String,Boolan数据类型 文中JSON.stringif比较对象是简写方法,完整的对象比较请看技巧24.对象是否相等

    const arr1 = [{ name: 'name1', id: 1 }, { name: 'name2', id: 2 }, { name: 'name3', id: 3 }, { name: 'name5', id: 5 }];
    const arr2 = [{ name: 'name1', id: 1 }, { name: 'name2', id: 2 }, { name: 'name3', id: 3 }, { name: 'name4', id: 4 }, { name: 'name5', id: 5 }];
    const result = arr2.filter(function (v) {
      return arr1.some(n => JSON.stringify(n) === JSON.stringify(v))
    })
    console.log(result); // [{ name: 'name1', id: 1 },{ name: 'name2', id: 2 },{ name: 'name3', id: 3 },{ name: 'name5', id: 5 }]
    

    2.数组并集

    普通数组

    const arr1 = [1, 2, 3, 4, 5, 8, 9]
    const arr2 = [5, 6, 7, 8, 9];
    const result = arr1.concat(arr2.filter(v => !arr1.includes(v)))
    console.log(result) //[1, 2, 3, 4, 5, 8, 9, 6, 7]
    

    数组对象

    const arr1 = [{ name: 'name1', id: 1 }, { name: 'name2', id: 2 }, { name: 'name3', id: 3 }];
    const arr2 = [{ name: 'name1', id: 1 }, { name: 'name4', id: 4 }, { name: 'name5', id: 5 }];
    let arr3 = arr1.concat(arr2);
    let result = [];
    let obj = [];
    result = arr3.reduce(function (prev, cur, index, arr) {
      obj[cur.id] ? '' : obj[cur.id] = true && prev.push(cur);
      return prev;
    }, []);
    console.log(result); //[{ name: 'name1', id: 1 },{ name: 'name2', id: 2 },{ name: 'name3', id: 3 },{ name: 'name4', id: 4 },{ name: 'name5', id: 5 }]
    

    3.数组差集

    数组arr1相对于arr2所没有的
    普通数组

    const arr1 = [1, 2, 3, 4, 5, 8, 9]
    const arr2 = [5, 6, 7, 8, 9];
    const diff = arr1.filter(item => !new Set(arr2).has(item))
    console.log(diff) //[ 1, 2, 3, 4 ]
    

    数组对象

    // 对象数组
    let arr1 = [{ name: 'name1', id: 1 }, { name: 'name2', id: 2 }, { name: 'name3', id: 3 }];
    let arr2 = [{ name: 'name1', id: 1 }, { name: 'name4', id: 4 }, { name: 'name5', id: 5 }];
    let result = arr1.filter(function (v) {
      return arr2.every(n => JSON.stringify(n) !== JSON.stringify(v))
    })
    console.log(result); // [ { name: 'name2', id: 2 }, { name: 'name3', id: 3 } ]
    

    4.数组补集

    两个数组各自没有的集合
    普通数组

    const arr1 = [1, 2, 3, 4, 5, 8, 9]
    const arr2 = [5, 6, 7, 8, 9];
    const difference = Array.from(new Set(arr1.concat(arr2).filter(v => !new Set(arr1).has(v) || !new Set(arr2).has(v)))) 
    console.log(difference) //[ 1, 2, 3, 4, 6, 7 ]
    

    数组对象

    let arr1 = [{ name: 'name1', id: 1 }, { name: 'name2', id: 2 }, { name: 'name3', id: 3 }];
    let arr2 = [{ name: 'name1', id: 1 }, { name: 'name4', id: 4 }, { name: 'name5', id: 5 }];
    let arr3 = arr1.concat(arr2);
    let result = arr3.filter(function (v) {
      return arr1.every(n => JSON.stringify(n) !== JSON.stringify(v)) || arr2.every(n => JSON.stringify(n) !== JSON.stringify(v))
    })
    console.log(result); // [{ name: 'name2', id: 2 },{ name: 'name3', id: 3 },{ name: 'name4', id: 4 },{ name: 'name5', id: 5 }]
    

    总结一下,差集就是数组arr1相对于arr2所没有的集合,补集是两个数组各自没有的集合

    5.数组去重

    普通数组

    console.log(Array.from(new Set([1, 2, 3, 3, 4, 4]))) //[1,2,3,4]
    console.log([...new Set([1, 2, 3, 3, 4, 4])]) //[1,2,3,4]
    

    数组对象

    const arr = [{ name: 'name1', id: 1 }, { name: 'name2', id: 2 }, { name: 'name3', id: 3 }, { name: 'name1', id: 1 }, { name: 'name4', id: 4 }, { name: 'name5', id: 5 }];
     const result = [];
     arr.forEach(item=>{
        !result.some(v => JSON.stringify(v) === JSON.stringify(item)) && result.push(item)
     })
     console.log(result) //[{ name: 'name1', id: 1 },{ name: 'name2', id: 2 },{ name: 'name3', id: 3 },{ name: 'name4', id: 4 },{ name: 'name5', id: 5 }]
    

    6.数组排序

    普通数组

    console.log([1, 2, 3, 4].sort((a, b) => a - b)); // [1, 2,3,4] 升序
    console.log([1, 2, 3, 4].sort((a, b) => b - a)); // [4,3,2,1] 降序
    

    数组对象

    const arr1 = [{ name: "Rom", age: 12 }, { name: "Bob", age: 22 }].sort((a, b) => { return a.age - b.age })//升序
    const arr2 = [{ name: "Rom", age: 12 }, { name: "Bob", age: 22 }].sort((a, b) => { return -a.age + b.age })//降序
    console.log(arr2) // [{ name: 'Bob', age:22 }, { name: 'Rom', age: 12 }]
    console.log(arr1) // [ { name: 'Rom', age: 12 }, { name: 'Bob', age: 22 } ]
    

    两个种类型数组都可以使用sort排序,sort是浏览器内置方法;
    默认是升序排序,默认返回一个函数,有两个参数:
    (a, b) => a - b 是升序;
    (a, b) => b - a 是降序。

    7.最大值

    普通数组

    Math.max(...[1, 2, 3, 4]) //4
    Math.max.apply(this, [1, 2, 3, 4]) //4
    [1, 2, 3, 4].reduce((prev, cur, curIndex, arr) => {
       return Math.max(prev, cur);
    }, 0) //4
    

    取数组对象中id的最大值

    const arr = [{ id: 1, name: 'jack' },{ id: 2, name: 'may' },{ id: 3, name: 'shawn' },{ id: 4, name: 'tony' }]
    const arr1 = Math.max.apply(Math, arr.map(item => { return item.id }))
    const arr2 = arr.sort((a, b) => { return b.id - a.id })[0].id
    console.log(arr1) // 4
    console.log(arr2) // 4
    

    8.数组求和

    普通数组

    [1, 2, 3, 4].reduce(function (prev, cur) {
      return prev + cur;
    }, 0) //10 
    

    数组对象

    const sum = [{age:1},{age:2}].reduce(function (prev, cur) {
      return prev + cur.age;
    }, 0) //3
    console.log(sum)
    

    9.数组合并

    普通数组

    const arr1 =[1, 2, 3, 4].concat([5, 6]) //[1,2,3,4,5,6]
    const arr2 =[...[1, 2, 3, 4],...[4, 5]] //[1,2,3,4,5,6]
    const arrA = [1, 2], arrB = [3, 4]
    const arr3 =[].concat.apply(arrA, arrB)//arrA值为[1,2,3,4]
    

    数组对象

    const arr4 = [{ age: 1 }].concat([{ age: 2 }])
    const arr5 = [...[{ age: 1 }],...[{ age: 2 }]]
    console.log(arr4) //[ { age: 1 }, { age: 2 } ]
    console.log(arr5) // [ { age: 1 }, { age: 2 } ]
    

    10.数组是否包含值

    普通数组

    console.log([1, 2, 3].includes(4)) //false
    console.log([1, 2, 3].indexOf(4)) //-1 如果存在换回索引
    console.log([1, 2, 3].find((item) => item === 3)) //3 如果数组中无值返回undefined
    console.log([1, 2, 3].findIndex((item) => item === 3)) //2 如果数组中无值返回-1
    

    数组对象

    const flag = [{age:1},{age:2}].some(v=>JSON.stringify(v)===JSON.stringify({age:2}))
    console.log(flag)
    

    11.数组每一项都满足

    普通数组

    [1, 2, 3].every(item => { return item > 2 })
    

    数组对象

    const arr = [{ age: 3 }, { age: 4 }, { age: 5 }]
    arr.every(item => { return item.age > 2 }) // true
    

    12.数组有一项满足

    普通数组

    [1, 2, 3].some(item => { return item > 2 })
    

    数组对象

    const arr = [{ age: 3 }, { age: 4 }, { age: 5 }]
    arr.some(item => { return item.age < 4 }) // true
    

    13.版本号排序

    方法一

    function sortNumber(a, b) {
      return a - b
    }
    const b = [1,2,3,7,5,6]
    const a = ["1.5", "1.5", "1.40", "1.25", "1.1000", "1.1"];
    
    console.log(a.sort(sortNumber)); // [ 1, 2, 3, 5, 6, 7 ]
    console.log(b.sort(sortNumber)); //[ '1.1000', '1.1', '1.25', '1.40', '1.5', '1.5' ]
    

    可见sort排序对整数可以,类似版本号这个格式就不适用了,因为sort函数在比较字符串的时候,是比较字符串的Unicode进行排序的。

    方法二

    //假定字符串的每节数都在5位以下
    //去除数组空值||空格
    if (!Array.prototype.trim) {
      Array.prototype.trim = function () {
        let arr = []; this.forEach(function (e) {
          if (e.match(/\S+/)) arr.push(e);
        })
        return arr;
      }
    }
    
    //提取数字部分
    function toNum(a) {
      let d = a.toString();
      let c = d.split(/\D/).trim();
      let num_place = ["", "0", "00", "000", "0000"], r = num_place.reverse();
      for (let i = 0; i < c.length; i++) {
        let len = c[i].length;
        c[i] = r[len] + c[i];
      }
      let res = c.join('');
      return res;
    }
    
    //提取字符
    function toChar(a) {
      let d = a.toString();
      let c = d.split(/\.|\d/).join('');
      return c;
    }
    
    function sortVersions(a, b) {
    
      let _a1 = toNum(a), _b1 = toNum(b);
      if (_a1 !== _b1) return _a1 - _b1;
      else {
        _a2 = toChar(a).charCodeAt(0).toString(16);
        _b2 = toChar(b).charCodeAt(0).toString(16);
        return _a2 - _b2;
      }
    }
    
    let arr1 = ["10", "5", "40", "25", "1000", "1"];
    let arr2 = ["1.10", "1.5", "1.40", "1.25", "1.1000", "1.1"];
    let arr3 = ["1.10c", "1.10b", "1.10C", "1.25", "1.1000", "1.10A"];
    console.log(arr1.sort(sortVersions)) //[ '1', '5', '10', '25', '40', '1000' ]
    console.log(arr2.sort(sortVersions)) //[ '1.1', '1.5', '1.10', '1.25', '1.40', '1.1000' ]
    console.log(arr3.sort(sortVersions)) // [ '1.10A', '1.10C', '1.10b', '1.10c', '1.25', '1.1000' ]
    
    

    可以看出这个函数均兼容整数,非整数,字母;
    字母排序是根据Unicode排序的,所以1.10b在1.10C的后面

    14. 对象转数组

    将数组的key和value转化成数组

    Object.keys({ name: '张三', age: 14 }) //['name','age']
    Object.values({ name: '张三', age: 14 }) //['张三',14]
    Object.entries({ name: '张三', age: 14 }) //[[name,'张三'],[age,14]]
    Object.fromEntries([name, '张三'], [age, 14]) //ES10的api,Chrome不支持 , firebox输出{name:'张三',age:14}
    

    15.数组转对象

    将数组的值转化为对象的value

    const arrName = ['张三', '李四', '王五']
    const arrAge=['20','30','40']
    const arrDec = ['描述1', '描述2', '描述3']
    const obj = arrName.map((item,index)=>{
      return { name: item, age: arrAge[index],dec:arrDec[index]}
    })
    
    console.log(obj) // [{ name: '张三', age: '20', dec: '描述1' },{ name: '李四', age: '30', dec: '描述2' },{ name: '王五', age: '40', dec: '描述3' }]
    

    16.数组解构

    const arr=[1,2]; //后面一定要加分号,因为不加解释器会认为在读数组
    [arr[1], arr[0]] = [arr[0], arr[1]]; // [2,1]
    

    Object

    17.对象变量属性

    const flag = true;
    const obj = {
        a: 0,
        [flag ? "c" : "d"]: 2
    };
    // obj => { a: 0, c: 2 }
    

    18.对象多余属性删除

    const { name, age, ...obj } = { name: '张三', age: 13, dec: '描述1', info: '信息' }
    console.log(name)  // 张三
    console.log(age)  // 13
    console.log(obj)  // {dec: '描述1', info: '信息' }
    

    19.对象嵌套属性解构

    const { info:{ dec} } = { name: '张三', age: 13, info:{dec: '描述1', info: '信息' }}
    console.log(dec) // 描述1
    

    20.解构对象属性别名

    const { name:newName } = { name: '张三', age: 13 }
    console.log(newName)  // 张三
    

    21.解构对象属性默认值

    const { dec='这是默认dec值' } = { name: '张三', age: 13 }
    console.log(dec) //这是默认dec值
    

    22.拦截对象

    利用Object.defineProperty拦截对象
    无法拦截数组的值

    let obj = { name: '', age: '', sex: '' },
      defaultName = ["这是姓名默认值1", "这是年龄默认值1", "这是性别默认值1"];
    Object.keys(obj).forEach(key => {
      Object.defineProperty(obj, key, { // 拦截整个object 对象,并通过get获取值,set设置值,vue 2.x的核心就是这个来监听
        get() {
          return defaultName;
        },
        set(value) {
          defaultName = value;
        }
      });
    });
    
    console.log(obj.name); // [ '这是姓名默认值1', '这是年龄默认值1', '这是性别默认值1' ]
    console.log(obj.age); // [ '这是姓名默认值1', '这是年龄默认值1', '这是性别默认值1' ]
    console.log(obj.sex); // [ '这是姓名默认值1', '这是年龄默认值1', '这是性别默认值1' ]
    obj.name = "这是改变值1";
    console.log(obj.name); // 这是改变值1
    console.log(obj.age);  // 这是改变值1
    console.log(obj.sex); // 这是改变值1
    
    let objOne = {}, defaultNameOne = "这是默认值2";
    Object.defineProperty(obj, 'name', {
      get() {
        return defaultNameOne;
      },
      set(value) {
        defaultNameOne = value;
      }
    });
    console.log(objOne.name); // undefined
    objOne.name = "这是改变值2";
    console.log(objOne.name); // 这是改变值2
    

    利用proxy拦截对象

    let obj = { name: '', age: '', sex: '' }
    let handler = {
      get(target, key, receiver) {
        console.log("get", key); 
        return Reflect.get(target, key, receiver);
      },
      set(target, key, value, receiver) {
        console.log("set", key, value); // set name 李四  // set age 24
        return Reflect.set(target, key, value, receiver);
      }
    };
    let proxy = new Proxy(obj, handler);
    proxy.name = "李四";
    proxy.age = 24;
    

    defineProterty和proxy的对比:
    1.defineProterty是es5的标准,proxy是es6的标准;
    2.proxy可以监听到数组索引赋值,改变数组长度的变化;
    3.proxy是监听对象,不用深层遍历,defineProterty是监听属性;
    4.利用defineProterty实现双向数据绑定(vue2.x采用的核心)

    23.对象深度拷贝

    JSON.stringify深度克隆对象;
    1.无法对函数 、RegExp等特殊对象的克隆;
    2.会抛弃对象的constructor,所有的构造函数会指向Object;
    3.对象有循环引用,会报错

    const mapTag = '[object Map]';
    const setTag = '[object Set]';
    const arrayTag = '[object Array]';
    const objectTag = '[object Object]';
    const argsTag = '[object Arguments]';
    
    const boolTag = '[object Boolean]';
    const dateTag = '[object Date]';
    const numberTag = '[object Number]';
    const stringTag = '[object String]';
    const symbolTag = '[object Symbol]';
    const errorTag = '[object Error]';
    const regexpTag = '[object RegExp]';
    const funcTag = '[object Function]';
    
    const deepTag = [mapTag, setTag, arrayTag, objectTag, argsTag];
    
    function forEach(array, iteratee) {
      let index = -1;
      const length = array.length;
      while (++index < length) {
        iteratee(array[index], index);
      }
      return array;
    }
    
    function isObject(target) {
      const type = typeof target;
      return target !== null && (type === 'object' || type === 'function');
    }
    
    function getType(target) {
      return Object.prototype.toString.call(target);
    }
    
    function getInit(target) {
      const Ctor = target.constructor;
      return new Ctor();
    }
    
    function cloneSymbol(targe) {
      return Object(Symbol.prototype.valueOf.call(targe));
    }
    
    function cloneReg(targe) {
      const reFlags = /\w*$/;
      const result = new targe.constructor(targe.source, reFlags.exec(targe));
      result.lastIndex = targe.lastIndex;
      return result;
    }
    
    function cloneFunction(func) {
      const bodyReg = /(?<={)(.|\n)+(?=})/m;
      const paramReg = /(?<=\().+(?=\)\s+{)/;
      const funcString = func.toString();
      if (func.prototype) {
        const param = paramReg.exec(funcString);
        const body = bodyReg.exec(funcString);
        if (body) {
          if (param) {
            const paramArr = param[0].split(',');
            return new Function(...paramArr, body[0]);
          } else {
            return new Function(body[0]);
          }
        } else {
          return null;
        }
      } else {
        return eval(funcString);
      }
    }
    
    function cloneOtherType(targe, type) {
      const Ctor = targe.constructor;
      switch (type) {
        case boolTag:
        case numberTag:
        case stringTag:
        case errorTag:
        case dateTag:
          return new Ctor(targe);
        case regexpTag:
          return cloneReg(targe);
        case symbolTag:
          return cloneSymbol(targe);
        case funcTag:
          return cloneFunction(targe);
        default:
          return null;
      }
    }
    
    function clone(target, map = new WeakMap()) {
    
      // 克隆原始类型
      if (!isObject(target)) {
        return target;
      }
    
      // 初始化
      const type = getType(target);
      let cloneTarget;
      if (deepTag.includes(type)) {
        cloneTarget = getInit(target, type);
      } else {
        return cloneOtherType(target, type);
      }
    
      // 防止循环引用
      if (map.get(target)) {
        return map.get(target);
      }
      map.set(target, cloneTarget);
    
      // 克隆set
      if (type === setTag) {
        target.forEach(value => {
          cloneTarget.add(clone(value, map));
        });
        return cloneTarget;
      }
    
      // 克隆map
      if (type === mapTag) {
        target.forEach((value, key) => {
          cloneTarget.set(key, clone(value, map));
        });
        return cloneTarget;
      }
    
      // 克隆对象和数组
      const keys = type === arrayTag ? undefined : Object.keys(target);
      forEach(keys || target, (value, key) => {
        if (keys) {
          key = value;
        }
        cloneTarget[key] = clone(target[key], map);
      });
    
      return cloneTarget;
    }
    
    console.log(clone({
      name: '张三', age: 23,
      obj: { name: '李四', age: 46 },
      arr: [1, 2, 3]
    })) // { name: '张三', age: 23, obj: { name: '李四', age: 46 }, arr: [ 1, 2, 3 ] }
    

    对象深度克隆实际上就是要兼容Array,RegExp,Date,Function类型;
    克隆函数可以用正则取出函数体和参数,再定义一个函数将取出来的值赋值进去
    详细请戳对象深度拷贝

    24.对象是否相等

    如果用JSON.stringify转化属性顺序不同,也不相等;
    而且不支持无法对函数 、RegExp等特殊对象的克隆

    
    function deepCompare(x, y) {
      var i, l, leftChain, rightChain;
    
      function compare2Objects(x, y) {
        var p;
    
        // remember that NaN === NaN returns false
        // and isNaN(undefined) returns true
        if (isNaN(x) && isNaN(y) && typeof x === 'number' && typeof y === 'number') {
          return true;
        }
    
        // Compare primitives and functions.     
        // Check if both arguments link to the same object.
        // Especially useful on the step where we compare prototypes
        if (x === y) {
          return true;
        }
    
        // Works in case when functions are created in constructor.
        // Comparing dates is a common scenario. Another built-ins?
        // We can even handle functions passed across iframes
        if ((typeof x === 'function' && typeof y === 'function') ||
          (x instanceof Date && y instanceof Date) ||
          (x instanceof RegExp && y instanceof RegExp) ||
          (x instanceof String && y instanceof String) ||
          (x instanceof Number && y instanceof Number)) {
          return x.toString() === y.toString();
        }
    
        // At last checking prototypes as good as we can
        if (!(x instanceof Object && y instanceof Object)) {
          return false;
        }
    
        if (x.isPrototypeOf(y) || y.isPrototypeOf(x)) {
          return false;
        }
    
        if (x.constructor !== y.constructor) {
          return false;
        }
    
        if (x.prototype !== y.prototype) {
          return false;
        }
    
        // Check for infinitive linking loops
        if (leftChain.indexOf(x) > -1 || rightChain.indexOf(y) > -1) {
          return false;
        }
    
        // Quick checking of one object being a subset of another.
        // todo: cache the structure of arguments[0] for performance
        for (p in y) {
          if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
            return false;
          } else if (typeof y[p] !== typeof x[p]) {
            return false;
          }
        }
    
        for (p in x) {
          if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
            return false;
          } else if (typeof y[p] !== typeof x[p]) {
            return false;
          }
    
          switch (typeof (x[p])) {
            case 'object':
            case 'function':
    
              leftChain.push(x);
              rightChain.push(y);
    
              if (!compare2Objects(x[p], y[p])) {
                return false;
              }
    
              leftChain.pop();
              rightChain.pop();
              break;
    
            default:
              if (x[p] !== y[p]) {
                return false;
              }
              break;
          }
        }
    
        return true;
      }
    
      if (arguments.length < 1) {
        return true; 
      }
    
      for (i = 1, l = arguments.length; i < l; i++) {
    
        leftChain = []; //Todo: this can be cached
        rightChain = [];
    
        if (!compare2Objects(arguments[0], arguments[i])) {
          return false;
        }
      }
    
      return true;
    }
    
    const obj1 = { 
      name: '张三', age: 23, 
      obj: { name: '李四', age: 46 }, 
      arr: [1, 2, 3],
      date:new Date(23),
      reg: new RegExp('abc'),
      fun: ()=>{}
     }
    const obj2 = { 
      name: '张三', age: 23, 
      obj: { name: '李四', age: 46 }, 
      arr: [1, 2, 3],
      date: new Date(23),
      reg: new RegExp('abc'),
      fun: ()=>{}
     }
    
    console.log(deepCompare(obj1,obj2)) // true
    

    判断对象是否相等,实际上就是要处理Array,Date,RegExp,Object,Function的特殊类型是否相等

    25.对象转化为字符串

    通过字符串+Object 的方式来转化对象为字符串(实际上是调用 .toString() 方法)

    'the Math object:' + Math.ceil(3.4)                // "the Math object:4"
    'the JSON object:' + {name:'曹操'}              // "the JSON object:[object Object]"
    

    覆盖对象的toString和valueOf方法来自定义对象的类型转换

    2  * { valueOf: ()=>'4' }                // 8
    'J' + { toString: ()=>'ava' }                // "Java"
    

    当+用在连接字符串时,当一个对象既有toString方法又有valueOf方法时候,JS通过盲目使用valueOf方法来解决这种含糊;
    对象通过valueOf方法强制转换为数字,通过toString方法强制转换为字符串

    '' + {toString:()=>'S',valueOf:()=>'J'}  //J
    

    Function

    26.函数隐式返回值

    (()=>3)()  //3
    (()=>(
       3
    ))()
    

    函数省略大括号,或者将大括号改成小括号可以确保代码以单个语句的形式进行求值

    27.函数自执行

    const Func = function() {}(); // 常用
    
    (function() {})(); // 常用
    (function() {}()); // 常用
    [function() {}()];
    
    new function() {};
    new function() {}();
    void function() {}();
    typeof function() {}();
    delete function() {}();
    
    + function() {}();
    - function() {}();
    ~ function() {}();
    ! function() {}();
    

    28.函数异步执行

    Promise

    Promise.reject('这是第二个 reject 值').then((data)=>{
      console.log(data)
    }).catch(data=>{
      console.log(data) //这是第二个 reject 值
    })
    

    Generator

    function* gen(x) {
      const y = yield x + 6;
      return y;
    }
    
    // yield 如果用在另外一个表达式中,要放在()里面
    // 像上面如果是在=右边就不用加()
    function* genOne(x) {
      const y = `这是第一个 yield 执行:${yield x + 1}`;
      return y;
    }
    
    const g = gen(1);
    //执行 Generator 会返回一个Object,而不是像普通函数返回return 后面的值
    g.next() // { value: 7, done: false }
    //调用指针的 next 方法,会从函数的头部或上一次停下来的地方开始执行,直到遇到下一个 yield 表达式或return语句暂停,也就是执行yield 这一行
    // 执行完成会返回一个 Object,
    // value 就是执行 yield 后面的值,done 表示函数是否执行完毕
    g.next() // { value: undefined, done: true }
    // 因为最后一行 return y 被执行完成,所以done 为 true
    

    Async/Await

    function getSomething() {
        return "something";
    }
    async function testAsync() {
        return Promise.resolve("hello async");
    }
    async function test() {
        const v1 = await getSomething();
        const v2 = await testAsync();
        console.log(v1, v2); //something 和 hello async
    }
    test();
    

    String

    29.字符串翻转

    function reverseStr(str = "") {
      return str.split("").reduceRight((t, v) => t + v);
    }
    
    const str = "reduce123";
    console.log(reverseStr(str)); // "321recuder"
    

    30.url参数序列化

    将对象序列化成url参数传递

    function stringifyUrl(search = {}) {
      return Object.entries(search).reduce(
        (t, v) => `${t}${v[0]}=${encodeURIComponent(v[1])}&`,
        Object.keys(search).length ? "?" : ""
      ).replace(/&$/, "");
    }
    
    console.log(stringifyUrl({ age: 27, name: "YZW" })); // "?age=27&name=YZW"
    

    31.url参数反序列化

    一般会通过location.search拿到路由传递的参数,并进行反序列化得到对象

    function parseUrlSearch() {
      const search = '?age=25&name=TYJ'
      return search.replace(/(^\?)|(&$)/g, "").split("&").reduce((t, v) => {
        const [key, val] = v.split("=");
        t[key] = decodeURIComponent(val);
        return t;
      }, {});
    }
    
    console.log(parseUrlSearch()); // { age: "25", name: "TYJ" }
    

    32.转化为字符串

    const val = 1 + ""; // 通过+ ''空字符串转化
    console.log(val); // "1"
    console.log(typeof val); // "string"
    
    const val1 = String(1);
    console.log(val1); // "1"
    console.log(typeof val1); // "string"
    

    Number

    33.数字千分位

    方法一:

    function thousandNum(num = 0) {
      const str = (+num).toString().split(".");
      const int = nums => nums.split("").reverse().reduceRight((t, v, i) => t + (i % 3 ? v : `${v},`), "").replace(/^,|,$/g, "");
      const dec = nums => nums.split("").reduce((t, v, i) => t + ((i + 1) % 3 ? v : `${v},`), "").replace(/^,|,$/g, "");
      return str.length > 1 ? `${int(str[0])}.${dec(str[1])}` : int(str[0]);
    }
    
    thousandNum(1234); // "1,234"
    thousandNum(1234.00); // "1,234"
    thousandNum(0.1234); // "0.123,4"
    console.log(thousandNum(1234.5678)); // "1,234.567,8"
    

    方法二

    console.log('1234567890'.replace(/\B(?=(\d{3})+(?!\d))/g, ","))
    console.log((1234567890).toLocaleString())
    

    34.字符串转数字

    方法一
    用*1来转化为数字,实际上是调用.valueOf方法

    '32' * 1            // 32
    'ds' * 1            // NaN
    null * 1            // 0
    undefined * 1    // NaN
    1  * { valueOf: ()=>'3' }        // 3
    

    方法二

    + '123'            // 123
    + 'ds'               // NaN
    + ''                    // 0
    + null              // 0
    + undefined    // NaN
    + { valueOf: ()=>'3' }    // 3
    

    35.判断小数是否相等

    肯定有人会说这还不简单,直接用'==='比较;
    实际上0.1+0.2 !==0.3,因为计算机不能精确表示0.1, 0.2这样的浮点数,所以相加就不是0.3了

    Number.EPSILON=(function(){   //解决兼容性问题
        return Number.EPSILON?Number.EPSILON:Math.pow(2,-52);
    })();
    //上面是一个自调用函数,当JS文件刚加载到内存中,就会去判断并返回一个结果
    function numbersequal(a,b){ 
        return Math.abs(a-b)<Number.EPSILON;
      }
    //接下来再判断   
    const a=0.1+0.2, b=0.3;
    console.log(numbersequal(a,b)); //这里就为true了
    

    36.双位运算符

    双位运算符比Math.floor(),Math.ceil()速度快

    ~~7.5                // 7
    Math.ceil(7.5)       // 8
    Math.floor(7.5)      // 7
    
    ~~-7.5              // -7
    Math.floor(-7.5)     // -8
    Math.ceil(-7.5)      // -7
    

    所以负数时,双位运算符和Math.ceil结果一致,正数时和Math.floor结果一致

    37.取整和奇偶性判断

    取整

    3.3 | 0         // 3
    -3.9 | 0        // -3
    
    parseInt(3.3)  // 3
    parseInt(-3.3) // -3
    
    // 四舍五入取整
    Math.round(3.3) // 3
    Math.round(-3.3) // -3
    
    // 向上取整
    Math.ceil(3.3) // 4
    Math.ceil(-3.3) // -3
    
    // 向下取整
    Math.floor(3.3) // 3
    Math.floor(-3.3) // -4
    

    判断奇偶数

    const num=5;
    !!(num & 1) // true
    !!(num % 2) // true
    

    Boolean

    38.判断数据类型

    function dataTypeJudge(val, type) {
      const dataType = Object.prototype.toString.call(val).replace(/\[object (\w+)\]/, "$1").toLowerCase();
      return type ? dataType === type : dataType;
    }
    console.log(dataTypeJudge("young")); // "string"
    console.log(dataTypeJudge(20190214)); // "number"
    console.log(dataTypeJudge(true)); // "boolean"
    console.log(dataTypeJudge([], "array")); // true
    console.log(dataTypeJudge({}, "array")); // false
    

    可判断类型:undefined、null、string、number、boolean、array、object、symbol、date、regexp、function、asyncfunction、arguments、set、map、weakset、weakmap

    39.使用Boolean过滤数组假值

    const compact = arr => arr.filter(Boolean)
    compact([0, 1, false, 2, '', 3, 'a', 'e' * 23, NaN, 's', 34])  //[ 1, 2, 3, 'a', 's', 34 ]
    

    40.短路运算

    ||(或)

    const flag = false || true //true
    // 某个值为假时可以给默认值
    const arr = false || []
    

    &&(与)

    const flag1 = false && true //false
    const flag2 = true && true //true
    

    41.switch 简写

    可以用对象替代switch,提高代码可读性

    switch(a) {
      case '张三':
        return 'age是12'
      case '李四':
        return 'age是120'
    }
    
    // 使用对象替换后
    const obj ={
      '张三': 'age12',
      '李四': 'age120',
    }
    console.log(obj['张三'])
    

    相关文章

      网友评论

          本文标题:JS开发必须知道的41个技巧

          本文链接:https://www.haomeiwen.com/subject/rnoxxltx.html