多个构造函数的JavaScript模式

时间:2010-07-10 20:23:41

标签: javascript design-patterns oop

我的实例需要不同的构造函数。那是什么常见模式?

12 个答案:

答案 0 :(得分:104)

JavaScript没有函数重载,包括方法或构造函数。

如果您希望函数的行为有所不同,具体取决于传递给它的参数的数量和类型,则必须手动嗅探它们。 JavaScript会愉快地调用一个函数,其函数的数量多于或少于声明的参数数量。

function foo(a, b) {
    if (b===undefined) // parameter was omitted in call
        b= 'some default value';

    if (typeof(a)==='string')
        this._constructInSomeWay(a, b);
    else if (a instanceof MyType)
        this._constructInSomeOtherWay(a, b);
}

您还可以像数组一样访问arguments以获取传入的任何其他参数。

如果您需要更复杂的参数,最好将其中的部分或全部放入对象查找中:

function bar(argmap) {
    if ('optionalparam' in argmap)
        this._constructInSomeWay(argmap.param, argmap.optionalparam);
    ...
}

bar({param: 1, optionalparam: 2})

Python演示了如何使用default和named参数以比函数重载更实际和更优雅的方式覆盖大多数用例。 JavaScript,不是那么多。

答案 1 :(得分:27)

你怎么找到这个?

function Foobar(foobar) {
    this.foobar = foobar;
}

Foobar.prototype = {
    foobar: null
};

Foobar.fromComponents = function(foo, bar) {
    var foobar = foo + bar;
    return new this(foobar);
};

答案 2 :(得分:12)

在bobince的回答中,我不想像手工那样做,所以我只是完全扯掉了jQuery的插件选项模式。

这是构造函数:

//default constructor for Preset 'class'
function Preset(params) {
    var properties = $.extend({
        //these are the defaults
        id: null,
        name: null,
        inItems: [],
        outItems: [],
    }, params);

    console.log('Preset instantiated');
    this.id = properties.id;
    this.name = properties.name;
    this.inItems = properties.inItems;
    this.outItems = properties.outItems;
}

以下是不同的实例化方法:

presetNoParams = new Preset(); 
presetEmptyParams = new Preset({});
presetSomeParams = new Preset({id: 666, inItems:['item_1', 'item_2']});
presetAllParams = new Preset({id: 666, name: 'SOpreset', inItems: ['item_1', 'item_2'], outItems: ['item_3', 'item_4']});

这就是:

presetNoParams
Preset {id: null, name: null, inItems: Array[0], outItems: Array[0]}

presetEmptyParams
Preset {id: null, name: null, inItems: Array[0], outItems: Array[0]}

presetSomeParams
Preset {id: 666, name: null, inItems: Array[2], outItems: Array[0]}

presetAllParams
Preset {id: 666, name: "SOpreset", inItems: Array[2], outItems: Array[2]}

答案 3 :(得分:9)

进一步了解eruciform的回答,您可以将new来电链接到init方法。

function Foo () {
    this.bar = 'baz';
}

Foo.prototype.init_1 = function (bar) {
    this.bar = bar;
    return this;
};

Foo.prototype.init_2 = function (baz) {
    this.bar = 'something to do with '+baz;
    return this;
};

var a = new Foo().init_1('constructor 1');
var b = new Foo().init_2('constructor 2');

答案 4 :(得分:4)

正在回答,因为此问题首先在 google,但答案现在已过时。

您可以使用Destructuring objects as constructor parameters in ES6

这是模式:

您不能有多个构造函数,但是可以使用解构和默认值来执行所需的操作。

export class myClass {

  constructor({ myArray = [1, 2, 3], myString = 'Hello World' }) {

    // ..
  }
}

如果要支持“无参数”构造函数,则可以执行此操作。

export class myClass {

      constructor({myArray = [1, 2, 3], myString = 'Hello World'} = {}) {

        // ..
      }
}

答案 5 :(得分:3)

有时,参数的默认值对于多个构造函数来说已经足够了。当这还不够时,我尝试将大部分构造函数包装到后来调用的init(other-params)函数中。还要考虑使用工厂概念来创建一个可以有效创建所需其他对象的对象。

http://en.wikipedia.org/w/index.php?title=Factory_method_pattern&oldid=363482142#Javascript

答案 6 :(得分:3)

一般来说你可以传递更多的参数,当你实例化对象时你也可能会错过一些值,它们的默认值将是 undefined,如果你不想 mange undefined,构建多构造函数的简单方法应该是这样:

class Car {
  constructor(brand, year = '', owner = '') { // assign default value
    this.carname = brand;
    this.year = year;
    this.owner = owner;
  }
  presentCarName() {
    return 'I have a ' + this.carname;
  }
  presentCarNameAndYear() {
    return 'I have a ' + this.carname + ' year: ' + this.year;
  }
}

let myCar = new Car("Ford");
console.log(myCar.presentCarName());
myCar = new Car("Ford", 1996);
console.log(myCar.presentCarNameAndYear());

答案 7 :(得分:2)

export default class Order {

    static fromCart(cart) {
        var newOrder = new Order();
        newOrder.items = cart.items;
        newOrder.sum = cart.sum;

        return newOrder;
    }

    static fromOrder(id, order) {
        var newOrder = new Order();
        newOrder.id = id;
        newOrder.items = order.items;
        newOrder.sum = order.sum;

        return newOrder;
    }
}

用途:

  var newOrder = Order.fromCart(cart)
  var newOrder = Order.fromOrder(id, oldOrder)

答案 8 :(得分:1)

所以是的,就像之前的措辞一样,多种解决方案,你在日期这样的事情中看到的是静态方法:

Date.UTC("some params"); 

这不是一个合适的构造函数,但这是

function someObject(a,b,c,d,e,f) {
 a = a || "default";
 b = b || "defaultb"
}

可以去打字稿或将其转换为es5吗?

constructor(a:string, b = "default value", c:number? //optional
) {

}

答案 9 :(得分:1)

这是Programming in HTML5 with JavaScript and CSS3 - Exam Ref中为多个构造函数提供的示例。

function Book() {
    //just creates an empty book.
}


function Book(title, length, author) {
    this.title = title;
    this.Length = length;
    this.author = author;
}

Book.prototype = {
    ISBN: "",
    Length: -1,
    genre: "",
    covering: "",
    author: "",
    currentPage: 0,
    title: "",

    flipTo: function FlipToAPage(pNum) {
        this.currentPage = pNum;
    },

    turnPageForward: function turnForward() {
        this.flipTo(this.currentPage++);
    },

    turnPageBackward: function turnBackward() {
        this.flipTo(this.currentPage--);
    }
};

var books = new Array(new Book(), new Book("First Edition", 350, "Random"));

答案 10 :(得分:1)

我相信有两个答案。一种使用带有 IIFE 函数的“纯”Javascript 来隐藏其辅助构造函数。另一个使用 NodeJS 模块也隐藏了它的辅助构造函数。

我将仅展示带有 NodeJS 模块的示例。

类 Vector2d.js:



/*

    Implement a class of type Vetor2d with three types of constructors.

*/

// If a constructor function is successfully executed,
// must have its value changed to 'true'.let global_wasExecuted = false;  
global_wasExecuted = false;   

//Tests whether number_value is a numeric type
function isNumber(number_value) {
    
    let hasError = !(typeof number_value === 'number') || !isFinite(number_value);

    if (hasError === false){
        hasError = isNaN(number_value);
    }

    return !hasError;
}

// Object with 'x' and 'y' properties associated with its values.
function vector(x,y){
    return {'x': x, 'y': y};
}

//constructor in case x and y are 'undefined'
function new_vector_zero(x, y){

    if (x === undefined && y === undefined){
        global_wasExecuted = true;
        return new vector(0,0);
    }
}

//constructor in case x and y are numbers
function new_vector_numbers(x, y){

    let x_isNumber = isNumber(x);
    let y_isNumber = isNumber(y);

    if (x_isNumber && y_isNumber){
        global_wasExecuted = true;
        return new vector(x,y);
    }
}

//constructor in case x is an object and y is any
//thing (he is ignored!)
function new_vector_object(x, y){

    let x_ehObject = typeof x === 'object';
    //ignore y type

    if (x_ehObject){

        //assigns the object only for clarity of code
        let x_object = x;

        //tests whether x_object has the properties 'x' and 'y'
        if ('x' in x_object && 'y' in x_object){

            global_wasExecuted = true;

            /*
            we only know that x_object has the properties 'x' and 'y',
            now we will test if the property values ​​are valid,
            calling the class constructor again.            
            */
            return new Vector2d(x_object.x, x_object.y);
        }

    }
}


//Function that returns an array of constructor functions
function constructors(){
    let c = [];
    c.push(new_vector_zero);
    c.push(new_vector_numbers);
    c.push(new_vector_object);

    /*
        Your imagination is the limit!
        Create as many construction functions as you want.    
    */

    return c;
}

class Vector2d {

    constructor(x, y){

        //returns an array of constructor functions
        let my_constructors = constructors(); 

        global_wasExecuted = false;

        //variable for the return of the 'vector' function
        let new_vector;     

        //traverses the array executing its corresponding constructor function
        for (let index = 0; index < my_constructors.length; index++) {

            //execute a function added by the 'constructors' function
            new_vector = my_constructors[index](x,y);
            
            if (global_wasExecuted) {
            
                this.x = new_vector.x;
                this.y = new_vector.y;

                break; 
            };
        };
    }

    toString(){
        return `(x: ${this.x}, y: ${this.y})`;
    }

}

//Only the 'Vector2d' class will be visible externally
module.exports = Vector2d;  

useVector2d.js 文件使用 Vector2d.js 模块:

const Vector = require('./Vector2d');

let v1 = new Vector({x: 2, y: 3});
console.log(`v1 = ${v1.toString()}`);

let v2 = new Vector(1, 5.2);
console.log(`v2 = ${v2.toString()}`);

let v3 = new Vector();
console.log(`v3 = ${v3.toString()}`);

终端输出:

v1 = (x: 2, y: 3)
v2 = (x: 1, y: 5.2)
v3 = (x: 0, y: 0)

这样我们就可以避免脏代码(许多 if 和 switch 散布在整个代码中),难以维护和测试。每个建筑功能都知道要测试哪些条件。增加和/或减少您的建筑功能现在很简单。

答案 11 :(得分:-1)

您可以将类与返回该类实例的静态方法一起使用

    class MyClass {
        constructor(a,b,c,d){
            this.a = a
            this.b = b
            this.c = c
            this.d = d
        }
        static BAndCInstance(b,c){
            return new MyClass(null,b,c)
        }
        static BAndDInstance(b,d){
            return new MyClass(null,b, null,d)
        }
    }

    //new Instance just with a and other is nul this can
    //use for other params that are first in constructor
    const myclass=new MyClass(a)

    //a Instance that has b and c params
    const BAndC=MyClass.BAndCInstance(b,c)

    //another example for b and d
    const BAndD=MyClass.BAndDInstance(b,d)

使用此模式,您可以创建多个构造函数