2012-08-07 117 views
80

Tôi đã dành vài giờ đồng hồ cố gắng tìm một giải pháp cho vấn đề của mình nhưng có vẻ như là vô vọng.Làm thế nào để gọi một phương thức cha mẹ từ lớp con trong javascript?

Về cơ bản, tôi cần biết cách gọi phương thức gốc từ lớp con. Tất cả những thứ mà tôi đã thử cho đến nay kết thúc bằng cách không hoạt động hoặc viết quá mức phương thức gốc.

Tôi đang sử dụng đoạn mã sau để thiết lập OOP trong javascript:

// SET UP OOP 
// surrogate constructor (empty function) 
function surrogateCtor() {} 

function extend(base, sub) { 
    // copy the prototype from the base to setup inheritance 
    surrogateCtor.prototype = base.prototype; 
    sub.prototype = new surrogateCtor(); 
    sub.prototype.constructor = sub; 
} 

// parent class 
function ParentObject(name) { 
    this.name = name; 
} 
// parent's methods 
ParentObject.prototype = { 
    myMethod: function(arg) { 
     this.name = arg; 
    } 
} 

// child 
function ChildObject(name) { 
    // call the parent's constructor 
    ParentObject.call(this, name); 
    this.myMethod = function(arg) { 
     // HOW DO I CALL THE PARENT METHOD HERE? 
     // do stuff 
    } 
} 

// setup the prototype chain 
extend(ParentObject, ChildObject); 

tôi cần phải gọi phương thức của cha mẹ đầu tiên và sau đó thêm một số công cụ hơn để nó trong lớp trẻ.

Trong hầu hết các ngôn ngữ OOP sẽ đơn giản như gọi parent.myMethod() Nhưng tôi thực sự không thể hiểu được cách thực hiện của nó trong javascript.

Bất kỳ trợ giúp nào được đánh giá cao, cảm ơn bạn!

Trả lời

123

Sau đây là cách thực hiện của nó: ParentClass.prototype.myMethod();

Hoặc nếu bạn muốn gọi nó là trong bối cảnh các trường hợp hiện tại, bạn có thể làm: ParentClass.prototype.myMethod.call(this)

Cùng đi cho gọi một phương thức cha mẹ từ lớp trẻ với đối số: ParentClass.prototype.myMethod.call(this, arg1, arg2, ..) * Gợi ý: sử dụng apply() thay vì call() để chuyển đối số dưới dạng mảng.

+7

Nếu bạn muốn gọi nó là trong bối cảnh các trường hợp hiện tại, bạn phải làm 'ParentClass.prototype.myMethod.apply() hoặc' ParentClass.prototype.myMethod.call() ', giống như bạn làm với hàm tạo của bạn. – JMM

+3

Chỉ cần thêm vào đó nếu bạn muốn gọi với đối số, họ đi bên trong hàm áp dụng hoặc gọi ('ParentClass.prototype.myMethod.call (this, arg1, arg2, arg3 ...);') –

+0

Tôi không hiểu không. Nếu tôi gọi ParentClass.prototype.myMethod.call (điều này); từ myOethod của ChildObject, tôi đã gặp lỗi "Uncaught TypeError: Không thể đọc thuộc tính 'call' of undefined". – zhekaus

2

Trong trường hợp có nhiều cấp độ thừa kế, hàm này có thể được sử dụng làm phương thức super() bằng các ngôn ngữ khác. Here is a demo fiddle, với một số thử nghiệm, bạn có thể sử dụng nó như thế này, bên trong phương pháp của bạn sử dụng: call_base(this, 'method_name', arguments);

Nó sử dụng các hàm ES gần đây, khả năng tương thích với trình duyệt cũ không được đảm bảo. Thử nghiệm trong IE11, FF29, CH35.

/** 
* Call super method of the given object and method. 
* This function create a temporary variable called "_call_base_reference", 
* to inspect whole inheritance linage. It will be deleted at the end of inspection. 
* 
* Usage : Inside your method use call_base(this, 'method_name', arguments); 
* 
* @param {object} object The owner object of the method and inheritance linage 
* @param {string} method The name of the super method to find. 
* @param {array} args The calls arguments, basically use the "arguments" special variable. 
* @returns {*} The data returned from the super method. 
*/ 
function call_base(object, method, args) { 
    // We get base object, first time it will be passed object, 
    // but in case of multiple inheritance, it will be instance of parent objects. 
    var base = object.hasOwnProperty('_call_base_reference') ? object._call_base_reference : object, 
    // We get matching method, from current object, 
    // this is a reference to define super method. 
      object_current_method = base[method], 
    // Temp object wo receive method definition. 
      descriptor = null, 
    // We define super function after founding current position. 
      is_super = false, 
    // Contain output data. 
      output = null; 
    while (base !== undefined) { 
     // Get method info 
     descriptor = Object.getOwnPropertyDescriptor(base, method); 
     if (descriptor !== undefined) { 
      // We search for current object method to define inherited part of chain. 
      if (descriptor.value === object_current_method) { 
       // Further loops will be considered as inherited function. 
       is_super = true; 
      } 
      // We already have found current object method. 
      else if (is_super === true) { 
       // We need to pass original object to apply() as first argument, 
       // this allow to keep original instance definition along all method 
       // inheritance. But we also need to save reference to "base" who 
       // contain parent class, it will be used into this function startup 
       // to begin at the right chain position. 
       object._call_base_reference = base; 
       // Apply super method. 
       output = descriptor.value.apply(object, args); 
       // Property have been used into super function if another 
       // call_base() is launched. Reference is not useful anymore. 
       delete object._call_base_reference; 
       // Job is done. 
       return output; 
      } 
     } 
     // Iterate to the next parent inherited. 
     base = Object.getPrototypeOf(base); 
    } 
} 
0

Làm thế nào về một cái gì đó dựa trên Douglas Crockford ý tưởng:

function Shape(){} 

    Shape.prototype.name = 'Shape'; 

    Shape.prototype.toString = function(){ 
     return this.constructor.parent 
      ? this.constructor.parent.toString() + ',' + this.name 
      : this.name; 
    }; 


    function TwoDShape(){} 

    var F = function(){}; 

    F.prototype = Shape.prototype; 

    TwoDShape.prototype = new F(); 

    TwoDShape.prototype.constructor = TwoDShape; 

    TwoDShape.parent = Shape.prototype; 

    TwoDShape.prototype.name = '2D Shape'; 


    var my = new TwoDShape(); 

    console.log(my.toString()); ===> Shape,2D Shape 
36

ES6 phong cách cho phép bạn sử dụng các tính năng mới, chẳng hạn như super từ khóa. super từ khóa đó là tất cả về bối cảnh lớp cha mẹ, khi bạn đang sử dụng cú pháp lớp ES6. Như một ví dụ rất đơn giản, thanh toán:

class Foo { 
    static classMethod() { 
     return 'hello'; 
    } 
} 

class Bar extends Foo { 
    static classMethod() { 
     return super.classMethod() + ', too'; 
    } 
} 
Bar.classMethod(); // 'hello, too' 

Ngoài ra, bạn có thể sử dụng super để gọi constructor mẹ:

class Foo {} 

class Bar extends Foo { 
    constructor(num) { 
     let tmp = num * 2; // OK 
     this.num = num; // ReferenceError 
     super(); 
     this.num = num; // OK 
    } 
} 

Và tất nhiên bạn có thể sử dụng nó để truy cập vào thuộc tính của lớp cha mẹ super.prop. Vì vậy, hãy sử dụng ES6 và vui vẻ.

+1

Tại sao câu trả lời này không có nhiều phiếu bầu hơn? :) – fsinisi90

+2

@ fsinisi90 Tôi tin rằng, câu hỏi không phải là về các phương thức lớp của cha mẹ, mà là về các phương thức thể hiện của cha mẹ mà không thể được gọi với siêu từ khóa như của ES6. – mcmlxxxiii

+0

nó cũng hoạt động đối với các phương thức không tĩnh (được thử nghiệm với Chrome, không có transpiliing, không được thử từ khóa tĩnh) –

0

Để làm được điều này, bạn không bị giới hạn với việc trừu tượng hóa Class của ES6. Việc truy cập các phương thức nguyên mẫu của hàm dựng của cha mẹ là có thể thông qua thuộc tính __proto__ (tôi khá chắc chắn sẽ có các lập trình viên JS đồng ý phàn nàn rằng nó bị khấu hao) mà bị khấu hao nhưng đồng thời phát hiện ra rằng nó thực sự là một công cụ cần thiết cho các nhu cầu phân lớp (đặc biệt là cho các nhu cầu phân lớp Array mặc dù).Vì vậy, trong khi tài sản __proto__ vẫn có sẵn trong tất cả các công cụ JS chính mà tôi biết, ES6 đã giới thiệu chức năng Object.getPrototypeOf() trên đầu trang của nó. Công cụ super() trong trừu tượng Class là một đường cú pháp của điều này.

Vì vậy, trong trường hợp bạn không có quyền truy cập vào tên của nhà xây dựng gốc và không muốn sử dụng trừu tượng Class, bạn vẫn có thể thực hiện như sau;

function ChildObject(name) { 
    // call the parent's constructor 
    ParentObject.call(this, name); 
    this.myMethod = function(arg) { 
    //this.__proto__.__proto__.myMethod.call(this,arg); 
    Object.getPrototypeOf(Object.getPrototypeOf(this)).myMethod.call(this,arg); 
    } 
} 
Các vấn đề liên quan