4/ 함수와 프로토타입 체이닝 (2)

4/ 함수와 프로토타입 체이닝 (2)

목차

📒 인사이드 자바스크립트 중 메모해야할 부분만 적었습니다.


4. 함수 호출과 this

  • 함수호출시 암묵적으로 arguments 객체와 this인자가 전달된다.

4.1 arguments 객체

  • 유사배열 객체
  • 정의된 함수의 인자보다 적게 함수를 호출할 경우 넘겨지지 않은 인자는 undefined
  • 함수 코드를 작성할 때, 런타임 시에 호출된 인자의 개수를 확인하고 이에 따라 동작을 다르게 해줘야 할 경우가 있다. arguments 객체가 그 역할을 한다.
  • 함수가 호출될 당시의 인자들에 배열 형태로 접근할 수 있다.
- 함수 호출할 때 넘겨진 인자 (배열형태) - length : 인자갯수 - callee : 현재 실행 중인 함수의 참조값
1
2
3
4
5
6
7
8
9
function sum(){
let result = 0;
for(let i = 0; i < arguments.length; i++) {
result += arguments[i];
}
return result;
}
sum(1,2,3); // 6
sum(1,2,3,4,5,6,7,8,9); // 45

4.2 호출패턴과 this 바인딩

함수가 호출되는 방식(호출패턴)에 따라 this가 다른 객체를 참조한다.(this 바인딩)

  • 객체의 메서드 호출할 때 this바인딩
  • 함수 호출할 때 this바인딩
  • 생성자 함수를 호출할 때 this 바인딩

4.2.1 객체의 메서드 호출할 때 this바인딩

자신을 호출한 객체에 바인딩된다!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const myObject = {
name: 'foo',
sayName: function(){
console.log(this.name);
}
// sayName은 function(){console.log(this.nmae)}을 가리킴
}

const otherObject = {
name: 'bar'
}

otherObject.sayName = myObject.sayName;
// myObject.sayName의 참조값을 할당함.
// otherObject.sayName은 생성과 동시에 function(){console.log(this.nmae)}을 가리킴
// this는 자신을 호출한 객체에 바인딩되므로, 결과값이 다름
myObject.sayName(); // foo
otherObject.sayName(); //bar

4.2.2 함수 호출할 때 this바인딩

자바스크립트의 모든 전역 변수는 전역 객체의 프로퍼티들이다.
(브라우저는 window, node는 global)

  • 함수 내부에서의 this는 전역 객체를 가리킨다.
  • 내부 함수에서도 this는 전역 객체를 가리킨다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const value = 100;
const myObject = {
value: 1,
func1: function(){
this.value += 1;
console.log('func1()' + this.value); // 2

func2 = function(){
this.value += 1;
console.log('func2()' + this.value); // 101

func3 = function() {
this.value += 1;
console.log('func3()' + this.value); // 102
}
func3();
}
func2();
}
};
myObject.func1();

부모함수의 this를 내부 함수가 접근 가능한 다른변수에 저장하는 방법이 사용된다.

  • 보통 관례상 this값을 저장하는 변수의 이름을 that이라고 짓는다.
  • 명시적으로 bind를 사용할 수도 있다.
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
const value = 100;
const myObject = {
value: 1,
func1: function(){
const that = this;
this.value += 1;
console.log('func1()' + this.value); // 2

const _func2 = func2.bind(this)
func2 = function(){
that.value += 1;
console.log('func2()' + that.value); // 101

func3 = function() {
that.value += 1;
console.log('func3()' + that.value); // 102
}
func3();
}
// const _func2 = func2.bind(this)
// _func2();
func2();
}
};
myObject.func1();

4.2.3 생성자 함수를 호출할 때 this 바인딩

객체를 생성하는 방법

  • 객체 리터럴 방식
  • Object() 생성자 함수
  • 생성자 함수 이용

기존 함수에 new 연산자를 붙여서 호출하면 해당 함수는 생성자 함수로 동작한다.
일반 함수에 new를 붙여 호출하면 원치 않는 생성자 함수처럼 동작할 수 있다.
특정함수가 생성자 함수로 정의되어 있음을 알리려고 함수 이름의 첫 문자를 대문자로 쓴다.

생성자 함수에서의 this는 함수호출방식의 this 바인딩과 다르게 동작한다.
이를 이해하기 위해 생성자 함수의 동작방식을 이해하면 된다!

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

1
2
3
4
5
6
const Person = function(name){
this.name = name;
}

var foo = new Person('foo');
console.log(foo.name); // foo

위 의 예시를 토대로 설명

  1. 빈 객체 생성 및 this 바인딩
  • 빈 객체가 생성된다.
  • new Person()을 통해서
    • 빈 객체는 this로 바인딩된다. 즉, 생성자 함수 내의 this는 빈 객체를 가리킨다.
    • 빈 객체에는 기본적으로 [[Prototype]]이 생성된다.
    • __proto__자신을 생성한 생성자 함수Person()의 prototype 프로퍼티가 가리키는 객체를
      자신의 프로토타입 객체로 설정한다.
  1. this를 통한 프로퍼티 생성
    • 함수코드 내부에서 this를 통해 foo에 동적으로 프로퍼티나 메서드를 생성할 수 있다.
  2. 생성된 객체 리턴

2. 객체 리터럴 방식과 생성자 함수를 통한 객체 생성 방식의 차이

1
2
3
4
5
6
7
8
9
10
11
12
const foo = {
name: 'foo',
age: 35,
}

function Person(name, age){
this.name = name;
this.age = age;
}

const bar = new Person('bar', 33);
const baz = new Person('baz', 25);

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

  • 객체 생성을 목적으로 작성한 생성자 함수를
    new 없이 호출하거나 일반함수를 new를 붙여서 호출할 경우 오류 발생
  • 일반 함수 호출과 생성자 함수를 호출할 때 this 바인딩 방식이 다르기 때문
  • 일반함수의 경우 this => window
    생성자함수의 경우 this => 새로 생성되는 빈 객체

대문자 표기 네이밍 규칙을 권장하나 휴먼에러가 발생 할 수 있기 때문에 다음과 같은 분기문이 있는 코드 패턴을 사용하기도 한다.

1
2
3
4
5
6
// 강제로 인스턴스 생성하기
function A(arg){
if (!(this instanceof arguments.callee))
return new A(arg);
this.value = arg? arg : 0;
}

4.2.4 call과 apply 메서드를 이용한 명시적인 this 바인딩

내부적인 this 바인딩 이외에도
this를 특정 객체에 명시적으로 바인딩 시키는 방법

  • apply()
  • call()

    함수의 부모 객체인 Function.prototype 객체의 메서드들이다.

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

  • Person()이라는 함수가 있고, Person.apply() 이렇게 호출한다면 이것의 기본적인 기능은 Person() 함수를 호출하는 것이다.
1
2
function.apply(thisArg, argArray)
function.call(thisArg, argItem1, argItem2, argItem3, ...)

function을 호출하라, 이때 thisthisArg에 바인딩해라.

  • thisArg : apply를 호출한 함수 내부에서 사용한 this에 바인딩할 객체
  • argArray: 인자배열
  • call : call은 인자를 각각 넘긴다.
1
2
3
4
5
6
7
8
9
10
function Person(name, age, gender){
this.name = name;
this.age = age;
this.gender = gender;
}
const foo = {}

Person.apply(foo, ['foo', 30, 'main']);
// Person 메서드를 호출해라, 이때 this는 foo객체에 바인딩해라.
// 명시적인 바인딩

유사배열 객체에서 사용하는 경우

1
2
3
4
5
6
7
function A(){
console.dir(arguments); // __proto__는 Object
// arguments.shift() // 에러
const args = Array.prototype.slice.apply(arguments);
console.dir(args); // __proto__는 Array
}
A(1,2,3);

Array.prototype.slice를 호출하라, 이때 Array.prototype.slice의 this는 arguments 객체로 바인딩하라.

4.3 함수리턴

일반 함수나 메서드는 리턴값을 지정하지 않을 경우, undefined 값이 리턴된다.

생성자 함수에서 리턴값을 지정하지 않을 경우 생성된 객체가 리턴된다.

  • 생성자 함수에서 별도의 리턴값을 지정하지 않을 경우 this로 바인딩 된 새로 생성된 객체가 리턴된다.
  • 때문에 생성자 함수에서는 일반적으로 리턴값을 지정하지 않는다.
  • 생성자 함수가 명시적으로 객체를 리턴할 경우 새로운 객체를 생성하더라도, 명시적으로 넘긴 객체나 배열이 리턴된다.
  • 불린, 숫자, 문자열을 리턴할 경우 무시된다.

5. 프로토타입 체이닝

5.1 프로토타입의 두가지 의미

자바스크립트는 프로토타입기반의 객체지향 프로그래밍을 지원
자바스크립트의 모든 객체는 자신의 부모인 프로토타입 객체를 가리키는 참조 링크 형태로 숨겨진 프로퍼티가 있다.
implicit prototype link : [[Prototype]]

  • prototype객체와 [[Prototype]]링크는 다른 개념이다.

5.2 객체 리터럴 방식으로 생성된 객체의 프로토타입 체이닝

프로토타입 체이닝
자바스크립트에서 특정 객체의 프로퍼티나 메서드에 접근하려고 할 때,
해당 객체에 접근하려는 프로퍼티 또는 메서드가 없다면
[[Prototype]]링크를 따라 자신의 부모 역할을 하는 프로토타입 객체의 프로퍼티를 차례대로 검색하는 것

5.3 생성자 함수로 생성된 객체의 프로토타입 체이닝

객체 리터럴 방식과 약간 다른 프로토타입 체이닝이 이뤄지지만 아래의 기본 원칙은 지킨다.

자바스크립트에서 모든 객체는 자신을 생성한 생성자함수의 prototype 프로퍼티가 가리키는 객체를
자신의 프로토타입객체(부모객체)로 취급한다.

5.4 프로토타입 체이닝의 종점

Object.prototype이 프로토타입 체이닝의 종점이다.

5.5 기본 데이터 타입 확장

자바스크립트는 Object.prtototype, String,prototype 등과 같이
표준 빌트인 프로토타입 객체에도 사용자가 직접 정의한 메서드들을 추가하는 것을 허용한다.

공용적으로 사용하는 메서드의 경우 prototype에 선언하긔
angular에서 pipe의 성격으로 사용하는 함수들을 prototype에 넣어도 됨

5.6 프로토타입도 자바스크립트 객체다

프로토타입 객체 역시 자바스크립트 객체이므로, 일반 객체처럼 동적으로 프로퍼티를 추가/삭제하는 것이 가능하다.

5.7 프로토타입 메서드와 this 바인딩

메서드 호출패턴에서의 this는 그 메서드를 호출한 객체에 바인딩된다.
프로토타입 객체도 이 this바인딩 규칙이 적용된다.

5.8 디폴트 프로토타입은 다른 객체로 변경이 가능하다

  • 이 특징을 이요해서 객체지향의 상속을 구현한다.

5.9 객체의 프로퍼티 읽기나 메서드를 실행할 때만 프로토타입 체이닝이 동작한다.

📚