用于多个构造函数的 JavaScript 模式

我的实例需要不同的构造函数。它的通用模式是什么?

149869 次浏览

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

Http://en.wikipedia.org/w/index.php?title=factory_method_pattern&oldid=363482142#javascript

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 演示了如何使用默认参数和命名参数以比函数重载更实用、更优雅的方式覆盖大多数用例。JavaScript 就没那么好了。

你觉得这个怎么样?

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


Foobar.prototype = {
foobar: null
};


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


//usage: the following two lines give the same result
var x = Foobar.fromComponents('Abc', 'Cde');
var y = new Foobar('AbcDef')

进一步研究 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');

我不想像 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]}

这是在 用 JavaScript 和 CSS3在 HTML5中编程-考试参考文献中为多个构造函数提供的示例。

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"));

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

    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)


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


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

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

回答这个问题是因为这个问题在 但是答案现在已经过时了。

你可以使用 将对象解构为 ES6中的构造函数参数

规律是这样的:

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

export class myClass {


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


// ..
}
}

如果你想要支持一个“无参数”的构造函数,你可以这样做。

export class myClass {


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


// ..
}
}
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)

我相信有两个答案。一种使用带 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 在代码中的传播) ,这些代码很难维护和测试。每个构建函数都知道要测试哪些条件。增加和/或减少构建功能现在很简单。

一般来说,你可以传递更多的参数,当你实例化一个对象的时候,你可能会遗漏一些值,它们的默认值将是未定义的,如果你不想让 mange 未定义,构建多重构造函数的简单方法应该是这样的:

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());

这是我的解决方案,简单地使用 methodsreturn this例如。

class Person{
name;
age;
gender;
cash;


constructor() {


}


init(name, age, gender, cash){
this.name = name;
this.age = age;
this.gender = gender;
this.cash = cash;


return this;
}


initCyborg(name, age){
this.name = name + ' Reborn';
this.age = age + 5;
this.cash = 999999;
this.gender = "cyborg";


return this;
}


initMale(name, age, salariesOf2000Received){
this.name = name;
this.age = age;
this.gender = "male";
this.cash = 2000 * salariesOf2000Received;


return this;
}
}

那么

var john = new Person().init("John Doe", 30, "male", 2000);
var cyborg = new Person().initCyborg("Terminator-6000", 3000);
var rickAstley = new Person().initMale("Rick Astley", 56, 2);


console.log(john);
console.log(cyborg);
console.log(rickAstley);