ตัวหนอนจะทำอย่างไรเมื่อมันนำหน้าการแสดงออก


195
var attr = ~'input,textarea'.indexOf( target.tagName.toLowerCase() )
           ? 'value'
           : 'innerHTML'

ฉันเห็นมันในคำตอบและฉันไม่เคยเห็นมันมาก่อน

มันหมายความว่าอะไร?


คำตอบ:


268

~เป็นตัวดำเนินการระดับบิตที่พลิกบิตทั้งหมดในตัวถูกดำเนินการ

ตัวอย่างเช่นหากหมายเลขของคุณคือ1การแสดงเลขฐานสองของโฟลว์ IEEE 754 (วิธีที่จาวาสคริปต์จัดการกับตัวเลข) จะเป็น ...

0011 1111 1111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

ดังนั้น~แปลงตัวถูกดำเนินการเป็นจำนวนเต็ม 32 บิต (ตัวดำเนินการ bitwise ใน JavaScript ทำอย่างนั้น) ...

0000 0000 0000 0000 0000 0000 0000 0001

ถ้ามันเป็นจำนวนลบมันจะถูกเก็บไว้ในส่วนเติมเต็มของ 2: สลับบิตทั้งหมดและเพิ่ม 1

... จากนั้นจึงพลิกบิตทั้งหมด ...

1111 1111 1111 1111 1111 1111 1111 1110

แล้วการใช้งานของมันคืออะไร? เมื่อใดบ้างที่ผู้ใช้อาจใช้

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

นอกจากนี้ยังเป็น (โดยทั่วไป) ไม่ชัดเจนเคล็ดลับที่จะเปิดindexOf()'s พบค่าตอบแทนเข้าtruthy (ในขณะที่ทำให้ไม่พบเป็น falsy ) และผู้คนมักจะใช้มันสำหรับผลข้างเคียงของการตัดทอนหมายเลขที่ 32 บิต (และลดลงทศนิยมตำแหน่งของตนโดยการเพิ่มมัน ได้ผลเช่นเดียวกับMath.floor()ตัวเลขบวก)

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

นอกจากนี้ยังมีความเกี่ยวข้องน้อยในขณะนี้ว่ามี JavaScript และArray.prototype.includes() String.prototype.includes()สิ่งเหล่านี้คืนค่าบูลีน หากแพลตฟอร์มเป้าหมายของคุณสนับสนุนคุณควรใช้วิธีนี้ในการทดสอบการมีอยู่ของค่าในสตริงหรืออาร์เรย์


2
คำที่น่ารังเกียจใช่ไหม? ถ้าได้ผลฉันจะเรียกมันว่าสำนวนภาษา มีสำนวนมากมาย เมื่อคุณเรียนรู้พวกเขาจะไม่ชัดเจน ความเข้าใจในรายการนั้นไม่ชัดเจนใน Python หากคุณไม่รู้จักและสามารถใช้ลูป verbose เพิ่มเติมได้ แต่คุณจะไม่ขอให้โปรแกรมเมอร์ Python ไม่ใช้มัน ในทำนองเดียวกันvalue = value || defaultใน JavaScript เป็นสำนวนที่ใช้กันทั่วไปและถูกต้องตราบใดที่คุณรู้เมื่อคุณสามารถและไม่สามารถใช้มันได้
gman

3
@gman ฉันคิดว่ามันไม่สำคัญว่ามีคนใช้หรือไม่ ฉันคิดว่าการเปรียบเทียบความเข้าใจในรายการ (คุณลักษณะภาษา) กับสิ่งนี้ไม่ใช่สิ่งเดียวกัน ( วิธีที่ชาญฉลาดเพื่อหลีกเลี่ยงการพิมพ์อักขระพิเศษบางอย่าง) หากคุณคิดว่าคำที่น่ารังเกียจนั้นรุนแรงเกินไปโปรดอย่าลังเลที่จะแก้ไขคำตอบของฉัน
alex

2
v = t ? a : b;อาจจะเป็นตัวอย่างที่พบมากคือ ฉันพบว่าชัดเจนกว่าvar v; if (t} { v = a; } else { v = b; }ปกติเสียมากกว่า5+ บรรทัดและชัดเจนกว่าvar v = b; if (t) { v = a; }ปกติซึ่งจะเป็น 4+ บรรทัด แต่ฉันรู้ว่าผู้คนจำนวนมากไม่คุ้นเคยกับ? :ผู้ให้บริการที่ต้องการวิธีที่สองหรือสาม ฉันพบว่าคนแรกอ่านง่ายขึ้น ฉันเห็นด้วยกับหลักการทั่วไปทำให้รหัสชัดเจนไม่ใช้แฮ็ก ฉันคิดว่าฉันเห็น~v.indexOf('...')ชัดเจนมากเมื่อฉันเรียนรู้แล้ว
gman

9
เมื่อคุณทำงานใน บริษัท ขนาดใหญ่ที่มีนักพัฒนาหลายคนคุณต้องการให้เขียนโค้ดอย่างชัดเจน (ภาษาอังกฤษ) และมีเอกสารที่ดี จุดสำคัญของการเขียนโค้ดระดับสูงในภาษาที่มีการรวบรวมขยะคือการหลีกเลี่ยงการคิดเกี่ยวกับการทำงานของไบนารีและนักพัฒนาส่วนหน้าหลายคนไม่มีประสบการณ์การใช้ภาษาแอสเซมบลีใต้เข็มขัด
user2867288

3
ฉันจะไม่เรียกว่า~สำนวน มันเป็นส่วนหนึ่งของภาษาสเปคแต่มันก็ไม่ได้เป็นส่วนมากของภาษาในการใช้งานทั่วไป
worc

110

ใช้มันก่อน indexOf()แสดงออกอย่างมีประสิทธิภาพจะช่วยให้คุณได้ผลลัพธ์ที่เป็นจริง / เป็นเท็จแทนดัชนีตัวเลขที่ส่งคืนโดยตรง

หากค่าส่งคืนเป็น-1เช่นนั้น~-1เป็น0เพราะ-1สตริงของ 1 บิตทั้งหมด ค่าใด ๆ ที่มากกว่าหรือเท่ากับศูนย์จะให้ผลลัพธ์ที่ไม่เป็นศูนย์ ดังนั้น

if (~someString.indexOf(something)) {
}

จะทำให้ifรหัสทำงานเมื่อ "บางอย่าง" อยู่ใน "someString" ถ้าคุณลองใช้.indexOf()เป็นบูลีนโดยตรงสิ่งนั้นจะไม่ทำงานเพราะบางครั้งมันกลับเป็นศูนย์ (เมื่อ "บางสิ่ง" อยู่ที่จุดเริ่มต้นของสตริง)

แน่นอนว่ามันก็ใช้ได้เช่นกัน:

if (someString.indexOf(something) >= 0) {
}

และมันลึกลับน้อยกว่ามาก

บางครั้งคุณจะเห็นสิ่งนี้ด้วย:

var i = ~~something;

การใช้ตัว~ดำเนินการสองครั้งเช่นนี้เป็นวิธีที่รวดเร็วในการแปลงสตริงเป็นจำนวนเต็ม 32 บิต อย่างแรก~คือการแปลงและครั้งที่สองก็~พลิกบิตกลับ แน่นอนว่าถ้าโอเปอเรเตอร์ถูกนำไปใช้กับบางสิ่งที่ไม่สามารถแปลงเป็นตัวเลขได้คุณจะได้NaNผลลัพธ์ ( แก้ไข - อันที่จริงมันเป็นตัวที่สอง~ที่ใช้ก่อน แต่คุณจะได้แนวคิด)


2
สำหรับผู้ที่ไม่ต้องการที่จะลบล้างบิตโดยบิตเมื่อดำเนินการในจำนวนเต็มมีค่าเท่ากับ~ -(x + 1)
FabrícioMatté

ดูเหมือนว่าคุณรู้ไหมค่าตัวเลขที่เป็นลบควรส่งกลับค่าบูลีนเชิงลบเช่นกันในตอนแรก แต่อีกหนึ่งใน JS 'ล้มเหลวฉันเดา?
wwaawaw

7
@adlwalrus เป็นอย่างดีใน0เรื่องของความเป็นอยู่falseและไม่เป็นศูนย์ซึ่งtrueย้อนกลับไปได้อย่างน้อยก็ถึง C ในยุค 70 และอาจเป็นภาษาโปรแกรมอื่น ๆ มันอาจเกิดจากวิธีการทำงานของฮาร์ดแวร์ CPU จำนวนมากตั้งค่าบิตศูนย์หลังจากการดำเนินการและมีคำสั่งสาขาที่สอดคล้องกันเพื่อทดสอบ
Pointy

4
วิธีที่เร็วกว่าในการแปลงเป็น 32 บิตจะเป็น| 0ในกรณีนี้การดำเนินการเดียวเท่านั้น
alex

@alex แน่นอนถึงแม้ว่าเราจะไม่สามารถไว้วางใจรันไทม์ที่จะไม่ตีความแอปพลิเคชันอย่างง่ายใน~~ลักษณะเดียวกัน
Pointy

30

~เป็นBitwise ไม่ Operator , เป็นประมาณเช่นเดียวกับ~x -(x+1)ง่ายต่อการเข้าใจเรียงลำดับจาก ดังนั้น:

~2;    // -(2+1) ==> -3

-(x+1)พิจารณา สามารถดำเนินการดังกล่าวในการผลิต-10

กล่าวอีกนัยหนึ่งการ~ใช้กับช่วงของค่าตัวเลขจะสร้างค่า falsy (coerce to falsefrom 0) เฉพาะ-1ค่าอินพุตเท่านั้นมิฉะนั้นค่าจริงอื่น ๆ

อย่างที่เรารู้ -1เป็นเรื่องปกติที่เรียกว่าค่าแมวมอง มันจะใช้สำหรับฟังก์ชั่นมากมายที่กลับ>= 0ค่าสำหรับความสำเร็จและ-1สำหรับความล้มเหลวในภาษา C ซึ่งมีกฎการคืนค่าเหมือนกันindexOf()ใน JavaScript

มันเป็นเรื่องธรรมดาที่จะตรวจสอบว่ามี / ไม่มีซับสตริงในสตริงอื่นด้วยวิธีนี้

var a = "Hello Baby";

if (a.indexOf("Ba") >= 0) {
    // found it
}
if (a.indexOf("Ba") != -1) { 
    // found it
}

if (a.indexOf("aB") < 0) { 
    // not found
}
if (a.indexOf( "aB" ) == -1) { 
    // not found
}

อย่างไรก็ตามมันจะง่ายกว่าที่จะทำผ่าน~ดังต่อไปนี้

var a = "Hello Baby";

~a.indexOf("Ba");         // -7   -> truthy
if (~a.indexOf("Ba")) {   // true
    // found it
}

~a.indexOf("aB");         // 0    -> falsy
!~a.indexOf("aB");        // true
if (!~a.indexOf( "aB" )) {  // true
    // not found
}

คุณไม่รู้ JS: ประเภทและไวยากรณ์โดย Kyle Simpson


1
มันแน่นอนง่ายต่อการเข้าใจที่มูลค่าแม้ถ้าเป็นคนที่ไม่เข้าใจพื้นหลังที่ทำให้การทำงานของมัน ฉันจะดูครั้งที่สอง-(x+1)ถ้าฉันเห็นมันในคำสั่ง if ตัวหนอนบอกฉันว่าสิ่งที่ทำเพื่อชดเชยธรรมชาติที่ใช้ 0 ของ Javascript นอกจากนี้วงเล็บที่น้อยกว่าจะดีกว่าสำหรับการอ่าน
ปกติ Joe

ในบล็อกรหัสตรวจสอบเริ่มต้นของคุณคุณสามารถพิมพ์ได้น้อยลงโดยใช้if (a.indexOf("Ba") > -1) {// found} //trueสิ่งที่แม้ว่าบิตที่ยาวกว่าตัวอย่างตัวหนอนจะน้อยกว่าตัวอย่างสองตัวอย่างที่คุณให้และสำหรับโปรแกรมเมอร์ใหม่ที่var opinion = !~-1 ? 'more' : 'less'เข้าใจได้
HalfMens

25

~indexOf(item) เกิดขึ้นบ่อยครั้งและคำตอบที่นี่ยอดเยี่ยม แต่บางทีบางคนก็ต้องรู้วิธีใช้และ "ข้าม" ทฤษฎี:

   if (~list.indexOf(item)) {
     // item in list
   } else {
     // item *not* in list
   }

1
ฉันเห็นด้วย คู่มือสไตล์ Airbnb JavaScript ไม่อนุญาต++และ--เพราะพวกเขา "กระตุ้นให้เกิดความยุ่งยากมากเกินไป" และยังมี~ชีวิตรอด (ซ่อนตัวอยู่ในเงามืด) github.com/airbnb/javascript/issues/540
Shanimal

@Sanimal ทางเลือกคือlist.indexOf(item) >= 0หรือ... > -1เนื่องจาก javascript มีพื้นฐานเป็นศูนย์และไม่ได้เลือกที่จะแก้ไขปัญหานี้ตั้งแต่เริ่มแรก เพิ่มเติมเพียงแค่ความเห็น (เช่นเดียวกับ Airbnb) ทุกคนที่ทำสิ่งที่มีความหมายในจาวาสคริปต์รู้++และในขณะที่--เป็นเรื่องธรรมดาน้อยกว่าความหมายที่สามารถอนุมาน
ปกติ Joe

@RegularJoe ฉันเห็นด้วยมากที่สุด ผมเองยังไม่จำเป็นต้องใช้++และ--ในขณะที่เพราะวิธีดั้งเดิมเช่นmap, forEachฯลฯ จุดของฉันคือเพิ่มเติมเกี่ยวกับเหตุผลที่พวกเขาไม่ได้พิจารณา~เรื่องยุ่งยากมากเกินไปเมื่อสิ่งที่มาตรฐานที่ใช้รวมถึงการเพิ่มขึ้นและลดลงผู้ประกอบการ เพื่อห้ามบางสิ่งบางอย่างดังนั้น CIS101 จึงไม่สมเหตุสมผล
Shanimal

12

สำหรับการพิจารณาการใช้เคล็ดลับตัวหนอนเพื่อสร้างtruthyค่าจากindexOfผลก็เป็นที่ชัดเจนมากขึ้นและมีความวิเศษน้อยแทนที่จะใช้วิธีการในการincludesString

'hello world'.includes('hello') //=> true
'hello world'.includes('kittens') //=> false

โปรดทราบว่านี่เป็นวิธีมาตรฐานใหม่ของ ES 2015 ดังนั้นจึงไม่สามารถใช้กับเบราว์เซอร์รุ่นเก่าได้ ในกรณีที่สำคัญให้พิจารณาใช้String.prototype.includes polyfill polyfill

คุณลักษณะนี้มีให้สำหรับอาร์เรย์ที่ใช้ไวยากรณ์เดียวกัน :

['apples', 'oranges', 'cherries'].includes('apples') //=> true
['apples', 'oranges', 'cherries'].includes('unicorns') //=> false

นี่คือArray.prototype.includes polyfillหากคุณต้องการการสนับสนุนเบราว์เซอร์รุ่นเก่า


2
หลีกเลี่ยงการใช้ include () ไม่รองรับ IE เวอร์ชันใด ๆ (ไม่ใช่แค่เบราว์เซอร์รุ่นเก่า) ในขณะเขียน: developer.mozilla.org/en/docs/Web/JavaScript/Reference/
......

8
หรือเพียงแค่ใช้คอมไพเลอร์เพื่อให้คุณสามารถเขียนรหัสที่ชัดเจนยิ่งขึ้นแทนที่จะเขียนภาษาตามล่ามตัวแปลร่วมที่เลวร้ายที่สุด JS
Kyle Baker

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