【问题标题】:JavaScript variable assignments from tuples来自元组的 JavaScript 变量赋值
【发布时间】:2011-05-29 14:01:13
【问题描述】:

在 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 更受青睐。

【问题讨论】:

  • 在 JavaScript 中,别忘了声明变量:var tuple, name, age;
  • var name=tuple[0], age=tuple[1]; 打字有点多,但可能言过其实。

标签: javascript tuples destructuring


【解决方案1】:

Javascript 1.7 添加了destructuring assignment,它允许您基本上做自己想做的事情。

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 嗯,这很有趣,我自己也遇到过这种情况,因为 JavaScript 似乎没有提供明显的元组支持,并且来自函数式编程的激烈时期,你开发了想要的元组。我也刚刚发现了这个,但不确定它是否有效,它在元组支持方面看起来也不错:cs.umd.edu/projects/PL/arrowlets/api-tuples.xhtml。我一定会看看 CoffeeScript。
    • 更新这个答案会很棒,很多 CoffeeScript 想法已经移植到 javascript,不再需要 CoffeeScript
    【解决方案3】:

    你可以做类似的事情:

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

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

    tuple.name 
    tuple.age 
    

    【讨论】:

    • 我不会投反对票,但从技术上讲这是不正确的。在声明对象后,您仍然可以更改(即改变) tuple.name 和 tuple.age 的值。根据定义,不可变类型在定义后不能更改。它们就像只读类型,参数及其值都只能声明一次。
    • @EvanPlaice 如果可变性是一个问题,您可以使用Object.freeze(),即:tuple = Object.freeze({ name:'Bob', age:14 })
    • @canon 我同意,这可能是整个线程中唯一可接受/正确的方法。不幸的是,mcm 的回答并没有冻结对象,所以它仍然是可变的。
    • @EvanPlaice 我不明白可变性问题来自哪里 - 元组在原始 Python 示例中不是不可变的!
    • @DanielBuckmaster Python 中列表和元组之间的区别在于,列表是可变的,而元组不是。见docs.python.org/2/tutorial/…。 JavaScript 本身不支持元组,因为所有 JS 数据结构都是可变的,除非您在创建后对对象调用 Object.freeze()。
    【解决方案4】:

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

    但是,您可以使用transpiler

    代码看起来和 python 一样好:

    let tuple = ["Bob", 24]
    let [name, age] = tuple
    
    console.log(name)
    console.log(age)
    

    【讨论】:

    【解决方案5】:

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

    目前适用于所有最新的浏览器。

    【讨论】:

    • 你不能把它设置成一个常量吗? const 是不可变的,不是吗?
    • 没有。 const 不可重新分配。你不能做 const tuple = ["Jim", 35];元组 = [“詹姆斯”,35]。你可以做 const tuple = ["Jim", 35];元组 [0] = "詹姆斯";因此它不是一成不变的。
    【解决方案6】:

    JavaScript 不支持元组

    如果您正在寻找不可变列表,可以使用 Object.freeze() 使数组不可变。

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

    来源:Mozilla Developer Network - 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 ]
    

    【讨论】:

    • 附带说明希望 Tuples 能够在 ES6 中获得一流的支持。真正的原生元组(即异源序列)也会提高速度。
    【解决方案7】:

    很遗憾,您不能在 (ECMA|Java)Script 中使用该元组赋值语法。

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

    【讨论】:

      【解决方案8】:

      这并不打算在现实生活中实际使用,只是一个有趣的练习。详情请见Why is using the JavaScript eval function a bad idea?

      这是您在不求助于供应商特定扩展的情况下可以获得的最接近的方法:

      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 中不受支持。

      【讨论】:

      • +1 表示超级聪明,但不要在现实生活中使用它:-p
      【解决方案9】:

      作为部长回答的更新,您现在可以使用 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 的问题,他要求解构
      【解决方案10】:

      您也可以在 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 不用作普通构造函数。该解决方案完全不依赖原型系统,而仅依赖于高阶函数。

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

      【讨论】:

        【解决方案11】:

        这是一个简单的 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 等元组。

        【讨论】:

        • 其实这不是t-uple,而是一对。
        • tuple 实例仍然是可变的,所以它在技术上不是一个元组。证明更改tuple.Item1 = "Steve" 然后console.log() 输出。
        • 感谢您的发现。我已经修改了示例以使元组不可变。
        • 如何修改它以支持任意长度?
        • 这没有回答 OP 的问题,他要求解构
        【解决方案12】:

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

        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
        
        
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-03-26
          • 2014-03-22
          • 2019-03-24
          • 2018-12-26
          • 2017-07-01
          • 2011-09-03
          • 1970-01-01
          相关资源
          最近更新 更多