19장 프로토타입
프로토타입이 소유한 프로퍼티를 프로토타입 프로퍼티, 인스턴스가 소유한 프로퍼티를 인스턴스 프로퍼티라고 한다.
프로토타입 프로퍼티와 같은 이름을 가진 프로퍼티를 인스턴스에 추가하면 프로토타입 체인을 따라 프로토타입 프로퍼티를 검색해서 덮어쓰지 않고 인스턴스 프로퍼티로 추가한다. 이때 인스턴스 메서드는 프로토타입 메서드를 오버라이딩했고 프로토타입 메서드는 가려진다. 이렇게 상속 관계에 따라 프로퍼티가 가려지는 현상을 프로퍼티 섀도잉이라고 한다.
🧐
오버라이딩 : 상위 클래스가 가지고 있는 메서드를 하위 클래스가 재정의해 사용하는 방식.
오버로딩 : 함수 이름은 동일하지만 매개변수 타입 또는 개수가 다른 메서드를 구현하고 매개변수에 의해 메서드를 구별해 호출하는 방식.
프로퍼티를 삭제할 때도 프로토타입 메서드가 아닌 인스턴스 메서드가 삭제된다. 하위 객체를 통해 프로토타입의 프로퍼티를 변경/삭제하는 것은 불가능하다.
프로토타입 프로퍼티를 변경/삭제하려면 프로토타입에 직접 접근해야 한다.
프로토타입은 임의의 다른 객체로 변경할 수 있고 이것은 부모 객체인 프로토타입을 동적으로 변경할 수 있다는 것을 의미한다.
프로토타입을 교체하면 constructor 프로퍼티와 생성자 함수 간의 연결이 파괴된다. 프로토타입으로 교체한 객체 리터럴에 constructor 프로퍼티를 추가해 되살릴 수 있다.
프로토타입은 생성자 함수의 prototype 프로퍼티 뿐만 아니라 인스턴스의 __proto__
접근자 프로퍼티(또는 Object.getPrototypeOf
)를 통해 접근할 수 있다.
생성자 함수의 prototype 프로퍼티에 다른 객체를 바인딩하는 것은 미래에 생성할 인스턴스의 프로토타입을 교체하는 것이다.
마찬가지로 프로토타입으로 교체한 객체에는 constructior 프로퍼티가 없으므로 생성자 함수와의 연결이 파괴된다.
프로토타입 교체를 통해 객체 간의 상속 관계를 동적으로 변경하는 것은 번거롭기 때문에 직접 교체하지 않는 것이 좋다.
instanceof 연산자는 이항 연산자로서 좌변에 객체를 가리키는 식별자, 우변에 생성자 함수를 가리키는 식별자를 피연산자로 받는다.
우변의 생성자 함수의 prototype에 바인딩된 객체가 좌변의 객체의 프로토타입 체인 상에 존재하면 true, 그렇지 않으면 false로 평가된다.
instanceof 연산자는 프로토타입의 constructor 프로퍼티가 가리키는 생성자 함수를 찾는 것이 아니라 생성자 함수의 prototype에 바인딩된 객체가 프로토타입 체인 상에 존재하는지 확인한다.
생성자 함수에 의해 프로토타입이 교체되어 constructor 프로퍼티와 생성자 함수 간의 연결이 파괴되어도 생성자 함수의 prototype 프로퍼티와 프로토타입 간의 연결은 파괴되지 않으므로 instanceof는 영향을 받지 않는다.
Object.create 메서드는 명시적으로 프로토타입을 지정해 새로운 객체를 생성한다. 이 메서드도 다른 객체 생성 방식과 마찬가지로 추상 연산 OrdinaryObjectCreate를 호출한다.
Object.create 메서드의 첫 번째 매개변수로 전달한 객체의 프로토타입 체인에 속하는 객체를 생성한다. 즉, 객체를 생성하면서 직접적으로 상속을 구현한다.
Object.prototype의 빌트인 메서드인 Object.prototype.hasOwnProperty, Object.prototype.isPrototypeOf, Object.prototype.propertyIsEnumerable 등은 모든 객체의 프로토타입 체인의 종점인 Object.prototype의 메서드이므로 모든 객체가 상속받아 호출할 수 있다.
__proto__
에 의한 직접 상속Object.create 메서드의 두 번째 인자로 프로퍼티를 정의하는 것은 번거로운데 ES6에서는 객체 리터럴 내부에서 __proto__
접근자 프로퍼티를 사용해 직접 상속을 구현할 수 있다.
정적 프로퍼티/메서드는 생성자 함수로 인스턴스를 생성하지 않아도 참조/호출할 수 있는 프로퍼티/메서드를 말한다.
정적 프로퍼티/메서드는 생성자 함수가 생성한 인스턴스로 참조/호출할 수 없다.
Object.create 메서드는 Object 생성자 함수의 정적 메서드고 Object.prototype.hasOwnProperty 메서드는 Object.prototype의 메서드이다. 즉 Object.create 메서드는 인스턴스로 호출할 수 없다. 하지만 Object.prototype.hasOwnProperty 메서드는 Object.prototype의 메서드이므로 모든 객체가 호출할 수 있다.
만약 인스턴스/프로토타입 메서드 내에서 this를 사용하지 않는다면 그 메서드는 정적 메서드로 변경할 수 있다. 인스턴스가 호출한 인스턴스/프로토타입 메서드 내에서 this는 인스턴스를 가리킨다. 메서드 내에서 인스턴스를 참조할 필요가 없다면 정적 메서드로 변경해도 동작한다.
in 연산자는 객체 내에서 특정 프로퍼티의 존재 여부를 확인한다.
in 연산자는 확인 대상 객체의 프로퍼티 뿐만 아니라 확인 대상 객체가 상속한 모든 프로토타입의 프로퍼티를 확인한다.
in 연산자 대신 ES6에서 도입된 Reflect.has
메서드를 사용할 수도 있다.
19.13.2 Object.prototype.hasOwnProperty 메서드를 사용해도 객체에 특정 프로퍼티가 존재하는지 확인할 수 있다.
인수로 전달받은 프로퍼티 키가 객체 고유의 프로퍼티 키인 경우에만 true를 반환하고, 상속받은 프로토타입의 프로퍼티 키인 경우에는 false를 반환한다.
for...in 문은 객체의 프로퍼티 객체만큼 순회하며 for...in 문의 변수 선언문에서 선언한 변수에 프로퍼티 키를 할당한다.
for...in 문은 객체의 프로토타입 체인 상에 존재하는 모든 프로토타입 프로퍼티 중에서 프로퍼티 어트리뷰트 [[Enumerable]]의 값이 true인 프로퍼티를 순회하며 열거한다.
배열에는 for...in 문을 사용하지 않고 for 문이나 for...of 문, Array.prototype.forEach 메서드를 권장한다.
for...in 문은 객체 자신의 고유 프로퍼티 뿐만 아니라 상속받은 프로퍼티도 열거하기 때문에 Object.prototype.hasOwnProperty
메서드를 사용해 객체 자신의 프로퍼티인지 확인하는 추가 처리가 필요하다.
Object.keys 메서드는 객체 자신의 열거 가능한 프로퍼티 키를 배열로 반환한다.
ES8에서 도입된 Object.values 메서드는 객체 자신의 열거 가능한 프로퍼티 값을 배열로 반환하고,
Obejct.entries 메서드는 객체 자신의 열거 가능한 프로퍼티 키와 값의 쌍의 배열을 배열에 담아 반환한다.
20장 strict mode
strict mode는 자바스크립트 언어의 문법을 더 엄격히 적용해 오류 가능성이 높거나 최적화 작업에 문자를 일으킬 수 있는 코드에 대해 명시적 에러를 발생시킨다.
ESLint를 사용하면 strict mode가 제한하는 오류는 물론 코딩 컨벤션을 설정 파일 형태로 정의하고 강제할 수 있어 더욱 강력하다.
전역의 선두 또는 함수 몸체의 선두에 'use strict';를 추가한다.
외부 서드파티 라이브러리를 사용하는 경우 라이브러리가 non-strict mode인 경우도 있기 때문에 전역에 strict mode를 적용하는 것은 바람직하지 않다.
어떤 함수는 strict mode를 적용하고 어떤 함수는 적용하지 않는 것은 바람직하지 않고 모든 함수에 일일이 strict mode를 적용하는 것은 번거롭다.
선언하지 않은 변수를 참조하면 ReferenceError가 발생한다.
delete 연산자로 변수, 함수, 매개변수를 삭제하면 SyntaxError가 발생한다.
중복된 매개변수 이름을 사용하면 SyntaxError가 발생한다.
with 문을 사용하면 SyntaxError가 발생한다.
strict mode에서 함수를 일반 함수로서 호출하면 this에 undefined가 바인딩된다.
strict mode에서는 매개변수에 전달된 인수를 재할당해 변경해도 arguments 객체에 반영되지 않는다.
21장 빌트인 객체
Math, Reflect, JSON을 제외한 표준 빌트인 객체는 모두 인스턴스를 생성할 수 있는 생성자 함수 객체이다.
문자열, 숫자, 불리언 값에 대해 객체처럼 접근하면 생성되는 임시 객체를 래퍼 객체라고 한다.
전역 객체는 코드가 실행되기 전 단계에 자바스크립트 엔진에 의해 어떤 객체보다도 먼저 생성되는 특수 객체이고 최상위 객체이다.
전역 객체는 개발자가 의도적으로 생성할 수 없다. 즉, 전역 객체를 생성할 수 있는 생성자 함수가 제공되지 않는다.
전역 객체의 프로퍼티를 참조할 때 window는 생략할 수 있다.
빌트인 전역 프로퍼티는 전역 객체의 프로퍼티를 의미한다.
빌트인 전역 함수는 애플리케이션 전역에서 호출할 수 있는 빌트인 함수로서 전역 객체의 메서드이다.
22장 this
메서드가 자신이 속한 객체의 프로퍼티를 참조하려면 먼저 자신이 속한 객체를 가리키는 식별자를 참조할 수 있어야 한다.
this는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수이다.
this 바인딩은 함수가 어떻게 호출되었는지에 따라 동적으로 결정된다.
기본적으로 this에는 전역 객체가 바인딩된다.
메서드 내부의 this에는 메서드를 호출한 객체, 즉 메서드를 호출할 때 메서드 이름 앞의 마침표 연산자 앞에 기술한 객체가 바인딩된다.
생성자 함수 내부의 this에는 생성자 함수가 생성할 인스턴스가 바인딩된다.
apply, call 메서드의 본질적 기능은 함수를 호출하는 것이다. 이 메서드는 함수를 호출하면서 첫 번째 인수로 전달한 특정 객체를 호출한 함수의 this에 바인딩된다.
bind 메서드는 메서드의 this와 메서드 내부의 중첩 함수 또는 콜백 함수의 this가 불일치하는 문제를 해결하기 위해 사용된다.