【问题标题】:Setting arbitrary object properties from a parameter on construct从构造上的参数设置任意对象属性
【发布时间】:2013-06-28 13:05:07
【问题描述】:

在 Javascript 中实例化对象时,我想启用以下场景:

  • 对象可以在没有任何参数的情况下实例化,并使用默认属性值创建
  • 可以使用设置对象属性初始值的参数来实例化对象

解决方案应该:

  • 不分配尚未在对象上定义的属性
  • 允许设置任意数量的属性(无、部分或全部)
  • 易于应用于脚本定义的任何对象,这意味着我们不知道可以设置哪些属性。

jQuery 可供使用,但欢迎使用纯 JS 解决方案。

取如下对象构造函数:

function User() {
    this.name = null;
    this.email = null;
    this.password = null;
}

解决方案应该让我实现以下目标:

new User();
// User { name: null, email: null, password: null }

new User(<parameter(s) that specify a value for name, email and password>);
// User { name: "passed value", email: "passed value", password: "passed value" }

new User(<parameter that specifies a value for name>);
// User { name: "passed value", email: null, password: null }

new User(<parameter(s) that specify values for name and non-existent property age>);
// User { name: "passed value", email: null, password: null }

【问题讨论】:

    标签: javascript jquery oop


    【解决方案1】:

    $.extend() 与一个默认对象结合起来让这变得非常简单:

    function User(options) {
        var defaults = {
            name: null,
            email: null,
            password: null
        };
        var options = $.extend({}, defaults, options);
        console.log(options);
    
    }
    
    new User(); 
    // null, null, null
    
    new User({name : 'First'});
    // First, null, null
    
    new User({name : 'First', email: 'users.name@users-email.com'});
    // First, users.name@users-email.com, null
    
    new User({name : 'First', email: 'users.name@users-email.comm', password : 'password1'});
    // First, users.name@users-email.com, password1
    

    它正在运行:http://jsfiddle.net/m6KQC/2/

    如果您不想在调用函数时添加额外的属性,那么只需使用下面的代码而不是$.extend()。这与您的原始功能具有相同的功能:

    function setObjectProperties(defaults, options, target) {
        var options = options || {};
        for (var prop in defaults) { 
            target[prop] = (typeof options[prop] === "undefined") ?  defaults[prop] : options[prop];
        }
    }
    

    您可以将其包装在原始函数中并像这样使用它:

    setObjectProperties(defaults, options, this);
    

    你可以在这里看到这个替代方案:http://jsfiddle.net/m6KQC/9/

    【讨论】:

    • 解决方案不应分配尚未在对象上定义的属性。对象需要定义其属性,不得通过未定义的参数设置属性。这个解决方案能做到吗?
    • @michaelward82 - 这有什么关系?这些变量只能通过options 对象访问,因此它们不会覆盖函数中的任何其他内容。另外,为什么还要将不必要的变量传递给函数?
    • @michaelward82 - 如果您不想添加传递给函数的额外属性,我已经用替代方法更新了我的答案 - 不需要 jQuery。
    • 谢谢,我们正在接近完美的解决方案 :) 但是,这需要干燥。我需要此代码存在于一个函数中,我可以(在最坏的情况下)使用我拥有的每个 Object 构造函数中的一个简单的衬线来调用该函数。
    • 该函数似乎实现了许多预期的效果,但它实际上并没有创建或分配任何对象属性。删除当前的 console.log 并记录对象创建的结果,它显示创建的对象没有公共属性。感谢您到目前为止所做的努力,我觉得我们完成后会有一个很好的答案。目前,我的答案符合所有要求,但我确实认为我们可以做得比我的解决方案更好。
    【解决方案2】:

    首先,我会允许对象构造函数接受一个参数

    function User(params) {
    ...
    

    为了允许提供任意数量的参数,params 参数将接受一个对象字面量:

    new User({name: 'name 1', id: 1});
    

    接下来,我将params 对象传递给一个通用函数,该函数将处理从params 对象到target 对象的属性分配。

    function setObjectProperties(params, target) {
    }
    

    setObjectProperties 函数需要验证我们是否正确传递了一些数据。它检查是否同时定义了 paramstarget

    function setObjectProperties(params, target) {
        if (params !== undefined && target !== undefined) {
    
        }
    }
    

    一旦我们确信我们可以使用某些东西,就该迭代参数并分配给对象属性了。

    function setObjectProperties(params, target) {
        if (params !== undefined && target !== undefined) {
            $.each(params, function (i, property) {
                this[i] = property;
            }, target);
        }
    }
    

    使用 jQuery 的 $.each 函数,该函数迭代对象属性而没有迭代所有对象属性(例如原型等)的副作用。这可行,但意味着我们需要处理 this 得到的副作用在 $.each 函数的回调闭包中重新定义。我们现在不再知道原始对象是什么。

    然而,jQuery 的 $.proxy 函数旨在允许我们调用函数,同时指定我们想要使用的 this

    function setObjectProperties(params, target) {
        if (params !== undefined && target !== undefined) {
            $.each(params, $.proxy(function (key, value) {
                    this[key] = value;
            }, target));
        }
    }
    

    这里我们告诉jQuery将this设置为目标参数的值,在对象构造函数内部设置为this,如下所示:

    function User(params) {
        this.name = null;
        this.email = null;
        this.password = null;
    
        setObjectProperties(params, this);
    }
    

    还有一个问题,我们应该检查对象上声明的属性,看看我们是否可以分配传入的值:

    function setObjectProperties(params, target) {
        if (params !== undefined && target !== undefined) {
            $.each(params, $.proxy(function (key, value) {
                if (this.hasOwnProperty(key)) {
                    this[key] = value;
                }
            }, target));
        }
    }
    

    这是完整的最终代码:

    function setObjectProperties(params, target) {
        if (params !== undefined && target !== undefined) {
            $.each(params, $.proxy(function (key, value) {
                if (this.hasOwnProperty(key)) {
                    this[key] = value;
                }
            }, target));
        }
    }
    
    function User(params) {
        this.name = null;
        this.email = null;
        this.password = null;
    
        setObjectProperties(params, this);
    }
    
    user1 = new User();
    // User { name: null, email: null, password: null }
    
    user2 = new User({ name: 'First Last' });
    // User { name: "First Last", email: null, password: null }
    
    user3 = new User({ name: 'First Last', age: '400' });
    // User { name: "First Last", email: null, password: null }
    

    【讨论】:

    • 你为什么不直接使用$.extend()——甚至是一个简单的for...in循环——来代替所有$.each()混乱?这是一个不必要的复杂实现。
    • @MattBall - 完全同意。迈克尔 - 请参阅我的答案以获得更简单的方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-10
    • 2018-02-22
    • 2018-06-11
    • 2010-10-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多