일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 중복 문자열
- 제로베이스
- 시퀄 문법
- 우분투 시간 변경
- indexOf()
- 5.3.8 Modifying Queries
- 객체의 밸류값만 찾기
- 레디스 확인
- 중복문자제거
- 재귀스왑
- 문자열순서바꾸기
- sql 문자열 패턴 검색
- js 문자열을 문자배열로
- 객체의키값만 찾기
- ubuntu타임존
- 배열을 객체로
- 깃 토큰 만료
- 객체를 배열로
- 문자열 중복
- 단어 제거
- ...점점점문법
- 스프링 데이타 JPA
- lastIndexOf()
- @Moditying @Query
- 프론트엔드 스쿨
- 코딩 어?
- 배엘에서 스왑
- 중복된 단어
- 중복단어제거
- sql like연산자
- Today
- Total
코딩기록
JS)모던 자바스크립트 Deep Dive 17장 생성자 함수에 의한 객체 생성 본문
17장 생성자 함수에 의한 객체 생성
17-1. Object 생성자 함수
- new 연산자와 함께 Object 생성자 함수를 호출하면 빈 객체를 생성해서 반환.
- 빈 객체를 생성한 이후 프러퍼티 또는 메서드를 추가하여 객체를 완성.
-
[예제 17-01]// 빈 객체의 생성const person = new Object();// 프로퍼티 추가person.name = 'Lee';person.sayHello = function () {console.log('Hi! My name is ' + this.name);};console.log(person); // {name: "Lee", sayHello: ƒ}person.sayHello(); // Hi! My name is Lee
- 생성자 함수란 new연산자와 함께 호출하여 객체(인스턴스)를 생성하는 함수.
- 자바스크립트는 Object 생성자 함수 이외에도 String, Number, Boolean, Function, Array, Date, RegExp, Promise 등의 빌트인 생성자 함수를 제공.
- 객체를 생성하는 방법은 객체 리터럴을 사용하는 것이 더 간편하며, Object 생성자 함수를 사용해 객체를 생성하는 방식은 특별한 이유가 없다면 그다지 유용해 보이지 않음.
-
[예제 17-02]// String 생성자 함수에 의한 String 객체 생성const strObj = new String('Lee');console.log(typeof strObj); // objectconsole.log(strObj); // String {"Lee"}// Number 생성자 함수에 의한 Number 객체 생성const numObj = new Number(123);console.log(typeof numObj); // objectconsole.log(numObj); // Number {123}// Boolean 생성자 함수에 의한 Boolean 객체 생성const boolObj = new Boolean(true);console.log(typeof boolObj); // objectconsole.log(boolObj); // Boolean {true}// Function 생성자 함수에 의한 Function 객체(함수) 생성const func = new Function('x', 'return x * x');console.log(typeof func); // functionconsole.dir(func); // ƒ anonymous(x)// Array 생성자 함수에 의한 Array 객체(배열) 생성const arr = new Array(1, 2, 3);console.log(typeof arr); // objectconsole.log(arr); // [1, 2, 3]// RegExp 생성자 함수에 의한 RegExp 객체(정규 표현식) 생성const regExp = new RegExp(/ab+c/i);console.log(typeof regExp); // objectconsole.log(regExp); // /ab+c/i// Date 생성자 함수에 의한 Date 객체 생성const date = new Date();console.log(typeof date); // objectconsole.log(date); // Mon May 04 2020 08:36:33 GMT+0900 (대한민국 표준시)
var strObj = new String('Lee'); 에서 new를 붙여 만든 것은 String 객체 [[Prototype]] 프로토타 입프로퍼티가 존재하고 이걸로 메서드들을 상속받아 쓸 수 있음.
String('Lee') -> 그냥 문자열 리터럴을 만듦.
객체 생성 방식 3가지
1. 객체 리터럴
2. 생성자 함수
3. Object() 생성자 함수 (제일 비효율적)
17-2. 생성자 함수
1). 객체 리터럴에 의한 객체 생성 방식의 문제점
- 객체 리터럴에 의한 객체 생성 방식은 직관적이고 간편 하지만 단 하나의 객체만 생성.
- 동일한 프로퍼티를 갖는 객체를 여러 개 생성해야 하는 경우 매번 같은 프로퍼티를 기술해야 하기 때문에 비효율적.
- 비슷한 코드가 중복됨.
-
[예제 17-03]const circle1 = {radius: 5,getDiameter() {return 2 * this.radius;}};console.log(circle1.getDiameter()); // 10const circle2 = {radius: 10,getDiameter() {return 2 * this.radius;}};console.log(circle2.getDiameter()); // 20
2). 생성자 함수에 의한 객체 생성 방식의 장점
- 객체(인스턴스)를 생성하기 위한 템플릿(클래스)처럼 생성자 함수를 사용하여 프로퍼티 구조가 동일한 객체(인스턴스) 여러개 를 간편하게 생성 가능.
- 생성자 함수 만들어서 인스턴스로 값만 넘겨줌.
-
[예제 17-04]// 생성자 함수function Circle(radius) {// 생성자 함수 내부의 this는 생성자 함수가 생성할 인스턴스를 가리킴.this.radius = radius;this.getDiameter = function () {return 2 * this.radius;};}// 인스턴스의 생성const circle1 = new Circle(5); // 반지름이 5인 Circle 객체를 생성const circle2 = new Circle(10); // 반지름이 10인 Circle 객체를 생성console.log(circle1.getDiameter()); // 10console.log(circle2.getDiameter()); // 20
- 일반 함수와 동일한 방법으로 생성자 함수를 정의하고 new 연산자와 함께 호출하면 해당 함수는 생성자 함수로 동작.
-
function test() { }
test(); // 일반 함수로서 호출new test(); // 생성자 함수로서 호출 - new 연산자와 함께 ⭕ 생성자 함수를 호출
- 해당 함수는 생성자 함수로 동작.
- new 연산자와 없이❌ 생성자 함수를 호출
- 생성자 함수가 아니라 일반 함수로 동작.
-
[예제 17-06]// new 연산자와 함께 호출하지 않으면 생성자 함수로 동작하지 않음.// 즉, 일반 함수로서 호출.const circle3 = Circle(15);// 일반 함수로서 호출된 Circle은 반환문이 없으므로 암묵적으로 undefined를 반환.console.log(circle3); // undefined// 일반 함수로서 호출된 Circle내의 this는 전역 객체를 가리킴.console.log(radius); // 15
- [예제 17-06] 리턴한 게 없어서 undefined를 반환.
- this는 객체 자신의 프로퍼티나 메서드를 참조하기 위한 자기 참조 변수(self-referencing variable).
- this가 가리키는 값, 즉 This 바인딩은 함수 호출 방식에 따라 동적으로 결정.
-
함수 호출 방식 this가 가리키는 값(this 바인딩) 일반 함수로서 호출 전역 객체 (브라우저 환경 - window, Node.js 환경 - global) 메서드로서 호출 메서드를 호출한 객체(마침표 앞의 객체) 생성자 함수로서 호출 생성자 함수가 생성할 인스턴스 -
[예제 17-05]// 함수는 다양한 방식으로 호출될 수 있음.function foo() {console.log(this);}// 일반적인 함수로서 호출// 전역 객체는 브라우저 환경에서는 window, Node.js 환경에서는 global을 가리킴.foo(); // window// 메서드로서 호출const obj = { foo }; // ES6 프로퍼티 축약 표현obj.foo(); // obj// 생성자 함수로서 호출const inst = new foo(); // inst
덧) 스터디에서 나온 이런저런 내용 240722월-
✨✨함수는 호출방식 3가지!!!! 강사님이 암기하라는 몇가지 안되는 것 중 하나 함수 호출방식.✨✨
this는 기본적으로 전역객체.
①일반함수, ②메서드, ③생성자 함수
①일반함수로서 호출되었을 때-
함수 내부 this는 무조건 전역, 그때 디스는 아무 의미 없음.
②메서드로서 호출되었을 때-
메서드 내 this는 점. 앞에 있는 객체.
③생성자 함수로서 호출되었을 때-
함수 내부의 this는 암묵적 빈 객체가 디스에 바인딩됨.
암묵적으로 생성된 객체가 생성자 함수가 생성한 인스턴스. 그 생성자 함수가 생성할 인스턴스.
*디스는 메서드, 생성자 함수일 때만 의미 있음. 디스는 동적으로 결정된다.
*함수의 인수 전달할 수 있는 자는 누구? 호출하는자,
3). 생성자 함수의 인스턴스 생성 과정
- 생성자 함수의 역할
- (필수 ❗) 프로퍼티 구조가 동일한 인스턴스를 생성하기 위한 템플릿(클래스)으로서 동작하여 인스턴스를 생성.
- (옵션 ❗ ) 생성된 인스턴스를 초기화(인스턴스 프로퍼티 추가 및 초기값 할당).
-
- [예제 17-07]
function Circle(radius) {// 인스턴스 초기화this.radius = radius;this.getDiameter = function () {return 2 * this.radius;};}// 인스턴스 생성const circle1 = new Circle(5); // 반지름이 5인 Circle 객체를 생성 - Circle인스턴스인 빈 객체가 만들어지고 이게 곧 this가 됨. 이 객체 { }가 반환됨.
- {
- radius: 5,
- getDiameter() {... }
- }
- [예제 17-07] 생성자 함수 내부의 코드를 살펴보면 this에 프로퍼티를 추가하고 필요에 따라 전달된 인수를 프로퍼티의 초기값으로 할당하여 인스턴스를 초기화함.
- 하지만 인스턴스를 생성하고 반환하는 코드는 보이지 않음.
- new 연산자와 함께 생성자 함수를 호출하면 자바스크립트 엔진은 아래와 같이 인스턴스를 생성하고 초기화한 후 반환하는 과정까지 암묵적으로 처리.
- 과정 1. 인스턴스 생성과 this 바인딩
- 암묵적으로 빈 객체가 생성(생성자 함수가 생성한 인스턴스, 미완성 단계임)되고 this에 바인딩됨.
- 이 이유로 생성자 함수 내부의 this가 생성자 함수가 생성할 인스턴스를 가리킴. (런타임 이전 실행).
- 바인딩(name binding)이란 식별자와 값을 연결하는 과정을 의미.
- 변수 선언 - 변수 이름(식별자)과 확보된 메모리 공간의 주소를 바인딩.
- this 바인딩 - this(키워드로 분류되지만 식별자 역할을 함)와 this가 가리킬 객체를 바인딩.
-
[예제 17-08]function Circle(radius) {// 1. 암묵적으로 인스턴스가 생성되고 this에 바인딩됨.console.log(this); // Circle {}this.radius = radius;this.getDiameter = function () {return 2 * this.radius;};}
- 바인딩(name binding)이란 식별자와 값을 연결하는 과정을 의미.
- 이 이유로 생성자 함수 내부의 this가 생성자 함수가 생성할 인스턴스를 가리킴. (런타임 이전 실행).
- 암묵적으로 빈 객체가 생성(생성자 함수가 생성한 인스턴스, 미완성 단계임)되고 this에 바인딩됨.
- 과정 2. 인스턴스 초기화
- 생성자 함수에 기술되어 있는 코드가 한 줄씩 실행되어 this에 바인딩되어 있는 인스턴스를 초기화함.
- 즉, this에 바인딩되어 있는 인스턴스에 프로퍼티나 메서드를 추가하고 생성자 함수가 인수로 전달받은 초기값을 인스턴스 프로퍼티에 할당하여 초기화하거나 고정값을 할당.(개발자가 하는 부분)
-
[예제 17-09]function Circle(radius) {// 1. 암묵적으로 인스턴스가 생성되고 this에 바인딩됨.// 2. this에 바인딩되어 있는 인스턴스를 초기화함.this.radius = radius;this.getDiameter = function () {return 2 * this.radius;};}
-
- 즉, this에 바인딩되어 있는 인스턴스에 프로퍼티나 메서드를 추가하고 생성자 함수가 인수로 전달받은 초기값을 인스턴스 프로퍼티에 할당하여 초기화하거나 고정값을 할당.(개발자가 하는 부분)
- 생성자 함수에 기술되어 있는 코드가 한 줄씩 실행되어 this에 바인딩되어 있는 인스턴스를 초기화함.
- 과정 3. 인스턴스 반환
- 생성자 함수 내부의 모든 처리가 끝나면 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환.
-
[예제 17-10]function Circle(radius) {// 1. 암묵적으로 인스턴스가 생성되고 this에 바인딩.// 2. this에 바인딩되어 있는 인스턴스를 초기화.this.radius = radius;this.getDiameter = function () {return 2 * this.radius;};// 3. 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환.}// 인스턴스 생성. Circle 생성자 함수는 암묵적으로 this를 반환.const circle = new Circle(1);console.log(circle); // Circle {radius: 1, getDiameter: ƒ}
- 만약 this가 아닌 다른 객체를 명시적으로 반환하면 this가 반환되지 못하고 return문에 명시한 객체가 반환.
-
[예제 17-11]// 3. 암묵적으로 this를 반환.// 명시적으로 객체를 반환하면 암묵적인 this 반환이 무시.return {}; // function() {}; function() {} 반환됨. 참조형 데이터가 있음 덮어 씌움.}// 인스턴스 생성. Circle 생성자 함수는 명시적으로 반환한 객체를 반환.const circle = new Circle(1);console.log(circle); // {}
- 하지만 명시적으로 원시 값을 반환하면 원시 값 반환은 무시되고 암묵적으로 this가 반환.
-
[예제 17-12]// 3. 암묵적으로 this를 반환.// 명시적으로 원시값을 반환하면 원시값 반환은 무시되고 암묵적으로 this가 반환.return 100;}// 인스턴스 생성. Circle 생성자 함수는 명시적으로 반환한 객체를 반환.const circle = new Circle(1);console.log(circle); // Circle {radius: 1, getDiameter: ƒ}
- 생성자 함수 내부에서 명시적으로 this가 아닌 다른 값을 반환하는 것은 생성자 함수의 기본 동작을 훼손하기에 반드시 생성자 함수 내부에서 return 문을 생략해야 함.
- 과정 1. 인스턴스 생성과 this 바인딩
덧) 스터디에서 나온 이런저런 내용 240722월-
*new가 왜 필요한가.
2015년 미국에서 이슈가 됨.
객체지향을 선호하던 시대적 배경.
자바스크립트에 new를 도입함.
만약 new를 빼먹으면 어떻게 하냐 하는 문제가 제기됨.
생성자 함수에서 new를 안 써줬을 때를 대비해 방어 코드를 써야 함.
문제는 일부러 new를 안 붙이고 사용하는 경우가 있음.
타입케스팅(타입변환)을 위해 문자형, 넘버형에 new를 안 붙이고 씀.
함수는 하나의 일만 하게 만드는 게 좋다.
생성자를 쓰면 객체만 만드는 게 좋다.
그래서 클래스가 만들어짐
클래스에서는 new 없이 인스턴스를 호출하면 에러가 남.
결론은 new를 생성자 함수 말고 클래스에서 써라.
*메서드는 점. 앞에 객체가 있고 그 객체를 읽어 들이고 그때 this가 쓰임.
함수는 객체로 전달.
*객체리터럴 내부에서 메서드를 만들 때 this를 씀 프로퍼티만 있으면 문제없음.
*어떤 두 가지 문법이 동일한 행동을 할 때 신텍틱슈가, 문법설탕이라 함.
*16장부터 객체지향 이야기.
4). 내부 메서드 [[Call]]과 [[Construct]]
- 함수 선언문 또는 함수 표현식으로 정의한 함수는 일반적인 함수로서도 생성자 함수로서도 호출 가능.
- 생성자 함수로서 호출한다는 것은 new 연산자와 함께 호출하여 객체를 생성한다는 의미.
- 일반 객체는 호출할 수 없지만 함수는 호출할 수 있음.
-
[예제 17 - 13]// 함수는 객체.function foo() { }// 함수는 객체이므로 프로퍼티를 소유할 수 있음.foo.prop = 10;// 함수는 객체이므로 메서드를 소유할 수 있음.foo.method = function () {console.log(this.prop);};foo.method(); // 10
- 함수 객체는 일반 객체가 가지고 있는 내부 슬롯과 내부 메서드는 물론, 함수 객체로 동작하기 위한 [[Environment]], [[FormalParameters]] 등의 내부 슬롯과 [[Call]], [[Construct]] 같은 내부 메서드를 추가로 가지고 있음.
- 함수가 일반 함수로서 호출되며 함수객체의 내부 메서드 [[Call]]이 호출되고, new 연산자와 함께 생성자 함수로서 호출되면 내부 메서드 [[Construct]]가 호출됨.
-
[예제 17 - 14]function foo() { }// 일반적인 함수로서 호출: [[Call]]이 호출.foo();// 생성자 함수로서 호출: [[Construct]]가 호출.new foo();
- callable, constructor, non-constructor
- callable
- 내부 메서드 [[Call]]을 갖는 함수 객체.
- 호출할 수 있는 객체. 즉, 함수.
- 내부 메서드 [[Call]]을 갖는 함수 객체.
- consructor
- 내부 메서드 [[Construct]]를 갖는 함수 객체.
- 생성자 함수로서 호출할 수 있는 함수.
- 내부 메서드 [[Construct]]를 갖는 함수 객체.
- non-constructor
- [[Construct]] 를 갖지 않는 함수 객체.
- 객체를 생성자 함수로서 호출할 수 없는 함수.
- [[Construct]] 를 갖지 않는 함수 객체.
- callable
- 호출할 수 없는 객체는 함수 객체가 아니므로 함수 객체는 반드시 callable.
- 모든 함수는 callable이면서 constructor이거나
- callable이면서 non-constructor.
- 즉, 모든 함수 객체는 호출할 수 있지만 모든 함수 객체를 생성자 함수로서 호출할 수 있는 것은 아님.
-
5). constructor와 non-constructor의 구분
- 자바스크립트 엔진은 함수 정의를 평가하여 함수 객체를 생성할 때 함수 정의 방식에 따라 함수를 constructor와 non-constructor로 구분.
- constructor: 함수 선언문, 함수 표현식, 클래스(클래스도 함수)
- non-constructor: 메서드(ES6 메서드 축약 표현), 화살표 함수
- ECMAScript 사양에서 메서드로 인정하는 범위가 일반적인 의미의 메서드보다 좁다는 것에 주의.
-
[예제 17-15]// 일반 함수 정의: 함수 선언문, 함수 표현식function foo() { }const bar = function () { };// 프로퍼티 x의 값으로 할당된 것은 일반 함수로 정의된 함수다. 이는 메서드로 인정하지 않음.const baz = {x: function () { }};// 일반 함수로 정의된 함수만이 constructor.new foo(); // -> foo {}new bar(); // -> bar {}new baz.x(); // -> x {}// non-constructor// 화살표 함수 정의const arrow = () => { };new arrow(); // TypeError: arrow is not a constructor// 메서드 정의: ES6의 메서드 축약 표현만을 메서드로 인정.const obj = {x() { }};new obj.x(); // TypeError: obj.x is not a constructor
-
[예제 17-16]function foo() { }// 일반 함수로서 호출// [[Call]]이 호출된다. 모든 함수 객체는 [[Call]]이 구현되어 있다.foo();// 생성자 함수로서 호출// [[Construct]]가 호출된다. 이때 [[Construct]]를 갖지 않는다면 에러가 발생한다.new foo();
- ❗ 주의 ❗ 생성자 함수로서 호출할 것을 기대하고 정의하지 않은 일반 함수(callable이면서 constructor)에 new 연산자를 붙여 호출하면 생성자 함수처럼 동작할 수 있음.
6). new 연산자
- Constructor인 함수를 New 연산자와 함께 호출하면 해당함수는 생성자 함수로 동작.
- 함수 객체의 내부 메서드 [[Call]]이 호출되는 것이 아니라 [[Construct]]가 호출됨.
- 단, new 연산자와 함께 호출하는 함수는 non-constructor가 아닌 constructor여야 함.
-
[예제 17-17]// 생성자 함수로서 정의하지 않은 일반 함수function add(x, y) {return x + y;}// 생성자 함수로서 정의하지 않은 일반 함수를 new 연산자와 함께 호출let inst = new add();// 함수가 객체를 반환하지 않았으므로 반환문이 무시된다. 따라서 빈 객체가 생성되어 반환.console.log(inst); // {}// 객체를 반환하는 일반 함수function createUser(name, role) {return { name, role };}// 일반 함수를 new 연산자와 함께 호출inst = new createUser('Lee', 'admin');// 함수가 생성한 객체를 반환.console.log(inst); // {name: "Lee", role: "admin"}
- 반대로 new 연산자 없이 생성자 함수를 호출하면 일반 함수로 호출.
- 함수 객체의 내부 메서드 [[Construct]]가 호출되는 것이 아니라 [[Call]]이 호출됨.
-
[예제 17-18]// 생성자 함수function Circle(radius) {this.radius = radius;this.getDiameter = function () {return 2 * this.radius;};}// new 연산자 없이 생성자 함수 호출하면 일반 함수로서 호출.const circle = Circle(5);console.log(circle); // undefined// 일반 함수 내부의 this는 전역 객체 window를 가리킴.console.log(radius); // 5console.log(getDiameter()); // 10circle.getDiameter();// TypeError: Cannot read property 'getDiameter' of undefined
- Circle 함수는 일반 함수로 호출되어 radius 프로퍼티와 getDiameter 메서드는 전역 객체의 프로퍼티와 메서드가 됨.
- 생성자 함수는 첫 문자를 대문자로 하는 파스칼 케이스(PascalCase)로 명명하여 일반 함수와 구분.
7). new.target (new.target 전체가 하나의 키워드)
- 생성자 함수가 new 연산자 없이 호출되는 것을 방지하기 위해 ES6에서는 new.target을 지원.
- new.target은 this와 유사하게 constructor인 모든 함수 내부에서 암묵적인 지역 변수와 같이 사용되며 메타 프로퍼티라 부름. 참고로 IE는 new.target을 지원하지 않음.
- new 연산자와 함께 생성자 함수로서 호출되면 함수 내부의 new.target은 함수 자신을 가리킴. new 연산자 없이 일반 함수로서 호출된 함수 내부의 new.target은 undefined.
- 함수 내부에서 new.target을 사용하여 new 연산자와 생성자 함수로서 호출했는지 확인하여 그렇지 않은 경우 new 연산자와 함께 재귀 호출을 통해 생성자 함수로서 호출 가능.
- undefined 거나 new와 함께하면 생성자 함수임.
-
[예제 17-19]// 생성자 함수function Circle(radius) {// 이 함수가 new 연산자와 함께 호출되지 않았다면 new.target은 undefined.if (!new.target) {// new 연산자와 함께 생성자 함수를 재귀 호출하여 생성된 인스턴스를 반환.return new Circle(radius);}this.radius = radius;this.getDiameter = function () {return 2 * this.radius;};}// new 연산자 없이 생성자 함수를 호출하여도 new.target을 통해 생성자 함수로서 호출.const circle = Circle(5);console.log(circle.getDiameter()); // 10
덧) 스터디에서 나온 이런저런 내용 240722월-
*new.target 등장 이유
생성자 함수는 반드시 new 키워드와 함께 호출해야 하는 규칙이 있음.
new를 붙여야 되는데 안 붙임. 안 붙여야 되는데 붙임.
지금 호출된 함수가 new와 함께 호출됐는지 아닌지 알 수 있음.
*Q: 무슨 함수인가?
function Foo() {}
A: 모른다.
이유: 함수란 무슨 함수로 호출하는지에 따라 존재.
함수라는 것은 정의를 가지고 어떻게 호출될지 알 수가 없음.
위 코드는 생성자 함수냐라고 물어보면 모른다고 얘기할 수 있고 컨스트럭터냐 넌컨스트럭터냐는 얘기할 수 있음.
[ 예시코드 01 ]
여기서 로. 서.라는 말이 중요함.function Foo() {} Foo(); // 일반함수로서 호출. new Foo(); // 생성자 함수로서 호출. const o = { a: Foo }; o.a() // 메서드로서 호출. o.a는 { }안의 Foo를 가리키고, 이때 펑션에 Foo 가 호출됨.
함수를 두 가지로 명확하게 구분, 컨스트럭터와 넌컨스트럭터로.
ㆍ컨스트럭터( constructor ) => 함수 (함수선언문, 함수표현식으로 만든), 클래스
callable 이면서 constructor다. 즉, new를 붙일 수 있다.
함수는 new를 빼먹었을 때를 대비한 방어코드를 써야 하지만
클래스라는 것은 일반함수로서의 호출이 자동방어됨. 즉, new를 빼먹으면 오류발생시킴.
*함수선언문, 함수표현식을 만드는 예전 문법의 함수들은 콜러블이면서 컨스트럭 터라는 약점을 갖고 있다.
이 약점이 뒤에 프로토타입까지 이어짐.
생성자함수로 호출될 가능성이 있기 때문에 프로토타입이라 불리는 친구를 같이 만듦.
일반 함수로서만 호출되는 케이스에는 불필요한 객체를 만드는 게 됨. 그래서 메모리를 많이 써서 퍼포먼스에 좋지 않음.
ㆍ 넌컨스트럭터( non-constructor ) => 화살표함수, 함수 축약형 (ES6에서 나온 문법들)
new가 없으면 오류가 난다.
함수 축약형 앞에 new를 붙이면 오류가 난다? -> 이거는 메서드라고 부름.
정리:
동일한 프로퍼티 구조를 만드는 인스턴스를 여러 개 만들려면 클래스 써라(생성자 함수 안됨!!).
new를 절대 붙이면 안 될 때는 화살표 함수를 써라. (무슨 이유인지는 모르겠으나 혹시나 그럴 일이 생기면)
이것이 문법적으로 가능하도록 모던 자바스크립트에서 분리가 되어있음.
[ 예시코드 02 ]person.sayHi는 person객체의 우항{ ~~ } 안의const person = { name: 'John Doe', sayHi: function () { console.log(`Hi, my name is ${this.name}`); }, }; person.sayHi(); // Hi, my name is John Doe new person.sayHi(); // Hi, my name is undefined
sayHi키의 값인 함수 ' function () '를 가리킴.
이 함수는 일반적 개념으론 매서드지만 문법적으로는 매서드가 아님.
왜냐면 new person.sayhi(); 함수 앞에 new가 있어도 호출이 되기 때문.
문법적 매서드라는 것은 non-constructor를 얘기함(new를 붙일 수 없음).
만약 위 코드를 ' function () '을 생략하고 축약해서
아래와 같이 변경하면
[ 예시코드 03 ] 메서드축약형
const person = { name: 'John Doe', sayHi() { console.log(`Hi, my name is ${this.name}`); }, }; person.sayHi(); // Hi, my name is John Doe new person.sayHi(); // TypeError: person.sayHi is not a constructor
sayHi()는 메서드라고 부른다. 그리고 new person.sayHi(); 부분이 에러가 나는 것을 볼 수 있다.
즉, 문법적으로 메서드이고 new를 붙일 수 없는 non-constructor다.
스코프 세이프 생성자 패턴(scope-safe constructor)
New.target을 사용할 수 없는 상황이면 스코프 세이프 생성자 패턴 사용 가능.
new 연산자와 함께 생성자 함수에 의해 생성된 객체(인스턴스)는 프로토타입에 의해 생성자 함수와 연결됨.
이를 이용해 new 연산자와 함께 호출되었는지 확인할 수 있음.
프로토타입과 instanceof 연산자에 대해서는 19장 프로토타입에서 자세히...
- 대부분의 빌트인 생성자 함수(Object, String, Number, Boolean, Function, Array, Date, RegExp, Promise 등)는 new 연산자와 함께 호출되었는지를 확인한 후 적절한 값을 반환.
- Object와 Function 생성자 함수는 new 연산자 없이 호출해도 new 연산자와 함께 호출했을 때와 동일하게 동작.
-
[예제 17-21]let obj = new Object();console.log(obj); // {}obj = Object();console.log(obj); // {}let f = new Function('x', 'return x ** x');console.log(f); // ƒ anonymous(x) { return x ** x }f = Function('x', 'return x ** x');console.log(f); // ƒ anonymous(x) { return x ** x }
- String, Number, Boolean 생성자 함수는 new 연산자와 함께 호출했을 때 각각 객체를 생성해서 반환하지만, new 연산자 없이 호출하면 문자열, 숫자, 불리언 값을 반환. 이를 통해 데이터 타입을 반환하기도 함.
-
[예제 17-22]const str = String(123);console.log(str, typeof str); // 123 stringconst num = Number('123');console.log(num, typeof num); // 123 numberconst bool = Boolean('true');console.log(bool, typeof bool); // true boolean
참고
도서 - 모던 자바스크립트 Deep Dive -이웅모
이웅모 강사님 홈피 - https://poiemaweb.com/js-prototype
이웅모 강사님 유튜브 - https://www.youtube.com/watch?v=0AjTZG6bGq8
✅ 덧, 부분은 스터디 내용을 기억에 의존해서 쓴 글이라 틀린 부분이 있다면 댓글 부탁드립니다.- 뽀짝코딩 주인장-
'프론트 > 모던 자바스크립트 Deep Dive 책 스터디' 카테고리의 다른 글
JS)모던 자바스크립트 Deep Dive 19장 프로토타입 (0) | 2024.07.21 |
---|---|
JS)모던 자바스크립트 Deep Dive 18장 함수와 일급 객체 (0) | 2024.07.21 |
JS)모던 자바스크립트 Deep Dive 16장 프로퍼티 어트리뷰트 (0) | 2024.07.19 |
JS)모던 자바스크립트 Deep Dive 15장 let, const 키워드와 블록 레벨 스코프 (0) | 2024.07.19 |
JS)모던 자바스크립트 Deep Dive 14장 전역 변수의 문제점 (0) | 2024.07.18 |