การสืบทอด JavaScript: Object.create vs new


123

ใน JavaScript ความแตกต่างระหว่างสองตัวอย่างนี้คืออะไร:

วิชาบังคับก่อน:

function SomeBaseClass(){
}

SomeBaseClass.prototype = {
    doThis : function(){
    },

    doThat : function(){
    }
}

ตัวอย่างการสืบทอด A โดยใช้ Object.create:

function MyClass(){
}

MyClass.prototype = Object.create(SomeBaseClass.prototype);

ตัวอย่างการสืบทอด B โดยใช้คำหลักใหม่

function MyClass(){
}

MyClass.prototype = new SomeBaseClass();

ทั้งสองตัวอย่างดูเหมือนจะทำในสิ่งเดียวกัน คุณจะเลือกอย่างใดอย่างหนึ่งเมื่อใด

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

https://github.com/mrdoob/three.js/blob/master/src/loaders/ImageLoader.js

ข้อความที่ตัดตอนมา (หากคุณไม่ต้องการเปิดลิงก์):

THREE.ImageLoader.prototype = {

    constructor: THREE.ImageLoader
}

23
ทำไมห่าถึงทำเครื่องหมายว่าซ้ำกัน!?! คำถามอื่น ๆ Object.createและคำตอบไม่ได้พูดถึง นี่เป็นข้อผิดพลาดและควรเปิดใหม่
Scott Rippey

2
ถ้ามีอะไรมันซ้ำกับstackoverflow.com/questions/4166616/…
Scott Rippey

1
13 โหวตความคิดเห็นนั้นแล้วยังไม่เปิดใหม่ .. ?!
TJ

คำตอบ:


112

ในคำถามของคุณที่คุณได้กล่าวไปนั้นBoth examples seem to do the same thingไม่เป็นความจริงเลยเพราะ

ตัวอย่างแรกของคุณ

function SomeBaseClass(){...}
SomeBaseClass.prototype = {
    doThis : function(){...},
    doThat : function(){...}
}
function MyClass(){...}
MyClass.prototype = Object.create(SomeBaseClass.prototype);

ในตัวอย่างนี้คุณแค่ได้รับมรดกSomeBaseClass' prototypeแต่จะเกิดอะไรขึ้นถ้าคุณมีอสังหาริมทรัพย์ในแบบที่คุณSomeBaseClassชอบ

function SomeBaseClass(){ 
    this.publicProperty='SomeValue'; 
}

และถ้าคุณใช้มันเช่น

var obj=new MyClass();
console.log(obj.publicProperty); // undefined
console.log(obj);​

objวัตถุจะไม่ได้มีpublicPropertyคุณสมบัติเหมือนในตัวอย่างนี้

ตัวอย่างที่สองของคุณ

MyClass.prototype = new SomeBaseClass();

มันกำลังเรียกใช้constructorฟังก์ชันสร้างอินสแตนซ์SomeBaseClassและสืบทอดSomeBaseClassวัตถุทั้งหมด ดังนั้นถ้าคุณใช้

    var obj=new MyClass();
    console.log(obj.publicProperty); // SomeValue
    console.log(obj);​

ในกรณีนี้publicPropertyคุณสมบัติของมันยังพร้อมใช้งานสำหรับobjวัตถุเช่นในตัวอย่างนี้

เนื่องจากObject.createไม่สามารถใช้งานได้ในเบราว์เซอร์รุ่นเก่าบางรุ่นคุณจึงสามารถใช้ได้

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

โค้ดด้านบนจะเพิ่มObject.createฟังก์ชันหากไม่มีเพื่อให้คุณสามารถใช้Object.createฟังก์ชันได้และฉันคิดว่าโค้ดด้านบนอธิบายถึงสิ่งที่Object.createทำจริง หวังว่ามันจะช่วยได้บ้าง


6
สวัสดีชีค ขอบคุณสำหรับความพยายามของคุณในเรื่องนี้ ใช่ความแตกต่างคือตัวสร้างถูกรันในตัวอย่างที่สอง แต่ไม่ใช่ในตัวแรก (ซึ่งเป็นที่ต้องการในกรณีของฉัน) เกี่ยวกับทรัพย์สินสาธารณะที่ไม่ได้กำหนดซึ่งไม่ได้รับมาจากการใช้งานขั้นสูงคุณเพียงแค่ต้องเรียก super ในตัวสร้างของเด็ก: SomeBaseClass.call (this) ตรวจสอบซอนี้: jsfiddle.net/NhQGB
ChrisRich

ฉันกำลังมองหาวิธีง่ายๆในการสืบทอดที่เหมาะสมใน JS โดยไม่ต้องใช้ไลบรารี / เฟรมเวิร์กใด ๆ ฉันคิดว่าตัวอย่างนี้ (ในซอของฉันด้านบน) เป็นแนวทางที่ดีที่สุดสำหรับเบราว์เซอร์สมัยใหม่ บางที Object.Create polyfill สามารถเพิ่มการรองรับเบราว์เซอร์เดิมได้หรือไม่?
ChrisRich

โดยพื้นฐานแล้วเพื่อสรุปถ้าคุณทำ. prototype = new แสดงว่าคุณกำลังสืบทอดค่าใด ๆ ที่คุณกำหนดให้กับสิ่งนี้ในคลาสพื้นฐานและเมื่อคุณทำ object.create คุณกำลังสืบทอดสิ่งที่อยู่บนต้นแบบในคลาสพื้นฐานเท่านั้นใช่ไหม?
davidjnelson

ทำการทดสอบ "MyClass.prototype = new SomeBaseClass ()" หมายความว่าอินสแตนซ์ใหม่ของ SomeBaseClass ถูกสร้างขึ้นเมื่อยังไม่ได้สร้างอินสแตนซ์ของ MyClass ใช่หรือไม่

ไม่ได้เฉพาะเมื่อคุณสร้างวัตถุหลักต้นแบบจะถูกตั้งค่าเป็นสิ่งSomeBaseClassนั้น
The Alpha

39

ทั้งสองตัวอย่างดูเหมือนจะทำในสิ่งเดียวกัน

นั่นเป็นเรื่องจริงในกรณีของคุณ

คุณจะเลือกอย่างใดอย่างหนึ่งเมื่อใด

เมื่อใดที่SomeBaseClassมีเนื้อความฟังก์ชันสิ่งนี้จะถูกดำเนินการด้วยnewคำหลัก โดยปกติไม่ได้มีไว้ แต่คุณต้องการตั้งค่าโซ่ต้นแบบเท่านั้น ในบางกรณีอาจทำให้เกิดปัญหาร้ายแรงได้เนื่องจากคุณสร้างอินสแตนซ์อ็อบเจ็กต์ซึ่งตัวแปรที่กำหนดขอบเขตส่วนตัวจะถูกแชร์โดยMyClassอินสแตนซ์ทั้งหมดในขณะที่พวกเขาสืบทอดวิธีการที่มีสิทธิพิเศษเดียวกัน ผลข้างเคียงอื่น ๆ เท่าที่จะนึกออก

Object.createดังนั้นคุณโดยทั่วไปควรต้องการ แต่ยังไม่รองรับในเบราว์เซอร์เดิมบางประเภท ซึ่งเป็นเหตุผลที่คุณเห็น - ใช้newบ่อยเกินไปเนื่องจากมักไม่เป็นอันตราย (ชัดเจน) ลองดูคำตอบนี้ด้วย


นี่คือคำตอบที่ดีที่สุดอธิบายได้ดี
Spex

8

ความแตกต่างจะชัดเจนหากคุณใช้Object.create()ตามที่ตั้งใจไว้ อันที่จริงมันซ่อนprototypeคำจากรหัสของคุณทั้งหมดมันจะทำงานภายใต้ประทุน โดยใช้Object.create()เราสามารถไปเช่น

var base =  {
    doThis : function(){
    },

    doThat : function(){
    }
};

จากนั้นเราสามารถขยาย / สืบทอดวัตถุอื่น ๆ จากสิ่งนี้

var myObject = Object.create( base );
// myObject will now link to "base" via the prototype chain internally

นี่จึงเป็นอีกแนวคิดหนึ่งซึ่งเป็นวิธีการสืบทอดแบบ "เชิงวัตถุ" มากกว่า ไม่มี "ฟังก์ชันตัวสร้าง" นอกกรอบที่ใช้Object.create()เช่น แต่แน่นอนว่าคุณสามารถสร้างและเรียกใช้ฟังก์ชันคอนสตรัคเตอร์ที่กำหนดเองภายในออบเจ็กต์เหล่านั้นได้

เหตุผลหนึ่งสำหรับการใช้Object.create()ก็คือว่ามันอาจจะดูมากขึ้นตามธรรมชาติที่จะผสม / * * * * * สืบทอดจากวัตถุอื่น ๆ กว่าการใช้ Javascripts วิธีการเริ่มต้น


7
Object.createไม่สามารถแทนที่แนวทางคลาสสิกได้จริง ๆnewเมื่อจำเป็นต้องใช้ฟังก์ชันตัวสร้าง
Bergi

1
แน่นอนที่สุดสามารถ @Bergi ดูโพสต์บล็อกนี้: davidwalsh.name/javascript-objects-deconstruction คุณยังสามารถส่งออบเจ็กต์เริ่มต้นเป็นพารามิเตอร์ที่สองไปยัง Object.create ()
LocalPCGuy

@LocalPCGuy: ไม่ได้เพราะObject.createไม่ได้เรียกใช้ฟังก์ชันที่จำเป็นในการสร้างการปิด แน่นอนว่าObject.createคุณสามารถวางinitเมธอดลงบนวัตถุของคุณและเรียกใช้สิ่งนั้นได้ทันทีและมันอาจจะส่งคืนthisเพื่อให้คุณได้ไวยากรณ์ที่กระชับ - แต่เดี๋ยวก่อนนั่นคือตัวสร้างแล้ว
Bergi

1
การเรียกใช้ฟังก์ชัน init จะทำงานคล้ายกับคอนสตรัคเตอร์ แต่ไม่ใช่ตัวสร้าง และวิธีการดังกล่าวช่วยให้คุณสามารถแทนที่วิธีการแบบคลาสสิกด้วย Object.create และแบบจำลองทางจิตของการเชื่อมโยงวัตถุนั้นง่ายกว่าแบบจำลองที่สร้างโดย 'ใหม่' มาก
LocalPCGuy

แบบจำลองทางจิตของฉันเกี่ยวกับการnewใช้ "การเชื่อมโยงวัตถุ" จึงไม่มีความแตกต่างกันมากนัก ในความเป็นจริงมีเพียงสองสิ่งเท่านั้น: .constructorเรียกว่า.initและฟังก์ชันนั้นไม่มี.prototypeคุณสมบัติชี้กลับไปที่วัตถุต้นแบบของคุณ ส่วนที่เหลือเป็นเพียงไวยากรณ์ - และฉันต้องการมากกว่าnew X Object.create(x).init()
Bergi

0

ฉันไม่ใช่ผู้เชี่ยวชาญด้านสคริปต์ java แต่นี่คือตัวอย่างง่ายๆเพื่อทำความเข้าใจความแตกต่างระหว่าง "Object.create" และ "new" ..

ขั้นตอนที่ 1: สร้างฟังก์ชันหลักที่มีคุณสมบัติและการดำเนินการบางอย่าง ..

function Person() {

this.name = 'venkat';

this.address = 'dallas';

this.mobile='xxxxxxxxxx'

}

Person.prototype.func1 = function () {

    return this.name + this.address;
}

ขั้นตอนที่ 2: สร้างฟังก์ชันลูก (PersonSalary) ซึ่งขยายเหนือฟังก์ชันบุคคลโดยใช้คำหลักใหม่ ..

function PersonSalary() {
    Person.call(this);
}
PersonSalary.prototype = new Person();

PersonSalary();

ขั้นตอนที่ 3: สร้างฟังก์ชันลูกคนที่สอง (PersonLeaves) ซึ่งขยายเหนือฟังก์ชัน Person โดยใช้คำสำคัญObject.create ..

function PersonLeaves() {
 Person.call(this);
}
PersonLeaves.prototype = Object.create(Person.prototype);


PersonLeaves();

// ตอนนี้ตรวจสอบต้นแบบฟังก์ชันลูกทั้งสอง

PersonSalary.prototype
PersonLeaves.prototype

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

นี่คือประเด็นสำคัญ

หากคุณต้องการมอบสิทธิ์ให้กับวิธีการบางอย่างในฟังก์ชันหลักและไม่ต้องการให้สร้างวัตถุใหม่การใช้ Object.create เป็นวิธีที่ดีที่สุด

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