JavaScript: .extend và .prototype được sử dụng để làm gì?


122

Tôi tương đối mới đối với JavaScript và vẫn thấy .extend và .prototype trong các thư viện của bên thứ ba mà tôi đang sử dụng. Tôi nghĩ nó liên quan đến thư viện javascript Nguyên mẫu, nhưng tôi bắt đầu nghĩ rằng không phải vậy. Chúng được sử dụng để làm gì?

Câu trả lời:


136

Tính kế thừa của Javascript dựa trên nguyên mẫu, vì vậy bạn mở rộng nguyên mẫu của các đối tượng như Ngày tháng, Toán học và thậm chí là các nguyên mẫu tùy chỉnh của riêng bạn.

Date.prototype.lol = function() {
 alert('hi');
};

( new Date ).lol() // alert message

Trong đoạn mã trên, tôi xác định một phương thức cho tất cả các đối tượng Ngày (những đối tượng đã có và tất cả những đối tượng mới).

extend thường là một hàm cấp cao sao chép nguyên mẫu của một lớp con mới mà bạn muốn mở rộng từ lớp cơ sở.

Vì vậy, bạn có thể làm điều gì đó như:

extend( Fighter, Human )

Và phương thức Fighterkhởi tạo / đối tượng sẽ kế thừa nguyên mẫu của Human, vì vậy nếu bạn định nghĩa các phương thức như livedietrên Humanthì Fightercũng sẽ kế thừa những phương thức đó.

Đã cập nhật làm rõ:

"hàm cấp cao" nghĩa là .extend không được tích hợp sẵn nhưng thường được cung cấp bởi một thư viện như jQuery hoặc Prototype.


75
nghĩa .extendlà "hàm cấp cao" không được tích hợp sẵn nhưng thường được cung cấp bởi một thư viện như jQuery hoặc Prototype.
visum

13
Tôi sẽ nói thêm rằng nó không được đề xuất để mở rộng các nguyên mẫu của các đối tượng gốc trong JS
framp

1
@meder - bạn nên thêm bình luận visum trong câu trả lời của mình. :)
Manish Gupta

9
Trong lập trình Javascript hiện đại, thường coi các khối cầu và các đối tượng gốc giống như các phần tử của phòng tắm công cộng; bạn không thể tránh đi vào đó, nhưng bạn nên cố gắng giảm thiểu tiếp xúc với các bề mặt. Điều này là do changing the native objects can break other developer's assumptions of these objects,dẫn đến các lỗi javascript thường có thể mất nhiều giờ để theo dõi. Câu đầu tiên trong câu trả lời này dường như đã xuyên tạc thực hành javascript có giá trị này.
Ninjaxor

24

.extend()được thêm vào bởi nhiều thư viện bên thứ ba để dễ dàng tạo đối tượng từ các đối tượng khác. Xem http://api.jquery.com/jQuery.extend/ hoặc http://www.prototypejs.org/api/object/extend để biết một số ví dụ.

.prototype đề cập đến "mẫu" (nếu bạn muốn gọi nó như vậy) của một đối tượng, vì vậy bằng cách thêm các phương thức vào nguyên mẫu của đối tượng (bạn thấy điều này rất nhiều trong các thư viện để thêm vào Chuỗi, Ngày, Toán hoặc thậm chí là Hàm) các phương thức đó được thêm vào mọi phiên bản mới của đối tượng đó.


19

Các extendphương pháp ví dụ như trong jQuery hoặc PrototypeJS , bản sao tất cả các thuộc tính từ nguồn đến đối tượng đích.

Bây giờ về thuộc prototypetính, nó là một thành viên của các đối tượng hàm, nó là một phần của lõi ngôn ngữ.

Bất kỳ hàm nào cũng có thể được sử dụng như một phương thức khởi tạo , để tạo các thể hiện đối tượng mới. Tất cả các chức năng đều cóprototype tính này.

Khi bạn sử dụng newtoán tử với trên một đối tượng hàm, một đối tượng mới sẽ được tạo và nó sẽ kế thừa từ hàm tạo của nóprototype .

Ví dụ:

function Foo () {
}
Foo.prototype.bar = true;

var foo = new Foo();

foo.bar; // true
foo instanceof Foo; // true
Foo.prototype.isPrototypeOf(foo); // true

18

Kế thừa Javascript dường như giống như một cuộc tranh luận mở ở khắp mọi nơi. Có thể gọi đây là "Trường hợp kỳ lạ của ngôn ngữ Javascript".

Ý tưởng là có một lớp cơ sở và sau đó bạn mở rộng lớp cơ sở để có được một tính năng giống như kế thừa (không hoàn toàn, nhưng vẫn có).

Toàn bộ ý tưởng là hiểu được ý nghĩa thực sự của nguyên mẫu. Tôi đã không hiểu nó cho đến khi tôi thấy mã của John Resig (gần giống với những gì jQuery.extend) đã viết một đoạn mã để thực hiện nó và anh ấy tuyên bố rằng base2 và thư viện nguyên mẫu là nguồn cảm hứng.

Đây là mã.

    /* Simple JavaScript Inheritance
     * By John Resig http://ejohn.org/
     * MIT Licensed.
     */  
     // Inspired by base2 and Prototype
    (function(){
  var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;

  // The base Class implementation (does nothing)
  this.Class = function(){};

  // Create a new Class that inherits from this class
  Class.extend = function(prop) {
    var _super = this.prototype;

    // Instantiate a base class (but only create the instance,
    // don't run the init constructor)
    initializing = true;
    var prototype = new this();
    initializing = false;

    // Copy the properties over onto the new prototype
    for (var name in prop) {
      // Check if we're overwriting an existing function
      prototype[name] = typeof prop[name] == "function" &&
        typeof _super[name] == "function" && fnTest.test(prop[name]) ?
        (function(name, fn){
          return function() {
            var tmp = this._super;

            // Add a new ._super() method that is the same method
            // but on the super-class
            this._super = _super[name];

            // The method only need to be bound temporarily, so we
            // remove it when we're done executing
            var ret = fn.apply(this, arguments);        
            this._super = tmp;

            return ret;
          };
        })(name, prop[name]) :
        prop[name];
    }

    // The dummy class constructor
    function Class() {
      // All construction is actually done in the init method
      if ( !initializing && this.init )
        this.init.apply(this, arguments);
    }

    // Populate our constructed prototype object
    Class.prototype = prototype;

    // Enforce the constructor to be what we expect
    Class.prototype.constructor = Class;

    // And make this class extendable
    Class.extend = arguments.callee;

    return Class;
  };
})();

Có ba phần đang thực hiện công việc. Đầu tiên, bạn lặp qua các thuộc tính và thêm chúng vào phiên bản. Sau đó, bạn tạo một phương thức khởi tạo để sau này được thêm vào đối tượng. Bây giờ, các dòng chính là:

// Populate our constructed prototype object
Class.prototype = prototype;

// Enforce the constructor to be what we expect
Class.prototype.constructor = Class;

Đầu tiên bạn trỏ Class.prototypeđến nguyên mẫu mong muốn. Bây giờ, toàn bộ đối tượng đã thay đổi có nghĩa là bạn cần buộc bố cục trở lại như của chính nó.

Và ví dụ sử dụng:

var Car = Class.Extend({
  setColor: function(clr){
    color = clr;
  }
});

var volvo = Car.Extend({
   getColor: function () {
      return color;
   }
});

Đọc thêm về nó tại đây tại Javascript Inheritance by John Resig 's post.


2

Một số extendchức năng trong thư viện của bên thứ ba phức tạp hơn những chức năng khác. Ví dụ: Knockout.js chứa một cái cực kỳ đơn giản không có một số kiểm tra như jQuery's:

function extend(target, source) {
    if (source) {
        for(var prop in source) {
            if(source.hasOwnProperty(prop)) {
                target[prop] = source[prop];
            }
        }
    }
    return target;
}

2
  • .extends() tạo một lớp là con của lớp khác.
    phía sau Child.prototype.__proto__thiết lập giá trị của nó để Parent.prototype
    các phương thức được kế thừa.
  • .prototype kế thừa các tính năng từ cái này sang cái khác.
  • .__proto__ là getter / setter cho Prototype.

Đây không phải là .extend () và không phải .extends ()?
SJHowe
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.