วิธีการแทนที่ JavaScript


87

สมมติว่าคุณมีรหัสด้านล่าง:

function A() {
    function modify() {
       x = 300;
       y = 400;
    }

    var c = new C();
}

function B() {
    function modify(){
       x = 3000;
       y = 4000;
    }

    var c = new C();
}

C = function () {
   var x = 10;
   var y = 20;

   function modify() {
      x = 30;
      y = 40;
   };

   modify();
   alert("The sum is: " + (x+y));
}

ตอนนี้คำถามคือหากมีทางใดที่ฉันสามารถแทนที่วิธีการmodifyจากCด้วยวิธีการที่อยู่ในและA Bใน Java คุณจะใช้super-keyword แต่คุณจะบรรลุสิ่งนี้ใน JavaScript ได้อย่างไร?


6
modifyไม่ใช่วิธีการ แต่เป็นฟังก์ชันที่ซ้อนกัน - มีความแตกต่างระหว่างสองอย่างนี้ ...
Šime Vidas

2
ใน Java คุณใช้superคีย์เวิร์ดเพื่อเข้าถึงฟิลด์ที่ไม่ใช่ไพรเวตและเมธอดของซูเปอร์คลาส คุณไม่ได้ใช้เพื่อลบล้างสิ่งเหล่านี้
FK82

คำตอบ:


138

แก้ไข: ตอนนี้เป็นเวลาหกปีแล้วที่คำตอบเดิมถูกเขียนขึ้นและมีการเปลี่ยนแปลงมากมาย!

  • หากคุณใช้ JavaScript เวอร์ชันใหม่กว่าซึ่งอาจรวบรวมด้วยเครื่องมือเช่นBabelคุณสามารถใช้คลาสจริงได้ได้
  • หากคุณกำลังใช้ตัวสร้างส่วนประกอบที่เหมือนคลาสที่จัดทำโดยAngularหรือReactคุณจะต้องค้นหากรอบงานนั้นในเอกสาร
  • หากคุณใช้ ES5 และสร้างคลาส "ปลอม" ด้วยมือโดยใช้ต้นแบบคำตอบด้านล่างยังคงถูกต้องเหมือนเดิม

โชคดี!


การสืบทอด JavaScript ดูแตกต่างจาก Java เล็กน้อย นี่คือลักษณะของระบบวัตถุ JavaScript ดั้งเดิม:

// Create a class
function Vehicle(color){
  this.color = color;
}

// Add an instance method
Vehicle.prototype.go = function(){
  return "Underway in " + this.color;
}

// Add a second class
function Car(color){
  this.color = color;
}

// And declare it is a subclass of the first
Car.prototype = new Vehicle();

// Override the instance method
Car.prototype.go = function(){
  return Vehicle.prototype.go.call(this) + " car"
}

// Create some instances and see the overridden behavior.
var v = new Vehicle("blue");
v.go() // "Underway in blue"

var c = new Car("red");
c.go() // "Underway in red car"

น่าเสียดายที่นี่ค่อนข้างน่าเกลียดและไม่มีวิธีที่ดีมากในการ "super": คุณต้องระบุเมธอดของคลาสหลักที่คุณต้องการเรียกด้วยตนเอง ด้วยเหตุนี้จึงมีเครื่องมือมากมายในการสร้างคลาสที่ดีกว่า ลองดู Prototype.js, Backbone.js หรือไลบรารีที่คล้ายกันซึ่งมีไวยากรณ์ที่ดีกว่าสำหรับการทำ OOP ใน js


2
อีกทางเลือกหนึ่งในการใช้เครื่องมือเพื่อ "สร้างคลาสให้ดีขึ้น" คุณไม่สามารถสร้างคลาสได้เลย การจำลอง OO แบบคลาสสิกใน js มักจะยุ่งเหยิง
Raynos

1
(ไม่ใช่ความคิดเห็นเชิงสร้างสรรค์) เนื่องจากการเป็นภาษา "ระดับต่ำ" ในโลกของเบราว์เซอร์นั้นน่าเกลียดมากโดยไม่มีเหตุผล ยังคงเรียนรู้อยู่ขอบคุณ!
dnuske

9
ฉันเชื่อว่าแทนที่จะเป็น Car.prototype = ยานพาหนะใหม่ (); ควรเป็น Car.prototype = Object.create (Vehicle.prototype); เปล่า?
จอร์แดน

@Martin ถูกต้องดูjavascript-inheritance
matoni

สาเหตุหนึ่งที่ทำให้ Car.prototype = new Vehicle (); ไม่ถูกต้องเนื่องจากไม่มีอะไรส่งผ่านไปยัง Vehicle () ในขณะที่ได้รับสี
Tushar Arora

63

เนื่องจากนี่เป็นรายการยอดนิยมใน Google ฉันจึงต้องการให้คำตอบที่อัปเดต

การใช้คลาส ES6ทำให้การสืบทอดและวิธีการลบล้างง่ายขึ้นมาก:

'use strict';

class A {
    speak() {
        console.log("I'm A");
    }
}

class B extends A {
    speak() {
        super.speak();

        console.log("I'm B");
    }
}

var a = new A();
a.speak();
// Output:
// I'm A

var b = new B();
b.speak();
// Output:
// I'm A
// I'm B

superคำหลักหมายถึงระดับผู้ปกครองเมื่อนำมาใช้ในชั้นการสืบทอด super.method.apply(this);นอกจากนี้วิธีการทั้งหมดในระดับผู้ปกครองจะผูกพันกับอินสแตนซ์ของเด็กเพื่อให้คุณไม่ต้องเขียน

สำหรับความเข้ากันได้: ตารางความเข้ากันได้ของ ES6จะแสดงเฉพาะเวอร์ชันล่าสุดของผู้เล่นหลักที่สนับสนุนคลาส (ส่วนใหญ่) เบราว์เซอร์ V8 มีตั้งแต่เดือนมกราคมของปีนี้ (Chrome และ Opera) และ Firefox ที่ใช้เอ็นจิ้น SpiderMonkey JS จะเห็นคลาสในเดือนหน้าพร้อมกับ Firefox 45 อย่างเป็นทางการ ในฝั่งมือถือ Android ยังไม่รองรับฟีเจอร์นี้ในขณะที่ iOS 9 ซึ่งเปิดตัวเมื่อ 5 เดือนที่แล้วมีการรองรับบางส่วน

โชคดีที่มีBabelซึ่งเป็นไลบรารี JS สำหรับรวบรวมโค้ด Harmony ใหม่เป็นโค้ด ES5 คลาสและคุณสมบัติเจ๋ง ๆ อีกมากมายใน ES6 สามารถทำให้โค้ด Javascript ของคุณอ่านง่ายและบำรุงรักษาได้มากขึ้น


10
คำตอบนี้สมควรได้รับเครดิตมากขึ้นและปัจจุบันเป็นคำตอบที่ถูกต้อง
Klompenrunner

6

ครั้งหนึ่งควรหลีกเลี่ยงการเลียนแบบ OO แบบคลาสสิกและใช้ OO ต้นแบบแทน ห้องสมุดยูทิลิตี้ที่ดีสำหรับ OO แม่บทเป็นลักษณะ

แทนที่จะเขียนทับเมธอดและการตั้งค่าโซ่การสืบทอด (เราควรให้ความสำคัญกับการจัดองค์ประกอบวัตถุมากกว่าการสืบทอดวัตถุ) คุณควรรวมฟังก์ชันที่ใช้ซ้ำได้เข้ากับลักษณะและสร้างวัตถุด้วยสิ่งเหล่านั้น

ตัวอย่างสด

var modifyA = {
    modify: function() {
        this.x = 300;
        this.y = 400;
    }
};

var modifyB = {
    modify: function() {
        this.x = 3000;
        this.y = 4000;
    }
};

C = function(trait) {
    var o = Object.create(Object.prototype, Trait(trait));

    o.modify();
    console.log("sum : " + (o.x + o.y));

    return o;
}

//C(modifyA);
C(modifyB);

10
คุณไม่ได้ตอบคำถาม นี่ควรเป็นความคิดเห็นถ้ามีอะไร
FK82


3

แก้ไข () ในตัวอย่างของคุณเป็นฟังก์ชันส่วนตัวที่ไม่สามารถเข้าถึงได้จากทุกที่ แต่อยู่ในนิยาม A, B หรือ C ของคุณ คุณจะต้องประกาศเป็นไฟล์

this.modify = function(){}

C ไม่มีการอ้างอิงถึงผู้ปกครองเว้นแต่คุณจะส่งต่อไปยัง C หาก C ถูกตั้งค่าให้สืบทอดจาก A หรือ B มันจะสืบทอดเมธอดสาธารณะของมัน (ไม่ใช่ฟังก์ชันส่วนตัวอย่างที่คุณได้กำหนดไว้ modified ()) เมื่อ C สืบทอดเมธอดจากพาเรนต์คุณสามารถแทนที่เมธอดที่สืบทอดมาได้


แก้ไขเป็นฟังก์ชันท้องถิ่น ไม่มีส่วนตัวใน javascript
Raynos

local / private ไม่เหมือนกันแค่คนละคำ?
Alex Heyd

2

วิธีการmodify()ที่คุณเรียกว่าในช่วงที่เรียกกันในบริบทของโลกถ้าคุณต้องการที่จะแทนที่modify()แรกคุณต้องสืบทอดหรือAB

บางทีคุณกำลังพยายามทำสิ่งนี้:

ในกรณีนี้CสืบทอดA

function A() {
    this.modify = function() {
        alert("in A");
    }
}

function B() {
    this.modify = function() {
        alert("in B");
    }
}

C = function() {
    this.modify = function() {
        alert("in C");
    };

    C.prototype.modify(); // you can call this method where you need to call modify of the parent class
}

C.prototype = new A();

1
C.prototype.modify()จะมีthisค่าผิด เช่นC.prototypeแทนที่จะเป็นเช่น c. โปรดใช้.call(this)แต่คำตอบของคุณซ้ำกัน :)
Raynos

1

ไม่เว้นแต่คุณจะทำให้ตัวแปรทั้งหมดเป็น "สาธารณะ" นั่นคือทำให้ตัวแปรเหล่านี้เป็นสมาชิกของคุณสมบัติFunctionโดยตรงหรือผ่านprototypeคุณสมบัติ

var C = function( ) {
    this.x = 10 , this.y = 20 ;
    this.modify = function( ) {
        this.x = 30 , this.y = 40 ;
        console.log("(!) C >> " + (this.x + this.y) ) ;
    } ;
} ;

var A = function( ) {
    this.modify = function( ) {
       this.x = 300 , this.y = 400 ;
       console.log("(!) A >> " + (this.x + this.y) ) ;
    } ;
} ;
    A.prototype = new C ;

var B = function( ) {
    this.modify = function( ) {
       this.x = 3000 , this.y = 4000 ;
       console.log("(!) B >> " + (this.x + this.y) ) ;
    } ;
} ;


new C( ).modify( ) ;
new A( ).modify( ) ;
new B( ).modify( ) ; 

คุณจะสังเกตเห็นการเปลี่ยนแปลงเล็กน้อย

ที่สำคัญที่สุดคือการเรียกตัวสร้าง "super-class" ที่ควรจะเป็นโดยปริยายในบรรทัดนี้:

<name>.prototype = new C ;

ทั้งสองAและBตอนนี้จะมีสมาชิกที่ปรับเปลี่ยนได้ทีละคนxและyจะไม่เป็นเช่นนั้นหากเราจะเขียน... = Cแทน

จากนั้นx, yและmodifyกำลังสมาชิกทุกคน "สาธารณะ" เพื่อให้การกำหนดที่แตกต่างกันFunctionกับพวกเขา

 <name>.prototype.modify = function( ) { /* ... */ }

จะ "ลบล้าง" ต้นฉบับFunctionด้วยชื่อนั้น

สุดท้ายการเรียกที่modifyไม่สามารถทำได้ในการFunctionประกาศเนื่องจากการเรียกโดยปริยายไปยัง "super-class" จะถูกดำเนินการอีกครั้งเมื่อเราตั้งค่า "super-class" ที่ควรเป็นprototypeคุณสมบัติของ "คลาสย่อย" ที่ควรจะเป็น

แต่นี่เป็นวิธีที่คุณจะทำสิ่งนี้ใน JavaScript ไม่มากก็น้อย

HTH,

FK


ไม่มีจุดใดในการ<name>.prototype = new C;ทำให้เงาของสมาชิกต้นแบบทั้งหมดของคุณเป็นเงา
Raynos

@Raynos: ใช่มี. เพื่อเป็นปัญญาการสืบทอดทั้งหมดObjectsจะแบ่งปันสมาชิกคนเดียวกันในCกรณีที่คุณไม่ได้สร้างอินสแตนซ์Cวัตถุ ดังนั้นการเปลี่ยนแปลงxในAจะเปลี่ยนxในCและทำให้การเปลี่ยนแปลงในx Bซึ่งเห็นได้ชัดว่าไม่สามารถต้านทานได้
FK82

คุณพลาดจุดนี้ คุณสามารถลบบรรทัดและรหัสจะยังคงใช้งานได้
Raynos

@Raynos: คุณพลาดประเด็นของตัวเองฉันกลัว ;-) เราต้องการAและจะได้รับมรดกจากB Cหากเส้นนั้นขาดหายไปก็ไม่เป็นเช่นนั้น ในความเป็นจริงเพียงต้นแบบทั้งสองAเช่นเดียวกับการBที่จะ "เงา" Object.prototypeจะมีการเข้าถึงในกรณีที่จะเป็น
FK82

ดูรหัส A และ B ไม่ใช้สมาชิกใด ๆ ของ C ขึ้นต้นแบบ ดังนั้นการ "สืบทอด" จาก C ก็ไม่มีประโยชน์ เพราะนี่คือ A และ B Redefine x, yและmodifyและทำให้เงาทั้งหมดของCสมาชิก 's อะไรคือจุดที่จะใส่ C ในต้นแบบถ้าคุณไม่ได้ใช้มัน? มันตายรหัส
Raynos

0

function A() {
    var c = new C();
	c.modify = function(){
		c.x = 123;
		c.y = 333;
	}
	c.sum();
}

function B() {
    var c = new C();
	c.modify = function(){
		c.x = 999;
		c.y = 333;
	}
	c.sum();
}


C = function () {
   this.x = 10;
   this.y = 20;

   this.modify = function() {
      this.x = 30;
      this.y = 40;
   };
   
   this.sum = function(){
	this.modify();
	console.log("The sum is: " + (this.x+this.y));
   }
}

A();
B();

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.