การกำหนดตัวแปร JavaScript จาก tuples


106

ในภาษาอื่น ๆ เช่น 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

มีวิธีที่ดีกว่าในการจำลอง Python tuples ใน JavaScript 5 หรือไม่?

อัปเดต:ดูคำตอบเกี่ยวกับ ES6 ซึ่งควรได้รับการสนับสนุนจาก CoffeeScript สำหรับโครงการใหม่


12
ใน JavaScript อย่าลืมประกาศตัวแปร:var tuple, name, age;
Šime Vidas

3
var name=tuple[0], age=tuple[1]; พิมพ์มากไปหน่อย แต่น่าเกลียดอาจเป็นการพูดเกินจริง
Brent Bradburn

คำตอบ:


125

Javascript 1.7 เพิ่มการกำหนดโครงสร้างที่ถูกทำลายซึ่งช่วยให้คุณสามารถทำสิ่งที่คุณเป็นอยู่ได้

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

5
นี่ไม่ใช่จาวาสคริปต์ตามมาตรฐาน แต่เป็นส่วนขยายเฉพาะของ Mozilla
ninjagecko

14
@ninjagecko: "JavaScript" เป็นการใช้งานของ Mozilla และการกำหนดโครงสร้างจะเป็นส่วนหนึ่งของมาตรฐาน ecmascript ที่กำลังจะมาถึง
Bergi

67
ตอนนี้เป็นส่วนหนึ่งของ ES6 แล้ว
ท่าเรือเปาโลรามอน

10
สองสามปีที่ผ่านมาคำตอบของฉันถูกต้องในทางเทคนิค แต่ไม่เป็นประโยชน์ทั้งที่ถูกต้องและเป็นประโยชน์ เย้!
pc1oad1etter

49

คุณต้องทำวิธีที่น่าเกลียด หากคุณต้องการสิ่งนี้จริงๆคุณสามารถตรวจสอบCoffeeScriptซึ่งมีสิ่งนั้นและคุณสมบัติอื่น ๆ อีกมากมายที่ทำให้ดูเหมือน python มากขึ้น (ขออภัยที่ทำให้ฟังดูเหมือนโฆษณา แต่ฉันชอบมาก)


อืมมันน่าสนใจทีเดียวเพิ่งจะพบกับสถานการณ์นี้ด้วยตัวเองเนื่องจาก JavaScript ดูเหมือนจะไม่ให้การสนับสนุนทูเพิลที่ชัดเจนและมาจากช่วงเวลาที่เข้มข้นของการเขียนโปรแกรมเชิงฟังก์ชันคุณจึงพัฒนาสิ่งที่ต้องการ ฉันยังพบเพียงแค่นี้ แต่ไม่แน่ใจว่าการทำงานมันก็ดูดีในแง่ของการสนับสนุน tuple เกินไป: cs.umd.edu/projects/PL/arrowlets/api-tuples.xhtml ฉันจะดู CoffeeScript แน่นอน
9codeMan9

29

คุณสามารถทำสิ่งที่คล้ายกัน:

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

จากนั้นอ้างถึงชื่อและอายุเป็นคุณลักษณะ

tuple.name 
tuple.age 

5
ฉันจะไม่ลงคะแนน แต่ในทางเทคนิคแล้วมันไม่ถูกต้อง คุณยังคงสามารถเปลี่ยน (เช่นกลายพันธุ์) ค่าของ tuple.name และ tuple.age ได้หลังจากประกาศอ็อบเจ็กต์ ตามคำจำกัดความประเภทที่ไม่เปลี่ยนรูปไม่สามารถเปลี่ยนแปลงได้หลังจากกำหนดแล้ว เป็นเหมือนประเภทอ่านอย่างเดียวที่สามารถประกาศทั้งพารามิเตอร์และค่าได้เพียงครั้งเดียว
Evan Plaice

4
@EvanPlaice หากเป็นปัญหาในการเปลี่ยนแปลงคุณสามารถใช้ได้Object.freeze()เช่น:tuple = Object.freeze({ name:'Bob', age:14 })
ศีล

@canon ฉันเห็นด้วยนั่นอาจเป็นแนวทางเดียวที่ยอมรับได้ / ถูกต้องในหัวข้อนี้ทั้งหมด น่าเสียดายที่คำตอบของ mcm ไม่ทำให้วัตถุหยุดนิ่งดังนั้นจึงยังคงไม่แน่นอน
Evan Plaice

@EvanPlaice ฉันไม่เห็นว่าปัญหาของการเปลี่ยนแปลงมาจากไหน - สิ่งที่ไม่สามารถเปลี่ยนรูปได้ในตัวอย่าง Python ดั้งเดิม!
Daniel Buckmaster

@DanielBuckmaster ความแตกต่างระหว่างรายการและทูเพิลใน Python คือรายการไม่แน่นอนในขณะที่ทูเปิลไม่ได้ ดูdocs.python.org/2/tutorial/... สิ่งทอไม่ได้รับการสนับสนุนโดยกำเนิดใน JavaScript เนื่องจากโครงสร้างข้อมูล JS ทั้งหมดไม่แน่นอนเว้นแต่คุณจะเรียก Object.freeze () บนวัตถุหลังการสร้าง
Evan Plaice

27

คุณลักษณะ "ทูเพิล" นี้เรียกว่าการทำลายโครงสร้างใน EcmaScript2015 และเร็ว ๆ นี้จะได้รับการสนับสนุนโดยเบราว์เซอร์รุ่นล่าสุด ในขณะนี้มีเพียง Firefox และ Chrome เท่านั้นที่รองรับสนับสนุนมัน

แต่เดี๋ยวก่อนคุณสามารถใช้ทรานสไพเลอร์ได้

รหัสจะดูดีเหมือน python:

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

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

2
ถึงผู้อ่านในอนาคต: คุณลักษณะนี้ได้รับการสนับสนุนใน chrome ตั้งแต่ chrome 49 (ตามเอกสารของ Mozilla) คุณยังสามารถตรวจสอบความเข้ากันได้โดยใช้เอกสาร Mozilla ที่นี่: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Jamie

14

อาร์เรย์ที่แช่แข็งจะทำงานเหมือนกับ 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

ใช้งานได้ในวันนี้ในเบราว์เซอร์ล่าสุดทั้งหมด


คุณไม่สามารถตั้งค่าเป็น const ได้หรือไม่? const ไม่เปลี่ยนรูปใช่ไหม
Mark A

3
ไม่ได้กำหนด const ใหม่ไม่ได้ คุณไม่สามารถทำ const tuple = ["Jim", 35]; tuple = ["James", 35] คุณสามารถทำ const tuple = ["Jim", 35]; tuple [0] = "เจมส์"; ดังนั้นจึงไม่เปลี่ยนรูป
Matthew James Davis

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 ทูเพิลพื้นเมืองที่แท้จริง (เช่นลำดับที่แตกต่างกัน) จะช่วยเพิ่มความเร็วด้วย
Evan Plaice

5

น่าเสียดายที่คุณไม่สามารถใช้ไวยากรณ์การกำหนดค่าทูเปิลในสคริปต์ (ECMA | Java) ได้

แก้ไข: มีคนเชื่อมโยงกับ Mozilla / JS 1.7 - สิ่งนี้จะไม่ทำงานข้ามเบราว์เซอร์ แต่ถ้าไม่จำเป็นก็มีคำตอบของคุณ


3

สิ่งนี้ไม่ได้ตั้งใจให้ใช้จริงในชีวิตจริงเป็นเพียงการออกกำลังกายที่น่าสนใจ ดูเหตุใดการใช้ 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 เพื่อความฉลาดสุด ๆ อย่าใช้สิ่งนี้ในชีวิตจริงแม้ว่า :-p
Jamund Ferguson

3

ในการอัปเดตคำตอบของรัฐมนตรีตอนนี้คุณสามารถทำได้กับ 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

3

คุณสามารถมีประเภททูเพิลใน Javascript ได้เช่นกัน เพียงกำหนดด้วยฟังก์ชันลำดับที่สูงขึ้น (คำศัพท์ทางวิชาการคือการเข้ารหัสของศาสนจักร):

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ว่าไม่ได้ใช้เป็นตัวสร้างปกติ โซลูชันนี้ไม่ได้พึ่งพาระบบต้นแบบเลย แต่ใช้ฟังก์ชันลำดับที่สูงกว่าเท่านั้น

อะไรคือข้อดีของ tuples over Arrays ที่ใช้เช่น tuples? สิ่งทอที่เข้ารหัสของโบสถ์นั้นไม่สามารถเปลี่ยนแปลงได้ตามการออกแบบดังนั้นจึงป้องกันผลข้างเคียงที่เกิดจากการกลายพันธุ์ สิ่งนี้ช่วยสร้างแอปพลิเคชันที่มีประสิทธิภาพมากขึ้น นอกจากนี้ยังง่ายกว่าที่จะให้เหตุผลเกี่ยวกับรหัสที่แยกความแตกต่างระหว่างArrays เป็นประเภทคอลเลกชัน (เช่น[a]) และสิ่งที่เพิ่มขึ้นเป็นข้อมูลที่เกี่ยวข้องในประเภทต่างๆ (เช่น(a, b))


1

นี่คือการใช้งาน 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-tuple แต่คุณสามารถปรับเปลี่ยนตัวอย่างของฉันเพื่อรองรับ 3,4,5,6 เป็นต้น


อันที่จริงนี่ไม่ใช่เสื้อยืด แต่เป็นคู่
ท่าเรือเปาโลรามอน

tupleอินสแตนซ์ยังคงไม่แน่นอนดังนั้นจึงเป็นเทคนิคที่ไม่ขอบเขตของ สำหรับการเปลี่ยนแปลงหลักฐานtuple.Item1 = "Steve"แล้วconsole.log()ส่งออก
Evan Plaice

1
ขอขอบคุณที่พบว่า ฉันได้แก้ไขตัวอย่างเพื่อทำให้ Tuple ไม่เปลี่ยนรูป
Faris Zacina

คุณจะแก้ไขสิ่งนี้เพื่อรองรับความยาวโดยพลการได้อย่างไร?
aij

สิ่งนี้ไม่ตอบคำถามของ OP เขาขอให้ทำลายโครงสร้าง
assembly_wizard

-1

นี่คือคำตอบของ Matthew James Davis ในเวอร์ชันที่มีการเพิ่มเมธอด Python tuple ใน:

class Tuple extends Array { 
  constructor(...items) { 
    super(...items); 
    Object.freeze(this);
  }
  toArray() {
    return [...this];
  }
  toString() {
    return '('+super.toString()+')';
  }
  count(item) {
    var arr = this.toArray();
    var result = 0;
    for(var i = 0; i < arr.length; i++) {
       if(arr[i] === item) {
         result++;
       }
    }
    return result;
  }

  

  
}

   let tuple = new Tuple("Jim", 35);
   let [name,age] = tuple;

console.log("tuple:"+tuple)
console.log("name:"+name)
console.log("age:"+age)


-1

ฉันทำการติดตั้งทูเพิลซึ่งใช้งานได้ดีทีเดียว โซลูชันนี้ช่วยในการทำลายโครงสร้างอาร์เรย์เช่นเดียวกับการตรวจสอบประเภทพื้นฐาน

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


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.