Javascript 中的结构

以前,当我需要存储许多相关变量时,我会创建一个类。

function Item(id, speaker, country) {
this.id = id;
this.speaker = speaker;
this.country = country;
}
var myItems = [new Item(1, 'john', 'au'), new Item(2, 'mary', 'us')];

但我在想这是不是个好习惯。在 JavaScript 中还有其他的 好多了方法来模拟结构吗?

253618 次浏览

我总是使用对象文字

{id: 1, speaker:"john", country: "au"}

我对哑结构(没有成员函数)使用对象 JSON 样式。

对象文字和构造对象之间的唯一区别是从原型继承的属性。

var o = {
'a': 3, 'b': 4,
'doStuff': function() {
alert(this.a + this.b);
}
};
o.doStuff(); // displays: 7

你可以做一个结构工厂。

function makeStruct(names) {
var names = names.split(' ');
var count = names.length;
function constructor() {
for (var i = 0; i < count; i++) {
this[names[i]] = arguments[i];
}
}
return constructor;
}


var Item = makeStruct("id speaker country");
var row = new Item(1, 'john', 'au');
alert(row.speaker); // displays: john

我认为创建一个类来模拟类似 C 的结构,就像您一直在做的那样,这是 最好的的方式。

这是一种对相关数据进行分组并简化向函数传递参数的好方法。考虑到模拟真实的面向对象特性所需的 额外的努力,我还认为 JavaScript 类更像是 C + + 结构,而不是 C + + 类。

我发现让 JavaScript 更像另一种语言会变得很复杂,但我完全支持使用 JavaScript 类作为无函数结构。

真正的问题是语言中的结构应该是值类型而不是引用类型。提出的答案建议使用对象(引用类型)来代替结构。虽然这可以实现其目的,但它回避了程序员实际上希望使用值类型(如原语)代替引用类型的好处这一点。首先,值类型不应该导致内存泄漏。

编辑: 有 正在进行中的提案来覆盖这个目的。

//today
var obj = {fname: "Kris", lname: "Kringle"}; //vanilla object
var gifts = ["truck", "doll", "slime"]; //vanilla array


//records and tuples - not possible today
var obj = #{fname: "Buddy", lname: "Hobbs"};
var skills = #["phone calls", "basketball", "gum recycling"];


在 Markus 的 回答之后,在更新的 JS 版本(我认为是 ES6)中,你可以更简单地使用 箭头函数休息参数创建一个“ struct”工厂,如下所示:

const Struct = (...keys) => ((...v) => keys.reduce((o, k, i) => {o[k] = v[i]; return o} , {}))
const Item = Struct('id', 'speaker', 'country')
var myItems = [
Item(1, 'john', 'au'),
Item(2, 'mary', 'us')
];


console.log(myItems);
console.log(myItems[0].id);
console.log(myItems[0].speaker);
console.log(myItems[0].country);

运行此命令的结果是:

[ { id: 1, speaker: 'john', country: 'au' },
{ id: 2, speaker: 'mary', country: 'us' } ]
1
john
au

你可以让它看起来像 Python 的 namedtuple:

const NamedStruct = (name, ...keys) => ((...v) => keys.reduce((o, k, i) => {o[k] = v[i]; return o} , {_name: name}))
const Item = NamedStruct('Item', 'id', 'speaker', 'country')
var myItems = [
Item(1, 'john', 'au'),
Item(2, 'mary', 'us')
];


console.log(myItems);
console.log(myItems[0].id);
console.log(myItems[0].speaker);
console.log(myItems[0].country);

结果是:

[ { _name: 'Item', id: 1, speaker: 'john', country: 'au' },
{ _name: 'Item', id: 2, speaker: 'mary', country: 'us' } ]
1
john
au

如果您使用 ES6兼容性,我创建了一个小库来定义 struct。

这是一个 JKT 解析器,您可以在这里检查项目存储库 JKT 解析器

例如,您可以像下面这样创建结构

const Person = jkt`
name: String
age: Number
`


const someVar = Person({ name: "Aditya", age: "26" })


someVar.name // print "Aditya"
someVar.age // print 26 (integer)


someVar.toJSON() // produce json object with defined schema

需要设置的工作更多,但是如果可维护性胜过一次性工作,那么这可能就是您的情况。

/**
* @class
*/
class Reference {


/**
* @constructs Reference
* @param {Object} p The properties.
* @param {String} p.class The class name.
* @param {String} p.field The field name.
*/
constructor(p={}) {
this.class = p.class;
this.field = p.field;
}
}

优点:

  • 不受论点顺序的约束
  • 很容易扩展
  • 输入脚本支持:

enter image description here

这是一个似乎尚未得到解决的老问题。不管怎样,我使用不变性来获得类似的行为。使用打字稿:

export class Point {
public readonly X: number;
public readonly Y: number;


constructor(x: number, y: number)
{
this.X = x;
this.Y = y;
}


public static SetX(value: number) : Point {
return new Point(value, this.Y);
}


public static SetY(value: number) : Point {
return new Point(this.X, value);
}
}

这使您获得了复杂值类型的一个关键好处,即您不能通过对它的引用意外地修改对象。

当然,缺点是如果您确实想要修改一个成员,您必须创建一个新的实例,因此需要使用静态 SetXSetY函数。

虽然有很多语法问题,但我认为对于特殊情况,比如 Point,这是值得的,因为如果值被意外地更改,那么它可能会被大量使用,并导致大量 bug。

这个过程对我来说很有效。从 Hardhat Dev-env 到 Ganachi 区块链测试网部署智能合同。

文件: ./script/loy.js

var structJS = { id: 55, title: "GP", iccid: "56", msisdn: "1712844177", imsi: "12345" };
const USIMContract = await ethers.getContractFactory("USIM");
const usimContract = await USIMContract.deploy(89, structJS);
console.log("USIM deployed to: ", usimContract.address);

剧本:

struct SolStruct { uint id; string title; string iccid; string msisdn; string imsi; }
contract USIM {
uint private _iccid;
SolStruct private _communicationProfile;
constructor( uint iccid_, SolStruct memory communicationProfile_ )
{
_iccid = iccid_;
_communicationProfile = communicationProfile_;
}
}