Helpex - Trao đổi & giúp đỡ Đăng nhập
350

Gần đây tôi đã tình cờ phát hiện ra Object.create()phương thức này trong JavaScript và đang cố gắng suy luận xem nó khác với việc tạo một thể hiện mới của một đối tượng như thế nào new SomeFunction()và khi nào bạn muốn sử dụng một đối tượng khác.

Hãy xem xét ví dụ sau:

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

Lưu ý rằng hành vi tương tự được quan sát trong cả hai trường hợp. Dường như với tôi rằng sự khác biệt chính giữa hai kịch bản này là:

  • Đối tượng được sử dụng trong Object.create()thực tế tạo thành nguyên mẫu của đối tượng mới, trong khi trong các new Function()thuộc tính / hàm được khai báo không tạo thành nguyên mẫu.
  • Bạn không thể tạo các bao đóng với Object.create()cú pháp như bạn làm với cú pháp chức năng. Điều này hợp lý với phạm vi loại từ vựng (so với khối) của JavaScript.

Các tuyên bố trên có đúng không? Và tôi có thiếu thứ gì không? Khi nào bạn sẽ sử dụng cái này hơn cái kia?

EDIT: liên kết đến phiên bản jsfiddle của mẫu mã trên: http://jsfiddle.net/rZfYL/

350 hữu ích 1 bình luận 114k xem chia sẻ
10 trả lời 10
231

Đối tượng được sử dụng trong Object.create thực sự tạo thành nguyên mẫu của đối tượng mới, trong đó như trong Hàm mới () tạo thành các thuộc tính / hàm được khai báo không tạo thành nguyên mẫu.

Có, Object.createxây dựng một đối tượng kế thừa trực tiếp từ đối tượng được truyền làm đối số đầu tiên của nó.

Với các hàm tạo, đối tượng mới được tạo kế thừa từ nguyên mẫu của hàm tạo, ví dụ:

var o = new SomeConstructor();

Trong ví dụ trên, okế thừa trực tiếp từ SomeConstructor.prototype.

Có một sự khác biệt ở đây, với việc Object.createbạn có thể tạo một đối tượng không kế thừa từ bất cứ thứ gì, Object.create(null);mặt khác, nếu bạn đặt SomeConstructor.prototype = null;đối tượng mới được tạo sẽ kế thừa từ đó Object.prototype.

Bạn không thể tạo các bao đóng với cú pháp Object.create như với cú pháp chức năng. Điều này hợp lý với phạm vi loại từ vựng (so với khối) của JavaScript.

Chà, bạn có thể tạo các bao đóng, ví dụ: sử dụng đối số mô tả thuộc tính:

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

o.foo; // "foobar"

Lưu ý rằng tôi đang nói về Object.createphương pháp ECMAScript 5 Edition , không phải là shim của Crockford.

Phương pháp này đang bắt đầu được triển khai tự nhiên trên các trình duyệt mới nhất, kiểm tra bảng tương thích này .

231 hữu ích 5 bình luận chia sẻ
366

Nói một cách đơn giản, new XObject.create(X.prototype)với constructorchức năng chạy bổ sung . (Và tạo constructorcơ hội cho returnđối tượng thực tế phải là kết quả của biểu thức thay vì this.)

Đó là nó. :)

Phần còn lại của câu trả lời chỉ là khó hiểu, vì rõ ràng không ai khác đọc định nghĩa của newmột trong hai. ;)

366 hữu ích 5 bình luận chia sẻ
191

Dưới đây là các bước xảy ra nội bộ cho cả hai cuộc gọi:
(Gợi ý: sự khác biệt duy nhất là ở bước 3)


new Test():

  1. tạo new Object()obj
  2. đặt obj.__proto__thànhTest.prototype
  3. return Test.call(obj) || obj; // normally obj is returned but constructors in JS can return a value

Object.create( Test.prototype )

  1. tạo new Object()obj
  2. đặt obj.__proto__thànhTest.prototype
  3. return obj;

Vì vậy, về cơ bản Object.createkhông thực hiện các nhà xây dựng.

191 hữu ích 2 bình luận chia sẻ
59

Hãy để tôi thử giải thích (thêm trên Blog ):

  1. Khi bạn viết Carconstructor var Car = function(){}, đây là cách mọi việc nội bộ: Chúng tôi có một {prototype}liên kết ẩn để Function.prototypemà không thể truy cập và một prototypeliên kết đến Car.prototypecó thể truy cập và có một thực tế constructorcủa Car. Cả Function.prototype và Car.prototype đều có các liên kết ẩn đến Object.prototype.
  2. Khi chúng ta muốn tạo hai đối tượng tương đương bằng cách sử dụng newtoán tử và createphương thức thì chúng ta phải làm như thế này: Honda = new Car();Maruti = Object.create(Car.prototype). Chuyện gì đang xảy ra vậy?

    Honda = new Car();- Khi bạn tạo một đối tượng như thế này thì thuộc tính ẩn {prototype}được trỏ đến Car.prototype. Vì vậy, ở đây, {prototype}đối tượng của Honda sẽ luôn như vậy Car.prototype- chúng tôi không có bất kỳ tùy chọn nào để thay đổi thuộc {prototype}tính của đối tượng. Nếu tôi muốn thay đổi nguyên mẫu của đối tượng mới tạo của chúng tôi thì sao?
    Maruti = Object.create(Car.prototype)- Khi bạn tạo một đối tượng như thế này, bạn có thêm một tùy chọn để chọn thuộc tính của đối tượng {prototype}. Nếu bạn muốn Car.prototype là {prototype}thì hãy chuyển nó dưới dạng tham số trong hàm. Nếu bạn không muốn bất kỳ {prototype}cho đối tượng của bạn thì bạn có thể vượt qua nullnhư thế này : Maruti = Object.create(null).

Kết luận - Bằng cách sử dụng phương thức Object.createbạn có quyền tự do chọn thuộc {prototype}tính đối tượng của mình . Trong new Car();, bạn không có tự do đó.

Cách ưa thích trong OO JavaScript:

Giả sử chúng ta có hai đối tượng ab.

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

Bây giờ, giả sử acó một số phương thức bcũng muốn truy cập. Vì vậy, chúng tôi yêu cầu kế thừa đối tượng ( achỉ nên là nguyên mẫu bnếu chúng tôi muốn truy cập vào các phương thức đó). Nếu chúng ta kiểm tra các nguyên mẫu absau đó chúng ta sẽ phát hiện ra rằng chúng chia sẻ nguyên mẫu Object.prototype.

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

Vấn đề - chúng tôi muốn đối tượng alà nguyên mẫu của b, nhưng ở đây chúng tôi đã tạo đối tượng bvới nguyên mẫu Object.prototype. Giải pháp - ECMAScript 5 được giới thiệu Object.create(), để đạt được sự kế thừa như vậy một cách dễ dàng. Nếu chúng ta tạo đối tượng bnhư thế này:

var b = Object.create(a);

sau đó,

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

Vì vậy, nếu bạn đang thực hiện kịch bản hướng đối tượng thì Object.create()rất hữu ích cho việc kế thừa.

59 hữu ích 5 bình luận chia sẻ
39

Điều này:

var foo = new Foo();

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

khá giống nhau. Một sự khác biệt quan trọng là new Foothực sự chạy mã xây dựng, trong khi Object.createsẽ không thực thi mã như

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

Lưu ý rằng nếu bạn sử dụng phiên bản hai tham số Object.create()thì bạn có thể làm những việc mạnh mẽ hơn nhiều.

39 hữu ích 1 bình luận chia sẻ
22

Sự khác biệt là cái gọi là "thừa kế giả so với nguyên mẫu". Gợi ý là chỉ sử dụng một loại trong mã của bạn, không trộn lẫn hai loại.

Trong kế thừa giả cổ điển (với toán tử "mới"), hãy tưởng tượng rằng trước tiên bạn định nghĩa một lớp giả, sau đó tạo các đối tượng từ lớp đó. Ví dụ: định nghĩa một "Người" giả lớp, sau đó tạo "Alice" và "Bob" từ "Người".

Trong kế thừa nguyên mẫu (sử dụng Object.create), bạn trực tiếp tạo một người cụ thể "Alice", sau đó tạo một người khác "Bob" bằng cách sử dụng "Alice" làm nguyên mẫu. Không có "lớp" ở đây; tất cả đều là đồ vật.

Trong nội bộ, JavaScript sử dụng "kế thừa nguyên mẫu"; cách "giả cổ điển" chỉ là một ít đường.

Xem liên kết này để so sánh hai cách.

22 hữu ích 0 bình luận chia sẻ
20
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

Tóm lược:

1) với newtừ khóa có hai điều cần lưu ý;

a) hàm được sử dụng như là một hàm tạo

b) function.prototypeđối tượng được chuyển đến thuộc __proto__tính ... hoặc nơi __proto__không được hỗ trợ, đó là nơi thứ hai mà đối tượng mới nhìn để tìm thuộc tính

2) với Object.create(obj.prototype)bạn đang xây dựng một đối tượng ( obj.prototype) và chuyển nó đến đối tượng dự định .. với sự khác biệt mà bây giờ đối tượng mới __proto__cũng đang trỏ đến obj.prototype (vui lòng tham khảo xj9 cho điều đó)

20 hữu ích 0 bình luận chia sẻ
10

Bên trong Object.createlàm điều này:

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

Cú pháp chỉ lấy đi ảo tưởng rằng JavaScript sử dụng Kế thừa cổ điển.

10 hữu ích 3 bình luận chia sẻ
9

Theo câu trả lời nàytừ khóa video new này thực hiện những việc tiếp theo:

  1. Tạo đối tượng mới.

  2. Liên kết đối tượng mới với hàm constructor ( prototype).

  3. Làm cho thisđiểm biến đến đối tượng mới.

  4. Thực hiện chức năng xây dựng bằng cách sử dụng đối tượng mới và thực hiện ngầm định return this;

  5. Gán tên hàm xây dựng cho thuộc tính của đối tượng mới constructor.

Object.createchỉ thực hiện 1st2ndcác bước !!!

9 hữu ích 0 bình luận chia sẻ
8

Các biến thể tạo đối tượng.


Biến 1 : ' new Object () ' -> Object constructor không có đối số.

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


Biến 2 : ' đối tượng mới (người) ' -> Trình tạo đối tượng có đối số.

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 }


Biến thể 3.1 : ' Object.create (người) '. Sử dụng Object.create với đối tượng đơn giản 'người'. 'Object.create (người)' sẽ tạo (và trả lại) đối tượng trống mới và thêm thuộc tính '__proto__' vào cùng một đối tượng trống mới. Thuộc tính này '__proto__' sẽ trỏ đến đối tượng 'người'.

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!


Biến thể 3.2 : ' Object.create (Object.prototype) '. Sử dụng Object.create với đối tượng tích hợp -> 'Object.prototype'. 'Object.create (Object.prototype)' sẽ tạo (và trả lại) đối tượng trống mới và thêm thuộc tính '__proto__' vào cùng một đối tượng trống mới. Thuộc tính này '__proto__' sẽ trỏ đến đối tượng '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


Biến thể 4 : 'Một số chức năng mới () '

// '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!

8 hữu ích 0 bình luận chia sẻ
loading
Không tìm thấy câu trả lời bạn tìm kiếm? Duyệt qua các câu hỏi được gắn thẻ javascript prototype object-create , hoặc hỏi câu hỏi của bạn.

Có thể bạn quan tâm

loading