什么是最有效的方式来克隆一个JavaScript对象?我已经看到obj = eval(uneval(o));被使用,但它是非标准的,仅支持Firefox.我做了事情,如obj = JSON.parse(JSON.stringify(o));但质疑效率。
当前回答
Shallow copy one-lineer (ECMAScript 第五版):
var origin = { foo : {} };
var copy = Object.keys(origin).reduce(function(c,k){c[k]=origin[k];return c;},{});
console.log(origin, copy);
console.log(origin == copy); // false
console.log(origin.foo == copy.foo); // true
并下载单线复制(ECMAScript 第六版,2015年):
var origin = { foo : {} };
var copy = Object.assign({}, origin);
console.log(origin, copy);
console.log(origin == copy); // false
console.log(origin.foo == copy.foo); // true
其他回答
這是我對象克隆器的版本. 這是一個獨立的版本的jQuery方法,只有幾個推文和調整. 檢查錯誤. 我使用了很多jQuery直到那一天我意識到我只會使用這個功能大部分時間 x_x。
使用方式与 jQuery API 所描述相同:
非深克隆: extend(object_dest,object_source);深克隆: extend(true,object_dest,object_source);
使用一个额外的函数来确定对象是否适合被克隆。
/**
* This is a quasi clone of jQuery's extend() function.
* by Romain WEEGER for wJs library - www.wexample.com
* @returns {*|{}}
*/
function extend() {
// Make a copy of arguments to avoid JavaScript inspector hints.
var to_add, name, copy_is_array, clone,
// The target object who receive parameters
// form other objects.
target = arguments[0] || {},
// Index of first argument to mix to target.
i = 1,
// Mix target with all function arguments.
length = arguments.length,
// Define if we merge object recursively.
deep = false;
// Handle a deep copy situation.
if (typeof target === 'boolean') {
deep = target;
// Skip the boolean and the target.
target = arguments[ i ] || {};
// Use next object as first added.
i++;
}
// Handle case when target is a string or something (possible in deep copy)
if (typeof target !== 'object' && typeof target !== 'function') {
target = {};
}
// Loop trough arguments.
for (false; i < length; i += 1) {
// Only deal with non-null/undefined values
if ((to_add = arguments[ i ]) !== null) {
// Extend the base object.
for (name in to_add) {
// We do not wrap for loop into hasOwnProperty,
// to access to all values of object.
// Prevent never-ending loop.
if (target === to_add[name]) {
continue;
}
// Recurse if we're merging plain objects or arrays.
if (deep && to_add[name] && (is_plain_object(to_add[name]) || (copy_is_array = Array.isArray(to_add[name])))) {
if (copy_is_array) {
copy_is_array = false;
clone = target[name] && Array.isArray(target[name]) ? target[name] : [];
}
else {
clone = target[name] && is_plain_object(target[name]) ? target[name] : {};
}
// Never move original objects, clone them.
target[name] = extend(deep, clone, to_add[name]);
}
// Don't bring in undefined values.
else if (to_add[name] !== undefined) {
target[name] = to_add[name];
}
}
}
}
return target;
}
/**
* Check to see if an object is a plain object
* (created using "{}" or "new Object").
* Forked from jQuery.
* @param obj
* @returns {boolean}
*/
function is_plain_object(obj) {
// Not plain objects:
// - Any object or value whose internal [[Class]] property is not "[object Object]"
// - DOM nodes
// - window
if (obj === null || typeof obj !== "object" || obj.nodeType || (obj !== null && obj === obj.window)) {
return false;
}
// Support: Firefox <20
// The try/catch suppresses exceptions thrown when attempting to access
// the "constructor" property of certain host objects, i.e. |window.location|
// https://bugzilla.mozilla.org/show_bug.cgi?id=814622
try {
if (obj.constructor && !this.hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf")) {
return false;
}
}
catch (e) {
return false;
}
// If the function hasn't returned already, we're confident that
// |obj| is a plain object, created by {} or constructed with new Object
return true;
}
Lodash 有一个功能,以便你这样做。
var foo = {a: 'a', b: {c:'d', e: {f: 'g'}}};
var bar = _.cloneDeep(foo);
// bar = {a: 'a', b: {c:'d', e: {f: 'g'}}}
阅读这里的DOC。
对于未来的参考,目前的 ECMAScript 6 草案将 Object.assign 引入为克隆对象的一种方式。
var obj1 = { a: true, b: 1 };
var obj2 = Object.assign(obj1);
console.log(obj2); // { a: true, b: 1 }
在编写时,支持仅限于Firefox 34在浏览器中,所以它还不能在生产代码中使用(除非您正在编写Firefox扩展)。
结构化克隆
HTML 标准包含一个内部结构化的克隆/序列化算法,可以创建对象的深度克隆,它仍然仅限于某些内置类型,但除了由 JSON 支持的少数类型之外,它还支持日期、RegExps、地图、套件、Blobs、FileLists、ImageDatas、Sparse Arrays、Typed Arrays等。
结构化Clone 全球功能由 Node 17.0 提供:
const clone = structuredClone(original);
以前版本: Node.js 的 v8 模块(如 Node 11 )直接展示了结构化序列化 API,但此功能仍然被标记为“实验性”,并在未来的版本中可更改或删除。
const v8 = require('v8');
const structuredClone = obj => {
return v8.deserialize(v8.serialize(obj));
};
直接支持浏览器:可用于Firefox 94
const clone = structuredClone(original);
class StructuredCloner {
constructor() {
this.pendingClones_ = new Map();
this.nextKey_ = 0;
const channel = new MessageChannel();
this.inPort_ = channel.port1;
this.outPort_ = channel.port2;
this.outPort_.onmessage = ({data: {key, value}}) => {
const resolve = this.pendingClones_.get(key);
resolve(value);
this.pendingClones_.delete(key);
};
this.outPort_.start();
}
cloneAsync(value) {
return new Promise(resolve => {
const key = this.nextKey_++;
this.pendingClones_.set(key, resolve);
this.inPort_.postMessage({key, value});
});
}
}
const structuredCloneAsync = window.structuredCloneAsync =
StructuredCloner.prototype.cloneAsync.bind(new StructuredCloner);
const main = async () => {
const original = { date: new Date(), number: Math.random() };
original.self = original;
const clone = await structuredCloneAsync(original);
// They're different objects:
console.assert(original !== clone);
console.assert(original.date !== clone.date);
// They're cyclical:
console.assert(original.self === original);
console.assert(clone.self === clone);
// They contain equivalent values:
console.assert(original.number === clone.number);
console.assert(Number(original.date) === Number(clone.date));
console.log("Assertions complete.");
};
main();
const structuredClone = obj => {
const oldState = history.state;
history.replaceState(obj, null);
const clonedObj = history.state;
history.replaceState(oldState, null);
return clonedObj;
};
雖然同步,但這可能非常慢。 它發生了所有與操縱瀏覽器歷史相關的頭部. 重複召喚這種方法可能會導致Chrome暫時變成無責任。
const structuredClone = obj => {
const n = new Notification('', {data: obj, silent: true});
n.onshow = n.close.bind(n);
return n.data;
};
structuredClone(value)
如有必要,先加载聚合物:
import structuredClone from '@ungap/structured-clone';
查看此答案更多详细信息
如果您不使用日期、功能、未定义、无限、RegExps、地图、集、博布、文件列表、图像数据、分布式列表、类型列表或其它复杂类型在您的对象中,那么一个非常简单的单一列表来深化对象是:
JSON.parse(JSON.stringify(对象) )
lodash - cloneDeep; 可以通过 lodash.clonedeep 模块单独进口,并且可能是您最好的选择,如果您已经不使用图书馆,提供深度克隆功能 AngularJS - angular.copy jQuery - jQuery.extend(真实, { }, oldObject);.clone() 只克隆 DOM 元素仅图书馆 - just-clone; 一部分图书馆的零依赖 npm 模块,只做一件事。
推荐文章
- 错误:'types'只能在.ts文件中使用- Visual Studio Code使用@ts-check
- React-Native:应用程序未注册错误
- LoDash:从对象属性数组中获取值数组
- src和dist文件夹的作用是什么?
- jQuery UI对话框-缺少关闭图标
- 如何使用AngularJS获取url参数
- 将RGB转换为白色的RGBA
- 如何将“camelCase”转换为“Camel Case”?
- 我们可以在另一个JS文件中调用用一个JavaScript编写的函数吗?
- 如何使用JavaScript重新加载ReCaptcha ?
- jQuery。由于转义了JSON中的单引号,parseJSON抛出“无效JSON”错误
- 在JavaScript关联数组中动态创建键
- ReactJS和公共文件夹中的图像
- 在React Native中使用Fetch授权头
- 为什么我的球(物体)没有缩小/消失?