Destucturing на обект

Синтаксиса за destucturing на обект използва обекта от лявата страна на операцията. Например:

let node = {
        type: "Identifier",
        name: "foo"
    };

let { type, name } = node;

console.log(type);      // "Identifier"
console.log(name);      // "foo"
				
			

В този код, стойността на node.type се съхранява в променлива наречена type, а стойността на node.name се съхранява в променлива наречена name. Този синтаксис е същия, като за инициализиране на свойства на обект с краткия синтаксис представен в Глава 4. Идентификаторите type и name са двете декларации за локални променливи и свойствата четат стойността от node обекта.

Не забравяйте инициализатора

Когато използваме destucturing за деклариране на променливи, използвайки var, let или const, ние трябва да предоставим инициализатор (стойността след знака за равенство). Следващите редове код, всички хвърлят синтактични грешки, поради липса на инициализатор:

// syntax error!
var { type, name };

// syntax error!
let { type, name };

// syntax error!
const { type, name };
				
			

Докато const винаги изисква инициализатор, дори когато се използва за nondestructured променливи, var и let изискват да се инициализират само, когато се използват за destucturing.

Destucturing на присвояването

Примерите за обектно destucturing досега използваха декларации на променливи. Обаче, е възможно да се използва за destucturing на присвоявания. Например, вие може да искате да промените стойностите на променливите след, като те са определени, както следва:

let node = {
        type: "Identifier",
        name: "foo"
    },
    type = "Literal",
    name = 5;

// задаване на различни стойности използвайки destucturing
({ type, name } = node);

console.log(type);      // "Identifier"
console.log(name);      // "foo"
				
			

В този пример type и name се инициализират със стойности, когато се декларират, а след това две променливи със същите имена се инициализират с различни стойности. Следващия ред използва присвояване за да промени тези стойности, чрез четенето им от node обекта. Имайте в предвид, че трябва да се сложат скоби около изявлението за присвояване. Това е така, защото във фигурните скоби се очаква да бъде блок изявление, а блок изявлението не може да се появи от лявата страна на присвояването. Скобите са сигнал, че следващите фигурни скоби не са изявление на блок и трябва да се тълкуват, като израз, позволявайки на присвояването да завърши.

destucturing на присвояването се оценява от дясната страна на израза (след =), това означава, че можете да използвате destucturing на присвояване навсякъде където се очаква стойност. Например, подаване на стойност към функция:

let node = {
        type: "Identifier",
        name: "foo"
    },
    type = "Literal",
    name = 5;

function outputInfo(value){
	console.log(value === node);    //true
}

outputInfo({ type, name } = node);

console.log(type);      // "Identifier"
console.log(name);      // "foo"
				
			

Функцията outputInfo() се извиква с израза на destucturing на присвояване. Израза оценява node, защото това е стойността от дясната страна на израза. Задачата към type и name е и двете да се държат нормално и node се подава към outputInfo().

worning
Грешка се хвърля, когато дясната страна на destucturing на израза за присвояване (израза след = ) се оцени на null или undefined. Това се случва, защото резултата на всеки опит да се прочете свойство на null или undefined, е грешка по време на работа.

Default стойности

Когато използвате destucturing в изявлението за присвояване, ако зададете локална променлива с име на свойство, което не съществува в обекта, тази локална променлива присвоява стойност undefined. Например:

let node = {
        type: "Identifier",
        name: "foo"
    };

let { type, name, value } = node;

console.log(type);      // "Identifier"
console.log(name);      // "foo"
console.log(value);     // undefined
				
			

Този код определя допълнителна локална променлива, наречена value и се опитва да и зададе стойност. Обаче, такова съответно value свойство не съществува в node обекта, така че на променливата се дава стойност undefined, както се очаква.

Може евентуално да се определи default стойност, за да я използвате, когато определено свойство не съществува. За да направите това, трябва да поставите знака за равенство (=) след името на свойството и да определите default стойността, като това:

let node = {
        type: "Identifier",
        name: "foo"
    };

let { type, name, value = true } = node;

console.log(type);      // "Identifier"
console.log(name);      // "foo"
console.log(value);     // true
				
			

В този пример, на променливата value се дава true, като default стойност. Default стойността се използва само, ако свойството липсва в node или има стойност undefined. Тъй като няма node.value свойство, променливата value използва default стойността. Това работи подобно на стойностите на параметрите по подразбиране за функции, както е описано в Глава 3.

Присвояване на различни локални имена на променливи

До този момент, всеки пример за destucturing на присвояването, използва името на свойство на обекта, като локално име на променлива. Например, стойността на node.type се съхранява в type променлива. Това работи добре, когато искате да използвате същото име, но какво ще стане ако не го направите? ECMAScript 6 има разширен синтаксис, който ви позволява да присвоите локална променлива с различно име, този синтаксис прилича на дългия запис за инициализиране на обект.

let node = {
        type: "Identifier",
        name: "foo"
    };

let { type: localType, name: localName } = node;

console.log(localType);     // "Identifier"
console.log(localName);     // "foo"
				
			

Този код използва destucturing на присвояването, за да декларира localType и localName променливите, които съдържат стойностите от node.type и node.name свойствата, съответно. Синтаксиса type: localType казва да се прочете свойство с име type и да съхрани стойността му в localType променливата. Този синтаксис е ефективно обратен на традиционния синтаксис за обект, където името е от лявата страна на двуточието, а стойността е от дясната страна. В този случай, името е от дясната страна на двуточието, а местоположението на стойността се чете от ляво.

Можете да добавите default стойност, като използвате различно име на променлива. Знака за равенство и default стойността все още се поставят след локалното име на променливата. Например:

let node = {
        type: "Identifier"
    };

let { type: localType, name: localName = "bar" } = node;

console.log(localType);     // "Identifier"
console.log(localName);     // "bar"
				
			

Тука, променливата localName има default стойност - "bar". Променливата присвоява тази default стойност, защото няма node.name свойство.

Досега видяхте, как да се справите с destucturing на даден обект, чиито свойства са примитивни стойности, но destucturingна обект, може да се използва за извличане на стойности от вложени структури на обекти.

Destucturing на вложени структури на обекти

С помощта на синтаксис подобен за обект, можете да навигирате във вложена структура на обект, за да извлечете само информацията, която искате. Ето един пример:

let node = {
        type: "Identifier",
        name: "foo",
        loc: {
            start: {
                line: 1,
                column: 1
            },
            end: {
                line: 1,
                column: 4
            }
        }
    };

let { loc: { start }} = node;

console.log(start.line);        // 1
console.log(start.column);      // 1
				
			

Схемата на destucturing в този пример използва фигурни скоби за да покаже, че моделът трябва да се спусне в свойството loc на node и да търси start свойството. Спомнете си от предния раздел, че винаги, когато има двуточие в модела на destucturing, това означава, че идентификатора преди двуточието дава мястото за инспектиране, а от дясната страна присвоява стойност. Когато има фигурни скоби след двуточието, показва, че дестинацията е вложена в друго ниво на обекта.

Можете да отидете една стъпка напред и да използвате различно име за локална променлива, като това:

let node = {
        type: "Identifier",
        name: "foo",
        loc: {
            start: {
                line: 1,
                column: 1
            },
            end: {
                line: 1,
                column: 4
            }
        }
    };

// extract node.loc.start
let { loc: { start: localStart }} = node;

console.log(localStart.line);   // 1
console.log(localStart.column); // 1
				
			

В тази версия на кода, node.loc.start се съхранява в нова локална променлива, наречена localStart. Моделите на destucturing могат да бъдат вложени до произволно ниво на дълбочина с всички налични опции на разположение за всяко ниво.

Обектното destucturing е много мощно и има много опции, но destucturing на масив предлага някои уникални възможности, които позволяват извличане на информация от масиви.

Синтаксис Gotcha (клопка на езика)

Бъдете внимателни, когато използвате вложено destucturing, защото може по невнимание да създадете изявление, което няма никакъв ефект. Празните фигурни скоби са разрешени при destucturing на обект, но те не правят нищо. Например:

// Няма обявени променливи!
let { loc: {} } = node;
				
			

Няма обявено обвързване в тази декларация. Понеже фигурните скоби в дясно от loc се използват, като място на инспектиране, а не за създаване на обвързване. В този случай, най-вероятно имаме намерение да използваме = за да определим стойност по подразбиране, вместо да определим местоположение. Възможно е този синтаксис да бъде забранен в бъдеще, но за сега gotcha се грижи затова.

Destucturing на масиви

Синтаксиса на destucturing на масиви е много подобен на синтаксиса на destucturing за обект. Той просто използва синтаксис за масив, вместо синтаксис за обект. Разликата е, че destucturing на масиви работи с позиции в рамките на масива, а не с обявените свойства, които са налични в обекта. Например:

let colors = [ "red", "green", "blue" ];

let [ firstColor, secondColor ] = colors;

console.log(firstColor);        // "red"
console.log(secondColor);       // "green"
				
			

Тука, destucturing на масива вади стойностите "red" и "green" от colors масива и ги съхранява в firstColor и secondColor променливите. Тези стойности са избрани заради тяхната позиция в масива, като действителните имена на променливите могат да бъдат всичко. Всеки елемент, който не е споменат в модела на destucturing се игнорира. Имайте в предвид, че самия масив не се променя по никакъв начин.

Можете също така да пропуснете елементи в схемата на destucturing и да предоставите само имената на променливите за елементите, които ви интересуват. Ако например, искате третата стойност в масива, не е необходимо да представяте имената на променливите за първия и втория елемент. Ето как става това:

let colors = [ "red", "green", "blue" ];

let [ , , thirdColor ] = colors;

console.log(thirdColor);        // "blue"
				
			

Този код използва destucturing на присвояване за извличане на третия елемент в colors. Запетаите преди thirdColor в модела са запазени места за елементите в масива, които са преди него. С използването на този подход, можете лесно да избирате стойности от произволен брой слотове в средата на масив, без да е необходимо да се предоставят имена на променливи за тях.

worning
Подобно на обектното destucturing, трябва винаги да се осигури инициализатор, когато се използва destucturing на масив с var, let или const.

Destucturing на присвояването

Можете да използвате destucturing на масив в контекста на присвояването, но за разлика от обектното destucturing не е необходимо да се обвива израза в скоби. Например:

let colors = [ "red", "green", "blue" ],
    firstColor = "black",
    secondColor = "purple";

[ firstColor, secondColor ] = colors;

console.log(firstColor);        // "red"
console.log(secondColor);       // "green"
				
			

Destucturing на присвояването в този код работи по подобен начин на миналия пример за destucturing на масив. Единствената разлика е, че firstColor и secondColor са вече дефинирани. През повечето време, това вероятно е всичко, което трябва да знаете за destucturing на присвояването за масиви, но има още нещо, което вероятно ще намерите за полезно.

Destucturing на присвояването за масиви има уникален случай на използване, което го прави по-лесен за сменяне на стойностите на две променливи. Смяната на стойност е обща операция в алгоритъм за сортиране, в ECMAScript 5 начина за смяна на променливи включва трета временна променлива, като този пример:

// Смяна на променлива в ECMAScript 5 
let a = 1,
    b = 2,
    tmp;

tmp = a;
a = b;
b = tmp;

console.log(a);     // 2
console.log(b);     // 1
				
			

Междинната променлива tmp е необходима за да се разменят стойностите на a и b. Използването на destucturing на присвояването за масив, не изисква такава променлива. Ето как можете да смените променливите в ECMAScript 6:

// Смяна на променлива в ECMAScript 6
let a = 1,
    b = 2;

[ a, b ] = [ b, a ];

console.log(a);     // 2
console.log(b);     // 1
				
			

Destucturing на присвояването за масив в този пример, изглежда като огледален образ. Лявата страна на присвояването (преди знака за равно) е модел за destucturing, точно както в другите примери за destucturing на масив. Дясната страна е масив, който временно е създаден за размяната. destucturing се случва в временния масив, който има стойности b и a копирани в своята първа и втора позиция. Ефекта е, че тези променливи разменят стойностите.

worning
Както destucturing на присвояването за обект, грешка се хвърля, когато дясната страна на израза на destucturing на присвояването се оценява на null или undefined.

Default стойности

Destucturing на присвояването за масиви, ни позволява да определим default стойност за всяка позиция в масива, също. Default стойността се използва, когато не съществува свойството на дадена позиция или има стойност undefined. Например:

let colors = [ "red" ];

let [ firstColor, secondColor = "green" ] = colors;

console.log(firstColor);        // "red"
console.log(secondColor);       // "green"
				
			

Този код за масива colors има само един елемент, така че няма нищо за съвпадение със secondColor. Тъй като има default стойност за secondColor, той е настроен на "green" вместо на undefined.

Вложен destucturing

Можем да destucturing на вложени масиви, по начин подобен на destucturing на вложени обекти. Чрез вмъкване на друг модел масив в общия модел и destucturing ще слезе във вложения масив, като това:

let colors = [ "red", [ "green", "lightgreen" ], "blue" ];

// later

let [ firstColor, [ secondColor ] ] = colors;

console.log(firstColor);        // "red"
console.log(secondColor);       // "green"
				
			

Тука secondColor променливата се отнася до "green" стойността в colors масива. Този елемент се съдържа в рамките на втория масив, така че не са необходими допълнителни квадратни скоби около secondColor в модела за destucturing. Както при обектите, можем да влизаме в масиви произволно дълбоко.

Rest елементи

В Глава 3 въведохме rest параметри за функции, destucturing на масиви има подобна концепция, наречена rest елементи. Rest елементите използват три точковия ... синтаксис за присвояване на останалите елементи в масив към конкретна променлива. Ето един пример:

let colors = [ "red", "green", "blue" ];

let [ firstColor, ...restColors ] = colors;

console.log(firstColor);        // "red"
console.log(restColors.length); // 2
console.log(restColors[0]);     // "green"
console.log(restColors[1]);     // "blue"
				
			

Първият елемент в colors се присвоява от firstColor, а останалите са разпределени в нов restColors масив. Масива restColors следователно, има два елемента "green" и "blue". Rest елементите са полезни за извличане на определени елементи от масив и запазване на останалите налични елементи, но има и друга полезна употреба.

Голям пропуск на JavaScript масивите е възможността лесно да създават клонинг. В ECMAScript 5, програмистите, често използват concat() метода, като лесен начин за клониране на масив. Например:

// клониране на масив в ECMAScript 5
var colors = [ "red", "green", "blue" ];
var clonedColors = colors.concat();

console.log(clonedColors);      //"[red,green,blue]"
				
			

Докато concat() метода е предназначен да се слепят два масива заедно, извиквайки го без аргумент, връща клонинг на масива. В ECMAScript 6 можете да използвате rest елементите, за да постигнете същото нещо, чрез синтаксис, предназначен да функционира по същия начин. Той работи по следния начин:

// клониране на масив в ECMAScript 6
let colors = [ "red", "green", "blue" ];
let [ ...clonedColors ] = colors;

console.log(clonedColors);      //"[red,green,blue]"
				
			

В този пример, rest елементите се използват за копиране на стойности от colors масива в clonedColors масива. Въпреки, че е въпрос на възприемане, дали тази техника прави намеренията на програмиста по-ясни от метода concat(), това е полезна информация, с която да бъдете наясно.

worning
Rest елементите трябва да са последното влизане в destucturing на масива и не могат да бъдат последвани от запетая. Включването на запетая след rest параметрите е syntax error.

Смесен destucturing

Destucturing на обекти и масиви може да се използва за създаване на по-сложни изрази. По този начин, вие ще сте в състояние да извличате парчета с информация, която искате от всяко смесване на обекти и масиви. Например:

let node = {
        type: "Identifier",
        name: "foo",
        loc: {
            start: {
                line: 1,
                column: 1
            },
            end: {
                line: 1,
                column: 4
            }
        },
        range: [0, 3]
    };

let {
    loc: { start },
    range: [ startIndex ]
} = node;

console.log(start.line);        // 1
console.log(start.column);      // 1
console.log(startIndex);        // 0
				
			

Този код извлича node.loc.start и node.range[0] в start и startIndex, съответно. Имайте в предвид, че loc: и range: в модела на destucturing са само места, които съответстват на свойства в node обекта. Те не са част от node, който не може да бъде извлечен чрез destucturing, когато използвате комбинация от destucturing на обект и масив. Този подход е особено полезен за извличане на стойности от структура на JSON конфигурация без навигиране по цялата структура.

Destucturing на параметри

Destucturing има още един особено полезен случай на употреба, това е при подаване на аргументи към функция. Когато JavaScript функцията взема голям брой избираеми параметри, един общ модел е да се създаде обект с опции, чиито свойства определят допълнителни параметри, като тези:

// свойствата на options предоставят допълнителни параметри
function setCookie(name, value, options) {

    options = options || {};

    let secure = options.secure,
        path = options.path,
        domain = options.domain,
        expires = options.expires;

    // код за задаване на cookie
}

// карта на третият аргумент за опции
setCookie("type", "js", {
    secure: true,
    expires: 60000
});
				
			

Много JavaScript библиотеки съдържат setCookie() функции, които изглеждат по подобен начин, като тази. В тази функция, name и value са задължителни аргументи, но secure, path, domain и experes не са. И тъй като няма никакъв приоритетен ред за другите данни, е по-добре просто да има един options обект с имена на свойства, от колкото списък с допълнителни имена на параметри. Този подход работи, но сега не можем да кажем, какво очаква на входа функцията, само като погледнем в дефиницията на функцията, налага се да прочетем тялото на функцията.

Destucturing на параметри предлага алтернатива, която прави по-ясно, какви аргументи очаква функцията. Destucturing на параметър използва модел на destucturing на един обект или масив, вместо име на параметър. За да видите това в действие, погледнете тази пренаписана версия на setCookie() функцията от последния пример

function setCookie(name, value, { secure, path, domain, expires }) {

    // код за задаване на cookie
}

setCookie("type", "js", {
    secure: true,
    expires: 60000
});
				
			

Тази функция се държи подобно на предишния пример, но сега третия аргумент използва destucturing за да извади необходимите данни. Параметрите извън destucturing параметър са ясно очаквани и в същото време става ясно на някой който използва setCookie(), какви опции са на разположение, като допълнителни аргументи. И разбира се, ако третият аргумент е необходим, стойностите които съдържа са кристално ясни. Destucturing параметрите също действат, като редовни параметри, те са определени на undefined, ако не са подадени.

Destucturing на параметрите имат всичко от възможностите на destucturing, които сте научили в тази глава. Можете да използвате стойности по подразбиране, смесените модели за обекти и масиви и използване на имена на променливите, които се различават от свойствата, които четат.

Destucturing на параметри е задължително

Една особеност на използването destucturing на default параметри е, че се хвърля грешка, когато те не са предвидени в извикването на функцията. Например това извикване на setCookie() функцията в последния пример хвърля грешка:

// Error!
setCookie("type", "js");
				
			

Третият аргумент липсва и така се оценява на undefined, както се очаква. Това предизвиква грешка, защото destucturing на параметри е само обозначение за destucturing декларация. Когато setCookie() функцията се извика, JavaScript машината всъщност прави това:

function setCookie(name, value, options) {

    let { secure, path, domain, expires } = options;

    // код за задаване на cookie
}
				
			

Тъй като destucturing хвърля грешка, когато изразът от дясната страна е null или undefined, същото важи и когато третия аргумент не се подава на setCookie() функцията.

Ако искате destucturing на параметър да се изисква, тогава това поведение не е толкова тревожно. Но ако искате destucturing на параметър да е по желание, може да заобиколите този проблем, като предоставите стойност по подразбиране на destucturing на параметър, като тази:

function setCookie(name, value, { secure, path, domain, expires } = {}) {

    // ...
}	
			

Този пример дава нов обект с default стойност за третия параметър. Осигуряване на default стойност за destucturing на параметър означава, че всичките secure, path, domain и expires ще бъдат undefined, ако трети аргумент за setCookie() не е осигурен и няма да бъде хвърлена грешка.

Default стойности за destucturing на параметри

Можете да определите default destucturing стойности за destucturing на параметри, точно както в destucturing на присвояването. Просто добавете знака за равенство след параметъра и определете default стойността. Например:

function setCookie(name, value,
    {
        secure = false,
        path = "/",
        domain = "example.com",
        expires = new Date(Date.now() + 360000000)
    }
) {

    // ...
}
				
			

Всяко свойство в destucturing на параметрите има default стойност в този код, така че можем да избегнем проверката дали дадено свойство е включено за да използва правилната стойност. Също така целия destucturing на параметри има default стойност - празен обект, като параметър по избор. Това прави декларацията на функцията изглеждат малко по-сложно от обикновено, но е малка цена, за осигуряване на всеки аргумент да има използваема стойност.