ความแตกต่างระหว่างอะไร
var A = function () {
this.x = function () {
//do something
};
};
และ
var A = function () { };
A.prototype.x = function () {
//do something
};
a1.x !== a2.x; ในต้นแบบ:a1.x === a2.x
ความแตกต่างระหว่างอะไร
var A = function () {
this.x = function () {
//do something
};
};
และ
var A = function () { };
A.prototype.x = function () {
//do something
};
a1.x !== a2.x; ในต้นแบบ:a1.x === a2.x
คำตอบ:
ตัวอย่างมีผลลัพธ์ที่แตกต่างกันมาก
ก่อนที่จะดูความแตกต่างควรสังเกตสิ่งต่อไปนี้:
[[Prototype]]คุณสมบัติส่วนตัวของอินสแตนซ์myObj.method()) สิ่งนี้ภายในวิธีการอ้างอิงถึงวัตถุ ที่นี่ไม่ได้ตั้งค่าโดยการโทรหรือโดยการใช้ผูกมันเริ่มต้นที่วัตถุระดับโลก (หน้าต่างในเบราว์เซอร์) หรือในโหมดเข้มงวดยังคงไม่ได้กำหนดดังนั้นนี่คือตัวอย่างที่เป็นปัญหา:
var A = function () {
this.x = function () {
//do something
};
};
ในกรณีนี้ตัวแปรAถูกกำหนดค่าที่อ้างอิงถึงฟังก์ชัน เมื่อฟังก์ชั่นที่เรียกว่าการใช้A()ฟังก์ชั่นเป็นนี้ไม่ได้ถูกกำหนดโดยการเรียกเพื่อให้ค่าเริ่มต้นของมันไปยังวัตถุโลกและการแสดงออกที่มีประสิทธิภาพthis.x ผลที่ได้คือการอ้างอิงถึงการแสดงออกของฟังก์ชั่นทางด้านขวามือได้รับมอบหมายให้window.xwindow.x
ในกรณีของ:
var A = function () { };
A.prototype.x = function () {
//do something
};
สิ่งที่แตกต่างกันมากเกิดขึ้น ในบรรทัดแรกตัวแปรAถูกกำหนดการอ้างอิงไปยังฟังก์ชัน ใน JavaScript วัตถุฟังก์ชั่นทั้งหมดมีคุณสมบัติต้นแบบตามค่าเริ่มต้นดังนั้นจึงไม่มีรหัสแยกต่างหากเพื่อสร้างวัตถุA.prototype
ในบรรทัดที่สองA.prototype.xได้รับการอ้างอิงไปยังฟังก์ชั่น สิ่งนี้จะสร้างคุณสมบัติxหากไม่มีอยู่หรือกำหนดค่าใหม่หากมี ดังนั้นความแตกต่างกับตัวอย่างแรกที่คุณสมบัติxของวัตถุนั้นเกี่ยวข้องกับการแสดงออก
ตัวอย่างอื่นอยู่ด้านล่าง มันคล้ายกับอันแรก (และอาจเป็นสิ่งที่คุณตั้งใจจะถาม):
var A = new function () {
this.x = function () {
//do something
};
};
ในตัวอย่างนี้ตัวnewดำเนินการถูกเพิ่มก่อนนิพจน์ฟังก์ชันเพื่อให้เรียกใช้ฟังก์ชันเป็นตัวสร้าง เมื่อเรียกว่ามีnewฟังก์ชั่นเป็นนี้จะถูกตั้งค่าการอ้างอิงวัตถุใหม่ซึ่งส่วนตัว[[Prototype]]ตั้งค่าคุณสมบัติการอ้างอิงสาธารณะคอนสตรัคของต้นแบบ ดังนั้นในคำสั่งการมอบหมายxคุณสมบัติจะถูกสร้างขึ้นบนวัตถุใหม่นี้ เมื่อเรียกว่าเป็นตัวสร้างฟังก์ชั่นจะคืนค่าวัตถุนี้เป็นค่าเริ่มต้นดังนั้นจึงไม่จำเป็นต้องมีreturn this;คำสั่งแยกต่างหาก
วิธีตรวจสอบว่าAมีคุณสมบัติx :
console.log(A.x) // function () {
// //do something
// };
นี่คือการใช้งานที่ผิดปกติของใหม่ตั้งแต่วิธีเดียวที่จะอ้างอิงสร้างผ่านA.constructor มันจะเป็นเรื่องธรรมดามากที่จะทำ:
var A = function () {
this.x = function () {
//do something
};
};
var a = new A();
อีกวิธีหนึ่งในการบรรลุผลลัพธ์ที่คล้ายกันคือใช้นิพจน์ฟังก์ชันที่เรียกใช้ทันที:
var A = (function () {
this.x = function () {
//do something
};
}());
ในกรณีนี้Aกำหนดค่าส่งคืนของการเรียกใช้ฟังก์ชันทางด้านขวามือ ที่นี่อีกครั้งตั้งแต่นี้ไม่ได้ตั้งค่าในการเรียกก็จะอ้างอิงวัตถุทั่วโลกและมีประสิทธิภาพthis.x window.xตั้งแต่ฟังก์ชั่นไม่ได้กลับอะไรจะมีค่าของAundefined
ความแตกต่างระหว่างสองวิธีนี้ยังแสดงให้เห็นว่าคุณกำลังทำให้เป็นอันดับและยกเลิกการทำให้เป็นอันดับวัตถุ Javascript ของคุณไปยัง / จาก JSON วิธีการที่กำหนดไว้ในต้นแบบของวัตถุนั้นจะไม่ต่อเนื่องกันเมื่อคุณทำให้วัตถุเป็นอนุกรมซึ่งสามารถทำได้อย่างสะดวกสบายเมื่อเช่นคุณต้องการทำให้เป็นอนุกรมเฉพาะส่วนข้อมูลของวัตถุ แต่ไม่ใช่วิธีการ:
var A = function () {
this.objectsOwnProperties = "are serialized";
};
A.prototype.prototypeProperties = "are NOT serialized";
var instance = new A();
console.log(instance.prototypeProperties); // "are NOT serialized"
console.log(JSON.stringify(instance));
// {"objectsOwnProperties":"are serialized"}
คำถามที่เกี่ยวข้อง :
Sidenote:อาจไม่มีการประหยัดหน่วยความจำที่สำคัญระหว่างสองวิธีอย่างไรก็ตามการใช้ต้นแบบเพื่อแบ่งปันวิธีการและคุณสมบัติน่าจะใช้หน่วยความจำน้อยกว่าแต่ละตัวอย่างมีสำเนาของตัวเอง
JavaScript ไม่ใช่ภาษาระดับต่ำ การคิดต้นแบบหรือรูปแบบการสืบทอดอื่น ๆ อาจไม่คุ้มค่านักที่จะเปลี่ยนวิธีจัดสรรหน่วยความจำอย่างชัดเจน
null) แต่นี่คือความแตกต่างจากprototypeทรัพย์สิน - newซึ่งเป็นฟังก์ชั่นและที่เป็นต้นแบบของทุกกรณีที่มีการตั้งค่าเมื่อมีการสร้างด้วย ไม่อยากเชื่อเลยว่านี่จะมีผู้
"The language is functional"คุณแน่ใจหรือว่านี่คือความหมายของหน้าที่
Aเป็นฟังก์ชันและอีกครึ่งหนึ่งเกี่ยวกับวิธีคลุมเครือ บางสิ่งบางอย่างตรงไปตรงมา
ดังที่คนอื่น ๆ บอกไว้ในเวอร์ชั่นแรกการใช้ "นี่" จะส่งผลให้เกิดทุกตัวอย่างของคลาส A ที่มีสำเนาของฟังก์ชั่นอิสระ "x" ในขณะที่ใช้ "ต้นกำเนิด" จะหมายถึงแต่ละอินสแตนซ์ของคลาส A จะใช้สำเนาวิธีการเดียวกัน "x"
นี่คือรหัสเพื่อแสดงความแตกต่างที่ลึกซึ้งนี้:
// x is a method assigned to the object using "this"
var A = function () {
this.x = function () { alert('A'); };
};
A.prototype.updateX = function( value ) {
this.x = function() { alert( value ); }
};
var a1 = new A();
var a2 = new A();
a1.x(); // Displays 'A'
a2.x(); // Also displays 'A'
a1.updateX('Z');
a1.x(); // Displays 'Z'
a2.x(); // Still displays 'A'
// Here x is a method assigned to the object using "prototype"
var B = function () { };
B.prototype.x = function () { alert('B'); };
B.prototype.updateX = function( value ) {
B.prototype.x = function() { alert( value ); }
}
var b1 = new B();
var b2 = new B();
b1.x(); // Displays 'B'
b2.x(); // Also displays 'B'
b1.updateX('Y');
b1.x(); // Displays 'Y'
b2.x(); // Also displays 'Y' because by using prototype we have changed it for all instances
ตามที่คนอื่น ๆ กล่าวถึงมีหลายเหตุผลที่จะเลือกวิธีหนึ่งหรืออื่น ๆ ตัวอย่างของฉันมีไว้เพื่อแสดงความแตกต่างอย่างชัดเจน
thisวัตถุซึ่งเป็นเจ้าของวิธีการ เช่นวิธีการไม่มีวัตถุที่เป็นเจ้าของ ในกรณีนี้มีthisวัตถุดังแสดงในคลาส A ในตัวอย่าง
นำตัวอย่าง 2 ตัวอย่างนี้:
var A = function() { this.hey = function() { alert('from A') } };
เมื่อเทียบกับ
var A = function() {}
A.prototype.hey = function() { alert('from prototype') };
คนส่วนใหญ่ที่นี่ (โดยเฉพาะคำตอบที่ติดอันดับยอดนิยม) พยายามอธิบายว่าพวกเขาแตกต่างกันอย่างไรโดยไม่อธิบายว่าทำไม ฉันคิดว่านี่เป็นสิ่งที่ผิดและถ้าคุณเข้าใจพื้นฐานก่อนความแตกต่างจะชัดเจน ลองอธิบายพื้นฐานก่อน ...
a) ฟังก์ชั่นเป็นวัตถุใน JavaScript วัตถุทุกอย่างใน JavaScript ได้รับคุณสมบัติภายใน (หมายถึงคุณไม่สามารถเข้าถึงได้เช่นเดียวกับคุณสมบัติอื่น ๆ ยกเว้นในเบราว์เซอร์เช่น Chrome) ซึ่งมักเรียกว่า__proto__(คุณสามารถพิมพ์anyObject.__proto__ใน Chrome จริงเพื่อดูว่ามันอ้างอิงอะไร คุณสมบัติไม่มีอะไรเพิ่มเติมคุณสมบัติใน JavaScript = ตัวแปรภายในวัตถุไม่มีอะไรเพิ่มเติมตัวแปรทำอะไรได้บ้างพวกเขาชี้ไปยังสิ่งต่าง ๆ
ดังนั้น__proto__คุณสมบัตินี้ชี้ไปที่อะไร? ปกติแล้ววัตถุอื่น (เราจะอธิบายว่าทำไมในภายหลัง) วิธีเดียวที่จะบังคับใช้ JavaScript สำหรับสถานที่ให้บริการไปยังจุดไม่ถึงวัตถุอื่นคือการใช้งาน__proto__ var newObj = Object.create(null)แม้ว่าคุณจะทำเช่นนี้สถานที่ให้บริการยังคงมีอยู่เป็นทรัพย์สินของวัตถุเพียงมันไม่ได้ชี้ไปที่วัตถุอื่นก็ชี้ไปที่__proto__null
ที่นี่คนส่วนใหญ่สับสน:
เมื่อคุณสร้างฟังก์ชั่นใหม่ใน JavaScript (ซึ่งเป็นวัตถุเช่นกันจำได้ไหม?) ช่วงเวลาที่มันถูกกำหนดไว้, JavaScript prototypeจะสร้างสถานที่ให้บริการใหม่ในฟังก์ชั่นที่เรียกว่า ลองมัน:
var A = [];
A.prototype // undefined
A = function() {}
A.prototype // {} // got created when function() {} was defined
A.prototypeแตกต่างจาก__proto__ทรัพย์สินโดยสิ้นเชิง ในตัวอย่างของเรา 'A' ตอนนี้มีสองคุณสมบัติที่เรียกว่า 'ต้นแบบ' __proto__และ นี่เป็นความสับสนครั้งใหญ่สำหรับผู้คน prototypeและ__proto__คุณสมบัติไม่เกี่ยวข้องกันพวกมันแยกสิ่งต่าง ๆ ที่ชี้ไปที่ค่าต่างกัน
คุณอาจสงสัยว่า: ทำไม JavaScript มี__proto__คุณสมบัติสร้างขึ้นในทุก ๆ วัตถุ? ดีหนึ่งคำ: คณะผู้แทน เมื่อคุณเรียกคุณสมบัติบนวัตถุและวัตถุนั้นไม่มีอยู่ JavaScript จะค้นหาวัตถุที่อ้างอิงโดย__proto__เพื่อดูว่าอาจมีหรือไม่ ถ้ามันไม่มีมันก็จะดูที่__proto__คุณสมบัติของวัตถุนั้นและอื่น ๆ ... จนกระทั่งโซ่จบ ดังนั้นชื่อของห่วงโซ่ต้นแบบ แน่นอนถ้า__proto__ไม่ชี้ไปที่วัตถุและแทนที่จะชี้ไปnullที่โชคดีอย่างยิ่ง JavaScript ตระหนักดีว่าและจะส่งคืนคุณundefinedสำหรับคุณสมบัติ
คุณอาจสงสัยว่าทำไม JavaScript จึงสร้างคุณสมบัติที่เรียกว่าprototypeฟังก์ชันเมื่อคุณกำหนดฟังก์ชัน เพราะมันพยายามหลอกคุณใช่หลอกคุณว่ามันใช้งานได้เหมือนภาษาที่ใช้ในห้องเรียน
ลองดูตัวอย่างของเราและสร้าง "วัตถุ" จากA:
var a1 = new A();
มีบางอย่างเกิดขึ้นในพื้นหลังเมื่อสิ่งนี้เกิดขึ้น a1เป็นตัวแปรธรรมดาที่ได้รับมอบหมายวัตถุใหม่ที่ว่างเปล่า
ความจริงที่ว่าคุณใช้โอเปอเรเตอร์newก่อนที่การเรียกใช้ฟังก์ชันA()จะทำบางสิ่งเพิ่มเติมในพื้นหลัง newคำหลักสร้างวัตถุใหม่ซึ่งขณะนี้การอ้างอิงa1และวัตถุที่เป็นที่ว่างเปล่า นี่คือสิ่งที่เกิดขึ้นนอกจากนี้:
เราบอกว่าในแต่ละคำนิยามฟังก์ชั่นมีคุณสมบัติใหม่ที่สร้างขึ้นเรียกว่าprototype(ซึ่งคุณสามารถเข้าถึงได้ซึ่งแตกต่างจาก__proto__คุณสมบัติ) สร้างขึ้น? ตอนนี้กำลังใช้คุณสมบัตินี้อยู่
ดังนั้นตอนนี้เราถึงจุดที่เรามีa1วัตถุเปล่าอบสดใหม่ เราบอกว่าวัตถุทั้งหมดใน JavaScript มี__proto__คุณสมบัติภายในที่ชี้ไปยังบางสิ่ง ( a1เช่นมี) ไม่ว่าจะเป็นวัตถุว่างเปล่าหรือวัตถุอื่น สิ่งที่newผู้ประกอบการทำคือมันตั้งค่า__proto__คุณสมบัตินั้นให้ชี้ไปที่prototypeคุณสมบัติของฟังก์ชัน อ่านอีกครั้ง มันเป็นแบบนี้:
a1.__proto__ = A.prototype;
เราบอกว่าA.prototypeไม่มีอะไรมากไปกว่าวัตถุเปล่า (เว้นแต่เราจะเปลี่ยนเป็นสิ่งอื่นก่อนกำหนดa1) ตอนนี้โดยทั่วไปแล้วa1.__proto__ชี้ไปที่สิ่งเดียวกันA.prototypeซึ่งเป็นวัตถุที่ว่างเปล่า พวกเขาทั้งสองชี้ไปที่วัตถุเดียวกันซึ่งสร้างขึ้นเมื่อสายนี้เกิดขึ้น:
A = function() {} // JS: cool. let's also create A.prototype pointing to empty {}
ตอนนี้มีสิ่งอื่นเกิดขึ้นเมื่อvar a1 = new A()ประมวลผลคำสั่ง โดยพื้นฐานแล้วA()จะถูกประหารชีวิตและหาก A เป็นดังนี้:
var A = function() { this.hey = function() { alert('from A') } };
สิ่งที่อยู่ข้างในfunction() { }นั้นกำลังจะดำเนินการ เมื่อคุณไปถึงthis.hey..เส้นนั้นthisจะเปลี่ยนเป็นa1และคุณจะได้รับสิ่งนี้:
a1.hey = function() { alert('from A') }
ฉันจะไม่ครอบคลุมว่าทำไมthisการเปลี่ยนแปลงa1แต่นี่เป็นคำตอบที่ดีในการเรียนรู้เพิ่มเติม
ดังนั้นเพื่อสรุปเมื่อคุณvar a1 = new A()มี 3 สิ่งที่เกิดขึ้นในพื้นหลัง:
a1วัตถุที่ว่างเปล่าใหม่ทั้งหมดถูกสร้างขึ้นและได้รับมอบหมายให้a1 = {}a1.__proto__property ถูกกำหนดให้ชี้ไปที่สิ่งเดียวกับที่A.prototypeชี้ไปที่ (object ว่างอีก {})
A()กำลังดำเนินการฟังก์ชั่นโดยthisตั้งค่าเป็นวัตถุใหม่ที่ว่างเปล่าที่สร้างขึ้นในขั้นตอนที่ 1 (อ่านคำตอบที่ฉันอ้างถึงข้างต้นว่าทำไมthisการเปลี่ยนแปลงถึงa1)
ตอนนี้ลองสร้างวัตถุอื่น:
var a2 = new A();
ขั้นตอนที่ 1,2,3 จะทำซ้ำ คุณสังเกตเห็นบางสิ่ง? คำสำคัญคือการทำซ้ำ ขั้นตอนที่ 1: a2จะเป็นวัตถุที่ว่างเปล่าใหม่ขั้นตอนที่ 2: __proto__คุณสมบัติของมันจะชี้ไปที่สิ่งเดียวกันA.prototypeและที่สำคัญที่สุดขั้นตอนที่ 3: ฟังก์ชั่นA()จะถูกดำเนินการอีกครั้งซึ่งหมายความว่าa2จะได้รับheyคุณสมบัติที่มีฟังก์ชั่น a1และa2มีสองคุณสมบัติ SEPARATE ตั้งชื่อheyซึ่งชี้ไปที่ 2 ฟังก์ชัน SEPARATE! ตอนนี้เรามีฟังก์ชั่นที่ซ้ำกันในสองวัตถุที่แตกต่างกันทำสิ่งเดียวกันอุ๊ปส์ ... คุณสามารถจินตนาการถึงความหมายของหน่วยความจำของสิ่งนี้ถ้าเรามีวัตถุ 1,000 ชิ้นที่สร้างขึ้นด้วยnew Aหลังจากการประกาศฟังก์ชั่นทั้งหมด เราจะป้องกันสิ่งนี้ได้อย่างไร
จำได้ไหมว่าทำไม__proto__คุณสมบัติจึงมีอยู่ในทุกวัตถุ ดังนั้นถ้าคุณดึงyoManคุณสมบัติบนa1(ซึ่งไม่มีอยู่) __proto__คุณสมบัติของมันจะได้รับการพิจารณาซึ่งถ้ามันเป็นวัตถุ (และเป็นกรณีส่วนใหญ่) มันจะตรวจสอบว่ามีyoManหรือไม่ มันจะปรึกษาวัตถุนั้น__proto__เป็นต้นถ้าเป็นเช่นนั้นมันจะเอาค่าคุณสมบัตินั้นและแสดงให้คุณเห็น
ดังนั้นบางคนตัดสินใจที่จะใช้ความจริงนี้ + ความจริงที่ว่าเมื่อคุณสร้างa1ขึ้น__proto__คุณสมบัติของมันจะชี้ไปที่ออบเจ็กต์ (ว่าง) เดียวกันA.prototypeและทำสิ่งนี้:
var A = function() {}
A.prototype.hey = function() { alert('from prototype') };
เย็น! ตอนนี้เมื่อคุณสร้างa1มันจะผ่านขั้นตอนทั้ง 3 ขั้นตอนข้างต้นอีกครั้งและในขั้นตอนที่ 3 ก็ไม่ได้ทำอะไรเลยเนื่องจากfunction A()ไม่มีสิ่งใดที่จะต้องดำเนินการ และถ้าเราทำ:
a1.hey
จะเห็นว่าa1ไม่มีheyและจะตรวจสอบ__proto__วัตถุคุณสมบัติเพื่อดูว่ามีหรือไม่ซึ่งเป็นกรณี
ด้วยวิธีการนี้เราจะกำจัดส่วนหนึ่งออกจากขั้นตอนที่ 3 ซึ่งฟังก์ชั่นจะทำซ้ำในการสร้างวัตถุใหม่แต่ละครั้ง แทนการa1และa2มีการแยกheyทรัพย์สินตอนนี้ไม่มีของพวกเขามีมัน ซึ่งตอนนี้ฉันคิดว่าคุณคงเข้าใจแล้ว นั่นเป็นสิ่งที่ดี ... ถ้าคุณเข้าใจ__proto__และFunction.prototypeคำถามเช่นนี้จะค่อนข้างชัดเจน
หมายเหตุ: บางคนมักจะไม่เรียกคุณสมบัติต้นแบบภายในเนื่องจาก__proto__ฉันใช้ชื่อนี้ผ่านการโพสต์เพื่อแยกความแตกต่างให้ชัดเจนกับFunctional.prototypeทรัพย์สินว่าเป็นสองสิ่งที่แตกต่างกัน
__proto__และ.prototypeต่าง ๆ โดยสิ้นเชิง
ในกรณีส่วนใหญ่จะเหมือนกัน แต่รุ่นที่สองจะบันทึกหน่วยความจำเนื่องจากมีเพียงอินสแตนซ์เดียวของฟังก์ชันแทนที่จะเป็นฟังก์ชันแยกต่างหากสำหรับแต่ละวัตถุ
เหตุผลที่ใช้แบบฟอร์มแรกคือเข้าถึง "สมาชิกส่วนตัว" ตัวอย่างเช่น:
var A = function () {
var private_var = ...;
this.x = function () {
return private_var;
};
this.setX = function (new_x) {
private_var = new_x;
};
};
เนื่องจากกฎการกำหนดขอบเขตของ javascript ทำให้ private_var มีฟังก์ชันที่กำหนดให้กับ this.x แต่ไม่ใช่นอกวัตถุ
ตัวอย่างแรกเปลี่ยนอินเทอร์เฟซสำหรับวัตถุนั้นเท่านั้น ตัวอย่างที่สองเปลี่ยนอินเทอร์เฟซสำหรับวัตถุทั้งหมดของคลาสนั้น
xพร้อมใช้งานสำหรับวัตถุทั้งหมดที่มีการกำหนดต้นแบบเป็นอินสแตนซ์ใหม่ของ:function B () {}; B.prototype = new A(); var b = new B(); b.x() // Will call A.x if A is defined by first example;
ปัญหาขั้นสุดท้ายด้วยการใช้thisแทนprototypeคือเมื่อแทนที่เมธอด constructor ของคลาสพื้นฐานจะยังคงอ้างถึงเมธอด overridden พิจารณาสิ่งนี้:
BaseClass = function() {
var text = null;
this.setText = function(value) {
text = value + " BaseClass!";
};
this.getText = function() {
return text;
};
this.setText("Hello"); // This always calls BaseClass.setText()
};
SubClass = function() {
// setText is not overridden yet,
// so the constructor calls the superclass' method
BaseClass.call(this);
// Keeping a reference to the superclass' method
var super_setText = this.setText;
// Overriding
this.setText = function(value) {
super_setText.call(this, "SubClass says: " + value);
};
};
SubClass.prototype = new BaseClass();
var subClass = new SubClass();
console.log(subClass.getText()); // Hello BaseClass!
subClass.setText("Hello"); // setText is already overridden
console.log(subClass.getText()); // SubClass says: Hello BaseClass!
เมื่อเทียบกับ:
BaseClass = function() {
this.setText("Hello"); // This calls the overridden method
};
BaseClass.prototype.setText = function(value) {
this.text = value + " BaseClass!";
};
BaseClass.prototype.getText = function() {
return this.text;
};
SubClass = function() {
// setText is already overridden, so this works as expected
BaseClass.call(this);
};
SubClass.prototype = new BaseClass();
SubClass.prototype.setText = function(value) {
BaseClass.prototype.setText.call(this, "SubClass says: " + value);
};
var subClass = new SubClass();
console.log(subClass.getText()); // SubClass says: Hello BaseClass!
หากคุณคิดว่านี่ไม่ใช่ปัญหามันก็ขึ้นอยู่กับว่าคุณจะอยู่ได้โดยปราศจากตัวแปรส่วนตัวหรือไม่และคุณมีประสบการณ์มากพอที่จะรู้ว่ามีรอยรั่วหรือไม่เมื่อคุณเห็น นอกจากนี้ยังต้องใส่ตรรกะตัวสร้างหลังจากนิยามวิธีการไม่สะดวก
var A = function (param1) {
var privateVar = null; // Private variable
// Calling this.setPrivateVar(param1) here would be an error
this.setPrivateVar = function (value) {
privateVar = value;
console.log("setPrivateVar value set to: " + value);
// param1 is still here, possible memory leak
console.log("setPrivateVar has param1: " + param1);
};
// The constructor logic starts here possibly after
// many lines of code that define methods
this.setPrivateVar(param1); // This is valid
};
var a = new A(0);
// setPrivateVar value set to: 0
// setPrivateVar has param1: 0
a.setPrivateVar(1);
//setPrivateVar value set to: 1
//setPrivateVar has param1: 0
เมื่อเทียบกับ:
var A = function (param1) {
this.setPublicVar(param1); // This is valid
};
A.prototype.setPublicVar = function (value) {
this.publicVar = value; // No private variable
};
var a = new A(0);
a.setPublicVar(1);
console.log(a.publicVar); // 1
ทุกวัตถุเชื่อมโยงกับวัตถุต้นแบบ เมื่อพยายามเข้าถึงคุณสมบัติที่ไม่มีอยู่ JavaScript จะค้นหาวัตถุต้นแบบของวัตถุสำหรับคุณสมบัตินั้นและส่งคืนถ้ามีอยู่
ทรัพย์สินของตัวสร้างฟังก์ชั่นหมายถึงวัตถุต้นแบบของทุกกรณีที่สร้างขึ้นด้วยฟังก์ชั่นว่าเมื่อใช้prototypenew
ในตัวอย่างแรกของคุณคุณกำลังเพิ่มคุณสมบัติxให้กับแต่ละอินสแตนซ์ที่สร้างด้วยAฟังก์ชัน
var A = function () {
this.x = function () {
//do something
};
};
var a = new A(); // constructor function gets executed
// newly created object gets an 'x' property
// which is a function
a.x(); // and can be called like this
ในตัวอย่างที่สองคุณกำลังเพิ่มคุณสมบัติให้กับวัตถุต้นแบบที่อินสแตนซ์ทั้งหมดที่สร้างโดยAชี้ไปที่
var A = function () { };
A.prototype.x = function () {
//do something
};
var a = new A(); // constructor function gets executed
// which does nothing in this example
a.x(); // you are trying to access the 'x' property of an instance of 'A'
// which does not exist
// so JavaScript looks for that property in the prototype object
// that was defined using the 'prototype' property of the constructor
โดยสรุปในตัวอย่างแรกสำเนาของการทำงานที่ได้รับมอบหมายให้แต่ละอินสแตนซ์ ในตัวอย่างที่สองสำเนาเดียวของการทำงานร่วมกันโดยทุกกรณี
ความแตกต่างคืออะไร? => มาก
ฉันคิดว่าthisเวอร์ชันนี้ใช้เพื่อเปิดใช้งานการห่อหุ้มข้อมูลเช่นการซ่อนข้อมูล ช่วยจัดการกับตัวแปรส่วนตัว
ให้เราดูตัวอย่างต่อไปนี้:
var AdultPerson = function() {
var age;
this.setAge = function(val) {
// some housekeeping
age = val >= 18 && val;
};
this.getAge = function() {
return age;
};
this.isValid = function() {
return !!age;
};
};
ตอนนี้prototypeโครงสร้างสามารถใช้ได้ดังนี้:
ผู้ใหญ่ที่แตกต่างกันมีอายุที่ต่างกัน แต่ผู้ใหญ่ทุกคนจะได้รับสิทธิเหมือนกัน
ดังนั้นเราเพิ่มมันโดยใช้ต้นแบบมากกว่านี้
AdultPerson.prototype.getRights = function() {
// Should be valid
return this.isValid() && ['Booze', 'Drive'];
};
ให้ดูที่การใช้งานตอนนี้
var p1 = new AdultPerson;
p1.setAge(12); // ( age = false )
console.log(p1.getRights()); // false ( Kid alert! )
p1.setAge(19); // ( age = 19 )
console.log(p1.getRights()); // ['Booze', 'Drive'] ( Welcome AdultPerson )
var p2 = new AdultPerson;
p2.setAge(45);
console.log(p2.getRights()); // The same getRights() method, *** not a new copy of it ***
หวังว่านี่จะช่วยได้
Prototype เป็นเทมเพลตของคลาส ซึ่งใช้กับอินสแตนซ์ในอนาคตทั้งหมดของมัน ในขณะที่นี่เป็นตัวอย่างเฉพาะของวัตถุ
ฉันรู้ว่าสิ่งนี้ได้รับคำตอบถึงความตาย แต่ฉันต้องการแสดงตัวอย่างจริงของความแตกต่างความเร็ว
ที่นี่เรากำลังสร้างวัตถุใหม่ 2,000,000 ชิ้นด้วยprintวิธีการใน Chrome เรากำลังจัดเก็บทุกวัตถุในอาเรย์ การวางprintเครื่องต้นแบบจะใช้เวลาประมาณ 1/2 นาน
ให้ฉันให้คำตอบที่ครอบคลุมมากขึ้นที่ฉันได้เรียนรู้ในระหว่างการฝึกอบรม JavaScript
คำตอบส่วนใหญ่พูดถึงความแตกต่างอยู่แล้วเช่นเมื่อการสร้างต้นแบบฟังก์ชั่นที่ใช้ร่วมกันกับทุกกรณี (ในอนาคต) ในขณะที่การประกาศฟังก์ชั่นในชั้นเรียนจะสร้างสำเนาสำหรับแต่ละอินสแตนซ์
โดยทั่วไปไม่มีถูกหรือผิดมันเป็นเรื่องของรสนิยมหรือการตัดสินใจออกแบบขึ้นอยู่กับความต้องการของคุณ อย่างไรก็ตามต้นแบบเป็นเทคนิคที่ใช้ในการพัฒนาในลักษณะที่มุ่งเน้นวัตถุฉันหวังว่าคุณจะเห็นในตอนท้ายของคำตอบนี้
คุณแสดงสองรูปแบบในคำถามของคุณ ฉันจะพยายามอธิบายอีกสองข้อและพยายามอธิบายความแตกต่างหากเกี่ยวข้อง รู้สึกอิสระที่จะแก้ไข / ขยาย ในตัวอย่างทั้งหมดเป็นเรื่องเกี่ยวกับวัตถุรถยนต์ที่มีตำแหน่งและสามารถเคลื่อนย้ายได้
ไม่แน่ใจว่ารูปแบบนี้ยังคงมีความเกี่ยวข้องทุกวันนี้ แต่มีอยู่จริง และมันเป็นเรื่องดีที่จะรู้เกี่ยวกับมัน คุณเพียงผ่านวัตถุและคุณสมบัติไปยังฟังก์ชันมัณฑนากร มัณฑนากรส่งคืนวัตถุที่มีคุณสมบัติและวิธีการ
var carlike = function(obj, loc) {
obj.loc = loc;
obj.move = function() {
obj.loc++;
};
return obj;
};
var amy = carlike({}, 1);
amy.move();
var ben = carlike({}, 9);
ben.move();
ฟังก์ชั่นใน JavaScript เป็นวัตถุพิเศษ นอกเหนือจากการเรียกใช้ฟังก์ชั่นสามารถจัดเก็บคุณสมบัติเช่นวัตถุอื่น ๆ
ในกรณีCarนี้เป็นฟังก์ชั่น ( ยังคิดว่าวัตถุ ) ที่สามารถเรียกใช้ตามที่คุณคุ้นเคย มันมีคุณสมบัติmethods(ซึ่งเป็นวัตถุที่มีmoveฟังก์ชั่น) เมื่อCarมีการรื้อฟื้นextendการเรียกฟังก์ชันที่ไม่มายากลบางส่วนและขยายCarฟังก์ชั่น (คิดว่าวัตถุ) methodsด้วยวิธีการที่กำหนดไว้ภายใน
ตัวอย่างนี้แม้ว่าจะแตกต่างกันมาใกล้เคียงกับตัวอย่างแรกในคำถาม
var Car = function(loc) {
var obj = {loc: loc};
extend(obj, Car.methods);
return obj;
};
Car.methods = {
move : function() {
this.loc++;
}
};
var amy = Car(1);
amy.move();
var ben = Car(9);
ben.move();
สองรูปแบบแรกให้การอภิปรายของการใช้เทคนิคในการกำหนดวิธีการที่ใช้ร่วมกันหรือใช้วิธีการที่กำหนดไว้แบบอินไลน์ในร่างกายของตัวสร้าง ในทั้งสองกรณีทุกอินสแตนซ์มีmoveฟังก์ชั่นของตัวเอง
รูปแบบต้นแบบไม่ได้ให้ผลดีกับการตรวจสอบเดียวกันเนื่องจากการแบ่งปันฟังก์ชั่นผ่านการมอบหมายต้นแบบเป็นเป้าหมายที่สำคัญสำหรับรูปแบบต้นแบบ ตามที่คนอื่น ๆ ชี้ให้เห็นก็คาดว่าจะมีรอยความทรงจำที่ดีขึ้น
อย่างไรก็ตามมีจุดหนึ่งที่น่าสนใจที่จะทราบ: prototypeวัตถุทุกชิ้นมีคุณสมบัติความสะดวกสบายconstructorซึ่งชี้กลับไปที่ฟังก์ชั่น (คิดว่าวัตถุ) มันมาพร้อมกับ
เกี่ยวกับสามบรรทัดสุดท้าย:
ในตัวอย่างนี้Carเชื่อมโยงไปยังprototypeวัตถุซึ่งเชื่อมโยงconstructorไปยังCarตัวเองCar.prototype.constructorคือCarตัวมันเอง สิ่งนี้ช่วยให้คุณทราบว่าฟังก์ชันตัวสร้างใดสร้างวัตถุบางอย่าง
amy.constructorการค้นหาล้มเหลวจึงได้รับการแต่งตั้งให้Car.prototypeซึ่งมีคุณสมบัติคอนสตรัคเตอร์ และamy.constructorเป็นCarเช่นนั้น
นอกจากนี้เป็นamy ประกอบการทำงานโดยเห็นว่าวัตถุต้นแบบตัวถูกดำเนินการทางด้านขวาของ ( ) สามารถพบได้ทุกที่ในต้นแบบถูกดำเนินการทางด้านซ้ายของ ( ) ห่วงโซ่instanceof CarinstanceofCaramy
var Car = function(loc) {
var obj = Object.create(Car.prototype);
obj.loc = loc;
return obj;
};
Car.prototype.move = function() {
this.loc++;
};
var amy = Car(1);
amy.move();
var ben = Car(9);
ben.move();
console.log(Car.prototype.constructor);
console.log(amy.constructor);
console.log(amy instanceof Car);
นักพัฒนาบางคนอาจสับสนในการเริ่มต้น ดูตัวอย่างด้านล่าง:
var Dog = function() {
return {legs: 4, bark: alert};
};
var fido = Dog();
console.log(fido instanceof Dog);
ตัวinstanceofดำเนินการส่งคืนfalseเนื่องจากDogต้นแบบไม่พบในfidoห่วงโซ่ต้นแบบ เป็นวัตถุที่เรียบง่ายที่จะถูกสร้างขึ้นพร้อมกับตัวอักษรวัตถุคือมันเป็นเพียงแค่ได้รับมอบหมายให้fidoObject.prototype
นี่เป็นเพียงรูปแบบต้นแบบอีกรูปแบบหนึ่งในรูปแบบที่ง่ายกว่าและคุ้นเคยมากกว่าที่จะทำผู้ที่เขียนโปรแกรมใน Java เช่นเนื่องจากมันใช้ตัวnewสร้าง
มันเหมือนกับในรูปแบบต้นแบบจริงๆมันเป็นเพียงน้ำตาลแบบซินแทคติกของรูปแบบต้นแบบ
อย่างไรก็ตามความแตกต่างหลักคือมีการปรับให้เหมาะสมในเอ็นจิน JavaScript ที่ใช้เฉพาะเมื่อใช้รูปแบบ pseudoclassical เท่านั้น คิดว่ารูปแบบ pseudoclassical อาจเป็นรูปแบบต้นแบบเร็วขึ้น ความสัมพันธ์เชิงวัตถุในตัวอย่างทั้งสองนั้นเหมือนกัน
var Car = function(loc) {
this.loc = loc;
};
Car.prototype.move = function() {
this.loc++;
};
var amy = new Car(1);
amy.move();
var ben = new Car(9);
ben.move();
ท้ายที่สุดไม่ควรยากเกินไปที่จะรู้ว่าการเขียนโปรแกรมเชิงวัตถุสามารถทำได้อย่างไร มีสองส่วน
ส่วนหนึ่งที่กำหนดคุณสมบัติ / วิธีการทั่วไปในต้นแบบ (เชน)
และอีกส่วนหนึ่งที่คุณใส่คำจำกัดความที่ทำให้วัตถุแตกต่างจากกัน ( locตัวแปรในตัวอย่าง)
นี่คือสิ่งที่ช่วยให้เราสามารถใช้แนวคิดเช่นซูเปอร์คลาสหรือคลาสย่อยใน JavaScript
รู้สึกอิสระที่จะเพิ่มหรือแก้ไข เสร็จสมบูรณ์อีกครั้งฉันสามารถทำให้ชุมชนนี้เป็น Wiki ได้
ฉันเชื่อว่า @ Matthewth Crumley นั้นถูกต้อง พวกมันใช้งานได้หากไม่ได้มีโครงสร้างเทียบเท่า หากคุณใช้ Firebug เพื่อดูวัตถุที่สร้างขึ้นโดยใช้newคุณจะเห็นว่าพวกเขาเหมือนกัน อย่างไรก็ตามการตั้งค่าของฉันจะเป็นดังต่อไปนี้ ฉันเดาว่ามันดูเหมือนกับสิ่งที่ฉันคุ้นเคยใน C # / Java นั่นคือกำหนดคลาสกำหนดเขตข้อมูลตัวสร้างและวิธีการ
var A = function() {};
A.prototype = {
_instance_var: 0,
initialize: function(v) { this._instance_var = v; },
x: function() { alert(this._instance_var); }
};
แก้ไขไม่ได้หมายความว่าขอบเขตของตัวแปรเป็นแบบส่วนตัวฉันแค่พยายามอธิบายวิธีกำหนดคลาสของฉันในจาวาสคริปต์ ชื่อตัวแปรมีการเปลี่ยนแปลงเพื่อแสดงถึงสิ่งนี้
initializeและx methods do not refer to the _instance_var` บนAอินสแตนซ์ แต่เป็นแบบโกลบอล ใช้this._instance_varหากคุณต้องการใช้_instance_varคุณสมบัติของAอินสแตนซ์
ดังที่ได้กล่าวไว้ในคำตอบอื่น ๆ มันเป็นการพิจารณาประสิทธิภาพเนื่องจากฟังก์ชั่นในต้นแบบถูกแชร์กับอินสแตนซ์ทั้งหมด - แทนที่จะเป็นฟังก์ชั่นที่สร้างขึ้นสำหรับแต่ละอินสแตนซ์
ฉันรวบรวม jsperf เพื่อแสดงสิ่งนี้ มีความแตกต่างอย่างมากในเวลาที่ใช้ในการยกตัวอย่างชั้นเรียนถึงแม้ว่ามันจะมีความเกี่ยวข้องเฉพาะในกรณีที่คุณทำหลายกรณี
นึกถึงภาษาที่พิมพ์แบบคงที่สิ่งต่าง ๆprototypeเป็นแบบคงที่และเป็นสิ่งที่thisเกี่ยวข้องกับอินสแตนซ์