来自元组的 JavaScript 变量赋值

在 Python2和 Python3等其他语言中,您可以定义和赋值一个 tuple 变量,并像下面这样检索它们的值:

tuple = ("Bob", 24)
name, age = tuple
print(name)           #name evaluates to Bob
print(age)            #age evaluates to 24

JavaScript 中有类似的东西吗? 或者我只需要用一个数组来做这件事:

tuple = ["Bob", 24]
name = tuple[0]       //name Evaluates to Bob
age = tuple[1]        //age Evaluates to 24

有没有更好的方法在 JavaScript5中模拟 Python 元组?

更新: 查看关于 ES6的答案,对于新项目来说,ES6应该比 CoffeeScript 更受欢迎。

184161 次浏览

不幸的是,您不能在(ECMA | Java)脚本中使用元组赋值语法。

编辑: 有人链接到 Mozilla/JS 1.7-这不会跨浏览器工作,但如果这不是必需的,那么有你的答案。

你必须用丑陋的方式。如果你的 真的想要这样的东西,你可以看看 咖啡脚本,它有很多其他的功能,使它看起来更像 Python (抱歉使它听起来像一个广告,但我真的很喜欢它)

Javascript 1.7添加了 破坏性转让,它允许您完成您想要完成的任务。

function getTuple(){
return ["Bob", 24];
}
var [a, b] = getTuple();
// a === "bob" , b === 24 are both true

这并不打算在现实生活中实际使用,只是一个有趣的练习。详情请参阅 为什么使用 JavaScript eval 函数是个坏主意?

这是最接近于不使用特定于供应商的扩展的方法:

myArray = [1,2,3];
eval(set('a,b,c = myArray'));

辅助功能:

function set(code) {
var vars=code.split('=')[0].trim().split(',');
var array=code.split('=')[1].trim();
return 'var '+vars.map(function(x,i){return x+'='+array+'['+i+']'}).join(',');
}

证明它在任意范围内有效:

(function(){
myArray = [4,5,6];
eval(set('x,y,z = myArray'));
console.log(y);  // prints 5
})()

Safari 不支持 eval

你可以做类似的事情:

var tuple = Object.freeze({ name:'Bob', age:14 })

然后将名称和年龄称为属性

tuple.name
tuple.age

下面是一个简单的 Javascript Tuple 实现:

var Tuple = (function () {
function Tuple(Item1, Item2) {
var item1 = Item1;
var item2 = Item2;
Object.defineProperty(this, "Item1", {
get: function() { return item1  }
});
Object.defineProperty(this, "Item2", {
get: function() { return item2  }
});
}
return Tuple;
})();


var tuple = new Tuple("Bob", 25); // Instantiation of a new Tuple
var name = tuple.Item1; // Assignment. name will be "Bob"
tuple.Item1 = "Kirk"; // Will not set it. It's immutable.

这是一个2元组,但是,您可以修改我的示例以支持3、4、5、6等元组。

JavaScript 不支持元组

如果您正在寻找一个不可变的列表,Object.free()可以用来使数组成为不可变的。

Zen ()方法冻结一个对象: 也就是说,阻止将新的属性添加到对象中; 阻止移除现有属性; 阻止更改现有属性或其可枚举性、可配置性或可写性。在本质上,对象是有效地不可变的。该方法返回被冻结的对象。

资料来源: Mozilla Developer Network-Object.zen ()

像往常一样分配一个数组,但使用‘ Object.free()锁定它

> tuple = Object.freeze(['Bob', 24]);
[ 'Bob', 24 ]

像使用常规数组一样使用这些值(不支持 python 多重赋值)

> name = tuple[0]
'Bob'
> age = tuple[1]
24

尝试分配新值

> tuple[0] = 'Steve'
'Steve'

但是值没有改变

> console.log(tuple)
[ 'Bob', 24 ]

这个“ tuple”特性在 EcmaScript2015中被称为解构,很快就会得到最新浏览器的支持。目前是 只有 Firefox 和 Chrome 支持

但是,你可以用 翻译器

这段代码看起来和 python 一样漂亮:

let tuple = ["Bob", 24]
let [name, age] = tuple


console.log(name)
console.log(age)

作为对部长回答的更新,你现在可以用 es2015做到这一点:

function Tuple(...args) {
args.forEach((val, idx) =>
Object.defineProperty(this, "item"+idx, { get: () => val })
)
}




var t = new Tuple("a", 123)
console.log(t.item0) // "a"
t.item0 = "b"
console.log(t.item0) // "a"

Https://jsbin.com/fubaluwimo/edit?js,console

也可以在 Javascript 中使用 tuple 类型。只需用高阶函数定义它(学术术语是 Church 编码) :

const Tuple = (...args) => {
const Tuple = f => f(...args);
return Object.freeze(Object.assign(Tuple, args));
};


const get1 = tx => tx((x, y) => x);


const get2 = tx => tx((x, y) => y);


const bimap = f => g => tx => tx((x, y) => Tuple(f(x), g(y)));


const toArray = tx => tx((...args) => args);


// aux functions


const inc = x => x + 1;
const toUpperCase = x => x.toUpperCase();


// mock data


const pair = Tuple(1, "a");


// application


console.assert(get1(pair) === 1);
console.assert(get2(pair) === "a");


const {0:x, 1:y} = pair;
console.log(x, y); // 1 a


console.log(toArray(bimap(inc) (toUpperCase) (pair))); // [2, "A"]


const map = new Map([Tuple(1, "a"), Tuple(2, "b")]);
console.log(map.get(1), map.get(2)); // a b

请注意,Tuple不作为普通构造函数使用。该解决方案完全不依赖于原型系统,而仅仅依赖于高阶函数。

与使用类似元组的 Array相比,元组有哪些优点?Church 编码的元组在设计上是不可变的,因此可以防止由突变引起的副作用。这有助于构建更健壮的应用程序。此外,对于区分作为集合类型的 Array(例如 [a])和作为不同类型的相关数据(例如 (a, b))的元组的代码,推理起来更容易。

冻结数组的行为与 python tuple 相同:

const tuple = Object.freeze(["Bob", 24]);
let [name, age]; = tuple
console.debug(name); // "Bob"
console.debug(age); // 24

花哨一点,定义一个类

class Tuple extends Array {
constructor(...items) {
super(...items);
Object.freeze(this);
}
}


let tuple = new Tuple("Jim", 35);
let [name, age] = tuple;
console.debug(name); // Jim
console.debug(age); // 35
tuple = ["Bob", 24]; // no effect
console.debug(name); // Jim
console.debug(age); // 25

如今在所有最新的浏览器中都可以使用。

我做了一个 tuple 实现,它工作得很好。这个解决方案允许数组解构,以及基本的类型检查。

const Tuple = (function() {
function Tuple() {
// Tuple needs at least one element
if (arguments.length < 1) {
throw new Error('Tuple needs at least one element');
}


const args = { ...arguments };


// Define a length property (equal to the number of arguments provided)
Object.defineProperty(this, 'length', {
value: arguments.length,
writable: false
});


// Assign values to enumerable properties
for (let i in args) {
Object.defineProperty(this, i, {
enumerable: true,
get() {
return args[+i];
},
// Checking if the type of the provided value matches that of the existing value
set(value) {
if (typeof value !== typeof args[+i]) {
throw new Error('Cannot assign ' + typeof value + ' on ' + typeof args[+i]);
}


args[+i] = value;
}
});
}


// Implementing iteration with Symbol.iterator (allows for array destructuring as well for...of loops)
this[Symbol.iterator] = function() {
const tuple = this;


return {
current: 0,
last: tuple.length - 1,
next() {
if (this.current <= this.last) {
let val = { done: false, value: tuple[this.current] };
this.current++;
return val;
} else {
return { done: true };
}
}
};
};


// Sealing the object to make sure no more values can be added to tuple
Object.seal(this);
}


// check if provided object is a tuple
Tuple.isTuple = function(obj) {
return obj instanceof Tuple;
};


// Misc. for making the tuple more readable when printing to the console
Tuple.prototype.toString = function() {
const copyThis = { ...this };
const values = Object.values(copyThis);
return `(${values.join(', ')})`;
};


// conctat two instances of Tuple
Tuple.concat = function(obj1, obj2) {
if (!Tuple.isTuple(obj1) || !Tuple.isTuple(obj2)) {
throw new Error('Cannot concat Tuple with ' + typeof (obj1 || obj2));
}


const obj1Copy = { ...obj1 };
const obj2Copy = { ...obj2 };


const obj1Items = Object.values(obj1Copy);
const obj2Items = Object.values(obj2Copy);


return new Tuple(...obj1Items, ...obj2Items);
};


return Tuple;
})();


const SNAKE_COLOR = new Tuple(0, 220, 10);


const [red, green, blue] = SNAKE_COLOR;
console.log(green); // => 220