Object
1.对象变量属性
const flag = true;
const obj = {
a: 0,
[flag ? "c" : "d"]: 2
};
// obj => { a: 0, c: 2 }
2.对象多余属性删除
const { name, age, ...obj } = { name: '张三', age: 13, dec: '描述1', info: '信息' }
console.log(name) // 张三
console.log(age) // 13
console.log(obj) // {dec: '描述1', info: '信息' }
3.对象嵌套属性解构
const { info:{ dec} } = { name: '张三', age: 13, info:{dec: '描述1', info: '信息' }}
console.log(dec) // 描述1
4.解构对象属性别名
const { name:newName } = { name: '张三', age: 13 }
console.log(newName) // 张三
5.解构对象属性默认值
const { dec='这是默认dec值' } = { name: '张三', age: 13 }
console.log(dec) //这是默认dec值
6.拦截对象
利用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采用的核心)
7.对象深度拷贝
JSON.stringify深度克隆对象;
1.无法对函数 、RegExp等特殊对象的克隆;
2.会抛弃对象的constructor,所有的构造函数会指向Object;
3.对象有循环引用,会报错
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 ] }
对象深度克隆实际上就是要兼容Array,RegExp,Date,Function类型;
克隆函数可以用正则取出函数体和参数,再定义一个函数将取出来的值赋值进去
详细请戳对象深度拷贝
8.对象是否相等
如果用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的特殊类型是否相等
9.对象转化为字符串
通过字符串+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
10.函数隐式返回值
(()=>3)() //3
(()=>(
3
))()
函数省略大括号,或者将大括号改成小括号可以确保代码以单个语句的形式进行求值
11.函数自执行
const Func = function() {}(); // 常用
(function() {})(); // 常用
(function() {}()); // 常用
[function() {}()];
new function() {};
new function() {}();
void function() {}();
typeof function() {}();
delete function() {}();
+ function() {}();
- function() {}();
~ function() {}();
! function() {}();
12.函数异步执行
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
13.字符串翻转
function reverseStr(str = "") {
return str.split("").reduceRight((t, v) => t + v);
}
const str = "reduce123";
console.log(reverseStr(str)); // "123recuder"
14.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"
15.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" }
16.转化为字符串
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
17.数字千分位
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"
18.字符串转数字
方法一
用*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
19.判断小数是否相等
肯定有人会说这还不简单,直接用’==='比较;
实际上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了
20.双位运算符
双位运算符比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结果一致
21.取整和奇偶性判断
取整
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
22.判断数据类型
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
23.使用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 ]
24.短路运算
||(或)
const flag = false || true //true
// 某个值为假时可以给默认值
const arr = false || []
&&(与)
const flag1 = false && true //false
const flag2 = true && true //true
25.switch 简写
可以用对象替代switch,提高代码可读性
switch(a) {
case '张三':
return 'age是12'
case '李四':
return 'age是120'
}
// 使用对象替换后
const obj ={
'张三': 'age12',
'李四': 'age120',
}
console.log(obj['张三'])
来源:web前端开发公众号