Javascript 将元素作为值传递

标签 javascript

我正在学习构建 Javascript 构造函数并遇到以下问题:

var x = new Constructor({
        B: '1000',
        child: document.getElementById('id'), // assume there is a div#id
    });

function Constructor(options) {
    var settings;

    this.defaults = {
        A: 'value A',
        B: 2,
        parent: document.body,
        child: document.getElementById('anotherId'), // assume there is a div#anotherId
    }

    settings = extend({}, this.defaults, options);

    console.log(this.defaults.A)      // outputs 'value A'
    console.log(this.defaults.B)      // outputs 2
    console.log(this.defaults.parent) // outputs <body>...</body>
    console.log(this.defaults.child)  // outputs <div id="id">...</div>

    console.log(settings.A)           // outputs 'value A'
    console.log(settings.B)           // outputs '1000'
    console.log(settings.parent)      // outputs empty object
    console.log(settings.child)       // outputs empty object
}

function extend(out) {
    out = out || {};
    for (var i = 1; i < arguments.length; i++) {
        var obj = arguments[i];

        if (!obj) continue;

        for (var key in obj) {
            if (obj.hasOwnProperty(key)) {
                out[key] = (typeof obj[key] === 'object') ? extend(out[key], obj[key])
                                                          : obj[key];
            }
        }
    }
    return out;
}

问题是 settings.parentthis.defaults.parent 的值不同,但其他属性都很好。

这可能是什么问题?

最佳答案

问题在于 extend() 函数 - 它显然不是为了支持复制 DOM 节点(如 document.body)而编写的。我建议改用 Object.assign() - 它是 ES6 中可用的 native 函数,并且有适用于旧版浏览器的垫片(例如 MDN 上的 this one)。

用法:

settings = Object.assign({}, this.defaults, options);

请注意,这只会生成浅拷贝,因此在某些情况下可能不起作用。如果您正在复制嵌套对象并且希望确保没有修改 this.defaultsoptions 中的原始对象,那么您可能需要做一些深入的操作复制/克隆。

我不确定你从哪里得到这个 extend() 函数,但它确实做了一些基本的深度复制。问题在于 DOM 节点无法像常规对象一样进行克隆。有一种方法可以克隆它们 - 您可以调用 clone() 方法 - 但其目的是创建 DOM 节点的实际副本,然后您可以将其添加到文档中的其他位置,该副本这不是您想要在这里做的。

您是否使用任何 DOM 库,例如 jQuery?如果是这样,您可以这样做并避免整个问题:

this.defaults = {
    A: 'value A',
    B: 2,
    parent: $('body'),
    child: $('#anotherId'), // assume there is a div#anotherId
}

这可行,因为 jQuery 对象是包装一个或多个 DOM 节点的常规 JS 对象。

更新:实际上,要使其工作,您仍然需要修改 extend() 函数或使用不同的克隆函数,因为 中还有另一个错误>扩展()。请参阅下面的“更新”。

或者,您可以更改 extend() 方法,使其仅复制对 DOM 节点的引用,但深层复制所有其他类型的对象。您可以按照以下方式执行此操作:

function extend(out){
    out = out || {};
    for (var i = 1; i < arguments.length; i++){
        var obj = arguments[i];
        if (!obj) continue;
        for (var key in obj){
            if (obj.hasOwnProperty(key)) {
                if (typeof obj[key] === 'object') {
                    //don't deep-copy DOM nodes
                    if (obj.nodeType && obj.nodeName && obj.cloneNode) {
                        out[key] = obj[key];
                    }
                    else out[key] = extend(out[key], obj[key]);
                }
                else out[key] = obj[key];
            }
        }
    }
    return out;
}

但是,我认为这是一个有点不寻常的解决方案;就我个人而言,如果我遇到一个名为 extend() 的函数,我希望它能够进行深复制或浅复制,没有特殊异常(exception)。因此,我建议使用 jQuery 对象或一些类似的包装对象,而不是 DOM 节点本身。


更新

如上所述,extend() 方法还有另一个问题,导致它无法正确克隆 jQuery 对象。问题在于它不保留对象的原型(prototype),而只是以空对象 {} 开头。您可以使用 Object.create() 来解决这个问题,但仍然需要额外的工作来解决它的其他一些问题(请参阅下面的评论)。因此,我建议仅使用现有的第 3 方克隆功能。

例如,您可以使用 lodash 中的 cloneDeepWith 函数:

settings = _.cloneDeepWith(this.defaults, options);

(注意:显然 lodash 只是复制 DOM 节点的引用并深度克隆其他所有内容,因此在这方面,它的工作方式类似于我上面编写的 extend() 的修改版本。所以它可能对您来说是一个方便的选择。)

或者 jQuery 的 extend() 方法使用 deep 选项:

settings = $.extend(true /* setting the first argument to true makes it deep copy */,
    {}, this.defaults, options);

我还编写了自己的 deepCopy() 函数,该函数非常强大...它是我的 simpleOO 库的一部分,随着时间的推移,由于 Javascript 有了类,该函数将变得无关紧要,但如果您有兴趣,这里是有关 deepCopy 方法的文档的链接:

https://github.com/mbrowne/simpleoo.js/wiki/Additional-Examples#deep-copying-cloning-objects

(我的 deepCopy 函数也可以单独使用;请参阅我的答案中的第二个示例: https://stackoverflow.com/a/13333781/560114 。)

关于Javascript 将元素作为值传递,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42546341/

相关文章:

javascript - 为什么是 !当 !'0' == false 时, '0' == true

javascript - Polymer:将动画应用于 dom-repeat 模板中的 dom 元素

javascript - 套索和 D3.js

javascript - 动态组件: React vs Vue

javascript - 如何以编程方式链接两个js函数?

javascript - 在 JavaScript 中进行 Prototype 面向对象编程的最佳方法

javascript - Node 中是否有更好的处理 Mongodb 并发的方法?

javascript - jQuery 根据父 URL 添加类

javascript - 滚动时查找 div 元素的可见高度

javascript - 使用 Jasmine 在对象上测试事件处理程序