การกำหนดตัวแปรทำงานอย่างไรใน JavaScript


99

ดังนั้นฉันจึงเล่นไปเรื่อย ๆ เพื่อดูว่าการกำหนดจำนวนมากทำงานอย่างไรใน JavaScript

ก่อนอื่นฉันลองใช้ตัวอย่างนี้ในคอนโซล:

a = b = {};
a.foo = 'bar';
console.log(b.foo);

ผลลัพธ์คือ "bar" แสดงในการแจ้งเตือน นั่นก็ยุติธรรมเพียงพอaและbเป็นเพียงนามแฝงของวัตถุเดียวกัน จากนั้นฉันก็คิดว่าฉันจะทำให้ตัวอย่างนี้ง่ายขึ้นได้อย่างไร

a = b = 'foo';
a = 'bar';
console.log(b);

มันก็เหมือนกันใช่มั้ย? คราวนี้กลับfooไม่barเป็นไปตามที่ฉันคาดหวังจากพฤติกรรมของตัวอย่างแรก

ทำไมสิ่งนี้ถึงเกิดขึ้น?

หมายเหตุตัวอย่างนี้สามารถทำให้ง่ายขึ้นด้วยรหัสต่อไปนี้:

a = {};
b = a;
a.foo = 'bar';
console.log(b.foo);

a = 'foo';
b = a;
a = 'bar';
console.log(b);

(ฉันสงสัยว่า JavaScript ถือว่าไพรเมทีฟเช่นสตริงและจำนวนเต็มแตกต่างจากแฮชแฮชจะส่งคืนตัวชี้ในขณะที่ไพรเวต "คอร์" ส่งคืนสำเนาของตัวเอง)


อธิบายการมอบหมายที่นี่syntaxsuccess.com/viewarticle/…
TGH

คำตอบ:


115

ในตัวอย่างแรกคุณกำลังตั้งค่าคุณสมบัติของวัตถุที่มีอยู่ ในตัวอย่างที่สองคุณกำลังกำหนดวัตถุใหม่

a = b = {};

aและbตอนนี้เป็นตัวชี้ไปยังวัตถุเดียวกัน ดังนั้นเมื่อคุณทำ:

a.foo = 'bar';

มันตั้งค่าb.fooเช่นกันaและbชี้ไปที่วัตถุเดียวกัน

อย่างไรก็ตาม!

หากคุณทำสิ่งนี้แทน:

a = 'bar';

คุณกำลังบอกว่าaตอนนี้ชี้ไปที่วัตถุอื่น สิ่งนี้ไม่มีผลต่อสิ่งที่aชี้ไปก่อนหน้านี้

ใน JavaScript การกำหนดตัวแปรและการกำหนดคุณสมบัติคือ 2 การดำเนินการที่แตกต่างกัน เป็นการดีที่สุดที่จะคิดว่าตัวแปรเป็นตัวชี้ไปยังวัตถุและเมื่อคุณกำหนดให้กับตัวแปรโดยตรงคุณจะไม่ได้ปรับเปลี่ยนวัตถุใด ๆ เพียง แต่กำหนดตัวแปรของคุณให้เป็นวัตถุอื่น

แต่การกำหนดคุณสมบัติเช่นa.fooจะแก้ไขวัตถุที่aชี้ไป แน่นอนว่านี่ยังปรับเปลี่ยนการอ้างอิงอื่น ๆ ทั้งหมดที่ชี้ไปที่วัตถุนี้เพียงเพราะพวกเขาชี้ไปที่วัตถุเดียวกันทั้งหมด


3
"คุณกำลังบอกว่าตอนนี้ชี้ไปที่วัตถุอื่น" ไม่ใช่อย่าใช้คำว่าวัตถุ สตริงไม่ใช่วัตถุใน JavaScript
Nosredna

10
บางทีสตริงอาจไม่ใช่ในทางเทคนิคของประเภทจาวาสคริปต์ "Object" แต่สามารถนึกถึงอ็อบเจ็กต์ในความหมาย OO
Alex Wayne

2
@ Squeegy: สตริงเป็นแบบดั้งเดิมไม่ใช่วัตถุ: คุณไม่สามารถกำหนดคุณสมบัติโดยพลการให้กับสตริงได้! พวกเขามีพฤติกรรมเหมือนวัตถุเท่านั้นเนื่องจากสิ่งที่เรียกว่า autoboxing ใน Java
Christoph

11
แต่สตริงมีวิธีการและคุณสมบัติและต้นแบบของสตริงสามารถแก้ไขได้อย่างแน่นอน พวกเขาทำหน้าที่เหมือนสิ่งของ
Cameron Martin

9
@ddlshack: ตามที่ Christoph อธิบายนี่เป็นเพราะการทำ autoboxing หากคุณกำลังจะกำหนดสตริงผ่านvar foo = new String('foo');แล้วว่าจะเป็นวัตถุสตริง (และtypeofจะยืนยันเรื่องนี้) แต่ถ้าคุณประกาศผ่านสตริงลิเทอรัลแสดงว่าเป็นสตริงไพรเมติก ดู: developer.mozilla.org/th/JavaScript/Reference/Global_Objects/…
หมิ่นพระบรมเดชานุภาพ

26

คำถามของคุณได้รับคำตอบที่น่าพอใจแล้วโดย Squeegy - ไม่มีอะไรเกี่ยวข้องกับวัตถุเทียบกับวัตถุดั้งเดิม แต่มีการกำหนดตัวแปรใหม่เทียบกับคุณสมบัติการตั้งค่าในวัตถุที่อ้างอิงเดียวกัน

ดูเหมือนว่าจะมีความสับสนมากมายเกี่ยวกับประเภท JavaScript ในคำตอบและความคิดเห็นดังนั้นนี่คือบทนำเล็ก ๆ เกี่ยวกับระบบประเภทของ JavaScript:

ใน JavaScript มีค่าสองประเภทที่แตกต่างกันโดยพื้นฐานคือค่าดั้งเดิมและอ็อบเจ็กต์ (และไม่มีสิ่งที่เหมือนกับ 'แฮช')

สตริงตัวเลขและบูลีนเช่นเดียวกับnullและundefinedเป็นพื้นฐานวัตถุคือทุกสิ่งทุกอย่างที่สามารถมีคุณสมบัติ แม้แต่อาร์เรย์และฟังก์ชันก็เป็นวัตถุทั่วไปดังนั้นจึงสามารถเก็บคุณสมบัติตามอำเภอใจได้ คุณสมบัติเหล่านี้แตกต่างกันในคุณสมบัติ [[Class]] ภายใน (ฟังก์ชันเพิ่มเติมมีคุณสมบัติที่เรียกว่า [[Call]] และ [[สร้าง]] แต่เดี๋ยวก่อนนั่นคือรายละเอียด)

สาเหตุที่ค่าดั้งเดิมอาจมีพฤติกรรมเหมือนอ็อบเจกต์เป็นเพราะการทำ autoboxing แต่ตัวดั้งเดิมไม่สามารถเก็บคุณสมบัติใด ๆ ไว้ได้

นี่คือตัวอย่าง:

var a = 'quux';
a.foo = 'bar';
document.writeln(a.foo);

ออกจะนี้undefined: ถือเป็นค่าดั้งเดิมที่ได้รับการเลื่อนตำแหน่งให้เป็นวัตถุเมื่อกำหนดสถานที่ให้บริการa fooแต่วัตถุใหม่นี้จะถูกทิ้งทันทีดังนั้นค่าของfooจึงหายไป

คิดแบบนี้:

var a = 'quux';
new String(a).foo = 'bar'; // we never save this new object anywhere!
document.writeln(new String(a).foo); // a completly new object gets created

หน้าของ Mozilla Foundation "บทแนะนำใหม่สำหรับ JavaScript (บทช่วยสอน JS)" อธิบายออบเจ็กต์ JavaScript "เป็นคอลเลกชันของคู่ชื่อ - ค่าอย่างง่ายดังนั้นจึงคล้ายกับ ... " จากนั้นตามด้วยรายการพจนานุกรมแฮช ตารางแฮชและแผนที่แฮชจากภาษาโปรแกรมต่างๆ หน้าเดียวกันนี้อธิบายการอ้างอิงคุณสมบัติออบเจ็กต์เป็นการค้นหาตารางแฮช ดังนั้นวัตถุจึงเป็นเหมือนตาราง 'แฮช' สิ่งนี้ไม่ได้ทำให้ข้อมูลที่เป็นประโยชน์อื่น ๆ เป็นโมฆะ แต่การระบุลักษณะดั้งเดิมของ Chris Lloyd นั้นไม่ถูกต้อง
C Perkins

2

คุณถูกต้องมากหรือน้อยยกเว้นว่าสิ่งที่คุณอ้างถึงเป็น "แฮช" นั้นเป็นเพียงไวยากรณ์ชวเลขสำหรับวัตถุ

ในตัวอย่างแรกaและbทั้งคู่อ้างถึงวัตถุเดียวกัน ในตัวอย่างที่สองคุณเปลี่ยนaเพื่ออ้างถึงอย่างอื่น


เหตุใดจึงต้องมีสองมาตรฐานสำหรับ Object?
Chris Lloyd

ไม่ใช่สองมาตรฐาน ในตัวอย่างแรก a และ b ยังคงอ้างถึงออบเจ็กต์เดียวกันคุณแค่ปรับเปลี่ยนคุณสมบัติของออบเจ็กต์นั้น ในตัวอย่างที่สองคุณกำลังชี้ไปที่วัตถุอื่น
Kevin

1
ไม่ความแตกต่างคือในกรณีที่สองคุณกำลังจัดการกับสตริงไม่ใช่วัตถุ
Nosredna

1
เพื่อความชัดเจน: สิ่งนี้ไม่เกี่ยวข้องกับสตริงที่ส่งคืนสำเนาของตัวเอง เหตุผลที่ข้อมูลโค้ดทั้งสองแตกต่างกันคือในย่อหน้าที่สองของ Kevin (อธิบายเพิ่มเติมในคำตอบของ Squeegy)
Chuck

ไม่สำคัญว่าคุณจะมีสตริงหรืออ็อบเจกต์ในตัวแปร คุณกำหนดค่าใหม่ที่แตกต่างจากนั้นตัวแปรจะมีค่าใหม่ที่แตกต่างกัน

2

นี่คือคำตอบในเวอร์ชันของฉัน:

obj = {a:"hello",b:"goodbye"}
x = obj
x.a = "bonjour"

// now obj.a is equal to "bonjour"
// because x has the same reference in memory as obj
// but if I write:
x = {}
x.a = obj.a
x.b = obj.b
x.a = "bonjour"

// now x = {a:"bonjour", b:"goodbye"} and obj = {a:"hello", b:"goodbye"}
// because x points to another place in the memory

0

คุณกำลังตั้งค่าให้ชี้ไปที่อ็อบเจ็กต์สตริงใหม่ในขณะที่ b ชี้ไปที่อ็อบเจ็กต์สตริงเก่า


0

ในกรณีแรกคุณเปลี่ยนคุณสมบัติบางอย่างของออบเจ็กต์ที่มีอยู่ในตัวแปรในกรณีที่สองคุณกำหนดค่าใหม่ให้กับตัวแปร นั่นคือสิ่งที่แตกต่างกันโดยพื้นฐาน ตัวแปรaและbไม่ได้เชื่อมโยงกันอย่างน่าอัศจรรย์ด้วยการกำหนดครั้งแรก แต่ก็มีวัตถุเดียวกัน เช่นเดียวกันในตัวอย่างที่สองจนกว่าคุณจะกำหนดค่าใหม่ให้กับbตัวแปร


0

ความแตกต่างระหว่างประเภทธรรมดาและวัตถุ

ทุกสิ่งที่เป็นวัตถุ (เช่นอาร์เรย์หรือฟังก์ชัน) จะถูกส่งผ่านโดยการอ้างอิง

ทุกสิ่งที่เป็นประเภทธรรมดา (เช่นสตริงหรือตัวเลข) จะถูกคัดลอก

ฉันมีฟังก์ชัน copyArray อยู่เสมอดังนั้นฉันจึงมั่นใจได้ว่าฉันไม่ได้สร้างนามแฝงจำนวนมากในอาร์เรย์เดียวกัน


ความแตกต่างไม่สามารถสังเกตเห็นได้ในหลาย ๆ สถานการณ์ แต่จริงๆแล้ว Javascript ไม่ได้ส่งผ่านหรือกำหนดโดยการอ้างอิง มันคัดลอกค่าอ้างอิง
Juan Pablo Califano

พวกนี้ทำงานได้ดีอยู่แล้วในการอธิบายดังนั้นฉันจะวางลิงค์: stackoverflow.com/questions/40480/is-java-pass-by-reference (ฉันหมายถึง Java แต่ความหมายสำหรับการส่งผ่านและกำหนด ค่า / การอ้างอิงเหมือนกับใน Javascript)
Juan Pablo Califano

1
อันที่จริงคำตอบนี้ไม่ถูกต้องทุกอย่างจะถูกส่งผ่านด้วยค่าใน JavaScript จาก MDN "พารามิเตอร์ของการเรียกใช้ฟังก์ชันคืออาร์กิวเมนต์ของฟังก์ชันอาร์กิวเมนต์จะถูกส่งผ่านไปยังฟังก์ชันตามค่าหากฟังก์ชันเปลี่ยนค่าของอาร์กิวเมนต์การเปลี่ยนแปลงนี้จะไม่สะท้อนทั่วโลกหรือในฟังก์ชันการเรียกใช้อย่างไรก็ตามการอ้างอิงอ็อบเจ็กต์คือ ค่าด้วยและมีความพิเศษ: หากฟังก์ชันเปลี่ยนคุณสมบัติของอ็อบเจ็กต์ที่อ้างถึงการเปลี่ยนแปลงนั้นจะมองเห็นได้ภายนอกฟังก์ชัน " developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
bittersweetryan

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