본문 바로가기

0/javascript

Learning JavaScript 요약 정리 (1) (2장 ~ 6장)

반응형

2장 자바스크립트 개발 도구

  • 깃 : 프로젝트가 커져도 쉽게 관리할 수 있고 다른 개발자와 협력할 수 있게 돕는 버전 컨트롤 도구.
  • 노드 : 브라우저 밖에서 자바스크립트를 실행할 수 있게 하는 도구. 노드와 함꼐 설치되는 npm은 이 리스트의 다른 도구를 설치할 때 필요.
  • 걸프 : 반복적인 개발 작업을 자동화하는 빌드 도구. 그런트도 널리 쓰임. (현재는 웹팩.. 아니 parcel?)
  • 바벨 : ES6 코드를 ES5 코드로 변환하는 트랜스컴파일러.
  • ES린트 : 자주 하는 실수를 피하고 더 나은 프로그래머가 되도록 돕는 린트 프로그램.

새 프로젝트 시작시 필요한 것.

  • 프로젝트에 사용할 전용 디렉터리. 이 디렉터리를 프로젝트 루트라고 부른다.
  • 깃 저장소 (git init)
  • package.json 파일 (npm init)
  • 웹팩과 바벨의 로컬 패키지. npm install --save-dev webpack webpack-dev-server babel-core babel-loader  babel-preset-2015
  • .babelrc 파일 ({"presets" : ["es2015"]})
  • .eslintrc 파일. eslint --init 명령으로 만들고 원하는 대로 수정.
  • 노드 소스용 서브디렉터리 app
  • 브라우저 소스용 서브디렉터리 public

 

3장 리터럴과 변수, 상수, 데이터 타입

  • 자바스크립트에는 문자열, 숫자, 불리언, null, undefined, 심볼의 여섯 가지 원시 타입과 객체 타입이 있다.
  • 자바스크립트의 모든 숫자는 배정도 부동소수점 숫자(더블)이다.
  • 배열은 특수한 객체이며, 객체와 마찬가지로 매우 강력하고 유연한 데이터 타입이다.
  • 날짜, 맵, 셋, 정규표현식 등 자주 사용할 다른 데이터 타입들은 특수한 객체 타입이다.
...더보기

참조형과 원시형

원시 값은 불변이고, 원시 값을 복사/전달할 때는 값 자체를 복사/전달한다. 따라서 '원본'의 값이 바뀌더라도 '사본'의 값이 바뀌지는 않는다.

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

a === 2 // true

function change(a){
	a = 5;
}

a = 3;
change(a);
console.log(a); // 3

객체는 가변이고, 객체를 복사/전달할 때는 객체가 아니라 그 객체를 가리키고 있다는 사실(참조)을 복사/전달한다. 따라서 원본이 바뀌면 사본도 따라서 바뀐다. 이런 특징을 강조하고 싶을 때 객체를 참조 타입이라고 부르기도 한다.

let o = {a:1};
let p = o; // p는 o가 '가리키고 있는 것'을 가리킨다.
o.a = 2;
console.log(p); // {a:2}

// 주의할 점
let o = {a:1};
let p = o;	// p는 o가 '가리키고 있는 것'을 가리킨다.
	p === o // true
o = {a:2};  // 이제 o는 다른 것을 가리킨다.
	p === o // false
console.log(p); // {a:1}

객체를 가리키는 변수는 그 객체를 가리키고 있을 뿐, 객체 자체는 아니다. 따라서 변수와 객체는 결코 일치하지 않는다.

let q = {a:1};
q === {a:1} // false

참조를 전달하므로 함수 안에서 객체를 변경하면 함수 외부에서도 바뀐다.

function change_o(o) {
	o.a = 999;
}

let o = {a:1};
change_o(o);
console.log(o); // {a: 999}

 

4장 제어문

  • 제어문은 실제로 프로그램이 동작하게 하는 가장 중요한 열쇠이다. 변수와 상수에 중요한 정보가 아무리 많이 들어있다 해도 제어문을 써야 그 데이터에 따라 필요한 선택을 할 수 있다.
  • 순서도는 프로그램의 흐름을 시각적으로 표현하는 좋은 방법이고, 코드를 작성하기 전에 추상적인 순서도를 그려 보면 문제를 어떻게 해결잘지 결정하는 데 도움이 될 수 있다. 하지만 순서도는 간단하게 그리기가 어려우며, 충분히 훈련된 사람은 프로그램의 흐름을 코드만으로도 자연스럽게 표현할 수 있다.

5장 표현식과 연산자

표현식(expression)은 값으로 평가될 수 있는 문이다. 표현식이 아닌 문(statement)는 일종의 지시라고 생각할 수 있고, 표현식은 무언가를 요청하는 것이라고 생각할 수 있다.

표현식 (expression) 문 (statement)
치킨과 맥주를 먹고, 뼈를 가져오세요. 당신이 할 일은 치킨과 맥주를 먹는 일입니다.

차이를 아시겠나요? 표현식은 값을 반환하지만, 문은 그렇지 않다.

자바스크립트는 표현식이 결함된 것을 보면 쪼갤 수 있는 만큼 쪼개서 한 부분씩 실행한다.

let x, y;
y = x = 3*5; // 원래 문
y = x = 15; // 곱셈 표현식 평가
y = 15; // 첫 번째 할당을 평가. 이제 x=15, y=undefined

15;	// 두 번째 할당을 평가. y=15.
// 전체 문의 결과는 15이다.
// 이 값은 사용하지도 않았고 어딘가에 할당하지도 않았으니 그냥 버려진다.

typeof 연산자는 피연산자의 타입을 나타내는 문자열은 반환한다.

표현식 반환값 참고
typeof undefined "undefined"  
typeof null "object" 애석하지만 사실이라네요..
typeof {} "object"  
typeof true "boolean"  
typeof 1 "number"  
typeof "" "string"  
typeof Symbol() "symbol" ES6에서 새로 생겼다.
typeof function(){} "function"  

ES6에서 새로 도입한 해체 할당(destructuring assignment)는 매우 환영 받는 기능이다. 이 기능은 객체나 배열을 변수로 '해체'할 수 있다.

// 객체 선언
const obj = {b:2, c:3, d:4};

// 해체 할당
const {a, b, c} = obj;
a;	// undefined : obj에는 "a" 프로퍼티가 없다.
b;	// 2
c;	// 3
d;	// ReferenceError: "d"는 정의되지 않았다.
const arr = [1, 2, 3, 4, 5];

let [x, y, ...rest] = arr;
x;	// 1
y;	// 2
rest;	// [3, 4, 5]

 

6장 함수

함수는 하나의 단위로 실행되는 문의 집합이다. 

명확히 말해서, 함수 안의 o와 함수 바깥의 o는 서로 다른 개체이다. 하지만 그 둘은 같은 객체를 가리키고 있다. 할당을 이용해 차이점을 다시 알아보자.

function f(o){
    o.message = 'f에서 수정함.';
    o = {
    	message: "새로운 객체!"
    };
    console.log(`f 내부 : o.message="${o.message}" (할당후)`);
}

let o = {
    message: '초기값'
};

console.log(`f를 호출하기 전: o.message="${o.message}"`);
f(o);
console.log(`f를 호출한 다음: o.message="${o.message}"`);

// 출력 값
f를 호출하기 전: o.message="초기값"
f 내부 : o.message="새로운 객체!" (할당후)
f를 호출한 다음: o.message="f에서 수정함."

이 예제를 이해하는 핵심은 함수 내부의 매개변수 o와 함수 바깥의 변수 o가 다르다는 것이다. f를 호출하면 둘은 같은 객체를 가리키지만, f내부에서 o에 할당한 객체는 새로운, 전혀 다른 객체이다. 함수 바깥의 o는 여전히 원래 객체를 가리키고 있다.

* 컴퓨터 과학에서는 자바스크립트의 원시 값을 값 타입(value type)이라고 말한다. 원시 값을 전달할 때 값이 복사되기 때문이다. 객체는 참조 타입(reference type)이라 부른다. 객체를 전달할 때 두 변수는 같은 객체를 가리키기 때문이다.

객체의 프로퍼티인 함수를 메서드라고 불러서 일반적인 함수와 구별한다. 함수와 메서드 사이에는 다른 차이도 있지만 나중에 설명하겠다.

// ES6 이전
const o = {
    name : 'Beenzino',
    sing: function(){ return '아까워~'; }
};

// ES6
const o = {
    name : 'Beenzino',
    sing() { return '아까워~'; }
};

함수 바디 안에는 특별한 읽기 전용 값인 this가 있다. this는 일반적으로 객체 지향 프로그래밍 개념에 밀접한 연관이 있다. 

일반적으로 this는 객체의 프로퍼티인 함수에서 의미가 있다. 메서드를 호출하면 this는 호출한 메서드를 소유하는 객체가 된다.

const o = {
    name : 'Ash',
    speak() {return `My name is ${this.name}!`}
}

// this는 o에 묶입니다.
o.speak(); // "My name is Ash!"

this는 함수를 어떻게 선언했느냐가 아니라 어떻게 호출했느냐에 따라 달라진다는 것을 이해해야 한다. 즉, this가 o에 묶인 이유는 speak가 o의 프로퍼티여서가 아니라, o에서 speak를 호출했기 때문이다. 같은 함수를 변수에 할당하면 어떻게 되는지 보자.

const speak = o.peak;
speak === o.speak; // true; 두 변수는 같은 함수를 가리킨다.
speak(); // "My name is undefined!"

함수를 이렇게 호출하면 자바스크립트는 이 함수가 어디에 속하는지 알 수 없으므로 thi는 undefined에 묶인다.

* 앞의 예제에서 한 것처럼 this가 어디에 묶이는지 명확히 알 수 없도록 호출했을 때 this를 결정하는 방법은 매우 복잡하다. 스트릭트 모드인지 아닌지, 함수를 어디에서 호출했는지에 따라서도 다르다. 이런 상황은 피하는 것이 최선이다.

중첩된 함수 안에서 this를 사용하려다 보면 혼란스러울 때가 많다. 다음 예제를 보면 메서드 안에 보조 함수가 있다.

const o = {
    name : 'Joey',
    greetBackwards: function(){
    	function getReverseName(){
        	let nameBackwards = '';
            for(let i=this.name.length-1; i>=0; i--){
            	nameBackwards += this.name[i];
            }
            return nameBackwards;
        }
        return `${getReverseName()} si eman ym, olleH`;
    },
};

o.greetBackwards();

이 중첩된 함수 getReverseName은 의도한 대로 동작하지 않는다. o.greetBackwards()를 호출하는 시점에서 자바스크립트는 this를 의도한 대로 o에 연결하지만, greetBackwards 안에서 getReverseName을 호출하면 this는 o가 아닌 다른 것이 묶입니다. 이런 문제를 해결하기 위해 널리 사용하는 방법은 다른 변수에 this를 할당하는 것이다.

const o = {
    name : 'Joey',
    greetBackwards: function(){
    	const self = this;
    	function getReverseName(){
        	let nameBackwards = '';
            for(let i=self.name.length-1; i>=0; i--){
            	nameBackwards += self.name[i];
            }
            return nameBackwards;
        }
        return `${getReverseName()} si eman ym, olleH`;
    },
};

o.greetBackwards();

 

call과 apply, bind

자바스크립트에서는 일반적인 방법 외에도, 함수를 어디서, 어떻게 호출했느냐와 관계없이 this가 무엇인지 지정할 수 있다. 먼저 call 메서드부터 시작해보자.

call 메서드는 모든 함수에서 사용할 수 있으며, this를 특정 값으로 지정할 수 있다.

const bruce = {name: "Bruce"};
const madeline = {name: "Madeline"};

function greet(){
	return `Hello, I'm ${this.name}`;
}

greet(); // "Hello, I'm undefined!"
greet.call(bruce); // "Hello, I'm Bruce!"
greet.call(madeline); // "Hello, I'm Madeline!"

함수를 호출하면서 call을 사용하고 this로 사용할 객체를 넘기면 해당 함수가 주어진 객체의 메서드인 것처럼 사용할 수 있다. call의 첫 번째 매개변수는 this로 사용할 값이고, 매개 변수가 더 있으면 그 매개변수는 호출하는 함수로 전달된다.

function update(birthYear, occupation){
	this.birthYear = birthYear;
	this.occupation = occupation;
}

update.call(bruce, 1949, 'singer'); 
// bruce는 이제 {name: 'Bruce', birthYear:1949, occupation: "singer"} 이다.
update.call(madeline, 1942, 'actress');
// madeline은 이제 {name: 'Madeline', birthYear:1942, occupation: "actress"} 이다.

apply는 함수 매개변수 처리하는 방법을 제외하면 완전히 같다.

update.apply(bruce, [1995, "actor"]);
update.apply(madeline, [1918, "writer"]);

apply는 배열 요소를 함수 매개변수로 사용해야 할 때 유용하다.

const arr = [1,2,3,4,-9,10]
Math.min.apply(null, arr); // -9
Math.max.apply(null, arr); // 10

or

Math.min(...arr);
Math.max(...arr);

this의 값을 바꿀 수 있는 마지막 함수는 bind이다. bind를 사용하면 함수의 this 값을 영구히 바꿀 수 있다. update 메서드를 이리저리 옮기면서도 호출할 때 this 값은 항상 bruce가 되게끔.

const updateBruce = update.bind(bruce);

updateBruce(1904, "actor");
// bruce는 이제 {name: "Bruce", birthYear: 1904, occupation: "actor"} 이다.
updateBruce.call(madeline, 1274, "king");
// bruce는 이제 {name: "Bruce", birthYea:1274, occupation: "king"} 이다.
// madeline은 변하지 않았다.

bind는 함수의 동작을 영구적으로 바꾸므로 찾기 어려운 버그의 원인이 될 수 있다. bind를 사용한 함수는 call이나 apply, 다른 bind와 함께 사용할 수 없는 거나 마찬가지이다. bind는 매우 유용하지만, 함수의 this가 어디에 묶이는지 정확히 파악하고 사용해야 한다.

bind에 매개변수를 넘기면 항상 그 매개변수를 받으면서 호출되는 새 함수를 만드는 효과가있다. 예를 들어 bruce가 태어난 해를 항상 1949로 고정하지만, 직업은 자유롭게 바꿀 수 있는 업데이트 함수를 만들고 싶다면 다음과 같이 하면된다.

const updateBruce1949 = update.bind(bruce, 1949);
updateBruce1949("singer, songwriter");
// bruce는 이제 {name: "Bruce", birthYear: 1949, occupation: "singer, songwriter"} 이다.

함수는 자바스크립트에서 핵심적인 부분이다. 함수는 단지 코드를 모듈화하는 역할만 있는 건 아니다. 함수는 아주 강력한 논리적 단위가 될 수 있다.

이 글은 이선 브라운. n.d. Learning JavaScript. n.p.: 한빛미디어(주).를 참고하여 정리한 글입니다. 

 

반응형