Comments (72)
@mengxin-FE javaScript 我还有很多地方需要研究,倒不算透彻,不过谢谢夸奖哈~ 如果说学习方法的话,就是确定一个要研究的主题,然后大量阅读该主题相关的文章,尽量保证每篇文章都能理解,如果不能理解,第二天再看一遍,直到看懂为止,如果可以的话,再写写文章,将学到的知识梳理出来,与大家分享~
from blog.
我从《你不知道的 JavaScript》一书看到,工具函数 JSON.stringify(..) 在将JSON对象序列化为字符串时也用到了 ToString 。请注意, JSON 字符串化并非严格意义上的强制类型转换,因为其中也涉及 ToString 的相 关规则。
对大多数简单值来说, JSON 字符串化和 果总是字符串:toString()的效果基本相同,只不过序列化的结
JSON.stringify( 42 ); // "42"
JSON.stringify( "42" ); // ""42""(含有双引号的字符串)
JSON.stringify( null ); // "null"
JSON.stringify( true ); // "true"
所有 安全的 JSON 值 (JSON-safe)都可以使用 JSON.stringify(..) 字符串化。 安全的 JSON 值是指能够呈现为有效 JSON 格式的值。
下面敲黑板划重点:
为了简单起见, 我们来看看什么是 不安全的 JSON 值 。 undefined 、 function 、 symbol (ES6+)和包含循环引用(对象之间相互引用,形成一个无限循环)的 对象 都不符合 JSON 结构标准,支持 JSON 的语言无法处理它们。
JSON.stringify(..) 在对象中遇到 undefined 、 function 和 symbol 时会自动将其忽略, 在 数组中则会返回 null (以保证单元位置不变)。
例如:
JSON.stringify( undefined );
JSON.stringify( function(){} );
JSON.stringify( [1,undefined,function(){},4] );
JSON.stringify({ a:2, b:function(){} } );
// undefined // undefined
// "[1,null,null,4]"
// "{"a":2}"
对包含循环引用的对象执行 JSON.stringify(..) 会出错。
...
from blog.
楼主对js的理解这么透彻,是怎么学的啊?
你水平是有多低啊,说他透彻 呵呵
from blog.
function deepClone (obj) {
if (Array.isArray(obj)) {
return obj.map(deepClone)
} else if (obj && typeof obj === 'object') {
var cloned = {}
var keys = Object.keys(obj)
for (var i = 0, l = keys.length; i < l; i++) {
var key = keys[i]
cloned[key] = deepClone(obj[key])
}
return cloned
} else {
return obj
}
}
这样也可以
from blog.
想请问下博主,为什么深拷贝那里for (var key in obj) 之后还需要一个判断if (obj.hasOwnProperty(key)) 呢
from blog.
@KingsonCheng 避免拷贝继承下来的属性.
from blog.
为什么还要用
hasOwnProperty
判断一下啊
因为for in 不仅遍历对象自身属性,还会遍历继承的inumerable 属性,这里只拷贝自身属性。
from blog.
@superwtt
function Person() {} Person.prototype = { name: 'hello' } var p = new Person(); for (var key in p) { console.log(key); //name } 就是这种情况,p只是Person的实例,for...in循环却能拿到Person原型上的name属性
from blog.
处理循环引用的版本:
function deepClone(target, map = new WeakMap()) {
if (map.has(target)) {
return map.get(target)
}
// 特殊处理:正则、日期
if (isRegExp(target) || isDate(target)) {
return new target.constructor(target)
}
if (isObject(target)) {
const cloneTarget = isArray(target) ? [] : {}
map.set(target, cloneTarget) // 缓存循环引用的拷贝结果
for (const prop in target) {
if (target.hasOwnProperty(prop)) {
cloneTarget[prop] = deepClone(target[prop], map)
}
}
return cloneTarget
} else {
return target
}
}
拷贝函数,有两种写法貌似可以:
eval(target.toString())
// or
new Function(`return ${target.toString()}`)()
from blog.
期待下一篇!!!
from blog.
养肥了再看一遍
from blog.
null应该特殊考虑一下吧,在深拷贝中,值为null会赋值一个空对象
from blog.
@yunlzhang 感谢指出,现在的 deepCopy 方法确实有这个问题
deepCopy({
value: null
})
的值为:
{value: {}}
这篇的目的在于讲解深浅拷贝的概念以及深浅拷贝的思路,下一篇 《JavaScript专题之从零实现jQuery的extend》 才是讲解深浅拷贝的详细实现,在下一篇的 extend 方法就有对于 null 的处理~
from blog.
楼主对js的理解这么透彻,是怎么学的啊?
from blog.
@Tvinsh 确实可以,感谢分享哈~
from blog.
@naihe138 非常感谢补充,o( ̄▽ ̄)d JSON.stringify 这部分确实写得太浅薄了。
from blog.
可以这样说吗?
=
是浅拷贝
slice
和concat
如果拷贝基本类型元素的数组是深拷贝,否则是浅拷贝
from blog.
@veedrin 可以,只是不知道为什么,我觉得怪怪的,好像一般不会这样描述……
from blog.
请问深拷贝的时候遇到相互引用的情况怎么处理?
from blog.
@JHanLu 这个可以参照下一篇 extend 的实现方式 #33
from blog.
这个深拷贝问题很大,没有考虑dom对象,正则对象,时间对象
from blog.
@UNDERCOVERj 确实没有考虑这些场景,不过常遇到的场景都是普通对象的拷贝,这样的也够了~
from blog.
写的很清楚,谢谢楼主
from blog.
star一下以示支持
from blog.
深拷贝这样写应该会好点:
var deepCopy = function(obj) {
if (obj === null || typeof obj !== 'object') return obj;
...
}
from blog.
Hi,你的深拷贝方法我觉得还是有点问题。如果遇到对象内嵌函数,typeof
判断返回的是function
,还是复制了指针而不是整个函数吧?可以看看下面代码理解我描述的问题
var arr = [function(){
console.log(a)
}, {
b: function(){console.log(b)
}],
new_arr = deepCopy(arr);
arr[0] === new_arr[0] // true
new_arr[1].b === arr[1].b // true
by the way, 博主的基础真是扎实,佩服
from blog.
@ishowman 函数的复制是一个很难解决的问题呀,即使是 jQuery 的 extend 也没有去处理函数……
from blog.
浅拷贝:es6中有两种新方法
方法1:
` let [...spread]= [12, 5, 8, 130, 44];
//等同于:let spread = 浅克隆([12, 5, 8, 130, 44]) `
方法2:
Array.from(array)//创建一个新数组
from blog.
@youzaiyouzai666 感谢分享呀~ 这些都是 ES6 提供的很好的方法~
from blog.
from blog.
@Fiv5 下一篇 #33 就涉及了深拷贝中的循环引用问题,不过处理的是
var a = {name : b};
var b = {name : a}
var c = extend(a, b);
console.log(c);
这种情况下导致的循环引用的问题,处理的思路是目标属性值和要复制的对象的属性值的引用是否相同,具体可以参见下一篇文章中的代码,对于本身就是循环引用的对象,正好评论中也有人提及了处理的方式,也可以参考一下~
from blog.
let a = {}
a = obj (手动滑稽)
from blog.
为什么这里递归调用就可以达到深拷贝呢?
from blog.
@A-birdFlyHigner 哈哈,你赢了 o(////▽////)q
from blog.
@Tzcodejs 嗯……我竟然不知道怎么回答……因为浅拷贝只能复制对象的一层属性,如果要复制更深层级的话,就需要用到递归吧……
from blog.
对于避免d归的深拷贝,我实现了一个:
原版
改进1
改进2
主要还是类似_.eq
,用了一个栈来保存已经 copy 过的 kv 对
from blog.
真正的深拷贝
function deepClone(obj) {
var _toString = Object.prototype.toString;
// null, undefined, non-object, function
if (!obj || typeof obj !== 'object') {
return obj;
console.log("null, undefined, non-object, function");
}
// DOM Node
if (obj.nodeType && 'cloneNode' in obj) {
return obj.cloneNode(true);
console.log("DOM Node");
}
// Date
if (_toString.call(obj) === '[object Date]') {
return new Date(obj.getTime());
console.log("Date");
}
// RegExp
if (_toString.call(obj) === '[object RegExp]') {
var flags = [];
if (obj.global) { flags.push('g'); }
if (obj.multiline) { flags.push('m'); }
if (obj.ignoreCase) { flags.push('i'); }
// console.log("RegExp");
return new RegExp(obj.source, flags.join(''));
}
var result = Array.isArray(obj) ? [] : {};
// console.log(result);
for (var key in obj) {
result[key] = deepClone(obj[key]);
}
return result;
}
from blog.
//其实,简单粗暴copy;只有,function不满足
function deepCopy(obj) {
return new Promise((resolve) => {
const {port1, port2} = new MessageChannel();
port2.onmessage = ev => resolve(ev.data);
port1.postMessage(obj);
});
}
deepCopy(obj).then((copy) => { // 请记住`MessageChannel`是异步的这个前提!
let copyObj = copy;
console.log(copyObj, obj)
console.log(copyObj == obj)
});
from blog.
我给加了个深度参数 不知道怎样 无限递归好像会卡死
// 对对象进行深拷贝
// deepin表示拷贝深度 deepin<=1 表示浅拷贝 >1表示拷贝深度 不填表示递归到底
const deepCopy = (obj, deepin) => {
let count = 0;
if (deepin === undefined) {
count = undefined;
} else {
count = deepin - 1;
}
if (count < 1) {
return obj;
} else {
if (typeof obj !== "object") return;
const newObj = obj instanceof Array ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] =
typeof obj[key] === "object" ? deepCopy(obj[key], count) : obj[key];
}
}
return newObj;
}
};
from blog.
// 创建指定深度广度的对象
function createData(deep = 0, breadth = 0) {
const root = {}
let pointer = root
for (let i = 0; i < deep; i++) {
pointer.data = {}
pointer = pointer.data
for (let j = 0; j < breadth && i === deep -1; j++) {
pointer[j] = j
}
}
return root
}
JSON.parse(JSON.stringify(createData(3000)))
// Uncaught RangeError: Maximum call stack size exceeded
// 层级够深时,会报错,这个拷贝也是不安全的
from blog.
@rutingjin 为什么我的电脑是4800层?这个和电脑内存有关系吧
from blog.
cloneData = (data) => {
if (Object.prototype.toString.call(data) === "[object Array]") {
var _arr = [];
for (let item of data) {
_arr.push(cloneData(item))
}
return _arr;
}
if (Object.prototype.toString.call(data) === "[object Object]") {
var _obj = {};
for (let item in data) {
_obj[item] = cloneData(data[item])
}
return _obj;
}
return data;
}
let obj = cloneData({a:1,b:2,c:{name:'tom',age:'22'},d:[1,3,4,5,6]});
console.log(obj);
from blog.
@rutingjin 为什么我的电脑是4800层?这个和电脑内存有关系吧
有可能,同一个电脑硬件下,用chrome测试和node测试层数也不一样
from blog.
感谢~对基础有很好的了解作用。
from blog.
点赞
from blog.
用 reduce 来个一行实现深拷贝的版本
const deepCopyObj = obj => Object.entries(obj).reduce((acc, [key, value]) => (
typeof value === 'object' ? { ...acc, [key]: deepCopyFun(value) } : { ...acc, [key]: value }
), {})
const deepCopyArr = arr => arr.reduce((acc, cur) => (
cur instanceof Array ? [...acc, deepCopyArr(cur)] : [...acc, cur]
), [])
from blog.
function getType(obj) {
return Object.prototype.toString.call(obj).slice(8, -1)
}
function BFSDeepClone(obj) {
if(typeof obj != 'object') return
let res = obj instanceof Array ? [] : {}
const origin = [obj]
const copy = [res]
while (origin.length) {
const _obj = origin.shift()
const copyObj = copy.shift()
Object.keys(_obj).forEach(k => {
const item = _obj[k]
if (getType(item) === 'Object' || getType(item) === 'Array') {
copyObj[k] = getType(item) === 'Object' ? {} : []
origin.push(item)
copy.push(copyObj[k]) //相当于引用res>>>[k]的值
}else {
copyObj[k] = item
}
})
}
return res
}
广度遍历实现深拷贝😄
from blog.
浅拷贝也可以用var newObj = Object.assign({}, obj)
from blog.
var x = {
a : {
e : 6
},
b : 2,
arr : [1,2,3],
fun : function(){
return 8
}
}
function clone(x){
if(x.constructor === Object){
var obj = {}
for(var k in x){
obj[k] = clone(x[k])
}
return obj
}else if(x.constructor === Array){
var arr = [];
for (var i = 0; i < x.length; i++) {
arr[i] = clone(x[i])
}
return arr
}else {
return x
}
}
var y = clone(x)
from blog.
楼主大大,深度拷贝遇到循环引用怎么去解这个问题呢?
from blog.
@cell617 你水平高,也没见你写个东西出来啊,键盘侠,就是你这种人(一般不怼人,忍不了)
from blog.
@KingsonCheng 避免拷贝继承下来的属性.
为啥要避免拷贝继承下来的属性?
from blog.
Date RegExp Error Promise Map Set... 写一个好的深拷贝好难呀
from blog.
@superwtt
function Person() {} Person.prototype = { name: 'hello' } var p = new Person(); for (var key in p) { console.log(key); //name } 就是这种情况,p只是Person的实例,for...in循环却能拿到Person原型上的name属性
所以意思是,不想把原型上的属性也拷贝过来,指向拷贝这个对象上自己的属性 对吧?
from blog.
感谢分享!
from blog.
这句可以优化
var newObj = obj instanceof Array ? [] : {};
可以这么写 var newObj = new obj.constructor
我还想问下,如果里面好几个元素都是一样的,请问怎么优化? 遇到面试官问了。。
from blog.
可以这样说吗?
=
是浅拷贝
slice
和concat
如果拷贝基本类型元素的数组是深拷贝,否则是浅拷贝
并不能这么说浅拷贝和赋值不能混为一谈
之所以需要深浅拷贝是因为赋值时候 如果遇到引用类型时候 两个变量直接会相互影响 所以是为了解决这一现象而采取的方案
赋值:
-
基本数据类型:赋值,赋值之后两个变量互不影响
-
引用数据类型:赋址,两个变量具有相同的引用,指向同一个对象,相互之间有影响 (为了解决这种场景)
from blog.
这句可以优化
var newObj = obj instanceof Array ? [] : {};
可以这么写var newObj = new obj.constructor
我还想问下,如果里面好几个元素都是一样的,请问怎么优化? 遇到面试官问了。。
请问你所说的,好几个元素都是一样的,是什么意思?我所理解的,如果是数组,即使值一样,但它们的 index 不一样吧?如果是对象,即使它们的值一样,但它们的 key 不一样吧?
是指将多个对象/数组拷贝合并到一个对象/数组中的情况吗?不是单单地对一个数组/对象进行深/浅拷贝。
from blog.
//其实,简单粗暴copy;只有,function不满足 function deepCopy(obj) { return new Promise((resolve) => { const {port1, port2} = new MessageChannel(); port2.onmessage = ev => resolve(ev.data); port1.postMessage(obj); }); } deepCopy(obj).then((copy) => { // 请记住`MessageChannel`是异步的这个前提! let copyObj = copy; console.log(copyObj, obj) console.log(copyObj == obj) });
postMessage应该也能实现
from blog.
为什么还要用hasOwnProperty
判断一下啊
from blog.
if (typeof obj !== 'object') return;
是不是return obj
比较好
from blog.
function deepClone(obj) {
function isObject(o) {
return typeof o === "object" && o !== null;
}
if (!isObject(obj)) {
throw new Error("非对象");
}
const newObj = obj instanceof Array ? [] : {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const cur = obj[key];
newObj[key] = isObject(cur) ? deepClone(cur) : cur;
}
}
return newObj;
}
from blog.
为什么没有人说递归深拷贝有缺点的: 1. 栈溢出、2. 尾递归优化、3. 动态规划来解决.
from blog.
大佬深拷贝没考虑环么?
from blog.
有两个问题
typeof obj[key] === 'object' 无法判断null的情况
如果拷贝的是类的实例对象 方法无法拷贝 并且输出的对象的类型是Object而不是class
var deepCopy = function(obj) {
if (!(obj instanceof Object)) return obj;
var newObj = obj instanceof Array ? [] : Object.create(obj.proto);
for (var key of Object.getOwnPropertyNames(obj)) {
newObj[key] = obj[key] instanceof Object ? deepCopy(obj[key]) : obj[key];
}
return newObj;
}
进行了略微的修改,大佬你看看
文章写得很好,点个赞!
from blog.
深拷贝的实现中 好像数组还是浅拷贝
from blog.
想咨询一下
var newObj = obj instanceof Array ? [] : {}; 这一句修改修改为:
var newObject = new obj.constructr(); 存在什么风险吗
from blog.
想咨询一下
var newObj = obj instanceof Array ? [] : {}; 这一句修改修改为:
var newObject = new obj.constructr(); 存在什么风险吗
严格意义上讲两个风险差不多,从原型链方面考虑:1. 开发者会存在修改 obj 的原型的情况,此时 instanceof 判断失效,constructor 同样不起作用 2. 基于前者,开发者还可以自定义原型、修改 constructor 变量指向等
能用 typeof 和 Object.prototype.toString.call() 就优先使用这两个
from blog.
完美深复制window.XMLHttpRequest这种类型的有啥办法吗? 上面的方法丢失构造方法或者原型方法
from blog.
最新的浏览器可以使用 structuredClone
API 来深拷贝了 structuredClone
浏览器兼容:
98 ✔ | 94 ✔ | 98 ✔ | 15.4 ✔ | 84 ✔ |
from blog.
hi,问下深拷贝中对于递归调用的怎么处理
from blog.
hi,问下深拷贝中对于递归调用的怎么处理
@ethanzhongyi 用 WeakMap 缓存已处理的对象
from blog.
Related Issues (20)
- 冴羽答读者问:如何在工作中打造影响力,带动同事?
- 无
- 冴羽答读者问:如何学习更有计划性、提升更稳更快? HOT 4
- 冴羽答读者问:过程比结果重要吗? HOT 1
- 冴羽答读者问:冴羽,你为什么写起了鸡汤? HOT 3
- 聊聊 npm 的语义化版本(Semver) HOT 4
- How to create Backlinks 😤
- 思考题第2题
- 可以理解为原型是prototype,原型链是通过__proto__ 链接起来的吗
- React 之 createElement 源码解读 HOT 8
- React 之元素与组件的区别 HOT 1
- React 之 Refs 的使用和 forwardRef 的源码解读
- React 之 Context 的变迁与背后实现 HOT 1
- 第一段不报错啊,刚试过了,会打印1
- how can i HOT 1
- Hosting of Blog Issue
- 全局对象
- 为啥每次都要创建一个 Child函数来new 子类?现在不都是 const p1 = new Person(); const p2 = new Person()吗
- 文档内容中文件结构的错位 HOT 1
- 原型链继承
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from blog.