본문 바로가기
Front_End/Inside-JavaScript

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

by Havi 2016. 11. 27.
반응형

1. 함수 생성 방식

  • 함수 리터럴
function add(x, y) {
               return x + y:
}
  • 함수 선언문
    • 함수 리터럴 형태와 같다. 반드시 함수명이 정의되어 있어야 한다.
  • 함수 표현식
    • 함수 리터럴로 하나의 함수를 만들고, 여기서 생성된 함수를 변수에 할당하여 함수를 생성하는 것
var add = function (x, y) { //익명 함수 vs 이름이 표현된 함수(기명 함수)
     return x + y; 
};
  • Function() 생성자 함수
var add = new Fnction('x', 'y', 'return x + y');

2. 함수 호이스팅

  • 함수 선언문 형태로 정의한 함수의 유효 범위는 코드의 맨 처음부터 시작한다. 즉, 선언위치와 상관없이 사용가능!
  • 따라서 더글러스 크락포드는 함수 표현식 사용을 권장한다.(코드 구조가 엉성하게 만들어지지 않기 위해)

var add = fuction (x, y) {     return x + y; };

3. 자바스크립트에서는 함수도 객체이다. -> 일급 객체

  • 리터럴에 의해 생성
  • 변수나 배열의 요소, 객체의 프로퍼티 등에 할당 가능
  • 함수의 인자로 전달 가능
  • 함수의 리턴값으로 리턴 가능
  • 동적으로 프로퍼티를 생성 및 할당 가능

4. 함수 객체의 기본 프로퍼티

  • ECMA5 스크립트 명세서에는 모든 함수가 length와 prototype 프로퍼티를 가져야 한다고 기술하고 있다.
  • name 프로퍼티는 함수의 이름을 나타낸다.
  • caller 프로퍼티는 자신을 호출한 함수를 나타낸다.
  • arguments 프로퍼티는 함수를 호출할 때 전달된 인자값을 나타낸다.
  • [[Prototype]] = Proto
  • Object.prototype 객체 <- Function.prototype 객체 <- 일반 함수 객체

5. prototype 프로퍼티 vs [[Prototype]] 프로퍼티

  • 모든 객체에 있는 내부 프로퍼티인 [[Prototype]]은 객체 입장에서 자신의 부모 역할을 하는 프로토타입 객체를 가리킨다.
  • 반면에, 함수 객체가 가지는 prototype 프로퍼티는 이 함수가 생성자로 사용될 때 이 함수를 통해 생성된 객체의 부모 역할을 하는 프로토타입 객체를 가리킨다.
  • prototype 프로퍼티는 함수가 생성될 때 만들어진다.

6. 함수의 다양한 형태

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

window.onload = function() {     alert('this is th callback function.'); };

  • 즉시 실행 함수 - 정의함과 동시에 실행되는 함수. 익명 함수를 응용한 형태. 최초 한 번의 실행만을 필요로 하는 초기화 코드 부분 등에 사용

(function (name) {     console.log('This is the immediate function -> ' + name); })('foo');

  • 내부 함수 - 함수 내부에 정의된 함수
    • 내부 함수는 자신을 둘러싼 외부 함수의 변수에 접근 가능하다.
    • 내부 함수는 일반적으로 자신이 정의된 부모 함수 내부에서만 호출이 가능하다.
    • 하지만 부모 함수에서 내부 함수를 외부로 리턴하면, 부모 함수 밖에서도 내부 함수를 호출하는 것이 가능하다. 이와 같이 실행이 끝난 parent()와 같은 부모 함수 스코프의 변수를 참조하는 inner()와 같은 함수를 클로저라고 한다.
      • var inner = parent();

7. 함수 호출과 this

  • arguments 객체 - 함수를 호출할 때 넘긴 인자들이 배열 형태로 저장된 객체를 의미한다.(유사 배열 객체)
    • 함수를 호출할 때 넘겨진 인자(배열 형태) - 함수를 호출할 때 첫 번째 인자는 0번 인덱스, 두 번째 인자는 1번 인덱스..
    • length 프로퍼티 - 호출할 때 넘겨진 인자의 개수를 의미
    • callee 프로퍼티 - 현재 실행 중인 함수의 참조값
function sum() {
     var result = 0;
     for(var i = 0; i < arguments.length; i++) {
          result += arguments[i];
     }
     return result;
}
console.log(sum(1,2,3)); //출력값 6
console.log(sum(1,2,3,4,5,6,7,8,9)); //출력값 45
  • this - 함수가 호출되는 방식(호출 패턴)에 따라 this가 다른 객체를 참조한다.(this 바인딩)
    • 메서드 내부 코드에서 사용된 this는 해당 메서드를 호출한 객체로 바인딩
    • 함수 내부에서 사용된 this는 전역 객체에 바인딩 된다.
      • 브라우저에서 자바스크립트를 실행하는 경우 전역 객체는 window객체가 된다.
var value = 100;
var myObject = {
     value: 1,
     func1: function() {
          var that = this; //this값을 저장하는 변수의 이름은 관례상 that
          this.value += 1;
          console.log('func1() called. this.value : ' + this.value);


       func2 = function() {
               that.value += 1;
               console.log('func2() called. this.value : ' + that.value);


               func3 = function() {
                    that.value += 1;
                    console.log('func3() called. this.value : ' + that.value);
               }
               func3();
          }
          func2();
     }
};
myObject.func1();
  • 생성자 함수를 호출할 때 this 바인딩 - 기존 함수에 new 연산자를 붙여서 호출하면 해당 함수는 생성자 함수로 동작한다.
    • 반대로 생각하면 일반 함수에 new를 붙여 호출하면 원치 않는 생성자 함수처럼 동작 가능
    • 특정 함수가 생성자 함수로 정의되어 있음을 알리려고 함수 이름의 첫 문자를 대문자로 쓴다.
    • 생성자 함수가 동작하는 방식
      • 1. 빈 객체 생성 및 this 바인딩 - 엄밀히 말하면 빈 객체는 아니다. (프로토타입이 있기 때문에...), 자신을 생성한 생성자 함수의 prototype 프로퍼티가 가리키는 객체를 자신의 프로토타입 객체로 설정
      • 2. this를 통한 프로퍼티 생성
      • 3. 생성된 객체 리턴 - 특별한 리턴문이 없는 경우, this로 바인딩된 새로 생성한 객체가 리턴된다.
var Person = function(name) { //Person() 생성자 함수
     this.name = name; //함수 리턴
};
var foo = new Person('foo'); //foo 객체 생성
  • 객체 리터럴 vs 생성자 함수
    • 차이는 프로토타입 객체(proto 프로퍼티)에 있다. 리터럴은 object로 생성자 함수는 Person으로...
    • 이유는 자신을 생성한 생성자 함수의 prototype 프로퍼티가 가리키는 객체를 자신의 프로토타입 객체로 설정한다.
  • 생성자 함수를 new를 붙이지 않고 호출할 경우 - 오류가 발생할 수 있다. 이유는 일반 함수 호출과 생성자 함수를 호출할 때 this 바인딩 방식이 다르기 때문이다.
  • 더글라스 크락포드의 객체를 생성하는 별도의 코드 패턴
function A(arg) {
     if(!(this instanceof A))
          return new A(arg);
     this.value = arg ? arg : 0;
}
var a = new A(100);
var b = A(10);


console.log(a.value); //출력값 100
console.log(b.value); //출력값 10
console.log(global.value); //출력값 undefined
  • 함수 A에서는 A가 호출될 때, this가 A의 인스턴스인지를 확인하는 분기문이 추가되었다.

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

  • function.apply(thisArg, argArray)
    • 첫 번째 인자로 넘긴 객체가 this로 명시적으로 바인딩된다.
    • 두 번째 argArray 인자는 함수를 호출할 때 넘길 인자들의 배열을 가리킨다. apply() 메서드의 기능도 결국 함수 호출
// person 생성자 함수 생성
...
var foo = {}; // foo 빈 객체 생성


// apply() 메서드 호출
Person.apply(foo, ['foo', 30, 'man']);
  • Person.call(foo, 'foo', 30, ',man');
    • call()은 apply()와 기능은 같지만, 두 번째 인자에서 각각 하나의 인자로 넘긴다.
  • 생성자 함수에서 리턴값을 지정하지 않을 경우 생성된 객체가 리턴된다.

9. 프로토타입 체이닝

  • [[Prototype]] - 자바스크립트의 모든 객체는 자신의 부모인 프로토타입 객체를 가리키는 참조 링크 형태의 숨겨진 프로퍼티
    • 자바스크립트에서 객체를 생성하는 건 생성자 함수의 역할이지만, 생성된 객체의 실제 부모 역할을 하는 건 생성자 자신이 아닌 생성자의 prototype 프로퍼티가 가리키는 프로토타입 객체다.
    • proto 프로퍼티 == [[Prototype]]
  • 프로토타입 체이닝 - 자신의 부모 역할을 하는 프로토타입 객체의 프로퍼티도 마치 자신의 것처럼 접근 가능하게 해줌
var myObject = {
     name: 'foo',
     sayName: function() {
          console.log('My Name is ' + this.name);
     }
};
myObject.sayName(); //My name is foo
console.log(myObject.hasOwnProperty('name'); //true
  • hasOwnProrperty()메서드는 object()라는 내장 생성자 함수에 있는 메서드
  • 프로토타입 체이닝은 해당 객체에 접근하려고 할 때, 접근하려는 프로퍼티나 메서드가 없다면 [[Prototype]]링크를 따라 자신의 부모 역할을 하는 프로토타입 객체의 프로퍼티를 차례대로 검색하는 것
  • Object.prototype객체는 프로토타입 체이닝의 종점이다.
  • 숫자는 Number.prototype, 문자열은 String.prototype, 배열은 Array.prototype 등의 기본 내장 프로토타입 객체가 존재하며 이들 또한 Object.prototype에 프로토타입 체이닝으로 연결된다.
String.prototype.testMethod = function() {
     console.log('This is the String.prototype.testMethod()');
};
var str = 'this is test';
str.testMethod();
  • 이 메서드는 일반 문자열 표준 메서드처럼, 모든 문자열에서 접근 가능하다.
  • 프로토타입 객체 역시 자바스크립트 객체이므로 일반 객체처럼 동적으로 프로퍼티를 추가/삭제하는 것이 가능하다.
function Person(name) {
     this.name = name;
}
var foo = new Person('foo');
Person.prototype.sayHello = function() {
     console.log('hello');
}
  • 디폴트 프로토타입 객체를 다른 일반 객체로 변경 가능하다.
function Person(name) {
     this.name = name;
}
console.log(Person.prototype.constructor); //Person(name)


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


Person.prototype = { //디폴트 프로토타입 객체 변경
     country: 'korea',
};
console.log(Person.prototype.costructor); //Object()


var bar = new Person('bar');
console.log(foo.country); //undefined, 변경 시점 이전은 기존 타입 그대로 유지
console.log(bar.country); //korea, 변경 시점 이후는 변경 타입 적용
console.log(foo.constructor); //Person(name)
console.log(bar.constructor); //Object()
  • 객체의 프로퍼티 읽기나 메서드를 실행할 때만 프로토타입 체이닝이 동작한다. 객체에 없는 프로퍼티에 값을 쓰려고 할 경우는 동적으로 객체에 프로퍼티를 추가한다.


반응형

댓글