태터데스크 관리자

도움말
닫기
적용하기   첫페이지 만들기

태터데스크 메시지

저장하였습니다.

* 지난번에는 prototype에 대한 기본적인 사용, 그리고 내부적으로 어떻게 돌아가는지 살펴보았다. 그렇다면 이번에는 그러한 prototype를 유용하게 활용할 수 있는 상속과 기타 다른 여러 가지 상속 방법들에 대해서 공부해보자.


* 이전글

2012/12/10 - [속깊은 자바스크립트 강좌] 시작 (예고편)

2012/12/17 - [속깊은 자바스크립트 강좌] 자바스크립트의 Scope와 Closure 기초

2013/01/07 - [속깊은 자바스크립트 강좌] function declaration vs function expression 차이점

2013/01/10 - [속깊은 자바스크립트 강좌] 함수를 호출하는 방법과 this의 이해

2013/01/21 - [속깊은 자바스크립트 강좌] Closure의 이해 / 오버로딩 구현하기

2013/01/30 - [속깊은 자바스크립트 강좌] Closure 쉽게 이해하기/실용 예제 소스

2013/02/13 - [속깊은 자바스크립트 강좌] 쉬어가기: 웹 개발 방법론의 변화/자바스크립트의 재발견

2013/02/22 - [속깊은 자바스크립트 강좌] 객체지향의 기본: prototype



* 자바스크립트에서 상속이라니?

: 옛날에는 자바스크립트에서 굳이 상속이라는 개념을 사용할 필요가 없었다. 하지만 웹의 개발 방법론이 발달함에 따라 자바스크립트만으로 웹페이지의 동작들을 구현하기 시작하면서 객체지향의 개념과 함께 상속이 활용되는 것이 집중 받게 되면서 몇몇가지 방법들이 나오게 되었다. 그럼 초창기의 상속 방식부터 최근의 ECMAScript 표준 규격을 통해서 제안하고 있는 방법까지 쭉 한번 살펴보도록 하자.


* 초창기 상속 구현 방법

: 옛날에는 자바스크립트에서는 별로 상속이라는 개념이 생겨나기 시작했을 때에는 2가지 객체가 있으면 하나의 객체에서 사용하고 있는 함수들을 사용하고자 할 때 사용했던 방법이다. 이전에 new를 사용했을 때 일어났던 단계들을 다시 한번 생각해보자. 스펙의 링크는 아래에 참고하면 된다.


http://www.ecma-international.org/ecma-262/5.1/#sec-13.2.2


1. Let obj be a newly created native ECMAScript object.

2. Set all the internal methods of obj as specified in 8.12.

3. Set the [[Class]] internal property of obj to "Object".

4. Set the [[Extensible]] internal property of obj to true.

5. Let proto be the value of calling the [[Get]] internal property of F with argument "prototype".

6. If Type(proto) is Object, set the [[Prototype]] internal property of obj to proto.

7. If Type(proto) is not Object, set the [[Prototype]] internal property of obj to the standard built-in Object prototype object as described in 15.2.4.

8. Let result be the result of calling the [[Call]] internal property of F, providing obj as the this value and providing the argument list passed into [[Construct]] as args.

9. If Type(result) is Object then return result.

10. Return obj.


: 이번에는 1번과 10번에만 주목을 해보자. 바로 new를 실행하게 되면, 새로운 obj가 만들어지고, 마지막에는 이 obj를 리턴한다는 것을 볼 수 있다. 원래 이 obj는 8번에 명시되어있는 것처럼 this로 설정되어 여러 가지 초기화 작업을 한다는 것을 알 수 있다. 이것을 변경 10번에 obj가 아닌 내가 원하는 상속할 객체를 리턴하던 것이 초창기 상속 방법이었다.

function Person() {
        this.name = "anonymous";
        this.job = "none";
        this.sayHello = function () {
        alert("Hello, my name is " + this.name);
    };
}

function Unikys() {
    var obj = new Person();
    obj.name = "Unikys";
    obj.job = "Programmer";
    return obj;
}

var me = new Unikys();
me.sayHello();

: 이렇게 ECMAScript 표준에서 거치고 있는 단계를 중간에서 캐치하여 나만의 return 값을 설정함으로써 상속을 했었다. 하지만 이 방법은 치명적인 단점이 있는데, 바로 위의 변수 me는 Unikys의 인스턴스가 아닌 Person으로 밖에 인식을 못한다는 것이다.

alert(me instanceof Unikys); // === false;
alert(me instanceof Person);  // === true;

: 그냥 생각하면 뭐가 불편한가 생각할지도 모르겠지만, 객체지향 프로그래머들에게는 도저히 용납 못할 일일 것이다. me는 new Unikys()로 만들어냈는데, me는 Unikys의 인스턴스가 아닌것이다. 일 예로, 그림판을 자바스크립트로 만든다고 하면 Component가 있고 Square와 Circle이 이렇게 상속 받는다고 하면 실제로 Square인지, Circle인지 별도의 변수를 다시 저장해야하는 불편함이 있을 것이다. 따라서, 이 방법이 아닌 function에 기본적으로 들어있는 prototype속성을 이용해서 상속하는 방법이 정착하게 된다. 아래의 예는 가장 기본적으로 특정 object를 이용해서 prototype을 설정하여 상속한 예이다.

var person = {
    name: "anonymous",
    sayHello: function () {
        alert("Hello, my name is " + this.name);
    }
};

function Unikys() {
    this.name = "Unikys";
};

Unikys.prototype = person;

var unikys = new Unikys();
unikys.sayHello(); // === "Hello, my name is Unikys";
person.sayHello(); // === "Hello, my name is anonymous";
unikys instanceof Unikys; // === true;

: 이렇게 person을 바로 object literal로 표시해서 생성이 가능한데, 이 때의 불만은 instanceof Person과 같이 부모 객체의 종류를 확인해볼 수 있는 방법이 없다는 것이다. prototype으로 사용한 person은 function이 아니기 때문에 unikys instanceof person이라고 사용할 수 없기 때문에 이를 해결하기 위하여 나온 방법이 prototype도 new로 새로운 객체를 만들어서 Unikys.prototype으로 설정하는 방법으로 이러한 문제를 해결하고자 하였다.

function Person() {
    this.name = "anonymous";
    this.job = "none";
    this.sayHello = function () {
        alert("Hello, my name is " + this.name);
    };
}

function Unikys() {
    this.name = "Unikys";
    this.job = "Programmer";
}
Unikys.prototype = new Person(); // (1)
var unikys = new Unikys();
unikys.sayHello(); // Hello, my name is Unikys
unikys instanceof Unikys; // === true
unikys instanceof Person; // === true

: 이렇게 (1)줄과 같이 Person 함수를 이용해서 새로운 객체를 생성한 다음 다른 함수의 prototype으로 설정하게 되면 instanceof Unikys와 instanceof Person은 모두 true가 된다. 이는 바로 앞의 상속 방법과 비교하여 객체지향을 선호하는 사람들이 만족할만한 결과일 것이다. 하지만 겉으로는 이렇게 문제가 없어보이나, 속은 그렇지가 못하다. 이렇게 새로운 객체를 생성하여 설정하였을 때 Unikys 함수와 Person 함수가 내부적으로 어떻게 형성이 되었을지 이전의 글에서 봤던 내부 구조를 토대로 한번 생각해보면 아래와 비슷한 그림이 그려질 수 있을 것이다.

: 개념적으로 보면 이것이 맞는듯 하고, 이 그림과 같은 개념으로 prototype이 여러 개 이어져 있는 것이 바로 prototype chain이라고 불리운다. 만약 Person.prototype을 또 다른 객체를 new로 생성하여 설정한 경우에는 뒤에 해당하는 객체에 대한 prototype chain이 쭉 이어질 것으로 예상할 수 있다.


* 팀: prototype이 2단이든 3단이든 쭉 이어져 있는 것도 가능하지만, 너무 긴 prototype chain은 성능 저하의 원인이 될 수 있음을 기억하자. prototype의 2단계 아래에 있는 변수를 접근하는 것은 prototype의 1단계 아래에 있는 변수를 접근하는 것보다 더 시간이 소요되는 것은 그림을 통해서 충분히 이해할 수 있을 것이다. 따라서 자주 사용하는 변수는 반드시 prototype chain의 상위에 놓고 제일 좋은 것은 로컬 변수를 사용하는 것이 성능상 유리하다는 것을 항상 생각해두자. 이에 대해서 나중에 한번 자바스크립트 성능 최적화에 대해서 이야기할 때 더 자세하게 살펴보자.


: 위에서 테스트했던 것처럼 instanceof Unikys와 instanceof Person이 모두 다 true로 위의 그림이 맞는듯 하지만 자바스크립트가 속에서 이를 실행하는 모습을 실제로 보면 이러한 구조가 아니다. 이전 글에서 잠깐 언급했던 부분인데, constructor는 객체에 있는 것이 아니라 바로 prototype에 있는 것이라고 했었고, implicit link로 이어져 있기 때문에 객체에서는 아무런 제약없이 접근이 가능한 것으로 되어있다. 하지만 constructor가 있는 prototype을 다른 새로운 객체로 덮어씌우게 되면 원래 자기 자신의 constructor는 상실하게 되고, 더 이상 new Unikys()로 생성한 객체들의 constructor는 Unikys가 아니고 prototype으로 덮어 씌운 객체의 constructor를 얻게 되어 Unikys가 실제 생성자임에도 불구하고 prototype으로 수정한 객체의 constructor로 바뀌게 된다. 이것을 그림으로 표현하면 아래와 같은 구조가 된 것이다.


: 내부적으로는 위와 같은 구조를 가지고 있다. 이러한 구조를 보면, var unikys에서 생성한 객체는 Unikys.prototype에 implicit link가 걸려있는데, 그 Unikys.prototype에 설정된 new Person() 객체는 다시 Person.prototype에 implicit link가 걸려있는 양상이다. 따라서 이것은 Unikys.prototype을 새로운 객체로 설정함으로써 내부적인 constructor의 연결이 깨진 것이라고 볼수도 있고, unikys를 실제로 생성한 것은 function Unikys()이지만 unikys.constructor로 생성자를 출력해보면 생성자가 function Unikys()가 아니라 function Person()으로 나타나는 것을 확인할 수 있다. 일부 자바스크립트 개발자들은 그래서 이렇게 new와 prototype을 수정해서 상속을 하는 것을 좋아하지 않는다. 일단 new를 사용하는 것부터 '자바스크립트스럽지 않다'라는 주장을 하기도 하고(많은 자바스크립트 개발자들은 자바에서 억지로, '괜히' 가져온 문법 중 하나라고 여긴다) 이렇게 자바스크립트에서 원래 의도로 사용해야할 constructor의 연결이 깨지는 현상을 두고 자바스크립트에서는 이렇게 사용하는 것을 의도한 것이 아니라는 주장을 하기도 한다. 그렇다면 이들이 주장하는 상속 방법은 무엇일까?


* 다음 방법으로 넘어가기 전에, 위처럼 constructor의 연결이 깨졌는데도 instanceof가 모두 정상적으로 의도한대로 나온 것이 의아할지도 모른다. 그렇다면 왜 instanceof는 정상적으로 작동했을까? 이것 또한 다시 한번 ECMAScript 표준으로 들어가서 보게 된다면 단순에 이해할 수 있다. 먼저 instanceof의 스펙을 살펴보자. 아래 링크는 instanceof의 동작 방식을 설명하는 원문의 링크이다.


http://www.ecma-international.org/ecma-262/5.1/#sec-11.8.6


The production RelationalExpression : RelationalExpression instanceof ShiftExpression is evaluated as follows:

  1. Let lref be the result of evaluating RelationalExpression.
  2. Let lval be GetValue(lref).
  3. Let rref be the result of evaluating ShiftExpression.
  4. Let rval be GetValue(rref).
  5. If Type(rval) is not Object, throw a TypeError exception.
  6. If rval does not have a [[HasInstance]] internal method, throw a TypeError exception.
  7. Return the result of calling the [[HasInstance]] internal method of rval with argument lval.

: 여기서 7번을 보면 오른쪽 인자의 [[HasInstance]] 함수를 왼쪽 인자의 값으로 호출한 결과를 리턴하고 있다. 그렇다면 여기서 [[HasInstance]]가 무엇인지 찾아봐야할 것이다. 아래 링크에 [[HasInstance]] 원문을 참고할 수 있다.


http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.5.3


Assume F is a Function object.

When the [[HasInstance]] internal method of F is called with value V, the following steps are taken:

  1. If V is not an object, return false.
  2. Let O be the result of calling the [[Get]] internal method of F with property name "prototype".
  3. If Type(O) is not Object, throw a TypeError exception.
  4. Repeat
    1. Let V be the value of the [[Prototype]] internal property of V.
    2. If V is null, return false.
    3. If O and V refer to the same object, return true.

: 여기서 보면 F는 Function 객체로 instanceof 뒤에 오는 인자는 반드시 Function 객체이어야한다는 것을 알 수 있다. 이것은 위에서 object literal로 person객체를 생성했던 상속 방식에서 instanceof를 사용하지 못했던 것 원인을 확인할 수 있다. 그리고 2번에서 O를 해당 F의 prototype으로 설정하고, 인자로 넘어오는 V의 prototype을 가져와서 서로 비교하여 prototype이 같다면 true를 리턴하고 prototype chain을 따라가다가 마지막에 다다르면 false를 리턴하는 방식이다. 이것은 약간 독특한 것이 객체지향에서는 생성자를 기준으로 판단하는 것에 반해 자바스크립트에서는 내부적으로 생성자를 기준으로 판단하는 것이 아니라, prototype을 기준으로 판단하고 있는 것이다. 따라서, 위의 그럼에서 이 로직을 적용해서 따라가 보면, var unikys의 prototype은 new Person()이고, function Unikys의 prototype 역시 new Person()으로 같다. 따라서 unikys instanceof Unikys === true가 되는 것이고, unikys instanceof Person인 경우에 prototype chain을 따라가다가 new Person()의 prototype인 객체와 function Person의 prototype 객체가 같으므로 이 역시도 true를 리턴하게 되는 결과를 보여주는 것이다. 내부적으로 생성된 생성자 링크는 깨졌어도, prototype을 기준으로 instanceof가 동작하기 때문에 의미적인 동작은 제대로 동작하게 되는 것이다.



* Object.create([Object] {,[Object]})

: '자바스크립트' 개발자들은 내부에서 이렇게 constructor가 망가지는 것을 원하지 않았기 때문에 고안한 것이 Object.create 함수이다. 물론 이러한 여론에다가 new를 사용한 객체의 생성이 '자바스크립트스럽지 않다'라는 의견 또한 많이 반영되어 new라는 키워드의 사용을 자제하고 싶어하는 사람들이 의 의견 또한 이러한 별도로 객체를 생성하는 함수를 제안하게 되고 이것이 표준 규격에도 들어가게 된 것이다. 하지만 표준에는 약간 늦게 추가되어 현재는 이 Object.create함수는 IE는 9이상 버전, 나머지 크롬/파폭/사파리5 이상의 브라우져에서 지원해주는 함수이다. IE9 이상 버전만 지원해주다 보니 아직은 마음대로 사용할 수는 없는 함수이지만, 앞으로 '자바스크립트' 개발자로서 상속을 활용하게 된다면 이러한 개념을 이해하고 Object.create 함수를 이용하는 것도 좋을 것이다.


* Object.create 지원 현황 (Safari와 IE를 빼면 다 오래된 구버전이므로 신경 안써도 된다.)

 Chrome

Firefox

 IE

Opera 

 Safari

 Mobile FF

Android 

Mobile Opera 

Mobile Safari 

 5

11.60 

 4

YES 

11.50 

YES 


: 처음으로 이 Object.create의 형태를 주장했던 것은 바로 Douglas Crockford로 그의 아주 오래된 자바스크립트 동영상 강좌 시리즈에서부터 이러한 구조에 대한 언급을 찾아볼 수 있다. 이 함수는 내부적으로 다음과 같은 형태로 돌아가고 있다고 볼 수 있다.

Object.create = function (o) {
    function F() {}
    F.prototype = o;
    return new F();
}

: 여기서 보면 function안의 F 함수는 아무런 초기화도 하지 않는 기본 함수가 되고, 이 F의 prototype만 인자로 받는 객체로 수정하여 새로운 객체를 생성하여 리턴하게 된다. 여기서 주의할 점은 바로 Object.create() 함수의 인자 o는 상속을 할 객체가 아닌, prototype으로 설정할 객체라는 점이다. 따라서, 간단한 사용법은 아래와 같다.

function Person(name) {
    this.name = name;
};
Person.prototype = {
    yell: function () {
        alert("My name is " + this.name);
    }
};
var unikys = Object.create(Person.prototype); // #1
unikys.name = "Unikys";  // #2
unikys.yell();

: 여기서 위에서 주의할점이라고 했던, #1에서 Object.create()의 인자를 보면 Person이 아니라, Object.create(Person.prototype)으로 Person의 prototype으로 객체를 생성하고 있는 것을 볼 수 있다. 이렇게 Person를 직접 넘겨주지 않고 prototype을 넘겨주는 것은 위의 Object.create의 내부적인 형태를 한단계씩 살펴보면 이해할 수 있을 것이다. 인자로 넘어온 o를 이용해서 그대로 기본함수 F의 prototype으로 설정해주고 있는 것이다. 그리고 new와 함수의 prototype을 설정하던 초창기 상속 방식과 비교를 해보면 #2에서 unikys.name을 Person 함수의 생성자에 할당하는 것이 아니라 직접 설정하는 것을 볼 수 있다. 이러한 것이 어떻게 보면 개발자의 입장에서 다소 귀찮은 일이 될지도 모르지만 Object.create의 2번째 인자로 이러한 초기화 작업을 별도로 진행할 수 있다. 여기서는 생성자가 호출이 되지 않았고 오로지 prototype만을 넘겨줬기 때문에 별도로 초기화를 해주는 부분을 추가한 것이다. 하지만 자바스크립트의 내부적으로 보면 위의 초창기 상속 방법에서처럼 constructor의 링크가 깨어지는 일은 없다. 이러한 Object.create함수는 ECMAScript 5의 표준에도 명시가 되어있으므로, 한번 살펴봐도 좋을 것이다.


http://www.ecma-international.org/ecma-262/5.1/#sec-15.2.3.5


: 상속의 여부를 따지기 위해서 초창기의 상속 방법에서는 instanceof를 사용하고 있다. 하지만 Object.create를 사용하게 되면 instanceof의 사용은 상황에 따라서 조금 다르게 인지가 될지도 모른다. 인자를 o를 prototype으로 받기 때문에 위에서 인자로 쓰고 있는 Person.prototype으로 unikys instanceof Person.prototype를 하면 안되고 해당 prototype를 가지고 있는 Person 함수를 넘겨줘야한다.

console.log(unikys instanceof Person); // === true

: 사실 위의 초창기의 상속 방법을 생각하면 당연한 것이지만, Object.create를 이용하는 조금 다른 경우에는 Object.create의 인자로 사용하는 동일한 객체를 instanceof와 비슷하게 하고 싶은 경우가 있을지도 모른다. 아래와 같이 하나의 object literal로 생성한 객체를 인자로 사용하는 경우가 그러한 상황일 것이다.

var person = {
    yell: function () {
        alert("My name is " + this.name);
    }
};
var unikys = Object.create(person);

console.log(unikys instanceof person); // === TypeError!

: 이러한 경우는 다소 이상할지도 모르지만, Object.create의 인자가 prototype을 받는 것을 기억하면, unikys의 prototype이 user로 설정되는 것이지, instanceof의 대상이 되지는 않는다. unikys의 prototype은 user 이지만, user의 prototype은 설정되지 않았기 때문에 빈 Object{}가 된다. 이것을 체크하기 위해서 Object.getPrototypeOf() 함수를 사용하면 된다.

console.log(Object.getPrototypeOf(unikys) === person); // === true
console.log(Object.getPrototypeOf(person)); // === Object {}

: 만약 위의 경우 user를 이용해서 instanceof와 비슷한 결과를 내고 싶다면 instanceof가 아닌 아래와 같이 isPrototypeOf 함수를 사용하면 된다.

console.log(person.isPrototypeOf(unikys)); // === true;
console.log(Object.prototype.isPrototypeOf(unikys)); // === true

: 주의할 점은  person이 인자로 넘어오는 unikys의 prototype인지 묻는 함수이기 때문에 instanceof에서 사용하고 있는 좌우의 인자가 바뀐 것을 알 수 있다. 이렇게 Object.create를 사용하게 되면 new와는 비슷하지만 위에서 unikys.name을 직접 설정했던것과 같은 과정을 거쳐야하는 것은 귀찮은 일이다. 이러한 생성자를 그대로 이용하고 싶다면, 다음과 같이 생성자 호출을 직접할 수도 있다.

var unikys = Object.create(Person.prototype);
Person.call(unikys, "Unikys");
unikys.yell(); // "My name is Unikys"

: User 생성자를 이렇게 호출함으로써 User에서 설정되어야할 값들을 설정할 수 있게 된다. 이러한 과정을 하나의 함수로 묶어준다면, 자신만의 간단한 상속 함수를 만들수도 있을 것이다. 하지만 위의 소제목에서 보다시피 Object.create는 선택적으로 2번째 인자를 받음으로써 생성하는 객체를 초기화하기도 한다. 2번째 인자의 사용법은 아래와 같다.

function Person(name) {
    this.name = name;
};
Person.prototype = {
    yell: function () {
        alert("My name is " + this.name);
    }
};
var unikys = Object.create(Person.prototype, {
    name: {value: "Unikys"}
});
unikys.yell(); // "My name is Unikys"
unikys.name = "Suniky";
unikys.yell(); // #1: 여기의 결과는?

: 이렇게 설정할 변수/속성의 값을 미리 설정할 수 있다. 하지만 여기서 #1의 2번째 yell을 하게 되면 무엇이라고 나오는지 살펴보면, 이상하게도, 바로 윗줄에서 설정한 "My name is Suniky"가 아니고, 원래의 값인 "My name is Unikys"라고 나오게 된다. 이렇게 기본 값만을 설정하게 되면 이 속성은 읽기 전용이 되어서 값을 수정할 수 없게 된다. 따라서, (조금 귀찮지만) 추가적인 설정을 해주면 된다.


var unikys = Object.create(Person.prototype, {
    name: {
        value: "Unikys",
        configurable: true,
        enumerable: true,
        writable: true
    }
});

: 위에서 한 세팅은 Object.defineProperty의 스펙에도 나와있기는 하지만, 간단하게 설명을 한다면, configurable은 해당 속성을 삭제할 수 있느냐 여부를 설정, enumerable은 for-in 등과 같이 루프를 돌 때 보이게 설정하는 여부, writable은 해당 속성에 값을 써 넣을 수 있는지 여부를 설정하는 것이고, 모두 default로 false 값을 가지게 된다. 위의 기능들 중에서 enumerable을 잘 활용한다면 for-in에서 if (obj.hasOwnProperty(key)) 를 매번 했던 것도 안해도 되므로 설계를 잘하면 프로그래밍 상의 에러를 줄이고 예기치 못하게 발생할 문제들을 해결할 수 있게 되므로 매우 편리하게 사용할 수 있을 것이다. 또한, configurable이나 writable등을 설정함으로써 나의 라이브러리를 배포하고자 한다면, readonly로 나의 라이브러리와 모듈들을 보호할 수 있을테니 라이브러리를 개발하고자한다면 이러한 정보는 알아두면 좋을 것이다.


* new와 Object.create의 결합

: 이러한 Object.create를 사용하게 되면 초창기의 new를 이용하여 객체를 생성하는 방법에 이미 익숙해져있다면, 다소 귀찮은 일일지도 모른다. 이럴 때에는 그대로 사용하면서 prototype으로 설정하는 객체를 Object.create로 설정하면서 .constructor를 다시 설정해주면 된다. 이는 순수하게 내부적으로 제대로 설정되지 않는 부분들에 대하여 제대로 설정해주고, 기존에 자바스크립트가 그대로 안고 가고 있었던 문제들을 해결하는 방법이기도 하다.

Unikys.prototype = Object.create(Person.prototype); //prototype 상속
Unikys.prototype.constructor = Unikys; // 깨진 constructor 링크를 제대로 수정
var unikys = new Unikys();

: 사실 겉으로 보기에는 그다지 문제가 되지도 않는 것들이지만, 언젠가는 이러한 잘못된 내부적인 동작이 문제를 일으킬지 아무도 모르기 때문에 이러한 현상과 이를 해결하는 방법에 대한 이해는 하고 있어서 나쁠 것이 없을 것이다.


* 크로스 브라우져 호환성

: 언제나 표준에 늦게 따라오는 브라우져는 있기 때문에(IE라던가..) 호환이 되기 이전의 브라우져들도 지원해주기 위하여 작업을 해야하는 경우도 많다. 만약 지금도 Object.create를 크로스 브라우져로 사용하고 싶다면 MDN에서 추천하고 있는 방법을 사용하면 쉽게 활용할수도 있다. 


https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create


if (!Object.create) {
    Object.create = (function(){
        function F(){}
            return function(o){
            if (arguments.length != 1) {
                throw new Error('Object.create implementation only accepts one parameter.');
            }
            F.prototype = o;
            return new F();
        }
    })();
}


* Object.create()를 써야하는가?

: 솔직히 말하면 아직은 이른 시기인 것 같다. 이제 막 ECMAScript5의 표준에 들어갔고, 하위 브라우져들은 지원하지 않는 것을 굳이 위험성을 안고 100% 호환되는 new 를 안 쓸 이유가 없기 때문이다. 그렇다면 왜 이러한 글을 썼는가? 자바스크립트라는 언어를 공부해보면 정말로 너무나 완벽하고 매우 매력적이지만 처음에 만들어졌을 때 약간 대충(?) 만든 탓에 위에서 언급한 것과 같은 결함들이 곳곳 숨겨져 있기 때문에 작은 위험성도 미리 알고 대처를 하는 것이 어떻겠느냐 하는 생각에서 쓴 것이고, 해외의 유명한 자바스크립트 개발자들은 이미 'new'라는 키워드를 안 쓰는 방향으로 개발을 하기 시작했기 때문에 이러한 경향에 탑승한다고 해서 해가 될 것은 없기 때문이다. (그들이 말하는 new는 'It's not like javascript' 자바스크립트스럽지 않다는 이유이다.) 물론 계속 new를 사용하면서 개발해도 되겠지만 현재에 안주하고 살면서 문제점을 인식하지 못 하고 새로운 변화가 왔을 때를 대비하지 못 한다면 그저그런 웹개발자가 되어버릴테니까.


: 참고로 아직 Object.create가 시기상조라고 느끼는 이유 중 하나는 바로 성능 때문이기도한데, 아래의 성능 테스트를 실제로 돌려보면 10배 가량의 성능 차이를 보이기 때문에, 퍼포먼스가 필요한 경우라면 new를 사용하는 것이 더 좋을 것이다.


http://jsperf.com/object-create-vs-constructor-vs-object-literal/7






: 사실 성능만 놓고보면 왜 써야할지 모를 정도로 압도적이다. 파란색바가 동일 시간 내에 수행 가능한 명령 횟수인데, Object.create는 보이지도 않고 new를 이용한 경우만 보이는 정도이니 이것은 성능상 차이가 10배도 넘어선다고 봐도 된다. 이렇게 성능상 안 좋은 Object.create를 언제부터 사용하는 것이 일반적이 될지는 모르겠지만 IE8의 점유가 다소 적어지고나서야 가능해지지 않을까 생각해보면, 위에서 말했던 상속을 하게 될 때에 new에서 가지는 문제점을 인지하고 그 지뢰를 밟지 않게 조심해야 할 것이다.


* 다음에는 시작 맨 처음 '시작(예고편)' 글 때 던졌던 떡밥 회수를 좀 해야 성능 편으로 넘어 갈 수 있을 것 같아서, 그 전에 성능과도 다소 연관이 있는 변수 선언과 글로벌-로컬 변수의 차이에 대해서 다루어 보도록 하겠다.


[속깊은 자바스크립트 강좌] 상속, new와 Object.create의 차이 끝.


- 다음 편

2013/10/29 - [속깊은 자바스크립트 강좌] 글로벌(전역) 변수와 window 객체

2013/11/06 - [속깊은 자바스크립트 강좌] 변수 선언 방법에 대하여

2016/11/13 - [속깊은 자바스크립트 강좌] 마무리(는 책으로!)



저작자 표시 비영리 동일 조건 변경 허락
신고

이 글을 공유하세요.

  • 속깊은 2013.10.07 09:21 신고  댓글주소  수정/삭제  댓글쓰기

    감사합니다

  • 산들바람 2013.10.21 10:13 신고  댓글주소  수정/삭제  댓글쓰기

    다음 강좌가 기대됩니다. 잘 배우고 갑니다.

    • Unikys 2013.10.21 12:21 신고  댓글주소  수정/삭제

      감사합니다! 잠시 다른 글들을 쓰면서 외도(?)를 하고 있었는데 댓글을 달아주셔서 바로 이후 글을 쓰기 시작했습니다! ㅋㅋ

  • 루아kr 2014.01.28 15:23 신고  댓글주소  수정/삭제  댓글쓰기

    Object.create 사용하면서 constructor 링크가 깨지지 않았다고 설명했는데...
    constructor 링크는 상속하면서 생긴 문제가 아닌가요 ? . . 잘못된 비교로 보여요. . .

    • Unikys 2014.01.29 07:53 신고  댓글주소  수정/삭제

      일단 이 2개를 단순히 비교하는 것이 중요한 메세지가 아니라, 중요한 것은 기존의 new와 prototype을 쓰는 것은 (치명적이진 않지만) 내부적인 오동작이 있고, Object.create는 그것을 피할 수 있다는 것, 그리고 new와 prototype으로 할 수 있는 것을 그대로 Object.create로 할 수 있다는 것이 글의 핵심 메세지이고, Object.create vs new의 단상적인 비교는 아닙니다. 왜냐하면 사실 성능과 호환성 모두 new가 더 좋기도 하고 외향상 2개는 거의 같기 때문이죠. 하지만 많은 자바스크립트 개발자들은 new를 사용하는 것을 지양하고 있기도 하고, 다른 언어와 달리 함수 기반 언어인 자바스크립트에서는 휴먼에러가 생기기 쉽기 때문에 이들 사이에서 Object.create를 쓰는 것이 요즘 트랜드라는 것도 이 글에서 전하고자하는 추가적인 메세지이기도 합니다.

  • batmask 2015.01.31 22:03 신고  댓글주소  수정/삭제  댓글쓰기

    C++, JAVA로 개발하다가 Javascript 처음 공부하고 있다가 prototype이나 상속이 너무 헷갈려서 여기저기 찾던중에 여기까지 왔습니다. 상속과 prototype에 대해 가장 속시원한 글이었네요. 잘보고 갑니다 :) 마음같아선 그냥 new만쓰면 constructor 깨지는 문제같은거 신경안쓰고 아무걱정 없었으면 합니다만... 제 욕심이겠죠 ㅎㅎ

    • Unikys 2016.10.29 02:13 신고  댓글주소  수정/삭제

      아주 복잡한 객체지향 로직을 사용한다면야 신경 쓰는게 좋지만, 솔직히 90% 이상의 경우에는 그냥 신경 안 쓰셔도 됩니다 크크

  • 프로가 되고픈 2016.10.28 14:35 신고  댓글주소  수정/삭제  댓글쓰기

    상속의 방법 예제중에서 new Person() 을 프로토타입 속성에 넣기
    unikys.yell() 호출시 정상적인 결과 외에 추가로 undifined 가 뜨는데 이건 왜그런지 모르겠어요

    • Unikys 2016.11.01 01:26 신고  댓글주소  수정/삭제

      위의 소스 중에서는 unikys.yell() 이외에는 알림창이 뜨지 않는데, 혹시 브라우져 개발자 콘솔창에다가 하신거라면 unikys.yell()의 리턴 값 undefined가 표시된 것이라 생각합니다.

  • falsy 2017.06.05 01:45 신고  댓글주소  수정/삭제  댓글쓰기

    개인적으로... 이 오래된 글 속에서도 모르는게 너무 많아 부끄러워지네요.
    자주 이곳에 많은 글들을 통해서 정말 많이 배우고 있습니다. 다른 글에서도 댓글을 남겼는지 기억이 잘 나질 않네요..
    여튼 다시 한번 좋은 글 너무 감사드립니다!!

질문이나 의견을 댓글로 달아 주세요