JavaScript变量赋值来自元组

142

在其他语言,如Python 2和Python 3中,您可以定义并向元组变量分配值,并像这样检索它们的值:

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

在 JavaScript 5 中,有没有更好的方法模拟 Python 元组?

更新:请查看关于 ES6 的答案,应该优先选择它而不是 CoffeeScript。


16
在JavaScript中,不要忘记声明变量:var tuple,name,age; - Šime Vidas
4
var name=tuple[0], age=tuple[1]; 的翻译是:将元组的第一个值赋给变量name,将元组的第二个值赋给变量age。尽管这种写法略微冗长,但称其为“丑陋”可能有些过头了。 - Brent Bradburn
12个回答

188
JavaScript 1.7 添加了解构赋值语法,它允许你基本上实现你想要的功能。
function getTuple(){
   return ["Bob", 24];
}
var [a, b] = getTuple();
// a === "bob" , b === 24 are both true

51

你必须使用丑陋的方式去做。如果你真的需要类似这样的功能,可以看看CoffeeScript,它拥有许多其他特性,使得它看起来更像Python(很抱歉听起来像广告,但我真的很喜欢它)。


嗯,这很有趣,我自己也遇到了这种情况,因为JavaScript似乎没有明显的元组支持,并且从一个强调函数式编程的时期过来,你会想要元组。我也刚发现这个,但不确定它是否有效,只是在元组支持方面看起来不错:http://www.cs.umd.edu/projects/PL/arrowlets/api-tuples.xhtml。我一定会看看CoffeeScript。 - 9codeMan9
很好更新这个答案,许多CoffeeScript的想法已经被移植到JavaScript中了,不再需要使用CoffeeScript。 - ncubica

35
你可以做类似的事情:
var tuple = Object.freeze({ name:'Bob', age:14 })

然后将名称和年龄作为属性引用
tuple.name 
tuple.age 

7
我不会点踩,但从技术上讲这是不正确的。在对象被声明之后,你仍然可以改变(即突变)元组的name和age值。按照定义,不可变类型在定义之后不能被更改。它们像只读类型一样,参数及其值只能被声明一次。 - Evan Plaice
4
如果可变性是个问题,你可以使用 Object.freeze()。例如:tuple = Object.freeze({ name:'Bob', age:14 })。请注意,在使用 Object.freeze() 后,对象不可被修改。 - canon
1
@EvanPlaice 我不明白可变性的问题从何而来 - 在原始的Python示例中,元组并不是不可变的! - Daniel Buckmaster
@DanielBuckmaster 是的,而且由于元组在创建后其结构是固定的,在本地支持它们的语言中可以更快地访问它们的值。 - Evan Plaice
@evan-plaice 字符串和数字在JavaScript中天然是不可变的,尽管我不会称它们为数据结构。 - gabssnake
显示剩余6条评论

32

这个“元组”功能在EcmaScript2015中被称为解构,最新的浏览器即将支持它。目前,只有Firefox和Chrome支持

但是,你可以使用转译器

代码会像Python一样美观:

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

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

2
给未来的读者:自Chrome 49起(根据Mozilla文档),此功能在Chrome中得到支持。您还可以使用Mozilla文档在此处检查兼容性:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment - Jamie

17

一个冻结数组的行为与Python元组完全相同:

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

现在可以在所有最新的浏览器中使用。


你不能将它设置为常量吗?常量是不可变的,对吧? - Mark A
5
不可以。const是不可重新赋值的。你不能这样做:const tuple = ["Jim", 35]; tuple = ["James", 35]。但你可以这样做:const tuple = ["Jim", 35]; tuple[0] = "James"。因此,它并非不可变的。 - Matthew James Davis

8

JavaScript不支持元组

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

Object.freeze()方法冻结一个对象:阻止向其中添加新属性;阻止删除现有属性;防止更改现有属性或其可枚举性、可配置性或可写性。本质上,该对象被有效地设置为不可变。该方法返回被冻结的对象。

来源:Mozilla 开发者网络 - Object.freeze()

像往常一样分配一个数组,但使用'Object.freeze()'将其锁定

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

将这些值用作普通数组(不支持python多重赋值)

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

尝试分配新值

(相关IT技术内容)
> tuple[0] = 'Steve'
'Steve'

但是这个值没有被更改。
> console.log(tuple)
[ 'Bob', 24 ]

顺便提一下,希望元组在ES6中能得到一流的支持。真正本地的元组(即异构序列)也将提高速度。 - Evan Plaice

5

很不幸,在(ECMA|Java)Script中无法使用元组赋值语法。

编辑:有人链接了Mozilla/JS 1.7 - 这种方法无法跨浏览器工作,但如果不要求跨浏览器,则可以使用该方法。


4
作为部长答案的更新,您现在可以使用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


在 ES2015 之前,你完全可以这样做... 另外,这并没有回答 OP 的问题,他问的是解构。 - assembly_wizard

4
这不是实际生活中使用的内容,只是一个有趣的练习。详情请参见为什么使用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
})()

eval在Safari浏览器中不被支持。


5
夸你超级聪明,但现实生活中不要使用这个词语。+1 - Jamund Ferguson

3

在Javascript中,您也可以使用元组类型。只需使用高阶函数(学术术语是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))之间的代码。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接