คำอธิบายการกำหนดตัวแปร JavaScript หรือ (||)


351

รับตัวอย่าง JavaScript นี้ ...

var a;
var b = null;
var c = undefined;
var d = 4;
var e = 'five';

var f = a || b || c || d || e;

alert(f); // 4

ใครช่วยอธิบายหน่อยได้ไหมว่าเทคนิคนี้เรียกว่าอะไร (เดาที่ดีที่สุดของฉันอยู่ในชื่อของคำถามนี้!) แล้วมันทำงานยังไงกันแน่?

ความเข้าใจของฉันคือตัวแปรfจะถูกกำหนดค่าที่ใกล้ที่สุด (จากซ้ายไปขวา) ของตัวแปรแรกที่มีค่าที่ไม่เป็นโมฆะหรือไม่ได้กำหนด แต่ฉันไม่สามารถหาวัสดุอ้างอิงเกี่ยวกับเทคนิคนี้ได้มากและมี เห็นมันใช้มาก

นอกจากนี้เทคนิคนี้ใช้เฉพาะกับ JavaScript หรือไม่ ฉันรู้ว่าการทำสิ่งที่คล้ายกันใน PHP จะส่งผลให้fมีค่าบูลีนที่แท้จริงมากกว่าค่าของdตัวเอง


4
คำถามเดิม แต่เกี่ยวกับ PHP, $f=$a or $f=$b or $f=$c; // etcมีการสร้างคุณสามารถใช้: PHP มีทั้ง||โอเปอเรเตอร์และorโอเปอเรเตอร์ซึ่งทำงานเหมือนกัน อย่างไรก็ตามorจะมีการประเมินหลังการมอบหมายในขณะที่||ประเมินก่อน สิ่งนี้ยังทำให้คุณมีสไตล์ที่ไม่เหมือนใครของ$a=getSomething() or die('oops');
Manngo

1
ใน PHP 5.3 คุณสามารถออกจากส่วนตรงกลางของผู้ประกอบการที่ประกอบไปด้วยดังนั้น basing จากนั้น ... นอกจากนี้คุณยังสามารถตัดที่บิตสั้นลงในบางอย่างเช่นนี้ $f = $a ?: $b ?: $c;
Rei

1
ตั้งแต่ PHP 7 คุณสามารถใช้??สิ่งนี้ได้ $a = $b ?? 'default'
Spencer Ruskin

@SpencerRuskin ดังนั้น$aจะได้รับการกำหนดค่า$bถ้า$bเป็นจริงอื่น ๆ'default'?
เด็ก

ถูกตัอง. ดูส่วนโอเปอเรเตอร์การรวมตัวที่ว่างเปล่า
Spencer Ruskin

คำตอบ:


212

ดูการประเมินการลัดวงจรสำหรับคำอธิบาย มันเป็นวิธีการทั่วไปในการใช้ตัวดำเนินการเหล่านี้ มันไม่ซ้ำกับ JavaScript


57
เพียงแค่คำนึงถึง 'gotcha' ซึ่งเป็นคนสุดท้ายที่จะได้รับมอบหมายเสมอแม้ว่าพวกเขาจะไม่ได้กำหนดทั้งหมดเป็นโมฆะหรือเท็จ การตั้งค่าบางอย่างที่คุณรู้ว่าไม่ใช่ของปลอมเป็นโมฆะหรือไม่ได้กำหนดไว้ที่ส่วนท้ายของโซ่เป็นวิธีที่ดีในการส่งสัญญาณไม่พบสิ่งใด
Erik Reppen

ฉันได้เห็นเทคนิคนี้มาหลายปีแล้ว แต่สิ่งที่ทำให้ฉันประทับใจเมื่อฉันต้องการใช้มันคือผลลัพธ์ของการแสดงออกไม่ได้ถูกส่งไปเป็นบูลีน if( true == f )คุณไม่สามารถทำในภายหลัง หากจำนวนเต็มถูกเก็บไว้ใน f แล้วการทดสอบนี้จะกลับเท็จ

9
ที่จริงคุณสามารถทำได้if(true == f)ซึ่งเหมือนกับif(f): การทดสอบจะผ่าน หากคุณต้องการทดสอบประเภทของfให้ใช้การเปรียบเทียบที่เข้มงวด: if(true === f)ซึ่งจะล้มเหลวแน่นอน
Alsciende

5
ใช่การประเมินการลัดวงจรเป็นเรื่องปกติ แต่ความแตกต่างอยู่ที่นี่ในวิธีที่ JavaScript ส่งคืนค่าสุดท้ายที่หยุดการทำงาน @ คำตอบของ Anurag ช่วยอธิบายเรื่องนี้ได้ดีกว่ามาก
Ben.12

ไม่แน่ใจว่าเป็นคำอธิบายที่ดีที่สุดสำหรับผู้เริ่มต้นหรือไม่ ฉันจะแนะนำ: javascript.info/logical-operators
csguy

198

นี้ทำเพื่อกำหนดค่าเริ่มต้นในกรณีนี้ค่าของyถ้าxตัวแปรfalsy

ตัวดำเนินการบูลีนใน JavaScript สามารถส่งคืนตัวถูกดำเนินการและไม่ได้ผลบูลีนเช่นเดียวกับในภาษาอื่น ๆ

ตัวดำเนินการ Logical OR ( ||) ส่งคืนค่าของตัวถูกดำเนินการตัวที่สองถ้าตัวแรกเป็นค่าเท็จมิฉะนั้นค่าของตัวถูกดำเนินการแรกจะถูกส่งกลับ

ตัวอย่างเช่น:

"foo" || "bar"; // returns "foo"
false || "bar"; // returns "bar"

Falsyค่าเป็นผู้ที่บีบบังคับไปfalseเมื่อนำมาใช้ในบริบทแบบบูลและพวกเขาจะ0, null, undefined, สตริงว่างและแน่นอนNaNfalse


1
+1 มีผู้ให้บริการรายอื่นเช่นนี้หรือไม่? หรือเป็น||เอกสิทธิ์
OscarRyz

9
@Support (@Oscar): &&โอเปอเรเตอร์Logical มีพฤติกรรมที่คล้ายกันซึ่งจะส่งคืนค่าของตัวถูกดำเนินการแรกหากตัวเองเป็นเท็จและส่งคืนค่าตัวถูกดำเนินการที่สองเฉพาะในกรณีที่ตัวแรกเป็นจริงเช่น("foo" && "bar") == "bar"และ(0 && "bar") == 0
CMS

12
ความจริงแล้วคำเท็จทางเทคนิค
ChaosPandion

8
ดังนั้นเราจึงเรียนรู้เกี่ยวกับ ||, && "Falsy" และ "Truly" ในโพสต์นี้ คำตอบที่ดีที่สุดด้วยของขวัญ "ซ่อน"
อเล็กซ์

4
@Alex NB: "Truthy" (! "Truly")
Bumpy

183

Javacript ใช้การประเมินผลการลัดวงจรสำหรับผู้ประกอบการเชิงตรรกะและ|| อย่างไรก็ตามมันแตกต่างจากภาษาอื่น ๆ โดยที่มันจะส่งคืนผลลัพธ์ของค่าสุดท้ายที่หยุดการดำเนินการแทนค่าหรือค่า&&truefalse

ค่าต่อไปนี้ถือว่าเป็นเท็จใน JavaScript

  • เท็จ
  • โมฆะ
  • "" (สตริงว่าง)
  • 0
  • น่าน
  • ไม่ได้กำหนด

การเพิกเฉยต่อกฎสำคัญของโอเปอเรเตอร์และทำให้สิ่งต่าง ๆ เรียบง่ายตัวอย่างต่อไปนี้แสดงค่าที่หยุดการประเมินและได้รับผลลัพธ์

false || null || "" || 0 || NaN || "Hello" || undefined // "Hello"

ค่า 5 อันดับแรกเกินNaNจริงดังนั้นค่าทั้งหมดจะถูกประเมินจากซ้ายไปขวาจนกระทั่งตรงตามค่าจริงแรก"Hello"ซึ่งทำให้การแสดงออกทั้งหมดเป็นจริงดังนั้นสิ่งใดที่เกินจะไม่ถูกประเมินและ"Hello"ได้รับผลตอบแทนจากการแสดงออก . ในทำนองเดียวกันในกรณีนี้:

1 && [] && {} && true && "World" && null && 2010 // null

5 ค่าแรกนั้นเป็นความจริงทั้งหมดและได้รับการประเมินจนกว่าจะตรงตามค่า falsy แรก ( null) ซึ่งทำให้นิพจน์เป็นเท็จดังนั้น2010จะไม่ถูกประเมินอีกต่อไปและnullได้รับผลตอบแทนจากการแสดงออก

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

var a = false;
var b = a || "Hello";

คุณสามารถเรียกตัวอย่างด้านล่างนี้เป็นการใช้ประโยชน์จากคุณลักษณะนี้และฉันเชื่อว่ามันทำให้โค้ดอ่านยากขึ้น

var messages = 0;
var newMessagesText = "You have " + messages + " messages.";
var noNewMessagesText = "Sorry, you have no new messages.";
alert((messages && newMessagesText) || noNewMessagesText);

ภายในการแจ้งเตือนเราตรวจสอบว่าmessagesเป็น falsy และถ้าใช่แล้วประเมินและผลตอบแทนอย่างอื่นในการประเมินและผลตอบแทนnoNewMessagesText newMessagesTextเนื่องจากเป็น falsy ในตัวอย่างนี้เราหยุดที่ noNewMessagesText "Sorry, you have no new messages."และการแจ้งเตือน


36
นี่คือคำตอบที่ดีที่สุดในความคิดของฉันเพราะคำอธิบายต่อไปนี้:However, it's different to other languages in that it returns the result of the last value that halted the execution, instead of a true, or false value.
mastazi

1
@ mastazi ใช่มันควรเป็นแบบอักษรตัวหนา IMHO
noober

6
ควรเป็นคำตอบมันจะแสดงค่าที่ถูกเลือกมากกว่ากรณีทดสอบ
Dadan

เห็นด้วยนี่เป็นคำตอบที่ฉันชอบเพราะมันเน้นความกังวลเกี่ยวกับการกำหนดตัวแปรจาวาสคริปต์โดยเฉพาะ นอกจากนี้หากคุณเลือกที่จะใช้ ternary เป็นหนึ่งในตัวแปรที่ตามมาเพื่อทดสอบสำหรับการมอบหมาย (หลังจากผู้ดำเนินการ) คุณต้องล้อมรอบไตรภาคในวงเล็บเพื่อให้การประเมินการมอบหมายให้ทำงานได้อย่างถูกต้อง
Joey T

49

ไม่ได้พิมพ์ตัวแปร Javascript ดังนั้น f สามารถกำหนดค่าจำนวนเต็มแม้ว่าจะถูกกำหนดผ่านตัวดำเนินการบูลีน

ฉถูกกำหนดค่าที่ใกล้ที่สุดที่เป็นไม่ได้เทียบเท่ากับการเท็จ ดังนั้น 0, false, null, undefined, ถูกส่งผ่านทั้งหมด:

alert(null || undefined || false || '' || 0 || 4 || 'bar'); // alerts '4'

13
อย่าลืม''เท็จในกรณีนี้ด้วย
Brigand

โหวตขึ้นโหวตให้เห็นสิ่งf is assigned the NEAREST valueที่เป็นประเด็นสำคัญที่นี่
steviejay

3
"ใกล้ที่สุด" ไม่เป็นความจริงแม้ว่ามันจะมีรูปลักษณ์นั้น บูลีน||ผู้ประกอบการเป็นผู้ประกอบการบูลีนมีสองตัวถูกดำเนินการ: ด้านซ้ายและด้านขวา ถ้าด้านซ้ายของ||เป็นtruthyที่แก้ไขการดำเนินงานไปทางด้านซ้ายและด้านขวาจะถูกละเว้น หากด้านซ้ายผิดเพี้ยนก็จะหายไปทางด้านขวา ดังนั้นnull || undefined || 4 || 0จริงหายไปundefined || 4 || 0ซึ่งมีมติที่จะแก้ไข4 || 0 4
devios1

29

ไม่มีเวทย์มนตร์ใด ๆ นิพจน์บูลีนที่ชอบa || b || c || dประเมินอย่างเกียจคร้าน Interpeter ค้นหาค่าของaมันไม่ได้กำหนดดังนั้นจึงเป็นเท็จดังนั้นจึงดำเนินการต่อจากนั้นจะเห็นว่าbเป็นโมฆะซึ่งยังคงให้ผลลัพธ์ที่เป็นเท็จดังนั้นจึงย้ายไปแล้วก็เห็นc- เรื่องเดียวกัน ในที่สุดมันก็เห็นdและพูดว่า 'ฮะมันไม่ใช่โมฆะดังนั้นฉันจึงได้ผลลัพธ์' และมันกำหนดให้ตัวแปรสุดท้าย

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


6
ในภาษา C # หนึ่งคงที่สามารถใช้ ?? ตัวดำเนินการá la: object f = a ?? ข ค ?? ง ?? E;
herzmeister

2
herzmeister - ขอบคุณ! ฉันไม่รู้เหมือนกัน ?? ผู้ประกอบการสามารถถูกผูกมัดใน C # และใช้ในเทคนิคการประเมินผลขี้เกียจ
Marek

3
ดังที่กล่าวไว้ในที่อื่นว่าครั้งสุดท้ายdจะได้รับมอบหมายไม่ว่าจะเป็นโมฆะ / ไม่ได้กำหนดหรือไม่
BlackVegetable

การแก้ไขเล็กน้อยหนึ่งครั้ง: ||ผู้ปฏิบัติงานจะแก้ไขตัวถูกดำเนินการด้านขวาทั้งหมดเสมอเมื่อด้านซ้ายเป็นเท็จ การเป็นผู้ประกอบการบูลีนจะเห็นเพียงสองอินพุต: ด้านซ้ายและด้านขวา เครื่องมือแยกวิเคราะห์ไม่เห็นพวกเขาเป็นชุดคำดังนั้นจึงไม่หยุดจริง ๆ เมื่อพบค่าความจริงแรกยกเว้นว่าค่านั้นเป็นตัวถูกดำเนินการทางซ้ายของอีก||ด้วย
devios1

8

คำถามนี้ได้รับคำตอบที่ดีหลายประการแล้ว

โดยสรุปเทคนิคนี้ใช้ประโยชน์จากคุณสมบัติของวิธีการรวบรวมภาษา นั่นคือจาวาสคริปต์ "การลัดวงจร" การประเมินผลตัวดำเนินการบูลีนและจะส่งคืนค่าที่เกี่ยวข้องกับค่าตัวแปรที่ไม่ใช่เท็จตัวแรกหรือตัวแปรสุดท้ายที่มี ดูคำอธิบายของ Anurag เกี่ยวกับค่าเหล่านั้นที่จะประเมินเป็นเท็จ

การใช้เทคนิคนี้ไม่ใช่วิธีปฏิบัติที่ดีด้วยเหตุผลหลายประการ อย่างไรก็ตาม

  1. ความสามารถในการอ่านรหัส: นี่คือการใช้ตัวดำเนินการบูลีนและหากพฤติกรรมของวิธีการคอมไพล์นี้ไม่เป็นที่เข้าใจแล้วผลที่คาดหวังจะเป็นค่าบูลีน
  2. ความเสถียร: นี่คือการใช้คุณสมบัติของวิธีการรวบรวมภาษาที่ไม่สอดคล้องกันในหลายภาษาและเนื่องจากสิ่งนี้เป็นสิ่งที่อาจเป็นเป้าหมายสำหรับการเปลี่ยนแปลงในอนาคต
  3. คุณสมบัติที่เป็นเอกสาร: มีทางเลือกที่มีอยู่ที่ตรงกับความต้องการนี้และสอดคล้องกันในหลายภาษา นี่จะเป็นผู้ประกอบการที่สาม:

    () ค่า 1: ค่า 2

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

var a;
var b = null;
var c = undefined;
var d = 4;
var e = 'five';

var f =  ( a ) ? a : 
                ( b ) ? b :
                       ( c ) ? c :
                              ( d ) ? d :
                                      e;

alert(f); // 4

potentially be targeted for change in the future.ใช่ แต่ฉันไม่ได้ใช้กับ javascript
Dadan

มาที่นี่และเห็นคำตอบข้างต้นทั้งหมดและคิดกับตัวเองว่ามีอะไรบางอย่างที่ดูไม่ดีเกี่ยวกับงานที่ได้รับมอบหมาย ฉันเพิ่งอ่าน Clean Code ของ Robert C Martin และการมอบหมายประเภทนี้ฝ่าฝืนกฎ "ไม่มีผลข้างเคียง" อย่างแน่นอน ... ในขณะที่ผู้เขียนเองระบุว่าหนังสือของเขาเป็นเพียงหนึ่งในเทคนิคต่าง ๆ ในการสร้างรหัสที่ดีฉัน ยังคงประหลาดใจที่ไม่มีใครคัดค้านการมอบหมายประเภทนี้ +1
Albert Rothman

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

1
คุณคิดว่าความโหดร้ายนั้นชัดเจนกว่าจริงa || b || c || d || eหรือ
devios1

1
@AlbertRothman ฉันไม่เห็นผลข้างเคียงใด ๆ ไม่มีการเปลี่ยนแปลงใด ๆ มันเป็นเพียงการจดชวเลขสำหรับการรวมกันเป็นโมฆะซึ่งเป็นคุณสมบัติที่ใช้กันทั่วไปในหลายภาษา
devios1

7

ส่งคืนค่าที่แท้จริงก่อน

หากทั้งหมดเป็นเท็จคืนค่าเท็จล่าสุด

ตัวอย่าง:-

  null || undefined || false || 0 || 'apple'  // Return apple

4

มันตั้งค่าตัวแปรใหม่ ( z) ให้เป็นค่าxถ้าเป็น "ความจริง" (ไม่ใช่ศูนย์วัตถุที่ถูกต้อง / อาร์เรย์ / ฟังก์ชั่น / สิ่งที่มันเป็น) หรือyอย่างอื่น เป็นวิธีที่ใช้กันโดยทั่วไปในการให้ค่าเริ่มต้นในกรณีที่xไม่มี

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

function doSomething(data, callback) {
    callback = callback || function() {};
    // do stuff with data
    callback(); // callback will always exist
}

1

หมายความว่าหากxตั้งค่าไว้ค่าzจะเป็นxอย่างอื่นหากyตั้งไว้ค่าจะถูกตั้งเป็นzค่าของ

มันเหมือนกับ

if(x)
  z = x;
else
  z = y;

เป็นไปได้เนื่องจากตัวดำเนินการเชิงตรรกะใน JavaScript ไม่ส่งคืนค่าบูลีน แต่ค่าขององค์ประกอบสุดท้ายที่จำเป็นในการดำเนินการให้เสร็จสมบูรณ์ (ในประโยค OR จะเป็นค่าที่ไม่ใช่ค่าแรกในประโยค AND และจะเป็นค่าสุดท้าย ) หากการดำเนินการล้มเหลวfalseจะถูกส่งคืน


5
นี่มันผิด! ถ้า (x) {z = x; } else {z = y;} หากค่าแรกเป็นเท็จค่าที่สองจะถูกกำหนดเสมอไม่ขึ้นอยู่กับค่าจริง
evilpie

ยกเว้นว่าผมคิดว่ามันเป็นเพียงแค่กำหนด Y ถึง z ถ้า x เท็จ นั่นเป็นวิธีที่เหมาะกับฉันใน FF แน่นอนว่าอาจต้องขึ้นอยู่กับการใช้งานด้วย
tvanfosson

7
ส่วนสุดท้ายเกี่ยวกับการคืนค่าเท็จไม่เป็นความจริง หากค่าแรกเป็นเท็จ||ผู้ประกอบการเพียงแค่ส่งกลับค่าที่สองโดยไม่คำนึงว่ามันเป็นความจริงหรือไม่
Matthew Crumley

-1 ข้อมูลโค้ดเทียบเท่าของคุณถูกต้อง แต่จุดสำคัญคือการที่zได้รับการกำหนดค่าของxถ้าค่าที่truthy มิฉะนั้นจะได้รับการตั้งค่าyเป็น ซึ่งหมายความว่าถ้าxมีการตั้งค่าตัวอย่างเช่น0หรือสตริงที่ว่างเปล่า""นี้ไม่ได้ทำในสิ่งที่คุณพูดว่าตั้งแต่ค่าเหล่านี้จะfalsy
Daniel Cassidy

1

มันเรียกว่าผู้ประกอบการลัดวงจร

การประเมินการลัดวงจรกล่าวว่าอาร์กิวเมนต์ที่สองจะถูกดำเนินการหรือประเมินผลก็ต่อเมื่ออาร์กิวเมนต์แรกไม่พอเพียงเพื่อกำหนดค่าของนิพจน์ เมื่ออาร์กิวเมนต์แรกของฟังก์ชัน OR (||) ประเมินค่าเป็นจริงค่าโดยรวมต้องเป็นจริง

นอกจากนี้ยังสามารถใช้เพื่อตั้งค่าเริ่มต้นสำหรับอาร์กิวเมนต์ของฟังก์ชันได้

function theSameOldFoo(name){ 
  name = name || 'Bar' ;
  console.log("My best friend's name is " + name);
}
theSameOldFoo();  // My best friend's name is Bar
theSameOldFoo('Bhaskar');  // My best friend's name is Bhaskar`

0

มันจะประเมินค่า X และถ้า X ไม่เป็นโมฆะสตริงว่างหรือ 0 (ตรรกะเท็จ) จากนั้นมันจะกำหนดให้กับ z ถ้า X เป็นโมฆะสตริงว่างหรือ 0 (ตรรกะเท็จ) จากนั้นมันจะกำหนด y เป็น z

var x = '';
var y = 'bob';
var z = x || y;
alert(z);

จะส่งออก 'บ๊อบ';


คุณควรอธิบายความหมายของคำว่า "empty" บีบบังคับสตริงที่ว่างเปล่าแต่อาร์เรย์ว่างเปล่าหรือวัตถุที่จะบีบบังคับfalse true
Daniel Cassidy

@Daniel "null, empty หรือ 0" - null จะมีผลกับอาร์เรย์และวัตถุ แม้ว่าจุดที่ถ่าย
tvanfosson

0

ตามโพสต์บล็อกของ Bill Higgins ' ; จาวาสคริปต์ตรรกะหรือการกำหนดสำนวน (ก.พ. 2550) พฤติกรรมนี้เป็นจริงเมื่อวันที่ v1.2 (อย่างน้อย)

นอกจากนี้เขายังแนะนำการใช้งานอื่นให้กับมัน (ที่ยกมา): "การทำให้ความแตกต่างของเบราว์เซอร์เป็นมาตรฐานมีน้ำหนักเบา "

// determine upon which element a Javascript event (e) occurred
var target = /*w3c*/ e.target || /*IE*/ e.srcElement;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.