'a' ['toUpperCase'] () ใน JavaScript ทำงานอย่างไรและทำไม?


209

JavaScript ทำให้ฉันประหลาดใจและนี่เป็นอีกตัวอย่างหนึ่ง ฉันเพิ่งเจอรหัสบางอย่างซึ่งฉันไม่เข้าใจในตอนแรก ดังนั้นฉันจึงดีบั๊กและมาพบสิ่งนี้:

alert('a'['toUpperCase']());  //alerts 'A'

ตอนนี้จะต้องชัดเจนหากtoUpperCase()ถูกกำหนดเป็นสมาชิกประเภทสตริง แต่มันไม่สมเหตุสมผลกับฉันในตอนแรก

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

  • ทำงานนี้เพราะtoUpperCaseเป็นสมาชิกของ 'a' หรือไม่ หรือมีอย่างอื่นเกิดขึ้นเบื้องหลังหรือไม่
  • รหัสผมอ่านมีฟังก์ชั่นดังต่อไปนี้:

    function callMethod(method) {
        return function (obj) {
            return obj[method](); //**how can I be sure method will always be a member of obj**
        }
    }
    
    var caps2 = map(['a', 'b', 'c'], callMethod('toUpperCase')); // ['A','B','C'] 
    // ignoring details of map() function which essentially calls methods on every 
    // element of the array and forms another array of result and returns it
    

    มันเป็นฟังก์ชั่นทั่วไปครับที่จะเรียกใด ๆวิธีการเกี่ยวกับการใด ๆวัตถุ แต่นั่นหมายความว่าวิธีที่ระบุจะเป็นสมาชิกโดยนัยของวัตถุที่ระบุหรือไม่

ฉันแน่ใจว่าฉันขาดความเข้าใจอย่างจริงจังเกี่ยวกับแนวคิดพื้นฐานของฟังก์ชัน JavaScript โปรดช่วยฉันเข้าใจสิ่งนี้


24
มีสองวิธีในการเข้าถึงคุณสมบัติของวัตถุ: สัญกรณ์ดอทและเครื่องหมายวงเล็บ ที่เกี่ยวข้องเล็กน้อยstackoverflow.com/a/11922384/218196 arr[5]คุณรู้อยู่แล้วว่าเกี่ยวกับเกี่ยวกับสัญกรณ์วงเล็บเพราะคุณมักจะใช้มันเมื่อมีการเข้าถึงองค์ประกอบมากมาย: arr.5ถ้าตัวเลขที่ชื่อระบุที่ถูกต้องคุณสามารถใช้จุดสัญกรณ์:
เฟลิกซ์คลิง

2
5['toString']()มันเป็นเช่นเดียวกับ
0x499602D2

1
นอกจากนี้ยังเกี่ยวข้องกับ: stackoverflow.com/q/4968406/218196
เฟลิกซ์คลิง

การอ่านที่เกี่ยวข้อง: 1) การสืบทอดและเชนต้นแบบ: developer.mozilla.org/en-US/docs/JavaScript/Guide/… 2) ชีวิตลับของ JavaScript แบบดั้งเดิม: javascriptweblog.wordpress.com/2010/09/27/ …
ธีระโรจน์

21
ตอนแรกอ่านฉันคิดว่าชื่อเรื่องเป็น "อย่างไรและทำไม JavaScript ทำงานอย่างไร" อ่า
ปัญหา

คำตอบ:


293

เพื่อทำลายมันลง

  • .toUpperCase() เป็นวิธีการของ String.prototype
  • 'a'เป็นค่าดั้งเดิม แต่ได้รับการแปลงเป็นการแสดงวัตถุ
  • เรามีสัญลักษณ์สองอย่างที่เป็นไปได้ในการเข้าถึงคุณสมบัติ / วิธีของวัตถุ, จุดและเครื่องหมายวงเล็บ

ดังนั้น

'a'['toUpperCase'];

คือการเข้าถึงผ่านทางสัญกรณ์วงเล็บในทรัพย์สินจากtoUpperCase String.prototypeเนื่องจากคุณสมบัตินี้อ้างอิงถึงวิธีหนึ่งเราจึงสามารถเรียกมันได้โดยการแนบ()

'a'['toUpperCase']();

นี่จะเป็นคำถามสัมภาษณ์ที่เฮฮา
FluffyBeing

78

foo.barและfoo['bar']เท่ากันดังนั้นโค้ดที่คุณโพสต์นั้นเหมือนกับ

alert('a'.toUpperCase())

เมื่อใช้งานfoo[bar](note tha ไม่มีเครื่องหมายคำพูด) คุณจะไม่ใช้ชื่อตามตัวอักษรbarแต่มีค่าใด ๆ ที่ตัวแปรbarมี ดังนั้นการใช้foo[]สัญกรณ์แทนfoo.อนุญาตให้คุณใช้ชื่อคุณสมบัติแบบไดนามิก


ลองดูที่callMethod:

ก่อนอื่นมันส่งคืนฟังก์ชันที่ใช้objเป็นอาร์กิวเมนต์ เมื่อมีการใช้งานฟังก์ชั่นนั้นมันจะเรียกหาmethodวัตถุนั้น ดังนั้นวิธีการที่กำหนดจะต้องมีอยู่ทั้งในobjตัวเองหรือที่อื่นในห่วงโซ่ต้นแบบของมัน

ในกรณีของtoUpperCaseวิธีการนั้นมาจากString.prototype.toUpperCase- มันค่อนข้างโง่ที่จะมีสำเนาของวิธีการแยกต่างหากสำหรับทุกสายเดียวที่มีอยู่


25

คุณสามารถเข้าถึงสมาชิกของวัตถุใด ๆ ด้วย.propertyNameสัญกรณ์หรือ["propertyName"]โน้ต นั่นคือคุณสมบัติของภาษาจาวา เพื่อให้แน่ใจว่าสมาชิกอยู่ในวัตถุเพียงตรวจสอบว่ามีการกำหนด:

function callMethod(method) {
    return function (obj) {
        if (typeof(obj[method]) == 'function') //in that case, check if it is a function
           return obj[method](); //and then invoke it
    }
}

17

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

โดยพื้นฐานแล้วคุณกำลังอ้างอิง / โทร (สังเกต ' () ') คุณสมบัติ 'toUpperCase' ของวัตถุ 'a' (ซึ่งเป็นประเภทสตริงในกรณีนี้)

นี่คือรหัสส่วนบนของหัวของฉัน:

function myObject(){
    this.msg = "hey there! ;-)";
    this.woop = function(){
        alert(this.msg); //do whatever with member data
    }
}

var obj = new myObject();
alert( obj.msg );
alert( obj['msg'] );
obj['woop']();

10

anyObject['anyPropertyName']เหมือนกับanyObject.anyPropertyNameเมื่อanyPropertyNameไม่มีอักขระที่มีปัญหา

ดูการทำงานกับวัตถุจาก MDN

toUpperCaseวิธีการที่แนบมากับชนิดสตริง เมื่อคุณเรียกใช้ฟังก์ชันบนค่าดั้งเดิมที่นี่ฟังก์ชัน'a'จะเลื่อนไปยังวัตถุโดยอัตโนมัติและนี่คือสตริง :

ในบริบทที่จะเรียกใช้เมธอดบนสตริงดั้งเดิมหรือเกิดการค้นหาคุณสมบัติ JavaScript จะตัดสตริงดั้งเดิมและเรียกเมธอดหรือทำการค้นหาคุณสมบัติ

String.prototype.toUpperCaseท่านสามารถเข้าดูฟังก์ชั่นที่มีอยู่โดยการเข้าสู่ระบบ


9

ดังนั้นใน Javascript, มีobjects objectsนั่นคือพวกเขามีลักษณะ{}นี้ คุณสมบัติของวัตถุสามารถตั้งค่าการใช้อย่างใดอย่างหนึ่งดังต่อไปนี้หรือa.greeting = 'hello'; a['greeting'] = 'hello';ทั้งสองวิธีทำงาน

การดึงข้อมูลกลับใช้งานได้เหมือนเดิม a.greeting(ไม่ทราบราคา) เป็น'hello', คือa['greeting'] 'hello'ข้อยกเว้น: ถ้าคุณสมบัติเป็นตัวเลขจะใช้วิธีการวงเล็บเท่านั้น วิธีการจุดไม่ได้

ดังนั้น'a'เป็นวัตถุที่มี'toUpperCase'คุณสมบัติซึ่งเป็นฟังก์ชั่น คุณสามารถเรียกฟังก์ชั่นและเรียกว่าต่อมาทางใดทางหนึ่ง: หรือ'a'.toUpperCase()'a'['toUpperCase']()

แต่ imo วิธีที่ดีกว่าในการเขียนฟังก์ชั่นแผนที่จะเป็น

var caps = ['a','b','c'].map( function(char) { return char.toUpperCase(); } )

ใครต้องการcallMethodฟังก์ชั่นแล้ว?


อธิบายได้อย่างสวยงาม :-)
Elisha Senoo

6

วัตถุ JavaScript ทุกชิ้นเป็นตารางแฮชเพื่อให้คุณสามารถเข้าถึงสมาชิกได้โดยการระบุรหัส ตัวอย่างเช่นถ้าตัวแปรเป็นสตริงก็ควรมีฟังก์ชัน toUpperCase ดังนั้นคุณสามารถเรียกใช้โดย

var str = "a"
str['toUpperCase'](). // you get the method by its name as a key and invoke it.

ดังนั้นโดย inline str คุณอาจมีด้านล่าง

"a"["toUpperCase"]()

6

toUpperCase เป็นวิธี javascript มาตรฐาน: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toUpperCase

เหตุผลมันทำงานเหมือน'a'['toUpperCase']()ก็คือฟังก์ชั่น toUpperCase 'a'เป็นทรัพย์สินของวัตถุสตริง คุณสามารถอ้างอิงคุณสมบัติบนวัตถุโดยใช้หรือobject[property] object.propertyไวยากรณ์ 'a''toUpperCase' หมายถึงคุณกำลังอ้างถึง 'toUppercase' อย่างเหมาะสมของวัตถุสตริง 'a' แล้วเรียกมัน ()


4

แต่นั่นหมายความว่าวิธีที่ระบุจะเป็นสมาชิกโดยนัยของวัตถุที่ระบุหรือไม่

ไม่ได้มีคนสามารถผ่านวัตถุนั้นได้

  1. ไม่มีชื่อคุณสมบัติtoUpperCase; หรือ
  2. มีคุณสมบัติชื่อtoUpperCaseที่ไม่ใช่ฟังก์ชั่น

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

ในกรณีที่สองข้อผิดพลาดจะถูกโยนเพราะอีกครั้งเราไม่สามารถเรียกฟังก์ชั่นที่ไม่ใช่ฟังก์ชั่น

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

ความจริงที่ว่าการobjโต้แย้งไม่ได้รับประกันว่าจะมีคุณสมบัติที่เหมาะสมไม่รบกวน JavaScript เลยดังนั้นการพูด มันใช้ทัศนคติ "รอดู" และไม่โยนข้อผิดพลาดจนกว่าปัญหาจริงจะเกิดขึ้นในเวลาทำงาน


4

เกือบทุกอย่างในจาวาสคริปต์สามารถใช้เป็นวัตถุได้ ในกรณีของคุณตัวอักษรทำหน้าที่เป็นวัตถุสตริงและtoUpperCaseสามารถเรียกใช้เป็นวิธีการ วงเล็บเป็นทางเลือกเพียงแค่การเข้าถึงคุณสมบัติของวัตถุและเนื่องจากtoUpperCaseเป็นวิธีการจึง simplebracket ()เป็นสิ่งจำเป็นต่อไปในการขึ้นรูป['toUpperCase']['toUpperCase']()

'a'['toUpperCase']() เทียบเท่ากับ 'a'.toUpperCase()

'a'['toUpperCase']() // returns A
'a'.toUpperCase() // returns A

3

สิ่งสำคัญที่ควรทราบคือที่นี่เนื่องจาก Javascript เป็นภาษาแบบไดนามิกวัตถุทุกชิ้นจึงเป็นเพียงแฮชแผนที่ที่มีชื่อเสียง ( มีข้อยกเว้นเล็กน้อย ) และทุกอย่างในวัตถุ Javascript สามารถเข้าถึงได้สองวิธี - เครื่องหมายวงเล็บและเครื่องหมายจุด

ฉันจะอ่านทั้งสองอย่างรวดเร็วเพื่อตอบคำถามแรกของคุณจากนั้นฉันจะไปที่ส่วนที่สอง

เครื่องหมายวงเล็บ

โหมดนี้คล้ายกับการเข้าถึง hashmaps และอาร์เรย์ในภาษาการเขียนโปรแกรมอื่น ๆ คุณสามารถเข้าถึงส่วนประกอบใด ๆ (ข้อมูล (รวมถึงวัตถุอื่น ๆ ) หรือฟังก์ชั่น) โดยใช้ไวยากรณ์นี้

นี่คือสิ่งที่คุณทำในตัวอย่างของคุณ คุณมี'a'ซึ่งเป็นสตริง (และไม่ใช่ตัวอักษรตามตัวอักษรเหมือนจะเป็นภาษาเช่น C ++)

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

alert('a'.toUpperCase());

ซึ่งอ่านได้มากขึ้น

ที่จริงแล้ววิธีที่ดีในการเข้าใจสิ่งนี้ดีขึ้นเล็กน้อยคือการใช้งาน Javascript ต่อไปนี้:

alert(alert)

สิ่งนี้เรียกalertโดยผ่านมันเป็นอ็อบเจกต์ของฟังก์ชั่นเช่นalertกันโดยไม่ต้องดำเนินการเตือนที่สอง สิ่งที่แสดง (ใน Chrome 26 อย่างน้อย) มีดังต่อไปนี้:

function alert() { [native code] } 

โทรศัพท์:

alert(alert())

undefinedแสดงให้เห็นสองกล่องข้อความที่มีการติดต่อกัน นี่เป็นเรื่องง่ายที่จะอธิบาย: ด้านในalert()จะถูกดำเนินการก่อนแสดงundefined(เพราะไม่มีพารามิเตอร์ใด ๆ ) และไม่ส่งคืนอะไร การแจ้งเตือนด้านนอกได้รับค่าตอบแทนของการแจ้งเตือนภายใน - ซึ่งไม่มีอะไรเลยและยังแสดงundefinedในกล่องข้อความ

ลองทุกกรณีใน jsFiddle!

เครื่องหมายดอท

นี่เป็นวิธีมาตรฐานที่ช่วยให้สมาชิกของวัตถุสามารถเข้าถึงได้โดยใช้ตัวดำเนินการ dot ( .) นี่คือลักษณะที่โค้ดของคุณจะมีลักษณะเป็นดอทโน้ต:

alert('a'.toUpperCase())

อ่านได้มากขึ้น ดังนั้นเมื่อไหร่ที่เราควรใช้เครื่องหมายจุดและเมื่อไหร่ที่เราควรใช้เครื่องหมายวงเล็บ?

การเปรียบเทียบ

ความแตกต่างที่สำคัญระหว่างทั้งสองวิธีคือความหมาย นอกจากนี้ยังมีรายละเอียดอื่น ๆ อีกด้วย แต่ฉันจะไปหาคนเหล่านั้นในไม่ช้า สิ่งที่สำคัญที่สุดคือสิ่งที่คุณต้องการทำจริงๆ - กฎง่ายๆคือคุณใช้เครื่องหมายจุดสำหรับเขตข้อมูลที่ได้รับการยอมรับและวิธีการที่วัตถุมีและเครื่องหมายวงเล็บสำหรับเมื่อคุณใช้วัตถุของคุณเป็นแผนที่แฮชจริง ๆ .

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

ในบางกรณีคุณต้องใช้เครื่องหมายวงเล็บแม้ว่าการใช้เครื่องหมายจุดจะสมเหตุสมผลมากขึ้น:

  • หากสมาชิกของวัตถุมีชื่อที่มีช่องว่างอย่างน้อยหนึ่งช่องหรืออักขระพิเศษอื่น ๆ คุณจะไม่สามารถใช้เครื่องหมายจุด: foo.some method()ไม่ทำงาน แต่foo["some method"]()ใช้;

  • หากคุณต้องการเข้าถึงสมาชิกของวัตถุแบบไดนามิกคุณยังติดอยู่โดยใช้เครื่องหมายวงเล็บ

ตัวอย่าง:

for(var i = 0; i < 10; ++i) {
   foo["method" + i](); 
 }

บรรทัดล่างคือคุณควรใช้ไวยากรณ์ของวงเล็บปีกกาเมื่อใช้วัตถุเป็นแผนที่กัญชา ( foods["burger"].eat()) และไวยากรณ์จุดเมื่อทำงานกับเขตข้อมูลและวิธีการ "ที่แท้จริง" ( enemy.kill()) ด้วย Javascript เป็นภาษาแบบไดนามิก, เส้นระหว่างเขตข้อมูล "จริง" และวิธีการของวัตถุและข้อมูล "อื่น ๆ " เก็บไว้ภายในสามารถเบลอสวย แต่ตราบใดที่คุณไม่ผสมพวกเขาในรูปแบบที่สับสนคุณก็น่าจะสบายดี


ตอนนี้คำถามที่เหลือของคุณ (ในที่สุด!: P)

ฉันจะแน่ใจได้อย่างไรว่าเมธอดจะเป็นสมาชิกของ obj เสมอ

คุณทำไม่ได้ ลองมัน. ลองโทรหาderpสาย คุณจะได้รับข้อผิดพลาดในบรรทัด:

Uncaught TypeError: Object a has no method 'derp' 

มันเป็นฟังก์ชั่นทั่วไปครับที่จะเรียกวิธีการใด ๆ บนวัตถุใด ๆ แต่นั่นหมายความว่าวิธีที่ระบุจะเป็นสมาชิกโดยนัยของวัตถุที่ระบุหรือไม่

ใช่ในกรณีของคุณมันจะต้องเป็น มิฉะนั้นคุณจะจบลงด้วยข้อผิดพลาดที่ฉันกล่าวถึงข้างต้น อย่างไรก็ตามคุณไม่จำเป็นต้องใช้return obj[method]();ในcallMethod()ฟังก์ชั่น คุณสามารถเพิ่มฟังก์ชั่นของคุณเองซึ่งจะถูกใช้โดยฟังก์ชั่นแผนที่ ต่อไปนี้เป็นวิธีการกำหนดค่าตายตัวที่เปลี่ยนตัวอักษรทั้งหมดให้เป็นตัวพิมพ์ใหญ่:

function makeCap()
{
    return function(obj) {
        return obj.toUpperCase();
    }
}

var caps2 = map(['a', 'b', 'c'], makeCap()); // ['A','B','C'] 
console.log(caps2)

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


หมายเหตุ: นี่คือรหัสของฟังก์ชั่นแผนที่ที่ใช้โดยรหัสในคำถามที่ว่าแหล่งที่มาที่นี่

function map(arr, iterator) {
  var narr = [];
  for (var i = 0; i < arr.length; i++) narr.push(iterator(arr[i], i));
  return narr;
}

2

หากคุณถามว่ามันใช้งานได้จริงอย่างไรซึ่งเป็นสิ่งที่ฉันอ่าน ตกลงนี่เป็นฟังก์ชันทางคณิตศาสตร์อย่างง่าย เพื่อให้เข้าใจได้คุณต้องดูตาราง ascii ซึ่งกำหนดค่าตัวเลขให้กับตัวอักษรแต่ละตัว หากต้องการปกปิดการแข่งขันให้ใช้คำแถลงเชิงตรรกะเพื่อปกปิดเช่นหาก (ChcrValue> 80 && charValue <106) // นี่คือชุดของตัวอักษรตัวพิมพ์เล็กดังนั้น charValue = charValue - 38; // ระยะห่างระหว่างชุดล่างและชุดด้านบน

มันง่ายมากฉันไม่ได้รำคาญที่จะค้นหาค่าที่ถูกต้อง แต่นี่คือการเปลี่ยนอักษรตัวพิมพ์เล็กทั้งหมดเป็นค่าตัวพิมพ์ใหญ่


สิ่งนี้เกี่ยวข้องกับคำถามอย่างไร
Quasdunk

2
@glh เขาถามว่า'a'['toUpperCase']()ทำงานอย่างไรและทำไม แต่ความเข้าใจผิดนั้นเข้าใจได้ถ้ามีคนไม่ได้อ่านคำถาม
LarsH

คำตอบที่ gr8 เนื่องจากไม่มีการtext-transform: uppercaseเพิ่มรูปแบบฉันอยู่ในช่วงท้ายด้วยวิธีการที่ JS จัดการการเปลี่ยนแปลงกรณีการล้างข้อสงสัยและเรียนรู้สิ่งหนึ่งหรือสอง ขอบคุณมาก.
dkjain
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.