Notion – The all-in-one workspace for your notes, tasks, wikis, and databases.
- var a = {name:1, sya:function(){ console.log("打印")},age:undefined}; // {name:1,sya:f}
-
- var b = JSON.parse(JSON.stringify(a)); // {name:1}
- function Obj() {
- this.func = function () {
- alert(1)
- };
- this.obj = {
- a: 1
- };
- this.arr = [1, 2, 3];
- this.und = undefined;
- this.reg = /123/;
- this.date = new Date(0);
- this.NaN = NaN;
- this.infinity = Infinity;
- this.sym = Symbol(1);
- }
- let obj1 = new Obj();
- Object.defineProperty(obj1, 'innumerable', {
- enumerable: false,
- value: 'innumerable'
- });
- console.log('obj1', obj1);
- let str = JSON.stringify(obj1);
- let obj2 = JSON.parse(str);
- console.log('obj2', obj2);
- // 深拷贝
- var sourceObj = {
- name: 'tt',
- age: 18,
- job: 'web',
- friends: ['t1', 't2']
- }
-
- // util作为判断变量具体类型的辅助模块
- var util = (function() {
- var class2Type = {}
- var objTypes = ["Null","Undefined","Number","Boolean","String","Object","Function","Array","RegExp","Date”]
- objTypes.forEach(function(item) {
- class2Type['[object ' + item + ']'] = item.toLowerCase()
- })
- function isType(obj, type) {
- return getType(obj) === type
- }
- function getType(obj) {
- return class2type[Object.prototype.toString.call(obj)] || 'object'
- }
- return {
- isType: isType,
- getType: getType
- }
- })()
- // deep参数用来判断是否是深度复制
- function copy(obj, deep){
- // 如果obj不是对象,那么直接返回值就可以了
- if(obj == null || typeof obj !== 'object'){
- return obj
- }
- // 定义需要的局部变量,根据obj的类型来调整target的类型
- var i,
- target = util.isType(obj,"array") ? [] : {},
- value,
- valueType
- for(i in obj){
- value = obj[i]
- valueType = util.getType(value)
- // 只有在明确执行深复制,并且当前的value是数组或对象的情况下才执行递归复制
- if(deep && (valueType === "array" || valueType === "object")){
- target[i] = copy(value)
- }else{
- target[i] = value
- }
- }
- return target
- }
- var targetObj = copy(sourceObj, true);
- targetObj.friends.push ('t3');
- console.log(sourceObj) // ['t1', 't2']
- // 深拷贝
- Object.prototype.clone = function() {
- var Constructor = this.constructor
- var obj = new Constructor()
- for (var attr in this) {
- if (this.hasOwnProperty(attr)) {
- if (typeof this[attr] !== 'function') {
- if (this[attr] === null) {
- obj[attr] = null
- } else {
- obj[attr] = this[attr].clone()
- }
- }
- }
- }
- return obj
- }
-
- /* Method of Array*/
- Array.prototype.clone = function() {
- var thisArr = this.valueOf()
- var newArr = []
- for (var i = 0; i < thisArr.length; i++) {
- newArr.push(thisArr[i].clone())
- }
- return newArr
- }
- /* Method of Boolean, Number, String*/
- Boolean.prototype.clone = function() {
- return this.valueOf()
- }
- Number.prototype.clone = function() {
- return this.valueOf()
- }
- String.prototype.clone = function() {
- return this.valueOf()
- }
- /* Method of Date*/
- Date.prototype.clone = function() {
- return new Date(this.valueOf())
- }
- /* Method of RegExp*/
- RegExp.prototype.clone = function() {
- var pattern = this.valueOf()
- var flags = ''
- flags += pattern.global ? 'g' : ''
- flags += pattern.ignoreCase ? 'i' : ''
- flags += pattern.multiline ? 'm' : ''
- return new RegExp(pattern.source, flags)
- }
- function deepClone(source){
- // 判断复制的目标是数组还是对象
- const targetObj = source.constructor === Array ? [] : {};
- for(let keys in source){ // 遍历目标
- if(source.hasOwnProperty(keys)){
- if(source[keys] && typeof source[keys] === 'object'){
- targetObj[keys] = source[keys].constructor === Array ? [] : {};
- targetObj[keys] = deepClone(source[keys]);
- }else{ // 如果不是,就直接赋值
- targetObj[keys] = source[keys];
- }
- }
- }
- return targetObj;
- }
- //测试一下
- var a = {name:1, sya:function(){ console.log("打印")},age:undefined};
- var b = deepClone(a);
- b.name=2;
- console.log(a)//{name:1,sya:f,age:undefined}
- console.log(b)//{name:2,sya:f,age:undefined}
-
- // 深拷贝函数并不能复制不可枚举的属性以及 Symbol 类型;
- // 这种方法只是针对普通的引用类型的值做递归复制,而对于 Array、Date、RegExp、Error、Function 这样的引用类型并不能正确地拷贝;
- // 对象的属性里面成环,即循环引用没有解决。
针对能够遍历对象的不可枚举属性以及 Symbol 类型,我们可以使用 Reflect.ownKeys 方法;
当参数为 Date、RegExp 类型,则直接生成一个新的实例返回;
利用 Object 的 getOwnPropertyDescriptors 方法可以获得对象的所有属性,以及对应的特性,顺便结合 Object 的 create 方法创建一个新对象,并继承传入原对象的原型链;
利用 WeakMap 类型作为 Hash 表,因为 WeakMap 是弱引用类型,可以有效防止内存泄漏,作为检测循环引用很有帮助,如果存在循环,则引用直接返回 WeakMap 存储的值。
- const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null);
-
- const deepClone = function (obj, hash = new WeakMap()) {
- if (obj.constructor === Date) return new Date(obj) // 日期对象直接返回一个新的日期对象
- if (obj.constructor === RegExp) return new RegExp(obj) // 正则对象直接返回一个新的正则对象
- if (hash.has(obj)) return hash.get(obj) // 如果循环引用了就用 weakMap 来解决
- let allDesc = Object.getOwnPropertyDescriptors(obj); // 遍历传入参数所有键的特性
- // ts报错
- // (<any>Object).getOwnPropertyDescriptors(obj); // 遍历传入参数所有键的特性
- // (Object as any).getOwnPropertyDescriptors(obj); // 遍历传入参数所有键的特性
- let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc); //继承原型链
- hash.set(obj, cloneObj);
- for (let key of Reflect.ownKeys(obj)) {
- cloneObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key], hash) : obj[key]
- }
- return cloneObj
- }
-
- // 下面是验证代码
- let obj = {
- num: 0,
- str: '',
- boolean: true,
- unf: undefined,
- nul: null,
- obj: {
- name: '我是一个对象',
- id: 1
- },
- arr: [0, 1, 2],
- func: function () {
- console.log('我是一个函数')
- },
- date: new Date(0),
- reg: new RegExp('/我是一个正则/ig'),
- [Symbol('1')]: 1,
- };
- Object.defineProperty(obj, 'innumerable', {
- enumerable: false,
- value: '不可枚举属性'
- });
- obj = Object.create(obj, Object.getOwnPropertyDescriptors(obj))
- obj.loop = obj // 设置loop成循环引用的属性
- let cloneObj = deepClone(obj)
- cloneObj.arr.push(4)
- console.log('obj', obj)
- console.log('cloneObj', cloneObj)
- // 深拷贝 lodash _.cloneDeep()
- var _ = require("lodash");
- var obj1 = {
- a: 1,
- b: { f: { g: 1 } },
- c: [1, 2, 3],
- };
- var obj2 = _.cloneDeep(obj1);
- console.log(obj1.b.f === obj2.b.f);
- // 深拷贝 jQuery.extend()
- // $.extend(deepCopy, target, object1, [objectN])//第一个参数为true,就是深拷贝
-
- var $ = require("jquery");
- var obj1 = {
- a: 1,
- b: { f: { g: 1 } },
- c: [1, 2, 3],
- };
- var obj2 = $.extend(true, {}, obj1);
- console.log(obj1.b.f === obj2.b.f);
MessageChannel 方式
- // 深度拷贝 使用 MessageChannel 方式
-
- function structuralClone(obj) {
- return new Promise((resolve) => {
- const { port1, port2 } = new MessageChannel();
- port2.onmessage = (ev) => resolve(ev.data);
- port1.postMessage(obj);
- });
- }
- const obj = {
- name: 'SeriousLose',
- obj: {
- name: 'fly',
- },
- };
- structuralClone(obj).then((res) => {
- console.log(res);
- });
History API 方式
- // history.replaceState 方式
-
- function structuralClone(obj) {
- const oldState = history.state;
- history.replaceState(obj, document.title);
- const copy = history.state;
- history.replaceState(oldState, document.title);
- return copy;
- }
-
- var obj = {};
- var b = { obj };
- obj.b = b;
- var copy = structuralClone(obj);
- console.log(copy);
Notification API
- // Notification API
-
- function structuralClone(obj) {
- return new Notification('', { data: obj, silent: true }).data;
- }
-
- var obj = {};
- var b = { obj };
- obj.b = b;
- var copy = structuralClone(obj);
- console.log(copy);
考验能力
基础编码能力
递归编码能力
代码的严谨性
代码抽象能力
js编码能力
熟练掌握WeakMap的能力
引用类型各种AP的熟练程度
准确判断JS各种数据类型的能力
考虑问题全面性
边界情况
解决循环引用
- // 练习题1
- var a = {n: 1}
- var b = a;
- a.x = a = {n: 2}
- console.log(a.x); // undefined
- console.log(b.x); // {n: 2}
| 堆内存 | ||
|---|---|---|
| var a = {n: 1} | a | {n: 1} |
| var b = a; | a、b | {n: 1} |
| .的优先级高于=号,先计算. a.x = a = {n:2} | ||
| a.x = a = {n: 2} | a、b | {n: 1,x=a={n:2}} |
| a={n:2} 开辟新堆内存,a引用地址改变;b引用地址未改变 | ||
| x=a={n:2} | a | {n:2} |
| b | {n:1,x:{n:2}} |
- // 练习题2
- var a = {n: 1}
- var b = a;
- b.n = a.f = {n: 2};
- console.log(a); // {n:{n:2},f:{n:2}}
- console.log(b); // {n:{n:2},f:{n:2}}
| 堆内存 | ||
|---|---|---|
| var a = {n: 1} | a | {n: 1} |
| var b = a; | a、b | {n: 1} |
| .的优先级高于=号,先计算. b.n = a.f = {n:2} | ||
| b.n = a.f = {n:2} | a、b | {n=a.f={n:2}} |
| a.f = {n:2} | a、b | {n:{n:2},f:{n:2}} |