JavaScript中的各种骚操作

写这篇文章的缘由是最近感觉在公司的一些需求和功能开发需求上,对一些js处理不是很熟练,缺乏一些技巧。因此整理了一份在实际开发过程中常用的 js 技巧,灵活地运用,能够解决问题的能力,也会对代码的简洁性有较大的提升。

01.数组去重

正常我们实现数组去重都是通过双层变量或者indexOf的方式。

(1)双层for循环去重
function unique(arr) {
    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--
            }
        }
    }
    return arr;
}
(2)利用indexOf去重
function unique(arr) {
    if(!Array.isArray(arr)) {
        console.log('type error!');
        return;
    }
    var array = [];
    for (var i=0;i<arr.length;i++) {
        if(array.indexOf(arr[i]) === -1) {
            array.push(arr[i]);
        }
    }
    return array;
}

但其实有一种更简单的方式:利用Array.from与set去重

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!');
        return;
        }
    return Array.from(new Set(arr));
}

02、截断数组

如果你有修改数组长度为某个固定值的需求,那么你可以试试这个

let arr = [0,1,2,3,4,5];
arr.length = 3;
console.log(array);
Output:[0,1,2];

03、获取数组中的最后一项

通常获取数组最后一项,我们用的比较多的是:

let arr = [0,1,2,3,4,5];
const last = arr[arr.length - 1];
console.log(last);
Output: 5;

但我们也可以通过slice操作来实现:

let arr = [0,1,2,3,4,5];
const last = arr.slice(-1)[0];
console.log(last);
Output: 5;

04、美化你的JSON

日常开发中,会经常使用到 JSON.stringify,但并不清楚他具体有哪些参数,实际上它有三个参数:

  • json: 必须,可以是数组或Object
  • replacer: 可选值,可以是数组,也可以是方法
  • space: 用什么来进行分割

而我们恰恰可以指定第三个参数space的值去美化我们的JSON。

05、数组转化为对象(Array to Object)

数组转化为对象,大多数同学想到的是通过遍历数组,放入空对象的方式:

var obj = {};
var arr = ["1","2","3"];
for (var key in arr) {
    obj[key] = arr[key];
}
console.log(obj);
Output: {0:1,1:2,2:3}

但是有一种比较简单快速的方法,即利用展开运算符展开数组

var arr = ["1","2","3"];
var obj = {...arr};
console.log(obj);
Output:{0:1,1:2,2:3}

06、合理利用三元表达式

有些场景我们需要针对不同的条件,给变量赋予不同的值,往往通过if—else的方式:

const isGood = true;
let feeling;
if (isGood) {
    feeling = 'good';
} else {
    feeling = 'bad';
}
console.log(`I feel ${feeling}`)
Output: I feel good

但是为什么不采用三元表达式呢?

const isGood = true;
const feeling = isGood ? 'good' : 'true';
console.log(`I feel ${feeling}`);
Output: I feel good

这种也是所谓的 Single line (单行)思想,其实就是代码趋向于极简的风格。

07、转换为数字类型(Convert to Number)

常见转换为数字的方式,可能主要通过parseInt() 、Number() 这两种方式:

const age = "30";
const ageConvert = parseInt(age);
console.log(typeof ageConvert);
Output: number;

其实也可以通过 + 操作来实现转换:

const age = "30";
const ageConvert = +age;
console.log(typeof ageConvert);
Output: number

08、转换为字符串类型(Convert to String)

常见转换为字符串的方式,可能主要通过toString()、String() 这两种方式:

let a = 123;
console.log(typeof a.toString());
Output: string

但也可以通过 value + "" 的方式来实现

let a = 123;
console.log(typeof (a + ""));
Output: string

09、性能追踪

如果想要测试一段 js 代码的执行耗时,可以尝试使用 performance

let start = performance.now();
let sum = 0;
for (let i = 0; i < 100000; i++ ) {
   sum += 1; 
}
let end = performance.now();
console.log(start);
console.log(end);

10、合并对象 (Combining Objects)

合并两个对象使用的最多 API 就是 Object.assign() 了:

const obj1 = {a:1};
const obj2 = {b:2};
console.log(Object.assign(obj1, obj2));
Output:{a:1, b:2}

实际上通过扩展运算符 (spread operator) 会特别简单:

const obj1 = {a:1};
const obj2 = {b:2};
console.log({...obj1, ...obj2});
Output:{a:1, b:2}

11、短路运算(Short-circuit evalutation)

可以通过 && 或 | | 来简化我们的代码,比如:

if (isOnline) { postMessage()};
// 使用 &&
isOnline && postMessage();
// 使用 ||
let name = null || "jerry";

12、数组扁平化(Flattening an array)

数组的扁平化一般会通过递归reduce 去实现。

(1) 递归:
var arr = [1,[2,[3,4]]];
function flatten(arr) {
    var result = [];
    for (var i = 0; i < arr.length; i++ ) {
        if (Array.isArray(arr[i])) {
             result = result.concat(flatten(arr[i]));
            } else {
            result.push(arr[i]); 
        }
    }
    return result;
}
console.log(flatten(arr));
Output:[1,2,3,4]    
(2) reduce:
var arr = [1,[2,[3,4]]];
function flatten(arr) {
    return arr.reduce(function(prev,next) {
            return prev.concat(Array.isArray(next) ? flatten(next) : next)
        }, []);
}

但是 ES6 提供了一个新方法 flat(depth), 参数 depth ,代表展开嵌套数组的深度,默认值是1

let arr = [1,[2,[3,[4,[5]]]]];
console.log(arr.flat(3));
Output:[1, 2, 3, 4, [5]]

13、求幂运算

平时实现指数运算,用的比较多的应该是 Math.pow(),比如求 2^10:

console.log(Math.pow(2,10));

在 ES7 中引入了指数运算符 **** 具有与 Math.pow() 一样的运算结果。

console.log(2**10); // 1024

14、浮点数转为整数 (Float to Integer)

一般将浮点数转化为整数会用到 Math.floor() , Math.ceil() , Math.round() 。但其实使用位运算符 ~ ,>>,<<,|,>>> 也能够实现取整。

console.log(~~5.96); // 5
console.log(5.96 >> 0);// 5
console.log(5.96 << 0);// 5
console.log(5.96 | 0);// 5
// >>> 不可对负数取整
console.log(5.96 >>> 0);// 5
15、Object.create(null)

在 Vue 和 Vuex 的源码中,作者都使用了 Object.create(null) 来初始化一个新对象。为什么不使用更简洁的 {} 呢?我们来看下 Object.create() 的定义:

Object.create(proto,[propertiesObject]);
  • proto: 新创建对象的原型对象;
  • propertiesObject: 可选。要添加到新对象上的可枚举的属性(新添加的属性是其自身的属性,而不是其原型链上的属性)。

我们可以对比分别通过 Object.create(null) 和 {} 创建对象的不同:

"Object.create(null) 和 {} 创建对象的区别"

从上图可以看到,通过 {} 创建的对象继承了 Object 自身的方法,如 hasOwnProperty 、toString 等,在新对象上可以直接使用。

而使用 Object.create(null) 创建的对象,除了自身属性之外,原型链上没有任务属性。也就是说通过Object.create(null) 的方式可以创建一个纯净的对象,我们可以自己定hasOwnProperty、toString 等方法,完全不必担心会将原型链上的同名方法覆盖掉。

16、拷贝数组

日常开发中,数组的拷贝是一个经常会遇到的场景。实现数组的拷贝有很多骚技巧。

(1)Array.slice()
const arr = [1,2,3];
const copyArr = arr.slice();
(2)展开操作符
const arr = [1,2,3];
const copyArr = [...arr];
(3)使用 Array 构造函数和展开操作符
const arr = [1,2,3,4];
const copyArr = new Array(...arr);
(4)Array.concat
const arr = [1,2,3,4];
const copyArr = arr.concat();

17、避免多条件并列

开发中有时会遇到多个条件,执行相同的语句,也就是多个 || 的情况:

if (status === "a" || status === "b" || status === "c") {doSomething();}

这种写法语义性、可读性都不太好。可以通过 switch case 或 includes 的方式进行改造。

(1) switch case
switch (case ) {
    case "a":
    case "b":
    case "c"doSomeThing();
}
(2) includes
const enum = ["a","b","c"];
if (enum.includes(status)) { doSomeThing(); }            

18.Object.freeze()

在 Vue 的文档中介绍数据绑定和响应时,特意标注了对于经过 Object.freeze() 方法的对象无法进行更新响应。Object.freeze() 方法用于冻结对象,禁止对于该对象的属性进行修改。正是由于这种特性,所以在实际项目中,它有很多的使用场景。

像一些纯展示类的页面,可能存在巨大的数组或对象,如果这些数据不会发生改变,那么你就可以使用 Object.freeze() 将它们冻结,这样 Vue 就不会对这些对象做 setter 或 getter 的转换,可以大大提升性能。