day-066-sixty-six-20230509-对象拷贝-ES6的兼容-对象的合并-前端开发中的同步异步编程
对象拷贝
对象浅拷贝与对象深拷贝
-
拷贝的定义是开辟一块对象堆内存。
-
let obj2 = obj这个操作不是拷贝(克隆),仅仅是把obj的地址赋值给了obj2,两个对象共有相同的空间。 -
拷贝一定是不同的空间,只不过空间中的内容是相同的。
-
浅拷贝:只拷贝对象的第一级内容,对于更深层级的内容,新对象和原有对象,用的还是相同的堆内存地址。
-
深拷贝:所有级都是重新创建新的对象,并且把对应的内容拷贝一份过来!
- 深拷贝后,新对象和原有对象,彻底没有了关联。
-
-
对象浅拷贝
-
对象浅拷贝方式
-
扩展运算符
let obj = { x: 10, y: { x: 20, }, bool:true, time:new Date() }; let obj2 = {...obj} console.log(`obj-->`, obj); console.log(`obj2-->`, obj2); console.log(`obj2===obj-->`, obj2===obj);//false console.log(`obj2.y===obj.y-->`, obj2.y===obj.y);//true console.log(`obj2.fn===obj.fn-->`, obj2.fn===obj.fn);//true -
Object.assign()对象合并
-
Object.assign(obj1,obj2)两个对象合并
-
浅合并两个对象,用 obj2 中的每个成员,去替换 obj1 中的每个成员
- 两个对象都具备的成员以 obj2 为主
- obj1有的但obj2没有:依然存在
- obj1没有但obj2有:给obj1新增这个成员
-
修改的是obj1这个对象,obj2对象不变,最后返回的值也是obj1这个对象!
let obj1 = { x: 10, y: 20, }; let obj2 = { y: 30, z: 40, }; let res = Object.assign(obj1, obj2); console.log(res === obj1); //true console.log(obj1, obj2);
-
-
Object.assign(obj1,obj2,obj3,obj4)多个对象合并
-
多个对象合并,返回和修改的都是obj1
obj2替换obj1obj3替换obj1- …
-
let obj = { x: 10, y: { z: 20, }, bool: true, time: new Date(), fn() {}, [Symbol("AA")]: "AAA", }; let obj2 = Object.assign({}, obj); console.log(`obj-->`, obj); console.log(`obj2-->`, obj2); console.log(`obj2===obj-->`, obj2===obj);//false console.log(`obj2.y===obj.y-->`, obj2.y===obj.y);//true console.log(`obj2.fn===obj.fn-->`, obj2.fn===obj.fn);//true -
-
笨办法:迭代源对象,把每一项分别值给目标对象。
- 也是最通用和灵活的。
let obj = { x: 10, y: { z: 20, }, bool: true, time: new Date(), fn() {}, [Symbol("AA")]: "AAA", }; let obj2 = {}; //_.each是之前写过的each函数,用来对一个对象进行forEach循环的。 _.each(obj, (value, key) => { obj2[key] = value; }); console.log(`obj-->`, obj); console.log(`obj2-->`, obj2); console.log(`obj2===obj-->`, obj2 === obj); //false console.log(`obj2.y===obj.y-->`, obj2.y === obj.y); //true console.log(`obj2.fn===obj.fn-->`, obj2.fn === obj.fn); //true-
相当于:
let obj = { x: 10, y: { z: 20, }, bool: true, time: new Date(), fn() {}, [Symbol("AA")]: "AAA", }; let obj2 = {}; let keys = Object.getOwnPropertyNames(obj); if (typeof Symbol !== "undefined") { keys = keys.concat(Object.getOwnPropertySymbols(obj)); } keys.forEach((value, key) => { obj2[key] = value; }); console.log(`obj-->`, obj); console.log(`obj2-->`, obj2); console.log(`obj2===obj-->`, obj2 === obj); //false console.log(`obj2.y===obj.y-->`, obj2.y === obj.y); //true console.log(`obj2.fn===obj.fn-->`, obj2.fn === obj.fn); //true
-
-
数组的浅拷贝
[...arr]Object.assign([], arr)[].concat(arr)arr.slice(0)arr.map(item => item)Array.from(arr)- …
let arr = [10, 20, 30, [40, 50]]; let arr2 = [...arr]; // let arr2 = Object.assign([], arr) // let arr2 = [].concat(arr) // let arr2 = arr.slice(0) // let arr2 = arr.map(item => item) console.log(arr, arr2); console.log(arr === arr2); //false console.log(arr[3] === arr2[3]); //true
对象深拷贝
-
对象深拷贝的方法
-
JSON.parse(JSON.stringify(变量))
-
对于正常的一些属性值,而且属性名都是字符串类型的,可以直接基于JSON.stringify()/JSON.parse()实现深拷贝。
- 例如:数字、字符串、布尔值、普通对象、普通数组…
-
原理:先把对象变为JSON字符串,再把字符串转换为对象。
-
此过程会把所有级别内容的内存空间,都重新创建一份新的。
// 对于正常的一些属性值,例如:数字/字符串/布尔/普通对象/数组...,而且属性名都是字符串类型的,我们可以直接基于 JSON.stringify/parse 实现深拷贝!! // 原理:先把对象变为JSON字符串,再把字符串转换为对象「此过程会把所有级别的内容空间,都重新创建一份新的」 let obj = { x: 10, y: { z: 20, arr: [10, 20, { b: 300 }], }, bool: true, str: "珠峰培训", }; let obj2 = JSON.parse(JSON.stringify(obj)); console.log(obj2); console.log(obj2 === obj); //false console.log(obj2.y === obj.y); //false console.log(obj2.y.arr === obj.y.arr); //false console.log(obj2.y.arr[2] === obj.y.arr[2]); //false
-
-
JSON.stringify()具备很强的局限性,因为它是ES3的,当时还没ES6相关的规则。
-
无法处理BigInt类型的值,会报错
Do not know how to serialize a BigInt。 -
成员值是
undefined或Symbol或函数的,会直接把此成员删除掉。 -
把
正则对象和错误对象直接变为{},这样等到基于parse转换为对象的时候,正则值会被处理为空对象。 -
把日期对象变为日期字符串
-
有点另类,是调用
日期对象.toJSON()方法来进行处理的,如'2023-05-09T03:14:12.344Z'。- 这个字符串基于
JSON.parse()转换为对象的时候,是无法再转换为日期对象的。
- 这个字符串基于
-
-
属性名是
Symbol类型的,也会直接把此成员删除掉。 -
一旦对象出现套娃循环操作,则直接报错
Converting circular structure to JSON。
-
…
let obj = { name: "珠峰", age: 13, bool: true, n: null, u: undefined, sym: Symbol("sym"), //big: 10n,//报错`Do not know how to serialize a BigInt`; list: [10, 20, { a: 100, b: 200 }], reg: /\d+/, time: new Date(), err: new Error("xxx"), ke: { js: "基础课", web: "高级课", arr: [1000, 2000] }, [Symbol("KEY")]: 100, fn: function () {}, }; //obj.obj = obj;//报错`Converting circular structure to JSON` console.log(JSON.parse(JSON.stringify(obj)))
-
-
-
迭代源对象,把每一项分别值给目标对象
-
1.数组对象的深浅拷贝.js-简易版//=================================== /* 深拷贝的处理 */ let obj = { name: "珠峰", age: 13, bool: true, n: null, u: undefined, sym: Symbol("sym"), big: 10n, list: [10, 20, { a: 100, b: 200 }], reg: /\d+/, time: new Date(), err: new Error("xxx"), ke: { js: "基础课", web: "高级课", arr: [1000, 2000] }, [Symbol("KEY")]: 100, fn: function () {}, }; obj.obj = obj; // 实现数组和对象的深拷贝 //里面的_就是自己写的utils.js中的函数 const clone = function clone(obj, exist = []) { if (!_.isObject(obj)) return obj; //原始值类型不进行处理 let type = _.isType(obj), Ctor = obj.constructor; if (type === "regexp" || type === "date") return new Ctor(obj); if (type === "error") return new Ctor(obj.message); if (!_.isArray(obj) && !_.isPlainObject(obj)) return obj; // 防止套娃操作 if (exist.indexOf(obj) >= 0) return obj; exist.push(obj); // 操作的是数组和纯粹对象 let result = new Ctor(); _.each(obj, (value, key) => { result[key] = clone(value, exist); //基于递归的方式,对每一个成员值,再次进行拷贝 }); return result; }; let obj2 = clone(obj); console.log(obj2); console.log(obj2 === obj); //false console.log(obj2.list === obj.list); //false console.log(obj2.list[2] === obj.list[2]); //false console.log(obj2.reg === obj.reg); //false console.log(obj2.fn === obj.fn); //true console.log(obj2.obj === obj); //true console.log(obj2.obj === obj2); //false -
1.数组对象的深浅拷贝.js-个人优化版//=================================== /* 深拷贝的处理 */ let obj = { name: "珠峰", age: 13, bool: true, n: null, u: undefined, sym: Symbol("sym"), big: 10n, list: [10, 20, { a: 100, b: 200 }], reg: /\d+/, time: new Date(), err: new Error("xxx"), ke: { js: "基础课", web: "高级课", arr: [1000, 2000] }, [Symbol("KEY")]: 100, fn: function () {}, }; obj.obj = obj; // 实现数组和对象的深拷贝 //里面的_就是自己写的utils.js中的函数 const clone = function clone(obj, exist = []) { // 原始值类型不进行处理。 if (!_.isObject(obj)) { return obj; } // 对特定类型进行创建。 let type = _.isType(obj); let Ctor = obj.constructor; // 正则或时间对象。 if (type === "regexp" || type === "date") { return new Ctor(obj); } // 错误对象; if (type === "error") { return new Ctor(obj.message); } // 非上方特定类型并且非数组或纯粹对象,直接返回-如函数。 if (!_.isArray(obj) && !_.isPlainObject(obj)) { return obj; } // 操作的是数组和纯粹对象 let result = new Ctor(); // // 防止套娃循环操作-这个个人觉得可以做一个循环,push([obj,result]),找到obj后,返回出result // if (exist.indexOf(obj) >= 0) { // return obj; // } // exist.push(obj); // 防止套娃操作 let index = exist.findIndex((item) => item[0] === obj); if (index >= 0) { return exist[index][1]; } exist.push([obj, result]); // 操作的是数组和纯粹对象 _.each(obj, (value, key) => { result[key] = clone(value, exist); //基于递归的方式,对每一个成员值,再次进行拷贝。 }); return result; }; let obj2 = clone(obj); console.log(obj2); console.log(obj2 === obj); //false console.log(obj2.list === obj.list); //false console.log(obj2.list[2] === obj.list[2]); //false console.log(obj2.reg === obj.reg); //false console.log(obj2.fn === obj.fn); //true console.log(obj2.obj === obj); //false console.log(obj2.obj === obj2); //true -
utils.js(function (global, factory) { "use strict"; if (typeof module === "object" && typeof module.exports === "object") { module.exports = factory(global, true); return; } factory(global); })( typeof window !== "undefined" ? window : this, function factory(window, noGlobal) { /* 检测数据类型 */ const toString = Object.prototype.toString, isArray = Array.isArray, typeReg = /^(object|function)$/, fnToString = Function.prototype.toString; // 万能检测数据类型的方法 const isType = function isType(obj) { if (obj == null) return obj + ""; let type = typeof obj, reg = /^\[object (\w+)\]$/; return !typeReg.test(type) ? type : reg.exec(toString.call(obj))[1].toLowerCase(); }; // 检测是否为对象 const isObject = function isObject(obj) { return obj !== null && typeReg.test(typeof obj); }; // 检测是否是window对象 const isWindow = function isWindow(obj) { return obj != null && obj === obj.window; }; // 检测是否为函数 const isFunction = function isFunction(obj) { return typeof obj === "function"; }; // 检测是否为数组或者伪数组 const isArrayLike = function isArrayLike(obj) { if (isArray(obj)) return true; let length = !!obj && "length" in obj && obj.length; if (isFunction(obj) || isWindow(obj)) return false; return ( length === 0 || (typeof length === "number" && length > 0 && length - 1 in obj) ); }; // 检测是否为一个纯粹的对象(标准普通对象) const isPlainObject = function isPlainObject(obj) { if (isType(obj) !== "object") return false; let proto, Ctor; proto = Object.getPrototypeOf(obj); if (!proto) return true; Ctor = proto.hasOwnProperty("constructor") && proto.constructor; return ( isFunction(Ctor) && fnToString.call(Ctor) === fnToString.call(Object) ); }; // 检测是否为空对象 const isEmptyObject = function isEmptyObject(obj) { if (!isObject(obj)) throw new TypeError(`obj is not an object`); // let keys = Reflect.ownKeys(obj) let keys = Object.getOwnPropertyNames(obj); if (typeof Symbol !== "undefined") keys = keys.concat(Object.getOwnPropertySymbols(obj)); return keys.length === 0; }; // 检测是否为有效数字 const isNumeric = function isNumeric(obj) { let type = isType(obj); return (type === "number" || type === "string") && !isNaN(+obj); }; /* 其它基础方法 */ // 迭代数组/伪数组/对象「支持中途结束循环」 const each = function each(obj, callback) { if (typeof callback !== "function") callback = () => {}; if (typeof obj === "number" && !isNaN(obj) && obj > 0) obj = new Array(obj); if (typeof obj === "string") obj = Object(obj); if (!isObject(obj)) return obj; if (isArrayLike(obj)) { for (let i = 0; i < obj.length; i++) { let item = obj[i]; let res = callback.call(obj, item, i); if (res === false) break; } return obj; } // let keys = Reflect.ownKeys(obj) let keys = Object.getOwnPropertyNames(obj); if (typeof Symbol !== "undefined") keys = keys.concat(Object.getOwnPropertySymbols(obj)); for (let i = 0; i < keys.length; i++) { let key = keys[i], value = obj[key]; let res = callback.call(obj, value, key); if (res === false) break; } return obj; }; // 具备有效期的LocalStorage存储 const storage = { set(key, value) { localStorage.setItem( key, JSON.stringify({ time: +new Date(), value, }) ); }, get(key, cycle = 2592000000) { cycle = +cycle; if (isNaN(cycle)) cycle = 2592000000; let data = localStorage.getItem(key); if (!data) return null; let { time, value } = JSON.parse(data); if (+new Date() - time > cycle) { storage.remove(key); return null; } return value; }, remove(key) { localStorage.removeItem(key); }, }; // 万能的日期格式化工具 const formatTime = function formatTime(time, template) { try { if (time == null) time = new Date().toLocaleString("zh-CN", { hour12: false }); if (typeof template !== "string") template = "{0}/{1}/{2} {3}:{4}:{5}"; let arr = []; if (/^\d{8}$/.test(time)) { let [, $1, $2, $3] = /^(\d{4})(\d{2})(\d{2})$/.exec(time); arr.push($1, $2, $3); } else { arr = time.match(/\d+/g); } return template.replace(/\{(\d+)\}/g, (_, $1) => { let item = arr[$1] || "00"; if (item.length < 2) item = "0" + item; return item; }); } catch (_) { return ""; } }; // 转移“_”的使用权 let origin = null; const noConflict = function noConflict() { if (window._ === utils) { window._ = origin; } return utils; }; /* 暴露API */ const utils = { isType, isObject, isArray, isArrayLike, isWindow, isFunction, isPlainObject, isEmptyObject, isNumeric, noConflict, each, storage, formatTime, }; if (typeof noGlobal === "undefined") { origin = window._; window.utils = window._ = utils; } return utils; } ); -
index.html<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>珠峰零基础高薪就业班</title> <!-- IMPORT CSS --> </head> <body> <!-- IMPORT JS --> <script src="utils.js"></script> <!-- <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script> --> <script src="1.数组对象的深浅拷贝.js"></script> </body> </html>
-
-
ES6的兼容
-
ES6兼容
-
直接基于babel和对应的语法包@babel/preset-env就可以处理了
-
有一些特殊的语法,需要依赖一些babel的插件
- 例如:装饰器
-
-
-
内置API的兼容:Promise、Object.is()…
-
上面的语法包是无法解决它的兼容的,它兼容的解决方法只有一种
-
原理:按照官方的说法,就是对内置API进行重写,重新添加一些对象
-
解决方式
-
corejs
-
@babel/polyfill
-
它们内部包含了很多ES6常用内置API的重写
- fetch()
- [].fill()
- 这一类可能没有polyfill。
-
-
-
-
项目中使用
caniuse.com查看浏览器兼容性。-
使用
babeljs.io/repl可以查看各种设置条件下的使用babel进行处理后得到的代码。- 这些代码需要转化,还要对
webpack及/.browserslistrc及/babel.config.js进行配置。
- 这些代码需要转化,还要对
-
也可以使用lodash去修改一些原生的方法。
-
lodash中的方法。
_.cloneDeep 深拷贝 _.assign 浅合并 _.merge 深合并
-
-
修改默认方法
-
用Object.defineProperty()修改快有对象的属性。
/* //merge: 实现数组和对象的合并 @params deep: 是否为深合并,布尔值:false浅合并,true深合并 target: 要被合并/替换的对象 objs: 替换target的对象集合 @return target: 被替换后的对象 */ const merge = function merge(deep = false, target, ...objs) {}; // 修改默认方法。 // 实现浅合并 Object.defineProperty(Object, "assign", { configurable: true, writable: true, enumerable: false, value: function shallowMerge(...parsms) { merge(deep, ...parsms); }, }); // 实现深合并 Object.defineProperty(Object, "assignDeep", { configurable: true, writable: true, enumerable: false, value: function deepMerge(...parsms) { merge(deep, ...parsms); }, });// 供外界调用的深浅合并方法 const def = function def(obj, key, value) { Object.defineProperty(obj, key, { configurable: true, writable: true, enumerable: false, value, }); }; def(Object, "assign", (...params) => merge([], false, ...params)); def(Object, "assignDeep", (...params) => merge([], true, ...params)); -
自然也可以修改全局变量,里面有一些方法。
对象的合并
对象的浅合并
-
Object.assign()处理的是浅合并
-
只合并第一级,或者不论属性值是什么类型,直接用后面对象中的值,替换前面对象中的值
let obj1 = { name: "张三", age: 25, hobby: { music: 100, jump: 80, }, }; obj1.AAA = obj1; let obj2 = { name: "李四", age: 22, sex: 0, hobby: { read: 100, music: 90, }, }; obj2.AAA = obj2; let obj3 = { name: "王五", age: 20, height: "158cm", score: { math: 100, chinese: 90, }, }; // Object.assign:处理的浅合并「只合并第一级,或者不论属性值是啥类型,直接用obj2中的值,替换obj1中的值」 let obj = Object.assign({}, obj1, obj2); console.log(obj);
-
-
通过循环,直接把第一层对象的成员赋值给新对象
对象的深合并
-
原理是通过循环,一个属性一个属性地合并
-
index.html<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>珠峰零基础高薪就业班</title> <!-- IMPORT CSS --> </head> <body> <!-- IMPORT JS --> <script src="utils.js"></script> <!-- <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script> --> <script src="2.数组对象的深浅合并.js"></script> </body> </html> -
2.数组对象的深浅合并.jslet obj1 = { name: "张三", age: 25, hobby: { music: 100, jump: 80, }, }; obj1.AAA = obj1; let obj2 = { name: "李四", age: 22, sex: 0, hobby: { read: 100, music: 90, }, }; obj2.AAA = obj2; let obj3 = { name: "王五", age: 20, height: "158cm", score: { math: 100, chinese: 90, }, }; /* merge:实现数组和对象的合并 @params deep:布尔 false浅合并 true深合并 target:要被合并/替换的对象 objs:替换target的“对象集合” @return target 被替换后的对象 */ //里面的_就是自己写的utils.js中的函数 const isArrayOrObject = (obj) => { return _.isArray(obj) || _.isPlainObject(obj); }; const merge = function merge(exist, deep, target, ...objs) { if (!isArrayOrObject(target)) throw new TypeError(`被替换的目标必须是数组或者纯粹对象`); if (objs.length === 0) return target; //如果不具备替换的对象,则直接返回目标对象 // 迭代 objs 对象集合中的每一项 objs.forEach((obj) => { // obj:每一个替换的对象,我们需要拿它去替换目标对象target「但要保证它也得是数组/对象」 if (!isArrayOrObject(obj)) return; // 防止套娃 if (exist.indexOf(obj) >= 0) return; exist.push(obj); // 依次迭代这个对象中的每个成员,去替换target目标对象中,同名的这个成员 _.each(obj, (value, key) => { let targetValue = target[key]; if (deep && isArrayOrObject(targetValue) && isArrayOrObject(value)) { // 把这两个对象,基于递归的方式,再次合并一下 target[key] = merge(exist, deep, targetValue, value); return; } // 浅合并「或者直接让 替换对象值 替换 目标对象值」 target[key] = value; }); }); return target; }; // 供外界调用的深浅合并方法 const def = function def(obj, key, value) { Object.defineProperty(obj, key, { configurable: true, writable: true, enumerable: false, value, }); }; def(Object, "assign", (...params) => merge([], false, ...params)); def(Object, "assignDeep", (...params) => merge([], true, ...params)); // 测试 console.log(Object.assign({}, obj1, obj2, obj3)); //->merge(false, {}, obj1, obj2, obj3) console.log(Object.assignDeep({}, obj1, obj2, obj3)); //->merge(true, {}, obj1, obj2, obj3) -
utils.js(function (global, factory) { "use strict"; if (typeof module === "object" && typeof module.exports === "object") { module.exports = factory(global, true); return; } factory(global); })( typeof window !== "undefined" ? window : this, function factory(window, noGlobal) { /* 检测数据类型 */ const toString = Object.prototype.toString, isArray = Array.isArray, typeReg = /^(object|function)$/, fnToString = Function.prototype.toString; // 万能检测数据类型的方法 const isType = function isType(obj) { if (obj == null) return obj + ""; let type = typeof obj, reg = /^\[object (\w+)\]$/; return !typeReg.test(type) ? type : reg.exec(toString.call(obj))[1].toLowerCase(); }; // 检测是否为对象 const isObject = function isObject(obj) { return obj !== null && typeReg.test(typeof obj); }; // 检测是否是window对象 const isWindow = function isWindow(obj) { return obj != null && obj === obj.window; }; // 检测是否为函数 const isFunction = function isFunction(obj) { return typeof obj === "function"; }; // 检测是否为数组或者伪数组 const isArrayLike = function isArrayLike(obj) { if (isArray(obj)) return true; let length = !!obj && "length" in obj && obj.length; if (isFunction(obj) || isWindow(obj)) return false; return ( length === 0 || (typeof length === "number" && length > 0 && length - 1 in obj) ); }; // 检测是否为一个纯粹的对象(标准普通对象) const isPlainObject = function isPlainObject(obj) { if (isType(obj) !== "object") return false; let proto, Ctor; proto = Object.getPrototypeOf(obj); if (!proto) return true; Ctor = proto.hasOwnProperty("constructor") && proto.constructor; return ( isFunction(Ctor) && fnToString.call(Ctor) === fnToString.call(Object) ); }; // 检测是否为空对象 const isEmptyObject = function isEmptyObject(obj) { if (!isObject(obj)) throw new TypeError(`obj is not an object`); // let keys = Reflect.ownKeys(obj) let keys = Object.getOwnPropertyNames(obj); if (typeof Symbol !== "undefined") keys = keys.concat(Object.getOwnPropertySymbols(obj)); return keys.length === 0; }; // 检测是否为有效数字 const isNumeric = function isNumeric(obj) { let type = isType(obj); return (type === "number" || type === "string") && !isNaN(+obj); }; /* 其它基础方法 */ // 迭代数组/伪数组/对象「支持中途结束循环」 const each = function each(obj, callback) { if (typeof callback !== "function") callback = () => {}; if (typeof obj === "number" && !isNaN(obj) && obj > 0) obj = new Array(obj); if (typeof obj === "string") obj = Object(obj); if (!isObject(obj)) return obj; if (isArrayLike(obj)) { for (let i = 0; i < obj.length; i++) { let item = obj[i]; let res = callback.call(obj, item, i); if (res === false) break; } return obj; } // let keys = Reflect.ownKeys(obj) let keys = Object.getOwnPropertyNames(obj); if (typeof Symbol !== "undefined") keys = keys.concat(Object.getOwnPropertySymbols(obj)); for (let i = 0; i < keys.length; i++) { let key = keys[i], value = obj[key]; let res = callback.call(obj, value, key); if (res === false) break; } return obj; }; // 具备有效期的LocalStorage存储 const storage = { set(key, value) { localStorage.setItem( key, JSON.stringify({ time: +new Date(), value, }) ); }, get(key, cycle = 2592000000) { cycle = +cycle; if (isNaN(cycle)) cycle = 2592000000; let data = localStorage.getItem(key); if (!data) return null; let { time, value } = JSON.parse(data); if (+new Date() - time > cycle) { storage.remove(key); return null; } return value; }, remove(key) { localStorage.removeItem(key); }, }; // 万能的日期格式化工具 const formatTime = function formatTime(time, template) { try { if (time == null) time = new Date().toLocaleString("zh-CN", { hour12: false }); if (typeof template !== "string") template = "{0}/{1}/{2} {3}:{4}:{5}"; let arr = []; if (/^\d{8}$/.test(time)) { let [, $1, $2, $3] = /^(\d{4})(\d{2})(\d{2})$/.exec(time); arr.push($1, $2, $3); } else { arr = time.match(/\d+/g); } return template.replace(/\{(\d+)\}/g, (_, $1) => { let item = arr[$1] || "00"; if (item.length < 2) item = "0" + item; return item; }); } catch (_) { return ""; } }; // 转移“_”的使用权 let origin = null; const noConflict = function noConflict() { if (window._ === utils) { window._ = origin; } return utils; }; /* 暴露API */ const utils = { isType, isObject, isArray, isArrayLike, isWindow, isFunction, isPlainObject, isEmptyObject, isNumeric, noConflict, each, storage, formatTime, }; if (typeof noGlobal === "undefined") { origin = window._; window.utils = window._ = utils; } return utils; } );
-
前端开发中的同步异步编程
前端异步任务
- 微任务 microtask
- 宏任务 macrotask
进程与线程
-
进程和线程
-
一个进程中,包含一到多个线程
-
进程:指的是一个程序(或者是浏览器打开一个页卡,就是开辟一个进程)
-
线程:干活的,一个程序中可能需要同时做多件事情,此时就需要开辟多个线程
- 一个线程同时只能干一件事件
-
-
-
同步编程
-
只有一个线程,所以同时只能干一件事件,只有当上一件事件处理完毕,下一事件才能处理。
- 线程有运行和空闲两个状态
-
-
异步编程
- 当前进程(程序)具备多个线程,可以同时处理多件事件,上一件事件即便没有处理完毕,也可以安排一个其它线程去处理,主线程可以继续向下执行!
-
现代浏览器是一个多进程多线程的软件。
-
浏览器是多线程的
-
每一次加载页面,浏览器都会分配很多线程,去同时做一些事件,此时来提高页面的渲染速度。
-
GUI渲染线程:渲染和解析HTML与css的,以及最后绘制出相应的页面。- HTML与CSS规范是w3c的。
-
JS引擎线程:渲染和解析JavaScript代码的。- JavaScript中ES是ECMA委员会的。
- 这个就是主线程。
-
定时器监听线程:监听定时器的。 -
事件监听线程:监听DOM事件的。 -
HTTP网络请求线程:发送网络请求,从服务器获取资源的。 -
…
-
-
-
-
JavaScript是单线程的:因为浏览器只分配了一个
JS引擎线程去渲染解析JavaScript代码。-
在JavaScript中大部分代码都是同步代码。
- 例如:循环…
-
但是JavaScript中也有异步代码。
- 通过事件循环EventLoop来做。
-