仅保存 Javascript 对象的原始属性的最优雅方式

Most elegant way to save only primitive properties of a Javascript object

我有一个包含很多自己的和继承的方法的对象。我需要制作一个只有原始属性的简单快照。

最优雅的方法是什么?

Javascript 片段

function copy(source) {
    var target;
    if (source) {
        target = {};
        for (var key in source) {
            var prop = source[key],
                type = typeof prop;
            if (type === "string" || type === "number" || type === "boolean") {
                target[key] = prop;
            }
            if (type == "object") {
                target[key] = copy(prop);
            }
        }
    }
    return target;
}

Underscore.js

function copy(source) {
    var target;
    if (source) {
        target = {};
        _.filter( _.keys(source), function( key ) {
            var prop = source[key];
            if (!_.isFunction(prop) && !_.isObject(prop)) {
                target[key] = prop;
            }
            if (_.isObject(prop)) {
                target[key] = copy(prop);
            }
        });
    }
    return target;
}

这里的问题是,由于 JavaScript 提供的松散类型,您正在处理混合类型,这些类型可能是您想要的也可能不是您想要的。具体来说,如果不遍历任何给定的数组,就无法判断它是否只包含原语,因此您可能想要保留它,或者它是否包含您想要过滤掉的对象或函数。

我知道图书馆推荐是最古老的帽子,但我也强烈建议您使用 Underscore.js ( or a derivative such as Lodash ),它提供了很多我一直使用的超级方便的功能。在非常真实的意义上,这些感觉像是核心 JavaScript 语言的缺失部分 - 事实上,在某些情况下,它们包装了本机功能,但允许您避免平台不一致。

如果您的库中包含 Underscore,那么您可以找到任何给定对象的非对象 属性 名称,如下所示:

var myObject = { 
   a: "hello",
   b: "goodbye",
   c: function() { console.log(this.b);}
   d: { hello: "banana" }
};

_.filter( _.keys(myObject), function( key ) { 
        return ! _.isObject(myObject[key]);
}) // => ["a", "b"]

这可以被整理成一个函数来克隆对象,但当然这是不必要的,因为 Underscore 为我们提供了 _.pick,returns 对象的副本只有白名单属性:

_.pick( myObject, _.filter( _.keys(myObject), function( key ) { return ! _.isObject(myObject[key]);}))

通过翻转 !_.isObject,您可以检索作为对象的所有属性,并在必要时对这些属性执行类似的映射。通过将两者都包装在一个函数中,您可以在要克隆的子对象上递归调用它。

这回答了您的问题,但请注意,这可能不是执行 undo/redo 操作的最佳方式,您在评论中提到的是您计划将其用于的用途。将操作和状态记录到历史堆栈中可能更容易,该堆栈仅记录任何操作执行的实际更改,可以在其中安全地检索和还原它们,而无需保留数据快照。