什么是最有效的方式来克隆一个JavaScript对象?我已经看到obj = eval(uneval(o));被使用,但它是非标准的,仅支持Firefox.我做了事情,如obj = JSON.parse(JSON.stringify(o));但质疑效率。
当前回答
只是因为我没有看到AngularJS提到并认为人们可能想知道......
angular.copy 还提供深复制对象和序列的方法。
其他回答
這是我對象克隆器的版本. 這是一個獨立的版本的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;
}
如果你发现自己做这种类型的事情是正常的(例如,创建 undo redo 功能 ) 它可能值得看看 Immutable.js
const map1 = Immutable.fromJS( { a: 1, b: 2, c: { d: 3 } } );
const map2 = map1.setIn( [ 'c', 'd' ], 50 );
console.log( `${ map1.getIn( [ 'c', 'd' ] ) } vs ${ map2.getIn( [ 'c', 'd' ] ) }` ); // "3 vs 50"
https://codepen.io/anon/pen/OBpqNE?编辑器=1111
单线 ECMAScript 6 解决方案(特殊对象类型如 Date/Regex 未处理):
const clone = (o) => typeof o === 'object' && o!== null? // only clone objects (Array.isArray(o)? // if cloning an array o.map(e => clone(e)) : // clone each of its elements Object.keys(o).reduce( // otherwise reduce every key in the object (r, k) => (r[k] = clone(o[k]), r), {} // and save its cloned value
如何在一行代码中克隆(而不是深克隆)对象
Object.assign 方法是 ECMAScript 2015 (ES6) 标准的一部分,并且正是您所需要的。
var clone = Object.assign({}, obj);
使用 Object.assign() 方法将所有可列的属性从一个或多个源对象的值复制到目标对象。
阅读更多...
支持老年浏览器的多元化:
if (!Object.assign) {
Object.defineProperty(Object, 'assign', {
enumerable: false,
configurable: true,
writable: true,
value: function(target) {
'use strict';
if (target === undefined || target === null) {
throw new TypeError('Cannot convert first argument to object');
}
var to = Object(target);
for (var i = 1; i < arguments.length; i++) {
var nextSource = arguments[i];
if (nextSource === undefined || nextSource === null) {
continue;
}
nextSource = Object(nextSource);
var keysArray = Object.keys(nextSource);
for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
var nextKey = keysArray[nextIndex];
var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
if (desc !== undefined && desc.enumerable) {
to[nextKey] = nextSource[nextKey];
}
}
}
return to;
}
});
}
需要新的浏览器,但...
让我们扩展本地对象并获得一个真正的.extend();
Object.defineProperty(Object.prototype, 'extend', {
enumerable: false,
value: function(){
var that = this;
Array.prototype.slice.call(arguments).map(function(source){
var props = Object.getOwnPropertyNames(source),
i = 0, l = props.length,
prop;
for(; i < l; ++i){
prop = props[i];
if(that.hasOwnProperty(prop) && typeof(that[prop]) === 'object'){
that[prop] = that[prop].extend(source[prop]);
}else{
Object.defineProperty(that, prop, Object.getOwnPropertyDescriptor(source, prop));
}
}
});
return this;
}
});
只需在任何使用.extend() 对象的代码之前点击它。
例子:
var obj1 = {
node1: '1',
node2: '2',
node3: 3
};
var obj2 = {
node1: '4',
node2: 5,
node3: '6'
};
var obj3 = ({}).extend(obj1, obj2);
console.log(obj3);
// Object {node1: "4", node2: 5, node3: "6"}