본문 바로가기

카테고리 없음

[Javascript] Inside Javascript 책 단어 뽀개기 (chapter 4/8)

반응형

 

Chapter 4. 함수와 프로토타입 체이닝

4.1 함수 정의

함수

 함수(function)란 하나의 특별한 목적의 작업을 수행하기 위해 독립적으로 설계된 코드의 집합

 함수를 사용하는 가장 큰 이유는 반복적인 프로그래밍을 피할 수 있다.

프로그램을 여러 개의 함수로 나누어 작성하면

모듈화  사전적인 의미로 "몇 개의 관련된 부품들을 하나의 덩어리로 생산해 장착하는 기술방식" 의미
클로저(closure)

 외부함수의 맥락(context)에 접근 가능한 내부함수 좀 더 포괄적으로는 함수 선언시 생성되는 유효 범위

 접근하려고 하는 함수의 생명 주기가 종료됐지만, 내부함수가 참조하고 있어서 그 함수에 접근할 수 있는 함수

참고: https://fullest-sway.me/blog/2017/11/13/js-closure/

리터럴

 리터럴이란 데이터를 표현하는 방식이다

 리터럴이란 소스 코드의 고정된 값을 대표하는 용어

javascript에서 리터럴의 종류

  리터럴은 더 이상 나눌 수 없는 단위의 값이다. ( 변화 O )

 객체 리터럴, 함수 리터럴 등 리터럴이 뒤에 붙으면 따로 정의하지 않고 만들어서 바로 사용할 수 있는것이다

함수 리터럴

  함수를 따로 정의하지 않고 만들어 바로 사용하는 것

함수 리터럴을 통한 add 함수 정의

  함수 리터럴 방식 사용의 예 : 함수 선언문, 함수 표현식

함수 선언문

(function statement)

 function 이라는 키워드를 명시적으로 사용하고,리턴값, 매개변수로 넘기는 값에 변수 타입을 기술 하지 않는다

- 방식 : 함수 리터럴 형태와 동일. 단, 반드시 함수명이 반드시 정의되어 있어야 한다. 세미콜론X

함수명 add가 정의 되어 있다

- add함수명이다. (필수)

함수 표현식

(function expression)

외부 코드가 실제 함수를 호출하는 것이

함수 변수(가능) 

함수이름 (불가능)

 

 함수 리터럴로 하나의 함수를 만들고, 여기서 생성된 함수를 변수에 할당하여 함수를 생성하는 것

- 방식 : 함수 리터럴 형태와 동일. 함수명 필수X, 보통 사용X, 세미콜론O, 함수 변수 필수

익명 함수 표현식

 인자로 넘겨진 두 수를 더하는 익명 함수를 만들고 이를 add 변수에 할당한 것이다.

 이것을 바로 익명 함수 표현식이다. (<-> 기명 함수 표현식)

- add함수명이 아닌 함수 리터럴로 생성한 함수를 참조하는 변수이다. 이렇게 함수가 할당된 변수를 함수 변수라고 부르기도 한다

- 함수 표현식으로 생성된 함수를 호출하기 위해선 함수 변수를 사용해야 한다. (ex: add(1,2) )

- add와 plus 함수 변수는 두 개의 인자는 더하는 동일한 익명 함수를 참조한다.

(1. 외부 코드함수 변수를 통해서 실제 함수를 호출하는 것이 가능하다)

- 익명 함수 호출 방법 : 함수 변수에 함수 호출 연산자() 를 붙여 기술한다. ( ex: add() )

 

 반대로 함수 이름이 포함된 함수 표현식을 기명 함수 표현식이라고 한다.

함수 add 호출은 되는데, 함수 sum 호출의 경우는 에러가 났다.

  sum() 함수를 정의하고 이 함수를 add 함수 변수에 할당했다. 그런데 함수 sum 호출의 경우 왜 에러가 발생할까? 2. 함수 표현식에서 사용된 함수 이름이 외부 코드에서 접근 불가능하기 때문이다 

 - 함수 표현식에 사용된 함수명은 정의된 함수 내부에서 해당 함수를 재귀적으로 호출하거나, 디버거 등에서 함수를 구분할 때 사용한다. 따라서 함수 내부에서 정의된 함수명 sum으로 함수 외부에서 해당 함수를 호출할 때 sum() 함수가 정의되어 있지 않다는 에러가 발생한다.

 그렇다면 add() 함수는 어떻게 함수 이름으로 함수 외부에서 호출이 가능할까? 자바스크립트 엔진에 의해 다음과 같은 함수 표현식 형태로 변경된다.  즉, 함수 이름으로 함수가 호출되는 것처럼 보일 뿐!

var add는 함수 변수, function add는 함수 이름

 함수 표현식에서 함수 이름은 선택사항이다! 사용하고 싶다면 함수 이름과 함수명을 구분해서 사용하자!

함수 외부에서 함수 변수 factorialVar로 함수를 호출 가능, 함수명 factorial로는 불가능을 알 수 있다.
재귀적으로 호출

 자기 자신을 다시 호출하는 것을 재귀 호출이라고 하고, 

자기 자신을 다시 호출해서 사용하는 것을 재귀 함수라고 한다.

재귀 함수

대개 factorial(계승) 함수로 설명들을 하고 있다.

 

팩토리얼( ! ) 자기 자신의 수 X (자기 자신의 수-1) X (자기 자신의 수-2) X ...  X 2 X 1
function() 생성자 함수

자바스크립트에서 객체를 만드는 방법 3가지

1. 객체 리터럴 - 가장 추천

2. new Object()를 통해 객체를 생성 - 비추 (인자로 전달되는 값에 따라 생성자 함수가 다른 내장 생성자에 객체 생성을 위임할 수 있고, 따라서 기대한 것과는 다른 객체가 리터럴이 될 수 있기 때문에)

3. function() 생성자 함수 - 자주 사용X, 상식으로만 알아두자

- 문법 : new Function ( arg1, arg2, arg3, ... , argN, functionBody ) 

x,  y는 함수의 매개변수, return x+y는 함수가 호출될 때 실행될 코드를 포함한 문자열
함수 호이스팅

 호이스팅은 var을 통해 정의된 변수의 선언문을 유효 범위의 최상단으로 끌어올리는 행위이다

 호이스팅은 '선언과 할당의 분리' 이다. 이때 변수의 선언이 초기화나 할당시에 발생하는 것이 아니라, 최상위로 호이스트 된다.

 - 발생 원인 : 자바스크립트의 변수 생성과 초기화의 작업이 분리되서 진행되기 때문

(5장에서 자세하게 다룰 예정)

 

  익명함수의 사용법 & 재귀 함수의 다양한 사용법 4가지
익명 함수

 여기서 square는 이름 없는 함수를 가리킨다. 여기서 square 변수를 어떻게 사용하는지 살펴보자

 이때, console.log는 로그 출력하는 함수일 뿐이다. 이와 같이 함수를 호출하듯이 parameter를 넣으면 결과를 리턴받고 그것을 출력한다. square는 함수명과 다를 바가 없지만, 지역변수 선언하듯 선언한 것이므로 로직이 끝나면 가비지컬렉터가 수거해 갈 것이다.

재귀 함수에서

익명 함수 사용방법

 내부적으로 자기 자신을 계속 호출해야 하므로 fac 라는 함수명을 명시해서 사용하고 있다

재귀 함수에서

일반적인 사용 방법

 조건 절이 약간 다르지만 위와 같은 표현이다.

재귀 함수에서 

인자값 = 함수 자체

 인자값으로 함수 자체를 받아 사용하는 경우이다

스스로 설명 써놓기

출처: https://emflant.tistory.com/65

 재귀 함수에서 

계층적인 프로퍼티 열거할 때 

재귀함수 사용 전 

 위의 예제는 객체의 key : value를 출력하는 예제이다. sports란 객체에는 또 다른 객체를 포함하고 있는 계층적인 객체구조를 이루고 있다. 

 하지만 여기서 soccer 객체의 soccer.meber의 값이 11이 아닌 {step : {value: 11}}의 형태인 계층적 객체를 또 포함하고 있다면 이 객체의 프로퍼티를 열거하기 위해 또 한번의 for-in문을 작성해야 한다. 

 그런데 이런 계층적인 객체에 맞추어 코드를 유동적으로 작성할 수는 없다. 이럴 경우에 재귀함수를 사용하여 해결할 수가 있다.

재귀함수 사용 후

 이와 같은 처리로 객체의 프로퍼티를 전부 열거할때까지 반복한다.

 따라서 객체의 계층에 제약을 받지 않고 처리할 수가 있다

재귀함수로 계층적인 객체 프로퍼티를 열거하는 또 다른 예제

출처: https://webclub.tistory.com/72

 

 

4.2 함수 객체·함수도 객체다

   

자바스크립트에서

함수도 객체이다

 함수 자체가 일반 객체처럼 프로퍼티를 가질 수 있다.

자바스크립트에서

함수는 값으로 취급된다

  함수도 일반 객체처럼 취급될 수 있다.
일급 객체

- 리터럴에 의해 생성

- 변수나 배열의 요소, 객체의 프로퍼티 등에 할당 가능

- 함수의 인자로 전달 가능

- 함수의 리턴값으로 리턴 가능

- 동적으로 프로퍼티를 생성 및 할당 기능

 위 다섯 가지 기능이 가능한 객체를 일급 객체라고 한다.

 

  함수가 값으로 취급 되는 경우 3가지
변수 or 프로퍼티

 함수가 숫자나 문자열처럼 변수나 프로퍼티의 값으로 할당될 수 있다.

함수 인자

 함수는 다른 함수의 인자로도 전달이 가능한다.

 foo( ) 함수를 호출할 때, 함수 리터러러 방식으로 생성한 익명 함수를 func 인자로 넘겼다. -> foo() 함수 내부에서는 func 매개변수로 인자에 넘겨진 함수를 호출할 수 있다.

함수를 인자값으로 받은 foo 함수 안에서 콘솔값을 찍은 결과값
리턴값

 함수는 다른 함수의 리턴값으로도 활용할 수 있다.

 foo() 함수는 console.log()를 이용해 출력하는 간단한 익명 함수를 리턴하는 역할을 한다.

1. foo() 함수가 호출되면, 리턴값으로 전달되는 함수가 bar 변수에 저장된다.

2. () 함수 호출 연산자를 이용해 bar()로 리턴된 함수를 실행하는 것이 가능하다.

 

함수 객체의

기본(표준) 프로퍼티

 ECMA5 스크립트 명세서에는 모든 함수가 length와 prototype 프로퍼티를 가져야 한다고 기술하고 있다.

- add 함수에는 표준 프로퍼티인 length, prototype 와 표준 프로퍼티가 아닌 name, caller, argument, __proto__ 프로퍼티를 가지고 있다.

length 프로퍼티

 

prototype 프로퍼티  객체로서 prototype 프로퍼티를 가지고 있다.  내부 프로퍼티와 혼동하지 말것!

 

 

4.3 함수의 다양한 형태

콜백 함수

 콜백 함수는 익명 함수의 대표적인 용도이다.

 코드를 통해 명시적으로 호출하는 함수가 아니라, 개발자는 단지 함수를 등록하기만 하고, 어떤 이벤트가 발생했거나 특정 시점에 도달했을 때 시스템에서 호출되는 함수이다.

즉시 실행 함수

 함수를 정의함과 동시에 바로 실행하는 함수

즉시 실행 함수 예제 코드

- 방법

1. 함수 리터럴를 () 로 감싼다. 여기선 function(name){} 을 감쌌다.

2. 함수가 바로 호출될 수 있게 소괄호()를 추가한다. 이때 괄호 안에 값을 추가해 즉시실행함수의 인자로 넘길 수 있다. 여기선 ('foo')로 즉시 실행 함수를 호출했으며, 이때 'foo'를 인자로 넘겼다. 이 값은 앞 예제 즉시실행함수의 name 매개변수로 넘겨지게 된다.

- 특징 : 같은 함수를 다시 호출할 수 없다.

- 용도 : 최초 한 번의 실행만을 필요로 하는 초기화 코드에 사용하기 / jQuery와 같은 자바스크립트 라이브러리나 프레임워크 소스들에 사용된다.

그럼 jQuery에서 즉시 실행함수를 사용하는 이유는? 자바스크립트의 변수 유효 범위 특성 때문에

자바스크립트에서는 함수 유효 범위를 지원한다. 기본적으로 자바스크립트는 변수를 선언할 경우 전역 유효 범위를 가지게 된다. 그러나 함수 내부에서 정의된 매개변수와 변수들은 함수 코드 내부에서만 유효할 뿐, 함수 밖에서는 유효하지 않다. 이때 var문을 사용하여 정의하고 그렇지 않으면 함수 내의 변수라도 전연 유효 범위를 갖게 된다. 함수 외부의 코드에서 함수 내부의 변수에 접근하는 게 불가능하다. 따라서 라이브러리 코드를 이렇게 즉시 실행 함수 내부에 정의해두게 되면, 라이브러리 내의 변수들은 함수 외부에서 접근할 수 없다. 따라서 이렇게 즉시 실행 함수 내에 라이브러리 코드를 추가하면 전역 네임스페이스를 더럽히지 않으므로, 이후 다른 자바스크립트 라이브러리들이 동시에 로드가 되더라도 라이브러리 간 변수 이름 충돌 같은 문제를 방지할 수 있다.

내부 함수

 자바스크립트에서는 함수 코드 내부에서도 다시 함수 정의가 가능하다. 이렇게 함수 내부에 정의된 함수를 내부함수라고 한다.

- 용도 : 자바스크립트의 기능을 보다 강력하게 해주는 클로저 생성 / 부모 함수 코드에서 외부에서의 접근을 막고 독립적인 헬퍼 함수를 구현

 - 내부 함수에서는 자신을 둘러싼 부모 함수의 변수에 접근이 가능하다. (child 안에서 변수 a를 출력했을 때 부모함수 parent에서 변수 a에 접근하여 값을 출력함) 자바스크립트의 스코프 체이닝 떄문

- 내부 함수는 일반적으로 자신이 정의된 부모 함수 내부에서만 호출이 가능하다 이것은 함수 스코핑 때문이다. 함수 내부에 선언된 변수는 함수 외부에서 접근이 불가능하다.

함수 외부에서 특정 함수 스코프 안에 선언된 내부 함수를 호출하는 방법

1. 내부 함수를 함수 표현식 형식으로 정의하고, 변수 child에 저장한다. 부모함수 parent() 의 리턴값으로 내부 함수의 참조값을 가진 child 함수변수를 리턴한다.

2. parent() 함수가 호출되면, inner 변수에 child 함수 변수 값이 리턴된다. child 함수 변수는 내부 함수의 참조값이 있으므로, inner변수도 child() 내부 함수를 참조한다.

3. 따라서 inner 변수에 함수호출연산자()를 붙여 함수 호출 구문을 만들면, parent() 함수 스코프 밖에서도 내부 함수 child()가 호출된다. 호출하는 내부 함수에는 a변수가 정의되어 있지 않아, 스코프 체이닝으로 부모 함수에 a변수가 정의되어 있는지 확인하게 되고 a가 정의되어 있으면 그 값을 그대로 출력된다. (정의되어 있지 않으면 ReferenceError: a is not defined 라고 출력됨)

 이와 같이 실행이 끝난 parent() 와 같은 부모 함수 스코프의 변수를 참조하는 inner()와 같은 함수를 클로저라고 한다.

함수를 리턴하는 함수

 자바스크립에서는 함수도 일급 객체이므로 일반 값처럼 함수 자체를 리턴할 수 있다. 이러한 특징으로 함수를 호출함과 동시에 다른 함수로 바꾸거나, 자신을 재정의하는 함수를 구현할 수 있다.

- 처음 self()가 호출됐을 때는 a를 출력한뒤, self 함수 변수에 self() 함수 호출 리턴값으로 내보낸 함수가 저장된다.

- 두번째로 self()가 호출되었을 때 b가 출력되는 이유는 첫번째 self()가 호출 후, self 함수 변수가 가리키는 함수가 원래 함수-> 리턴받은 새 함수 로 변경되었기 때문이다.

 

 

4.4 함수 호출과 this

   
함수 형식에 맞춰 인자를 넘기지 않더라도 함수 호출이 가능하다

 정의된 함수의 인자보다 적게 함수를 호출하면 undefined값이, 많게 함수를 호출하면 초과된 인수는 무시된다.

argument 객체

 함수 형식에 맞춰 인자를 넘기지 않더라도 함수 호출이 가능한 자바스크립트의 특성 때문에 런타임 시에 호출된 인자의 개수를 확인하고 동작을 다르게 해줘야할 경우가 있는데 argument 객체가 이를 가능하게 한다.

 자바스크립트에서 함수를 호출할 때 인수들과 함께 암묵적으로 argument 객체가 함수 내부로 전달된다. argument 객체는 함수를 호출할 때, 넘긴 인자들이 배열 형태로 저장된 객체(유사 배열 객체)이다.

this 바인딩  여러 가지 함수가 호출되는 방식에 따라 this가 다른 객체를 참조하는 것
  this 바인딩 종류
this 바인딩 여러 가지 함수가 호출되는 방식에 따라 this가 다른 객체를 참조하는 것

객체의 메서드를

호출할 때

this 바인딩

메서드 내부 코드에서 사용된 this는 해당 메서드를 호출한 객체로 바인딩된다.

 

- 객체의 프로퍼티가 함수일 때, 이 함수를 메서드라고 부른다.

- 메서드 호출 패턴에서의 this도 같은 규칙

 

함수를 호출할 때

this 바인딩

해당 함수 내부 코드에서 사용된 this는 전역 객체에 바인딩된다.

 예 : 브라우저 환경 -> window객체, node js 런타임 환경 -> global객체

 

내부 함수를

호출했을 때

this 바인딩

 내부 함수도 함수이므로 함수 호출로 취급된다.

내가 예상한 답은 2,3,4였지만 다르게 나온다.

 

왜 다르게 나올까? 자바스크립트에서 내부 함수 호출 패턴을 정의해 놓지 않는다.

이 한계를 극복하기 위해서 부모 함수의 this를 내부 함수가 가능한 다른 변수에 저장하는 방법을 사용한다. 관례상 변수 이름은 that으로 짓는다.

 자바스크립트에서 this의 바인딩 한계를 극복하기 위해 this 바인딩을 명시적으로 할 수 있도록 call 과 apply 메서드를 제공한다. (j-query, underscroe.js에서는 bind메서드 제공 7장에서 다룰 예정)

생성자 함수를

호출할 때

this 바인딩

자바스크립트 객체를 생성하는 방법은 크게 객체 리터럴 방식 & 생성자 함수를 이용하는 방식이 있다.

 기존 함수에 new 연산자를 붙여서 호출하면 해당 함수는 생성자 함수로 동작한다.

생성자 함수를 이용한 객체 생성방법 : 함수 앞에 new 붙이기, 함수 이름 첫 문자를 대문자로 쓰기


 생성자 함수가 동작하는 방식

1. 빈 객체 생성 및 this 바인딩

- 생성자 함수 코드가 실행되기 전에 빈 객체가 생성된다.(빈 객체는 사실 proptype 객체와 연결됨)

- 빈 객체는 생성자 함수가 새로 생성하는 객체이다. 이 객체는 this로 바인딩된다. 따라서 this는 이 빈 객체를 가리킨다고 볼 수 있다.

2. this를 통한 프로퍼티 생성

3. 생성된 객체 리턴

- 리턴문이 없을 경우, this로 바인딩된 새로 생성한 객체가 리턴된다. (생성자함수가 아닌 일반 함수를 호출할 때 리턴문이 없으면 undefined가 리턴된다)

- 리턴값이 A 객체를 반환하는 경우, 생성자 함수를 호출했더라도 해당 A 객체가 리턴된다.

Person()라는 생성자 함수를 정의하고, 이를 통해 foo 객체 만드는 예제

- Person() 함수를 new로 호출하면, Person()은 생성자 함수로 동작한다.

객체 리터럴 방식 & 생성자 함수를 통한 객체 생성 방식 차이 = 프로토타입 객체에 있다

위에서부터 foo객체, bar 객체, baz 객체 순

- foo객체의 constructor는 function Object지만 bar객체와 baz객체의 constructor는 fucntion Person이다. 이때 constructor가 내가 아는 Mounting할 때 rendering보다 먼저 호출되는 constructor()인가요? Yes!!

객체 리터럴 방식의 경우 자신의 프로토타입 객체가 Object.prototype이고,

생성자 함수 방식의 경우는 Person.prototype이다

왜 이런 차이가 발생할까? 자바스크립트 객체 생성 규칙 때문. 자바스크립트 객체는 자신을 생성한 생성자 함수의 prototype 프로퍼티가 가리키는 객체를 자신의 프로토타입 객체로 설정한다.

객체 리터럴 방식에서 객체 생성자 함수는 Object()이고, 생성자 함수 방식에서는 생성자 함수 자체이다. 이것은 프로토타입 체이닝과 관련이 있다(아래에 계속)

 생성자 함수를 new를 붙이지 않고 호출할 경우

- 객체 생성을 목적으로 작성한 생성자 함수를 new 없이 호출하거나 일반 함수를 new를 붙여서 호출할 경우 코드에서 오류가 날 수 있다. 왜? 일반 함수 호출과 생성자 함수를 호추랗ㄹ 때 this 바인딩 방식이 다르기 때문

- 일반 함수 호출의 경우 this가 window 전역 객체에 바인딩되는 반면, 생성자 함수 호출의 경우 this는 새로 생성되는 빈 객체에 바인딩된다.

call()

&

apply()

메서드를

이용하여

명시적인

this 바인딩

- 자바스크립트는 내부적인 this 바인딩 이외에도 this를 특정 객체에 명시적으로 바인딩시키는 방법도 있다.

- apply() 메서드를 호출하는 주체는 함수이고, apply() 메서드도 this를 특정 객체를 바인딩할 뿐 결국 본질적인 기능은 함수 호출이다.

functuin.apply(thisArg, argArray) 를 예시로 apply를 살펴보면 argArray 배열을 자신을 호출한 함수의 인자로 사용하되, 이 함수 내부에서 사용된 this는 첫번째 인자인 thisArg객체로 바인딩해서 함수를 호출하는 기능을한다.

- foo 는 객체 리터럴 방식으로 생성한 빈 객체이다

- 첫번째 인자로 넘긴 foo가 Person() 함수에서 this로 바인딩된다. 그리고 두번째 인자로 넘긴 배열은 호출하려는 Person()함수의 인자로 각각 전달된다. 이 코드는 결국 Person('foo', 30, 'man') 함수를 호출하면서, this를 foo객체에 명시적으로 바인딩하는 것을 의미한다.

 

 

4.5 프로토타입 체이닝

프로토타입 기반의 객체지향 프로그래밍 프로토타입 기반 프로그래밍은 객체지향 프로그래밍의 한 형태의 갈래로 클래스가 없고, 클래스 기반 언어에서 상속을 사용하는 것과는 다르게, 객체를 원형(프로토타입)으로 하여 복제의 과정을 통하여 객체의 동작방식을 다시 사용할 수 있다.
OOP 기능 OOP란 객체지향 프로그래밍을 뜻한다. Object-oriented programming
프로토타입

- 국어로는 원형정도로 번역되는 prototype은 말 그대로 객체의 원형이라고 할 수 있다. 함수는 객체다. 그러므로 생성자로 사용될 함수도 객체다. 객체는 프로퍼티를 가질 수 있는데 prototype이라는 프로퍼티는 그 용도가 약속되어 있는 특수한 프로퍼티다. prototype에 저장된 속성들은 생성자를 통해서 객체가 만들어질 때 그 객체에 연결된다. 

- 프로토타입을 이용하면 객체와 객체를 연결하고 한쪽 방향으로 상속을 받는 형태를 만들 수가 있다

프로토타입 체이닝

- 프로토타입 체이닝이란 자바스크립트에서 프로퍼티(속성)이나 메서드를 참조하게 되면먼저 자신 안에 멤버가 정의되어있는지 찾아본 다음발견하지 못하면 그 프로토타입으로 이동하여 해당 프로토타입 객체 내에서 멤버를 찾는다이는 멤버를 찾거나멤버를 찾지 못하고 null을 반환하고서야 비로소 끝나는데이러한 객체들의 연쇄를 가리킨다.

- 프로토타입 체이닝이란 자바스크립트에서 객체는 자기 자신의 프로퍼티뿐만 아니라, 자신의 부모 역할을 하는 프로토타입 객체의 프로퍼티 또한 마치 자신의 것처럼 접근하는걸 가능하게 해주는 것.

자바스크립트의 객체 생성 규칙

- 자바스크립트에서 모든 객체는 자신을 생성한 생성자 함수의 prototype 프로퍼티가 가리키는 프로토타입객체를 자신의 부모 객체로 설정하는 [[Prototype]]링크로 연결한다.(크롬에서 __proto__ 조상)

[[Prototype]]링크

- 자바스크립트의 모든 객체는 자신의 부모인 프로토타입 객체를 가리키는 참조 링크 형태의 숨겨진 프로퍼티가 있는데 이러한 링크를 암묵적 프로토타입 링크라고 부르며 모든 객체의 [[Prototype]] 링크라고 한다.

 
prototype 프로퍼티와 [[Prototype]] 링크 구분

- Person() 생성자 함수는 prototype 프로퍼티로 자신과 링크된 프로토타입 객체를 가리킨다.

- 자바스크립트의 객체 생성 규칙에 의하여 Person() 생성자 함수로 생성된 foo 객체는 Person() 함수의 프로토타입 객체를 [[Prototype]] 링크로 연결한다. 결국 prototype프로퍼티와 [[Prototype]] 링크는 같은 프로토타입 객체를 가리킨다.

- prototype 프로퍼티는 함수의 입장에서 자신과 링크된 프로토타입 객체를 가리키고 있고, 반면 [[Prototype]]링크는 객체의 입장에서 자신의 부모 객체인 프로토타입 객체를 내부의 숨겨진 링크로 가리키고 있다.

 결국, 자바스크립트에서 객체를 생성하는 건 생성자 함수의 역할이지만 생성된 객체의 실제 부모 역할을 하는 건 생성자 자신이 아닌 생성자의 prototype 프로퍼티가 가리키는 프로토타입 객체이다.

  ( 객체 리터럴 / 생성자 함수 ) 로 생성된 프로토타입 체이닝

객체 리터럴 방식로

생성된

객체의

프로토타입 체이닝

hasOwnProperty 메서드란? 객체에 인자로 넘긴 문자열 이름의 프로퍼티나 메서드가 있는지 체크하는 자바스크립트 표준 API 함수이다.

 - myObject는 객체 리터럴로 생성한 객체이다.

- sayName() 메서드를 호출할 때는 객체 내에 메서드가 있어서 바로 수행된다.

- hasOwnProperty() 메서드를 호출할 떄는 myObject에 없기 때문에myObject의 [[Prototype]]링크를 따라 부모 역할을 하는 Object.prototype 객체 내에 있는 지 검색한다. hasOwnProperty() 메서드는 표준 API로 포함이 되어 있다.

* Object.prototype 객체는 모든 객체의 조상 역할을 하는 객체로서, 자바스크립트 모든 객체가 호출할 수 있는 toString(), hasOwnProperty() 등 표준메서드를 제공한다. 출처: ECMAScript 명세서


생성자 함수로

생성된

객체의

프로토타입 체이닝

- foo 객체의 생성자는 Person()함수이다.

- 자바스크립트의 룰에 따르면 foo 객체의 프로토타입 객체는 자신을 생성한 Person 생성자 함수 객체의 prototype 프로퍼티가 가리키는 객체(Person.prototype)가 된다.

- 즉, foo객체의 프로토타입 객체는 Person.prototype이 된다.

- foo.hasOwnProperty() 메서드는 foo에 없어서 프로토타입 체이닝으로 foo의 부모 객체인 Person.prototype 객체에서 찾는다. 하지만 함수에 연결된 프로토타입 객체는 디폴트로 constructor프로퍼티만을 가진 객체이므로 역시 없다. (prototype 프로퍼티는 함수가 생성될 떄 만들어지고 constructor 프로퍼티 하나만 있는 객체를 가리킨다)

그럼 이렇게 프로토타입 체이닝이 끝나는가? NO, true가 출력되어서! Person.prototype 객체 역시 조상인 Object.prototype을 프로토타입 객체로 가지기 때문에 프로토타입 체이닝은 Object.prototype객체로 계속 이어진다~

   

프로토타입 체이닝의

종점

조상 객체인 Object.prototype 객체가 되겠다!

- 이를 통해 모든 자바스크립트 객체는 프로토타입 체이닝으로 Object.prototype 객체가 가진 프로퍼티와 메서드에 접근 및 공유가 가능하다는 것을 알 수 있다

기본 데이터 타입

확장

 자바스크립트의 숫자, 문자열, 배열 등에서 사용되는 표준 메서드들의 경우 이들의 프로토타입인 Number.prototype, String.prototye, Arrray.prototype 등 정의되어 있다.

 기본 내장 프로토타입 객체 또한 Object.prototype을 자신의 프로토타입으로 가지고 있어서 프로토 체이닝으로 연결된다.

자바스크립트의

각 네이티브 객체별로

공통으로 제공해야 하는 메서드들을    각각의 프로토타입  객체 내에

메서드로 정의해야 한다

 prototype 객체 내에 없는 메서드를 사용하고 싶으면 prototype 객체에 추가한 다음에 사용하라?

예 : str.testMethod(); 주석 맞는지 확인받기!

네이티브 객체

=

빌트인 객체

 네이티브 객체(빌트인 객체)는 ECMAScript 명세에 정의된 객체를 말하며 애플리케이션 전역의 공통 기능을 제공한다. 네이티브 객체는 애플리케이션의 환경과 관계없이 언제나 사용할 수 있다.

 Object, String, Number, Function, Array, RegExp, Date, Math와 같은 객체 생성에 관계가 있는 함수 객체와 메소드로 구성된다.

빌트인 프로토타입 객체

Object.prototype, String.prototype, Number.prototype, Function.prototype, Array.prototype, RegExp.prototype, Date.prototype, Math.prototype 등

프로토타입도

자바스크립트 객체

이다

  함수가 생성될 때, 자신의 prototype 프로퍼티에 연결되는 프로토타입 객체는 디폴트로 constructor 프로퍼티만을 가진 객체이다.

 프로토타입 객체 역시 자바스크립트 객체이므로 일반 객체처럼 동적으로 프로퍼티를 추가/삭제가 가능하다. 이렇게 변경된 프로퍼티는 실시간으로 프로토타입 체이닝에 반영된다.

프로토타입 메서드

&

this 바인딩

 프로토타입 메서드 내부에서 this를 사용하면 this는 어디에 바인딩될까?

 => 객체의 메서드를 호출할 때 this 바인딩의 규칙과 동일하다

 즉, this는 메서드를 호출한 객체에 바인딩된다

디폴트

프로토타입은

다른 객체로 변경이

가능하다

 디폴트 프로토타입 객체는 함수가 생성될 때 같이 생성되며, 함수의 prototype 프로퍼티에 연결된다.

 디폴트 프로토타입 객체를 다른 일반 객체로 변경하는 것이 가능하다. (객체지향의 상속 구현 가능)(6장에서 더 자세히 살펴볼 것)

 * 주의! 생성자 함수의 프로토타입 객체가 변경되면 이후에 생성된 객체들은 변경된 프로토타입 객체로 [[Prototype]] 링크를 연결해야한다!

(반대로 이전에 생성된 객체들은 기존 프로토타입 객체로의 [[Prototype]] 링크를 그대로 유지한다)

 - Person.prototype.constructor는 Person() 생성자 함수를 가리킨다

너 이해했다. 혼자서 작성해보기

디폴트 프로토타입을 예를 들어 함수->객체로 변경하는 것은 사실 변경이 아니라 프로토타입이 새로 생성되는 것이다.

객체의

프로퍼티 읽기나

메서드를

실행할 때만

프로토타입 체이닝이

동작한다

- foo.country에 접근하려 했을 때 foo 객체는 name 프로퍼티밖에 없으므로 프로토타입 체이닝이 이뤄지면서 foo의 프로토타입 객체인 Person.prototype의 country 프로퍼티값인 'Korea'가 출력돈다.

- 위와 반대로 foo.ocuntry 값에 'USA'라는 값을 저장하면, 프로토타입 체이닝이 동작하는 것이 아니라, foo 객체에 country 프로퍼티값이 동적으로 생성된다.

- 그러므로 foo.country는 프로토타입 체이닝 없이 바로 바로 'USA'가 출력되는 반면, bar 객체는 프로토타입 체이닝을 거쳐 'Korea'가 출력된다.

반응형