ความแตกต่างระหว่างอะไร
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.x
window.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
ตั้งแต่ฟังก์ชั่นไม่ได้กลับอะไรจะมีค่าของA
undefined
ความแตกต่างระหว่างสองวิธีนี้ยังแสดงให้เห็นว่าคุณกำลังทำให้เป็นอันดับและยกเลิกการทำให้เป็นอันดับวัตถุ 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 จะค้นหาวัตถุต้นแบบของวัตถุสำหรับคุณสมบัตินั้นและส่งคืนถ้ามีอยู่
ทรัพย์สินของตัวสร้างฟังก์ชั่นหมายถึงวัตถุต้นแบบของทุกกรณีที่สร้างขึ้นด้วยฟังก์ชั่นว่าเมื่อใช้prototype
new
ในตัวอย่างแรกของคุณคุณกำลังเพิ่มคุณสมบัติ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
Car
instanceof
Car
amy
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
ห่วงโซ่ต้นแบบ เป็นวัตถุที่เรียบง่ายที่จะถูกสร้างขึ้นพร้อมกับตัวอักษรวัตถุคือมันเป็นเพียงแค่ได้รับมอบหมายให้fido
Object.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
เกี่ยวข้องกับอินสแตนซ์