Object.create ()와 new SomeFunction ()의 차이점 이해


392

나는 최근 Object.create()에 JavaScript 에서 메소드를 우연히 발견했으며을 사용하여 객체의 새 인스턴스를 만드는 것과 다른 점과 다른 것을 사용하려고 할 때를 추론하려고합니다 new SomeFunction().

다음 예제를 고려하십시오.

var test = {
  val: 1,
  func: function() {
    return this.val;
  }
};
var testA = Object.create(test);

testA.val = 2;
console.log(test.func()); // 1
console.log(testA.func()); // 2

console.log('other test');
var otherTest = function() {
  this.val = 1;
  this.func = function() {
    return this.val;
  };
};

var otherTestA = new otherTest();
var otherTestB = new otherTest();
otherTestB.val = 2;
console.log(otherTestA.val); // 1 
console.log(otherTestB.val); // 2

console.log(otherTestA.func()); // 1
console.log(otherTestB.func()); // 2

두 경우 모두 동일한 동작이 관찰됩니다. 이 두 시나리오의 주요 차이점은 다음과 같습니다.

  • Object.create()실제로 사용 된 객체 는 새로운 객체의 프로토 타입을 형성하는 반면 new Function(), 선언 된 속성 / 함수는 프로토 타입을 형성하지 않습니다.
  • Object.create()기능적 구문 과 마찬가지로 구문을 사용하여 클로저를 작성할 수 없습니다 . JavaScript의 어휘 (vs 블록) 유형 범위가 주어지면 논리적입니다.

위의 진술이 맞습니까? 그리고 뭔가 빠졌습니까? 언제 다른 것을 사용 하시겠습니까?

편집 : 위 코드 샘플의 jsfiddle 버전 링크 : http://jsfiddle.net/rZfYL/


답변:


247

Object.create에 사용 된 객체는 실제로 새 객체의 프로토 타입을 형성하며, new Function () 양식에서와 같이 선언 된 속성 / 함수는 프로토 타입을 형성하지 않습니다.

예, Object.create첫 번째 인수로 전달 된 객체에서 직접 상속되는 객체를 만듭니다.

생성자 함수를 사용하면 새로 생성 된 객체가 생성자의 프로토 타입에서 상속됩니다. 예 :

var o = new SomeConstructor();

위의 예에서는에서 o직접 상속합니다 SomeConstructor.prototype.

여기에는 차이점이 있습니다. 반면 새로 생성 된 객체가에서 상속 받도록 설정 하면 Object.create어떤 것도 상속하지 않는 객체를 만들 수 있습니다 .Object.create(null);SomeConstructor.prototype = null;Object.prototype

기능적 구문에서와 마찬가지로 Object.create 구문으로 클로저를 작성할 수 없습니다. JavaScript의 어휘 (vs 블록) 유형 범위가 주어지면 논리적입니다.

예를 들어 속성 ​​설명자 인수를 사용하여 클로저를 만들 수 있습니다.

var o = Object.create({inherited: 1}, {
  foo: {
    get: (function () { // a closure
      var closured = 'foo';
      return function () {
        return closured+'bar';
      };
    })()
  }
});

o.foo; // "foobar"

저는 Object.createCrockford의 심이 아니라 ECMAScript 5th Edition 방법 에 대해 이야기하고 있습니다.

이 방법은 최신 브라우저에서 기본적으로 구현되기 시작 합니다 . 이 호환성 표를 확인하십시오 .


2
@CMS 2 질문. 1) Object.create (null)의 범위 체인이 여전히 전역 범위 (예 : 브라우저의 '창')에서 종료됩니까, 아니면 자체적으로 종료됩니까? 2) 왜 Object.create가 도입되었는지 (예를 들어 어떤 기능이 누락 되었습니까?) 왜 new Function () 대신에 그것을 사용할지 명확하지 않습니다.
Matt

9
@Matt, 1) 스코프 체인은 여기서 실제로 관련된 개념이 아니며, 스코프 체인은 식별자 분석 과 관련이 있습니다 . 예 : foo;현재 어휘 환경 에서 어떻게 해결 되는지 . 2) 상속을 구현하는 쉬운 방법을 제공하기 위해 정말 강력한 구조입니다. IMO 정말 간단하고 가벼우므로 사용 하겠지만 프로덕션 코드의 경우 ES5가 광범위하게 지원 될 때까지 약간의 시간을 기다려야합니다. 누락 된 기능, "원시"개체를 생성한다는 사실 Object.create(null);이 누락 된 경우 신뢰할 수있는 해시 테이블과 같은 개체를 구현하는 것이 매우 유용합니다.
CMS

@CMS 감사합니다. 따라서 'Object.create'를 사용하여 객체를 만들 때 프로토 타입이 될 객체를 선택할 수 있습니다.
안슐

@CMS OK이므로 반복 할 때 헛소리 Object.create(null)를 사용할 필요가 없다는 것을 의미합니다. ?? hasOwnProperty()고마워요 물론, 모든 사람들이 사용 하지는 않기 때문에 모든 사람들이 여전히 할 것이므로 그것이 진짜 이익인지 확신 할 수 없습니다 ... 지금까지 나는 완전히 확신 할 수없는 다른 "이점"을 발견했습니다 . hasOwnPropertyObject.create(null)Object.create()
949300

425

아주 간단했다 new X입니다 Object.create(X.prototype)추가로 실행하는 constructor기능. (그리고 대신 실제 표현식의 결과가되어야하는 실제 객체에 constructor기회를 return줍니다 this.)

그게 다야. :)

나머지는 그 new어느 것도 그 정의를 읽지 않기 때문에 나머지 대답은 혼란 스럽습니다 . ;)


23
+1 단순성과 선명도! (Object.create (null)은 좋은 옵션으로 보이지만 언급해야 할 수도 있습니다).
user949300

가는 길을 간결하게 유지하십시오
Bill

그건 그냥 "그래서, 대기의 질문을 남긴다 기능 프로토 타입이 너무 ? 그와의 관계를 무엇 객체의 프로토 타입은?"
Qwertie

3
@ Qwertie : JS에서는 모든 것이 객체입니다. :) 그들은 Java에서 그것을 복사했고, SmallTalk에서 그것을 복사했고, 그는 그것을 끝까지 갔다. “긴급한”사례로 일반적인 삶을 편하게 해줍니다.
Evi1M4chine

실제로 @ Evi1M4chine은 Java에서 함수는 객체가 아니며 (그 문제에 대한 기본 요소도 아니며) 객체에는 프로토 타입이 없으므로 비교가 적합하지 않은 것 같습니다. JS가 다른 인기있는 OO 언어와 다르게 작동한다는 사실은 혼란의 주요 원인입니다 (그리고 브라우저가 함수 및 프로토 타입을 포함한 객체 네트워크를 쉽게 시각화 할 수있는 방법을 제공하지는 않습니다). 추신 : 나는이 링크가 유용 하다고 생각했다
Qwertie

204

다음은 두 호출 모두에 대해 내부적으로 수행되는 단계입니다.
(힌트 : 유일한 차이점은 3 단계입니다)


new Test():

  1. new Object()obj 생성
  2. 설정 obj.__proto__Test.prototype
  3. return Test.call(obj) || obj; // normally obj is returned but constructors in JS can return a value

Object.create( Test.prototype )

  1. new Object()obj 생성
  2. 설정 obj.__proto__Test.prototype
  3. return obj;

따라서 기본적으로 Object.create생성자를 실행하지 않습니다.


@Ray 그래서 object.create를 사용하여 폰트는 생성자 함수에서 언급 된 함수의 속성을 가지고 있습니까?

속성이 비공개이며 프로토 타입에 지정되지 않는 한 @sortednoun, 예, 상속되지 않으며 새 객체에 속성 이 없습니다 (그리고 추가로 최종 프로토 타입 속성을 얻을 수 있습니다 부모 생성자 (한 번 이상 실행 된 경우)
카마 페더

대부분의 생성자 함수와 마찬가지로 메소드는 반환 된 객체 내에 정의되며 new기본적으로 모든 함수가 복제되지만 Object.create그렇지는 않습니다.
SparK

61

설명해 드리겠습니다 ( 블로그 에 대한 자세한 내용 참조 ).

  1. Car생성자 를 작성할 때 var Car = function(){}, 이것은 내부적으로 방법입니다 : 자바 스크립트 객체를 만들 때 프로토 체인의 다이어그램 우리는 접근 할 수없는 하나의 {prototype}숨겨진 링크 Function.prototype와 접근 할 수 있고 실제 의 prototype링크를 Car.prototype가지고 있습니다 . Function.prototype과 Car.prototype 모두에 대한 숨겨진 링크가 있습니다.constructorCarObject.prototype
  2. 우리가 사용하여 동일한 두 개체를 생성 할 때 new연산자와 create방법을 우리는 이런 식으로 할 필요가 : Honda = new Car();Maruti = Object.create(Car.prototype). 다른 객체 생성 방법을위한 프로토 타입 체인 다이어그램 무슨 일이야?

    Honda = new Car();— 이와 같은 객체를 만들면 숨겨진 {prototype}속성이 가리 킵니다 Car.prototype. 따라서 여기서 {prototype}Honda 객체는 항상 있습니다 . 객체 Car.prototype{prototype}속성 을 변경할 수있는 옵션이 없습니다 . 새로 만든 객체의 프로토 타입을 변경하려면 어떻게합니까?
    Maruti = Object.create(Car.prototype)— 이와 같은 객체를 만들 때 객체의 {prototype}속성 을 선택할 수있는 추가 옵션이 있습니다 . Car.prototype을 원하는 경우 {prototype}함수의 매개 변수로 전달하십시오. {prototype}객체를 원하지 않으면 다음 null과 같이 전달할 수 있습니다 Maruti = Object.create(null).

결론 —이 방법 Object.create을 사용하면 객체 {prototype}속성을 자유롭게 선택할 수 있습니다. 의 new Car();자유는 없습니다.

OO JavaScript에서 선호되는 방법 :

두 개의 객체 a와 가 있다고 가정합니다 b.

var a = new Object();
var b = new Object();

이제 접근하려는 a메소드가 있다고 가정 해 봅시다 b. 이를 위해서는 객체 상속이 필요합니다 ( 해당 메소드에 액세스하려는 경우에만 a프로토 타입이어야 함 b). 우리의 프로토 타입을 선택하면 a그리고 b우리는 그들이 프로토 타입을 공유하는 것을 발견 할 것이다 Object.prototype.

Object.prototype.isPrototypeOf(b); //true
a.isPrototypeOf(b); //false (the problem comes into the picture here).

문제 — 객체 a를 프로토 타입으로 원 b하지만 여기서는 프로토 타입을 b사용하여 객체 를 만들었습니다 Object.prototype. 솔루션 — ECMAScript 5가 도입 Object.create()되어 그러한 상속을 쉽게 달성 할 수 있습니다. 다음 b과 같이 객체를 생성 하면 :

var b = Object.create(a);

그때,

a.isPrototypeOf(b);// true (problem solved, you included object a in the prototype chain of object b.)

따라서 객체 지향 스크립팅을 수행하는 경우 Object.create()상속에 매우 유용합니다.


그렇다면 생성자 호출이없는 객체 생성과 다소 유사합니까? 수업의 모든 혜택을 누리 실 수 있습니다. obj instanceof Class도 true입니다. 그러나 우리는 new를 통해 Class 함수를 호출하지 않습니다.
Praveen

@Anshul 당신은 두 객체가 다르고 다른 메모리를 가리 키기 때문에 어느 것이 옳은지 a.isPrototypeOf(b);를 반환 한다고 말했습니다 false. new운영자 와 함께하는 올바른 방법은 다음과 같습니다. - jsfiddle.net/167onunp .
Sagar Karira

왜이 대신에 b의 프로토 타입 속성을 a로 설정하지 않습니까?
Amnestic

블로그의 기사도 마음에 들었습니다. 개념을 훨씬 잘 이해하도록 도와주었습니다. 감사합니다.
steady_daddy

1
결론은 모든 것을 말합니다.
kushalvm

44

이:

var foo = new Foo();

var foo = Object.create(Foo.prototype);

꽤 비슷합니다. 한 가지 중요한 차이점은 new Foo실제로 생성자 코드를 실행하지만 Object.create다음과 같은 코드는 실행하지 않는다는 것입니다.

function Foo() {
    alert("This constructor does not run with Object.create");
}

2- 매개 변수 버전을 사용하면 Object.create()훨씬 더 강력한 작업을 수행 할 수 있습니다.


1
좋은 설명입니다. 내가 사용하여 추가 할 수 있습니다 Object.create프로토 타입 상속을 활용하면서이 코드에서 생략 생성자 함수에 당신을 수 있습니다처럼 간단한 형태로.
Ricky Boyce

23

차이점은 이른바 "가상 고전 대 프로토 타입 상속"입니다. 코드에서 한 가지 유형 만 사용하고 두 유형을 혼합하지 말 것을 제안합니다.

의사 클래식 상속 ( "new"연산자 사용)에서 먼저 의사 클래스를 정의한 다음 해당 클래스에서 오브젝트를 작성한다고 가정하십시오. 예를 들어, 의사 클래스 "Person"을 정의한 다음 "Person"에서 "Alice"및 "Bob"을 작성하십시오.

프로토 타입 상속 (Object.create 사용)에서는 특정 사람 "Alice"를 직접 만든 다음 "Alice"를 프로토 타입으로 사용하여 다른 사람 "Bob"을 만듭니다. 여기에는 "클래스"가 없습니다. 모두 개체입니다.

내부적으로 JavaScript는 "프로토 타입 상속"을 사용합니다. "의사 고전적인"방법은 설탕 일뿐입니다.

두 가지 방법을 비교하려면 이 링크 를 참조하십시오 .


21
function Test(){
    this.prop1 = 'prop1';
    this.prop2 = 'prop2';
    this.func1 = function(){
        return this.prop1 + this.prop2;
    }
};

Test.prototype.protoProp1 = 'protoProp1';
Test.prototype.protoProp2 = 'protoProp2';
var newKeywordTest = new Test();
var objectCreateTest = Object.create(Test.prototype);

/* Object.create   */
console.log(objectCreateTest.prop1); // undefined
console.log(objectCreateTest.protoProp1); // protoProp1 
console.log(objectCreateTest.__proto__.protoProp1); // protoProp1

/* new    */
console.log(newKeywordTest.prop1); // prop1
console.log(newKeywordTest.__proto__.protoProp1); // protoProp1

요약:

1) new 키워드에는 두 가지 유의할 사항이 있습니다.

a) 함수는 생성자로 사용됩니다

b) function.prototype객체가 __proto__속성에 전달 되거나 ...__proto__ 지원되지 않는 경우 새 객체가 속성을 찾는 두 번째 장소입니다

2) Object.create(obj.prototype)당신 과 함께 객체 ( obj.prototype)를 만들고 그것을 의도 한 객체로 전달하고 있습니다 .. 이제 새로운 객체 __proto__가 obj.prototype을 가리키고 있다는 차이점이 있습니다 (xj9로 ans를 참조하십시오)


15

객체 생성 변형.


변형 1 : ' new Object () '-> 인수없는 객체 생성자.

var p1 = new Object(); // 'new Object()' create and return empty object -> {}

var p2 = new Object(); // 'new Object()' create and return empty object -> {}

console.log(p1); // empty object -> {}

console.log(p2); // empty object -> {}

// p1 and p2 are pointers to different objects
console.log(p1 === p2); // false

console.log(p1.prototype); // undefined

// empty object which is in fact Object.prototype
console.log(p1.__proto__); // {}

// empty object to which p1.__proto__ points
console.log(Object.prototype); // {}

console.log(p1.__proto__ === Object.prototype); // true

// null, which is in fact Object.prototype.__proto__
console.log(p1.__proto__.__proto__); // null

console.log(Object.prototype.__proto__); // null

여기에 이미지 설명을 입력하십시오


변형 2 : ' new Object (person) '-> 인수가있는 객체 생성자.

const person = {
    name: 'no name',
    lastName: 'no lastName',
    age: -1
}

// 'new Object(person)' return 'person', which is pointer to the object ->
//  -> { name: 'no name', lastName: 'no lastName', age: -1 }
var p1 = new Object(person);

// 'new Object(person)' return 'person', which is pointer to the object ->
//  -> { name: 'no name', lastName: 'no lastName', age: -1 }
var p2 = new Object(person);

// person, p1 and p2 are pointers to the same object
console.log(p1 === p2); // true
console.log(p1 === person); // true
console.log(p2 === person); // true

p1.name = 'John'; // change 'name' by 'p1'
p2.lastName = 'Doe'; // change 'lastName' by 'p2'
person.age = 25; // change 'age' by 'person'

// when print 'p1', 'p2' and 'person', it's the same result,
// because the object they points is the same
console.log(p1); // { name: 'John', lastName: 'Doe', age: 25 }
console.log(p2); // { name: 'John', lastName: 'Doe', age: 25 }
console.log(person); // { name: 'John', lastName: 'Doe', age: 25 }

여기에 이미지 설명을 입력하십시오


변형 3.1 : ' Object.create (person) '. 간단한 객체 'person'과 함께 Object.create를 사용하십시오. 'Object.create (person)'는 비어있는 새 객체를 만들고 반환하고 동일한 새 비어있는 객체에 '__proto__'속성을 추가합니다. 이 속성 '__proto__'은 (는) 'person'개체를 가리 킵니다.

const person = {
        name: 'no name',
        lastName: 'no lastName',
        age: -1,
        getInfo: function getName() {
           return `${this.name} ${this.lastName}, ${this.age}!`;
    }
}

var p1 = Object.create(person);

var p2 = Object.create(person);

// 'p1.__proto__' and 'p2.__proto__' points to
// the same object -> 'person'
// { name: 'no name', lastName: 'no lastName', age: -1, getInfo: [Function: getName] }
console.log(p1.__proto__);
console.log(p2.__proto__);
console.log(p1.__proto__ === p2.__proto__); // true

console.log(person.__proto__); // {}(which is the Object.prototype)

// 'person', 'p1' and 'p2' are different
console.log(p1 === person); // false
console.log(p1 === p2); // false
console.log(p2 === person); // false

// { name: 'no name', lastName: 'no lastName', age: -1, getInfo: [Function: getName] }
console.log(person);

console.log(p1); // empty object - {}

console.log(p2); // empty object - {}

// add properties to object 'p1'
// (properties with the same names like in object 'person')
p1.name = 'John';
p1.lastName = 'Doe';
p1.age = 25;

// add properties to object 'p2'
// (properties with the same names like in object 'person')
p2.name = 'Tom';
p2.lastName = 'Harrison';
p2.age = 38;

// { name: 'no name', lastName: 'no lastName', age: -1, getInfo: [Function: getName] }
console.log(person);

// { name: 'John', lastName: 'Doe', age: 25 }
console.log(p1);

// { name: 'Tom', lastName: 'Harrison', age: 38 }
console.log(p2);

// use by '__proto__'(link from 'p1' to 'person'),
// person's function 'getInfo'
console.log(p1.getInfo()); // John Doe, 25!

// use by '__proto__'(link from 'p2' to 'person'),
// person's function 'getInfo'
console.log(p2.getInfo()); // Tom Harrison, 38!

여기에 이미지 설명을 입력하십시오


변형 3.2 : ' Object.create (Object.prototype) '. 내장 객체-> 'Object.prototype'과 함께 Object.create를 사용하십시오. 'Object.create (Object.prototype)'은 비어있는 새 객체를 만들고 반환하며 동일한 새 비어있는 객체에 '__proto__'속성을 추가합니다. 이 속성 '__proto__'는 'Object.prototype'개체를 가리 킵니다.

// 'Object.create(Object.prototype)' :
// 1. create and return empty object -> {}.
// 2. add to 'p1' property '__proto__', which is link to 'Object.prototype'
var p1 = Object.create(Object.prototype);

// 'Object.create(Object.prototype)' :
// 1. create and return empty object -> {}.
// 2. add to 'p2' property '__proto__', which is link to 'Object.prototype'
var p2 = Object.create(Object.prototype);

console.log(p1); // {}

console.log(p2); // {}

console.log(p1 === p2); // false

console.log(p1.prototype); // undefined

console.log(p2.prototype); // undefined

console.log(p1.__proto__ === Object.prototype); // true

console.log(p2.__proto__ === Object.prototype); // true

여기에 이미지 설명을 입력하십시오


변형 4 : ' new SomeFunction () '

// 'this' in constructor-function 'Person'
// represents a new instace,
// that will be created by 'new Person(...)'
// and returned implicitly
function Person(name, lastName, age) {

    this.name = name;
    this.lastName = lastName;
    this.age = age;

    //-----------------------------------------------------------------
    // !--- only for demonstration ---
    // if add function 'getInfo' into
    // constructor-function 'Person',
    // then all instances will have a copy of the function 'getInfo'!
    //
    // this.getInfo: function getInfo() {
    //  return this.name + " " + this.lastName + ", " + this.age + "!";
    // }
    //-----------------------------------------------------------------
}

// 'Person.prototype' is an empty object
// (before add function 'getInfo')
console.log(Person.prototype); // Person {}

// With 'getInfo' added to 'Person.prototype',
// instances by their properties '__proto__',
// will have access to the function 'getInfo'.
// With this approach, instances not need
// a copy of the function 'getInfo' for every instance.
Person.prototype.getInfo = function getInfo() {
    return this.name + " " + this.lastName + ", " + this.age + "!";
}

// after function 'getInfo' is added to 'Person.prototype'
console.log(Person.prototype); // Person { getInfo: [Function: getInfo] }

// create instance 'p1'
var p1 = new Person('John', 'Doe', 25);

// create instance 'p2'
var p2 = new Person('Tom', 'Harrison', 38);

// Person { name: 'John', lastName: 'Doe', age: 25 }
console.log(p1);

// Person { name: 'Tom', lastName: 'Harrison', age: 38 }
console.log(p2);

// 'p1.__proto__' points to 'Person.prototype'
console.log(p1.__proto__); // Person { getInfo: [Function: getInfo] }

// 'p2.__proto__' points to 'Person.prototype'
console.log(p2.__proto__); // Person { getInfo: [Function: getInfo] }

console.log(p1.__proto__ === p2.__proto__); // true

// 'p1' and 'p2' points to different objects(instaces of 'Person')
console.log(p1 === p2); // false

// 'p1' by its property '__proto__' reaches 'Person.prototype.getInfo' 
// and use 'getInfo' with 'p1'-instance's data
console.log(p1.getInfo()); // John Doe, 25!

// 'p2' by its property '__proto__' reaches 'Person.prototype.getInfo' 
// and use 'getInfo' with 'p2'-instance's data
console.log(p2.getInfo()); // Tom Harrison, 38!

여기에 이미지 설명을 입력하십시오


좋은 요약. 감사. 오늘 도움이되었습니다 !!
Anandaraja_Srinivasan '12

11

내부적 Object.create으로이 작업을 수행합니다.

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

이 구문은 JavaScript가 Classical Inheritance를 사용한다는 환상을 없애줍니다.


25
ECMAScript 5 Object.create방법은 그 이상을 수행하며 속성 설명자 별로 속성을 정의 할 수 있으며 아무 것도 상속하지 않는 객체를 만들 수 있습니다 ( Object.create(null);). ES3에서의 행동. 추가 정보
CMS

@CMS에 동의하지만 일반적으로의 간단한 polyfill입니다 Object.create.
V. Kovpak

10

따라서에 이 대답 과에 이 비디오의 new 키워드는 다음 일을한다 :

  1. 새 객체를 만듭니다.

  2. 새 객체를 생성자 함수 ( prototype)에 연결합니다.

  3. 만드는 this새로운 객체에 변수 지점을.

  4. 새 객체를 사용하여 생성자 함수를 실행하고 암시 적 수행을 수행합니다 return this.

  5. 생성자 함수 이름을 새 객체의 속성에 할당합니다 constructor.

Object.create만 수행 1st하고 2nd단계!

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.