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



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


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

พอมาเป็นใน 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()) // ได้ ສະບາຍດີ




ตัวแปร 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

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

สารบัญ

รวมคำแปลวลีเด็ดจากญี่ปุ่น
python
-- numpy
-- matplotlib

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

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



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

  ค้นหาบทความ

  บทความแนะนำ

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

บทความแต่ละเดือน

2020年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2019年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2018年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2017年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2016年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

ค้นบทความเก่ากว่านั้น

ไทย

日本語

中文