github

深度复制这个问题看似简单,实际上要想完美实现需要很多知识。

需要考虑Set,Map,Promise等类型的对象

这个库简洁明了,是一个小巧玲珑的JS库

Promise简单例子

function f1() {
console.log("f1")
} function f2() {
console.log("f2")
} function wait(ms) {
return new Promise(
function(resolve, reject) {
setTimeout(function() {
resolve();
}, ms)
});
}
wait(3000).then(f1)
Promise.all([wait(3000), wait(4000)]).then(f1)

源码

/**
* 使用如下结构来进行变量隐藏,防止暴露变量
* var clone=(function(){
* function f(){
* }
* return f
* })()
*/
var clone = (function() {
'use strict'; //工具函数:判断obj是否是type类型
function _instanceof(obj, type) {
return type != null && obj instanceof type;
} /**
* JS中有Map、Set等集合
* Promise也是一种数据结构,也是可以复制的
*/
var nativeMap;
try {
nativeMap = Map;
} catch (_) {
// maybe a reference error because no `Map`. Give it a dummy value that no
// value will ever be an instanceof.
nativeMap = function() {};
} var nativeSet;
try {
nativeSet = Set;
} catch (_) {
nativeSet = function() {};
} var nativePromise;
try {
nativePromise = Promise;
} catch (_) {
nativePromise = function() {};
} /**
* Clones (copies) an Object using deep copying.
*
* This function supports circular references by default, but if you are certain
* there are no circular references in your object, you can save some CPU time
* by calling clone(obj, false).
*
* Caution: if `circular` is false and `parent` contains circular references,
* your program may enter an infinite loop and crash.
* 注意:如果禁用circular选项,那么出现循环包含的地方程序就会进入子循环。
*
* @param `parent` - the object to be cloned
* @param `circular` - set to true if the object to be cloned may contain
* circular references. (optional - true by default)
* @param `depth` - set to a number if the object is only to be cloned to
* a particular depth. (optional - defaults to Infinity)
* @param `prototype` - sets the prototype to be used when cloning an object.
* (optional - defaults to parent prototype).
* @param `includeNonEnumerable` - set to true if the non-enumerable properties
* should be cloned as well. Non-enumerable properties on the prototype
* chain will be ignored. (optional - false by default)
*/
//定义了工具函数、一些变量之后,大boss终于登场了
function clone(parent, circular, depth, prototype, includeNonEnumerable) {
//支持第二种传参方式:把参数打包成json传到clone的第二个参数
if (typeof circular === 'object') {
depth = circular.depth;
prototype = circular.prototype;
includeNonEnumerable = circular.includeNonEnumerable;
circular = circular.circular;
}
// maintain two arrays for circular references, where corresponding parents
// and children have the same index
var allParents = [];
var allChildren = []; var useBuffer = typeof Buffer != 'undefined'; if (typeof circular == 'undefined') //默认行为:执行循环
circular = true; if (typeof depth == 'undefined') //默认行为:有一定深度
depth = Infinity; // recurse this function so we don't reset allParents and allChildren
//克隆的过程是一个递归的过程,上面的全局变量allParents和allChildren就是为这个函数服务的
function _clone(parent, depth) {
// cloning null always returns null
if (parent === null)
return null; if (depth === 0)
return parent; var child;
var proto;
if (typeof parent != 'object') {
return parent;
} if (_instanceof(parent, nativeMap)) {
child = new nativeMap();
} else if (_instanceof(parent, nativeSet)) {
child = new nativeSet();
} else if (_instanceof(parent, nativePromise)) {
child = new nativePromise(function(resolve, reject) {
parent.then(function(value) {
resolve(_clone(value, depth - 1));
}, function(err) {
reject(_clone(err, depth - 1));
});
});
} else if (clone.__isArray(parent)) {
child = [];
} else if (clone.__isRegExp(parent)) {
child = new RegExp(parent.source, __getRegExpFlags(parent));
if (parent.lastIndex) child.lastIndex = parent.lastIndex;
} else if (clone.__isDate(parent)) {
child = new Date(parent.getTime());
} else if (useBuffer && Buffer.isBuffer(parent)) {
child = new Buffer(parent.length);
parent.copy(child);
return child;
} else if (_instanceof(parent, Error)) {
child = Object.create(parent);
} else {
if (typeof prototype == 'undefined') {
proto = Object.getPrototypeOf(parent);
child = Object.create(proto);
} else {
child = Object.create(prototype);
proto = prototype;
}
} if (circular) {
var index = allParents.indexOf(parent); if (index != -1) {
return allChildren[index];
}
allParents.push(parent);
allChildren.push(child);
} if (_instanceof(parent, nativeMap)) {
parent.forEach(function(value, key) {
var keyChild = _clone(key, depth - 1);
var valueChild = _clone(value, depth - 1);
child.set(keyChild, valueChild);
});
}
if (_instanceof(parent, nativeSet)) {
parent.forEach(function(value) {
var entryChild = _clone(value, depth - 1);
child.add(entryChild);
});
} for (var i in parent) {
var attrs;
if (proto) {
attrs = Object.getOwnPropertyDescriptor(proto, i);
} if (attrs && attrs.set == null) {
continue;
}
child[i] = _clone(parent[i], depth - 1);
} if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(parent);
for (var i = 0; i < symbols.length; i++) {
// Don't need to worry about cloning a symbol because it is a primitive,
// like a number or string.
var symbol = symbols[i];
var descriptor = Object.getOwnPropertyDescriptor(parent, symbol);
if (descriptor && !descriptor.enumerable && !includeNonEnumerable) {
continue;
}
child[symbol] = _clone(parent[symbol], depth - 1);
if (!descriptor.enumerable) {
Object.defineProperty(child, symbol, {
enumerable: false
});
}
}
} if (includeNonEnumerable) {
var allPropertyNames = Object.getOwnPropertyNames(parent);
for (var i = 0; i < allPropertyNames.length; i++) {
var propertyName = allPropertyNames[i];
var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName);
if (descriptor && descriptor.enumerable) {
continue;
}
child[propertyName] = _clone(parent[propertyName], depth - 1);
Object.defineProperty(child, propertyName, {
enumerable: false
});
}
} return child;
} return _clone(parent, depth);
} /**
* Simple flat clone using prototype, accepts only objects, usefull for property
* override on FLAT configuration object (no nested props).
*
* USE WITH CAUTION! This may not behave as you wish if you do not know how this
* works.
*/
clone.clonePrototype = function clonePrototype(parent) {
if (parent === null)
return null; var c = function() {};
c.prototype = parent;
return new c();
}; // private utility functions function __objToStr(o) {
return Object.prototype.toString.call(o);
}
clone.__objToStr = __objToStr; function __isDate(o) {
return typeof o === 'object' && __objToStr(o) === '[object Date]';
}
clone.__isDate = __isDate; function __isArray(o) {
return typeof o === 'object' && __objToStr(o) === '[object Array]';
}
clone.__isArray = __isArray; function __isRegExp(o) {
return typeof o === 'object' && __objToStr(o) === '[object RegExp]';
}
clone.__isRegExp = __isRegExp; //获取正则表达式的开关
function __getRegExpFlags(re) {
var flags = '';
if (re.global) flags += 'g';
if (re.ignoreCase) flags += 'i';
if (re.multiline) flags += 'm';
return flags;
}
clone.__getRegExpFlags = __getRegExpFlags; return clone;
})(); //使用如下结构进行判断,如果module变量存在,那么说明是node;
//如果module变量不存在,说明是JS,无需进行module.exports
if (typeof module === 'object' && module.exports) {
module.exports = clone;
}

例子

var clone = require('clone');

var a, b;

a = { foo: { bar: 'baz' } };  // initial value of a

b = clone(a);                 // clone a -> b
a.foo.bar = 'foo'; // change a console.log(a); // show a
console.log(b); // show b

This will print:

{ foo: { bar: 'foo' } }
{ foo: { bar: 'baz' } }

API

clone(val, circular, depth)

*val -- the value that you want to clone, any type allowed

*circular -- boolean

Call clone with circular set to false if you are certain that obj contains no circular references. This will give better performance if needed. There is no error if undefined or null is passed as obj.

*depth -- depth to which the object is to be cloned (optional, defaults to infinity)

*prototype -- sets the prototype to be used when cloning an object. (optional, defaults to parent prototype).

*includeNonEnumerable -- set to true if the non-enumerable properties should be cloned as well. Non-enumerable properties on the prototype chain will be ignored. (optional, defaults to false)

clone.clonePrototype(obj)

*obj -- the object that you want to clone

循环引用问题

var a, b;

a = { hello: 'world' };

a.myself = a;
b = clone(a); console.log(b);

This will print:

{ hello: "world", myself: [Circular] }
So, b.myself points to b, not a. Neat!

javascript中的复制的更多相关文章

  1. 关于Javascript中的复制

    在做项目时有一个需求,是需要复制内容到剪切板,因为有众多浏览器,所以要兼容性很重要 1.最简单的copy,只能在IE下使用 使用clipboardData方法 <script type=&quo ...

  2. javascript 中的深复制 和 其实现方法

    首先,我们需要明白什么是深复制(侧重指对象方面)? 在javascript中,复制分为浅复制和深复制,个人理解,浅复制就是直接将引用复制,复制前后的两个对象指向同一个内存地址,对其中一个进行操作,另外 ...

  3. 理解javascript中的连续赋值

    之前在扒源码时经常看到类似的连续赋值操作:  var a = b = 1;  在某度搜了众多前辈的博客,总算对这骚操作有点眉目. Case analysis 首先,javascript中连续赋值最典型 ...

  4. javascript中关于深复制与浅复制的问题

    在javascript中,变量的类型分为基本类型和引用类型. 对于基本类型的变量来说,值的复制以及作为函数参数实参传递的过程都是值的复制传递,换句话说,是会在内存中开辟出一个新空间用于存放新的值的.这 ...

  5. 【转】JavaScript中的对象复制(Object Clone)

    JavaScript中并没有直接提供对象复制(Object Clone)的方法.因此下面的代码中改变对象b的时候,也就改变了对象a. a = {k1:1, k2:2, k3:3}; b = a; b. ...

  6. Javascript中的一种深复制实现

    在javascript中,所有的object变量之间的赋值都是传地址的,可能有同学会问哪些是object对象.举例子来说明可能会比较好: typeof(true) //"boolean&qu ...

  7. 深度解析javascript中的浅复制和深复制

    原文:深度解析javascript中的浅复制和深复制 在谈javascript的浅复制和深复制之前,我们有必要在来讨论下js的数据类型.我们都知道有Number,Boolean,String,Null ...

  8. JavaScript 中的对象深度复制(Object Deep Clone)

    JavaScript中并没有直接提供对象复制(Object Clone)的方法. JavaScript中的赋值,其实并不是复制对象,而是类似`c/c++`中的引用(或指针),因此下面的代码中改变对象b ...

  9. 对 JavaScript 中的5种主要的数据类型进行值复制

    定义一个函数 clone(),可以对 JavaScript 中的5种主要的数据类型(包括 Number.String.Object.Array.Boolean)进行值复制 使用 typeof 判断值得 ...

随机推荐

  1. Http协议中Get和Post的浅谈

    起名困难户,每次写文章最愁的就是不知道该如何起个稍具内涵的名字,如果这篇文章我只是写写Get和Post的区别,我可以起个名字“Get和Post的那点事”,如果打算阐述一下Http协议原理性内容,那该叫 ...

  2. 泊松分布E(X^2)

    由于求期望实际就是求平均值,所以E(X^2)=E[X*X]=E[X*X]+E(X)-E(X)=E[X*X+X-X]=E[X(X-1)+X]E[X(X-1)+X]=E[X(X-1)]+E(X)即:和的平 ...

  3. 我们为何放弃Eclipse,投奔IntelliJ IDEA

    本文来源于我在InfoQ中文站原创的文章,原文地址是:http://www.infoq.com/cn/news/2013/11/why-drop-eclipse-use-intellij Nikita ...

  4. Centos设置开机启动Apache和Mysql[总结]

    1.前言 最近学习搭建wordpress,需要用到apahce和mysql.我是下载源代码进行安装的,安装在/url/local目录下,每次开机都需要手动启动,有点麻烦.如是想设置开机启动,从网上查了 ...

  5. Spark Structured Streaming:将数据落地按照数据字段进行分区方案

    方案一(使用ForeachWriter Sink方式): val query = wordCounts.writeStream.trigger(ProcessingTime(5.seconds)) . ...

  6. 启动项目时出现Not a JAR.......Find JAR........一指循环就是起不来

    出现问题原因就是mapper的映射文件有问题,里面的返回类型如是实体找不到或者找重复的就会这样 解决办法就是:确保在用的实体(路径)能找到,切记不能有重名的实体

  7. ExtJS学习笔记2:响应事件、使用AJAX载入数据

    响应事件: 1.设置一个html标记 <div id="my-div">Ext JS 4 Cookbook</div> 2.使用get函数获取此标记对象 v ...

  8. 2014秋C++ 第9周项目 循环程序设计

    课程主页在http://blog.csdn.net/sxhelijian/article/details/39152703.课程资源在云学堂"贺老师课堂"同步展示,使用的帐号请到课 ...

  9. springcloud报错Cannot execute request on any known server

    启动springcloud服务中心,提示这个错误:Cannot execute request on any known server 网上一般说是修改application.properties配置 ...

  10. 【转】java 线程的几种状态

    java thread的运行周期中, 有几种状态, 在 java.lang.Thread.State 中有详细定义和说明: NEW 状态是指线程刚创建, 尚未启动 RUNNABLE 状态是线程正在正常 ...