Bleeding edge

2022/08/16 - TIL 본문

ConnecTo

2022/08/16 - TIL

codevil 2022. 8. 16. 19:06

16장 내부 슬롯과 내부 매서드

16.1 내부 슬롯과 내부 메서드

내부 슬롯과 내부 메서드는 자바스크립트 엔진의 구현 알고리즘을 설명하기위해 사용하는 의사 프로퍼티와 메서드이다.

예시 ([[…]])

원칙 : 내부 슬롯과 내부 메서드는, 개발자가 직접 접근할 수 있도록 공개된 객체 프로퍼티는 아니다. 단, 일부 내부 슬롯과 내부 매서드에 한하여 직접적으로 접근할 수 있는 수단을 제공하기는 한다. 내부 슬롯은 원칙적으로 접근할 수 없지만, [[ProtoType]] 내부 슬롯의 경우, proto 를 통해 간접적으로 접근할 수 있다

const o = {};
o.[[Prototype]];
o.__proto__ //Object.prototype

16.2 프로퍼티 어트리뷰트와 프로퍼티 디스크립터 객체

자바스크립트 엔진은 프로퍼티를 생성할 때 프로퍼티 상태를 나타내는 프로퍼티 어트리뷰트를 기본값으로 자동 정의한다.

	프로퍼티의 상태 :[값, 값의 갱신 여부, 열거 가능 여부, 정의 가능]

Object.getOwnPropertyDescriptor 메서드를 사용하여 간접적으로 확인할 수는 있다.

16.3 데이터 프로퍼티와 접근자 프로퍼티

데이터 프로퍼티 : 키와 값으로 구성된 일반적인 프로퍼티다. 지금까지 살펴본 모든 프로퍼티는 데이터 프로퍼티다.

접근자 프로퍼티 : 자체적으로는 값을 갖지 않고 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 호출되는 접근자 함수로 구성된 프로퍼티다.

16.3.1 데이터 프로퍼티

[[Value]] : 값을 반환한다, 
[[Writable]] : 읽기 가능 여부를 정한다, false일 때 읽기 전용 프로퍼티가 된다, 
[[Enumerable]] : Object.keys 매서드 등으로 열거할 수 없다, 
[[Configurable]] : false인 경우 해당 프로퍼티의 삭제, 프로퍼티 값의 변경이 금지된다.
단 Writable이 true인 경우 [[value]]의 변경과 [[Writable]]을 false로 변경하는 것이
허용된다

프로퍼티가 생성될 때, Writable, Enumerable, Configurable의 값이 true가 된다. (value는 처음에 설정한 값이 된다)

16.3.2 접근자 프로퍼티

다른 데이터 프로퍼티의 값을 읽거나 저장할 때 사용하는 접근자 함수로 구성된 프로퍼티다.

[[Get]], [[Set]], [[Enumerable]], [[Configurable]]

접근자 프로퍼티는 자체적으로 값을 가지지 않으며, 데이터의 값을 읽거나 저장할떄만 관여한다

프로토 타입 : 하위 객체에게 프로퍼티와 매서드를 상속한다.

프로토타입 체인 : 프로토타입이 단방향 링크드 리스트 형태로 연결되어있는 상속 구조를 말한다.

16.4 프로퍼티 정의

새로운 프로퍼티를 추가하면서 프로퍼티 어트리뷰트를 명시적으로 정의하거나, 기존 프로퍼티의 프로퍼티 어트리뷰트를 재정의하는 것을 말한다.

const person : {};
Object.defineProperty(person, 'firstName, {
	value : 'Ungmo',
	writable : true,
	enumerable : true,
	configurable : true
})

console.log('firstName', descriptor);
//firstName {value : 'Ungmo',	writable : true, enumerable : true,	configurable : true}

delete person.lastName;

16.5 객체 변경 방지

Object.preventExtensions : 프로퍼티 추가만 금지

Object.seal : 프로퍼티 추가, 프로퍼티 삭제 금지(읽기, 쓰기만 가능하다)

Object.freeze : 프로퍼티 추가, 프로퍼티 삭제, 프로퍼티 값 쓰기,

프로퍼티 어트 리뷰트 재정의 금지(읽기만 가능하다)

Object.isFrozen :

Object.isFrozen은 중첩 객체까지 동결하지 못한다

console.log(Object.isFrozen(person)); //true
console.log(Object.isFrozen(person.address)); //false
deepFreeze(person);

17장 생성자 함수에 의한 객체 생성

17.1 Object 생성자 함수

const person = new Object();

생성자 함수란 new 연산자와 함께 호출하여 객체 인스턴스를 생성하는 함수를 말한다.

생성자 함수에 의해 생성된 객체를 인스턴스라 한다.

Object외에도, String, Number, Boolean, Function, Array, Date, RegExp, Promise등의 빌트인 생성자 함수를 제공한다

17.2 생성자 함수

17.2.1 객체 리터럴에 의한 객체 생성 방식의 문제점

객체 리터럴에 의한 객체 생성 방정식은 직관적이고 간편하다. 하지만 객체 리터럴에 의한 객체 생성 방정식은 단 하나의 객체만 생성한다.

→ 동일한 프로퍼티를 갖는 객체를 생산해야하는 경우에는 비효율적이다

const circle= {
	radius : 5,
	getDiameter(){
		return 2*this.radius;
	}
}
const circle2= {
	radius : 10,
	getDiameter(){
		return 2*this.radius;
	}
}
console.log(circle.getDiameter, circle2.getDiameter) // 10, 20

17.2.2 생성자 함수에 의한 객체 생성 방식의 장점(문제점 개선)

생성자 함수에 의한 객체 생성 방식은 템플릿(클래스)처럼 동일한 구조의 프로퍼티를 여러개 생성할 수 있다.

function Circle(radius){
	this.radius = radius;
	this.getDiameter = function(){
		return 2* this.radius;
	}
	//return이 없다
}
const circle1 = new Circle(5);
const circle2 = new Circle(10);
//new 연산자와 함께 호출하면 해당 함수는 생성자 함수로 동작한다
console.log(circle1.getDiameter());
console.log(circle2.getDiameter());

→ 생성자 함수로 호출할 때의 this

일반 함수로서 호출 전역 객체

매서드로서 호출 메서드를 호출한 객체(마침표 앞의 객체)
생성자 함수로써 호출 생성자 함수가 생성할 인스턴스
  • 바인딩 : 식별자와 값을 연결하는 과정을 의미한다. 생성자 함수는 인스턴스와 this가 바인딩한다.

주의 : new 연산자 없이 호출하면 일반함수로 동작한다. new를 꼭 붙일 것. 일반 함수로 호출된 생성자 함수 프로퍼티(의 모양을 한 일반함수)의 this는 전역 객체이다.

17.2.3 생성자 함수의 인스턴스 생성 과정

function CircleCase1(radius){
	console.log(this);
	//1. 암묵적으로 인스턴스가 생성되고 this에 바인딩된다.
	this.radius = radius;
	//2. this에 바인딩되어 있는 인스턴스를 초기화한다.
	this.getDiameter = function(){
		return 2 * radius;
	}
	//3. 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환된다.
}
const circle = new CircleCase1(1);
console.log(circle); //Circle{radius:1, getDiameter:f;};

function CircleCase2(radius){
	console.log(this);
	//1. 암묵적으로 인스턴스가 생성되고 this에 바인딩된다.
	this.radius = radius;
	//2. this에 바인딩되어 있는 인스턴스를 초기화한다.
	this.getDiameter = function(){
		return 2 * radius;
	}
return {}
	//3. 암묵적으로 this를 반환한다
  // 명시적으로 객체를 반환하면 암묵적인 this 반환이 무시된다.
}
const circle2 = new CircleCase2(2);
console.log(circle2)//Circle {radius:1, getDiameter:f}
//return이 this가 아닌 경우에는, 
//생성자 함수의 기본 동작을 훼손하기에 return은 반드시 생략해야한다

17.2.4/~17.2.16 내부 메서드 [[Call]]과 [[Construct]]

함수는 개체이므로 일반 객체와 동일하게 동작할 수 있다.

함수 객체는 일반 객체가 가지고 있는 내부 슬롯과 내부 메서드를 모두 가지고 있다.

함수는 객체이지만 일반 객체와는 다르다. 일반 객체는 호출할 수 없지만 함수는 호출할 수 있다. 때문에, Environment, FormalParameters 등의 내부 슬롯과 Construct같은 내부 메서드를 추가로 가지고 있다.

함수가 일반 함수로서 호출되면 함수 객체의 내부 메서드 Call이 호출되고 new 연산자와 함꼐 생성자 함수로서 호출되면 내부 메서드 Construct가 호출된다

→ Call을 갖는 함수 객체를 callable. Construct를 갖는 함수를 constructor, Construct를 가지고 있지 않는 객체를 non-constructor라고 부른다.

constructor : 함수 선언문, 함수 표현식, 클래스(클래스도 함수다)

non-constructor : 메서드 (ES6 메서드 축약 표현), 화살표 함수

//1. Function declaration
function function_declaration(){}
//2. Function expression
const function_expression = function() {}
//3. Function in Object
//프로퍼티에 있는 일반함수는 메서드로 인정되지 않는다.
const ObjectOfFunction = {
	keyOfFunction : function(){}
}
/////////////////////////////////////////////////////
new function_declaration(); // -> function_declaration {}
new function_expression(); // -> function_expression {}
new ObjectOfFunction.keyOfFunctio(); // -> ObjectOfFunction{}
/////////////////////////////////////////////////////
//4. Arrow function
const arrow_function = () => {};
//5. Method in Object
const ObjectOfMethod = {
	MethodName(){}	
};
new arrow_function(); //TypeError : arrow is not a constructor
new ObjeectOfMethod.MethodName(); // TypeError : ObjectOfMethod.MethodName 
																	// is not a constructor

위의 코드처럼 차이가 나는 이유는, 내부 메서드 Constructor가 있으면 , new로 호출이 가능하고 없다면, 내부 메서드 Call로 써 호출이 되기 때문이다. 둘의 차이는 this에서도 차이가 난다. Constructor의 경우 그 객체 자체에 바인딩되며, 일반 함수의 경우 전역 객체 window에 바인딩된다.

17.2.6 new.target

new 연산자 없이 호출이 되는 것을 방지하기 위해, ES6에서는 new.target을 지원한다.(주의 IE에서는 지원하지 않는다)

function Circle(radius){
	if(!new.target){
	//new 연산자와 함께 호출되지 않았다면
	return newCircle(radius);
	//new 연산자와 함께 생성자 함수를 재귀 호출하여 생성된 인스턴스를 반환한다.
	}

	this.radius = radius;
	this.getDiameter = function(){
		return 2 * this.radius;
	}
}

아래에는

function Circle(radius){
	if(!this instanceof Circle){
	//new 연산자와 함께 호출되지 않았다면
	return newCircle(radius);
	//new 연산자와 함께 생성자 함수를 재귀 호출하여 생성된 인스턴스를 반환한다.
	}

	this.radius = radius;
	this.getDiameter = function(){
		return 2 * this.radius;
	}
}

참고 : Object, String, Number, Boolean, Function Array, Date, RegExp, Promise 모두 new 연산자와 함께 호출되었는지 확인한 후에 적절한 값을 반환한다.

const str = new String(123);
const num = new Number(123);
const boo = new Boolean(1);

String, Number, Boolean 생성자 함수 모두 new 연산자로 호출하면 객체를 생성하여 반환한다

18장 함수와 일급 객체

18.1 일급 객체

일급 객체의 조건

  1. 무명의 리터럴로 생성할 수 있다. 즉 런타임에 생성이 가능하다
  2. 변수나 자료 구조(객체, 배열 등)에 저장할 수 있다.
  3. 함수의 매개변수에 전달할 수 있다
  4. 함수의 반환값으로 사용할 수 있다
//1. 무명의 리터럴로 생성할 수 있다. 런타임에 생성이 가능하다.
//2. 변수나 자료구조(객체, 배열 등)에 저장할 수 있다
//2-1. 변수에 저장되고 변수에 할당된다.
const increase = function (num){return ++num;}
const decrease = function (num){return --num;}
//2-2. 함수는 객체에 저장될 수 있다
const list = {increase, decrease}
function makeCount(aux){
	let num = 0;
	return function (){
		num = aux(num);
//3. 함수의 반환값으로 사용할 수 있다
		return num;
	}
}
//4. 함수의 매개변수에 함수를 전달할 수 있다.
const increaser = makeCount(list.increase);

자바스크립트의 함수는 다음 예제와 같이 위의 조건을 모두 만족하므로 일급 객체이다.

함수가 일급객체라는 것은 객체와 동일하게 사용할 수 있다는 것이지만, 호출이 가능하다는 차이점이 있다.

18.2 함수 객체의 프로퍼티

함수는 객체기 때문에 프로퍼티도 가질 수 있다.

function square(number){
  return number *number;
}
console.dir(square)//f square()
console.log(Object.getOwnPropertyDescriptors(square)) //{length :... , name:..., arguments:..., }
console.log(Object.getOwnPropertyDescriptor(square, '__proto__')) //undefined
//Object prototype으로부터, 접근자 프로퍼티를 상속받는다
console.log(Object.getOwnPropertyDescriptor(Object.prototype, '__proto__')) 
//{get:f, set:f, enumerable:false, counfigurable:true}

arguments, caller, length, name, prototype 프로퍼티는 모두 함수 객체의 데이터 프로퍼티다.

이 프로퍼티는 일반 객체에는 없는 함수 객체 고유의 프로퍼티다.

하지만 proto는 접근자 프로퍼티여서, Object.prototype 객체의 프로퍼티를 상속받을 수 있다. (모든 객체가 이를 받을 수 있다)

18.2.1 arguments 프로퍼티

function multiply(x, y){
	console.log(arguments);
	return x*y;
}
console.log(multiply())//NaN
console.log(multiply(1))//NaN
console.log(multiply(1,2))//2
console.log(multiply(1,2,3))//2

매개 변수가 부족하면 undefined로 초기화 하여 값을 넣고, 값이 남으면 arguments에 보관한다.(버리지 않는다!)

주의 : 유사 배열 객체는 배열이 아니기 때문에 배열 메서드를 사용할 경우 에러가 발생한다. 배열 메서드를 사용하려면 Function.prototype.apply/call/blind같은 것을 간접 호출해야 한다

18.2.3 length 프로퍼티

함수를 정의할 때 선언한 매개 변수의 갯수를 가리킨다

18.2.4 name 프로퍼티

ES5와 ES6는 다르게 동작하니 주의 할 것. 익명 함수 표현식의 경우 ES5에서 name 프로퍼티는 빈 문자열을 가지고 ES6에서는 함수 객체를 가리키는 식별자를 값으로 갖는다.

→ 함수 이름과 함수 객체를 가리키는 식별자는 의미가 다르다

18.2.5 proto 접근자 프로퍼티

prototype 내부 슬롯은 객체지향 프로그램의 상속을 구현하는 프로토타입 객체를 가진다. Prototype 내부 슬롯에는 직접 접근이 불가능하며 proto 을 통해 간접적으로만 접근할 수 있다.

18.2.6 prototype 프로퍼티

prototype 프로퍼티는 생성자 함수로 호출할 수 있는 함수 객체만이 소유하고 있는 프로퍼티이다.

'ConnecTo' 카테고리의 다른 글

2022/08/18 - TIL  (0) 2022.08.18
2022/08/17 - TIL  (0) 2022.08.17
2022/08/12 - TIL  (0) 2022.08.12
2022/08/11 - TIL  (0) 2022.08.11
2022/08/10 - TIL  (0) 2022.08.10