Khi nào nên sử dụng lập trình nguyên mẫu trong JavaScript


15

Tôi đã dành một chút thời gian để phát triển các widget đơn giản cho các dự án theo cách sau:

var project = project || {};

(function() {

  project.elements = {
    prop1: val1,
    prop2: val2
  }

  project.method1 = function(val) {
    // Do this
  }

  project.method2 = function(val) {
    // Do that
  }

  project.init = function() {
    project.method1(project.elements.prop1)
    project.method2(project.elements.prop2)
  }
})()

project.init();

Nhưng tôi đã bắt đầu thay đổi định dạng của mình thành như sau:

function Project() {
  this.elements = {
    prop1: val1,
    prop2: val2
  }

  this.method_one(this.elements.prop1);
  this.method_two(this.elements.prop2);
}

Project.prototype.method_one = function (val) {
  // 
};

Project.prototype.method_two = function (val) {
  //
};

new Project();

Cấp, đây là những ví dụ ngớ ngẩn để không bị quấn quanh trục. Nhưng sự khác biệt về chức năng là gì và khi nào tôi nên chọn cái này hay cái khác?


Tôi nghĩ rằng ví dụ mã đầu tiên của bạn sẽ không hoạt động vì dự án không rời khỏi phạm vi. Để sử dụng tính kế thừa trong JS, bạn sử dụng nguyên mẫu. Ví dụ đầu tiên không có nghĩa là mở rộng dự án, vì vậy khả năng sử dụng lại có thể bị hạn chế.
Aitch

Vâng, tôi vô tình đặt projectkhai báo bên trong chức năng. Cập nhật.
JDillon522

tương tự ở đây ví dụ đầu tiên là tĩnh, thứ hai là hướng đối tượng.
Aitch

1
Nếu bạn chỉ sử dụng MỘT thể hiện của đối tượng, thì không có lý do gì để sử dụng định nghĩa nguyên mẫu. Và những gì bạn dường như nhu cầu - là một định dạng mô-đun - có nhiều lựa chọn cho điều đó, xem: addyosmani.com/writing-modular-js
c69

1
Cái đầu tiên cho mẫu singleton Cái thứ hai cho non-singleton (khi 1 lớp có thể có nhiều đối tượng)
Kokizzu 17/2/2015

Câu trả lời:


6

Sự khác biệt đầu tiên có thể được tóm tắt là: thisđề cập đến Trường hợp của lớp. prototypeđề cập đến Định nghĩa .

Hãy nói rằng chúng ta có lớp sau:

var Flight = function ( number ) { this.number = number; };

Vì vậy, ở đây chúng tôi đang gắn this.numbervào mọi trường hợp của lớp, và nó có ý nghĩa bởi vì mỗi Flightnên có số hiệu chuyến bay riêng của họ.

var flightOne = new Flight( "ABC" );
var flightTwo = new Flight( "XYZ" );

Ngược lại, prototypeđịnh nghĩa một thuộc tính duy nhất có thể được truy cập bởi tất cả các trường hợp.

Bây giờ nếu chúng tôi muốn lấy số hiệu chuyến bay, chúng tôi chỉ cần viết đoạn mã sau và tất cả các phiên bản của chúng tôi sẽ nhận được Tài liệu tham khảo cho đối tượng mới được tạo mẫu này.

Flight.prototype.getNumber = function () { return this.number; };

Sự khác biệt thứ hai là về cách JavaScript tìm kiếm một thuộc tính của một đối tượng. Khi bạn đang tìm kiếm Object.whatever, JavaScript sẽ tìm đến đối tượng Object chính (đối tượng mà mọi thứ khác được thừa hưởng từ đó) và ngay khi tìm thấy kết quả khớp, nó sẽ trả về hoặc gọi nó.

Nhưng điều đó chỉ xảy ra đối với các thuộc tính nguyên mẫu. Vì vậy, nếu bạn có một nơi nào đó ở các tầng cao hơn this.whatever, JavaScript sẽ không coi đó là một trận đấu và sẽ tiếp tục tìm kiếm.

Chúng ta hãy xem nó xảy ra như thế nào trong thực tế.

Lưu ý đầu tiên rằng [hầu hết] mọi thứ đều là Đối tượng trong JavaScript. Thử đi:

typeof null

Bây giờ chúng ta hãy xem những gì bên trong một Object(lưu ý Uppercase O.cuối cùng). Trong Công cụ dành cho nhà phát triển của Google Chrome khi bạn nhập. bạn sẽ nhận được danh sách các thuộc tính khả dụng bên trong đối tượng cụ thể đó.

Object.

Bây giờ làm điều tương tự cho Function :

Function.

Bạn có thể nhận thấy name phương pháp. Chỉ cần đi và bắn nó lên và xem điều gì sẽ xảy ra:

Object.name
Function.name

Bây giờ hãy tạo một hàm:

var myFunc = function () {};

Và hãy xem nếu chúng ta có name phương pháp ở đây là tốt:

myFunc.name

Bạn sẽ nhận được một chuỗi rỗng, nhưng không sao. Bạn không nên nhận được Lỗi hoặc Ngoại lệ.

Bây giờ chúng ta hãy thêm một cái gì đó giống như thần Objectvà xem liệu chúng ta có nhận được nó ở những nơi khác không?

Object.prototype.test = "Okay!";

Và ở đó bạn đi:

Object.prototype.test
Function.prototype.test
myFunc.prototype.test

Trong mọi trường hợp bạn nên xem "Okay!" .

Về ưu và nhược điểm của từng phương pháp, bạn có thể coi việc tạo nguyên mẫu là một cách làm "hiệu quả hơn", vì nó giữ một tham chiếu trên mọi trường hợp thay vì sao chép toàn bộ thuộc tính trong mỗi đối tượng. Mặt khác, đây là một ví dụ về Khớp nối chặt chẽ , một điều không nên lớn cho đến khi bạn thực sự có thể biện minh cho lý do. thislà khá phức tạp vì nó liên quan đến bối cảnh. Bạn có thể tìm thấy rất nhiều tài nguyên tốt miễn phí trên internet.

Như đã nói, cả hai cách chỉ là công cụ ngôn ngữ và nó thực sự phụ thuộc vào bạn và vấn đề bạn đang cố gắng giải quyết để chọn ra cái gì phù hợp hơn.

Nếu bạn cần phải có một thuộc tính phù hợp với mọi thể hiện của một lớp, thì hãy sử dụng this . Nếu bạn cần phải có một thuộc tính để hoạt động giống nhau trên mọi trường hợp, sau đó sử dụng prototype.

Cập nhật

Về đoạn mã mẫu của bạn, cái đầu tiên là một ví dụ về Singleton , vì vậy nó có ý nghĩa khi sử dụng thistrong cơ thể đối tượng. Bạn cũng có thể cải thiện ví dụ của mình bằng cách biến nó thành mô-đun như thế này (và bạn không cần phải luôn luôn sử dụng this).

/* Assuming it will run in a web browser */
(function (window) {
    window.myApp = {
        ...
    }
})( window );

/* And in other pages ... */
(function (myApp) {
    myApp.Module = {
        ...
    }
})( myApp );

/* And if you prefer Encapsulation */
(function (myApp) {
    myApp.Module = {
         "foo": "Foo",
         "bar": function ( string ) {
             return string;
         },
         return {
             "foor": foo,
             "bar": bar
         }
    }
})( myApp );

Đoạn mã thứ hai của bạn không có ý nghĩa nhiều như vậy bởi vì trước tiên bạn đang sử dụng thisvà sau đó bạn đang cố gắng hack nó prototype, điều này không hoạt động vìthis được ưu tiên hơn prototype. Tôi không chắc những mong đợi của bạn từ đoạn mã đó và cách nó hoạt động, nhưng tôi khuyên bạn nên cấu trúc lại nó.

Cập nhật

Để giải thích về thisviệc được ưu tiênprototype tôi có thể chỉ cho bạn một ví dụ và cho bạn biết làm thế nào để giải thích nó, nhưng tôi không có bất kỳ nguồn lực bên ngoài nào để sao lưu.

Ví dụ rất đơn giản:

var myClass = function () { this.foo = "Foo"; };
myClass.prototype.foo = "nice try!";
myClass.prototype.bar = "Bar";

var obj = new myClass;
obj.foo;     // Still contains "Foo" ...
obj.bar;     // Contains "Bar" as expected

Giải thích là, như chúng ta biết, thiscó liên quan đến bối cảnh. Vì vậy, nó sẽ không tồn tại cho đến khi bối cảnh đã sẵn sàng. Khi bối cảnh đã sẵn sàng? Khi phiên bản mới đang được tạo! Bạn nên đoán phần còn lại bây giờ! Điều đó có nghĩa là mặc dù có một prototypeđịnh nghĩa, nhưng thiscó ý nghĩa hơn để được ưu tiên bởi vì đó là tất cả về trường hợp mới được tạo ra tại thời điểm đó.


Đây là một câu trả lời tuyệt vời một phần tuyệt vời! Bạn có thể chỉnh sửa nó và đi sâu vào chi tiết so sánh và đối chiếu hai phương pháp không? Hai đoạn đầu tiên của bạn làm tôi bối rối về phương pháp thiết kế mà bạn đang đề cập đến. Bài tập của bạn thật tuyệt vời! Tôi chưa bao giờ nghĩ nhiều đến sức mạnh của việc tạo mẫu như thế. Bạn cũng có thể đưa ra một ví dụ sử dụng trường hợp bán chi tiết về thời điểm sử dụng từng phương pháp? Cảm ơn một lần nữa, điều này là tuyệt vời.
JDillon522

@ JDillon522 Vui mừng khi nghe điều đó. Tôi sẽ cố gắng cập nhật nó trong vài giờ tới!
53777A 17/2/2015

@ JDillon522 Chỉ cần cập nhật nhanh. Hy vọng nó sẽ rõ ràng hơn lần này.
53777A

@ JDillon522 Một chút nữa về đoạn trích mẫu của bạn ...
53777A

Ví dụ đơn đẹp. Vì vậy, tôi đã sử dụng thistrong ví dụ nguyên mẫu ngớ ngẩn vì thisđề cập đến các thuộc tính riêng của nó, bao gồm các phương thức của nó. Tôi không học tốt nhất từ ​​việc đọc về mã, nhưng nhiều hơn từ việc xem mã. ( Bit của MDN trên đó , Object Playground (tuyệt vời) và một vài thứ khác). Bạn có thể chỉ ra điều gì đó giải thích ý của bạn về " thisưu tiên hơn prototype" không? Tôi muốn nhìn vào nó nhiều hơn.
JDillon522
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.