JS技巧

知识库编程语言Web基础JavaScript大约 15 分钟

------ 数组 ------

填充数组

console.log(new Array(5).fill("1")); // returns ["1","1","1","1","1"]

展平数组的数组

[123, 500, [1, 2, [34, 56, 67, [234, 1245], 900]], 845].flat(Infinity)
// [123, 500, 1, 2, 34, 56, 67, 234, 1245, 900, 845, 30257]

数组交集

// 普通数组
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数据类型
const arr1 = [{name: 'name1', id: 1}, {name: 'name2', id: 2}];
const arr2 = [{name: 'name1', id: 1}, {name: 'name3', id: 3}];
const result = arr2.filter(function (v) {
    return arr1.some(n => JSON.stringify(n) === JSON.stringify(v))
});
console.log(result); // [{ name: 'name1', id: 1 }]
//方法三
const intersection = (list, ...args) => list.filter(item => args.every(list => list.includes(item)));
console.log(intersection([2, 1], [2, 3])); // [2] 
console.log(intersection([1, 2], [3, 4])); // []

数组并集

// 普通数组
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]
// 数组对象
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 obj = [];
let 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}]

数组差集

//普通数组
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}]

数组补集

//普通数组
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}]

数组去重

 //普通数组
 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 obj = [];
const result = arr.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}]

分割指定长度的元素数组

const listChunk = (list, size = 1, cacheList = []) => {
     const tmp = [...list];
     if (size <= 0) {
         return cacheList
     }
     while (tmp.length) {
         cacheList.push(tmp.splice(0, size))
     }
     return cacheList
 };
 
 console.log(listChunk([1, 2, 3, 4, 5, 6, 7, 8, 9])) // [[1], [2], [3], [4], [5], [6], [7], [8], [9]]
 console.log(listChunk([1, 2, 3, 4, 5, 6, 7, 8, 9], 3)) // [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
 console.log(listChunk([1, 2, 3, 4, 5, 6, 7, 8, 9], 0)) // []
 console.log(listChunk([1, 2, 3, 4, 5, 6, 7, 8, 9], -1)) // []

数组替换

var fruits = ["banana", "apple", "orange", "watermelon", "apple", "orange", "grape", "apple"];
fruits.splice(0, 2, "potato", "tomato");
console.log(fruits); // returns ["potato", "tomato", "orange", "watermelon", "apple", "orange", "grape", "apple"]

最大值

//普通数组
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: 3, name: 'jack'}, {id: 2, name: 'may'}];
const arr1 = Math.max.apply(Math, arr.map(item => {
    return item.id;
}));
console.log(arr1); // 3
const arr2 = arr.sort((a, b) => {
    return b.id - a.id
})[0].id;
console.log(arr2); // 3

数组求和

//普通数组
console.log([1, 2, 3, 4].reduce((x, y) => x + y));  //10
console.log([1, 2, 3, 4].reduce(function (prev, cur) {return prev + cur;}, 0)); //10
//数组对象
console.log([{age: 1}, {age: 2}].reduce(function (prev, cur) {return prev + cur.age;}, 0)); //3

数组合并

console.log([1, 2, 3, 4].concat([5, 6])); //[1, 2, 3, 4, 5, 6]
console.log([...[1, 2, 3, 4], ...[4, 5]]); //[1, 2, 3, 4, 4, 5]
const arrA = [1, 2], arrB = [3, 4];
Array.prototype.push.apply(arrA, arrB);
console.log(arrA);//[1, 2, 3, 4]

数组是否包含值

//普通数组
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
//数组对象
console.log([{age:1},{age:2}].some(v=>JSON.stringify(v)===JSON.stringify({age:2})));//true

数组每一项都满足

//普通数组
[1, 2, 3].every(item => {return item > 2}); //false
//数组对象
[{age: 3}, {age: 4}, {age: 5}].every(item => {return item.age > 2}); // true

数组有一项满足

//普通数组
[1, 2, 3].some(item => { return item > 2 });
//数组对象
[{ age: 3 }, { age: 4 }, { age: 5 }].some(item => { return item.age < 4 }); // true

数组排序

// 普通数组
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([1, 2, 3, 4].reverse()); //倒序
// 按字母顺序排序
["d", "c", "b", "a"].sort((a, b) => a.localeCompare(b))

版本号排序

//去除数组空值||空格
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']

对象转数组

Object.keys({ name: '张三', age: 14 }); //['name','age']
Object.values({ name: '张三', age: 14 }); //['张三',14]
Object.entries({ name: '张三', age: 14 }); //[[name,'张三'],[age,14]]

数组转对象

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' }]

数组解构

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

数组随机元素

var colors = ["blue", "white", "green", "navy", "pink", "purple", "orange", "yellow", "black", "brown"];
console.log(colors[(Math.floor(Math.random() * (colors.length) | 0))]);

指定长度的数字数组

const getNumArray = len => [...new Array(len).keys()];
const getNumArray = len => Array.from({length: len}, (v, k) => k);

随机打乱数组元素的顺序

const shuffle = items => items.sort(() => Math.random() - 0.5);
const shuffle = items => {
    for (var i = items.length-1; i >=0; i--) {
        var j = Math.floor(Math.random() * (i + 1));
        [items[j], items[i]] = [items[i], items[j]];
    }
    return items;
}

------ Object ------

对象变量属性

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

对象多余属性删除

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

对象嵌套属性解构

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

解构对象属性别名

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

解构对象属性默认值

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

拦截对象

  • 利用Object.defineProperty拦截对象
let obj = {name: '', age: '', sex: ''}, defaultName = ["这是姓名默认值1", "这是年龄默认值1", "这是性别默认值1"];
Object.keys(obj).forEach(key => {
    // 拦截整个object 对象,并通过get获取值,set设置值,vue 2.x的核心就是这个来监听
    Object.defineProperty(obj, key, { 
        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. prox是监听对象,不用深层遍历,defineProterty是监听属性;

    4. 利用defineProterty实现双向数据绑定(vue2.x采用的核心)

对象深度拷贝

对象有循环引用,会报错。

const objDeepClone = obj => {
    return clone(obj)
}
const isType = (obj, type) => {
    if (typeof obj !== 'object') return false;
    // 判断数据类型的经典方法:
    const typeString = Object.prototype.toString.call(obj);
    let flag;
    switch (type) {
        case 'Array':
            flag = typeString === '[object Array]';
            break;
        case 'Date':
            flag = typeString === '[object Date]';
            break;
        case 'RegExp':
            flag = typeString === '[object RegExp]';
            break;
        default:
            flag = false;
    }
    return flag;
};

/**
     * deep clone
     * @param  {[type]} parent object 需要进行克隆的对象
     * @return {[type]}        深克隆后的对象
     */
const clone = parent => {
    // 维护两个储存循环引用的数组
    const parents = []
    const children = []
    const _clone = parent => {
        if (parent === null) return null
        if (typeof parent !== 'object') return parent
        let child, proto;
        if (isType(parent, 'Array')) {
            // 对数组做特殊处理
            child = []
        } else if (isType(parent, 'RegExp')) {
            // 对正则对象做特殊处理
            child = new RegExp(parent.source, getRegExp(parent))
            if (parent.lastIndex) child.lastIndex = parent.lastIndex
        } else if (isType(parent, 'Date')) {
            // 对Date对象做特殊处理
            child = new Date(parent.getTime())
        } else {
            // 处理对象原型
            proto = Object.getPrototypeOf(parent)
            // 利用Object.create切断原型链
            child = Object.create(proto)
        }

        // 处理循环引用
        const index = parents.indexOf(parent)

        if (index !== -1) {
            // 如果父数组存在本对象,说明之前已经被引用过,直接返回此对象
            return children[index]
        }
        parents.push(parent)
        children.push(child)

        for (const i in parent) {
            // 递归
            child[i] = _clone(parent[i])
        }
        return child
    }
    return _clone(parent)
}
//测试
console.log(objDeepClone({
    name: '张三', age: 23,
    obj: {name: '李四', age: 46},
    arr: [1, 2, 3]
})); // { name: '张三', age: 23, obj: { name: '李四', age: 46 }, arr:[ 1, 2, 3 ] }

对象是否相等

如果用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

------ Function ------

函数隐式返回值

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

函数自执行

const Func = function() {}(); // 常用

(function() {})(); // 常用
(function() {}()); // 常用
[function() {}()];

new function() {};
new function() {}();
void function() {}();
typeof function() {}();
delete function() {}();

+ function() {}();
- function() {}();
~ function() {}();
! function() {}();

函数异步执行

  • 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 ------

随机的数字字母字符串

const genRandomString = len => {
    let rdmString = '';
    while(rdmString.length < len) {
        rdmString += (Math.random() * 37 | 0).toString(36);
   }
   return rdmString;
}
const genRandomString = len => {
    const text = 'abcdefghijklmnopqrstuvwxyz0123456789';
    const rdmIndex = text => Math.random() * text.length | 0;
    let rdmString = '';    
    for(; rdmString.length < len; rdmString += text.charAt(rdmIndex(text)));
    return rdmString;
}

字符串翻转

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

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"

url参数反序列化

// 一般会通过location.search拿到路由传递的参数,并进行反序列化得到对象
function parseUrlSearch(search) {
 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")); // { age: "25", name: "TYJ" }

转化为字符串

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 ------

特定范围内的随机数

const genRandom = (min, max) => (Math.random() * (max - min + 1) | 0) + min;
genRandom(1,5)

生成数字范围

let Start = 100, End = 105;
console.log([...new Array(End + 1).keys()].slice(Start));
//[100, 101, 102, 103, 104, 105]
console.log(Array.from({length: End - Start + 1}, (_,i) => Start + i));
//[100, 101, 102, 103, 104, 105]

将数字转换为二进制

var num = 200
console.log(num.toString(2)) // 11001000
num = 300
console.log(num.toString(2)) //100101100

字符“e”表示过多的零

var num = 20000000
var num2 = 2e7
console.log(num2) //20000000
20000000 == 2e7   //true

数字千分位

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"

字符串转数字

//方法一:用*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

判断小数是否相等

肯定有人会说这还不简单,直接用'==='比较;实际上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了

双位运算符

双位运算符比Math.floor(),Math.ceil()速度快,所以负数时,双位运算符和Math.ceil结果一致,正数时和Math.floor结果一致

~~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

取整和奇偶性判断

  • 取整

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 ------

判断数据类型

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

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

使用Boolean过滤数组假值

console.log([0, 1, false, 2, '', 3, 'a', 'e' * 23, NaN, 's', 34].filter(Boolean));//[1, 2, 3, "a", "s", 34]

短路运算

  • ||(或)
const flag = false || true //true
// 某个值为假时可以给默认值
const arr = false || []
  • &&(与)
const flag1 = false && true //false
const flag2 = true && true //true

switch 简写

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

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