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. 使用Java开发微信公众平台(二)——消息的接收与响应

    上一篇文章(http://www.jerehedu.com/fenxiang/171807_for_detail.htm )中,我们学习了使用Java语言开发微信公众平台的第一部分——环境搭建与开发接 ...

  2. POJ 2280 Amphiphilic Carbon Molecules 极角排序 + 扫描线

    从TLE的暴力枚举 到 13313MS的扫描线  再到 1297MS的简化后的扫描线,简直感觉要爽翻啦.然后满怀欣喜的去HDU交了一下,直接又回到了TLE.....泪流满面 虽说HDU的时限是2000 ...

  3. Java提高篇(转)

    http://www.cnblogs.com/mfrank/category/1118474.html Day1 抽象类 Day2 接口 Day3 抽象类与接口的比较 Day4 Java中的回调 Da ...

  4. Linux动态链接库的使用

    1.前言 在实际开发过程中,各个模块之间会涉及到一些通用的功能,比如读写文件,查找.排序.为了减少代码的冗余,提高代码的质量,可以将这些通用的部分提取出来,做出公共的模块库.通过动态链接库可以实现多个 ...

  5. Kafka:ZK+Kafka+Spark Streaming集群环境搭建(九)安装kafka_2.11-1.1.0

    如何搭建配置centos虚拟机请参考<Kafka:ZK+Kafka+Spark Streaming集群环境搭建(一)VMW安装四台CentOS,并实现本机与它们能交互,虚拟机内部实现可以上网.& ...

  6. Mongoose Connection best practice

    There is often quite a lot of confusion about how best to set up a database connection with Mongoose ...

  7. How to center body on a page?

      [提问] I'm trying to center the body element on my HTML page. Basically, in the CSS I set the body e ...

  8. oauth2-server-php-docs 授权类型

    授权码 概观 在Authorization Code交付式时使用的客户端想要请求访问受保护资源代表其他用户(即第三方).这是最常与OAuth关联的授予类型. 详细了解授权码 用例 代表第三方来电 履行 ...

  9. Python爬虫学习系列教程

    最近想学一下Python爬虫与检索相关的知识,在网上看到这个教程,觉得挺不错的,分享给大家. 来源:http://cuiqingcai.com/1052.html 一.Python入门 1. Pyth ...

  10. 被查封7周之后,全球最大BT网站“海盗湾”又重新活过来了【36kr】

    原文地址 原文地址 "免费"和"版权","自由"和"监管"永远在较量啊,矛盾共同体,事物的两面性~ 被查封7周之后,全球 ...