개발 공부 기록

[독서] 자바스크립트 완벽가이드 내용 정리 본문

개발/독서

[독서] 자바스크립트 완벽가이드 내용 정리

_김도연 2021. 4. 16. 13:23

1장. 자바스크립트 소개

- 1종 함수(first-class function)란, 함수의 인자나 함수의 반환 값으로 또 다른 함수를 사용할 수 있다는 개념

- 입출력을 비롯해 통신과 파일 저장, 그래픽 처리 등 복잡한 기능들은 자바스크립트를 내장하고 있는 호스트 환경에서 담당한다. (브라우저가 자바스크립트 엔진을 내장하고 있기 때문에 클라이언트 측 자바스크립트의 호스트 환경은 웹브라우저다.)

- 표현식(expression)은 값으로 평가될 수 있는 구절을 말한다.

- 함수는 객체의 프로퍼티로 할당될 수 있다. 이때, 프로퍼티로 할당된 함수를 "메서드"라고 한다.

- this 키워드는 메서드가 정의된 객체 자신을 가리킨다.

2장. 어휘구조

- 리터럴(literal)은 프로그램에 직접 나타내는 데이터 값이다. (ex. 24, "hello", true, null)

- 자바스크립트가 항상 모든 줄 바꿈을 세미콜론으로 해석하는 것은 아니다. 일반적으로 세미콜론 없이 코드를 해석할 수 없는 경우에만 줄바꿈을 세미콜론으로 해석한다. 이러한 문장 종료 규칙은 상황에 따라 일부 의도하지 않은 결과를 초래할 수 있으므로 세미콜론을 사용하는 것이 좋다.

   - 일반적으로 문장이 (, [, /, +, - 로 시작하면 자바스크립트 인터프리터는 해당 문장을 이전 문장에 이어서 해석한다.

예시)

var y = x + f
(a+b).toString(); // => var y = x + f(a+b).toString();

3장. 타입, 값, 변수

- 데이터 타입이란 프로그래밍 언어로 다룰 수 있는 값의 유형

- 자바스크립트의 타입

1) 원시 타입(primitive type) : 숫자, 문자열, 불리언, null, undefined

2) 객체 타입(object type)

- 객체는 이름과 값을 갖는 프로퍼티의 집합이다.

- 일반적인 자바스크립트 객체는 순서가 없는 값들의 집합이며, 각 값에는 이름이 지정되어 있다.

- 특별한 종류의 객체

  * 배열 : 순서가 있는 값들의 집합이며, 각 값에는 번호(index)가 부여된다.

  * 함수 : 실행 코드를 가지고 있는 객체로서, 함수를 호출하면 코드가 수행되고 계산된 값이 반환된다. 함수는 값이고, 자바스크립트 프로그램은 함수를 보통 객체처럼 다룰 수 있다.

- 자바스크립트 인터프리터는 메모리 관리를 위해 자동으로 가비지 컬렉션을 수행한다. 객체에서 더 이상 접근할 수 없을 때(프로그램이 더 이상 객체를 참조하지 않을 때) 인터프리터는 그 객체가 다시 사용되지 않을 거라 판단하고 자동으로 메모리에서 해제한다.

- 기술적으로 자바스크립트 객체만이 메서드를 소유한다. null과 undefined는 자바스크립트에서 유일하게 메서드를 가질 수 없는 값이다.

- 자바스크립트의 산술 연산은 오버플로와 언더 플로, 0으로 나누는 에러를 발생시키지 않는다.

    - 산술 연산의 결과가 표현할 수 있는 가장 큰 수보다 더 크다면(오버플로), 자바스크립트는 Infinity라고 표현하는 무한대의 값을 출력한다.

    - 산술 연산의 결과가 표현할 수 있는 가장 작은 값보다 더 작다면(언더 플로), 자바스크립트는 0을 돌려준다. (음의 경우에는 특수 값인 '음의 0'을 반환한다.)

    - 0을 0으로 나누거나 무한대를 무한대로 나누기, 음수 값에 루트 등 정의되지 않는 값을 갖게 되는 경우 NaN 값이 출력된다. (NaN: Not a Number)

- NaN은 그 자신뿐만 아니라 다른 값과 같은지 비교할 수 없다. 즉, x == NaN은 불가능하고 x!= x이나 isNaN()을 사용해야 한다.

- isFinite()는 인자가 NaN, Infinity, - Infinity 이외의 숫자라면 참을 반환한다.

- 0과 음의 0은 같다.

- 무한대와 음의 무한대는 같지 않다.

- 자바스크립트는 실수 연산을 할 때 근사 값으로 표현한다. 이진 부동소수점 숫자를 사용하기 때문에 문제가 발생한다.

- 문자열에서 줄 끝에 역슬래시를 넣으면 한 줄을 여러 줄로 작성할 수 있다.

- null은 보통 아무 값도 갖지 않음을 가리킬 때 사용한다.

- undefined는 초기화되어있지 않은 변수나, 존재하지 않는 객체 프로퍼티나 배열의 원소 값에 접근하려고 할 때 얻는 값이다.

전역 객체

- 자바스크립트 인터프리터가 시작할 때(or 웹 브라우저가 새로운 페이지를 불러올 때), 새로운 전역 객체를 만들고 그 프로퍼티들은 초기화한다.

- 전역 객체가 생성될 때 초기화되는 프로퍼티들(undefined, Infinity, NaN 등)은 예약어가 아니지만 예약어처럼 취급된다.

- 최상위 코드(함수의 일부가 아닌 코드)에서는 this 키워드를 통해 전역 객체를 참조할 수 있다.

- 전역 변수를 선언하면, 그 변수는 전역 객체의 프로퍼티가 된다.

window 객체

- window 객체는 브라우저 창에 포함된 모든 자바스크립트 코드를 위한 전역 객체이다.

- window 객체는 핵심적인 전역 프로퍼티들을 정의할 뿐만 아니라, 웹 브라우저와 클라이언트 측 자바스크립트에서 사용되는 몇 가지 전역 객체들도 정의하고 있다.

wrapper 객체

- wrapper 객체: 문자열, 숫자, 불리언 프로퍼티에 접근하려고 할 때 생성되는 임시 객체

- 문자열, 숫자, 불리언 값의 프로퍼티는 읽기 전용. 새로운 프로퍼티를 정의할 수 없다.

var s = "test";
var S = new String(s);

    - 자바스크립트는 래퍼 객체를 필요에 따라 기본 타입으로 변환한다. 그래서 보통 S는 s처럼 동작한다.
    - ==(동치 연산자)는 값과 그 값의 래퍼 객체를 동등하게 다른다. 하지만 ===(엄격한 동치 연산자)로 이것들을 구분할 수 있다.

var s = 'test';
s.len = 4;
var t = s.len;
console.log(s); // test
console.log(t); // undefined

    - 2행에서 임시 string 객체의 len 프로퍼티에 4를 할당하고 임시 객체는 바로 삭제된다. 3행에서 기존 문자열과 같은 값을 가진 string 객체를 생성하고 len 프로퍼티를 읽으려고 한다. 이 프로퍼티는 존재하지 않아서 'undefined'로 평가된다. → 문자열, 숫자, 불리언 값의 프로퍼티에 어떤 값을 할당하면 무시된다. 값을 할당하는 것은 임시 객체에서 수행되며, 지속되지 않는다.

변경이 불가능한 원시 타입 값과 변경 가능 객체 참조

- 원시 타입은 값으로 비교된다.

- 객체는 값으로 비교되지 않는다. 두 객체가 같은 프로퍼티와 값을 가지고 있어도 두 객체는 같지 않다. 두 배열은 같은 순서로 같은 원소를 갖고 있어도 같지 않다.

- 객체는 참조 타입(reference type)으로 불리는데, 이는 자바스크립트의 원시 타입과 구별하기 위함이다.

- 객체의 값은 참조이다. 즉, 객체는 참조로 비교되고 두 객체가 같은 객체를 참조한다면 같다.

- 객체를 변수에 할당하는 것은 단순히 참조를 할당하는 것이다.

변환

null == undefined
"0" == 0
0 == false
"0" == false

- 자바스크립트는 값의 타입을 유연하게 변환시킬 수 있다.

- 동치 연산자 ==도 유연하게 동작한다.

- 래퍼 객체를 위한 생성자(Boolean(), Number(), String(), Object()... )로 사용되는 함수들이 new 연산자 없이 호출되면, 이 함수들은 변환 함수로 작동한다.

- null과 undefined를 객체로 변환하려고 하면 TypeError가 발생한다.

// 타입변환 숙어
x + "" // String(x)
+x // Number(x)
!!x // Boolean(x)

객체를 문자열로 변환하는 방법

1. 객체가 toString() 메서드를 갖고 있다면 이 메서드를 호출한 뒤, 반환한다.

2. 갖고 있지 않거나 반환 값이 원시 타입이 아니라면, valueOf() 메서드를 찾아 반환한다.

3. 둘 다 아니라면 toString() 또는 valueOf()로 부터 원시 값을 얻을 수 없기 때문에 TypeError를 발생시킨다.

객체를 숫자로 전환하는 방법

1. 객체가 원시 타입을 반환하는 valueOf() 메서드를 가지고 있다면, JS는 반환된 값을 숫자로 변환하여 반환한다.

2. 그렇지 않다면 toString() 값을 변환하여 반환한다.

3. 이외의 경우 TypeError를 발생시킨다.

(Date 객체만 예외적으로 2번을 먼저 진행함)

- Date 클래스는 코어 자바스크립트에 포함되어 있는 타입 가운데, 문자열로의 변환 절차와 수로의 변환 절차를 전부 구현하고 있는 유일한 타입이다.

- "<"와 같은 다른 관계 연산자들은 ==처럼 객체에서 원시 타입으로의 변환을 수행한다.

변수 선언

- var 문에서 변수에 초기값을 지정하지 않는다면, 변수는 값이 설정될 때까지 undefined 값이다.

- non-strict mode에서는 선언하지 않은 변수에 값을 배정하면, 그 변수는 전역 객체의 속성으로 생성되어 동작한다.

- 함수가 정의될 때 함수는 유효 범위 체인을 정의

- 함수가 호출될 때, 해당 함수의 지역 변수를 저장하기 위해 새로운 객체를 하나 생성하고, 해당 객체를 기존에 저장된 유효범위 체인에 추가한다.

- 중첩 함수의 경우, 외부에서 함수를 호출할 때마다 중첩된 함수가 매번 선언된다. 이러한 이유로 함수를 호출할 때마다 유효범위 체인이 조금씩 달라진다.

4장. 표현식과 연산자

- 표현식은 자바스크립트 인터프리터가 값으로 평가하는 JS구문이다. 복합 표현식을 만드는 가장 쉬운 방법은 연산자를 사용하는 것이다.

- 기본 표현식(primary expression): 다른 표현식을 포함하지 않은 독립적인 표현식으로 상수나 리터럴 값, 특정 키워드의 변수 참조를 말한다.

- 객체와 배열의 초기화 표현식: 새로 생성된 객체나 배열을 값으로 하는 표현식

    - 초기화 표현식은 주로 객체 리터럴, 배열 리터럴이라고 한다. 일반 리터럴과 달리 기본 표현식이 아니다. (프로퍼티와 원소의 값을 지정하는 수많은 하위 표현식을 포함할 수 있기 때문이다.)

    - 배열 초기화 표현식의 마지막 원소 표현식 다음에 쉼표가 올 수는 있지만, 그렇다고 해서 값이 정의되지 않은 원소가 만들어지지는 않는다.

- 함수 정의 표현식: 함수를 정의하고, 함수 정의 표현식의 값은 새로 정의된 함수이다.

- 호출 표현식

    - JS에서 함수나 메서드를 호출하는 문법이며, 호출될 함수를 가리키는 함수 표현식으로 시작한다.

    - 호출 표현식이 평가될 때 함수 표현식이 가장 먼저 평가되고, 그 후에 호출 인자 표현식이 순서대로 인자 값으로 평가된다.

    - return 문의 값이 호출 표현식의 값. 함수가 값을 반환하지 않는다면 함수 표현식의 값은 undefined

    - 메서드 호출이 아닌 호출 표현식은 보통 전역 객체를 this 키워드의 값으로 사용한다.

- 객체 생성 표현식: 새 객체를 생성하고 생성자를 호출하여 객체에 속한 프로퍼티들을 초기화한다.

    - 객체 표현식 평가

    1) 빈 객체 생성 ( { } )

    2) 주어진 인자들과 함께 생성자 호출(이때, 방금 생성된 빈 객체를 this 키워드의 값으로 설정하여 전달)

    3) 생성자 함수는 이 this 키워드를 사용해 새로 생성된 객체의 프로퍼티들을 초기화

연산자

- 자바스크립트에서 모든 값은 참이나 거짓으로 평가된다.

- 수로 변환이 불가능한 피연산자는 NaN 값으로 변환되며, 피연산자 중 하나라도 NaN일 경우에는 연산 결과도 NaN이다.

- 자바스크립트에서 모든 숫자는 부동소수점 숫자로 취급된다.

- '%'에서 결과는 첫 번째 피연산자의 부호와 동일하다.

- + 연산자

    - + 연산자는 타입 변환 시에 문자열 이어 붙이기를 먼저 수행한다. (피연산자가 객체에서 원시 타입으로 변환된 후, 만일 어떤 피연산자라도 문자열이면, 다른 피연산자를 문자열로 변환한 후 두 문자열을 이어 붙인다.)

    - 이외의 경우, 두 피연산자가 숫자(또는 NaN)로 변환되고 덧셈 연산이 수행된다.

true + true // 2 : 불리언 값을 숫자로 바꾼 후 더하기
2 + null // 2 : null 값을 0으로 바꾼 후 더하기
2 + undefined // NaN : undefined를 NaN으로 바꾼 후 더하기
1 + 2 + "blind mice" // 3 blind mice
1 + (2 + "blind mice") // 12 blind mice

- 단항 산술 연산자

 

    - 표현식 ++x는 x=x+1과 항상 같지 않음을 명심하자.

"1" + 1 // 11
++"1" // 2

- 비트 단위 연산자

    - NaN, Infinity, -Infinity는 비트 단위 연산자의 피연산자로 사용될 때 모두 값이 0으로 바뀐다.

- 동치와 부동치 연산자

    - = (할당), == (동치), === (일치)

    - JS는 값 비교가 아닌 참조 비교를 수행한다. (객체는 자기 자신과만 같다)

    - === 규칙

    1) 두 값의 타입이 서로 다르면 두 값은 일치하지 않는다.

    2) 두 값이 모두 null 이거나 undefined면 일치한다. (true, false도 포함)

    3) 적어도 하나의 값이 NaN이면 두 값은 일치하지 않는다. (NaN은 자기 자신과도 일치하지 않는다.)

    4) 0과 -0은 일치한다.

    5) 두 객체의 프로퍼티가 일치해도 서로 다른 객체를 참조할 경우에 두 값은 일치하지 않는다.

- 비교 연산자

    - 타입에 제한이 없으나 숫자와 문자열만 비교할 수 있기 때문에, 숫자와 문자열이 아닌 피연산자는 먼저 변환된다.

    - 변환 과정

    1) valueOf() 메서드의 값이 원시 타입이면 그 값을 이용하고, 그렇지 않은 경우에는 toString() 메서드의 반환 값을 이용한다.

    2) 객체가 원시 타입으로 변환된 후, 피연산자가 모두 문자열이라면 '알파벳 순서'대로 두 문자열을 비교하게 된다.

    3) 객체가 원시 타입으로 변환된 후, 문자열이 아닌 피연산자가 하나 이상 존재할 경우 피연산자 모두 숫자로 변환된 후 값을 비교하게 된다.

    - 둘 중 하나의 피연산자가 NaN이거나 NaN으로 변환된다면 늘 false를 반환

    예시) "one" < 3 // 은 one이 NaN으로 바뀌어 false

in연산자

    - in 연산자는 좌변의 피연산자로 문자열(또는 문자로 변환될 수 있는 것)을 받는다.

    - 우변의 피연산자로는 객체나 배열을 받는다.

- instanceof 연산자

    - instanceof 연산자는 좌변의 피연산자로 객체를, 우변의 피연산자로 객체의 클래스 이름을 식별자로 받는다. 좌변에 오는 객체가 우변 클래스의 인스턴스일 경우 연산 결과는 true로 평가되고 그렇지 않을 경우 false로 평가된다.

    - 모든 객체는 Object의 인스턴스다.

- 논리 표현식

    - 모든 JS 값은 true로 평가될 수 있거나 false로 평가될 수 있으므로 논리 표현식의 피연산자로 반드시 boolean이 올 필요는 없다.

    - && 연산자의 단축 특성 덕분에 아래 코드는 동일하게 동작한다.

      > if(a == b) stop();

      > (a == b) && stop();

    - || 연산자의 경우 매개변수의 기본값을 설정하는 경우에도 종종 사용된다.

      > var max = max_width || preferences.max_width || 500;

- 평가표 현식

    - eval(): 문자열을 JS 코드로 해석하고 이를 평가한 결과를 출력

    - eval() 사용 시 문제점: eval()이 평가하거나 호출하는 함수를 최적화할 수 없음.

5장. 문장

- 표현문(expression statement): 할당 문이나 함수처럼 부수 효과가 있는 표현식

- 선언문(declaration statement): 새 변수나 함수를 선언하거나 정의할 때 쓰임

var

- 다른 전역 프로퍼티와는 달리 var로 선언된 프로퍼티는 delete로 삭제할 수 없다.

- 초기화는 var문이 선언된 시점에서 발생하고, 그전까지는 변수 값이 undefined가 된다.

function

- 함수는 중첩될 수 있으나, 다른 함수 속에 중첩될 때는 중첩된 함수 내에서 최상위 단계에 위치해야 한다. (즉, 함수 선언은 if문이나 while문 등의 문장 안에 있을 수 없다)

- 함수 선언문은 함수 정의 표현식과 다르다.

- 함수 선언문은 함수 이름을 변수로 선언한 후, 이 변수에 함수 객체를 할당한다.

- 함수 선언문을 이용하면 함수의 이름과 본문 모두 유효 범위 최상단으로 끌어올려진다.

- var문처럼 함수 선언문은 삭제할 수 없는 변수를 만든다. 하지만 읽기 전용 변수이므로 언제든 값이 바뀔 수 있다.

for/in

- 인터프리터는 루프가 매회 반복되기 전에 각 객체의 각 열거 가능한 프로퍼티 이름을 '변수'에 할당한다.

- for/in 루프는 실제로 객체가 가진 모든 프로퍼티를 열거하지 않고 오직 '열거할 수 있는 프로퍼티'만 열거한다. 상속된 사용자 정의 프로퍼티도 for/in 루프를 통해 열거할 수 있다.

기타

- 레이블: 어떤 문장이라도 그 앞에 식별자 이름과 콜론(:)을 넣음으로써 레이블을 붙일 수 있다.

- debugger: debugger문은 코드의 중단점과 같이 동작한다.

- use strict: with문 사용금지, 모든 변수는 반드시 선언되어야 함, 메서드로 호출된 함수가 아닌, 함수로 호출된 함수의 this 값은 undefined

6장. 객체

- 객체는 이름과 값으로 구성된 프로퍼티들의 정렬되지 않은 집합이다.

- JS 객체는 객체가 가진 고유 프로퍼티를 유지하는 것 외에 '프로토타입'이라고 하는 다른 객체의 프로퍼티를 상속받는다.

- 프로퍼티 속성

1) 쓰기(writable): 프로퍼티 값의 수정 가능 여부를 결정

2) 열거(enumerable): 프로퍼티의 이름을 for/in 루프에서 읽을 수 있는지 여부 결정

3) 설정(configurable): 프로퍼티의 삭제 가능 여부와 프로퍼티 속성의 변경 가능 여부를 결정

- 네이티브 객체: ES 명세에 정의된 객체 또는 그 객체의 클래스(Array, Function, Date, 정규표현식)

- 호스트 객체: 브라우저와 같이 자바스크립트 인터프리터가 내장된 호스트 환경에 정의된 객체 (예시: HTMLElement. 호스트 환경에서 메서드들을 정의할 때 일반적으로 JS Function 객체로 정의하는 것과 마찬가지로, 호스트 객체는 네이티브 객체일 수도 있다.)

- 사용자 정의 객체: JS코드의 실행으로 생성된 객체

- 고유 프로퍼티: 객체에 직접 정의된 프로퍼티

- 상속받은 프로퍼티: 객체의 프로토타입 객체가 정의한 프로퍼티

객체 생성하기

- 객체 리터럴( { } ), new, Object.create()

프로토타입

- 자바스크립트의 모든 객체는 또 다른 JS 객체와 연관되어 있다. 이 객체는 프로토타입으로 알려져 있고, 객체는 프로토타입으로부터 프로퍼티들을 상속받는다.

- 객체 리터럴로 생성되는 모든 객체는 프로토타입 객체가 같다. (Object.prototype)

- new 키워드를 사용해 생성자를 호출하면, 생성자 함수의 프로토타입이 생성된 객체의 프로토타입이 된다.

- 모든 내장 생성자는(+사용자 정의 생성자) Object.prototype을 상속하는 객체를 프로토타입으로 갖는다.

- 이처럼 프로토타입 객체들이 연결된 것을 '프로토타입 체인'이라고 한다.

프로퍼티 접근 및 설정

- 연관 배열: 숫자가 아닌 문자열을 인덱스로 가짐. (해시, 맵, 사전)

- 모든 JS 객체는 연관 배열이다.

상속

- 내장된 생성자의 프로토타입 프로퍼티들은 읽기 전용이다.

프로퍼티 삭제하기

- 프로퍼티의 값을 지우는 것이 아니라 프로퍼티를 지운다.

- delete 연산자는 상속받은 프로퍼티가 아닌 고유 프로퍼티만 지울 수 있다.

- delete 표현식이 true로 평가될 때: 삭제에 성공 or 프로퍼티가 존재하지 않아서 아무런 영향을 끼치지 못함 or 올바른 표현식이 아니라서 아무 효력을 낼 수 없음

프로퍼티 검사하기

- in 연산자, hasOwnProperty(), propertyIsEnumerable(), 단순 프로퍼티 접근

- hasOwnProperty() 메서드는 상속받은 프로퍼티의 경우에는 false로 반환한다.

- propertyIsEnumerable()은 hasOwnProperty() 보다 상세한 검사를 한다.

프로퍼티 열거하기

- for/in 루프 말고도 ES5에는 프로퍼티 이름을 열거하는 두 가지 함수가 더 있다.

1) Object.keys(): 객체가 가진 '열거할 수 있는' 고유 프로퍼티들을 배열에 담아 반환

2) Object.getOwnPropertyNames(): 해당 객체가 가진 모든 고유 프로퍼티의 이름을 배열로 반환

프로퍼티 속성

- 데이터 프로퍼티의 4가지 속성: value, writable, enumerable, configurable

- 접근자 프로퍼티의 4가지 속성: get, set, enumerable, configuration

- Object.getOwnPropertyDescriptor(): 객체가 가진 특정 프로퍼티에 대한 프로퍼티 디스크립터 객체를 반환함. 객체의 고유 프로퍼티에서만 동작함(상속된 프로퍼티 속성을 검사하기 위해서는 프로토타입 체인을 명시적으로 직접 순회해야 한다.)

- Object.defineProperty(): 프로퍼티의 속성을 설정하거나 임의의 속성으로 새 프로퍼티를 만듦.

객체 속성

1) prototype 속성: 프로퍼티를 상속하는 객체를 지정

- 객체가 만들어지는 시점에 설정됨

- 객체 리터럴이나 Object.create()로 생성된 객체는 Object()의 생성자를 constructor 프로퍼티로 갖는다. 따라서 constructor.property는 객체 리터럴에 대해서는 정확한 프로토타입을 참조하지만 Object.create()로 생성된 객체는 그렇지 않다.

- isPrototypeOf(): 메서드를 통해 객체 A가 객체 B의 프로토타입(또는 프로토타입 체인의 일부)인지 알아볼 수 있다.

2) class 속성: 객체의 타입에 대한 정보를 담고 있는 문자열

3) extensible 속성: 객체에 새 프로퍼티를 추가할 수 있는지 여부를 결정

객체 직렬화하기

- 객체 직렬화: 객체의 상태를 문자열로 변환하는 과정

> 객제 직렬화: JSON.stringify() // 객체가 가진 열거 가능한 고유 프로퍼티만 직렬화

> 직렬화 문자열 객체로 복원: JSON.parse()

(참고: JSON - JavaScript Object Notation)

- JSON 문법에서 NaN, -Infinity, Infinity는 null로 직렬화된다.

- Date 객체는 ISO 날짜 형식을 따르는 문자열로 직렬화된다. 그러나 JSON,parse() 함수는 문자열을 Date 객체로 복원하지 않는다.

- Function, RegExp, Error 객체, undefined 값은 직렬화하거나 복원할 수 없다.

객체 메서드

- toString() 메서드는 어떠한 인자도 받지 않고, 호출 대상 객체의 값을 어떠한 방식으로든 문자열로 만들어서 반환한다.

- toLocaleString() 메서드: 객체의 지역화(localized)된 문자열 표현을 반환하는 것.

- toJSON() 메서드: 직렬화하려는 객체에 해당 메서드가 있으면, toJSON()메서드가 호출되고 그 결과 값이 원래 객체 대신 직렬화된다.

- valueOf() 메서드: JS가 객체를 숫자와 같은 다른 원시 타입으로 변환하려할 때 호출된다.

7장. 배열

- 배열은 정렬된 값의 집합이다.

- JS의 배열은 타입이 고정되어 있지 않다.

- JS 배열은 32비트 인덱스를 사용한다. (약 42억개의 원소를 갖을 수 있음)

- 배열의 인덱스는 프로퍼티 이름. 일반적으로 배열은 객체 프로퍼티를 통해 원소에 접근하는 것보다 정수 첨자를 통해 원소에 접근하는 것이 훨씬 빠르도록 최적화되어 있다.

- var a = new Array(n); // n 크기의 배열 생성. 배열에는 어떤 값도 저장되어 있지 않음(undefined도 없음)

- 배열의 [ ] 구문은 객체 프로퍼티 접근 때 쓰는 [ ]와 똑같이 동작한다. JS는 사용자가 명시한 숫자 배열 인덱스를 문자열 형태로 바꿔서 프로퍼티 이름으로 사용한다.

- 배열이 일반 객체와 다른 점은 속성이름으로 2^32보다 작은 양수를 사용할 때, 자동으로 length 프로퍼티의 값을 바꾼다는 것이다.

- 모든 인덱스 값은 프로퍼티 이름이지만, 프로퍼티 이름은 0과 2^32 - 1 사이의 정수여야만 인덱스가 될 수 있다.

 (모든 배열은 객체이므로, 어떤 이름의 프로퍼티라도 자유롭게 만들 수 있다.)

- 배열의 인덱스가 특별한 종류의 객체 프로퍼티 이름일 뿐이라는 것은, JS 배열에서는 "out of bounds" 에러가 발생하지 않는다는 뜻이다.

희소 배열

- 희소 배열은(sparse array) 배열에 속한 원소의 위치가 연속적이지 않은 배열을 말한다.

- 희소 배열은 보통 배열보다 일반적으로 느리고, 메모리를 많이 사용하며, 원소를 찾는데 걸리는 시간이 일반 객체의 속성 값을 찾는 시간만큼 오래 걸린다.

배열의 길이

- 원소의 인덱스는 배열의 length보다 절대로 크거나 같을 수 없다.

배열의 원소 추가/삭제

- unshift(): 배열의 앞쪽에 원소 추가

- delete a[n]: a 배열의 n번째 원소 삭제

배열 순회

for(var i = 0; i < a.length; i++) {
	if(!(i in a)) continue;
}
// 아무 원소도 없는 인덱스는 건너뛰지만 원소 값이 (undefined 포함)있는 경우는 루프 안에서 처리

for(var idx in array) {
	var value = array[index];
}
// for/in 루프는 배열의 인덱스를 포함한 프로퍼티 이름들은 한 번에 하나씩 루프 변수에 할당한다.
// 이때, 존재하지 않는 인덱스는 루프에 나타나지 않는다.

- for/in 루프는 상속받은 프로퍼티 이름까지 넘겨주기 때문에 a.hasOwnProperty(i) 사용을 통해 상속받은 속성을 건너뛸 수 있다.

- ES 명세서에는 for/in 루프가 객체의 프로퍼티들을 어떤 순서로 순회해야하는지 정해 놓고 있지 않다.

- 만약 배열이 객체 프로퍼티와 배열의 원소를 둘 다 가지고 있다면, 프로퍼티 이름은 그 크기 순서가 아니라 생성된 순서대로 반환될 수도 있다.

- 알고리즘을 작성할 때 순회하는 순서가 중요하다면 for/in 루프보다 for 루프를 사용할 것을 권장한다.

다차원 배열

- 진정한 의미의 JS는 다차원 배열을 지원하지 않는다. 그러나 배열의 배열을 통해 다차원 배열을 흉내낼 수 있다.

배열 메서드

- Array.sort(): sort()메서드를 별도의 전달인자 없이 호출하면, 배열 안의 원소들을 알파벳 순으로 정렬한다.

- 배열에 undefined 원소들이 존재하면, 이 원소들은 배열의 끝부분으로 정렬된다.

- 알파벳순이 아니라 다른 순서로 배열을 정렬하려면, sort() 메서드의 전달인자를 통해 비교 함수를 직접 명시해주어야 한다.

    - 1-2 순서인 경우: 비교함수는 0보다 작은 숫자를 반환해야 한다.

    - 2-1 순서인 경우: 비교함수는 0보다 큰 숫자를 반환해야 한다.

    - 동등한 경우(순서가 무의미한 경우): 0을 반환한다.

ES5 배열 메서드

- forEach() : 모든 원소 순회전에 종료 안됨. try-catch를 이용하여 종료 가능

- map() : 기존 배열을 수정하지 않고 새 배열을 반환

- filter() : 배열의 일부분을 반환. 이 메서드에 전달하는 함수는 조건자함수

- every() : 전달인자로 넘긴 함수가 배열의 모든 원소에 대하여 true를 반환하면 true

- some() : 전달인자로 넘긴 함수가 배열의 일부 원소에 대해 true를 반환하는 경우에 some() 메서드는 true를 반환

- 고차함수: 함수의 매개변수나 반환 값으로 또 다른 함수를 사용할 수 있는 함수

- reduce() : 어떻게든 배열 원소 중 두 값을 하나로 결합하면서 크기를 줄이고, 마지막 남은 값을 반환 (초기값을 설정하지 않을시, 배열의 첫번째 원소를 초기 값으로 사용한다.)

- reduceRight() : reduce()와 동작은 같지만, 배열의 끝부터 시작해 반대방향으로 처리한다.

- indexOf() : 배열의 원소 중 특정한 값을 찾아 해당 인덱스를 반환(없을시 -1 반환)

- lastIndexOf() : indexOf()와 반대로 배열의 마지막 원소부터 시작

- isArray() : 특정 객체가 배열인지 여부를 반단할 수 있다.

유사 배열 객체

- 클라이언트측 자바스크립트에서는 상당수의 DOM 메서드가 배열과 유사한 객체를 반환한다.

- JS의 배열 메서드는 배열 뿐만 아니라 유사 배열 객체에도 적용이 가능하도록 범용 메서드로 구현되었다.

- 유사 배열은 Array.prototype을 상속받지 않기 때문에, 배열 메서드를 해당 객체의 메서드로 호출할 수는 없다. 대신 Function.call 메서드를 통해서 간접적으로 호출할 수 있다.

- ES5에서는 문자열은 읽기 전용 배열처럼 동작한다. 따라서 배열을 직접 수정하는 배열 메서드에는 작용하지 않는다.

8장. 함수

- 한 번 정의하면 몇 번이든 실행할 수 있고 호출할 수 있는 자바스크립트 코드 블록

- 매개변수는 함수 몸체 내에서 지역 변수처럼 취급된다.

- 각 호출에는 전달인자 외에도 호출 컨텍스트(invocation context)가 포함되는데, this 키워드의 값이 바로 해당 컨텍스트다.

// 선언문 형태
function factorial(x) {
 if(x <= 1) return 1;
    return x * factorial(x - 1);
}

// 함수 표현식
var square = function (x) { return x * x; }

- 함수 정의 표현식에서 함수 이름은 옵션이다.

- 함수 정의 표현식이 이름을 포함하면, 이 함수의 몸체의 유효 범위에 해당 함수 객체에 연결된 이름이 포함된다. (사실상 그 함수 이름이 해당 함수의 지역 변수가 되는 것이다.)

- 표현식 형태로 정의된 함수 대부분은 이름이 필요하지 않고, 따라서 함수를 더욱 단순하게 정의할 수 있다.

- 표현식 형태로 함수를 정의하는 것은 한 번만 사용되는 함수에 특히 적합하다.

- 함수 선언문은 그 함수를 둘러싼 스크립트나 함수의 맨 위로 '끌어올려(hoisted)'진다.

- 표현식으로 정의된 함수는 정의되는 지점 위에서는 호출할 수 없다.

- 리턴 값이 없는 함수를 가끔 프로시져(procedure)라고 부르기도 한다.

- 중첩된 함수는 해당 함수가 속한 함수(또는 함수들)의 매개변수와 변수에 접근할 수 있다.

- 함수 선언문은 전역 코드 혹은 다른 함수 안에는 등장할 수 있지만, 반복문, try-catch, 조건문, with 문 안에는 들어갈 수 없다. 이는 선언되는 함수에만 적용된다.

- 함수 정의 표현식은 JS 코드 어디에나 사용가능하다.

- JS에서 함수는 원시 값이 아니지만 특별한 종류의 객체이고 이는 함수가 프로퍼티를 가질 수 있음을 의미한다.

함수 호출하기

- 자바스크립트 함수 호출 방법

1) 일반적인 함수 형태

- ES5의 strict 모드에서 호출 컨텍스트는 undefined이다.

2) 메서드 형태

- 메서드 호출 표현식에서는 객체가 호출 컨텍스트가 되므로, 함수 몸체에서 this 키워드를 사용해서 객체를 참조할 수 있다.

- 메서드 체이닝: 메서드가 객체를 반환하면, 메서드의 반환 값을 후속 호출의 일부로 사용할 수 있다.

- 자바스크립트 문법은 this에 값을 할당하는 것을 허용하지 않는다.

- this 키워드에는 유효범위가 없고 중첩 함수는 호출자의 this 값을 상속하지 않는다.

3) 생성자

- 함수나 메서드 호출 앞에 new 키워드가 있다면, 그것은 생성자 호출이다.

- 생성자 함수는 객체를 초기화하고, 새로 생성된 이 객체는 생성자 함수의 호출 컨텍스트로 사용된다. 따라서 생성자 함수는 새로 생성된 객체를 this 키워드로 참조할 수 있다.

4) call()과 apply() 메서드를 통한 간접호출

- call()과 apply()는 함수를 간접적으로 호출한다. 어떤 함수든지 특정 객체의 메서드로 호출할 수 있다.

함수 전달인자와 매개변수

- 함수가 호출될 때 정의된 매개변수보다 더 많은 인자가 전달되면, 매개변수 이름이 붙지 않은 인자 값을 직접적으로 참조할 방법이 없다. (→ 해결책: Arguments 객체)

- 함수 몸체 내에서 arguments 식별자는 해당 호출에 대한 Arguments 객체를 참조한다.

- Arguments 객체는 유사배열 객체이고, 이름이 아니라 인덱스 숫자를 통해 함수의 전달 인자를 얻어올 수 있다.

- arguments는 실제로 배열이 아니라 Arguments 객체이다.

- Arguments 객체는 기술적으로 배열은 아니다. 숫자로된 프로퍼티를 가지고 있는 객체이다.

클로저

- 함수 객체와 함수의 변수가 해석되는 유효범위(변수 바인딩 집합)를 아울러 컴퓨터 과학 문헌에서는 클로저라고 일컫는다.

- 기술적으로 모든 JS 함수는 클로저인데, 함수는 객체이고 함수 자신과 관련된 유효범위 체인을 가지고 있기 때문이다.

- 어휘적 유효범위의 기본적인 규칙: 자바스크립트 함수는 함수가 정의되었을 때의 유효범위 체인을 사용하여 실행된다.

- this는 자바스크립트 키워드이지 변수가 아니다.

- 모든 함수 호출에는 this 값이 있고, 바깥쪽 함수가 this 값을 별도의 변수로 저장하지 않으면 클로저는 바깥쪽 함수의 this 값에 접근할 수 없다.

함수 프로퍼티, 메서드, 생성자

- 모든 함수는 서로 다른 프로토타입 객체를 가지고 있다.

- bind() : 함수와 객체를 서로 묶음. bind()에 전달하는 인자 중 첫번째 이후의 인자는 this 값과 함께 해당 함수의 인자로 바인딩된다. (currying 이라고 부르기도 함)

- Function() 생성자

    - 동적으로 JS함수를 생성하고 실행 시간에 컴파일되는 것을 가능하게 한다.

    - 생성자가 호출될 때마다 함수 몸체를 분석(parse)하여 새로운 함수 객체를 생성하기 때문에 루프 내부 또는 자주 호출되는 함수 내에서 생성자를 호출한다면 비효율적이다.

    - 함수 생성자가 생성하는 함수는 어휘적 유효범위를 사용하지 않는다. 함수 생성자가 생성한 함수는 언제나 최상위 레벨 함수로 컴파일 된다.

- 함수형 프로그래밍

    - 고차함수: 하나 이상의 함수를 인자로 받고, 새 함수를 반환하는 함수

    - 메모제이션: 이전 계산결과를 캐싱해두는 방식

9장. 클래스와 모듈

- 인스턴스 상태를 정의하거나 저장하는 프로퍼티와, 그들의 동작을 정의하는 프로퍼티(보통 메서드라고 한다)를 가지고 있기도 하다.

- 메서드는 클래스 수준에서 정의되고 모든 인스턴스가 공유한다.

- JS에서 클래스는 프로토타입 기반의 상속 매커니즘을 기반으로 하고 있다.

- 두 객체가 같은 프로토타입 객체로부터 프로퍼티를 상속받고 있다면, 둘은 같은 클래스의 인스턴스이다.

- JS 클래스는 동적으로 확장될 수 있다.

클래스와 프로토타입

- 팩터리(factory) 함수: 클래스를 위한 프로토타입 객체를 정의하고, 새 인스턴스를 생성하고 초기화하는 함수

- this는 메서드의 호출 대상 객체를 가리킨다.

클래스와 생성자

- 생성자 호출의 핵심적인 특징은 생성자의 prototype 프로퍼티가 새 객체의 프로토타입으로 사용된다는 것이다.

- 모든 객체가 프로토타입 객체를 상속하려면, 프로퍼티 이름은 반드시 "prototype"이어야 한다.

- 생성자를 호출하면 새 객체는 자동으로 생성되고, 새 객체의 메서드로서 생성자 함수가 호출된 다음, 초기화 완료된 새 객체가 반환된다.

- 서로 다른 두 생성자 함수라도 같은 프로토타입 객체를 가리키는 prototype 프로퍼티를 가질 수 있다.

- 모든 자바스크립트 함수는 생성자로 사용될 수 있는데, 함수가 생성자로 호출되려면 prototype 프로퍼티가 있어야 한다. 따라서 모든 JS 함수에는 자동으로 prototype 프로퍼티가 설정된다. 이 prototype 프로퍼티의 값은 constructor 프로퍼티 하나만 가진 객체이다.

- constructor 프로퍼티는 열거되지 않으며 constructor 프로퍼티 값은 해당 함수 객체이다.

- 모든 함수에 대해 F.prototype.constructor == F 는 true이다.

- 미리 정의된 프로토타입 객체가 있고 이 프로토타입 객체가 constructor 프로퍼티를 가지고 있다는 말은, 일반적으로 어떤 객체가 자기 자신의 생성자를 가리키는 constructor 프로퍼티 또한 상속하고 있음을 뜻한다. 따라서 생성자는 클래스를 구별하는데 사용될 수 있고, constructor 프로퍼티를 통해 객체의 클래스를 얻을 수 있다.

JS 클래스 정의 시 관련된 객체

- 생성자 객체: 생성자 함수(객체)는 클래스 이름을 정의한다.

- 프로토타입 객체: 이 객체의 프로퍼티는 클래스의 모든 인스턴스에 상속된다. 그리고 그 값이 함수인 프로퍼티는 인스턴스 메서드로 동작한다.

- 인스턴스 객체: 각 인스턴스는 독립적인 객체이고, 인스턴스에 직접 정의한 프로퍼티는 다른 인스턴스에 공유되지 않는다. 함수가 아닌 프로퍼티는 클래스의 인스턴스 필드로 작동한다.

클래스 확장하기

- 자바스크립트 객체의 프로토타입에 메서드를 추가함으로써 간단히 JS 클래스를 확장할 수 있다.

- 그냥 Object.prototype에 프로퍼티를 추가하면, 이 프로퍼티는 모든 for/in 루프에서 열거된다. 안전한 확장을 위해서는 Object.defineProperty() 메서드를 사용하는 것이 좋다.

- 호스트 환경에서 정의된 클래스의 확장 여부는 호스트 환경의 구현체마다 다르다.

클래스와 자료형

- instanceof 연산자는 생성자 함수를 요구하지만, 실제로 instanceof 연산자는 객체가 어떤 프로토타입을 상속했는지를 검사한다.

- isPrototype() 메서드를 통해 어떤 객체의 프로토타입 체인에 특별한 프로토타입 객체가 있는지를 검사할 수 있다.

- instanceof 연산자와 isPrototypeOf() 메서드의 단점은 오직 주어진 객체와 클래스의 관계만 테스트할뿐, 어떤 객체의 클래스가 무엇인지 알아내는 데 사용할 수 없다는 것이다.

- constructor 프로퍼티: constructor 프로퍼티를 통해 클래스 구분이 가능하다. 자바스크립트 객체 가운데는 constructor 프로퍼티가 없는 것도 있을 수 있다.

- 생성자 이름(Object.constructor.getName())

- 덕 타이핑(Duck-Typing): 만약 어떤 객체가 오리처럼 걷고 헤엄치고 꽥꽥거릴 수 있다면, 오리 클래스의 프로토타입 객체를 상속하지 않았더라도 그 객체를 오리로 취급할 수 있다. 일반적으로 덕 타이핑이라고 하는 것은 객체가 어떤 메서드들을 구현하고 있는지 테스트하는 것이다.

- 무간섭주의: 입력 객체가 메서드를 실제로 구현하고 있는지 검사하지 않는 것

자바스크립트의 객체 지향 방법

- set: 중복되지 않은 값을 정렬되지 않은 형태로 저장하는 데이터 구조

- 표준변환 메서드

    - toString() : 객체의 문자열 표현을 반환

    - toLocaleString() : 객체를 로케일에 맞는 문자열로 변환

    - valueOf() : 객체를 원시 값으로 반환

    - toJSON() : JSON.stringify() 에 의해 자동 호출

- 비교 메서드

    - 자바스크립트의 동치 연산자들은 객체를 비교할 때, 값이 아니라 참조를 사용한다.

    - 이런 연산자들은 두 객체에, 이름이 같고 값도 같은 프로퍼티가 존재하는지를 검사하지 않는다.

    - compareTo() 메서드는 하나의 인자를 받고, 메서드 호출 대상 객체와 인자를 비교한다.

- 메서드 빌려오기

    - 메서드는 단순히 객체의 프로퍼티로 할당된 함수일 뿐이며, 객체를 '통해' 또는 객체를 '대상'으로 호출될 따름이다.

- private 상태

    - private 필드는 오직 인스턴스 메서드로만 접근할 수 있고 클래스의 외부에는 보이지 않는다.

    - 인스턴스를 생성할 때, 생성자 호출의 클로저에 포착된 변수(혹은 인자)를 사용하면 private 인스턴스 필드를 흉내낼 수 있다.

    - 캡슐화 기법에는 오버헤드가 있다. 상태를 캡슐화하도록 클로저를 사용하는 클래스는 그렇지 않은 클래스보다 확실히 느리고 크다.

생성자 오버로딩과 팩터리 메서드

- 팩터리 메서드는 해당 클래스의 인스턴스를 반환하는 클래스 메서드이다.

- 팩터리 메서드의 매력은 메서드 이름을 원하는 대로 지을 수 있다는 점이고, 이름이 다른 메서드는 다른 방식의 초기화를 하도록 할 수 있다는 점이다.

- 자바스크립트에서는 하나의 프로토타입 객체를 공유하는 생성자 함수를 여러개 정의할 수 있다. 이 경우, 어떤 생성자 함수를 사용하건 생성된 객체는 같은 자료형이 될 것이다.

서브 클래스

- 클래스A(superclass) -- extend --> 클래스B(subclass)

- 메서드 체이닝: 클래스B의 메서드가 클래스A의 메서드를 재정의했을 때, 클래스B의 재정의된 메서드에서 클래스A의 원래 메서드를 호출하는 것

- 생성자 체이닝: 서브클래스의 생성자B()가 슈퍼클래스의 생성자A()를 호출

- 추상 클래스: 실제로는 구현되지 않은 추상 메서드가 하나 이상 있는 클래스. 추상 메서드의 실제 구현은 추상 클래스를 상속한 서브 클래스가 담당함.

- JS의 객체는 클래스의 프로토타입 객체로부터 프로퍼티(보통 메서드)를 상속한다.

- singletonSet은 읽기 전용이고, 하나의 상수 멤버만 가진 특별한 집합이다.

- 서브클래스를 정의할 때, 메서드를 완전히 교체하지 않고 확장하거나 수정하고 싶을 때를 위해 체이닝이 필요하다.

ES5 클래스

- Object.preventExtensions()는 객체를 확장할 수 없게 하는데, 이는 해당 객체에 새로운 프로퍼티가 추가될 수 없다는 뜻이다.

- Object.seal()은 새로운 프로퍼티가 추가되는 것을 막을 뿐만 아니라, 현재 객체에 있는 모든 프로퍼티를 재설정될 수 없게 한다.

- Object.freeze()는 Object.seal()의 모든 과정을 수행하고 추가로 모든 프로퍼티를 읽기 전용으로 설정하여 또한 재설정할 수 없도록 만든다.

- 인스턴스 메서드를 읽기 전용으로 만든다면, 서브클래스에서 재정의하기 훨씬 더 어려워진다.

모듈

- 코드를 클래스로 구성하는 중요한 이유 중 하나는 코드를 더욱 모듈화하여 여러 상황에서 재사용하기 적합하게 하는 것이다.

- 모듈은 JS 코드로 이루어진 하나의 파일

- 모듈은 전역 실행 환경을 수정해서는 안되고, 뒤따르는 모듈이 간섭 없는(또는 간섭을 거의 받지 않는) 환경에서 실행되도록 해야 한다.

- 각 모듈은 전역 심볼을 최소한으로 정의해야 하며, 이상적으로는 단 하나의 전역 심볼도 정의하지 않아야 한다.

네임 스페이스로서의 객체

- 모듈에서 전역 변수 생성을 피하는 방법

- 전역 함수와 전역 변수를 저장하는 대신에, 네임스페이스 객체는 함수와 변수를 프로퍼티에 저장한다.

- private 네임스페이스 내에서 실행되는 코드는 "(function(){}());"로 끝난다.

 

'개발 > 독서' 카테고리의 다른 글

[독서] Clean Code 내용정리  (0) 2020.08.26
Comments