내부 슬롯과 내부 메서드는 자바스크립트 엔진이 구현 알고리즘을 설명하기 위해 ECMAScript 사양에서 사용하는 의사 프로퍼티와 의사 메서드이다.
내부 슬롯과 내부 메서드는 이중 대괄호([[...]])로 감싼 이름들이 모두 내부 슬롯과 내부 메서드이다.
개발자가 직접 접근할 수 없게 설계되었으며 일부 몇개의 내부 슬롯과 내부 메서드에 간접적으로 접근할 수 있는 수단이 존재한다.
const o = {};
o.[[Prototype]] // Uncaught SyntaxError
o.__proto__ // Object.prototype
모든 객체는 [[Prototype]] 이라는 내부 슬롯을 갖는데 이 슬롯의 경우 proto 를 통해 간접적으로 접근이 가능하다
JS엔진은 프로퍼티를 생성할 때, 프로퍼티의 상태를 나타내는 프로퍼티 어트리뷰트를 기본값으로 자동 정의한다.
프로퍼티 어트리뷰트: 내부 상태 값인 내부 슬롯 [[Value]], [[Writable]], [[Enumerable]], [[Configurable]] 이다.
Object.getownpropertyDescriptor 메서드를 사용하여 간접적으로 확인이 가능하다.
const person = {
name: 'Lee'
};
// 프로퍼티 어트리뷰트의 정보를 제공하는 프로퍼티 디스크립터 객체를 반환한다.
console.log(Object.getOwnPropertyDescriptor(person, 'name'));
// {value: "Lee", writable: true, enumerable: true, configurable: true}
위 메서드를 사용하게 되면 프로퍼티 디스크립터 객체를 반환한다. 이는 프로퍼티 어트리뷰트 정보를 제공한다. 존재하지 않거나 상속받은 프로퍼티에 대한 디스크립터 요구시 undefinedd가 반환된다.
메서드 사용법
// 하나의 프로퍼티에 대한 프로퍼티 디스크립터 객체 반환
Object.getOwnPropertyDescriptor(person, 'name');
// 모든 프로퍼티에 대한 프로퍼티 디스크립터 객체 반환 (ES8에서 도입)
Object.getOwnPropertyDescriptors(person);
데이터 프로퍼티: 키와 값으로 구성된 일반적인 프로퍼티
접근자 프로퍼티: 자체적으로 값을 갖지 않고 다른 데이터 프로퍼티의 값을 읽거나 저장할때 호출되는 접근자 함수로 구성된 프로퍼티
자체적으로 값을 갖지 않고 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 사용하는 접근자 함수로 구성된 프로퍼티이다.
프로토타입은 어떤 객체의 상위 객체의 역할을 하는 객체이다. 이 프로토타입 객체의 프로퍼티나 메서드를 상속받은 하위 객체는 자신의 프로퍼티 또는 메서드인 것 처럼 자유롭게 사용이 가능하다.
프로토타입 체인은 프로토타입이 단방향 링크드 리스트 형태로 연결되어 있는 상속 구조이다. 객체의 프로퍼티나 메서드에 접근하려고 할 때 해당 객채에 접근하려는 프로퍼티 또는 메서드가 없다면 프로토타입 체인을 따라 차례대로 검색한다.
// 일반 객체의 `__proto__`는 접근자 프로퍼티
Object.getOwnpropertyDescriptor(Object.prototype, '__proto__');
// get, set enumerable, configurable
// 함수 객체의 prototype은 데이터 프로퍼티
Object.getOwnpropertyDescriptor(function() {}, 'prototype');
// value, writable enumerable, configurable
새로운 프로퍼티를 추가하면서 프로퍼티 어트리뷰트를 명시적으로 정의하거나, 기존 프로퍼티의 프로퍼티 어트리뷰트를 재정의하는 것
Object.defineProperty 함수로 정의 및 재정의를 할 수 있으며 인수로 객체의 참조, 데이터 프로퍼티의 키, 프로퍼티 디스크립터 객체를 전달한다.
Object.defineProperty(person, 'name' { value: 'JeongMin', writable: true enumerable: true, configurable: true });
디스크립터의 객체의 프로퍼티를 누락시키는 경우 value, get ,set은 undefined로, writable, configurable, enumerable은 false로 기본값이 설정된다.
여러개의 프로퍼티를 한 번에 정의려면 Object.defineProperties(객체, 디스크립터 객체) 로 사용하면 된다.
확장이 금지된 객체는 프로퍼티 추가가 금지된다. --> preventExtensions(객체) 메서드
확인 메서드 --> isExtensible(객체)
읽기와 쓰기만 가능
isSealed(객체) 메서드로 seal되었는 지 확인
isFrozen(객체) 메서드로 동결되었는 지 확인.
위의 메서드들은 얕은 변경 방지 --> 중첩 객체까지 동결할 수 없다.
불변 객체 구현을 위해서는 객체를 값으로 갖는 모든 프로퍼티에 대해 재귀적으로 Object.freeze 메서드를 호출해야 한다.
function deepFreeze(target) {
if(target && typeof target === 'object' && !Object.isFrozen(target)) {
Object.freeze(target);
// 모든 프로퍼티 순회
Object.keys(target).forEach(key => deepFreeze(target[key]));
}
return target;
}
deepFreeze(객체);
깊은 객체 동결 (재귀함수로 구현)
객체 리터럴 이외에도 다양한 방법으로 생성할 수 있는데, 생성자 함수를 사용하여 객체를 생성하는 방식에 대해서 알아보고, 객체 리터럴 방식과 생성자 함수 방식의 장단점에 대해 알아보자.
const person = new Object();
생성자 함수란 new 연산자와 함게 호출하여 객체를 생성하는 함수이다.
이때 생성된 객체를 인스턴스라고 한다 (person이 인스턴스)
Object 외에도 String, Number, Boolean 등등의 생성자 함수를 제공한다.
동일한 프로퍼티를 갖는 객체를 여러 개 생성해야하는 경우 매번 같은 프로퍼티를 기술해야함.
--> circle이라는 객체 여러개
객체를 생성하기 위한 템플릿(클래스)처럼 생성자 함수를 사용하여 프로퍼티 구조가 동일한 객체 여러 개를 간편하게 생성할 수 있다.
function Circle(radius) {
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
};
}
const circle1 = new Circle(5);
const circle2 = new Circle(10);
객체 여러개를 객체 리터럴로 정의할 때보다 간편하게 생성할 수 있다. (함수 하나만 정의해 놓고 new연산자와 같이 생성자 함수로 객체 생성)
생성자 함수는 일반 함수와 동일한 방법으로 생성자 함수를 정의하고 new 연산자와 함께 호출하면 생성자 함수로 동작한다.
new연산자 같이 안쓰면 생성자 함수가 아닌 일반 함수로 동작
this는 자기 참조 변수로 this가 가리키는 값 바인딩은 함수 호출 방식에 따라 동적으로 결정된다.
일반함수로서 호출 --> this는 전역 객체를 가리킴
메서드로서 호출 --> this는 메서드를 호출한 객체를 가리킴
생성자 함수로서 호출 --> this는 생성자 함수가 생성할 인스턴스를 가리킴
생성자 함수의 역할: 인스턴스를 생성하기 위한 템플릿(클래스)으로서 동작하여 인스턴스 생성, 생성된 인스턴스를 초기화(인스턴스 프로퍼티 추가 및 초기값 할당)
함수 내부에 인스턴스 생성및 반환하는 코드가 없어도 JS엔진은 암묵적인 처리를 통해 인스턴스를 생성하고 반환한다. 그 처리과정은 아래와 같다.
인스턴스 생성과 this 바인딩 (런타임 이전에 암묵적으로 빈 객체 생성, 이 빈 객체는 인스턴스이고 this에 바인딩 된다.)
바인딩 --> 식별자와 값을 연결하는 과정 (this라는 식별자)
인스턴스 초기화 (런타임에 코드가 한줄 씩 실행되어 this에 바인딩 되어 있는 인스턴스 초기화)
인스턴스 반환 (완성된 인스턴스가 바인딩된 this가 암묵적으로 반환된다.)
3번의 과정에서 만약 다른 객체를 명시적으로 반환하면 this가 반환되지 못하고 명시한 객체가 반환되고, 원시값 반환 시 암묵적으로 this가 반환된다.
생성자 함수 내부에선 return문을 반드시 생략해야 생성자 함수의 기본 동작을 훼손시키지 않을 수 있다.
함수 선언문 또는 함수 표현식으로 정의한 함수는 생성자 함수로서 호출할 수 있다 (new와 같이 호출하여 객체 생성한다는 말)
함수는 객체이므로 일반 객체와 같이 동일하게 동작하지만 유일한 차이점은 일반객체는 호출할 수 없고, 함수는 호출이 가능하다
함수 호출 시: 함수 객체의 내부 메서드 [[Call]]이 호출 --> 이것을 가지고 있는 함수 객체는 callable
생성자 함수 호출 시: 내부 메서드 [[Construct]]가 호출 --> 이것을 가지고 있는 함수 객체 constructor, 그렇지 않은 것은 non-constructor
모든 함수 객체는 callable이지만 constructor일 수도 있고 non-constructor일 수도 있다.
객체의 메서드로 정의할 시, 메서드 축약표현이 아닌 함수형식(x: function() {})으로 정의한 것들은 일반 함수로 정의되어 constructor이다.
ECMAScript 사양에서 메서드란 ES6의 메서드 축약 표현 만을 의미한다.
new 연산자와 함께 함수를 호출하게 되면 내부 메서드 [[Call]\이 호출 되는 것이 아니라 [[Construct]]가 호출된다. 단 이 경우 함수는 non-constructor가 아닌 constructor이어야 한다.
생성자 함수는 파스칼 케이스로 명명하여 일반 함수와 구별할 수 있도록 노력하자.
new 연산자와 함께 생성자 함수로서 호출되면 함수 내부의 new.target은 함수 자신을 가리킨다. new 연산자 없이 일반함수로서 호출된 함수 내부의 new.target은 undefined다.
if (!new.target) { return new Circle(radius); }
ES6에서 도입되어 IE는 지원하지 않아서 아래의 스코프 세이프 생성자 패턴을 사용하기도 함.
if (!(this instanceof Circle)) { return new Circle(radius); }
일급 객체의 조건
함수는 객체이므로 함수또한 프로퍼티를 가질 수 있기 때문에 console.dir, Object.getOwnPropertyDescriptor로 확인해볼 수 있다.
확인 결과 함수 객체의 데이터 프로퍼티에는 arguments, caller, length, name, prototype과 같은 고유의 프로퍼티를 가지고 있음을 알 수 있다.
Object.prototype객체의 프로퍼티는 모든 객체가 상속받아 사용할 수 있기 때문에 proto 접근자 프로퍼티는 모든 객체가 사용할 수 있다.
arguments 객체는 함수 호출 시 전달된 인수들의 정보를 담고 있는 순회가능한 유사배열 객체이며 함수 내부에서 지역 변수처럼 사용된다. --> 가변 인자 함수를 구현할 때 유용
Function.arguments 처럼 사용하면 X, 지역 변수처럼 사용하라
arguments 객체는 인수를 프로퍼티 값으로 소유하고, 키는 인수의 순서를 나타낸다.
프로퍼티로 인수 외에도 callee(arugments객체를 생성한 함수), length(인수 개수)를 가지고 있다.
최종적으로는 이 arguments 객체의 번거로움을 해결하기 위해 Rest 파라미터를 도입하였다.
caller 프로퍼티는 함수 자신을 호출한 함수를 가리킨다.
ECMAScript 사양에 포함되지 않는 비표준 프로퍼티이다.
함수를 정의할 때 선언한 매개변수의 개수를 가리킨다.
length !== arguments 객체의 length
length --> 매개변수 개수
arugments length --> 인자(수)의 개수
함수 이름을 나타내고 익명함수인 경우 빈 문자열을 값으로 갖는다.
[[Prototype]] 내부 슬롯이 가리키는 프로토타입 객체에 접근하기 위해 사용하는 접근자 프로퍼티다.
모든 객체는 [[Prototype]] 이라는 내부 슬롯을 갖고 proto를 통해 접근할 수 있다.
인수로 전달받은 프로퍼티 키가 객체 고유의 프로퍼티 키인 경우에만 true이고 상속받은 프로토타입의 프로퍼티 키인 경우 false를 반환한다.
함수가 객체를 생성하는 생성자 함수로 호출될 때 생성자 함수가 생성할 인스턴스의 프로포타입 객체를 가리킨다.
생성자 함수로 호출할 수 있는 함수 객체, constructor만이 소유하는 프로퍼티이다.
일반 객체와 non-constructor 함수는 prototype 프로퍼티가 없다.
자바스크립트 --> 프로토타입 기반의 객체지향 프로그래밍 언어
JS에도 클래스를 제공하여 클래스와 생성자 함수 모두 프로토타입 기반의 인스턴스를 생성하지만 정확히 동일하게 동작하지는 않는다.
자바스크립트를 이루고 있는 거의 모든 것이 객체이며 원시 타입의 값을 제외한 나머지 값들을 모두 객체이다.
객체의 집합으로 프로그램을 표현하려는 프로그래밍 패러다임.
실체는 특징이나 성질을 나타내는 속성을 가지고 있고, 객체에 필요한 속성만 간추려 표현하는 것을 추상화 라고 한다.
속성을 통해 여러 개의 값을 하나의 단위로 구성한 복합적인 자료구조 --> 객체
객체 안에는 상태를 나타내는 데이터와 상태 데이터를 조작할 수 있는 동작이 있는데 이를 각각 프로퍼티, 메서드라고 한다.
객체란 상태 데이터와 동작을 하나의 논리적인 단위로 묶은 복합적인 자료구조이다.
자바스크립트에서의 상속은 프로토타입 기반으로 구현함
예제
// 생성자 함수
function Circle(radius) {
this.radius = radius;
}
// Circle 생성자 함수가 생성한 모든 인스턴스가 이 메서드를 사용할 수 있도록
// 프로토타입에서 관리한다.
// 프로토타입은 Circle 생성자 함수의 prototype 프로퍼티에 바인딩 되어있다.
Circle.prototype.getArea = function () {
return Math.PI * this.radius ** 2;
};
const circle1 = new Circle(1);
const circle2 = new Circle(2);
console.log(circle1.getArea === circle2.getArea); // true
console.log(circle1.getArea());
console.log(circle2.getArea());
getArea라는 함수를 상위 객체인 프로토타입으로부터 상속받아 사용하는 모습
circle1, circl2의 프로토타입은 Circle.prototype이다.(Circle 생성자 함수로부터 만들어짐)
객체 리터럴로 만들어진 객체의 프로토타입 --> Object.prototype
생성자 함수에 의해 생성된 객체의 프로토타입 --> 생성자 함수의 prototype 프로퍼티에 바인딩된 객체(생성자.prototype)
[[Prototype]]이라는 내부 슬롯에 직접 접근은 할 수 없지만 proto 라는 접근자 프로퍼티를 통해 [[Prototype]]이 가리키고 있는 프로토타입에 간접적 접근이 가능하다.
생성자 함수로 만든 인스턴스는 proto로 생성자.prototype에 접근이 가능.
생성자.prototype의 constructor 프로퍼티를 통해 생성자 함수에 접근이 가능하며, 생성자 함수는 prototype 프로퍼티를 통해 생성자.prototype에 접근이 가능하다.

이 접근자 프로퍼티로 프로토타입에 접근을 하게 되면 프로퍼티의 getter함수인 [[Get]]이 호출되고, 새로운 프로토타입을 할당하면 setter 함수인 [[Set]]이 호출된다.
proto 접근자 프로퍼티는 상속을 통해 사용된다. --> 객체가 직접 소유한 프로퍼티가 아닌 Object.prototype의 프로퍼티이다.
proto 를 통해 프로토타입에 접근하는 이유는 [[Prototype]] 에 직접 접근하여 조작하면 두 객체간 순환참조 되게 만들 어 무한루프에 빠질 수 있는데(프로토타입 체인에서 프로퍼티 검색 시), proto를 사용하면 프로토타입을 할당 시 순환참조가 되지 않게 타입 에러를 발생시킨다.
proto 접근자 프로퍼티를 직접 사용하는 것을 권장하진 않는데, 그 이유로 직접 상속을 통해 Object.prototype을 상속받지 않는 객체에서는 사용할 수 없기 때문이다. --> 대신에 Object.getPrototypeOf 메서드나 Object.setPrototypeOf 메서드 사용을 권장한다.
함수 객체의 prototype 프로퍼티
함수 객체만이 소유하는 prototype 프로퍼티는 생성자 함수가 생성할 인스턴스의 프로토타입을 가리킨다.
non-constructor인 함수는 prototype 프로퍼티를 소유하지 않는다.
function Person(name) {
this.name = name;
}
const me = new Person('Lee');
console.log(Person.prototype === me.__proto__); // true
모든 객체가 가지고 있는 proto 접근자 프로퍼티와 함수 객체만이 가지고 있는 prototype 프로퍼티는 결국 동일한 프로토타입을 가진다.
그러나 사용 주체에서 차이가 있다.
모든 프로토타입은 constructor 프로퍼티를 갖는데, 자신을 참조하고 있는 생성자 함수를 가리킨다.
생성자 함수가 아닌 리터럴로 객체가 생성되었을 때 프로토타입은 어떻게 되는 것일까?
--> 리터럴 표기법에 의해 생성된 객체도 상속을 위해 프로토타입이 필요하다. 따라서 가상적인 생성자 함수를 가지며 프로토타입과 생성자 함수는 단독으로 존재할 수 없고 언제나 쌍으로 존재한다.
프로토타입은 생성자 함수가 생성되는 시점에 더불어 생성된다.
사용자 정의 생성자 함수와 프로토타입 생성 시점
console.log(Person.prototype); // {constructor: f}
function Person(name) {
this.name = name;
}
이 예제를 보면 함수선언문이 함수 호이스팅이 일어나 런타임 이전에 JS엔진에 의해 먼저 실행되어 함수 객체로 만들어지고 위의 console.log에 값이 찍히게 된다.
이때 생성된 프로토타입은 constructor 프로퍼티만을 갖는 개체이며 자신의 프로토타입인 Object.prototype을 갖는다.
빌트인 생성자 함수도 일반 함수와 마찬가지로 빌트인 생성자 함수가 생성되는 시점에 프로토타입이 생성되고, 모든 빌트인 생성자 함수는 전역 객체가 생성되는 시점에 생성된다.
이후 생성자 함수 또는 리터럴 표기법으로 객체를 생성하면 프로토타입은 생성된 객체의 [[Prototype]] 내부 슬롯에 할당된다. --> 프로토타입 상속
프로토타입은 추상 연산 OrdinaryObjectCreate에 전달되는 인수에 의해 결정된다.
객체 리터럴의 경우 OrdinaryObjectCreate가 호출되면 여기에 Object.prototype이 인수로 전달 된다. --> 따라서 객체 리터럴의 프로토타입은 Object.prototype이다.
Object 생성자 또한 인수없이 함수를 호출하게 되면 빈 객체가 생성되고 OrdinaryObjectCreate가 호출되어 Object.prototype이 인수로 전달되기 때문에 생성된 객체의 프로토타입은 Object.prototype이다.
생성자 함수로 인스턴스 생성시 OrdinaryObjectCreate가 호출되면서 이 경우에 이수는 생성자 함수 prototype 프로퍼티에 바인딩 되어있는 객체이다. (함수.prototype)
하지만 사용자 정의 생성자 함수는 Object.prototype이 가지고 있는 빌트인 메서드가 없고 constructor프로퍼티만 가지고 있게 된다.
따라서 특정 기능 상속을 위해서는 직접 구현해주어야 한다.
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log(`Hi! My name is ${this.name}`);
};
const me = new Person('Lee');
const you = new Person('Kim');
me.sayHello();
you.sayHello();
Person 생성자 함수를 통해 생성된 인스턴스들이 프로토타입에 추가된 sayHello 메서드를 상속받아 자신의 메서드 처럼 사용하고 있다.
person이라는 생성자 함수에 의해 만들어진 person.prototype또한 프로토타입으로 Object.prototype을 가지고 있다.
따라서 자바스크립트 객체의 프로퍼티에 접근하려고 할 때 해당 객체에 접근하려는 프로퍼티가 없다면 [[Prototype]] 내부 슬롯의 참조를 따라 자신의 부모 역할을 하는 프로토타입의 프로퍼티를 순차적으로 검색한다. 이를 프로토타입 체인이라고 한다.
이 프로토타입 체인으로 JS가 객체지향 프로그래밍의 상속을 구현한다.
상속과 프로퍼티 검색을 위한 메커니즘 --> 프로토타입 체인
식별자 검색을 위한 메커니즘 --> 스코프 체인
Object.prototype을 프로토타입 체인의 종점이라 하며 Object.prototype의 프로토타입, 즉[[Prototype]] 내부 슬롯의 값은 null이다.
프로토타입으로부터 메서드를 상속받는 것이 아닌 인스턴스 자체에 같은 메서드가 오버라이딩(상위 클래스가 가지고 있는 메서드를 하위 클래스가 재정의) 되어 있어서 상속관계에 의해 프로퍼티가 가려지는 현상을 프로퍼티 섀도잉 이라고 한다.
함수 이름은 동일한데, 매개변수의 타입 또는 개수가 다른 메서드를 구현하고 매개변수에 의해 메서드를 구별하여 호출하는 방식. JS는 오버로딩 지원하지 않지만 arguments 객체를 사용해 구현 가능
메서드 삭제시 자기 자신의 메서드는 삭제가 가능하지만 프로토타입의 메서드를 삭제하기 위해서는 프로토타입에 직접 접근해야한다.
하위 객체를 통한 프로토타입 get은 허용하나 set 액세스는 허용하지 않는다.
프로토타입을 동적으로 변경할 수 있다. --> 객체 간의 상속 관계를 동적으로 변경 가능
// Person이라는 생성자 함수가 있다고 가정.
Person.prototype = {
sayHello() {
console.log(`Hi! My name is ${this.name}`);
}
};
const me = new Person('Lee');
console.log(me.constructor === Object); // true
이런 Person 생성자 함수의 프로토타입을 객체 리터럴을 할당하게 되면 원래 constructor를 가지고 있어야할 Person.prototype의 프로퍼티가 바뀌게 된다.
따라서 me 객체의 constructor는 Person.prototype의 프로토타입인 Object.prototype이 가지고 있는 constructor인 Object 이다.
Object.setPrototypeOf(me, parent); // parent는 프로토타입으로 교체할 객체
다음과 같이 setPrototypeOf 메서드를 사용하여 프로토타입을 교체하게 되면 위의 생성자 함수로 프로토타입이 교체된것 처럼 constructor가 Object를 가리킨다.
딱 하나 다른점은 Person 생성자 함수의 prototype 프로퍼티가 교체된 프로토타입을 가리키느냐 안가르키느냐에 차이가 있다. (인스턴스로 프로토타입 교체하면 안가리킴 --> 새로운 객체로 프로토타입을 교체했으니 그럴만하다.)
결론: 프로토타입 교체를 통해 상속 관계를 동적으로 변경하는 것은 번거로우니 웬만하면 프로토타입 직접 교체 하지말자.
이항 연산자로 좌변에 객체를 가리키는 식별자, 우변에 생성자 함수를 가리키는 식별자를 피연산자로 받는다.
우변의 피연산자가 함수가 아닌 경우 TypeError가 발생한다.
즉 me라는 객체의 프로토타입 체인안에 존재한다면 true이므로 Person.prototype = 다른객체; 이렇게 바꾸어도 체인 안에 존재하기 때문에 true이다.
constructor 프로퍼티와 생성자 함수간의 연결이 파괴되어도 instanceOf는 아무런 영향 X
Object.create 메서드도 추상 연산 OrdinaryObjectCreate를 호출하는데 첫 번째 매개변수로 생성할 객체의 프로토타입으로 지정할 객체와 두 번째 매개변수는 생성할 객체의 프로퍼티 키와 프로퍼티 디스크립터 객체로 이뤄진 객체를 전달한다.(두 번째는 생략가능)
이 메서드의 장점
// 프로토타입이 null인 객체, 즉 프로토타입 체인의 종점에 위치하는 객체 생성
const obj = Object.create(null);
obj.a = 1;
console.log(obj.hasOwnProperty('a')); // TypeError
console.log(Object.prototype.hasOwnProperty.call(obj,'a')); // true
다음과 같이 프로토타입 체인의 종점에 위치하는 객체가 Object.prototype의 빌트인 메서드를 직접 호출할 수 있기 때문에 객체가 직접 호출하는 것을 권장하지 않는다.
따라서 아래와 같이 Object.prototype.hasOwnProperty.call(obj, 'a')와 같은 메서드를 사용하여 호출한다.
객체 리터럴 내부에서 proto 접근자 프로퍼티를 사용하여 직접 상속을 구현할 수 있지만 깔끔한 방법은 아니다.
const myProto = { x: 10 };
// 객체 리터럴에 의해 객체를 생성하면서 프로토타입을 지정하여 직접 상속받기 가능
const obj = {
y: 20,
// obj -> myProto -> Object.prototype -> null
__proto__: myProto
};
생성자 함수로 인스턴스를 생성하지 않아도 참조/호출할 수 있는 프로퍼티/메서드를 말함.
인스턴스는 자신의 프로토타입 체인에 속한 객체의 프로퍼티/메서드만 접근할 수 있으므로 프로토타입에 속하지 않은 것은 접근 불가함
Object.create 메서드 또한 Object 생성자 함수의 정적 메서드이기 때문에 Object 생성자 함수가 생성한 객체로 호출할 수 없다.
this를 사용하지 않는다면 그 메서드는 정적 메서드로 변경할 ㅜㅅ 있다.
객체 내에 특정 프로퍼티가 존재하는 지 여부 (상속받는 모든 프로토타입의 프로퍼티 확인)
key in object
ES6에선 Reflect.has 메서드를 사용할 수도 있다.
hasOwnProperty --> 프로퍼티 키가 객체 고유의 프로퍼티 키인 경우에만 true 반환
for (변수선언문 in 객체) {...} --> 상속받은 프로토타입의 프로퍼티까지 ([[Enumerable]] 값이 true인 것만 열거 해줌)
원래 열거는 순서 보장 X이지만 모던 브라우저는 순서를 보장하고 숫자인 프로퍼티 키에 대해서는 정렬을 실시한다.
배열에는 for...in문 대신 for or for...of or forEach 메서드 사용 권장(상속받은 프로퍼티가 포함될 수 있기 때문에)
객체 고유의 프로퍼티 키만 보려면 Object.keys(객체)를 사용하고
값은 Object.values(객체)
객체 자신의 열거 가능한 프로퍼티 키와 값의 쌍의 배열 --> Object.entries(객체)
x를 선언할 때 키워드를 붙이지 않고 선언 시, 암묵적 전역과 같은 오류를 발생시킬 원인이 되는 일이 발생할 수 있는데, 이 strict mode를 통해 문법을 좀 더 엄격히 적용하여 오류를 발생시킬 가능성이 높거나 자바스크립트 엔진의 최적화 작업에 문제를 일으킬 수 있는 코드에 대해 명시적인 에러를 발생 시킨다.
--> Eslint 같은 도구 써도 됨
use strict;
위 키워드로 사용한다.
외부 서드 파티 라이브러리가 nono-strict mode인 경우도 있어서 전역에 strict mode 적용은 바람직 하지 않다. --> 즉시 실행함수 선두에 strict mode를 적용한다.
함수 일일히 적용하기 버겁고 strict mode 적용된 함수가 외부 컨텍스트에 strict mode를 적용하지 않는다면 문제가 발생할 수 있기 때문에 즉시 실행 함수로 감싼 스크립트 단위로 적용하는 것이 바람직하다.
암묵적 전역 --> 선언하지 않은 변수 참조 시 ReferenceError
변수, 함수, 매개변수의 삭제 --> delete로 삭제 시 SyntaxError
매개변수 이름의 중복 --> 중복된 매개변수 사용 시 SyntaxError
with 문 사용 --> SyntaxError
일반함수의 this --> undefined이 바인딩 됨
매개변수에 전달된 인수를 재할당 하여도 arguments 객체에 반영되지 않는다.