[Day 22] 퀴즈(array to object) + 값 더 알아보기 + 참조 + 불변성 + 래퍼 객체 + 함수 더 알아보기
퀴즈(array to object)
-
아래와 같이
cart
라는 배열 안에 items와 price를 객체 배열로 변형하고 싶다.const items = ['toy', 'bread'] const prices = [10000, 3000] const cart = ______ // cart: [ {name: 'toy', price: 10000}, {name: 'bread', price: 3000} ] console.log(cart) // 답 const cart = items.map((item, index) => ({name: item, price: prices[index]}))
-
9명의 개발자들로부터 좋아하는 프레임워크가 무엇인지를 조사해본 결과를 배열
preferredFrameworks
에 담았다. 각 프레임워크 별 득표수를 알고 싶다. (reduce 메소드 활용)const preferredFrameworks = ['vue', 'react', 'react', 'react', 'angular', 'react', 'angular', 'vue', 'vanilaJS'] const result = // 여기에 들어갈 적절한 코드를 만들어 주세요. console.log(result) // {vue: 2, react: 4, angular: 2, vanilaJS: 1} // 답 const result = preferredFrameworks.reduce((acc, item, index, array) => { acc[array[index]] = ++acc[array[index]] || 1 return acc }, {}) // 또는 const result = preferredFrameworks.reduce((acc, item) => { if(!(acc[item])){ acc[item] = 1 } else { acc[item] += 1 } return acc; }, {})
값 더 알아보기
-
let, const 변수와 블록 스코프
let
과const
는 같은 이름을 갖는 변수의 재선언을 허용하지 않는다.- 변수가 선언되기 전에 참조하려고 하면 에러가 난다.(hoisting X)
let
과const
는 블록 스코프를 갖는다.- 함수의 매개변수나
var
변수는 함수 스코프를 갖는다.
- 함수의 매개변수나
if
,for
,while
,function
등의 구문을 사용하면 블록이 형성되어, 그 안에서let
또는const
를 통해 선언된 변수는 외부에서 접근할 수 없다.- 같은 이름의 let 변수를 재선언하기 위해 특별한 기능이 없는
{}
블록을 만들 수 있다.
const
let
var
스코프 블록 스코프 블록 스코프 함수 스코프 재대입 X O O 재선언 X X O 호이스팅 X X O 사용 권장 1순위 2순위 3순위 -
전역 변수 (Global Variable)
-
전역 스코프는 스코프 체인의 가장 바깥쪽에 있는 스코프를 말한다.
-
전역 스코프에서 선언된 변수를
전역 변수(global variable)
라고 한다. -
변수를 명시적으로 전역 스코프에서 선언하지 않더라도, 한 번도 선언되지 않은 이름으로 안쪽 스코프에서
let
,const
,var
를 붙여주지 않고 변수를 선언하면 전역 스코프에 변수가 만들어진다.function func() { variable = 1; // `variable`이라는 변수가 선언된 적 없으므로, 전역 변수가 됩니다. } func(); console.log(variable); // 1
-
전역 변수는 코드의 어떤 부분에서든 아무런 제한 없이 접근하고 조작할 수 있다.
-
전역 변수에 의존해서 프로그래밍을 하는 것은 굉장히 금기시되는 일이다.
- 전역 변수에는 아무런 제한 없이 접근할 수 있으므로, 프로그램의 크기가 커짐에 따라 변수의 값이 어디서 어떻게 변경될지 예측하기 힘들다.
- 전역 변수를 통해 프로그램의 너무 많은 부분이 결합(coupling) 된다. (A를 고쳤을 때 전혀 상관 없는 B의 코드가 오동작 할 수 있다.)
- 코드가 전혀 다른 곳에 위치한 부분에 의존하게 되므로, 코드를 이해하기 어렵다.
-
-
전역 객체 (Global Object)
- 전역 변수(var)가 선언되면, 이 변수는 전역 객체의 속성이 되어 전역 객체를 통해서도 접근이 가능해진다.
let
변수는 전역 객체의 속성이 되지 않는다.
구동 환경 전역 객체 이름 웹 브라우저 window
웹 워커 self
Node.js global
- 전역 객체에는 구동 환경에서 유용하게 쓸 수 있는 속성과 함수가 미리 적재되어 있다.
- 브라우저의
fetch
함수 - Node.js의
require
함수
- 브라우저의
- 전역 변수(var)가 선언되면, 이 변수는 전역 객체의 속성이 되어 전역 객체를 통해서도 접근이 가능해진다.
참조 (Reference)
-
Javascript의 7가지 타입
- Boolean
- Null
- Undefined
- Number
- String
- Symbol
- Object
-
타입들 중 객체(Object)를 제외하고는 모두 원시 타입(primitive type)으로 불린다. 객체는 참조 타입(reference type)으로 불린다.
-
참조(reference)란, 객체가 컴퓨터 메모리 상에서 어디에 저장되었는지를 가리키는 값을 말한다.
-
우리가 객체라고 생각하고 다뤄왔던 값은, 실제로는 객체에 대한 참조
-
객체의 속성에 접근하면 Javascript 엔진은 참조를 통해 메모리(heap)에 저장되어 있는 객체에 접근해서 해당 객체의 속성을 읽는다. 이를 역참조(dereference)라고 한다.
-
함수 호출
- 함수 호출 시에 인수가 복사되어 매개변수로 넘어갈 때, 인수로 객체를 넘긴다면 참조가 복사되어 매개변수로 넘어간다. 이를 이용해 원본 객체의 내용을 변경할 수 있다.
- 원시타입을 인수로 넘길 때에는 값이 복사되기 때문에 원본을 변경할 수 없지만, 참조 타입을 인수로 넘길 때에는 참조가 복사되기 때문에 원본을 변경할 수 있다.
-
객체의 같음 (Equality)
-
객체에 대해서 비교 연산자(
===
) 를 사용할 경우 객체의 값이 아닌 참조를 비교한다.- 두 참조가 같은 객체를 가리키고 있을 경우에만 등호 연산자가 true를 반환한다.
-
객체의 값을 통한 비교를 하고 싶다면, 직접 함수나 메소드를 만들어서 사용하는 방법이 있다.
// 계정 관리 시스템에서는, 사용자의 '아이디'가 같다면 같은 사용자라고 볼 수 있습니다. function User(id) { this.id = id; } User.prototype.isEqual = function(other) { return this.id === other.id; } const john1 = new User('john'); const john2 = new User('john'); john1 === john2; // false john1.isEqual(john2); // true
-
불변성 (Immutability)
- 원시 타입의 값 자체의 내용을 변경할 수 있는 방법은 없다.
- Javascript의 원시값은 불변(immutable) 이다.
- 변수에 저장된 원시 타입의 값을 바꾸려면, 오직 변수에 다른 값을 대입하는 방법밖에 없다.
- 문자열을 변형하는 메소드는 모두 기존 문자열의 내용을 바꾸는 것이 아니라 새 문자열을 반환한다.
- 객체는 가변(mutable) 이다.
- 객체의 가변성 문제를 해결하기 위한 해결책
Object.freeze
를 사용해 객체를 얼려서 속성의 추가/변경/삭제를 막는다.- 다만, 객체 안에 있는 객체까지는 얼리지 못하기 때문에 중첩된 객체에서는 사용하기 까다롭다.
- Immutable.js 또는 Immer.js 같은 라이브러리를 사용해서 객체를 불변인 것 처럼 다룬다.
- 내용이 달라지면 참조 역시 달라지게 된다.
- 객체의 가변성 문제를 해결하기 위한 해결책
const
와immutable
구분- immutable(불변성)은 ‘값 자체가 변하지 않는다’ 는 것이다.
const
는 재대입이 불가능하다는 것이다.const
로 선언된 변수에 객체를 대입하면, 새로운 값을 대입할 수는 없지만 객체의 내용은 변경할 수 있다.- 즉, 재대입이 불가능할지라도 가변일 수 있다.
래퍼 객체 (Wrapper Object)
- 원시 타입의 값은 객체가 아님에도 불구하고 점 표기법을 써서 메소드를 호출하거나 속성을 읽어올 수 있는데, 이는 래퍼 객체 기능 덕분에 가능한 것이다.
- 원시 타입의 값에 대해 속성을 읽으려고 시도하면, 그 값은 그 순간에만 객체로 변환되어 마치 객체인 것처럼 동작한다.
- 래퍼 객체를 생성시키기 위해 사용되는 생성자들
- String
- Number
- Boolean
- Symbol
함수 더 알아보기
-
객체로서의 함수
- 함수는
Function
생성자로부터 생성되는 객체이다. 다만, 다른 객체들과는 다르게 호출할 수 있다(callable)는 특징이 있다. - 함수 객체의 두 가지 속성
length
- 함수의 매개변수의 개수를 반환name
- 함수의 이름을 반환
- 함수는
-
주인 없는 this
- 생성자나 메소드가 아닌 함수에서
this
키워드를 사용한다고 해서 에러가 나지는 않는다. - 이 때,
this
키워드는 전역객체를 가리킨다.- 전역 객체에 접근해서 조작하는 것은 매우 안좋은 일이다.
- 생성자나 메소드가 아닌 함수에서
-
엄격 모드 (Strict Mode)
- 주인 없는 this와 같은 실수들을 방지하기 위한 모드
- 엄격 모드에서는 구버전 자바스크립트의 특징으로 인해 실수하기 쉬운 몇 가지 문법에 대해 제약사항을 추가한다.
- 엄격 모드를 활성화하려면
.js
파일의 가장 위에'use strict';
- 파일 전체 엄격 모드- 함수의 가장 위에
'use strict';
- 해당 함수만 엄격 모드
- ES2015 모듈을 이용해 작성된 코드는 항상 엄격 모드로 동작한다.
- 최근의 클라이언트 측 Javascript 코드는 대부분 Babel과 Typescript 같은 트랜스파일러를 통해 ES2015 모듈 방식으로 작성된다.
-
this 바꿔치기
-
this
가 우리가 원하는 값을 가리키기 만들기 위해서는 함수 객체의bind
,call
,apply
메소드를 사용하면 된다. -
bind
- 함수 객체의
bind
메소드를 호출하면 메소드의 인수로 넘겨준 값이this
가 되는 새로운 함수를 반환한다.
function printGrade(grade) { console.log(`${this.name} 님의 점수는 ${grade}점입니다.`); } const student = {name: 'Mary'}; const printGradeForMary = printGrade.bind(student); printGradeForMary(100); // Mary 님의 점수는 100점입니다.
- 함수 객체의
-
call
,apply
- 새로운 함수를 만들지 않고도 임시적으로
this
를 바꿔버릴 수 있다. call
과apply
는 인수를 넘겨주는 형식에 차이가 있을 뿐, 나머지 기능은 동일하다.
function printGrade(grade) { console.log(`${this.name} 님의 점수는 ${grade}점입니다.`); } const student = {name: 'Mary'}; printGrade.call(student, 100); // Mary 님의 점수는 100점입니다. printGrade.apply(student, [100]); // Mary 님의 점수는 100점입니다.
- 새로운 함수를 만들지 않고도 임시적으로
-
-
argument와 나머지 매개변수 (Rest Parameters)
arguments
는 유사 배열 객체(array-like object)이자 반복 가능한 객체(iterable object)로, 함수에 주어진 인수가 순서대로 저장되기 때문에 인덱스를 가지고 인수를 읽어오거나for...of
를 통해 순회할 수 있다.- 나머지 매개변수(rest parameters) 문법
function sum(...ns) { let result = 0; for (let item of ns) { result += item; } return result; } sum(1, 2, 3, 4); // 10
- 매개변수 앞에
...
을 붙여주면 해당 매개변수에 모든 인수가 저장된다.arguments
와는 달리 나머지 매개변수는 실제 배열이기 때문에, 배열의 메소드를 활용할 수 있다.
function sum(...ns) { // `for...of` 루프 대신에 `reduce` 메소드를 사용해서 합계를 구할 수 있습니다. return ns.reduce((acc, item) => acc + item, 0); } sum(1, 2, 3, 4); // 10
- 단,
...
문법은 마지막 매개변수에만 사용할 수 있다. (마지막이 아닌 매개변수에 사용하면 에러 발생)
function printGrades(name, ...grades) { console.log(`${name} 학생의 점수는 ${grades.join(', ')} 입니다.`); } printGrades('Mary', 96, 78, 68); // Mary 학생의 점수는 96, 78, 68 입니다.
- 자바스크립트에서는 함수의 매개변수의 개수와 인수의 개수가 달라도 에러가 나지 않는다.
- 필요없는 매개변수를 생략할 수 있다는 장점이 있다.
-
화살표 함수 (Arrow Function)
-
ES2015 에서 도입된 새로운 유형의 함수
-
(매개변수 목록) => {함수 내용}
-
화살표 함수의 특별한 성질
-
화살표 함수는 생성자로 사용될 수 없다. 따라서
prototype
속성을 갖지 않는다. 또한 스스로의new.target
을 가지지 않는다. -
화살표 함수는 스스로의
this
,arguments
,super
를 가지지 않는다.-
함수 내부에서
this
를 사용할 수 없다는 말은 아니다. -
화살표 함수 내부에서
this
를 사용하면, 그this
는 함수가 정의된 스코프에 존재하는this
를 가리킨다. -
스스로의
this
를 갖지 않기 때문에, 화살표 함수에 대해bind
,call
,apply
메소드를 호출해도 아무런 효과가 없다.function Person1(name) { this.name = name; this.getName = () => { // 여기에서 사용된 `this`는 '함수가 정의된 스코프', // 즉 'Person 함수 스코프'에 존재하는 `this`를 가리키게 됩니다. return this.name; } } const mary = new Person1('mary'); console.log( 'mary.getName():', mary.getName() ) // `this`를 바꿔보려고 해도, 아무런 효과가 없습니다. console.log( "mary.getName.call({name: 'john'}):", mary.getName.call({name: 'john'}) );
-
-
화살표 함수 내부에서
yield
키워드를 사용할 수 없다. 즉, 제너레이터로 사용될 수 없다. -
화살표 함수 내부에서
this
를 사용하면 함수가 정의된 스코프에 있는this
를 가리킨다.-
화살표 함수 내부의
this
는 화살표 함수가 정의된 문맥에 의해 결정된다. -
function
구문으로 정의된 함수에서 쓰이는this
는 어떻게 호출되는지에 의해 결정된다.function getName() { return this.name; } const john = { name: 'john', getName }; const bob = { name: 'bob', getName } // .getName() 앞에 있는 john이 함수 내부의 this로 사용됩니다. console.log('john.getName():', john.getName()) // .getName() 앞에 있는 bob이 함수 내부의 this로 사용됩니다. console.log('bob.getName():', bob.getName())
-
-
객체의 속성 값에 메소드를 직접 정의할 때에는 화살표 함수를 사용해서는 안 된다.
this
가 전역 객체를 가리키게 된다.
-
화살표 함수는 함수를 다른 함수의 인수로 넘겨야 할 때 사용하면 편리하다.
function Person2(name) { this.name = name; this.getName = () => { return this.name; } this.getName2 = function () { return this.name; } } const kate = new Person2('kate'); // 함수를 인수로 받는 함수 function printResult(func) { // 아래 func는 '메소드로서 호출'되고 있지 않습니다. // 따라서 function 키워드를 통해 생성된 함수일 경우 문제가 생길 수 있습니다. console.log(func()); } // 화살표 함수로 정의된 메소드를 다른 함수의 인수로 넘겨도 아무런 문제가 없습니다! console.log('printResult(kate.getName)') printResult(kate.getName); // function 키워드 함수의 경우 this에 문제가 생깁니다. '메소드로서 호출'되고 있지 않기 때문입니다. console.log('printResult(kate.getName2)'); printResult(kate.getName2); // 위와 같은 경우 bind 메소드를 사용하면 됩니다만, 좋아보이지는 않습니다. console.log('printResult(kate.getName2.bind(kate))') printResult(kate.getName2.bind(kate));
-
-
function
구문으로 생성되는 함수는 단순한 함수 이외에 생성자나 제너레이터 등의 여러 기능까지 구현할 수 있다. -
반면, 화살표 함수는 오직 함수 혹은 메소드로 사용되도록 만들어졌다.
- 화살표 함수의
this
는 어떻게 호출되더라도 변하지 않기 때문에 함수를 값으로 다루어야 하는 경우(함수를 다른 함수의 인수로 넘겨야 하는 경우) 에는 화살표 함수가 일반 함수보다 편리한 경우가 많다.
- 화살표 함수의
-
-
매개변수의 기본값 (Default Parameter)
- 매개변수가 있는 함수에 아무런 인수도 주지 않으면 매개변수에
undefined
가 대입된다. - 이를 이용해 인수가 주어지지 않았을 때, 대신 미리 설정된 값을 사용하는 함수를 만들 수 있다.
function hello(name) { // 매개변수는 `var` 변수와 같은 성질을 갖기 때문에, 재대입을 할 수 있습니다. if (name === undefined) { name = 'Mary'; } console.log(`Hello, ${name}!`); } hello('John'); // Hello, John! hello(); // Hello, Mary! hello(undefined); // Hello, Mary!
- 위와 같은 기법이 많이 사용되어왔기 때문에, ES2015에서는 이를 편리하게 해주는 문법이 도입되었다. 이를 매개변수의 기본값(default parameter) 이라고 한다.
// 매개변수의 기본값 문법 // 'Mary'가 `name` 매개변수의 기본값이 됩니다. function hello(name = 'Mary') { // 코드가 훨신 더 깔끔해졌습니다! console.log(`Hello, ${name}!`); } hello('John'); // Hello, John! hello(); // Hello, Mary! hello(undefined); // Hello, Mary!
- 매개변수가 있는 함수에 아무런 인수도 주지 않으면 매개변수에
댓글남기기