从对象中删除所有属性

102

我有一个Javascript对象。

req.session

在我的代码中,我向这个对象添加属性。这些属性可以是其他对象、数组或普通字符串。

req.session.savedDoc = someObject; 
req.session.errors = ['someThing', 'anotherThing', 'thirdThing'];
req.session.message = 'someString'

如果我想要删除此对象的所有添加属性,最简单/最好的方法是什么?

一定有比这更好的方法吧?

// Delete all session values
delete req.session.savedDoc;
delete req.session.errors;
delete req.session.message;

10个回答

164

如果您想清除特定的引用,@VisioN的答案是有效的,但如果您实际上想清除一个对象,我发现以下方法可行:

for (var variableKey in vartoClear){
    if (vartoClear.hasOwnProperty(variableKey)){
        delete vartoClear[variableKey];
    }
}

34
为什么这个回答没有更多的赞...实际上,它清除了对象,而不是创建一个新的对象(如果旧对象被某些东西引用,则旧数据仍然存在)。 - basic6
2
没错!就像 @basic6 说的那样。今天我遇到了一个问题,创建一个新对象会破坏我已有的另一个引用。这应该是正确的答案。 - James Manes
2
在枚举属性时删除属性是否会导致某些JavaScript实现混淆?或者,“in”枚举的定义是否明确指定了如果在枚举期间添加和删除属性,应该发生什么? - Adam Gawne-Cain
@AdamGawne-Cain 不应该存在这种危险。ECMAScript 5.1标准第12.6.4节(有关for-in循环)中指出:“[在枚举时可以删除正在枚举的对象的属性。] (https://dev59.com/C3A75IYBdhLWcg3wH1RB#19564686)” - Ruan Mendes
更不用说,在函数式编程中,您无法重新分配值,因此通过此方法清除它将是首选。 - tukool

92

这个问题有两种可能的解决方案:

分配一个空对象

req.session = {};

垃圾回收器会自动清理内存。这个变量非常快,在大多数情况下都可以工作,但是需要小心使用,因为它可能会保留内存中对象的引用。这个注意事项在下面的TLDR部分有描述。

逐个删除属性

Object.keys(object).forEach(key => delete object[key]);
这将通过遍历每个非原型属性并删除它来清理对象。这样更安全但速度较慢。您需要决定在特定情况下是否使用它是有意义的。
TLDR
以上提供的任何解决方案都可以在当前情况下为作者完成工作,以及在此问题中提供的任何其他有效解决方案。这主要取决于开发人员希望如何操作废弃数据。
会话对象可能包含由不同变量链接的数据,并将一个新的空对象设置为req.session不会破坏对旧数据的引用,因此仍然需要旧数据的地方仍然可用。尽管保留旧数据的正确方法是克隆初始对象,但现实情况可能不同。让我们看以下示例:
req.session.user = { name: "Alexander" };  // we store an object in the session
var session = req.session;                 // save reference to the session in a variable
console.log( req.session, session );       // {user: Object}, {user: Object}

req.session = {};                          // let's replace session with a new object
console.log( req.session, session );       // {}, {user: Object}

我们仍然可以从session变量中获取旧数据,但是req.session为空:在这里,设置一个新对象可以作为深度克隆的一种替代方法。垃圾回收器不会从旧的req.session对象中删除数据,因为它仍然被session变量引用。

使用以下代码对对象进行深度清理:

Object.keys(object).forEach(key => delete object[key]);

...会显式地req.session对象中删除所有值,由于session变量链接到同一个对象,因此session也将变为空。让我们看看它是如何工作的:

req.session.user = { name: "Alexander" };  // we store an object in the session
var session = req.session;                 // save reference to the session in a variable
console.log( req.session, session );       // {user: Object}, {user: Object}

Object.keys(req.session).forEach(key => delete req.session[key]);
console.log( req.session, session );       // {}, {}

如你所见,无论哪种情况下我们都获得空对象。

从速度和内存的角度来看,设置一个新的空对象会比逐个清除旧对象的属性快得多,但是从内存方面考虑,如果旧数据仍然被其他地方引用,则新对象方法不会释放旧数据占用的内存。

选择使用哪种方法取决于你的编程方案,但在大多数情况下req.session = {}; 就足够了:它又快又简洁。然而,如果你在其他变量中保留了对原始对象的引用,则可以考虑使用深层隐式对象属性删除。


1
我想知道在编程中使用req.session = null还是req.session = {}更好。Express网站建议将其设置为null http://expressjs.com/api.html#cookieSession - Sriharsha
63
这是一个有用的回答,但我感觉它不完整,因为它并没有真正从对象中删除所有属性。如果您有两个指向同一对象的引用req.session1和req.session2,并且将req.session1 = {}分配。那么req.session2仍然保持原样,其所有属性仍然被定义。 - Theo
19
虽然结果是一个空对象,但该对象现在引用了内存中的一个新位置——也就是说,它是一个新对象。这意味着如果该对象有任何双重引用,它们现在已经不存在了。总之,这并不是清空对象,而是用一个新对象来替换它。 - Tomer
9
这并没有回答提问者的问题。 - Pete Alvin
2
他认为它应该打印一个空对象,因为这就是问题所要求的。一旦你从一个对象中移除了所有属性,你就得到了一个空对象。 - Dave Lugg
显示剩余7条评论

14

我只能看到一种正确的方法来从对象中删除自己的属性:

for (var x in objectToClean) if (objectToClean.hasOwnProperty(x)) delete objectToClean[x];

如果您想多次使用它,应该创建一个清理函数:

function deleteProperties(objectToClean) {
  for (var x in objectToClean) if (objectToClean.hasOwnProperty(x)) delete objectToClean[x];
}

对于你的情况,用法如下:

deleteProperties(req.session);

这种解决方案会删除对象中的属性,同时保留旧的引用。

例子:
使用空对象赋值:
var x = {a: 5};
var y = x;
x = {};    // x will be empty but y is still {a: 5}, also now reference is gone: x !== y

使用清洁方法:

var x = {a: 5};
var y = x;
deleteProperties(x);  // x and y are both empty and x === y

6
如果你想删除所有属性而不影响方法,则可以使用以下代码:
for(var k in req.session) if(!req.session[k].constructor.toString().match(/^function Function\(/)) delete req.session[k];

谢谢 ^^ constructor.toString() 这个东西非常有用,还有一个:如果你想要一个更好的 typeof 版本,请尝试 Object.prototype.toString.call(thing).replace(/^\[.+\s(.+)\]$/g, '$1').toLowerCase() :p - yent
但是为什么不直接使用 typeof req.session[k] === 'function' 呢? - VisioN
非常正确... 一段时间以前,typeof 只能区分数字、字符串、布尔值、null 和 undefined,其他所有东西都返回 "object",包括函数,所以我养成了使用更精确的东西的习惯... 说实话,我给出的代码是我在使用的一个更大脚本的一部分,在其中我需要获取一个对象的类名,该类是使用原型定义的(function foo() { this.a = 1; } foo.prototype.doThing = function ...)... - yent
for(var k in req.session) if(!(req.session[k] instanceof Function)) delete req.session[k]; - Domi

5

如果您关注性能,可以使用地图(map)。

const map = new Map()
map.set("first", 1)
map.set("second", 1)
map.clear()

这是一个O(1)操作,因此即使您有一个巨大的对象,您也不需要迭代x次来删除条目。

2
我已经按照如下方式完成了这项任务。
var 
    i,
    keys = Object.keys(obj);
for(i = 0; i < keys.length; i++){
    delete obj[keys[i]];
}

您可以将其添加到 Object 中(原型不是理想的选择)- 将是静态的。
Object.defineproperties(Object, {
    'clear': function(target){
        var 
            i,
            keys = Object.keys(target);
        for(i = 0; i < keys.length; i++){
            delete target[keys[i]];
        }
    }
});

然后您可以使用以下方式清除随机对象:
Object.clear(yourObj);

yourObj = {} 将引用替换为一个新对象,上述代码会删除其属性 - 引用仍然相同。


我想补充一点,那些操作有些昂贵 - 重构,并且可能有更好的方法来实现自己的目标。 - EnTr0cKs

1
一个好的做法是通过循环遍历Object.getOwnPropertyNames()的返回值来实现。这将捕捉到可枚举和隐藏属性,但不会包括符号,并且它已经排除了继承属性。值得注意的是,在使用任何这些答案中的方法删除属性之前,该属性必须是configurable的。如果你想要同时捕捉符号,可以添加Object.getOwnPropertySymbols()
const obj = Object.create( Object.prototype, {
    visibleProperty: {
        value: 'visibleProperty',
        enumerable: true,
        configurable: true,
    },
    hiddenProperty: {
        value: 'hiddenProperty',
        configurable: true,
    },
    [ Symbol( 'symbolProperty' ) ]: {
        value: 'symbolProperty',
        configurable: true,
    },
    visibleMethod: {
        value: () => 'visibleMethod',
        enumerable: true,
        configurable: true,
    },
    hiddenMethod: {
        value: () => 'hiddenMethod',
        configurable: true,
    },
    [ Symbol( 'symbolMethod' ) ]: {
        value: () => 'symbolMethod',
        configurable: true,
    },
} );

for( const key of Object.getOwnPropertyNames( obj ) ) delete obj[ key ];

console.log( Object.getOwnPropertyDescriptors( obj ) );

这将删除除符号标识的属性之外的所有内容,因此最后一行将产生以下输出:

{
    [Symbol(symbolProperty)]: {
        value: 'symbolProperty',
        writable: false,
        enumerable: false,
        configurable: true
    },
    [Symbol(symbolMethod)]: {
        value: [Function: value],
        writable: false,
        enumerable: false,
        configurable: true
    }
}

Reflect.deleteProperty(obj, key) 可能比仅使用 delete obj[key] 更好。 - Peter Seliger

0
let obj = { a: 1, b: 2, c: 3 };

// Get an array of all the keys of the object
let keys = Object.keys(obj);

// Loop through the keys and delete each property
for (let i = 0; i < keys.length; i++) {
  delete obj[keys[i]];
}

console.log(obj); // Output: {}

0

对于普通的Object,天真的object = {}方法是可以的,但它会删除自定义对象的原型。

这种方法使用Object.getPrototypeOf()Object.create(),生成一个空对象,保留原型

    emptyObj = Object.create(Object.getPrototypeOf(obj), {});

例子:

class Custom extends Object {
  custom() {}
}

let custom = new Custom();
custom.foo = "bar";
console.log(custom.constructor.name, custom);
// Custom {"foo": "bar"}

// naive method:
let objVanilla = {}
console.log(objVanilla.constructor.name, objVanilla);
// Object {}

// safe method:
objSafe = Object.create(Object.getPrototypeOf(custom), {});
console.log(objSafe.constructor.name, objSafe);
// Custom {}


-1

此脚本递归地删除属性,除了向量中报告的数据。

你需要lodash库

-- 函数:

function removeKeysExcept(object, keysExcept = [], isFirstLevel = true) {
        let arrayKeysExcept = [],
            arrayNextKeysExcept = {};
        _.forEach(keysExcept, (value, i) => {
            let j = value.split('.');
            let keyExcept = j[0];
            arrayKeysExcept.push(keyExcept);
            j.shift();
            if (j.length) {
                j = j.join('.');
                if (!arrayNextKeysExcept[keyExcept]) {
                    arrayNextKeysExcept[keyExcept] = [];
                }
                arrayNextKeysExcept[keyExcept].push(j);
            }
        })
        _.forEach(arrayNextKeysExcept, (value, key) => {
            removeKeysExcept(object[key], value, false);
        });
        if (isFirstLevel) {
            return;
        }
        Object.keys(object).forEach(function (key) {
            if (arrayKeysExcept.indexOf(key) == -1) {
                delete object[key];
            }
        });
    }

运行方式:

-- 删除除第一级以外的所有属性,并在向量中报告:

removeKeysExcept(obj, ['department.id','user.id']);

-- 移除所有属性

removeKeysExcept(obj, ['department.id','user.id'], false);

-- 示例:

let obj = {
    a: {
        aa: 1,
        ab: {
            aba: 21
        }
    },
    b: 10,
    c: {
        ca: 100,
        cb: 200
    }
};

removeKeysExcept(obj, ['a.ab.aba','c.ca']);
/*OUTPUT: {
    a: {
        ab: {
            aba: 21
        }
    },
    b: 10,
    c: {
        ca: 100,
    }
};*/

removeKeysExcept(obj, ['a.ab.aba','c.ca'], false); //Remove too firt level
/*OUTPUT: {
    a: {
        ab: {
            aba: 21
        }
    },
    c: {
        ca: 100,
    }
};*/

removeKeysExcept(obj);
/*OUTPUT: {b:10};*/

removeKeysExcept(obj, [], false); //Remove too firt level
/*OUTPUT: {};*/

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接