주로 배열을 풀어서 인자로 전달하거나, 배열을 풀어서 각각의 요소로 넣을 때 사용
function sum(x, y, z) {
return x + y + z;
}
const numbers = [1, 2, 3];
sum(...numbers)
// 문: 어떤 값을 리턴하나요?
// 답: numbers를 sum의 인자값으로 던져서 sum연산을 한 결과를 리턴 (6)
파라미터를 배열의 형태로 받아서 사용할 수 있음. 파라미터 개수가 가변적일 때 유용함
function sum(...theArgs) {
return theArgs.reduce((previous, current) => {
return previous + current;
});
}
sum(1,2,3) // 질문: 어떤 값을 리턴하나요? => 6
sum(1,2,3,4) // 질문: 어떤 값을 리턴하나요? => 10
Spread 문법은 배열에서 강력한 힘을 발휘한다.
let parts = ['shoulders', 'knees'];
let lyrics = ['head', ...parts, 'and', 'toes'];
let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1 = [...arr1, ...arr2];
// spread 문법은 기존 배열을 변경하지 않으므로(immutable)
// arr1의 값을 바꾸려면 새롭게 할당해야 함.
let arr = [1, 2, 3];
let arr2 = [...arr]; // arr.slice() 와 유사
// spread 문법은 기존 배열을 변경하지 않으므로(immutable),
// arr2를 수정해도 arr이 바뀌지 않음
let obj1 = { foo: 'bar', x: 42 };
let obj2 = { foo: 'baz', y: 13 };
let clonedObj = { ...obj1 };
let mergedObj = { ...obj1, ...obj2 };
function numbers(a, b, ...manyMoreArgs) {
console.log("a", a);
console.log("b", b);
console.log("manyMoreArgs", manyMoreArgs);
}
numbers("one", "two", "three", "four", "five", "six");
구조 분해 할당은 Spread 문법을 이용하여 값을 해체한 후, 개별 값을 변수에 새로 할당하는 과정을 말한다
[a, b, ...rest] = [10, 20, 30, 40, 50]
// a: 10 , b: 20 , rest: 30, 40, 50
{a, b, ...rest} = {a:10, b:20, c:30, d:40}
객체 지향 프로그래밍은 하나의 모델이 되는 청사진을 만들고, 그것을 바탕으로 한 객체를 만드는 프로그래밍 패턴이다.
이 개념에서 '청사진'을 클래스라고 하고, 그것을 바탕으로 한 객체를 인스턴스라고 부르면 이해가 쉽다.
차를 만드는 청사진이 있고, 이 청사진을 기반으로 여러 종류의 차를 만들었을 때
청사진(class)는 function Car(clolr) {}
이 되고,
객체(instance)는
let bluecar = new Car('blue')
let cyancar = new Car('cyan')
let redcar = new Car('red')
이러한 형태로 만들 수 있다.
그냥 일반적인 함수를 정의하듯 만들면 되는데, 함수를 이용하는 방법이 조금 다르다.
새로운 인스턴스를 만드는 방법
인스턴스를 만들 때에는 new
키워드를 사용한다.
즉시 생성자 함수가 실행되며, 변수에 인스턴스가 할당된다.
각각의 인스턴스는 클래스의 고유한 속성과 메소드를 갖게 된다.
클래스를 그냥 함수로 정의하면 일반적인 다른 함수와 구분하기 어려울 수 있는데,
암묵적인 규칙에 의해 클래스는 대문자로 시작하는 일반명사로 만들고,
일반적인 함수는 소문자로 시작하는 동사로 만드는 것이 보통 통용된다.
그리고 ES5까지는 함수로 정의할 수 있었는데, ES6부터는 class
키워드를 이용해서 정의할 수도 있게 되었다.
ES5
function Car(brand, name, color) { // 인스턴스가 만들어질 때 실행되는 코드 }
ES6
class Car { constructor(brand, name, color) { // 인스턴스가 만들어질 때 실행되는 코드 } }
최근에는 ES6의 방법을 주로 사용하고 있다.
여기서 보이는 함수는 객체 지향 프로그래밍에서 생성자 함수(인스턴스가 만들어질 때 실행되는 코드)라고 부른다.
참고로, 생성자 함수는 return값을 만들지 않는다
차를 기준으로 프로그래밍 모델을 만든다고 가정했을 때를 비교해 보면 이해가 쉽다.
refuel()
, setSpeed()
, drive()
메소드는 객체에 딸린 함수라고 이해하면 쉽다.
객체 지향 프로그래밍에서는 빠지지 않고 등장하는 것이 바로 this
인데,
이것은 인스턴스 객체를 의미한다.
parameter
로 넘어온 브랜드, 이름, 색상 등 속성은 인스턴스 생성 시 지정하는 값이며, this에 할당한다는 것은 만들어진 인스턴스에 부여하겠다는 의미이다.
//ES6
class Car {
constructor(brand, name, color) {
this.brand = brand;
this.name = name;
this.color = color;
}
}
ES5
function Car(brand, name, color) { // 생략 } Car.prototype.refuel = function() { // 연료 공급을 구현하는 코드 } Car.prototype.drive = function() { // 운전을 구현하는 코드 }
ES6
class Car { constructor(brand, name, color) { // 생략 } refuel() { // 연료 공급을 구현하는 코드 } drive() { // 운전을 구현하는 코드 }
ES5에서는 prototype
이라는 키워드가 등장하지만, 문법의 차이로 간주하고 우선 넘어가자.
let avante = new Car('hyundai', 'avante', 'black');
avante.color; // 'black'
avante.drive(); // 아반떼가 운전을 시작합니다
let mini = new Car('bmw', 'mini', 'white');
mini.brand; // 'bmw'
mini.refuel(); // 미니에 연료를 공급합니다
설정한 속성과 메소드를 위와 같이 인스턴스에서 사용할 수 있다.
생성자 함수 정도만 객체 지향 프로그래밍에서 보편적인 개념이며,
prototype
이나 this
는 언젠가는 알아야 할 것들이지만 지금은 코드상에서 어떻게 쓰이는 지만 알고 넘어가자.
모델의 청사진을 만들 때 사용하는 원형 객체(original form)
인스턴스가 초기화될 때 실행하는 생성자 함수
함수가 실행될 때 해당 scope마다 생성되는 고유한 실행 context
new
키워드로 인스턴스를 생성했을 때는, 해당 인스턴스가 바로 this
의 값이 된다.
this
는 함수 실행 시 호출 방법에 의해 결정되는 특별한 객체이다.
함수 실행시 결정되므로, 실행되는 맥락에 따라 this
는 다르게 결정된다.
foo()
obj.foo()
new Foo()
.call
또는 .apply
호출foo.call()
foo.apply()
1. Function 호출
this를 사용하지 않는 것을 권장한다.
사용할 이유가 없기 때문.
2. Method 호출
this는 부모 객체(실행 시점에 온점 왼쪽에 있는 객체)를 지칭하게 된다.
하나의 객체에 값과 연관된 메소드를 묶어서 사용할 때 주로 사용한다.
3. new 키워드를 이용한 생성자 호출
this는 새롭게 생성된 인스턴스 객체를 지칭하게 된다.
객체 지향 프로그래밍에서 주로 사용한다.
4.
.call
또는.apply
호출
this는 첫번째 인자로 전달된 객체를 지칭하게 된다.
this값을 특정할 때 사용하며, 특히.apply
의 경우 배열의 엘리먼트를 풀어서 인자로 넘기고자 할 때 유용하다.
화살표 함수는 그 어떤 경우에도 this
를 결정하지 않는다.
화살표 함수를 어떻게 설정해도 this
값은 전혀 영향을 끼치지 않는다.
.call
과 .apply
호출은 명시적으로 this
를 지정하고 싶을 때 사용한다.
첫 번째 인자가 항상 this값이 되고, 나머지 인자는 필요한 파라미터를 전달한다.
''.split.call('피, 땀, 눈물', ',')
// 이 상황에서는 '피, 땀, 눈물'이 this가 되고, 인자값으로 ','을 넘기게 된다.
// this를 앞으로 넘겨준다고 생각해보면 this.split.call(',')의 형태가 되고,
// 결론적으로
'피, 땀, 눈물'.split(',')
// 이와 완벽히 동일한 결과를 리턴한다.
.apply
는 거의 .call
과 유사하다.
근본적인 차이점은, .call
은 함수에 전달 될 인수 리스트를 받는 데 비해
.apply
는 인수들의 단일 배열을 받는다는 점이다.
const numbers = [5, 6, 2, 3, 7];
const max = Math.max.apply(null, numbers);
const min = Math.min.apply(null, numbers);
console.log(max); // 7
console.log(min); // 2
.bind
는 .call
과 유사하게 this
및 인자를 바인딩하지만,
당장 실행하는 것이 아닌 바인딩된 함수를 리턴하는 함수이다.
첫 번째 인자가 항상 this값이 되고, 나머지 인자는 필요한 파라미터를 전달(바인드된 함수의 인수에 제공)한다.
bind는 나중에 추가로 심도있게 공부할 필요가 있다.