3 개의 점이 JavaScript를 변경 한 방법
본문
함수 호출의 인수에 액세스 할 때 인수 키워드를 좋아하지 않습니다. 하드 코딩 된 이름은 내부 함수 (자체 인수를 정의)에서 외부 함수의 인수에 액세스하기 어렵게 합니다.
더 나쁜 인수는 배열과 같은 객체입니다. .map() 또는 .forEach()와 같은 배열 메소드는 직접 사용할 수 없습니다.
https://dmitripavlutin.com/how-three-dots-changed-javascript/
엔 클로징 함수에서 인수에 액세스하려면 별도의 변수에 저장하여 임시 해결책을 사용해야 합니다. 이 배열 같은 객체를 살펴 보려면 duck typing을 사용하고 간접 호출을 해야 합니다. 다음 예를 참조하십시오.
function outerFunction() {
// store arguments into a separated variable
const argsOuter = arguments;
function innerFunction() {
// args is an array-like object
const even = Array.prototype.map.call(argsOuter, function(item) {
// do something with argsOuter
});
}
}
또 다른 상황은 동적 개수의 인수를 허용하는 함수 호출입니다. 배열에서 인수를 채우는 것은 불쾌합니다.
예를 들어 .push (item1, ..., itemN)은 요소를 하나씩 배열에 삽입합니다. 각 요소를 인수로 열거해야 합니다. 항상 편리하지는 않습니다. 종종 새로운 인스턴스를 만들지 않고 전체 요소 배열을 기존 배열로 푸시해야 합니다.
ES5에서는 친근하고 장황한 접근 방식 인 .apply()로 해결되었습니다. 한 번 보자:
const fruits = ['banana'];
const moreFruits = ['apple', 'orange'];
Array.prototype.push.apply(fruits, moreFruits);
console.log(fruits); // => ['banana', 'apple', 'orange']
다행히도 JavaScript 세계가 변화하고 있습니다. 3 개의 점 연산자는 이러한 많은 상황을 해결합니다. 운영자는 ECMAScript 6에 의해 소개되었으며 내 의견으로는 눈에 띄게 개선되었습니다.
이 기사에서는 ... 연산자 사용 사례를 살펴보고 유사한 문제를 해결하는 방법을 보여줍니다.
1. 세 개의 점
나머지 연산자는 인수 및 배열 구조 해제에서 인수 목록을 함수에 전달하는 데 사용됩니다. 운전자가 나머지를 모은 경우는 수술 후에 남아있었습니다.
function countArguments(...args) {
return args.length;
}
// get the number of arguments
countArguments('welcome', 'to', 'Earth'); // => 3
// destructure an array
let otherSeasons, autumn;
[autumn, ...otherSeasons] = ['autumn', 'winter'];
otherSeasons // => ['winter']
스프레드 연산자는 배열 구성 및 구조 해제에 사용되며 호출시 배열에서 함수 인수를 채우는 데 사용됩니다. 연산자가 배열 (또는 반복 가능한 객체) 요소를 분산 시키는 경우입니다.
let cold = ['autumn', 'winter'];
let warm = ['spring', 'summer'];
// construct an array
[...cold, ...warm] // => ['autumn', 'winter', 'spring', 'summer']
// function arguments from an array
cold.push(...warm);
cold // => ['autumn', 'winter', 'spring', 'summer']
2. 파라미터 접근 개선
2.1 휴식 파라미터
서론에서 제시 한 것처럼 함수 본문에서 인수 객체를 다루는 것은 복잡한 시나리오에서 번거로워집니다.
예를 들어 JavaScript 내부 함수 filterNumbers()는 외부 함수 sumOnlyNumbers()에서 인수에 액세스 하려고 합니다.
function sumOnlyNumbers() {
const args = arguments;
const numbers = filterNumbers();
return numbers.reduce((sum, element) => sum + element);
function filterNumbers() {
return Array.prototype.filter.call(args,
element => typeof element === 'number'
);
}
}
sumOnlyNumbers(1, 'Hello', 5, false); // => 6
filterNumbers() 내부의 sumOnlyNumbers() 인수에 액세스하려면 임시 변수 인수를 작성해야 합니다. filterNumbers()는 외부 인수를 덮어 쓰는 자체 인수 객체를 정의하기 때문에 발생합니다.
접근 방식은 효과가 있지만 너무 장황 합니다. const args = 인수를 생략 할 수 있으며 rest 매개 변수를 사용하여 Array.prototype.filter.call(args)을 args.filter()로 변환 할 수 있습니다. 이 부분을 최적화 합시다.
나머지 연산자는 이것을 우아하게 해결합니다. 함수 선언에서 나머지 매개 변수 ... args를 정의 할 수 있습니다.
function sumOnlyNumbers(...args) {
const numbers = filterNumbers();
return numbers.reduce((sum, element) => sum + element);
function filterNumbers() {
return args.filter(element => typeof element === 'number');
}
}
sumOnlyNumbers(1, 'Hello', 5, false); // => 6
함수 선언 함수 sumOnlyNumbers (... args)는 args가 배열에서 호출 인수를 수신함을 나타냅니다. 이름 충돌이 해결되었으므로 filterNumbers() 내에서 인수를 사용할 수 있습니다.
또한 배열과 유사한 객체를 잊어 버리십시오. args는 배열이므로 좋은 보너스입니다. 결과적으로 filterNumbers()는 Array.prototype.filter.call()을 제거하고 args.filter()를 직접 필터 메소드 호출로 만들 수 있습니다.
나머지 매개 변수는 함수 매개 변수 목록에서 마지막 매개 변수 여야 합니다.
2.2 선택적 휴식 파라미터
나머지 매개 변수에 모든 값을 포함하지 않아야 하는 경우 처음에 쉼표로 구분 된 매개 변수로 정의 할 수 있습니다. 명시 적으로 정의 된 매개 변수는 나머지 매개 변수에 포함되지 않습니다.
예를 보자.
function filter(type, ...items) {
return items.filter(item => typeof item === type);
}
filter('boolean', true, 0, false); // => [true, false]
filter('number', false, 4, 'Welcome', 7); // => [4, 7]
arguments 객체에는 이 선택적 속성이 없으며 항상 모든 값을 포함합니다.
2.3 화살표 기능 사례
화살표 함수는 본문에서 인수 객체를 정의하지 않지만 둘러싸는 범위에서 인수 객체에 액세스합니다. 모든 인수를 얻으려면 rest 매개 변수를 사용하십시오. 예제로 시도해 봅시다.
(function() {
let outerArguments = arguments;
const concat = (...items) => {
console.log(arguments === outerArguments); // => true
return items.reduce((result, item) => result + item, '');
};
concat(1, 5, 'nine'); // => '15nine'
})();
items rest 매개 변수는 배열의 모든 함수 호출 인수를 포함합니다. 또한 arguments 객체는 포함 범위에서 가져와 outerArguments 변수와 같으므로 중요한 의미가 없습니다.
3. 개선 된 함수 호출
기사 소개에서 두 번째 문제는 배열에서 호출 인수를 채우는 더 좋은 방법을 묻습니다.
ES5는 이를 해결하기 위해 함수 객체에 .apply() 메소드를 제공합니다. 불행히도 이 기술에는 3 가지 문제가 있습니다.
- 함수 호출의 컨텍스트를 수동으로 표시해야 합니다.
- 생성자 호출에 사용할 수 없습니다
- 더 짧은 용액이 더 바람직하다
.apply() 사용법의 예를 보자.
const countries = ['Moldova', 'Ukraine'];
const otherCountries = ['USA', 'Japan'];
countries.push.apply(countries, otherCountries);
console.log(countries); // => ['Moldova', 'Ukraine', 'USA', 'Japan']
언급했듯이 컨텍스트 국가를 두 번째로 .apply ()에 표시하는 것은 무의미합니다. 속성 접근 자 countries.push는 개체에 대한 메서드 호출을 결정하기에 충분합니다. 그리고 전체 호출은 장황하게 보입니다.
스프레드 연산자는 함수 호출 인수를 배열의 값으로 채 웁니다 (또는 반복 가능한 개체의 값은 5를 참조하십시오).
스프레드 연산자를 사용하여 위 샘플을 개선해 보겠습니다.
const countries = ['Moldova', 'Ukraine'];
const otherCountries = ['USA', 'Japan'];
countries.push(...otherCountries);
console.log(countries); // => ['Moldova', 'Ukraine', 'USA', 'Japan']
보시다시피, 스프레드 연산자는 깨끗하고 간단한 솔루션입니다. 유일한 추가 문자는 3 개의 점 (...)입니다.
spread 연산자는 배열에서 생성자 호출 인수를 구성하며 .apply()를 사용할 때는 직접 사용할 수 없습니다. 예를 보자.
class King {
constructor(name, country) {
this.name = name;
this.country = country;
}
getDescription() {
return `${this.name} leads ${this.country}`;
}
}
const details = ['Alexander the Great', 'Greece'];
const Alexander = new King(...details);
Alexander.getDescription(); // => 'Alexander the Great leads Greece'
또한 동일한 호출에서 여러 스프레드 연산자와 일반 인수를 결합 할 수 있습니다. 다음 예제는 기존 요소를 배열에서 제거한 다음 다른 배열과 요소를 추가합니다.
const numbers = [1, 2];
const evenNumbers = [4, 8];
const zero = 0;
numbers.splice(0, 2, ...evenNumbers, zero);
console.log(numbers); // => [4, 8, 0]
4. 배열 조작 개선
4.1 배열 구성
배열 리터럴 [item1, item2, .., itemN]은 초기 배열 요소 열거 이외의 기능을 제공하지 않습니다.
스프레드 연산자는 다른 배열 (또는 다른 반복 가능 항목)을 초기화 된 인스턴스에 즉시 삽입하여 배열 리터럴을 향상시킵니다. 이 개선으로 아래 설명 된 일반적인 작업을 보다 쉽게 수행 할 수 있습니다.
다른 배열의 초기 요소로 배열을 만듭니다.
const initial = [0, 1];
const numbers1 = [...initial, 5, 7];
console.log(numbers1); // => [0, 1, 5, 7]
const numbers2 = [4, 8, ...initial];
console.log(numbers2); // => [4, 8, 0, 1]
number1 및 number2 배열은 배열 리터럴을 사용하여 초기에 항목으로 초기화되는 동안 생성됩니다.
2 개 이상의 배열을 연결합니다.
const odds = [1, 5, 7];
const evens = [4, 6, 8];
const all = [...odds, ...evens];
console.log(all); // => [1, 5, 7, 4, 6, 8]
모든 배열은 확률과 짝수 배열의 연결에서 생성됩니다.
배열 인스턴스를 복제하십시오.
const words = ['Hi', 'Hello', 'Good day'];
const otherWords = [...words];
console.log(otherWords); // => ['Hi', 'Hello', 'Good day']
console.log(otherWords === words); // => false
otherWords는 단어 배열의 복제 버전입니다. 복제는 배열 자체에서만 발생하지만 포함 된 요소에서는 발생하지 않습니다 (즉, 딥 클론이 아님).
4.2 배열 구조
ECMAScript 6에서 사용 가능한 구조 지정 할당은 배열 및 객체에서 데이터를 추출하는 강력한 표현입니다.
구조 해제의 일부로 나머지 연산자는 배열의 일부를 추출합니다. 추출 결과는 항상 배열입니다.
구문 측면에서 나머지 연산자는 구조 지정 할당에서 마지막 연산자 여야합니다. [extractedItem1, ... restArray] = destructuredArray.
몇 가지 응용 프로그램을 살펴 보겠습니다.
const seasons = ['winter', 'spring', 'summer', 'autumn'];
const head, restArray;
[head, ...restArray] = seasons;
console.log(head); // => 'winter'
console.log(restArray); // => ['spring', 'summer', 'autumn']
[head, ... restArray]는 첫 번째 항목 'winter'를 head 변수로 추출하고 나머지 요소는 restArray로 추출합니다.
5. 연산자 및 반복 프로토콜 확산
스프레드 연산자는 반복 프로토콜을 사용하여 컬렉션의 요소를 탐색합니다. 모든 객체가 연산자가 데이터를 추출하는 방법을 정의 할 수 있기 때문에 스프레드 연산자가 더욱 가치가 있습니다.
반복 가능한 프로토콜을 준수 할 때 객체는 반복 가능합니다.
반복 가능한 프로토콜에는 개체에 특수 속성이 포함되어 있어야 합니다. 속성 이름은 Symbol.iterator 여야 하며 iterator 객체를 반환하는 함수로서의 값이어야 합니다.
interface Iterable {
[Symbol.iterator]() {
//...
return Iterator;
}
}
반복자 객체는 반복자 프로토콜을 준수해야 합니다.
다음에 속성을 제공해야 합니다.이 값은 속성이 완료된 객체 (반복 종료를 나타내는 부울)와 값 (반복 결과)을 갖는 객체를 반환하는 함수입니다.
interface Iterator {
next() {
//...
return {
value: <value>,
done: <boolean>
};
};
}
구두 설명을 통해 반복 프로토콜을 이해하기는 어렵지만 그 배후의 코드는 매우 간단합니다.
스프레드 연산자가 데이터를 추출하려면 오브젝트 또는 기본 요소가 반복 가능해야 합니다.
문자열, 배열, 형식화 된 배열, 집합 및 맵과 같은 많은 기본 형식과 객체를 반복 할 수 있습니다. 기본적으로 스프레드 연산자와 함께 작동합니다.
예를 들어 문자열이 반복 프로토콜을 따르는 방법을 살펴 보겠습니다.
const str = 'hi';
const iterator = str[Symbol.iterator]();
iterator.toString(); // => '[object String Iterator]'
iterator.next(); // => { value: 'h', done: false }
iterator.next(); // => { value: 'i', done: false }
iterator.next(); // => { value: undefined, done: true }
[...str]; // => ['h', 'i']
객체의 커스텀 반복 구현을 사용할 수 있는 스프레드 연산자를 좋아합니다. 스프레드 연산자가 객체를 소비하는 방법 (효과적인 코딩 기술)을 제어 할 수 있습니다.
다음 샘플에서는 배열과 유사한 객체를 반복 프로토콜에 맞게 만든 다음 스프레드 연산자를 사용하여 배열로 변환합니다.
function iterator() {
let index = 0;
return {
next: () => ({ // Conform to Iterator protocol
done : index >= this.length,
value: this[index++]
})
};
}
const arrayLike = {
0: 'Cat',
1: 'Bird',
length: 2
};
// Conform to Iterable Protocol
arrayLike[Symbol.iterator] = iterator;
const array = [...arrayLike];
console.log(array); // => ['Cat', 'Bird']
arrayLike [Symbol.iterator]는 iterator () 반복 함수를 포함하는 객체에 속성을 생성하여 객체가 반복 가능한 프로토콜을 준수하도록 합니다.
iterator()는 제어 오브젝트 {done : <boolean>, value : <item>}을 리턴하는 함수로 next 특성을 가진 오브젝트 (반복 프로토콜에 따름)를 리턴 합니다.
arrayLike는 이제 반복 가능하므로 spread 연산자를 사용하여 요소를 배열로 추출합니다 : [... arrayLike].
6. 피날레
3 개의 점 연산자는 JavaScript에 많은 훌륭한 기능을 추가합니다.
rest 매개 변수를 사용하면 인수를 훨씬 쉽게 수집 할 수 있습니다. 하드 코딩 된 배열과 같은 객체 인수를 대체하는 것이 합리적입니다. 상황이 나머지 매개 변수와 인수 중에서 선택할 수 있으면 첫 번째 매개 변수를 사용하십시오.
.apply() 메소드는 장황한 구문에는 편리하지 않습니다. 확산 연산자는 호출 인수를 배열에서 가져와야 할 때 좋은 대안입니다.
스프레드 연산자는 배열 리터럴을 향상 시킵니다. 어레이를 초기화, 연결 및 복제하는 것이 훨씬 간단합니다.
구조적 할당을 사용하여 배열의 일부를 추출 할 수 있습니다. 반복 프로토콜과 함께 스프레드 연산자를 보다 구성 가능한 방식으로 사용할 수 있습니다.
앞으로 스프레드 연산자가 코드에 더 자주 나타나기를 바랍니다.
- 이전글JavaScript의 객체 레스트 / 스프레드 속성에 대한 쉬운 안내서 19.08.22
- 다음글JavaScript Destructuring의 흥미로운 사용법 19.08.22