javascript中的复制
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中的复制的更多相关文章
- 关于Javascript中的复制
在做项目时有一个需求,是需要复制内容到剪切板,因为有众多浏览器,所以要兼容性很重要 1.最简单的copy,只能在IE下使用 使用clipboardData方法 <script type=&quo ...
- javascript 中的深复制 和 其实现方法
首先,我们需要明白什么是深复制(侧重指对象方面)? 在javascript中,复制分为浅复制和深复制,个人理解,浅复制就是直接将引用复制,复制前后的两个对象指向同一个内存地址,对其中一个进行操作,另外 ...
- 理解javascript中的连续赋值
之前在扒源码时经常看到类似的连续赋值操作: var a = b = 1; 在某度搜了众多前辈的博客,总算对这骚操作有点眉目. Case analysis 首先,javascript中连续赋值最典型 ...
- javascript中关于深复制与浅复制的问题
在javascript中,变量的类型分为基本类型和引用类型. 对于基本类型的变量来说,值的复制以及作为函数参数实参传递的过程都是值的复制传递,换句话说,是会在内存中开辟出一个新空间用于存放新的值的.这 ...
- 【转】JavaScript中的对象复制(Object Clone)
JavaScript中并没有直接提供对象复制(Object Clone)的方法.因此下面的代码中改变对象b的时候,也就改变了对象a. a = {k1:1, k2:2, k3:3}; b = a; b. ...
- Javascript中的一种深复制实现
在javascript中,所有的object变量之间的赋值都是传地址的,可能有同学会问哪些是object对象.举例子来说明可能会比较好: typeof(true) //"boolean&qu ...
- 深度解析javascript中的浅复制和深复制
原文:深度解析javascript中的浅复制和深复制 在谈javascript的浅复制和深复制之前,我们有必要在来讨论下js的数据类型.我们都知道有Number,Boolean,String,Null ...
- JavaScript 中的对象深度复制(Object Deep Clone)
JavaScript中并没有直接提供对象复制(Object Clone)的方法. JavaScript中的赋值,其实并不是复制对象,而是类似`c/c++`中的引用(或指针),因此下面的代码中改变对象b ...
- 对 JavaScript 中的5种主要的数据类型进行值复制
定义一个函数 clone(),可以对 JavaScript 中的5种主要的数据类型(包括 Number.String.Object.Array.Boolean)进行值复制 使用 typeof 判断值得 ...
随机推荐
- 【大数据】大数据处理-Lambda架构-Kappa架构
大数据处理-Lambda架构-Kappa架构 elasticsearch-head Elasticsearch-sql client NLPchina/elasticsearch-sql: Use S ...
- java中两种发起POST请求,并接收返回的响应内容的方式 (转)
http://xyz168000.blog.163.com/blog/static/21032308201162293625569/ 2.利用java自带的java.net.*包下提供的工具类 代码如 ...
- 主机无法访问虚拟机的apache解决办法
1.前言 今天学习搭建wordpress,apache服务器安装在虚拟机的Centos上.配置好以后,发现在虚拟机上可以访问,但在windows主机上不能访问.于是百度.google一下,终于解决问题 ...
- 【python】安装bcoding
C:\Users\horn1>pip install bcodingCollecting bcoding Downloading https://files.pythonhosted.org/p ...
- jquery ready方法实现原理
先看这两句代码: window.addEventListener('load',loaded,false); document.addEventListener('DOMContentLoaded', ...
- Android studio 将 Module 打包成 Jar 包
整理记录 AndroidStudio 把一个 module 项目打包成 jar 包. 一.默认自动生成的 jar 包 众所周知 android studio 会在library所依赖的 app运行 或 ...
- JavaScript 之 最佳位置选择
Javascript 文件(下面简称脚本文件)需要被HTML文件引用才能在浏览器中运行.在HTML文件中可以通过不同的方式来引用脚本文件,我们需要关注的是,这些方式的具体实现和这些方式可能会带来的性能 ...
- ExtJs4.2中Tab选项卡的右击关闭其它和关闭当前功能不准确的解决方法
一.ExtJs4.2中Tab选项卡的右击关闭其它和关闭当前功能不准确的解决方法 二.找到ux目录下的TabCloseMenu.js文件,将内容替换成下面代码. 三.代码: /** * Plugin f ...
- Python-统计svn变更代码行数
1 #!/bin/bash/python 2 # -*-coding:utf-8-*- 3 #svn统计不同url代码行数变更脚本,过滤空行,不过滤注释. 4 import subprocess,os ...
- Struck 跟踪算法(二)
以下開始读详细源代码 config.h文件 /* * Struck: Structured Output Tracking with Kernels * * Code to accompany the ...