122

在 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。

4

12 回答 12

157

Javascript 1.7 添加了解构赋值,它允许你做你想要做的事情。

function getTuple(){
   return ["Bob", 24];
}
var [a, b] = getTuple();
// a === "bob" , b === 24 are both true
于 2010-12-22T19:44:41.297 回答
50

你必须以丑陋的方式去做。如果你真的想要这样的东西,你可以看看CoffeeScript,它有这个和很多其他的特性,使它看起来更像 python(很抱歉让它听起来像广告,但我真的很喜欢它。)

于 2010-12-22T18:34:32.007 回答
35

你可以做类似的事情:

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

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

tuple.name 
tuple.age 
于 2013-02-22T13:49:40.147 回答
29

这个“元组”特性在 EcmaScript2015 中被称为解构,并且很快就会被最新的浏览器支持。暂时只有 Firefox 和 Chrome 支持

但是,您可以使用转译器

代码看起来和 python 一样好:

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

console.log(name)
console.log(age)
于 2015-11-06T10:18:50.893 回答
16

冻结数组的行为与 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

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

于 2017-11-25T14:16:05.467 回答
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

尝试分配新值

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

但是值没有改变

> console.log(tuple)
[ 'Bob', 24 ]
于 2015-03-11T22:21:13.547 回答
5

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

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

于 2010-12-22T18:32:32.880 回答
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
})()

evalSafari 不支持。

于 2012-04-24T13:00:36.187 回答
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

于 2016-02-09T04:55:08.283 回答
3

您也可以在 Javascript 中使用元组类型。只需用高阶函数定义即可(学术术语是 Church encoding):

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它不用作普通构造函数。该解决方案根本不依赖原型系统,而仅依赖于高阶函数。

元组与像元组一样使用的 s相比有什么优势Array?Church 编码的元组在设计上是不可变的,因此可以防止由突变引起的副作用。这有助于构建更强大的应用程序。此外,更容易推理区分Arrays 作为集合类型(例如[a])和元组作为各种类型的相关数据(例如(a, b))的代码。

于 2017-05-21T15:30:33.403 回答
1

这是一个简单的 Javascript 元组实现:

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 等元组。

于 2014-09-26T16:57:56.850 回答
0

我做了一个很好的元组实现。此解决方案允许数组解构以及基本类型检查。

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


于 2020-09-29T11:02:57.760 回答