φυβλαςのβλογ
บล็อกของ phyblas



javascript เบื้องต้น บทที่ ๓๑: ความสามารถเพิ่มเติมเกี่ยวกับการสร้างและใช้ฟังก์ชัน
เขียนเมื่อ 2020/01/30 17:01
แก้ไขล่าสุด 2020/05/06 18:07


ในบทที่ ๑๐ ได้เขียนถึงเรื่องการสร้างฟังก์ชันในจาวาสคริปต์แบบดั้งเดิมไปแล้ว

พอมาเป็นใน ES6 การสร้างฟังก์ชันมีความยืดหยุ่นขึ้น ทำอะไรได้หลากหลายและสะดวกมากขึ้น

ในบทนี้จะมาพูดถึงสิ่งที่ทำได้เพิ่มเติมมาจากเดิมในการสร้างฟังก์ชันใน ES6




การสร้างฟังก์ชันที่ไม่จำกัดจำนวนพารามิเตอร์

ในจาวาสคริปต์ก่อนที่จะมี ES6 นั้นถ้าหากต้องการสร้างฟังก์ชันที่รับพารามิเตอร์แบบจำนวนไม่จำกัด ปกติจะใช้ตัวแปร arguments เช่น
var f = function() {
  var len = arguments.length;
  var i = 0;
  var s = "";
  while (i < len) {
    s += "ตัวที่ " + i + " คือ " + arguments[i] + ", ";
    i++;
  }
  alert(s);
};
f("ไก่", "ไข่", "ควาย"); // ได้ ตัวที่ 0 คือ ไก่, ตัวที่ 1 คือ ไข่, ตัวที่ 2 คือ ควาย, 

แต่ว่าใน ES6 มีวิธีที่สะดวกกว่านั้นมาก คือการใช่จุด ๓ จุด ... แบบนี้นำหน้าพารามิเตอร์ แล้วพารามิเตอร์ตัวนั้นจะกลายเป็นแถวลำดับที่มีค่าเป็นอาร์กิวเมนต์ทั้งหมดที่รับเข้ามา ไม่ว่าจะมีกี่ตัวก็ตาม

หากจะเขียนตัวอย่างข้างต้นด้วยวิธีใหม่อาจเขียนได้แบบนี้
let f = function(...agumon) {
  let len = agumon.length;
  let i = 0;
  let s = "";
  while (i < len) {
    s += "ตัวที่ " + i + " คือ " + agumon[i] + ", ";
    i++;
  }
  alert(s);
};

f("ไก่", "ไข่", "ควาย"); // ได้ ตัวที่ 0 คือ ไก่, ตัวที่ 1 คือ ไข่, ตัวที่ 2 คือ ควาย, 

จะเห็นว่าแค่เปลี่ยนจากการใช้ตัวแปร arguments เป็นใช้ตัวแปรที่กำหนดขึ้นเองโดยใส่ต่อจาก ... ในวงเล็บหลัง function ซึ่งในที่นี้คือ agumon

ถึงแม้วิธีการใช้ตัวแปร arguments แบบเดิมก็ยังคงใช้ได้อยู่ แต่เมื่อเทียบกับวิธีใหม่ที่ใช้ ๓ จุดแบบนี้แล้วถือว่าด้อยกว่า ทำให้ไม่จำเป็นต้องใช้อีกต่อไป เนื่องจากชื่อตัวแปรก็ยาวด้วย แล้วก็เป็นออบเจ็กต์ที่ไม่ใช่แถวลำดับจริงๆ ไม่สามารถเอามาทำงานแบบแถวลำดับได้จริงๆ

ตัว arguments ไม่มีเมธอดอย่าง .join หรือ .forEach หรือ .map อีกทั้งไม่สามารถแสดงค่าได้โดยตรงโดย alert ไม่ว่าจะทำอะไรก็จะใช้ต้องวนซ้ำด้วย for หรือ while เท่านั้น

แต่หากใช้ ๓​ จุด สิ่งที่ได้มาจะเป็นข้อมูลแถวลำดับจริงๆ
let f = function (...agumon) {
  if(agumon instanceof Array){
    alert(agumon + " เป็น Array");
  }
};

f("อา","กุ","มอน"); // ได้ อา,กุ,มอน เป็น Array

เมื่อเป็นแถวลำดับจึงสามารถใช้ map ได้ (รายละเอียดเรื่อง map อ่านในบทที่ ๒๗)
let f = function (...agapi) {
  alert(
    agapi.map(
      function (x) {
        return x + "เปลี่ยนร่าง";
      }
    )
  );
};

f("อากุมอน", "กาบุมอน", "ปิโยมอน"); // ได้ อากุมอนเปลี่ยนร่าง,กาบุมอนเปลี่ยนร่าง,ปิโยมอนเปลี่ยนร่าง

แต่ถ้าเปลี่ยนมาใช้ arguments จะทำแบบนี้ไม่ได้
var f = function () {
  alert(
    arguments.map(
      function (x) {
        return x + "เปลี่ยนร่าง";
      }
    )
  );
};

f("อากุมอน", "กาบุมอน", "ปิโยมอน"); // ได้ TypeError: arguments.map is not a function

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

ตัวอย่าง
let f = function (z, ...argu) {
  alert(
    argu.map(
      function (x) {
        return x + z;
      }
    )
  );
};

f("หิวแล้ว", "โกมามอน", "ปาตามอน", "ไดโดมอน"); // ได้ โกมามอนหิวแล้ว,ปาตามอนหิวแล้ว,ไดโดมอนหิวแล้ว




การแตกค่าในแถวลำดับเพื่อใช้ในฟังก์ชัน

เดิมทีใน ES3 หรือ ES5 หากเราสร้างฟังก์ชันที่รับพารามิเตอร์หลายตัว ปกติเราจะต้องใส่ข้อมูลในวงเล็บไล่ไปทีละตัว จะใส่แถวลำดับแทนไม่ได้ เช่น
var f = function (a,b,c) {
  alert(a+b+c);
};
  
f(1, 2, 3); // ได้ 6

let arr = [1,2,3];
f(arr); // ได้ 1,2,3undefinedundefined

ในที่นี้ arr ถูกแทนลงตัวแปร a ซึ่งเป็นพารามิเตอร์ตัวแรกตัวเดียว ส่วน b กับ c ไม่ได้ค่าไปด้วยจึงเป็น undefined

แต่ใน ES6 นั้นหากมีแถวลำดับที่ใส่ข้อมูลอยู่ แล้วต้องการแตกให้ตัวแปรในแถวลำดับนั้นไปใช้เป็นอาร์กิวเมนต์แต่ละตัวของฟังก์ชันแยกกันก็ทำได้โดยเติม ๓ จุด ... ไว้ข้างหน้าแถวลำดับ

เช่น
let arr = [2,3,4];
f(...arr); // ได้ 9

ถ้าเป็นใน ES3 หรือ ES5 อาจจำเป็นต้องไล่แยกข้อมูลในแถวลำดับทีละตัวด้วยตัวเอง หรือไม่ก็ถ้าจะทำแบบนี้ได้อาจต้องใช้ฟังก์ชัน apply ซึ่งจะดูแล้วเข้าใจยากกว่า
f(arr[0], arr[1], arr[2]); // ได้ 9
f.apply(null, arr); // ได้ 9

การเติม ๓​ จุดแบบนี้คล้ายกับการใช้ ๓ จุดในหัวข้อที่แล้ว คือใช้ใส่ในวงเล็บตอนนิยามฟังก์ชันขึ้น แค่เปลี่ยนจากที่เดิมใช้กับพารามิเตอร์มาเป็นใช้กับอาร์กิวเมนต์




การกำหนดค่าตั้งต้นให้พารามิเตอร์ในฟังก์ชัน

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

ใน ES3 หรือ ES5 ถ้าหากเราต้องการให้ค่าของตัวแปรที่ไม่ได้ใส่ไปนั้นเป็นค่าตามที่เรากำหนดไว้ให้เป็นค่าตั้งต้นก็จะต้องตั้งเงื่อนไขด้วย if ว่าถ้าค่าเป็น undefined ให้มีการใส่ค่าตั้งต้นตามที่กำหนดเข้าไป เช่น
var f = function (chue, ayu) {
  if(ayu===undefined){
    ayu = 17;
  }
  alert("- ฉันชื่อ " + chue + " อายุ " + ayu + " ปี -")
};

f("ฮาจิเมะ"); // ได้ - ฉันชื่อ ฮาจิเมะ อายุ 17 ปี -
f("เยวี่ย", 323); // ได้ - ฉันชื่อ เยวี่ย อายุ 323 ปี -



แต่ว่าใน ES6 มีวิธีทำให้สามารถกำหนดค่าตั้งต้นให้พารามิเตอร์ได้โดยตรง โดยทำได้ง่ายแค่เติมเครื่องหมายเท่ากับ = แล้วตามด้วยค่าตั้งต้นที่ต้องการ

ตัวอย่าง
let f = function (chue, ayu = 17) {
  alert("- ฉันชื่อ " + chue + " อายุ " + ayu + " ปี -")
};

f("คาโอริ"); // ได้ - ฉันชื่อ คาโอริ อายุ 17 ปี -
f("ชิซึกุ", undefined); // ได้ - ฉันชื่อ ชิซึกุ อายุ 17 ปี -
f("ไอโกะ", 25); // ได้ - ฉันชื่อ ไอโกะ อายุ 25 ปี -

จะเห็นว่าถ้าไม่ได้ใส่ค่าหรือใส่เป็น undefined ก็จะได้ค่าตามค่าตั้งต้นที่ใส่ลงไป

ค่าตั้งต้นที่กำหนดไปนี้จะถูกใช้เมื่อไม่มีการป้อนค่าให้ หรือเมื่อป้อนค่าให้เป็น undefined

ดังนั้นหากต้องการให้พารามิเตอร์ที่อยู่ลำดับก่อนหน้าได้ค่าตั้งต้น แต่ต้องการกำหนดค่าให้ตัวที่อยู่หลังจากนั้นไปก็อาจใส่ undefined ให้
let f = function (a = 3, b = 7) {
  alert("มีซูแบ็ต " + a + " ตัว อาร์บ็อก " + b + " ตัว");
};

f(); // ได้ มีซูแบ็ต 3 ตัว อาร์บ็อก 7 ตัว
f(5) // ได้ มีซูแบ็ต 5 ตัว อาร์บ็อก 7 ตัว
f(undefined, 4); // ได้ มีซูแบ็ต 3 ตัว อาร์บ็อก 4 ตัว



ค่าตั้งต้นอาจกำหนดโดยการใช้ค่าจากพารามิเตอร์อีกตัวที่อยู่ลำดับก่อนหน้าก็ได้ เช่น
let f = function (a, b = a * 2) {
  alert("มีค้างคาว " + a + " ตัว งูเห่า " + b + " ตัว");
};

f(3); // ได้ มีค้างคาว 3 ตัว งูเห่า 6 ตัว
f(4, 10); // ได้ มีค้างคาว 4 ตัว งูเห่า 10 ตัว

แต่จะให้ค่าตัวก่อนหน้าขึ้นอยู่กับค่าข้างหลังไม่ได้ จึงสลับลำดับกันไม่ได้
let f = function (b = a * 2, a) {
  alert("มีค้างคาว " + a + " ตัว งูเห่า " + b + " ตัว");
};

f(undefined, 3); // ได้ ReferenceError: can't access lexical declaration `a' before initialization




การสร้างฟังก์ชันแบบที่อาร์กิวเมนต์มีชื่อ

ฟังก์ชันที่สร้างแบบปกติตามวิธีที่เคยทำมานั้น เวลาที่ใช้จะต้องใส่ค่าลงไปตามลำดับพารามิเตอร์ที่วางไว้

ตัวอย่างเช่น ลองสร้างฟังก์ชันหาปริมาตรทรงสี่เหลี่ยม โดยมีตัวแปร ๓ ตัวคือกว้าง, ยาว และ สูง
let haParimat = function (kwang, yao, sung) {
  let parimat = kwang * yao * sung;
  alert("" + kwang + "×" + yao + "×" + sung + " = " + parimat);
};

haParimat(10, 12, 2) // ได้ 10×12×2 = 240

แต่ว่าแบบนี้อาจเข้าใจยากเวลาใช้ เพราะต้องจำว่าตัวแปรลำดับไหนคืออะไร ดังนั้นเพื่อให้เข้าใจง่ายขึ้นอาจทำได้โดยป้อนค่าในรูปแบบของออบเจ็กต์ซึ่งมีพรอเพอร์ตีเป็นชื่อตัวแปรตามที่ต้องการ
let haParimat = function (kys) {
  let parimat = kys.kwang * kys.yao * kys.sung;
  alert("" + kys.kwang + "×" + kys.yao + "×" + kys.sung + " = " + parimat);
};

haParimat({sung: 2, kwang: 10, yao: 12}); // ได้ 10×12×2 = 240

พอทำแบบนี้แล้วก็จะดูง่ายขึ้น รู้ว่าตัวแปรไหนมีค่าเท่าไหร่ และไม่ขึ้นอยู่กับลำดับด้วย เพียงแต่ว่าทำให้โค้ดดูซับซ้อนขึ้น

ใน ES6 มีวิธีเขียนให้ป้อนค่าเป็นออบเจ็กต์แบบนี้ได้โดยเขียนง่ายขึ้น คือใช้วงเล็บปีกกา { } คร่อมชื่อตัวแปร
let haParimat = function ({kwang, yao, sung}) {
  let parimat = kwang * yao * sung;
  alert("" + kwang + "×" + yao + "×" + sung + " = " + parimat);
};

haParimat({sung: 2, kwang: 10, yao: 12}); // ได้ 10×12×2 = 240

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

ต่อให้ออบเจ็กต์ที่ใส่ไปมีพรอเพอร์ตีที่ไม่ได้ระบุไว้ในฟังก์ชันก็ไม่มีผลอะไร แค่ไม่ถูกนำมาคิด
haParimat({sung: 12, kwang: 70, yao: 100, nak: 600}); // ได้ 12×70×100 = 84000

หรือถ้าใส่ไม่ครบตัวที่ขาดไปก็จะกลายเป็น undefined
haParimat({sung: 15, kwang: 9}); // ได้ 9×undefined×15 = NaN

วิธีการนี้ก็สามารถกำหนดค่าตั้งต้นได้โดยการเติม = แล้วตามด้วยค่าตั้งต้นที่ต้องการ โดยค่าตั้งต้นจะถูกใช้เมื่อออบเจ็กต์ที่ป้อนเข้ามาไม่มีพรอเพอร์ตีตัวนั้น
let haParimat = function ({ kwang, yao = 10, sung = 5 }) {
  let parimat = kwang * yao * sung;
  alert("" + kwang + "×" + yao + "×" + sung + " = " + parimat);
};

haParimat({ sung: 15, kwang: 9 }); // ได้ 9×10×15 = 1350

ในที่นี้ตัวแปร yao ไม่ได้ถูกป้อนเข้าไปจึงใช้ค่าตั้งต้น ส่วน sung มีการป้อนเข้าไปจึงใช้ค่าที่ป้อนเข้าไป ไม่สนค่าตั้งต้น

หรือถ้าค่าเป็น undefined ค่าตั้งต้นก็จะถูกนำมาใช้เช่นกัน
haParimat({ sung: undefined, kwang: 9 }); // ได้ 9×10×5 = 450




การประกาศฟังก์ชันในแบบลูกศร

รูปแบบวิธีใหม่ในการสร้างฟังก์ชันได้ถูกเพิ่มเข้ามาใน ES6 คือการใช้ลูกศร => ในการสร้างฟังก์ชัน แทนที่จะเขียนว่า function

ตัวอย่างเช่น เดิมทีอาจเขียนโดยใช้ function แบบนี้
let f = function (kai, khai) {
  alert("ไก่ " + kai + " ตัวมีไข่ " + khai + " ฟอง");
};

f(20, 70); // ได้ ไก่ 20 ตัวมีไข่ 70 ฟอง

แต่สามารถเขียนใหม่แทนด้วย => ได้โดยเขียนแบบนี้
let f = (kai, khai) => {
  alert("ไก่ " + kai + " ตัวมีไข่ " + khai + " ฟอง");
};

f(20, 70); // ได้ ไก่ 20 ตัวมีไข่ 70 ฟอง

การเขียนแบบนี้ดูกะทัดรัดสะดวกโดยเฉพาะเมื่อประกาศแล้วใช้ทันที เช่น
let ar = [3, 6, 8];
let s = ar.map((x) => { return "2×" + x + "=" + (x + x) });
alert(s); // ได้ 2×3=6,2×6=12,2×8=16

วงเล็บ () ด้านซ้ายของ => สามารถละได้ในกรณีที่พารามิเตอร์มีเพียงตัวเดียว เช่นในตัวอย่างที่แล้ว เขียนฟังก์ชันใหม่ได้เป็น
let s = ar.map(x => { return "2×" + x + "=" + (x + x) });

การละวงเล็บทำได้แค่กรณีที่มีตัวแปร ๑ ตัวเท่านั้น แม้แต่กรณีที่ฟังก์ชันไม่มีพารามิเตอร์ก็ต้องใส่วงเล็บเปล่าไว้ ()

ตัวอย่างฟังก์ชันที่ไม่มีพารามิเตอร์
let f = () => { return "ສະບາຍດີ"};
alert(f()) // ได้ ສະບາຍດີ




การเขียนย่อฟังก์ชันแบบลูกศร

ฟังก์ชันที่ประกาศในแบบลูกศรหากมีโครสร้างเรียบง่ายและแค่ต้องการให้คืนค่าสามารถละวงเล็บปีกกา { } ได้ และไม่ต้องใส่ return
let f = (x, y) => x * y;
alert(f(2, 3)); // ได้ 6

แบบนี้มีค่าเท่ากับการเขียนว่า
let f = (x, y) => { return x * y };

เพียงแต่ว่ากรณีที่สิ่งที่ต้องการคืนค่านั้นเป็นออบเจ็กต์จะต้องใส่วงเล็บล้อมด้วย เช่นแบบนี้
let f = x => ({ 1: x });
alert(f(2)); // ได้ [object Object]

เพราะหากเขียนโดยไม่มีวงเล็บล้อมแบบนี้ กรอบวงเล็บปีกกา { } จะถูกตีความเป็นกรอบนิยามฟังก์ชัน แทนที่จะเป็นออบเจ็กต์ แบบนี้ก็จะเกิดข้อผิดพลาดขึ้น
let f = x => { 1: x }; // ได้ SyntaxError: unexpected token: ':'




ตัวแปร this เมื่อประกาศฟังก์ชันในแบบลูกศร

โดยภาพรวมแล้วฟังก์ชันที่สร้างโดยใช้ลูกศรจะมีลักษณะเหมือนกับฟังก์ชันที่สร้างด้วยคำสั่ง function() แต่ก็มีข้อแตกต่างบางอย่าง ที่สำคัญก็คือ ตัวแปร this เมื่อใช้ฟังก์ชันในโครงสร้างออบเจ็กต์

ดังที่ได้อธิบายไปในบทที่ ๑๙ this เป็นตัวแปรพิเศษซึ่งจะแทนอะไรขึ้นอยู่กับว่าอยู่ที่ไหน

ปกติถ้าใช้นอกฟังก์ชัน this จะแทบออบเจ็กต์ global แต่ถ้าหากฟังก์ชันถูกเรียกใช้ในฐานะเมธอดภายในออบเจ็กต์ กรณีนี้ this จะแทนตัวออบเจ็กต์นั้น เช่น
let krapuk1 = {
  ngoen: 10000,
  bokNgoen: function() {
    alert("มีเงิน "+this.ngoen);
  }
};

krapuk1.bokNgoen() // ได้ มีเงิน 10000

แต่ถ้าเป็นฟังก์ชันที่สร้างด้วยลูกศร this จะไม่ถูกแทนด้วยออบเจ็กต์นั้น

เช่น ลองเขียนฟังก์ชันเหมือนเดิมแต่เปลี่ยนเป็นใช้ลูกศร
let krapuk2 = {
  ngoen: 10000,
  bokNgoen: () => {
    alert("มีเงิน "+this.ngoen);
  }
};

krapuk2.bokNgoen() // ได้ มีเงิน undefined

ในที่นี้ this จะไม่แทนตัวออบเจ็กต์ที่เรียก ดังนั้น this ก็จะไปแทนสิ่งเดิมทีควรเป็น this อยู่ ซึ่งในที่นี้ก็คือแทนออบเจ็กต์ global ดังนั้นเมื่อหาพรอเพอร์ตี .ngoen จึงไม่พบ และได้ undefined ไป

หากมีการใช้ฟังก์ชันด้านในฟังก์ชันหรือเมธอดจะซับซ้อนขึ้นไปอีก และจะทำให้เกิดความสับสนได้ง่าย จึงต้องทำความเข้าใจให้ดี ตรงนี้จะเห็นความแตกต่างระหว่างการสร้างฟังก์ชันด้วย function กับด้วยการใช้ลูกศร

ตัวอย่างเช่น สร้างออบเจ็กต์ตัวหนึ่งให้มีเมธอดอยู่ และภายในเมธอดนั้นก็มีการเรียกใช้ฟังก์ชัน โดยทั้งเมธอดและฟังก์ชันด้านในใช้วิธีการนิยามด้วยคำสั่ง function แบบนี้
let krapukB = {
  ngoen: 7,
  toemNgoen: function (ar) {
    ar.forEach(function (x) {
      this.ngoen += x; // this ในที่นี้คือ global
    })
    alert(this.ngoen); // this ในที่นี้คือ krapukB
  }
};

krapukB.toemNgoen([20, 500, 1000]); // ได้ 7

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

แต่พอเขียนแบบนี้จะเห็นว่าผลไม่ได้ตามที่ต้องการ เงินเริ่มต้นมี 7 แต่หลังจากที่เติมเงินโดยใช้เมธอด .forEach ไปเงินกลับไม่เพิ่มขึ้น ทั้งที่ในฟังก์ชันใน forEach มีการสั่งให้บวกเงินเพิ่มเข้าไปใน this.ngoen

ที่เป็นแบบนี้ก็เพราะว่า this ในที่นี้อยู่ในฟังก์ชันซึ่งถูกสร้างขึ้นมาใหม่ใน forEach จึงไม่ได้แทนตัวออบเจ็กต์ krapukB ซึ่งเป็นตัวเรียก แต่แทนตัวออบเจ็กต์ global ดังนั้นการบวก this.ngoen จึงไม่มีผลกับเงินใน krapukB

ทำให้ไม่สามารถเข้าถึงตัวออบเจ็กต์นั้นผ่าน this ได้ ต้องแก้ปัญหาโดยการฝากให้ตัวแปรอื่นแทนออบเจ็กต์นั้นก่อนที่จะเข้าฟังก์ชัน เช่นทำแบบนี้
let krapukC = {
  ngoen: 7,
  toemNgoen: function (ar) {
    let cette = this; // สร้างตัวแปรใหม่ใช้แทน this ชั่วคราว
    ar.forEach(function (x) {
      cette.ngoen += x; // ตัวแปรชั่วคราว cette แทน krapukC
    })
    alert(this.ngoen); // this ในที่นี้คือ krapukC
  }
};

krapukC.toemNgoen([20, 500, 1000]); // ได้ 1527

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

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

เมื่อเขียนแทนด้วย => จะกลายเป็นแบบนี้
let krapukD = {
  ngoen: 7,
  toemNgoen: function (ar) {
    ar.forEach(x => {
      this.ngoen += x; // this ในที่นี้คือ krapukD
    })
    alert(this.ngoen); // this ในที่นี้คือ krapukD
  }
};

krapukD.toemNgoen([20, 500, 1000]); // ได้ 1527

จะเห็นว่าพอใช้ลูกศรแทนแล้ว ในที่นี้ this ด้านใน forEach เองก็ถือเป็น this ตัวเดียวกับนอก forEach คือแทนตัวออบเจ็กต์ krapukD เหมือนกัน ไม่จำเป็นต้องเอา this ไปฝากไว้ที่ไหน

อย่างไรก็ตาม ตัวเมธอด toemNgoen จะใช้เป็นลูกศรแทนไม่ได้ เพราะถ้าใช้ลูกศรสร้างจะทำให้ this ในนั้นไม่ได้แทนตัวออบเจ็กต์ แต่ก็กลับจะไปแทนตัว global แทน
let krapukF = {
  ngoen: 7,
  toemNgoen: ar => {
    ar.forEach(x => {
      this.ngoen += x; // this ในที่นี้คือ global
    })
    alert(this.ngoen); // this ในที่นี้คือ global
  }
};

krapukF.toemNgoen([20, 500, 1000]); // ได้ NaN

คราวนี้ this ทั้งด้านในและนอก forEach ก็กลายเป็นแทนออบเจ็กต์ global ทั้งหมด จึงไม่มีพรอเพอร์ตี .ngoen และทำให้ได้ค่าออกมาเป็น NaN

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




ใส่ , หลังพารามิเตอร์ตัวสุดท้าย [ES2017]

มีความสามารถจุกจิกเล็กๆน้อยๆที่ถูกเพิ่มเข้ามาใน ES2017 นั่นคือการที่สามารถใส่ , ลงไปหลังจากพารามิเตอร์ตัวสุดท้ายตอนประกาศสร้างฟังก์ชันได้
let f = function (a, b,) {
  alert("a=" + a + " b=" + b);
};

f(50,60); // ได้ a=50 b=60

ในขณะที่ถ้าทำแบบนี้ในเวอร์ชันก่อนหน้านี้ก็จะเกิดข้อผิดพลาดขึ้นมา ต้องเขียนเป็นแค่ function (a, b) ไม่มี , ต่อท้าย

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





-----------------------------------------

囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧

ดูสถิติของหน้านี้

หมวดหมู่

-- คอมพิวเตอร์ >> เขียนโปรแกรม >> javascript

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

สารบัญ

รวมคำแปลวลีเด็ดจากญี่ปุ่น
มอดูลต่างๆ
-- numpy
-- matplotlib

-- pandas
-- opencv
-- pytorch
การเรียนรู้ของเครื่อง
-- โครงข่าย
     ประสาทเทียม
maya
javascript
ความน่าจะเป็น
บันทึกในญี่ปุ่น
บันทึกในจีน
-- บันทึกในปักกิ่ง
-- บันทึกในฮ่องกง
-- บันทึกในมาเก๊า
บันทึกในไต้หวัน
บันทึกในยุโรปเหนือ
บันทึกในประเทศอื่นๆ
เรียนภาษาจีน
qiita
บทความอื่นๆ

บทความแบ่งตามหมวด



ติดตามอัปเดตของบล็อกได้ที่แฟนเพจ

  ค้นหาบทความ

  บทความแนะนำ

ภาษาจีนแบ่งเป็นสำเนียงอะไรบ้าง มีความแตกต่างกันมากแค่ไหน
ทำความเข้าใจระบอบประชาธิปไตยจากประวัติศาสตร์ความเป็นมา
เรียนรู้วิธีการใช้ regular expression (regex)
หลักการเขียนทับศัพท์ภาษาจีนกวางตุ้ง
การใช้ unix shell เบื้องต้น ใน linux และ mac
หลักการเขียนทับศัพท์ภาษาจีนกลาง
g ในภาษาญี่ปุ่นออกเสียง "ก" หรือ "ง" กันแน่
ทำความรู้จักกับปัญญาประดิษฐ์และการเรียนรู้ของเครื่อง
ค้นพบระบบดาวเคราะห์ ๘ ดวง เบื้องหลังความสำเร็จคือปัญญาประดิษฐ์ (AI)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
บันทึกการเที่ยวสวีเดน 1-12 พ.ค. 2014
แนะนำองค์การวิจัยและพัฒนาการสำรวจอวกาศญี่ปุ่น (JAXA)
เล่าประสบการณ์ค่ายอบรมวิชาการทางดาราศาสตร์โดยโซวเคนได 10 - 16 พ.ย. 2013
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
บันทึกการเที่ยวญี่ปุ่นครั้งแรกในชีวิต - ทุกอย่างเริ่มต้นที่สนามบินนานาชาติคันไซ
หลักการเขียนทับศัพท์ภาษาญี่ปุ่น
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ
ทำไมถึงอยากมาเรียนต่อนอก
เหตุผลอะไรที่ต้องใช้ภาษาวิบัติ?

ไทย

日本語

中文