Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> JavaScript

JavaScript Date.prototype[@@toPrimitive]() - แปลง Date เป็น Primitive Value


Symbol.toPrimitive คืออะไร?

Date.prototype[Symbol.toPrimitive] เป็นเมธอดพิเศษ (Well-known Symbol) ที่ JavaScript เรียกใช้เมื่อต้องการแปลง Date object ให้เป็น Primitive Value (string, number, หรือ default)

  • ถูกเรียกอัตโนมัติเมื่อมีการ Type Coercion
  • รับ parameter hint 3 ค่า: "string", "number", "default"
  • Date object ignore hint และทำงานเหมือน hint === "string" เสมอ (สำหรับ backward compatibility)

ตัวอย่างโค้ดครบถ้วน

<!DOCTYPE html>
<html lang="th">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Date @@toPrimitive</title>
  <style>
    body { font-family: 'Segoe UI', sans-serif; padding: 20px; max-width: 700px; margin: 0 auto; }
    .panel { background: #f5f5f5; padding: 20px; border-radius: 8px; margin-bottom: 20px; }
    .code { background: #2d2d2d; color: #f8f8f2; padding: 15px; border-radius: 4px; font-family: monospace; overflow-x: auto; }
    .output { background: #e8f5e9; padding: 15px; border-radius: 4px; font-family: monospace; white-space: pre-wrap; }
    button { padding: 10px 20px; background: #7e32d4; color: white; border: none; border-radius: 4px; cursor: pointer; margin-right: 10px; }
    button:hover { background: #6a1b9a; }
    table { width: 100%; border-collapse: collapse; margin-top: 10px; }
    th, td { padding: 10px; border: 1px solid #ddd; text-align: left; }
    th { background: #7e32d4; color: white; }
  </style>
</head>
<body>
  <h1>Date.prototype[Symbol.toPrimitive]()</h1>
  <p>สาธิตการแปลง Date object เป็น Primitive values ด้วย hint ต่างๆ</p>
  
  <div class="panel">
    <button onclick="demoToPrimitive()">ทดสอบ Symbol.toPrimitive</button>
    <button onclick="demoCoercion()">ทดสอบ Type Coercion อัตโนมัติ</button>
    <button onclick="demoCustom()">ทดสtoverride Symbol.toPrimitive</button>
  </div>
  
  <div class="panel">
    <h3>ผลลัพธ์ Symbol.toPrimitive</h3>
    <div class="output" id="output1"></div>
  </div>
  
  <div class="panel">
    <h3>Type Coercion ต่างๆ</h3>
    <div class="output" id="output2"></div>
  </div>

  <script>
    const output1 = document.getElementById('output1');
    const output2 = document.getElementById('output2');
    const date = new Date('2024-01-15T10:30:45.123Z');
    
    function log(el, msg) {
      el.textContent += msg + '\n';
    }
    
    function demoToPrimitive() {
      output1.textContent = '';
      const sym = Symbol.toPrimitive;
      
      log(output1, `Date object: ${date}`);
      log(output1, `typeof date: ${typeof date}`);
      log(output1, `---`);
      
      // เรียกโดยตรงด้วย hint ต่างๆ
      const asString = date[sym]('string');
      const asNumber = date[sym]('number');
      const asDefault = date[sym]('default');
      
      log(output1, `date[Symbol.toPrimitive]('string')  = "${asString}" (type: ${typeof asString})`);
      log(output1, `date[Symbol.toPrimitive]('number')  = ${asNumber} (type: ${typeof asNumber})`);
      log(output1, `date[Symbol.toPrimitive]('default') = "${asDefault}" (type: ${typeof asDefault})`);
      log(output1, `---`);
      log(output1, 'หมายเหตุ: Date ไม่สนใจ hint - คืนค่า string เสมอ (เหมือน toString)');
    }
    
    function demoCoercion() {
      output2.textContent = '';
      
      // String context
      log(output2, `String(date)           = "${String(date)}"`);
      log(output2, `date.toString()        = "${date.toString()}"`);
      log(output2, `date.toISOString()     = "${date.toISOString()}"`);
      log(output2, `date.toLocaleString()  = "${date.toLocaleString('th-TH')}"`);
      log(output2, `'' + date              = "${'' + date}"`);
      log(output2, `\`Date: \${date}\`      = \`Date: ${date}\``);
      log(output2, `---`);
      
      // Number context
      log(output2, `Number(date)           = ${Number(date)}`);
      log(output2, `+date                  = ${+date}`);
      log(output2, `date.valueOf()         = ${date.valueOf()}`);
      log(output2, `date.getTime()         = ${date.getTime()}`);
      log(output2, `---`);
      
      // Default context (==, +, etc.)
      log(output2, `date == "${date}"      → ${date == date.toString()}`);
      log(output2, `date + 1000            = ${date + 1000} (string concat)`);
      log(output2, `date - 0               = ${date - 0} (number - date = timestamp)`);
    }
    
    function demoCustom() {
      output1.textContent += '\n--- Custom Override ---\n';
      
      // สร้าง object ที่มี Symbol.toPrimitive กำหนดเอง
      const customObj = {
        value: 42,
        [Symbol.toPrimitive](hint) {
          log(output1, `  [Custom] hint = "${hint}"`);
          if (hint === 'number') return this.value;
          if (hint === 'string') return `[Custom: ${this.value}]`;
          return this.value; // default
        }
      };
      
      log(output1, `Number(customObj)  = ${Number(customObj)}`);
      log(output1, `String(customObj)  = "${String(customObj)}"`);
      log(output1, `customObj + 10     = ${customObj + 10}`);
      log(output1, `\`Value: \${customObj}\` = \`Value: ${customObj}\``);
    }
    
    demoToPrimitive();
  </script>
</body>
</html>

พฤติกรรมของ Date กับ Hint ต่างๆ

Hintการเรียกใช้ผลลัพธ์Type
stringString(date), date.toString(), \`\${date}\`"Mon Jan 15 2024 17:30:45 GMT+0700"string
numberNumber(date), +date, date.valueOf()1705312245123 (timestamp ms)number
defaultdate == "something", date + 1000"Mon Jan 15 2024 17:30:45 GMT+0700"string

ข้อสังเกตสำคัญ: Date ไม่เคารพ Hint "number" ใน toPrimitive

// Date object behavior (special case for backward compatibility)
date[Symbol.toPrimitive]('number')  // คืนค่า STRING ไม่ใช่ number!
// ต้องใช้ date.valueOf() หรือ +date หรือ Number(date) แทน

เหตุผล: เพื่อความเข้ากันได้กับโค้ดเก่า ที่คาดหวังว่า date + "" และ date == "string" จะทำงานแบบ string comparison

การ Override Symbol.toPrimitive ใน Object ตัวเอง

const temperature = {
  celsius: 25,
  [Symbol.toPrimitive](hint) {
    switch (hint) {
      case 'number': return this.celsius;
      case 'string': return `${this.celsius}°C`;
      default: return this.celsius; // default มักใช้ number
    }
  }
};

console.log(Number(temperature));    // 25
console.log(String(temperature));    // "25°C"
console.log(temperature + 10);       // 35 (default → number)
console.log(`Temp: ${temperature}`); // "Temp: 25°C" (string context)

Related Methods

  • date.toString() - Human readable string (local time)
  • date.toISOString() - ISO 8601 format (UTC)
  • date.toLocaleString() - Locale-aware formatting
  • date.valueOf() / date.getTime() - Timestamp (milliseconds since epoch)
  • +date / Number(date) - แปลงเป็น number (timestamp)

Browser Support

  • Symbol.toPrimitive: Chrome 47+, Firefox 44+, Safari 10+, Edge 15+
  • รองรับในทุกเบราว์เซอร์สมัยใหม่ (ES2015+)