new
คำหลักใน JavaScript อาจจะค่อนข้างสับสนเมื่อมันเป็นครั้งแรกที่พบเป็นคนมักจะคิดว่า JavaScript ไม่ได้เป็นภาษาการเขียนโปรแกรมเชิงวัตถุ
- มันคืออะไร?
- มันแก้ปัญหาอะไรได้บ้าง?
- มันเหมาะสมเมื่อใดและเมื่อใด
new
คำหลักใน JavaScript อาจจะค่อนข้างสับสนเมื่อมันเป็นครั้งแรกที่พบเป็นคนมักจะคิดว่า JavaScript ไม่ได้เป็นภาษาการเขียนโปรแกรมเชิงวัตถุ
คำตอบ:
มันทำ 5 สิ่ง:
this
จุดตัวแปรไปยังวัตถุที่สร้างขึ้นใหม่this
มีการกล่าวถึงnull
อ้างอิงที่ไม่ใช่วัตถุ ในกรณีนี้การอ้างอิงวัตถุนั้นจะถูกส่งกลับแทนหมายเหตุ: ฟังก์ชั่นคอนสตรัคหมายถึงฟังก์ชั่นหลังnew
คำหลักเช่นเดียวกับใน
new ConstructorFunction(arg1, arg2)
เมื่อดำเนินการเสร็จแล้วหากมีการร้องขอคุณสมบัติที่ไม่ได้กำหนดของวัตถุใหม่สคริปต์จะตรวจสอบวัตถุ[ต้นแบบ] ของวัตถุนั้นเพื่อหาคุณสมบัติแทน นี่คือวิธีที่คุณจะได้รับสิ่งที่คล้ายกับการสืบทอดคลาสแบบดั้งเดิมใน JavaScript
ส่วนที่ยากที่สุดเกี่ยวกับเรื่องนี้เป็นจำนวนจุด 2. ทุกวัตถุ (รวมถึงฟังก์ชั่น) มีคุณสมบัติภายในที่เรียกว่า[[ต้นแบบ]] มันสามารถเพียงถูกตั้งค่าในเวลาสร้างวัตถุอย่างใดอย่างหนึ่งกับใหม่กับObject.createหรือขึ้นอยู่กับตัวอักษร (ค่าเริ่มต้นฟังก์ชั่นการ Function.prototype ตัวเลขเพื่อ Number.prototype ฯลฯ ) มันสามารถอ่านได้เฉพาะกับObject.getPrototypeOf (someObject) นอกจากนี้ไม่มีทางอื่นที่จะตั้งหรืออ่านค่านี้
ฟังก์ชั่นนอกเหนือจากคุณสมบัติ[[ต้นแบบ]] ที่ซ่อนอยู่ยังมีคุณสมบัติที่เรียกว่าแบบตัวอย่างและนี่คือสิ่งที่คุณสามารถเข้าถึงและแก้ไขเพื่อมอบคุณสมบัติและวิธีการสืบทอดสำหรับวัตถุที่คุณทำ
นี่คือตัวอย่าง:
ObjMaker = function() {this.a = 'first';};
// ObjMaker is just a function, there's nothing special about it that makes
// it a constructor.
ObjMaker.prototype.b = 'second';
// like all functions, ObjMaker has an accessible prototype property that
// we can alter. I just added a property called 'b' to it. Like
// all objects, ObjMaker also has an inaccessible [[prototype]] property
// that we can't do anything with
obj1 = new ObjMaker();
// 3 things just happened.
// A new, empty object was created called obj1. At first obj1 was the same
// as {}. The [[prototype]] property of obj1 was then set to the current
// object value of the ObjMaker.prototype (if ObjMaker.prototype is later
// assigned a new object value, obj1's [[prototype]] will not change, but you
// can alter the properties of ObjMaker.prototype to add to both the
// prototype and [[prototype]]). The ObjMaker function was executed, with
// obj1 in place of this... so obj1.a was set to 'first'.
obj1.a;
// returns 'first'
obj1.b;
// obj1 doesn't have a property called 'b', so JavaScript checks
// its [[prototype]]. Its [[prototype]] is the same as ObjMaker.prototype
// ObjMaker.prototype has a property called 'b' with value 'second'
// returns 'second'
มันก็เหมือนกับการสืบทอดคลาสเพราะตอนนี้วัตถุใด ๆ ที่คุณใช้new ObjMaker()
จะปรากฏว่ามีการสืบทอดคุณสมบัติ 'b' ด้วย
หากคุณต้องการบางสิ่งบางอย่างเช่นคลาสย่อยคุณต้องทำสิ่งนี้:
SubObjMaker = function () {};
SubObjMaker.prototype = new ObjMaker(); // note: this pattern is deprecated!
// Because we used 'new', the [[prototype]] property of SubObjMaker.prototype
// is now set to the object value of ObjMaker.prototype.
// The modern way to do this is with Object.create(), which was added in ECMAScript 5:
// SubObjMaker.prototype = Object.create(ObjMaker.prototype);
SubObjMaker.prototype.c = 'third';
obj2 = new SubObjMaker();
// [[prototype]] property of obj2 is now set to SubObjMaker.prototype
// Remember that the [[prototype]] property of SubObjMaker.prototype
// is ObjMaker.prototype. So now obj2 has a prototype chain!
// obj2 ---> SubObjMaker.prototype ---> ObjMaker.prototype
obj2.c;
// returns 'third', from SubObjMaker.prototype
obj2.b;
// returns 'second', from ObjMaker.prototype
obj2.a;
// returns 'first', from SubObjMaker.prototype, because SubObjMaker.prototype
// was created with the ObjMaker function, which assigned a for us
ฉันอ่านขยะจำนวนหนึ่งเกี่ยวกับเรื่องนี้ก่อนที่จะหาหน้านี้ซึ่งอธิบายได้ดีมากกับไดอะแกรมที่สวยงาม
ObjMaker
นิยามว่าเป็นฟังก์ชันที่ส่งกลับค่า
new
มีอยู่เพื่อให้คุณไม่ต้องเขียนวิธีการของโรงงานเพื่อสร้าง / คัดลอกฟังก์ชั่น / วัตถุ มันหมายถึง "คัดลอกสิ่งนี้ทำให้มันเหมือนกับของผู้ปกครอง 'คลาส' ทำอย่างมีประสิทธิภาพและถูกต้องและเก็บข้อมูลการสืบทอดที่สามารถเข้าถึงได้เฉพาะฉัน JS ภายใน" ในการทำเช่นนั้นมันจะแก้ไขสิ่งที่ไม่สามารถเข้าถึงได้ภายในprototype
ของวัตถุใหม่เพื่อแค็ปซูลสมาชิกที่ได้รับการสืบทอดโดยเลียนแบบห่วงโซ่การสืบทอด OO แบบคลาสสิก (ซึ่งไม่สามารถแก้ไขได้) คุณสามารถจำลองสิ่งนี้ได้โดยไม่ต้องnew
แต่การสืบทอดจะสามารถแก้ไขได้แบบไทม์ ดี? ไม่ดี? แล้วแต่คุณ.
Notice that this pattern is deprecated!
เป็น รูปแบบที่ถูกต้องทันสมัยเพื่อตั้งค่าต้นแบบของคลาสคืออะไร
สมมติว่าคุณมีฟังก์ชั่นนี้:
var Foo = function(){
this.A = 1;
this.B = 2;
};
หากคุณเรียกสิ่งนี้ว่าเป็นฟังก์ชั่นสแตนด์อโลน:
Foo();
การใช้งานฟังก์ชั่นนี้จะเพิ่มคุณสมบัติสองอย่างให้กับwindow
วัตถุ ( A
และB
) มันเพิ่มเข้าไปในwindow
เพราะwindow
เป็นวัตถุที่เรียกว่าฟังก์ชั่นเมื่อคุณดำเนินการเช่นนั้นและthis
ในฟังก์ชั่นเป็นวัตถุที่เรียกว่าฟังก์ชั่น ใน Javascript อย่างน้อย
ตอนนี้เรียกมันว่าสิ่งนี้ด้วยnew
:
var bar = new Foo();
เกิดอะไรขึ้นเมื่อคุณเพิ่มnew
ในการเรียกใช้ฟังก์ชันคือวัตถุใหม่ถูกสร้างขึ้น (แค่var bar = new Object()
) และthis
ภายในฟังก์ชันชี้ไปที่สิ่งใหม่ที่Object
คุณเพิ่งสร้างแทนที่จะไปยังวัตถุที่เรียกว่าฟังก์ชัน ดังนั้นbar
ขณะนี้วัตถุที่มีคุณสมบัติและA
B
ฟังก์ชั่นใด ๆ สามารถเป็นตัวสร้างมันก็ไม่ได้ทำให้รู้สึก
window
โดยปริยาย แม้ในการปิดแม้ว่าจะไม่เปิดเผยชื่อ อย่างไรก็ตามในตัวอย่างมันเป็นวิธีการที่ง่ายภาวนาบนหน้าต่าง: Foo();
=> =>[default context].Foo();
window.Foo();
ในการแสดงออกนี้window
เป็นบริบท (ไม่เพียง แต่ผู้โทรซึ่งไม่สำคัญ)
นอกจากคำตอบของ Daniel Howard แล้วนี่คือสิ่งที่new
ทำ (หรืออย่างน้อยก็ดูเหมือนว่าจะทำ):
function New(func) {
var res = {};
if (func.prototype !== null) {
res.__proto__ = func.prototype;
}
var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));
if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
return ret;
}
return res;
}
ในขณะที่
var obj = New(A, 1, 2);
เทียบเท่ากับ
var obj = new A(1, 2);
func.prototype
ไปได้null
อย่างไร? คุณช่วยอธิบายเพิ่มเติมหน่อยได้ไหม?
A.prototype = null;
ในกรณีnew A()
นั้นจะส่งผลให้เกิดบนวัตถุนั่นคือจุดเริ่มต้นต้นแบบภายในไปยังObject
วัตถุ: jsfiddle.net/Mk42Z
Object(ret) === ret
เพื่อทดสอบว่าสิ่งที่เป็นวัตถุที่ฉันชอบ
typeof
ทดสอบช่วยให้เข้าใจได้ง่ายขึ้นว่าเกิดอะไรขึ้นเบื้องหลัง
ลองใช้รหัสต่อไปนี้ในคอนโซลของเบราว์เซอร์
function Foo() {
return this;
}
var a = Foo(); //returns window object
var b = new Foo(); //returns empty object of foo
a instanceof Window; // true
a instanceof Foo; // false
b instanceof Window; // false
b instanceof Foo; // true
ตอนนี้คุณสามารถอ่านคำตอบ wiki ของชุมชนได้แล้ว :)
return this;
ให้ผลผลิตให้ผลลัพธ์เดียวกัน
ดังนั้นมันอาจไม่ใช่สำหรับการสร้างอินสแตนซ์ของวัตถุ
มันใช้ตรงนั้น คุณกำหนด constructor ของฟังก์ชันดังนี้:
function Person(name) {
this.name = name;
}
var john = new Person('John');
อย่างไรก็ตามประโยชน์พิเศษที่ ECMAScript มีคือคุณสามารถขยายออกไปได้ด้วย.prototype
คุณสมบัติดังนั้นเราจึงสามารถทำสิ่งที่ ...
Person.prototype.getName = function() { return this.name; }
วัตถุทั้งหมดที่สร้างขึ้นจากตัวสร้างนี้จะมีgetName
ห่วงโซ่ต้นแบบที่พวกเขาสามารถเข้าถึงได้
class
คีย์เวิร์ด แต่คุณสามารถทำสิ่งเดียวกันได้
JavaScript เป็นภาษาการเขียนโปรแกรมเชิงวัตถุและมันถูกใช้อย่างแม่นยำสำหรับการสร้างอินสแตนซ์ มันใช้ต้นแบบมากกว่าคลาส แต่นั่นไม่ได้หมายความว่ามันไม่ใช่เชิงวัตถุ
Javascript เป็นภาษาการเขียนโปรแกรมแบบไดนามิกที่รองรับกระบวนทัศน์การเขียนโปรแกรมเชิงวัตถุและใช้สำหรับการสร้างอินสแตนซ์ใหม่ของวัตถุ
คลาสไม่จำเป็นสำหรับวัตถุ - Javascript เป็นภาษาที่ใช้ต้นแบบ
มีคำตอบที่ดีมากอยู่แล้ว แต่ฉันกำลังโพสต์คำตอบใหม่เพื่อเน้นการสังเกตของฉันในกรณีที่IIIด้านล่างเกี่ยวกับสิ่งที่เกิดขึ้นเมื่อคุณมีคำสั่งส่งคืนอย่างชัดเจนในฟังก์ชันที่คุณกำลังnew
ทำอยู่ ดูกรณีด้านล่าง:
กรณี I :
var Foo = function(){
this.A = 1;
this.B = 2;
};
console.log(Foo()); //prints undefined
console.log(window.A); //prints 1
Foo
ดังกล่าวข้างต้นเป็นกรณีธรรมดาของการเรียกฟังก์ชั่นที่ไม่ระบุชื่อโดยชี้ให้เห็น เมื่อคุณเรียกใช้ฟังก์ชั่นนี้มันจะกลับundefined
มา เนื่องจากไม่มีคำสั่งส่งคืนที่ชัดเจนดังนั้นล่าม JavaScript จึงใส่return undefined;
คำสั่งลงในส่วนท้ายของฟังก์ชัน หน้าต่างที่นี่คือวัตถุที่เรียกใช้ (ตามบริบทthis
) ซึ่งรับค่าใหม่A
และB
คุณสมบัติ
กรณีที่สอง :
var Foo = function(){
this.A = 1;
this.B = 2;
};
var bar = new Foo();
console.log(bar()); //illegal isn't pointing to a function but an object
console.log(bar.A); //prints 1
นี่ JavaScript ล่ามเห็นnew
คำหลักที่สร้างวัตถุใหม่ซึ่งทำหน้าที่เป็นวัตถุภาวนา (บริบทthis
) Foo
ของฟังก์ชั่นที่ไม่ระบุชื่อโดยชี้ให้เห็น ในกรณีนี้A
และB
กลายเป็นคุณสมบัติบนวัตถุที่สร้างขึ้นใหม่ (แทนที่วัตถุหน้าต่าง) เนื่องจากคุณไม่มีคำสั่งส่งคืนที่ชัดเจนดังนั้นล่าม JavaScript จึงใส่คำสั่ง return เพื่อส่งคืนออบเจ็กต์ใหม่ที่สร้างขึ้นเนื่องจากการใช้new
คำหลัก
กรณีที่สาม :
var Foo = function(){
this.A = 1;
this.B = 2;
return {C:20,D:30};
};
var bar = new Foo();
console.log(bar.C);//prints 20
console.log(bar.A); //prints undefined. bar is not pointing to the object which got created due to new keyword.
ที่นี่อีกครั้ง JavaScript ล่ามเห็นnew
คำหลักที่สร้างวัตถุใหม่ซึ่งทำหน้าที่เป็นวัตถุภาวนา (บริบทthis
) Foo
ของฟังก์ชั่นที่ไม่ระบุชื่อโดยชี้ให้เห็น อีกครั้งA
และB
กลายเป็นคุณสมบัติในวัตถุที่สร้างขึ้นใหม่ แต่คราวนี้คุณมีคำสั่งส่งคืนอย่างชัดเจนดังนั้นล่าม JavaScript จะไม่ทำสิ่งใดของมันเอง
สิ่งที่ควรทราบในกรณีที่IIIคือวัตถุที่สร้างขึ้นเนื่องจากnew
คำหลักหายไปจากเรดาร์ของคุณ bar
จริง ๆ แล้วชี้ไปที่วัตถุที่แตกต่างอย่างสิ้นเชิงซึ่งไม่ใช่ล่าม JavaScript ที่สร้างขึ้นเนื่องจากnew
คำหลัก
อ้างถึง David Flanagan จาก JavaScripit: The Definitive Guide (6th Edition), Ch. 4, หน้า # 62:
เมื่อนิพจน์การสร้างวัตถุได้รับการประเมิน JavaScript จะสร้างวัตถุว่างเปล่าใหม่เช่นเดียวกับที่สร้างขึ้นโดย object initializer {} ถัดไปจะเรียกใช้ฟังก์ชันที่ระบุพร้อมกับอาร์กิวเมนต์ที่ระบุส่งผ่านวัตถุใหม่เป็นค่าของคำหลักนี้ ฟังก์ชันสามารถใช้ฟังก์ชันนี้เพื่อเริ่มต้นคุณสมบัติของวัตถุที่สร้างขึ้นใหม่ ฟังก์ชั่นที่เขียนขึ้นเพื่อใช้เป็นคอนสตรัคเตอร์จะไม่ส่งคืนค่าและค่าของนิพจน์การสร้างวัตถุเป็นวัตถุที่สร้างขึ้นใหม่และเริ่มต้นได้ หากตัวสร้างส่งคืนค่าวัตถุค่านั้นจะกลายเป็นค่าของนิพจน์การสร้างวัตถุและวัตถุที่สร้างขึ้นใหม่จะถูกทิ้ง
--- ข้อมูลเพิ่มเติม ---
ฟังก์ชั่นที่ใช้ในตัวอย่างโค้ดของกรณีข้างต้นมีชื่อพิเศษใน JS world ดังต่อไปนี้:
Case I และ II - ฟังก์ชันตัวสร้าง
Case III - ฟังก์ชั่นจากโรงงาน ไม่ควรใช้ฟังก์ชั่นจากโรงงานกับnew
คำหลักที่ฉันได้ทำเพื่ออธิบายแนวคิดในเธรดปัจจุบัน
คุณสามารถอ่านเกี่ยวกับความแตกต่างระหว่างพวกเขาในนี้ด้าย
บางครั้งรหัสง่ายกว่าคำ:
var func1 = function (x) { this.x = x; } // used with 'new' only
var func2 = function (x) { var z={}; z.x = x; return z; } // used both ways
func1.prototype.y = 11;
func2.prototype.y = 12;
A1 = new func1(1); // has A1.x AND A1.y
A2 = func1(1); // undefined ('this' refers to 'window')
B1 = new func2(2); // has B1.x ONLY
B2 = func2(2); // has B2.x ONLY
สำหรับฉันตราบใดที่ฉันไม่ได้ต้นแบบฉันใช้รูปแบบของ func2 เพราะให้ความยืดหยุ่นเล็กน้อยทั้งภายในและภายนอกฟังก์ชั่น
B1 = new func2(2);
<- ทำไมจะไม่ได้B1.y
?
new
คำหลักที่เปลี่ยนแปลงบริบทตามที่ฟังก์ชั่นจะถูกเรียกใช้และกลับชี้ไปที่บริบท
เมื่อคุณไม่ใช้new
คำสำคัญบริบทภายใต้ฟังก์ชันที่Vehicle()
ทำงานจะเป็นบริบทเดียวกันกับที่คุณเรียกใช้Vehicle
ฟังก์ชัน this
คำหลักจะอ้างถึงบริบทเดียวกัน เมื่อคุณใช้new Vehicle()
บริบทใหม่จะถูกสร้างขึ้นดังนั้นคำหลักthis
ภายในฟังก์ชั่นหมายถึงบริบทใหม่ สิ่งที่คุณจะได้รับคือบริบทที่สร้างขึ้นใหม่
new
คำหลักสำหรับการสร้างอินสแตนซ์วัตถุใหม่ และใช่จาวาสคริปต์เป็นภาษาการเขียนโปรแกรมแบบไดนามิกซึ่งรองรับกระบวนทัศน์การเขียนโปรแกรมเชิงวัตถุ ข้อตกลงเกี่ยวกับการตั้งชื่อวัตถุนั้นให้ใช้ตัวพิมพ์ใหญ่สำหรับวัตถุที่ควรจะสร้างอินสแตนซ์โดยคำหลักใหม่
obj = new Element();
new
คำหลักที่ใช้ในจาวาสคริปต์เพื่อสร้างวัตถุจากฟังก์ชั่นผู้สร้าง new
คำหลักที่จะต้องมีการวางไว้ก่อนการเรียกฟังก์ชั่นคอนสตรัคและจะทำสิ่งต่อไปนี้:
this
คำหลักกับวัตถุที่สร้างขึ้นใหม่และดำเนินการฟังก์ชั่นการสร้างfunction Dog (age) {
this.age = age;
}
const doggie = new Dog(12);
console.log(doggie);
console.log(doggie.__proto__ === Dog.prototype) // true
เกิดอะไรขึ้น:
const doggie
พูดว่า: เราต้องการหน่วยความจำสำหรับการประกาศตัวแปร=
พูดว่า: เราจะเริ่มต้นตัวแปรนี้ด้วยนิพจน์หลังจาก=
new Dog(12)
การแสดงออกเป็น เอ็นจิน JS เห็นคำสำคัญใหม่สร้างวัตถุใหม่และตั้งค่าต้นแบบเป็น Dog.prototypethis
ตั้งค่าเป็นวัตถุใหม่ ในขั้นตอนนี้คือที่อายุถูกกำหนดให้กับวัตถุ doggie ที่สร้างขึ้นใหม่new
คำหลักที่สร้างอินสแตนซ์ของวัตถุที่ใช้ทำหน้าที่เป็นผู้สร้าง ตัวอย่างเช่น
var Foo = function() {};
Foo.prototype.bar = 'bar';
var foo = new Foo();
foo instanceof Foo; // true
อินสแตนซ์รับช่วงจากprototype
ฟังก์ชันของตัวสร้าง ได้รับตัวอย่างข้างต้น ...
foo.bar; // 'bar'
จาวาสคริปต์ต่อ Si สามารถแตกต่างกันอย่างมากจากแพลตฟอร์มไปยังแพลตฟอร์มเนื่องจากมันเป็นการใช้งานของข้อกำหนดดั้งเดิม EcmaScript
ไม่ว่าในกรณีใดการใช้งาน JavaScript ทั้งหมดที่เป็นไปตามข้อกำหนดของ EcmaScript นั้นจะให้ภาษาเชิงวัตถุแก่คุณ ตามมาตรฐาน ES:
ECMAScript เป็นภาษาโปรแกรมเชิงวัตถุสำหรับดำเนินการคำนวณและจัดการวัตถุคำนวณภายในสภาพแวดล้อมโฮสต์
ตอนนี้เราได้ตกลงกันแล้วว่า JavaScript คือการนำ EcmaScript มาใช้ดังนั้นมันจึงเป็นภาษาเชิงวัตถุ คำจำกัดความของการnew
ดำเนินการในภาษาเชิงวัตถุใด ๆ กล่าวว่าคำหลักดังกล่าวถูกใช้เพื่อสร้างอินสแตนซ์ของวัตถุจากคลาสบางประเภท (รวมถึงประเภทที่ไม่ระบุชื่อในกรณีเช่น C #)
ใน EcmaScript เราไม่ใช้คลาสอย่างที่คุณสามารถอ่านได้จากสเปค:
ECMAScript ไม่ได้ใช้คลาสเช่นที่อยู่ใน C ++, Smalltalk หรือ Java วัตถุอาจถูกสร้างขึ้นด้วยวิธีการต่าง ๆ รวมถึงผ่านสัญกรณ์ตามตัวอักษรหรือผ่านตัวสร้างที่สร้างวัตถุแล้วรันโค้ดที่เริ่มต้นทั้งหมดหรือบางส่วนโดยการกำหนดค่าเริ่มต้นให้กับคุณสมบัติของพวกเขา ตัวสร้างแต่ละตัวเป็นฟังก์ชั่นที่มีคุณสมบัติชื่อว่า - prototype ‖ที่ใช้ในการสร้างการสืบทอดที่อิงกับต้นแบบและคุณสมบัติที่ใช้ร่วมกัน วัตถุถูกสร้างขึ้นโดย
ใช้ constructors ในนิพจน์ใหม่ ตัวอย่างเช่นใหม่ Date (2009,11) สร้างวัตถุ Date ใหม่ การเรียกคอนสตรัคเตอร์โดยไม่ใช้ใหม่จะมีผลกระทบที่ขึ้นอยู่กับคอนสตรัคเตอร์ ตัวอย่างเช่น Date () สร้างการแสดงสตริงของวันที่และเวลาปัจจุบันแทนที่จะเป็นวัตถุ