오늘 배운 것
let, const와 블록 레벨 스코프
1 | // 전역 변수 |
window는 전역 객체면서 window 자체가 전역 스코프이다.
프로퍼티 어트리뷰트
프로퍼티 어트리뷰트는 프로퍼티의 상태를 나타낸다. 프로퍼티 상태는 프로퍼티의 값(value), 값의 갱신 가능 여부(writable), 열거 가능 여부(enumerable), 재정의 가능 여부(configurable)를 말한다.
프로퍼티 어트리뷰트는 자바스크립트 엔진이 관리하는 내부 상태가 값은 내부 슬롯이다. 또한 내부 상태 값이 메서드로 되어있는 것은 내부 메서드( [[Environment]]
, [[Prototype]]
)이다.
내부 슬롯(internal slot)
내부 슬롯( [[Value]]
, [[Writable]]
, [[Enumerable]]
, [[Configurable]]
)은 기본적으로 은닉되어있지만 사용이 필요할 경우 간접적으로 접근할 수 있다.
1 | const o = { |
접근자 프로퍼티 ([[Get]]
, [[Set]]
)를 잘 이용하는 것이 중요하다. 함수의 경우 호출할 때 인수가 필요할 수도 있기 때문에, 인수가 필요없는 접근자 프로퍼티가 유용하고 간편할 때가 있다.
접근자 프로퍼티는 자체적으로 갖는 값과 내부슬롯이 없다. 접근자 프로퍼티는 다른 데이터 프로퍼티의 값을 조작한다.
1 | const circle = { |
생성자 함수에 의한 객체 생성
객체 리터럴 말고 생성자 함수에 의해서도 객체를 생성할 수 있다.
생성자 함수(Contruct) = 객체를 생성하는 함수
생성자 함수 이름, Class는 파스칼 케이스로 네이밍한다.
객체 리터럴은 하나의 객체를 만들 때는 유용하지만 같은 형식의 객체를 여러개 만들 때는 생성자 함수가 유리하다.
1 | function Circle(radius) { |
객체 Circle의 프로퍼티 radius는 현재 상태를 말한다. 따라서 각각의 Circle은 radius가 각각 다를 수 있으며 중복되는 것이 아니다. 하지만 객체 Circle의 메서드는 모든 Circle 객체에서 동일하며 중복된다. 메서드 중복을 방지하는 것은 프로토타입을 알아야 가능하다.
생성자 함수를 파스칼 케이스 사용하여 구별하는 이유가 new를 붙여서 호출하라는 뜻이기도하다. (방어코드도 생성하는 것이 좋음 new.target 등)
생성사 함수로서 호출되면 맨 위에 this에 빈객체가 바인딩되고 동적으로 프로퍼티가 추가된다.
1 | function Circle(radius) { |
this 바인딩은 함수 호출 방식에 따라 동적으로 결정된다.
1 | function foo() { |
내부 메서드 [[Call]]과 [[Construct]]
new 연산자와 함께 함수를 호출하면 해당 함수는 생성자 함수로 동작하며 함수 객체의 내부 메서드 [[Call]]
이 호출되는 것이 아니라 [[Construct]]
가 호출된다.
- constructor: 함수 선언문, 함수 표현식, 클래스(클래스도 함수다)
- non-constructor: 메서드(ES6 메서드 축약 표현), 화살표 함수
1 | const o = { |
1 | // 화살표 함수 정의 |
new.target
new 연산자 없이 일반 함수로서 호출된 함수 내부의 new.target은 undefined다.
1 | function foo() { |
undefined값이 false인 것을 이용한다.
1 | function foo() { |
1 | // 생성자 함수 |
아래 예를 보면 둘 다 생성자 함수로 동작한다. 즉, Object 생성자 함수는 new.target과 같은 행위를 자동으로 한다.
1 | let obj = new Object(); |
함수와 일급 객체
일급 객체
함수형 프로그래밍이 가능하려면 함수가 일급 객체여야한다.
무명의 리터럴로 생성할 수 있다. 즉, 런타임에 생성이 가능하다.
1
2
3
4
5
6function foo(f) {
f();
}
// 런타임에 함수 만들어서 인수로 함수를 넘김
foo(function () {});변수나 자료구조(객체, 배열 등)에 저장할 수 있다.
함수의 매개변수에게 전달할 수 있다.
함수의 반환값으로 사용할 수 있다.
함수 객체의 프로퍼티
arguments, caller, length, name, prototype 프로퍼티는 모두 함수 객체의 데이터 프로퍼티다. 이들 프로퍼티는 일반 객체에는 없는 함수 객체 고유의 프로퍼티다. 하지만 proto는 접근자 프로퍼티이며, 함수 객체 고유의 프로퍼티가 아니라 Object.prototype 객체의 프로퍼티를 상속받은 것이다. Object.prototype 객체의 프로퍼티는 모든 객체가 상속받아 사용할 수 있다. 즉, Object.prototype 객체의 __**proto__**
접근자 프로퍼티는 모든 객체가 사용할 수 있다.
arguments
arguments는 유사배열 객체이다.
1 | const sum = function () { |
caller 프로퍼티
함수 객체의 caller 프로퍼티는 함수 자신을 호출한 함수를 가리킨다.
에러처리할 때 유용하다.
1 | function foo(func) { |
length 프로퍼티
함수 객체의 length 프로퍼티는 함수를 정의할 때 선언한 매개변수의 개수를 가리킨다.
arguments 객체의 length 프로퍼티와 함수 객체의 length 프로퍼티의 값은 다를 수 있으므로 주의해야 한다. arguments 객체의 length 프로퍼티는 인자(argument)의 개수를 가리키고, 함수 객체의 length 프로퍼티는 매개변수(parameter)의 개수를 가리킨다.
name 프로퍼티
함수 객체의 name 프로퍼티는 함수 이름을 나타낸다. name 프로퍼티는 ES6 이전까지는 비표준이었다가 ES6에서 정식 표준이 되었다.
1 | // 기명 함수 표현식 |
prototype 프로퍼티
prototype 프로퍼티는 생성자 함수로 호출할 수 있는 함수 객체, 즉 constructor만이 소유하는 프로퍼티다.
생성자 함수가 생성될 때 prototype 프로퍼티를 가지고 생성된다. 처음 prototype에는 constructor 프로퍼티만을 가지며 이 프로퍼티는 함수 객체를 가리킨다. new로 인스턴스를 생성하면 새로운 인스턴스는 부모격인 생성자 함수의 prototype과 연결된다.
모든 객체는 자신의 부모 역할을 하는 prototype객체와 연결된다.
1 | function Person(name) { |
prototype 프로퍼티는 함수가 객체를 생성하는 생성자 함수로 호출될 때 생성자 함수가 생성할 인스턴스의 프로토타입 객체를 가리킨다.
sayHi 함수는 계속 중복되어 생성된다. 이 함수를 prototype의 프로퍼티로 넣으면 중복 생성하지 않아도 된다.
1 | function Person(name) { |
Person.prototype은 후에 생성할 인스턴스에 필요한 것들을 위한 프로퍼티다.
Person을 가지고 prototype을 찾아갈 수 있다. 인스턴스의 [[Prototype]]
을 간접적으로 이용해서 Person.prototype을 찾아갈 수 있다.( __proto__
접근자 프로퍼티)
prototype chain은 scope chain과 비슷한 방식으로 프로퍼티를 찾는 연결된 순서이다.