diff --git a/README.md b/README.md index bcd0b3c..9199649 100644 --- a/README.md +++ b/README.md @@ -902,9 +902,9 @@ http://www.ebooks.in.th/ebook/40184/รีวีวฟรีเจอร์ใ ## สำหรับผู้ที่สนใจหนังสือเล่มนี้ -หนังสือ __ภาษาไทยเล่มแรก__ ที่กล่าวถึงจาวาสคริปต์มาตรฐานใหม่ ES6 พิมพ์ครั้งที่สามแล้วนะครับ มีการปรับปรุงแก้ไขคำผิดไป **แต่ทว่าตีพิม์จำนวนจำกัด ไม่มากเท่าไร** มีขายบางแห่งเท่านั้น ควรเช็คอีกที +หนังสือ __ภาษาไทยเล่มแรก__ ที่กล่าวถึงจาวาสคริปต์มาตรฐานใหม่ ES6 **ตอนนี้ไม่ตีพิมพ์ซ้ำอีกแล้ว** ยังมีขายบางแห่งเท่านั้น ควรเช็คอีกที -![cover](images/cover_big5.png) +![cover](images/cover_big.jpg) * [ศูนย์หนังสือจุฬา]( http://www.chulabook.com/description.asp?barcode=9786160825394) * [ร้านนายอินทร์](https://www.naiin.com/product/detail/191081/) @@ -913,3 +913,9 @@ http://www.ebooks.in.th/ebook/40184/รีวีวฟรีเจอร์ใ * [ผ่านฟ้าบุ๊คเซ็นเตอร์] (http://www.phanpha.com/item/พัฒนาเว็บแอปพลิเคชั่นด้วย-javascript) * [ซีเอ็ดบางสาขา]( https://www.se-ed.com/product/พัฒนาเว็บแอปพลิเคชั่นด้วย-JavaScript.aspx?no=9786160825394) * และร้านหนังสืออื่น ๆ ที่ไม่ได้กล่าว + +เล่มใหม่เนื้อหาอัปเดต เป็นจาวาสคริปต์ตั้งแต่ ES6 เป็นต้นไป (ล่าสุดเนื้อหาถึง ES14) เนื้อหาเกือบ 700 กว่าหน้า + +![cover](images/cover_new.png) + +[สั่งซ์้อได้ที่เว็บ MEB (ขายเป็นอีบุ๊กเท่านั้น)](https://www.mebmarket.com/web/index.php?action=BookDetails&data=YToyOntzOjc6InVzZXJfaWQiO3M6NzoiMTcyNTQ4MyI7czo3OiJib29rX2lkIjtzOjY6IjE1Njg1NCI7fQ) diff --git a/examples/budget_thailand/fiscal_year_2024.html b/examples/budget_thailand/fiscal_year_2024.html new file mode 100644 index 0000000..aab1c38 --- /dev/null +++ b/examples/budget_thailand/fiscal_year_2024.html @@ -0,0 +1,162 @@ + + + + + The Budget of Thailand 2024 + + + + + + +

ผ่างบประมาณของประเทศไทยปี 2567

+
+
+ +
+ + + +
+

โปรดติดตามแฟนเพจ https://www.facebook.com/programmerthai +

+ + \ No newline at end of file diff --git a/examples/google_translate/translate_general.html b/examples/google_translate/translate_general.html index f45782b..6ef2b3d 100644 --- a/examples/google_translate/translate_general.html +++ b/examples/google_translate/translate_general.html @@ -167,6 +167,9 @@ } +
+
ฝากติดตามเพจด้วย โปรแกรมเมอร์ไทย thai programmer +
diff --git a/examples/google_translate/translate_thai-eng.html b/examples/google_translate/translate_thai-eng.html index e20f0de..a69699f 100644 --- a/examples/google_translate/translate_thai-eng.html +++ b/examples/google_translate/translate_thai-eng.html @@ -156,6 +156,9 @@ }; */ +
+
ฝากติดตามเพจด้วย โปรแกรมเมอร์ไทย thai programmer +
diff --git a/examples/speech-recognition/game/index.html b/examples/speech-recognition/game/index.html index ecb77d3..2aeceb9 100644 --- a/examples/speech-recognition/game/index.html +++ b/examples/speech-recognition/game/index.html @@ -92,7 +92,9 @@ - +
+
ฝากติดตามเพจด้วย โปรแกรมเมอร์ไทย thai programmer +
diff --git a/examples/speech-recognition/web/index.html b/examples/speech-recognition/web/index.html index 90c2454..5073bd6 100644 --- a/examples/speech-recognition/web/index.html +++ b/examples/speech-recognition/web/index.html @@ -115,7 +115,7 @@

Translate:

-
x
+
@@ -265,5 +265,8 @@ } +
+
ฝากติดตามเพจด้วย โปรแกรมเมอร์ไทย thai programmer +
diff --git a/examples/timer/count_time.html b/examples/timer/count_time.html new file mode 100644 index 0000000..1caf30c --- /dev/null +++ b/examples/timer/count_time.html @@ -0,0 +1,231 @@ + + + + + + + + +
+ +
พักเบรก 15 นาทีนะครับ
+ +
+ + + + +
+ +
+
+
+
+ + + + +
+
+ + + + \ No newline at end of file diff --git a/examples_book/.gitignore b/examples_book/.gitignore new file mode 100644 index 0000000..e71f774 --- /dev/null +++ b/examples_book/.gitignore @@ -0,0 +1,7 @@ +node_modules/ +test_js/ +*.html +*.json +deploy* +*.bat +mybooks/ \ No newline at end of file diff --git a/examples_book/Appendix_B/README.md b/examples_book/Appendix_B/README.md new file mode 100644 index 0000000..f17914a --- /dev/null +++ b/examples_book/Appendix_B/README.md @@ -0,0 +1,7 @@ +# ภาคผนวก ข + +* [ไฟล์ hello.js](hello.js) +* [ไฟล์ server.mjs](server.mjs) +* [ไฟล์ ajax.mjs](ajax.mjs) + + diff --git a/examples_book/Appendix_B/ajax.mjs b/examples_book/Appendix_B/ajax.mjs new file mode 100644 index 0000000..106770b --- /dev/null +++ b/examples_book/Appendix_B/ajax.mjs @@ -0,0 +1,22 @@ +import * as http from "http"; +import * as fs from "fs"; +import * as url from "url"; +/* ถ้านำเข้ามอดูลตามมาตรฐาน CommonJS ก็ให้ใช้ require() +var http = require("http"), + fs = require("fs"), + url = require("url"); */ +http.createServer( function(request, response) { + var parser = url.parse(request.url,true); + var path = parser.pathname; + var query = parser.query; + if( path=="/message" ) { + var echo = `{"echo" : "${query.msg}"}`; + //response.writeHead(200, {"Content-Type": "text/plain"}); + response.writeHead(200, { + "Content-Type": "text/html", + "Access-Control-Allow-Origin" : "*"}); + response.end(echo); + console.log(echo); + } +}).listen(8001,"127.0.0.1"); +console.log("Server running at http://127.0.0.1:8001/"); \ No newline at end of file diff --git a/examples_book/Appendix_B/hello.js b/examples_book/Appendix_B/hello.js new file mode 100644 index 0000000..2f56984 --- /dev/null +++ b/examples_book/Appendix_B/hello.js @@ -0,0 +1,2 @@ +let msg = "Hello, world!"; // เป็นการประกาศตัวแปรแบบ let ซึ่งเป็นไวยากรณ์ของ ES6 +console.log(msg); \ No newline at end of file diff --git a/examples_book/Appendix_B/server.mjs b/examples_book/Appendix_B/server.mjs new file mode 100644 index 0000000..6850331 --- /dev/null +++ b/examples_book/Appendix_B/server.mjs @@ -0,0 +1,9 @@ +import * as http from "http"; +// ถ้านำเข้ามอดูลตามมาตรฐาน CommonJS ก็ให้ใช้ require() +// var http = require("http"); +http.createServer(function (req, res) { + res.writeHead(200, {"Content-Type": "text/html"}); + res.write("

Hello, world!

"); + res.end(); +}).listen(8001, "127.0.0.1"); +console.log("Server running at http://127.0.0.1:8001/"); diff --git a/examples_book/Appendix_G.md b/examples_book/Appendix_G.md new file mode 100644 index 0000000..6950e56 --- /dev/null +++ b/examples_book/Appendix_G.md @@ -0,0 +1,444 @@ +# ภาคผนวก ง Well-Known symbols + +```js +console.log(typeof Symbol.asyncIterator); // symbol --ส่วนข้อมูลเป็นฟังก์ชัน หรือเมธอด +console.log(typeof Symbol.hasInstance); // symbol --ส่วนข้อมูลเป็นฟังก์ชัน หรือเมธอด +console.log(typeof Symbol.isConcatSpreadable); // symbol –-ส่วนข้อมูลเป็นบูลีน +console.log(typeof Symbol.iterator); // symbol --ส่วนข้อมูลเป็นฟังก์ชัน หรือเมธอด +console.log(typeof Symbol.match); // symbol --ส่วนข้อมูลเป็นฟังก์ชัน หรือเมธอด +console.log(typeof Symbol.matchAll); // symbol --ส่วนข้อมูลเป็นฟังก์ชัน หรือเมธอด +console.log(typeof Symbol.replace); // symbol --ส่วนข้อมูลเป็นฟังก์ชัน หรือเมธอด +console.log(typeof Symbol.search); // symbol --ส่วนข้อมูลเป็นฟังก์ชัน หรือเมธอด +console.log(typeof Symbol.split); // symbol --ส่วนข้อมูลเป็นฟังก์ชัน หรือเมธอด +console.log(typeof Symbol.species); // symbol --ส่วนข้อมูลเป็นคอนสตรัคเตอร์ +console.log(typeof Symbol.toPrimitive); // symbol --ส่วนข้อมูลเป็นฟังก์ชัน หรือเมธอด +console.log(typeof Symbol.toStringTag); // symbol --ส่วนข้อมูลเป็นสตริง +console.log(typeof Symbol.unscopables); // symbol --ส่วนข้อมูลเป็นอ็อบเจ็กต์ +``` + +## การใช้งาน Symbol.hasInstance + +```js +function MyConstructor() { } +class MyClass { } +let obj1 = new MyConstructor(); +let obj2 = new MyClass(); +console.log(obj1 instanceof MyConstructor); // true +console.log(obj2 instanceof MyClass); // true + +console.log(MyConstructor[Symbol.hasInstance](obj1)); // true +// จะเหมือนเขียน console.log(obj1 instanceof MyConstructor); // true +console.log(MyClass[Symbol.hasInstance](obj2)); // true +// จะเหมือนเขียน console.log(obj2 instanceof MyClass); // true +``` + +```js +class PositiveNumber { + constructor(value) { + this.value = value; + } +} +Object.defineProperty( PositiveNumber, Symbol.hasInstance, { + // ดัดแปลงพฤติกรรมของพร็อพเพอร์ตี้ Symbol.hasInstance อันเดิม ด้วยการประกาศเมธอดอันใหม่ + value: function(objNumber) { + // พารามิเตอร์ objNumber รับค่าอากิวเมนต์เป็นอ็อบเจ็กต์ที่ถูกสร้างขึ้นมา + if(objNumber.value > 0) { + return true; // ถ้าค่าในอ็อบเจ็กต์มากกว่า 0 + } else { + return false; // ถ้าค่าในอ็อบเจ็กต์น้อยกว่าหรือเท่ากับ 0 + } + } // สิ้นสุดประกาศฟังก์ชัน +}); // สิ้นสุด Object.defineProperty +let positive = new PositiveNumber(10); +let negative = new PositiveNumber(-10); +console.log(positive instanceof PositiveNumber); // true +console.log(negative instanceof PositiveNumber); // false +``` + +## การใช้งาน Symbol.isConcatSpreadable + +```js +let a1 = [1, 2, 3]; +let a2 = [4, 5, 6]; +let a3 = a1.concat(a2); +console.log(a3); // [ 1, 2, 3, 4, 5, 6 ] +console.log(a1[Symbol.isConcatSpreadable]); // undefined +console.log(a2[Symbol.isConcatSpreadable]); // undefined +console.log(a3[Symbol.isConcatSpreadable]); // undefined +``` + +หมายเหตุ ตัวอย่างต่อไปนี้ อย่าลืมก็อปปี้ a1 กับ a2 ของตัวอย่างอย่างก่อนหน้านี้มาใช้งานด้วย + +```js +a1[Symbol.isConcatSpreadable] = true; +a2[Symbol.isConcatSpreadable] = true; +let a3 = a1.concat(a2); +console.log(a3); // [ 1, 2, 3, 4, 5, 6 ] +``` + +```js +a1[Symbol.isConcatSpreadable] = false; +a2[Symbol.isConcatSpreadable] = false; +let a3 = a1.concat(a2); +console.log(a3); // [ [ 1, 2, 3 ], [ 4, 5, 6 ] ] +``` + +```js +a1[Symbol.isConcatSpreadable] = true; +a2[Symbol.isConcatSpreadable] = false; +let a3 = a1.concat(a2); +console.log(a3); // [ 1, 2, 3, [ 4, 5, 6 ] ] +``` + +```js +a1[Symbol.isConcatSpreadable] = false; +a2[Symbol.isConcatSpreadable] = true; +let a3 = a1.concat(a2); +console.log(a3); // [ [ 1, 2, 3 ], 4, 5, 6 ] +``` + +```js +let likedArray = { + 0: "a", + 1: "b", + length: 2, + [Symbol.isConcatSpreadable]: true +}; +let arr = [ "A", "B" ].concat(likedArray); +console.log(arr); // [ 'A', 'B', 'a', 'b' ] +``` + +## การใช้งาน Symbol.match, Symbol.matchAll, Symbol.replace, Symbol.search, Symbol.split + +```js +console.log(typeof RegExp.prototype[Symbol.match]); // "function" +console.log(typeof RegExp.prototype[Symbol.matchAll]); // "function" +console.log(typeof RegExp.prototype[Symbol.replace]); // "function" +console.log(typeof RegExp.prototype[Symbol.search]); // "function" +console.log(typeof RegExp.prototype[Symbol.split]); // "function" +``` + +```js +let regexObj = { + [Symbol.match](value) { + return value.match(/Hello/); // จับคู่คำว่า "Hello" + }, + [Symbol.replace](value, replacement) { + return value.replace(/!/, replacement); // แทนที่คำว่า "!" ด้วย replacement + }, + [Symbol.search](value) { + return value.search(/World/); // ค้นหาคำว่า "World" + }, + [Symbol.split](value) { + return value.split(/\s/); // ใช้ช่องว่างเป็นตัวแบ่ง + } +} +let str = "Hello World !"; +// เรียกเมธอด [Symbol.match] +console.log(str.match(regexObj)); // [ 'Hello', index: 0, input: 'Hello World !', groups: undefined ] +// เรียกเมธอด [Symbol.replace] +console.log(str.replace(regexObj, "?")); // "Hello World ?" +// เรียกเมธอด [Symbol.search] +console.log(str.search(regexObj)); // 6 +// เรียกเมธอด [Symbol.split] +console.log(str.split(regexObj)); // [ 'Hello', 'World', '!' ] +``` + +```js +let regexObj = { + [Symbol.matchAll](value) { + return value.matchAll(/o/g); // จับคู่ตัวอักษร "o" + } +} +let str = "Hello World !"; +result = str.matchAll(regexObj) +for (const m of result) { + console.log(m); +} +// แสดงผลลัพธ์ +// [ 'o', index: 4, input: 'Hello World !', groups: undefined ] +// [ 'o', index: 7, input: 'Hello World !', groups: undefined ] +``` + +## การใช้งาน Symbol.species + +```js +class MyArray extends Array { +} +let array = new MyArray(1, 2, 3, 4); +let newarray = array.filter((item) => item > 2); +console.log(newarray); // [ 3, 4 ] +console.log(array instanceof MyArray); // true +console.log(newarray instanceof Array); // true +console.log(newarray instanceof MyArray); // true +``` + +```js +class MyArray extends Array { + static get [Symbol.species]() { + return Array; + } +} +let array = new MyArray(1, 2, 3, 4); +let newarray = array.filter((item) => item > 2); +console.log(newarray); // [ 3, 4 ] +console.log(array instanceof MyArray); // true +console.log(newarray instanceof Array); // true +console.log(newarray instanceof MyArray); // false +``` + +```js +class MyArray extends Array { + static get [Symbol.species]() { + return this; + } +} +let array = new MyArray(1, 2, 3, 4); +let newarray = array.filter((item) => item > 2); +console.log(newarray); // [ 3, 4 ] +console.log(array instanceof MyArray); // true +console.log(newarray instanceof Array); // true +console.log(newarray instanceof MyArray); // true +``` + +```js +class MyClass { + static get [Symbol.species]() { + console.log("Symbol.species is called"); + return this; + } + constructor(value) { + this.value = value; + } + create() { + return new this.constructor[Symbol.species](this.value); + // เสมือนเขียน return new MyClass(this.value); + } +} +let obj = new MyClass(); +obj.create(); // "Symbol.species is called" +``` + +หมายเหตุ ตัวอย่างต่อไปนี้ อย่าลืมก็อปปี้ MyClass จากตัวอย่างก่อนหน้ามาใช้ด้วย + +```js +class MyClass2 extends MyClass { +} +let obj = new MyClass2(); +let obj2 = obj.create(); // "Symbol.species is called" +console.log(obj2 instanceof MyClass); // true +console.log(obj2 instanceof MyClass2); // true +``` + +```js +class MyClass2 extends MyClass { + static get [Symbol.species]() { + console.log("Symbol.species is overrided"); + return MyClass; + } +} +let obj = new MyClass2(); +let obj2 = obj.create(); // "Symbol.species is overrided" +console.log(obj2 instanceof MyClass); // true +console.log(obj2 instanceof MyClass2); // false +``` + +## การใช้งาน Symbol.toPrimitive + +```js +let a = {}; +console.log("It is " + a) // "It is [object Object]" +``` + +```js +let two = new Number(2); // ประกาศอ็อบเจ็กต์ Number +console.log(two.toString()); // "2" +console.log(two.valueOf()); // 2 +// เบื้องหลังจะเรียกใช้ two.valueOf() ให้กลายเป็น 2 ก่อน แล้วถึงคูณด้วย 5 จึงกลายเป็น 2 * 5 +console.log(two * 5); // 10 +``` + +```js +let obj = {}; +console.log(obj.toString() ); // "[object Object]" +console.log(obj.valueOf()); // {} +// เบื้องหลังจะเรียกใช้ obj.toString() +console.log(String(obj)); // "[object Object]" +``` + +```js +let date = new Date("12-30-2023"); +console.log(date.toString()); // "Sat Dec 30 2023 00:00:00 GMT+0700 (Indochina Time)" +console.log(date.valueOf()); // 1703869200000 +// เบื้องหลังจะเรียกใช้ date.toString() +console.log(new Date(date)); // "Sat Dec 30 2023 00:00:00 GMT+0700 (Indochina Time)" +``` + +```js +class ThaiCurrency { + constructor(amount) { + this.amount = amount; + } +} +let obj = new ThaiCurrency(); +console.log(obj[Symbol.toPrimitive]); // undefined +``` + +```js +class ThaiCurrency { + constructor(amount){ + this.amount = amount; + } + [Symbol.toPrimitive](hint) { + switch (hint) { + case "number": + return this.amount; + case "string": + return this.amount + " Baht"; + case "default": + return this.amount + " THB"; + } // สิ้นสุด switch + } // สิ้นสุดประกาศเมธอด +} // สิ้นสุดประกาศคลาส +let money = new ThaiCurrency (100); +console.log(money / 2); // 50 -- case "number": +console.log(String(money)); // "100 Baht" -- case "string" +console.log("Price " + money); // "Price 100 THB" -- case "default" +console.log(money == "100 THB"); // true -- case "default" +``` + +```js +function ThaiCurrency(amount) { + this.amount = amount; +} +ThaiCurrency.prototype[Symbol.toPrimitive] = function(hint) { + switch (hint) { + case "number": + return this.amount; + case "string": + return this.amount + " Baht"; + case "default": + return this.amount + " THB"; + } // สิ้นสุด switch +}; // สิ้นสุดประกาศเมธอด +let money = new ThaiCurrency (100); +console.log(money / 2); // 50 -- case "number": +console.log(String(money)); // "100 Baht" -- case "string" +console.log("Price " + money); // "Price 100 THB" -- case "default" +console.log(money == "100 THB"); // true -- case "default" +``` + +## การใช้งาน Symbol.toStringTag + +```js +obj = { } +obj[Symbol.toStringTag] = "My Object"; +console.log(obj.toString()); // "[object My Object]" +console.log(Object.prototype.toString.call(obj)); // "[object My Object]" +``` + +```js +class MyClass { } +let obj = new MyClass(); +console.log(obj[Symbol.toStringTag]); // undefined +console.log(obj.toString()); // "[object Object]" +``` + +```js +class MyClass { + constructor() { + this[Symbol.toStringTag] = "MyClass"; + } +} +let obj = new MyClass(); +console.log(obj[Symbol.toStringTag]); // "MyClass" +console.log(obj.toString()); // "[object MyClass]" +// toString() เบื้องหลังจะเก็บไว้ที่ MyClass.Prototype.toString() +// ซึ่งสืบทอดมาจาก Object.prototype.toString() +console.log(obj.toString === MyClass.prototype.toString); // true +console.log(obj.toString === Object.prototype.toString); // true +// กำหนดให้ this ชี้ไปยัง obj +console.log(Object.prototype.toString.call(obj)); // "[object MyClass]" +``` + +```js +class MyClass { + constructor() { + this[Symbol.toStringTag] = "MyClass"; + } + toString() { // โอเวอร์ไลด์เมธอด Object.prototype.toString + return "This is MyClass"; // ไม่ได้แสดงข้อความ "[object MyClass]" + } +} +let obj = new MyClass(); +console.log(obj.toString()); // "This is MyClass" +console.log(Object.prototype.toString.call(obj)); // "[object MyClass]" +``` + +```js +function MyConstructor() { } +MyConstructor.prototype[Symbol.toStringTag] = "My Object"; +let obj = new MyConstructor(); +console.log(obj.toString()); // "[object My Object]" +console.log(Object.prototype.toString.call(obj)); // "[object My Object]" +``` + +```js +Array.prototype[Symbol.toStringTag] = "My Array"; +let myarray = [1, 2, 3]; +console.log(Object.prototype.toString.call(myarray)); // "[object My Array]" +// ต่อไปจะทำการเรียก toString() ของอาร์เรย์ +console.log(myarray.toString()) // "1,2,3" +console.log(Array.prototype.toString.call(myarray)) // "1,2,3" +``` + +## การใช้งาน Symbol.unscopables + +```js +Array.prototype[Symbol.unscopables] = Object.assign(Object.create(null), { // โดยดีฟอลต์ + copyWithin: true, + entries: true, + fill: true, + find: true, + findIndex: true, + keys: true, + values: true +}); +``` + +```js +let array = [4, 3, 2, 1]; +console.log(array); // [ 4, 3, 2, 1 ] +with(array) { + sort(); // เรียงลำดับอาร์เรย์ใหม่จากน้อยไปหามาก + console.log(typeof copyWithin); // "undefined" +} +console.log(array); // [ 1, 2, 3, 4 ] +``` + +```js +let obj = { + foo: "foo value", + bar: "bar value" +} +with(obj) { + console.log(foo); // "foo value" + console.log(bar); // "bar value" +} +``` + +```js +let obj = { + foo: "foo value", + bar: "bar value" +} +obj[Symbol.unscopables] = Object.assign(Object.create(null), { + foo: true // ห้ามใช้ foo ในบล็อกของประโยคคำสั่ง with +}); +with(obj) { + console.log(typeof foo); // "undefined" + console.log(bar); // "bar value" +} +``` + + diff --git a/examples_book/Chapter02.md b/examples_book/Chapter02.md new file mode 100644 index 0000000..ba6f4da --- /dev/null +++ b/examples_book/Chapter02.md @@ -0,0 +1,281 @@ +# โค้ดบทที่ 2 รันจาวาสคริปต์อย่างง่าย + +## จาวาสคริปต์บนเว็บเบราเซอร์ + +### วิธีแทรกจาวาสคริปต์ลงในไฟล์ HTML + +```html + + + + + + +

+
+

+ + + +``` + +### ตัวอย่างการวางจาวาสคริต์ใน <body> ...</body> + +```html + + + + + +

Hello, world!

+ + +``` + +```html + + + + + +

+ + + +``` + +### ตัวอย่างการวางจาวาสคริต์ใน <head> ...</head> + +```html + + + + + + + +

Hello, World!

+ + +``` + +หมายเหตุ ตัวอย่างนี้จะเกิด error (ต้องดูที่หน้าคอนโซล) +```html + + + + + + + +

+ + +``` + +```html + + + + + + + +

+ + + +``` + +### ตัวอย่างการใช้งาน ES6 ในฝั่งเว็บเบราเซอร์ + +```html + + + + + +

+ + + +``` + +### แยกไฟล์จาวาสคริปต์ ออกจาก HTML + + +* [ไฟล์ myScript_1.js](Chapter03/myScript_1.js) + +* [ไฟล์ myScript_2.js](Chapter03/myScript_2.js) + +```html + + + + + + + + + +``` + +## จาวาสคริปต์ ES บนเว็บเบราเซอร์รุ่นเก่า + +### ตัวอย่างการใช้งาน Traceur + +```tab.html + + + + + + + + + +

+ + + +``` + +### ตัวอย่างการใช้งาน Babel + +```tab.html + + + + + + + +

+ + + +``` + +## จาวาสคริปต์นอกเว็บเบราเซอร์ด้วย Node.js + +### รันจาวาสคริปต์นอกเว็บเบราเซอร์ จากไฟล์นามสกุล .js ตามลำพัง + +```js +class Chat { + constructor(message) { + this.message = message; + } + say() { + console.log(this.message); + } +}; +let chat = new Chat("Hello, world!"); +chat.say(); +``` + +### ตัวอย่างการรันจาวาสคริปต์ให้กลายเป็นเซิร์ฟเวอร์ + +```js +var http = require('http'); +http.createServer(function (request, response) { + response.writeHead(200, {'Content-Type': 'text/plain'}); + response.end("Hello, world!"); +}).listen(8001, '127.0.0.1'); +console.log('Server running at http://127.0.0.1:8001/'); +``` + +## จาวาสคริปต์นอกเว็บเบราเซอร์ด้วย Deno + +```js +import { serve } from "https://deno.land/std@0.97.0/http/server.ts"; +const s = serve({ port: 8000 }); +console.log("http://localhost:8000/"); +for await (const req of s) { + req.respond({ body: "Hello, world!" }); +} +``` + +## เครื่องมือในการดีบั๊ก + +```html + + + + + +

+ + + +``` diff --git a/examples_book/Chapter03.md b/examples_book/Chapter03.md new file mode 100644 index 0000000..d9d1c60 --- /dev/null +++ b/examples_book/Chapter03.md @@ -0,0 +1,1129 @@ +# โค้ดบทที่ 3 ทบทวนมาตรฐานเก่า + +## คอมเมนต์ +```js +var x = 10; //This is an example. +``` + +```js +/* This is an example +ECMAScript 6 is very easy*/ +var x = 10; +``` + +## console.log() +```html + + + + +

Hello, world!

+ + + +``` + +```js +console.log("Hello world", 122, 333.333); // "Hello world 122 333.333" +``` + +### debugger +```js +console.log("line 1"); // "line 1" +debugger; // บรรทัดนี้เป็นตำแหน่งของ breakpoint เพื่อหยุดการทำงานของโปรแกรมชั่วขณะ ตอนดีบั๊กโปรแกรม +console.log("line 2"); // "line 2" +``` + +## การใช้เซมิโคลอน (;) +```js +console.log("Hello world"); +``` + +```js +console +.log +( +"Hello world" +); +``` + +```js +; +; +; +``` + +```js +;;;;; +``` + +```js +console.log("Hello world") +console +.log +( +"Hello world" +) +``` + +```js +var a = 1 +var b = 5 +var c = a + b +(a * b) +``` + +```js +var a = 1; +var b = 5; +var c = a + b(a * b); +``` + +```js +var a = 1 + 2 +-3 + 10 +console.log(a) // 10 +``` + +```js +var a = 1 + 2 -3 + 10; +console.log(a); // 10 +``` + +```js +function todo() { + return { + "foo" : 1 + }; +} +``` + +```js +function todo() { + return + { + "foo" : 1 + }; +} +``` + +## เส้นทางการทำงานโปรแกรม + +```js +console.log(1); +console.log(2); +console.log(3); +console.log(4); +``` + +```js +console.log(1); console.log(2); console.log(3); console.log(4); +``` + +```js +console.log(1) console.log(2) console.log(3) console.log(4) +``` + +## การตั้งชื่อ +```js +var _ = 100; // ตั้งชื่อแบบนี้ได้ +var $ = 100; // ตั้งชื่อแบบนี้ได้ +var _a =100; // ตั้งชื่อแบบนี้ได้ +var $a =100; // ตั้งชื่อแบบนี้ได้ +var Abc = 100; // ตั้งชื่อแบบนี้ได้ +var abc = 100; // ตั้งชื่อแบบนี้ได้ แต่ทว่าตัวแปร Abc กับ abc จะถือว่าคนละชื่อกัน +var a0123 = 100; // ตั้งชื่อแบบนี้ได้ +var 9b = 100; // ทำไม่ได้ มันจะเกิด SyntaxError เพราะมีเลข 9 นำหน้าชื่อตัวแปร +var สวัสดีครับ = 100; // ตั้งชื่อภาษาไทยแบบนี้ได้ แต่ส่วนใหญ่นิยมตั้งชื่อเป็นภาษาอังกฤษ) +``` + +## คำสงวน + +```js +var let = -1; // ทำไม่ได้ มันจะเกิด SyntaxError เพราะ let เป็นคำสงวน +var this= -10; // ทำไม่ได้ มันจะเกิด SyntaxError เพราะ this เป็นคำสงวน +``` + +```js +var Date = 100; +console.log(Date); // แสดงผลลัพธ์ 100 +``` + +```js +var Date = 100; +console.log(Date); // แสดงผลลัพธ์ 100 +var d = new Date(); +console.log(d); +``` + +## การประกาศตัวแปร +```js +var x = 100; +``` + +```js +var x = 1, y = 2, z = 3; // ประกาศตัวแปร x, y และ z ให้อยู่ในบรรทัดเดียวกัน +``` + +```js +var x = 1; +var y = 2; +var z = 3; +``` + +```js +var x= 1, y = x; +/* จะเสมือนเขียนซอร์สโค้ดแบบนี้ +var x = 1; +var y = x; +*/ +``` + +```js +var x; +console.log(x); // undefined +``` + +* หมายเหตุ แสดงผลลัพธ์เหมือนรันใน Node.js +```js +var undefined = 55555; +console.log(undefined); // บนเว็บบราวเซอร์จะแสดงเป็น undefined แต่บน Node.js จะแสดงเป็น 55555 +var x; +console.log(x); // undefined +``` + +### การประกาศตัวแปรโดยไม่มี var +```js +x = 1; +``` + +* หมายเหตุ ควรรันคำส่ง delete x; ก่อน เพื่อลบตัวแปร x = 1; ที่รันก่อนหน้านี้ +```js +console.log(x); // ReferenceError +``` + +```js +var x; // ประกาศตัวแปร แต่ยังไมีการกำหนดค่าให้ x จึงมีค่าเป็น undefined +console.log(x); // undefined +``` + + +## ไดนามิกไทป์ +```js +var foo = 42; // เริ่มต้นตัวแปร foo จะมีชนิดข้อมูลเป็นตัวเลข +foo = "bar"; // ภายหลัง foo เปลี่ยนมาเก็บข้อมูลเป็นสตริง +foo = true; // ภายหลัง foo เปลี่ยนมาเก็บข้อมูลเป็นบูลีน +``` + +## ข้อมูล + +### null กับ undefined + +```js +console.log(null === undefined) // false +console.log(null == undefined) // true +console.log(typeof null) // "object" +console.log(typeof undefined) // "undefined" +``` + +### ตัวเลข +```js +console.log(100, 0, -0, -300, 3.14, -78.222); // 100 0 -0 -300 3.14 -78.222 +``` + +```js +console.log(200e5); // 200 x 105 = 20000000 +console.log(2E-5); // 2 x 10-5 = 0.00002 +``` + +```js +console.log(Math.sqrt(-1)); // NaN (ไม่สามารถถอดรากที่สองของ -1) +console.log(0/0); // NaN ( 0 หาร 0 ไม่มีนิยาม) +console.log(parseInt("Hi")); // NaN (ไม่สามารถแปลง "Hi" เป็นตัวเลขได้) +``` + +```js +console.log(Infinity * Infinity); // Infinity +console.log(Infinity / Infinity); // NaN +console.log(-344 * Infinity); // -Infinity +console.log(3 / Infinity); // 0 +``` + +```js +console.log(Number.MAX_VALUE); // 1.7976931348623157e+308 โดยประมาณ +console.log(Number.MAX_VALUE + 100); // 1.7976931348623157e+308 โดยประมาณ (บรรทัด 2) +console.log(Number.MAX_VALUE * 10); // Infinity (บรรทัด 3) +console.log(Number.MAX_VALUE * -10); // -Infinity (บรรทัด 4) +``` + +```js +console.log(Number.MIN_VALUE); // 5e-324 (เป็นค่าโดยประมาณ) +``` + +### สตริง +```js +"Learning JavaScript" +'Learning JavaScript' +``` + +```js +// ใช้ \" อยู่ภายในสตริง ส่วนเครื่องหมาย ' สามารถเขียนอยู่ในสตริงได้เลย +console.log("...\"Learning\" 'JavaScript'..."); // ..."Learning" 'JavaScript'... +// ใช้ \' อยู่ภายในสตริง ส่วนเครื่องหมาย " สามารถเขียนอยู่ในสตริงได้เลย +console.log('..."Learning" \'JavaScript\'...'); // ..."Learning" 'JavaScript'... +``` + +```js +var str= "line1\ +line2\ +line3"; +console.log(str); // line1line2line3 +``` + +```js +console.log("1234567890".length); // 10 +``` + +### บูลีน + +```js +var TRUE_value = true; +var FALSE_value = false +console.log(TRUE_value); // true +console.log(FALSE_value); // false +``` + +```js +-5; +-10.71; +"Learning JavaScript"; +true; +var a = -5; +var b = -10.71; +var c = "Learning JavaScript"; +var d = true; +``` + +## โอเปอเรเตอร์ + +### โอเปอเรเตอร์ทางคณิตศาสตร์ +```js +console.log(true + 0); // ได้ผลลัพธ์เป็น 1 เพราะ true จะถือว่ามีค่าเป็น 1 +console.log(false + false); // ได้ผลลัพธ์เป็น 0 เพราะ false จะถือว่ามีค่าเป็น 0 +console.log(100 - true); // 99 +console.log(true * 30); // 30 +console.log(true / 2); // 0.5 +console.log(true % 10); // 1 +console.log(+true); // 1 +console.log(-false); // -0 +var a = false, b=true; +console.log(++a); // 1 +console.log(--b); // 0 +``` + +```js +console.log(NaN + 30); // NaN +console.log(NaN - 30); // NaN +console.log(NaN * 30); // NaN +console.log(NaN / 30); // NaN +console.log(NaN % 30); // NaN +console.log(+NaN); // NaN +console.log(-NaN); // NaN +var x = NaN; +console.log(++x); // NaN +console.log(--x); // NaN +``` + +```js +console.log(Infinity * 0); // NaN +console.log(Infinity * 1); // Infinity +console.log(Infinity * Infinity); // Infinity +console.log(Infinity * -Infinity); // -Infinity +console.log(Infinity / 0); // Infinity +``` + +* การใช้งานโอเปอเรเตอร์ยกกำลัง (Exponentiation Operator) + +```js +var ans = 10 ** 2; // นำเลข 10 มายกกำลัง 2 ( 102 ) +console.log(ans); // 100 +// เสมือนใช้เมธอด Math.pow() ดังนี้ +console.log(ans === Math.pow(10, 2)); // true +``` + +* ลำดับของโอเปอเรเตอร์ ** + +```js +var ans = 3 * 10 ** 2; +console.log(ans); // 300 +``` + +```js +var ans = 3 * (10 ** 2); +console.log(ans); // 300 +``` +* ข้อเข้มงวดของโอเปอเรเตอร์ ** + +```js +var ans1 = -10 ** 2; // syntax error +var ans2 = +10 ** 2; // syntax error +``` + +```js +ans1 = -(10 ** 2); // -100 +ans1 = (-10) ** 2; // 100 +ans2 = +(10 ** 2); // 100 +ans2 = (+10) ** 2; // 100 +``` + +```js +var value1 = 9, value2 = 10; +// ใช้งานโอเปอเรเตอร์ ++ แบบ prefix +// ค่าของ value1 ถูกบวกด้วยหนึ่ง ก่อนที่จะยกกำลัง 2 +console.log(++value1 ** 2); // 100 +console.log(value1); // 10 +// ใช้งานโอเปอเรเตอร์ ++ แบบ postfix +// หลังจากยกกำลัง 2 ไปแล้ว ค่าของ value2 จึงถูกบวกด้วยหนึ่งทีหลัง +console.log(value2++ ** 2); // 100 +console.log(value2); // 11 +``` + +```js +var value1 = 11, value2 = 10; +// ใช้งานโอเปอเรเตอร์ -- แบบ prefix +// ค่า value1 ถูกลบด้วยหนึ่ง ก่อนที่จะยกกำลัง 2 +console.log(--value1 ** 2); // 100 +console.log(value1); // 10 +// ใช้งานโอเปอเรเตอร์ -- แบบ postfix +// หลังจากยกกำลัง 2 ไปแล้ว ค่าของ value2 จึงถูกลบด้วยหนึ่งทีหลัง +console.log(value2-- ** 2); // 100 +console.log(value2); // 9 +``` + +### โอเปอเรเตอร์ที่ใช้กำหนดค่าให้กับตัวแปร + +```js +var a = 20; +a +=true; // true มีค่าเป็น 1 +console.log(a); // 21 +a *=NaN; +console.log(a); // NaN +``` + +### โอเปอเรเตอร์ที่ใช้กับสตริง + +```js +100 + true + "50"; // "10150" +// เสมือนเขียน (100 + true) + "50" +``` + +```js +20 / "10" + "76"; // 276 +// เสมือนเขียน ( 20 / "10") + "76" +``` + +### โอเปเรอเตอร์แบบตรรกะ +```js +console.log(true || true); // true (เงื่อนไข short circuit) +console.log(true || false); // true (เงื่อนไข short circuit) +console.log(false || true); // true +console.log(false || false); // false +console.log(true && true); // true +console.log(true && false); // false +console.log(false && true); // false (เงื่อนไข short circuit) +console.log(false && false); // false (เงื่อนไข short circuit) +console.log(!true); // false +console.log(!false); // true +``` + +### โอเปอเรเตอร์ระหว่างบิต + +* ตัวอย่าง Bitwise AND +```js +var a = 12; // 1100 (เลขฐานสอง) +var b = 5; // 0101 (เลขฐานสอง) +var c = a & b; // 0100 (เลขฐานสอง) +console.log(c.toString(2)); // 100 (เลขฐานสอง) +console.log(c); // 4 +console.log(12 & 5); // 4 +``` + +* ตัวอย่าง Bitwise OR + +```js +var a = 12; // 1100 +var b = 5; // 0101 +var c = a | b; // 1101 +console.log(c.toString(2)); // 1101 +console.log(c); // 13 +console.log(12 | 5); // 13 +``` + +* ตัวอย่าง Bitwise XOR + +```js +var a =12; // 1100 +var b= 5; // 0101 +var c = a ^ b; // 1001 +console.log(c.toString(2)); // 1001 +console.log(c); // 9 +console.log(12 ^ 5); // 9 +``` + +* ตัวอย่าง Bitwise NOT + +```js +var a= 9; // 00000000000000000000000000001001 +var b = ~a; // 11111111111111111111111111110110 (1's Complement) +console.log(b); // -10 +console.log(~9); // -10 +``` + +* ตัวอย่าง การเลื่อนบิตไปทางซ้ายมือ + +```js +var a = 9; // 00000000000000000000000000001001 +var c = a << 2; // เลื่อนบิตจากขวามือไปทางซ้ายมือ 2 ตำแหน่ง + // 00000000000000000000000000100100 +console.log(c); // 36 +console.log(9 << 2); // 36 +``` + +* ตัวอย่าง การเลื่อนบิตไปทางขวามือ + +```js +var a = 9; // 00000000000000000000000000001001 +var c = a >> 2; // เลื่อนบิตจากซ้ายมือไปทางขวามือ 2 ตำแหน่ง + // 00000000000000000000000000000010 +console.log(c); // 2 +console.log(9 >> 2); // 2 +``` + +```js +-9; // 11111111111111111111111111110111 (เลขฐานสอง) +-9 >> 2; // 11111111111111111111111111111101 (เลขฐานสอง) = -3 (เลขฐานสิบ) +``` + +```js +var a = 9; // 00000000000000000000000000001001 +var c = a >>> 2; // เลื่อนบิตจากซ้ายมือไปทางขวามือ 2 ตำแหน่ง พร้อมเติมเลข 0 ที่บิตด้านหน้าสุด + // 00000000000000000000000000000010 +console.log(c); // 2 +console.log(9 >>> 2); // 2 +``` + +### โอเปอเรเตอร์ typeof + +```js +console.log(typeof true); // "boolean" +console.log(typeof false); // "boolean" +console.log(typeof -0.13); // "number" +console.log(typeof NaN); // "number" +console.log(typeof Infinity); // "number" +console.log(typeof undefined); // "undefined" +console.log(typeof ''); // "string" +console.log(typeof "Hi"); // "string" +console.log(typeof (typeof 100) ); // "string" +console.log(typeof null); // "object" +console.log(typeof {x: 1, y: 2}); // "object" +console.log(typeof [1, 2]); // "object" +console.log(typeof function(){ }); // "function" +console.log(typeof Math.sqrt); // "function" +console.log(typeof class C { }); // "function" +console.log(typeof Symbol()); // "symbol" +``` + +### โอเปอเรเตอร์วงเล็บ + +```js +var a = 1 + 2 * 3 + 5; +// จะเสมือนเขียนเป็น var a = 1 + (2 * 3) + 5; +console.log(a); // 12 +``` + +```js +var a = (1 + 2) * (3 + 5); +console.log(a); // 24 +``` + +### โอเปอเรเตอร์คอมม่า + +```js +var a = 1, b = 2; +var x = (1+34, a+=2, b*=10, b+1); +console.log(x); // 21 +``` + +### โอเปอเรเตอร์ void + +```js +var a = void 12; +console.log(a); // undefined +console.log(Math.ceil(4.4)); // 5 +console.log(void Math.ceil(4.4)); // undefined +var b = 1; +console.log(void (++b)); // undefined +console.log(b); // แสดงค่าออกมาเป็น 2 เพราะตัวแปร b ถูกบวกเพิ่มไป 1 ค่า +``` + +## อาร์เรย์ +```js +[1, 1, 1, true, "Array"]; // อาร์เรย์ +``` + +```js +var a = ["a", "b", "c", "d", "e"]; +console.log(typeof a); // "object" +console.log(a[0], a[1], a[2], a[3], a [4]); // "a b c d e" +``` + +```js +var array = [ ]; // ประกาศเป็นอาร์เรย์ว่าง +array[0] = 1; +array[1] = 2; +``` + +```js +var array = [1, 2, 3, 4, 5]; +console.log(array.length); // 5 +``` + +```js +var array = [1, 2, 3, 4, 5]; +console.log(array.length); // 5 +array.length = 7; // เพิ่มขนาดอาร์เรย์จาก 5 เป็น 7 +console.log(array); // [ 1, 2, 3, 4, 5, <2 empty slots> ] +console.log(array.length); // 7 +array[9] = 100; +console.log(array); // [ 1, 2, 3, 4, 5, <4 empty slots>, 100 ] +console.log(array.length); // 10 +``` + +## การประกาศฟังก์ชัน + +```js +function calculate(param1, param2) { + return param1 * param2; +} + +var result = calculate(10, 2); +console.log(result); // 20 +``` + +```js +function calculate() { + return 20; +} +var result = calculate(); +console.log(result); // 20 + +calculate(); // คืนค่า 20 +calculate(); // คืนค่า 20 +calculate(); // คืนค่า 20 +``` +## ประโยค return + +```js +function myFunction() { + return 1; + console.log("myFunction"); // บรรทัดนี้เส้นทางการทำงานของโปรแกรมจะมาไม่ถึง +} +var result = myFunction(); +console.log(result); // 1 +``` + +```js +function myFunction() { + return; +} +var result = myFunction(); +console.log(result); // undefined +``` + +```js +function myFunction() { + console.log("myFunction"); + // จะเสมือนมีประโยค return undefined; วางไว้ตำแหน่งสุดท้าย ก่อนฟังก์ชันจบการทำงาน +} +var result = myFunction(); // "myFunction" +console.log(result); // undefined +``` + +```js +var result = calculate(); +console.log(result); // 20 +function calculate() { + return 20; +} +``` + +### ฟังก์ชันไร้ชื่อ + +```js +function (param1, param2) { // ถ้ารันจะเกิด error + return param1 * param2; +} +``` + +### นิพจน์ฟังก์ชัน + +```js +var calculate = function (param1, param2) { + return param1 * param2; +} +console.log(calculate(10, 2)); // 20 +calculate = 100; // ตัวแปร calculation สามารถแก้ไขให้เป็นค่าอื่นได้ +console.log(calculate); // 100 +``` + +```js +var calculate = function calc2(param1, param2) { + return param1 * param2; +} +console.log(calculate(10, 2)); // 20 +``` + +```js +function cal(a, b) { + console.log(a * b); +} +``` + +```js +cal = function(a, b) { + console.log(a*b); +} + +var myFunction = cal; + +myFunction(5,4); // 20 +cal(5,4); // 20 +``` + +### ฟังก์ชันคอลแบ็ค + +```js +function sayHi() { + console.log("Hi"); +} +function sayBye() { + console.log("Bye"); +} +function say(func) { + func(); // เรียกฟังก์ชันให้ทำงาน +} +say(sayHi); // "Hi" +say(sayBye); // "Bye" +``` + +### รีเทิร์นออกมาเป็นฟังก์ชัน + +```js +function say(func) { + console.log("Say..."); + function sayHi() { + console.log("Hi"); + } + return sayHi; // รีเทิร์นฟังก์ชัน +} // สิ้นสุดการประกาศฟังก์ชัน +var hi = say(); // "Say..." +hi(); // "Hi" +``` + +```js +function say(func) { + console.log("Say..."); + return function() { // รีเทิร์นฟังก์ชันไร้ชื่อ + console.log("Hi"); + } +} // สิ้นสุดการประกาศฟังก์ชัน +var hi = say(); // "Say..." +hi(); // "Hi" +``` + +### อ็อบเจ็กต์ arguments + +```js +function myFunction(param1, param2) { + console.log(param1, param2); +} +myFunction(); // undefined undefined +myFunction(100); // 100 undefined +myFunction(100,200); // 100 200 +myFunction(100,200,300,400); // 100 200 +``` + +```js +function myFunction (param1, param2) { + console.log(arguments); +} +myFunction(100,200,300,400); // [Arguments] { '0': 100, '1': 200, '2': 300, '3': 400 } +``` + +### ข้อควรระวังในการประกาศฟังก์ชัน + +* 1) +```js +function myFunction(param1, param2) { // ฟังก์ชันนี้ไม่เคยถูกเรียกใช้งาน + console.log("function1 value:", param1, param2); +} +myFunction(100, 200); // เรียกใช้ฟังก์ชันที่ประกาศอยู่ด้านล่าง +function myFunction(param) { // จะโอเวอร์ไรด์ทับฟังก์ชันที่ประกาศไว้ก่อนหน้านี้ + console.log("function2 value:", param); +} +myFunction(100); +myFunction(100, 200); +/* แสดงผลลัพธ์เป็น +"function2 value: 100" +"function2 value: 100" +"function2 value: 100" */ +``` + +* 2) +```js +alert = function(data) { // alert ถูกกำหนดให้เป็นฟังก์ชันตัวใหม่ + console.log(data); // แสดงข้อความออกทางหน้าคอนโซล +} +alert("Hello, World"); // แสดงข้อความ "Hello, World" ออกทางหน้าคอนโซล +``` + +* 3) +```js +function todo(a, a, a) { // ประกาศพารามิเตอร์ชื่อ a ซ้ำกัน + console.log(a); +} +todo(1, 5, 10); // 10 +``` + +### ขอบเขตการมองเห็นของตัวแปร + +```js +if(true) { + var a = 1; // a มีขอบเขตการมองเห็นแบบโกลบอล +} +{ + var b = 2; // b มีขอบเขตการมองเห็นแบบโกลบอล +} +console.log(a, b); // 1 2 +``` + +```html + + + + + + + +``` + +```js +console.log(NaN, undefined, Infinity); // NaN undefined Infinity +console.log(window.NaN, window.undefined, window.Infinity); // NaN undefined Infinity +// ถ้ารันอยู่ใน Node.js +// console.log(global.NaN, global.undefined, global.Infinity); +// NaN undefined Infinity +``` + +### ฟังก์ชันซ้อนฟังก์ชัน + +```js +function outerFunc() { + var value = 0; + function innerFunc() { + console.log(++value); + } + return innerFunc; +} +var func1 = outerFunc(); // บรรทัด a +func1(); // 1 +func1(); // 2 +var func2 = outerFunc(); // บรรทัด b +func2(); // 1 +func2(); // 2 +``` + +```js +function cumulative(num) { + n = num + return function(a) { + n += a + console.log("answer = ", n) + } +} +cumA= cumulative(100) +cumA(1) // "answer = 101" +cumA(1) // "answer = 102" +cumA(1) // "answer = 103" +cumB = cumulative(20) +cumB(-1) // "answer = 19" +cumB(-1); // "answer = 18" +cumB(-1); // "answer = 17" +``` + +```js +function part_cal(x) { + return function(y) { + return function(z) { + console.log(x+y+z); // บรรทัด a + } + } +} +part_cal(10)(20)(30) // 60 +``` + +```js +function cal(x, y, z) { + console.log(x + y + z) +} +cal(10, 20 , 30) // 60 +``` + + +## Hoist +```js +var value = 100; +``` + +```js +console.log(x) // เกิด error +x = 1 +/* แบบนี้ไม่เกิด error + x = 1 + console.log(x) */ +``` + +```js +console.log(x) // undefined +var x = 1 +``` + +```js +x = undefined // เสมือนลอยขึ้นไปข้างบน +console.log(x) // undefined +x = 1 +``` + +```js +function myFunction(num) { + // สามารถมองเห็นตัวแปร value + console.log(value); // undefined + if(num > 10) { + var value = num*10; // ประกาศตัวแปร value ที่ตรงนี้ แต่มองเห็นได้ทั่วฟังก์ชัน + /* ซอร์สโค้ด */ + } else { + // ถ้าเงื่อนไขประโยค if เป็นเท็จ ก็จะเข้ามาทำงานที่ else + // ซึ่งจะเห็นตัวแปร value มีค่าเป็น undefined + console.log(value); // undefined + } + // สามารถมองเห็นตัวแปร value ได้ หลังจากประโยค if …else ทำงานเสร็จสิ้น + console.log(value); +} +``` + +```js +function myFunction(num) { + var value; // ประกาศตัวแปร value โดยไม่มีค่าเริ่มต้น จึงทำให้มีค่าเป็น undefined + console.log(value); // undefined + if(num > 10) { + value = num*10; // บรรทัดนี้เป็นเพียงการกำหนดค่าให้กับตัวแปร value + /* ซอร์สโค้ด */ + } else { + console.log(value); // undefined + } + console.log(value); +} +``` + +```js +// สามารถมองเห็นตัวแปร value +console.log(value); // undefined +if(true) { + var value = 100; // ประกาศตัวแปรแบบ var +} +console.log(value); // 100 +``` + +```js +var value; // ประกาศตัวแปร value โดยไม่มีค่าเริ่มต้น จึงทำให้มีค่าเป็น undefined +console.log(value); // undefined +if(true) { + value = 100; // บรรทัดนี้เป็นเพียงการกำหนดค่าให้กับตัวแปร value +} +console.log(value); // 100 +``` + +```js +// มองเห็นฟังก์ชันก่อนการประกาศใช้งาน +myFunction(); // "Hoisted" +function myFunction() { + console.log("Hoisted"); +} +myFunction(); // "Hoisted" +``` + +```js +function outerFunc() { + innerFunc(); // มองเห็นฟังก์ชันก่อนการประกาศใช้งาน + function innerFunc() { + console.log("inner function"); + } +} +outerFunc(); // "inner function" +console.log(typeof innerFunc); // undefined +``` + +## สตริคท์โหมด + +```js +"use strict"; // ประกาศโหมดสตริคท์ ด้วยการเขียนไว้ที่ตอนต้นของไฟล์ +var x = 1; +``` + +```js +function myFunction() { + "use strict"; // เฉพาะฟังก์ชันนี้จะอยู่ในโหมดสตริคท์ + var x = 1; +} +``` + +* 1) + +* หมายเหตุ ถ้าไม่เกิด error ให้รันคำส่ง delete x; ก่อน เพื่อลบตัวแปร var x = 1; ที่รันก่อนหน้านี้ จะได้เกิด error ตามตัวอย่าง +```js +"use strict"; +x = 1; // เกิด error เพราะไม่ได้ประกาศตัวแปรแบบ var ถ้าอยู่ดี ๆ จะมากำหนดค่าให้ทันทีแบบนี้จะทำไม่ได้ +``` + +* 2) + +```js +"use strict"; +function x(a, a) { }; // เกิด error เพราะประกาศพารามิเตอร์ ที่มีชื่อ a ซ้ำกัน +``` + +* 3) + +```js +"use strict"; +var x = 1; +delete x; // เกิด error ไม่สามารถลบตัวแปรได้ +``` + +* 4) + +```js +"use strict"; +delete Object.prototype; //เกิด error เพราะพร็อพเพอร์ตี้ตัวนี้ห้ามลบ +``` + +* 5) + +```js +"use strict"; +var x = 010; // เกิด error ไม่สามารถประกาศแบบนี้ได้ +var y = \010; // เกิด error ไม่สามารถประกาศแบบนี้ได้ +``` + +* 6) + +```js +"use strict"; +var obj = { }; +Object.defineProperty(obj, "x", {value:0, writable:false}); +obj.x = 1; // เกิด error เนื่องจากมันเป็นพร็อพเพอร์ตี้ที่อ่านค่าได้อย่างเดียว +``` + +* 7) + +```js +"use strict"; +var obj = {get x() {return 0} }; +obj.x = 1; // เกิด error ไม่สามารถกำหนดค่าให้กับ x ได้ +``` + +* 8) + +```js +"use strict"; +var obj = { }; +Object.preventExtensions(obj); +obj.a= 1; // เกิด error ไม่สามารถเพิ่มพร็อพเพอร์ตี้เข้าไปในอ็อบเจ็กต์ได้ +``` + +* 9) + +```js +"use strict"; +function f() { return this; } +console.log(f()); // undefined +``` + +* 10) + +```js +"use strict"; +var eval = 1; // เกิด error ไม่สามารถใช้ชื่อ eval เป็นตัวแปร +``` + +* 11) + +```js +"use strict"; +var arguments = 1; // เกิด error ไม่สามา รถใช้ชื่อ arguments เป็นตัวแปร +``` + +* 12) + +```js +"use strict"; +with (Math) { a = cos(1) }; // เกิด error ไม่สามารถใช้ประโยคคำสั่ง with ได้ +``` + +* 13) + +```js +"use strict"; +eval("var x = 1;"); // ประกาศตัวแปร x ด้วย eval() +x = 2; // เกิด error +// แบบนี้จะไม่เกิด error +// eval("var x = 1; x = 2;"); // ไม่เกิด error +``` + +* 14) + +```js +"use strict"; +var implements =1; // เกิด error เพราะ implements คือคำสงวนในโหมดสตริคท์ +``` + +* 15) + +```js +"use strict"; +if(true) { + function myFunction1(){ } // ขอบเขตแบบโลคอล +} +{ + function myFunction2(){ } // ขอบเขตแบบโลคอล +} +console.log(typeof myFunction1); // undefined (ถ้าไม่ใช่โหมดสตริคท์จะแสดงค่าเป็น "function") +console.log(typeof myFunction2); // undefined (ถ้าไม่ใช่โหมดสตริคท์จะแสดงค่าเป็น "function") +``` + +```js +"use strict"; +var isStrict = (function() { return !this; })( ); +console.log(isStrict); // true +``` diff --git a/examples_book/Chapter03/js/myScript_1.js b/examples_book/Chapter03/js/myScript_1.js new file mode 100644 index 0000000..ade84ee --- /dev/null +++ b/examples_book/Chapter03/js/myScript_1.js @@ -0,0 +1 @@ +alert("Hello"); \ No newline at end of file diff --git a/examples_book/Chapter03/js/myScript_2.js b/examples_book/Chapter03/js/myScript_2.js new file mode 100644 index 0000000..e3c1dc9 --- /dev/null +++ b/examples_book/Chapter03/js/myScript_2.js @@ -0,0 +1 @@ +alert("Good Bye"); diff --git a/examples_book/Chapter04.md b/examples_book/Chapter04.md new file mode 100644 index 0000000..53e89c7 --- /dev/null +++ b/examples_book/Chapter04.md @@ -0,0 +1,491 @@ +# โค้ดบทที่ 4 ทบทวนประโยคคำสั่งเบื้องต้น + +## บล็อก + +บล็อก (Block) ในจาวาสคริปต์ คือการใช้เครื่องหมายปีกกาเปิดและปิด ({ …} ) เพื่อรวมกลุ่มประโยคคำสั่งต่าง ๆ โดยมันจะมีโครงสร้างดังต่อไปนี้ + + +```notrun +{ + ประโยคคำสั่ง_1; + ประโยคคำสั่ง_2; + ... + ประโยคคำสั่ง_n; +} +``` + +```js +{ + var a = 2; // ประโยคคำสั่ง 1 + a++; // ประโยคคำสั่ง 2 + console.log(a); // ประโยคคำสั่ง 3 +} +``` + +```js +if (true) { + var a = 2; // ประโยคคำสั่ง 1 + a++; // ประโยคคำสั่ง 2 + console.log(a); // ประโยคคำสั่ง 3 +} +``` +## ประโยควนลูป + +### ประโยคคำสั่ง while + +```notrun +while(condition) { + statement +} +// ถ้ามีประโยคคำสั่งในบอดี้เพียงตัวเดียว ก็สามารถเขียนสั้นๆ โดยไม่ต้องมีเครื่องหมาย {} ครอบ +while(condition) statement; +``` + +```js +var i = 0; +while (i < 3) { + console.log(i); + i++; +} +/* แสดงผลลัพธ์ +0 +1 +2 */ +``` + +### ประโยคคำสั่ง do …while +```notrun +do { + statement +} while(condition); +// ถ้ามีประโยคคำสั่งในบอดี้เพียงตัวเดียว ก็สามารถเขียนสั้นๆ โดยไม่ต้องมีเครื่องหมาย {} ครอบ +do + statement +while(condition); +``` + +```js +var i = 0; +do { + console.log(i); + i++; +} while (i < 3); +/* แสดงผลลัพธ์ +0 +1 +2 */ +``` + +### ประโยคคำสั่ง for +```notrun +for ([expr1]; [expr2]; [expr3]) { + statement +} +// ถ้ามีประโยคคำสั่งในบอดี้เพียงตัวเดียว ก็สามารถเขียนสั้นๆ โดยไม่ต้องมีเครื่องหมาย {} ครอบ +for ([expr1]; [expr2]; [expr3]) statement; +``` + +```js +for(var i=0; i<3; i++) { + console.log(i); +} +/* แสดงผลลัพธ์ +0 +1 +2 */ +``` + +### ประโยคคำสั่ง continue +```js +for(var i=0; i<3; i++) { + if(i<=1) { + continue; // ข้ามประโยคคำสั่งที่เหลือ ไปทำงานรอบถัดไปแทน + } + console.log(i); +} +// แสดงผลลัพธ์ +// 2 +``` + +### ประโยคคำสั่ง break + +```js +for(var i=0; i<3; i++) { + if(i==2) { + break; // ยุติการทำงานของ for + } + console.log(i); +} +/* แสดงผลลัพธ์ +0 +1 */ +``` + +### ประโยคคำสั่ง label + +```js +outer: for(var i=0; i < 2; i++) { // บรรทัด a + console.log("i: ", i); + for (var j=i; j < 2; j++ ) { + if ( j == 1) { + continue outer; // บรรทัด b + } + console.log("j: ", j); + } // สิ้นสุดประโยคคำสั่ง for +} // สิ้นสุดประโยคคำสตั่ง for ที่ได้ติดฉลาก outer: +/* แสดงผลลัพธ์ +"i: 0" +"j: 0" +"i: 1" */ +``` + +```js +outer: while(true) { // บรรทัด a + for (var i=0; i < 3; i++ ) { + if ( i == 1) { + break outer; // บรรทัด b + } + console.log("i: ", i); + } // สิ้นสุดประโยคคำสั่ง for +} // สิ้นสุดประโยคคำสั่ง while +// แสดงผลลัพธ์ +// "i: 0" +``` + +## ประโยคเลือกเส้นทางการทำงาน + +### ประโยคคำสั่ง if +```notrun +if (condition) { + statement +} +``` + +```notrun +if (condition) { + ประโยคคำสั่ง_1 +} else { + ประโยคคำสั่ง_2 +} +``` + +```notrun +if (เงื่อนไข_1) { + ประโยคคำสั่ง_1 +} else if (เงื่อนไข_2) { + ประโยคคำสั่ง_2 +} else if (เงื่อนไข_3) { + ประโยคคำสั่ง_3 +} +... +else { // มีหรือไม่มีก็ได้ + ประโยคคำสั่ง_N // เข้ามาทำงานก็ต่อเมื่อไม่ตรงกับเงื่อนไขใดๆ ใน if ก่อนหน้านี้เลย +} +``` + +```notrun +if (เงื่อนไข_1) + ประโยคคำสั่ง_1 +else if (เงื่อนไข_2) + ประโยคคำสั่ง_2 +... +else if (เงื่อนไข_N) + ประโยคคำสั่ง_N +else + ประโยคคำสั่ง_Z +``` + +```js +// var i = 1; +// var i = 2; +// var i = 3; +if(i == 1) + console.log("if statement"); +else if(i == 2) + console.log("else if statement"); +else + console.log("else statement"); +``` + +### ประโยคคำสั่ง switch +```notrun +switch (นิพจน์_switch) { + case value1: + // เมื่อนิพจน์_switch มีค่าตรงกับ value1 เส้นทางการทำงานก็จะเริ่มจากจุดนี้เป็นต้นไป + [ประโยคคำสั่ง] + [break;] + case value2: + // เมื่อนิพจน์_switch มีค่าตรงกับ value2 เส้นทางการทำงานก็จะเริ่มจากจุดนี้เป็นต้นไป + [ประโยคคำสั่ง] + [break;] +... + case valueN: + // เมื่อนิพจน์_switch มีค่าตรงกับ valueN เส้นทางการทำงานก็จะเริ่มจากจุดนี้เป็นต้นไป + [ประโยคคำสั่ง] + [break;] + default: + // เมื่อนิพจน์_switch ไม่ตรงกับค่าที่อยู่ด้านหลัง case ตัวใดเลย + // เส้นทางการทำงานก็จะเริ่มจากจุดนี้เป็นต้นไป + [ประโยคคำสั่ง] + [break;] +} +``` + +```js +// var i = 1; +// var i = 2; +// var i = 3; +switch (i) { + case 1: + console.log("case1"); // ถ้า i มีค่าเป็น 1 ก็จะมาทำประโยคนี้ + break; // ออกจากประโยค switch + case 2: + console.log("case2"); // ถ้า i มีค่าเป็น 2 ก็จะมาทำประโยคนี้ + break; // ออกจากประโยค switch + default: + console.log("case_default"); // ถ้า i ไม่ใช่ 1 กับ 2 ก็จะมาทำประโยคนี้ +} +``` + +```js +switch (1) { + case 1: + console.log("case1"); + case 2: + console.log("case2"); + default: + console.log("case_default"); +} +/* แสดงผลลัพธ์เป็น +"case1" +"case2" +"case_default" */ +``` + +```js +switch (1) { + case 1: + case 2: + default: + console.log("case_default"); +} +// แสดงผลลัพธ์เป็น +// "case_default" +``` + +### โอเปอเรเตอร์แบบเงื่อนไข +```notrun +เงื่อนไข ? นิพจน์_1 : นิพจน์_2 +``` + +```js +// var condition = true; +// var condition = false; +var value = condition ? "foo" : "bar"; +console.log(value); // "foo" หรือ "bar" +``` + +```js +// var condition = true; +// var condition = false; +var value; +if(condition) { + value = "foo"; +} else { + value = "bar"; +} +console.log(value); // "foo" หรือ "bar" +``` + +## ประโยคคำสั่งว่าง +```js +; +``` + +```js +while(true); // วนลูปไม่รู้จบ +// หรือจะใช้ในประโยค for +for(;;); // วนลูปไม่รู้จบ +``` + +## การจัดการความผิดพลาด + +```js +throw "Error"; // โยน exception เป็นชนิดข้อมูลสตริง +throw 100; // โยน exception เป็นชนิดข้อมูลตัวเลข +throw true; // โยน exception เป็นชนิดข้อมูลบูลีน +throw new Object(); // โยน exception เป็นชนิดข้อมูลอ็อบเจ็กต์ +throw new Error("Error") // โยน exception เป็นชนิดข้อมูลอ็อบเจ็กต์ +``` + +```js +console.log(x); // ReferenceError +``` + +```js +new Error(["ข้อความ error"]) +``` + +```js +throw new SyntaxError ("Syntax error"); +``` + +### ประโยคคำสั่งจัดการความผิดพลาด +```js +try { + console.log(x); // บรรทัด a เกิด error + x++; // บรรทัด b +} catch (e) { + console.log(typeof e); // "object" + console.log(e.message); // "x is not defined" + console.log(e.name); // "ReferenceError" +} +console.log("Last statement"); // บรรทัด c +/* แสดงผลลัพธ์เป็น +"object" +"x is not defined" +"ReferenceError" +"Last statement" */ +``` + +```js +try { + throw 42; // โยน exception ออกมาเอง + console.log(42); // เส้นทางการทำงานของโปรแกรมจะมาไม่ถึง +} catch (e) { + console.log(e); // 42 +} +console.log("Last statment"); +/* แสดงผลลัพธ์ +42 +"Last statment" */ +``` + +```js +try { + console.log(x); // บรรทัด a เกิด error +} catch (e) { + console.log(e.message); // "x is not defined" +} finally { + console.log("finally"); +} +console.log("Last statement"); +/* แสดงผลลัพธ์ +"x is not defined" +"finally"; +"Last statement" */ +``` + +```js +try { + console.log("no error"); // บรรทัด a ทำงานปกติ +} catch (e) { + console.error(e.message); +} finally { + console.log("finally"); +} +console.log("Last statement "); +/* แสดงผลลัพธ์ +"no error" +"finally"; +"Last statement" */ +``` + +```js +function foo() { + try { + return "foo"; + } finally { + console.log("finally"); + } +} +console.log(foo()); +/* แสดงผลลัพธ์ +"finally"; +"foo"; */ +``` + +```js +function foo() { + try { + console.log(x); // บรรทัด a เกิด error + return "foo"; + } finally{ + console.log("finally"); // บรรทัด b ทำงาน + } +} +console.log(foo()); // บรรทัด c ไม่เข้ามาทำงาน +/* แสดงผลลัพธ์ +"finally" +จากนั้นจะจบการทำงานพร้อมแจ้ง error ว่า +"Uncaught ReferenceError: x is not defined" +*/ +``` + +### Optional Catch Binding + +```js +try { + console.log(x); // บรรทัด a เกิด error +} catch { // มาตรฐานใหม่ ด้านหลัง catch ไม่จำเป็นต้องมีวงเล็บ ไม่ต้องระบุชื่อตัวแปร + console.log("Error"); +} +/* แสดงผลลัพธ์ +"Error" */ +``` + +```js +try { + console.log(x); // บรรทัด a เกิด error +} catch(e) { // มาตรฐานเก่า ด้านหลัง catch ต้องมีวงเล็บแล้วให้ระบุชื่อตัวแปร + console.log("Error"); +} +/* แสดงผลลัพธ์ +"Error" */ +``` + +### cause ใน error + +```js +new Error( + "My error", // ค่าอากิวเมนต์ตัวแรก + {cause: "otherError"} // ค่าอากิวเมนต์ตัวที่สอง + ); +``` + +```js +try { + console.log(x); // บรรทัด a + } catch (error) { + throw new Error( + "My Error", + {cause: error} + ); +} +/* โปรแกรมจะตาย และแจ้ง error ออกมา */ +``` + +### ทิ้งท้าย +```js +try { + va a=1 // บรรทัด a เกิด SyntaxError +} catch { + console.log("Error"); +} finally { + console.log("finally"); +} +/* โปรแกรมจะตาย และแจ้งว่าเกิด SyntaxError ออกมา */ +``` + +```js +try { + throw new SyntaxError() // บรรทัด a เกิด SyntaxError +} catch { + console.log("Error"); +} finally { + console.log("finally"); +} +/* แสดงผลลัพธ์ +"Error" +"finally" */ +``` diff --git a/examples_book/Chapter05.md b/examples_book/Chapter05.md new file mode 100644 index 0000000..b32fbc7 --- /dev/null +++ b/examples_book/Chapter05.md @@ -0,0 +1,987 @@ +# โค้ดบทที่ 5 ทบทวนอ็อบเจ็กต์ + +## วิธีสร้างอ็อบเจ็กต์อย่างง่าย + +```js +var font = { }; +``` + +```js +var font = { + color: "red", // คีย์ชื่อ color : ข้อมูลเป็นสตริง "red" + myFunction: function (param) { // คีย์ชื่อ myFunction : ข้อมูลเป็นเมธอด (ฟังก์ชัน) + /* ซอร์สโค้ดของเมธอดอยู่นี้ */ + }, + option: { // คีย์ชื่อ option : ข้อมูลเป็นอ็อบเจ็กต์ (อ๊อบเจ็กต์ซ้อนอ๊อบเจ็กต์) + value: 1 + } +}; +``` + +## การเข้าถึงพร็อพเพอร์ตี้ + +```js +var obj = { + a: 1, // กำหนดให้ a มีค่าตั้งต้นเป็น 1 + myFunction : function() { + console.log("call myFunction"); + } +}; +obj.a = 100; // กำหนดให้ obj.a มีค่าเป็น 100 +console.log(obj.a); // 100 +console.log(typeof obj.myFunction); // "function" +obj.myFunction(); // "call myFunction" +``` + +## การใช้วงเล็บเหลี่ยม + +```js +var student = { + "First name": "Somchai", + "Last name": "Jaidee", + "Who are you": function() { + console.log("I'm a student"); + }, + nickname: "Tom" +}; +console.log(student["First name"]); // "Somchai" +var lastName = "Last name"; +console.log(student[lastName]); // "Jaidee" +student["Who are you"](); // "I'm a student" +console.log(student.nickname); // "Tom" +console.log(student["nickname"]); // "Tom" +``` + +```js +var obj = { + 1: 1, + true: 2, + null : 3, + undefined: 4 +}; +console.log(obj[1 + 0]); // 1 +console.log(obj[true && true]); // 2 +console.log(obj[null]); // 3 +console.log(obj[undefined]); // 4 +``` + +```js +var obj = { + { }: 1 // เกิด SyntaxError ไม่สามารถใช้อ็อบเจ็กต์เป็นคีย์โดยตรง +}; +``` + +## เพิ่มพร็อพเพอร์ตี้เข้าไปทีหลัง + +```js +var obj = { }; +obj.a = 1; // เพิ่มพร็อพเพอร์ตี้ที่เป็นตัวแปร a +obj[1]=100; // เพิ่มพร็อพเพอร์ตี้ที่มีคีย์เป็นตัวเลข 1 +obj["property name"]= 200; // เพิ่มพร็อพเพอร์ตี้ที่มีคีย์เป็นสตริง "property name" +obj.myFunction = function() { // เพิ่มพร็อพเพอร์ตี้ที่เป็นเมธอด + console.log("to do something"); +}; +console.log(obj.a); // 1 +console.log(obj[1]); // 100 +console.log(obj["property name"]); // 200 +obj.myFunction(); // "to do something" +``` + +```js +var obj = { }; +var key = { }; +obj[key] = 100; // มีคีย์เป็นอ็อบเจ็กต์ว่าง +console.log(obj[key]); // 100 +``` + +## การส่งค่าให้ตัวแปร + +```js +function myFunction(param1, param2) { + param1.a = 3; // pass by reference + param2 = 200; // pass by value +} +var obj = {a:1, b:2}, value = 100; +myFunction(obj, value); +console.log(obj.a); // 3 +console.log(value); // 100 +``` + +```js +var obj1 = {a:1, b:2}; +var obj2 = obj1; // บรรทัด a -- pass by reference +obj2.a = 3; // บรรทัด b +console.log(obj1.a); // 3 +var value1 = 1; +var value2 = value1; // บรรทัด d -- pass by value +value2 = 3; // บรรทัด e +console.log(value1); // 1 +``` + +## การเปรียบเทียบความเท่ากัน + +```js +console.log( {a:1} == {a:1} ); // false +console.log( {a:1} === {a:1} ); // false +``` + +```js +var a = 1, b = 1; +console.log(a == b); // true +console.log(a === b); // true +``` + +## this + +```js +var obj = { + a: 1, + foo: function() { + return 2; + }, + bar: function() { + console.log(this.a); + }, + zoo: function() { + console.log(this.foo()); + } +}; +obj.bar(); // 1 +obj.zoo(); // 2 +``` + +```js +var obj = { + foo: function () { + this.a = 1; // เพิ่มตัวแปร a เข้าไปในอ็อบเจ็กต์ + console.log(this.a); + }, + bar: function() { + console.log(this.a); + } +}; +obj.foo(); // 1 +obj.bar(); // 1 +console.log(obj.a); // 1 +``` + +### การผูก this ไว้กับอ็อบเจ็กต์ + +```js +var obj1 = { }; +var obj2 = { + a: 1, + bar : function() { + console.log("this.a =", this.a); + obj1.foo = function() { // บรรทัด a + console.log("this.a =", this.a); + } // สิ้นสุดการประกาศฟังก์ชัน foo() + } // สิ้นสุดการประกาศฟังก์ชัน bar() +}; +obj2.bar(); // "this.a = 1" +obj1.foo(); // "this.a = undefined" +``` + +### this ในฟังก์ชัน + +```js +//"use strict"; +function myFunction() { + return this; +} +var obj = myFunction(); +console.log(typeof obj); +// แสดงผลลัพธ์เป็น +// undefined (ถ้าเป็นโหมดสตริคท์) +// "object" (ถ้าไม่ใช่โหมดสตริคท์) +``` + +### เมธอด call() apply() และ bind() + +```js +var obj1 = { + value: 20 +}; +var obj2 = { + myFunction: function(param1, param2) { + var value = this.value; // this จะชี้ไปยัง obj1 + console.log(param1, param2, value); + } +} +obj2.myFunction(1, 10); // 1 10 undefined +obj2.myFunction.call(obj1, 1, 10); // 1 10 20 +obj2.myFunction.apply(obj1, [1, 10]); // 1 10 20 +var f = obj2.myFunction.bind(obj1, 1, 10); +f(); // 1 10 20 +``` + +## พร็อพเพอร์ตี้แอคเซสเซอร์ + +```js +var font = { color: "red" } ; +font.color = "blue"; +``` + +```js +var font = { + set color(param) { // ประกาศเมธอด setter โดยมีพารามิเตอร์ ได้เพียงตัวเดียว + this.col = param; // กำหนดค่าให้กับข้อมูลภายในอ็อบเจ็กต์ + } +}; +font.color = "blue"; // แก้ไขค่าได้ +console.log(font.color) // undefined +``` + +```js +var font = { + col: "red", + get color() { // ประกาศเมธอด getter โดยไม่ต้องมีพารามิเตอร์ + return this.col; // รีเทิร์นข้อมูลภายในอ็อบเจ็กต์ออกไป + } +}; +console.log(font.color); // "red" +font.color = "blue"; // ไม่มีผลอะไรเกิดขึ้น หรือเกิด TypeError ในโหมดสตริคท์ +console.log(font.color); // "red" +``` + +```js +var font = { + col: "red", + set color(param) { + this.col = param; + }, + get color() { + return this.col; + } +}; +console.log(font.color); // "red" +font.color = "blue"; +console.log(font.color); // "blue" +``` + +## โอเปอเรเตอร์ delete +* หมายเหตุ โค้ดนี้ควรรันผ่านเว็บเบราเซอร์ หรือ Node.js จะดีกว่า เพราะถ้ากดปุ่มรัน ตัวแปร a จะไม่ใช่ตัวแปรของอ็อบเจ็กต์โกลบอล จึงทำให้ประโยคคำสั่ง delete a ได้คำตอบเป็น true +```js +var obj = {x:1 ,y:2}; +console.log(delete obj.x); // true +console.log(delete obj["y"]); // true +console.log(obj); // {} +var a = 1; +console.log(delete a); // false หรือเกิด SyntaxError ในโหมดสตริคท์ +``` + +```js +console.log(delete Number.MAX_VALUE); // false หรือเกิด TypeError โหมดสตริคต์ +``` + +```js +var a = [1, "Hi"]; +console.log(a.length); // 2 +console.log(delete a[0]); // true +console.log(delete a[1]); // true +console.log(a[0]); // undefined +console.log(a[1]); // undefined +console.log(a.length); // 2 +``` + +## Descriptor + +```js +var obj1 = { }; +Object.defineProperty(obj1, "foo", { // อ็อบเจ็กต์ descriptor + value: 100, + writable: true +}); +console.log(obj1.foo); // 100 +console.log(Object.getOwnPropertyDescriptor(obj1,"foo")); // รีเทิร์น descriptor +// { value: 100, writable: true, enumerable: false, configurable: false } +var obj2 = { }; +Object.defineProperties(obj2, { + "foo": { // อ็อบเจ็กต์ descriptor + value: "fooValue", + writable: true + }, + "bar": { // อ็อบเจ็กต์ descriptor + value: "barValue", + writable: false + } + // พร็อพเพอร์ตี้อื่น ๆ +}); +console.log(obj2.foo, obj2.bar); // "fooValue barValue" +console.log(Object.getOwnPropertyDescriptor(obj2,"foo")); // รีเทิร์น descriptor +// { value: "fooValue", writable: true, enumerable: false, configurable: false } +console.log(Object.getOwnPropertyDescriptor(obj2,"bar")); // รีเทิร์น descriptor +// { value: "barValue", writable: false, enumerable: false, configurable: false } +``` + +## ฟังก์ชันคอนสตรัคเตอร์ + +```js +function Car(color) { + this.color = color; + return true; // เขียนเกินมา ไม่มีผลอะไรต่อการทำงาน +} +var redCar = new Car("red"); +var blueCar = new Car("blue"); +//… สร้างอ็อบเจ็กต์ใหม่ได้เรื่อยด้วยโอเปอเรเตอร์ new +console.log(redCar.color); // "red" +console.log(blueCar.color); // "blue" +``` + +```js +function Car(color) { + this.color = color; // ถ้าเป็นโหมดสตริคท์จะเกิด TypeError ขึ้นได้ + return true; +} +var blueCar = Car("blue"); // เป็นการเรียกฟังก์ชันธรรดา +console.log(blueCar); // true +``` + +```js +function Car(color) { + console.log("constructor"); +} +var redCar = new Car(); // "constructor" +var blueCar = new Car; // "constructor" +``` + +## เมธอด Object.create() + +```js +var car = { + drive: function() { console.log("driving a car") ; } +} +var redCar = Object.create(car); +var blueCar = Object.create(car, +{ // เพิ่มพร็อพเพอร์ตี้เข้าไป ด้วยการระบุ descriptor + foo: { writable: true, configurable: true, value: "fooValue" }, // descriptor + bar:{ writable: true, configurable: true, value: "barValue" } // descriptor +} +); +// สร้างอ็อบเจ็กต์ใหม่ได้เรื่อย ๆ ด้วย Object.create() +// … +redCar.drive(); // "driving a car" +blueCar.drive(); // "driving a car" +console.log(blueCar.foo); // "fooValue" +console.log(blueCar.bar); // "barValue" +``` + +```js +var car = { } +var redCar = Object.create(car); +var blueCar = Object.create(car); +car.drive = function() { + console.log("driving a car"); +} +redCar.drive(); // "driving a car" +blueCar.drive(); // "driving a car" + +console.log( Object.getPrototypeOf(redCar) === car ); // true (เพราะโปรโตไทป์คือ car) +console.log( Object.getPrototypeOf(blueCar) === car ); // true (เพราะโปรโตไทป์คือ car) +``` + +```js +var obj1 = Object.create(null); // ไม่มีโปรโตไทป์ +console.log(obj1); // {} +console.log(Object.getPrototypeOf(obj1)); // null +var obj2 = Object.create(Object.prototype); // จะเหมือนสร้างอ็อบเจ็กต์ด้วยวิธีนี้ var obj = {} +console.log(obj2); // {} +console.log(Object.getPrototypeOf(obj2) === Object.prototype); // true +``` + +## prototype + +```js +function Car(color) { + this.color = color; +} +var redCar = new Car("red"); +console.log( Object.getPrototypeOf(redCar) === Car.prototype); // true +``` + +```js +function Car(color) { + this.color = color; +} +Car.prototype.drive = function() { + console.log("Drive a", this.color, "car"); // this จะชี้ไปยังอ็อบเจ็กต์ที่ถูกสร้างขึ้นมา +}; +var redCar = new Car("red"); +``` + +```js +function Car(color) { + this.color = color; +} +Car.prototype.drive = function() { + console.log("Drive a", this.color, "car"); // this จะชี้ไปยังอ็อบเจ็กต์ที่ถูกสร้างขึ้นมา +}; +var redCar = new Car("red"); +redCar.drive(); // "Drive a red car" +var blueCar = new Car("blue"); +blueCar.drive(); // "Drive a blue car" +console.log( Object.getPrototypeOf(redCar) === Car.prototype); // true +console.log( Object.getPrototypeOf(blueCar) === Car.prototype); // true +``` + +```js +function Car(color) { + this.color = color; +} +Car.drive = function() { + console.log("driving a car"); +}; +Car.drive(); // "driving a car" +var redCar = new Car("red"); +console.log(typeof redCar.drive); // undefined +``` + +## การสืบทอดสมาชิกหลายระดับชั้น + +```js +var extend = { + drive: function() { + console.log("Drive a", this.color, "car"); + } +} +function Car(color) { + this.color = color; +} +Car.prototype = extend; // บรรทัด a +Car.prototype.stop = function() { // บรรทัด b + console.log("Stop a", this.color, "car"); +}; +var redCar = new Car("red"); +console.log( Object.getPrototypeOf(redCar) === extend ); // true +redCar.drive(); // "Drive a red car" +redCar.stop(); // "Stop a red car" +``` + +```js +function Car(color) { } +Car.prototype.drive = function() { + console.log("Drive a", this.color, "car"); // this ชี้ไปยังอ็อบเจ็กต์ที่ถูกสร้างขึ้นมา +}; +function RedCar(color) { + this.color = color; +} +RedCar.prototype = Object.create(Car.prototype); // บรรทัด a +console.log( Object.getPrototypeOf(RedCar.prototype) === Car.prototype ); // true +RedCar.prototype.stop = function() { + console.log("Stop a", this.color, "car"); // this ชี้ไปยังอ็อบเจ็กต์ที่ถูกสร้างขึ้นมา +} +var redCar = new RedCar("red"); +console.log( Object.getPrototypeOf(redCar) === RedCar.prototype ); // true +redCar.drive(); // "Drive a red car" +redCar.stop(); // "Stop a red car" +``` + +## โอเปอรเตอร์ instanceof + +```js +function Foo() { } +var obj = new Foo(); +console.log(obj instanceof Foo); // true +console.log(Object.getPrototypeOf(obj) === Foo.prototype); // true +``` + +```js +function Foo() { } +var obj = new Foo(); +console.log(obj instanceof Foo); // true +console.log(Object.getPrototypeOf(obj) === Foo.prototype); // true +console.log(obj instanceof Object); // true +console.log(Foo.prototype instanceof Object); // true +console.log(Object.getPrototypeOf(Foo.prototype) === Object.prototype); // true +console.log(Object.getPrototypeOf(obj) === Object.prototype); // false +``` + +## prototype ที่มีอยู่ในภาษา + +```js +console.log( Object.getPrototypeOf(function(){ }) === Function.prototype); // true +console.log( Object.getPrototypeOf([ ]) === Array.prototype); // true +console.log( Object.getPrototypeOf({ }) === Object.prototype); // true +console.log( Object.getPrototypeOf('') === String.prototype); // true +console.log( Object.getPrototypeOf(true) === Boolean.prototype); // true +console.log( Object.getPrototypeOf(1) === Number.prototype); // true +console.log(function(){ } instanceof Function); // true +console.log([ ] instanceof Array); // true +console.log({ } instanceof Object); // true +console.log('' instanceof String); // false +console.log(true instanceof Boolean); // false +console.log(1 instanceof Number); // false +``` + +```js +// ฟังก์ชัน +Function.prototype.sayMsg = function(msg) { + console.log("Function say:", msg); +}; +function myFunction() { } +myFunction.sayMsg("Hello"); // "Function say: Hello" +// อาร์เรย์ +Array.prototype.sayMsg = function(msg) { + console.log("Array say:", msg); +}; +[ ].sayMsg("Hello"); // "Array say: Hello" +// อ็อบเจ็กต์ +Object.prototype.sayMsg = function(msg) { + console.log("Object say:", msg); +}; +var obj = { }; +obj.sayMsg("Hello"); // "Object say: Hello" +({ }).sayMsg("Hello"); // "Object say: Hello" +// สตริง +String.prototype.sayMsg = function(msg) { + console.log("String say:", msg); +}; +"123".sayMsg("Hello"); // "String say: Hello" +// บูลีน +Boolean.prototype.sayMsg = function(msg) { + console.log("Boolean say:", msg); +} +true.sayMsg("Hello"); // "Boolean say: Hello" +// ตัวเลข +Number.prototype.sayMsg = function(msg) { + console.log("Number say:", msg); +} +var num = 123; +num.sayMsg("Hello"); // "Number say: Hello" +(123).sayMsg("Hello"); // "Number say: Hello" +``` + +* หมายเหตุ เพื่อป้องกันความผิดจากการรันโคดด้านล่าง ก็ควรจะลบ sayMsg ก่อนหน้านี้ออกไปก่อน ด้วยโค้ดต่อไปนี้ (ในหนังสือไม่มีโค้ดชุดนี้) +```js +delete Function.prototype.sayMsg; +delete Array.prototype.sayMsg; +delete Object.prototype.sayMsg +delete String.prototype.sayMsg +delete Boolean.prototype.sayMsg +delete Number.prototype.sayMsg +``` + +## โอเปอเรเตอร์ in + +```js +var obj = {x: 1, y: 2}; +console.log("x" in obj); // true +console.log("xyz" in obj); // false (อ็อบเจ็กต์ไม่มีพร็อพเพอร์ตี้ xyz) +var a = ["a", "b", "c"]; +console.log(0 in a); // true (อาร์เรย์นี้มีอินเด็กซ์ 0) +console.log(5 in a); // false (อาร์เรย์ไม่มีอินเด็กซ์ 5) +console.log("1" in a); // true (อาร์เรย์นี้มีอินเด็กซ์ 1) +console.log("length" in a); // true (อาร์เรย์จะมี length เป็นพร็อพเพอร์ตี้) +``` + +```js +var parent = {x:1}; +var obj = Object.create(parent); +console.log("x" in obj); // true +``` + +## ประโยคคำสั่ง for …in + +```js +function Font() { + this.color = "red"; + this.size = 200; +} +var coordinate = {x: 1, y: 1, z: 1} +Font.prototype = Object.create(coordinate); +Font.prototype.myFunction = function(){ }; +var font = new Font(); +font[1] = "fontValue"; +for(var prop in font) { + console.log(prop); +} +/* แสดงผลลัพธ์เป็น +"1" +"color" +"size" +"myFunction" +"x" +"y " +"z " */ +``` + +```js +function Font() { + this.color = "red"; + this.size = 200; +} +var coordinate = {x: 1, y: 1, z: 1} +Font.prototype = Object.create(coordinate); +Font.prototype.myFunction = function(){ }; +var font = new Font(); +font[1] = "fontValue"; +for(var prop in font) { + if(font.hasOwnProperty( prop ) ) { // เข้าถึงคีย์ที่อยู่ในอ็อบเจ็กต์เท่านั้น + console.log("font." + prop, "=", font[prop]); + } +} +/* แสดงผลลัพธ์เป็น +"font.1 = fontValue" +"font.color = red" +"font.size = 200" */ +``` + +## Object.preventExtensions() กับ Object.isExtensions() + +```js +var obj1 = {}; +var obj2 = Object.preventExtensions(obj1); +console.log(obj1 === obj2); // true +obj1.a = 1; // จะเพิกเฉย แต่ถ้าอยู่ในโหมดสตริคท์จะเกิด TypeError +var obj3 = Object.create(obj1); +console.log(obj3); // {} +obj3.a = 1; // เพิ่มพร็อพเพอร์ตี้เข้าไปได้ +console.log(Object.getPrototypeOf(obj3) === obj1); // true +``` + +```js +var obj = {}; +console.log(Object.isExtensible(obj)); // true +Object.preventExtensions(obj); +console.log(Object.isExtensible(obj)); // false +``` + +## ฟังก์ชันคอนสตรัคเตอร์ที่มีในภาษา + +### Object + +```js +var obj1 = new Object(1234); +var obj2 = new Object(); // อ็อบเจ็กต์ว่าง +var obj3 = new Object(undefined); // อ็อบเจ็กต์ว่าง +var obj4 = new Object(null); // อ็อบเจ็กต์ว่าง +var obj5 = Object(1); // เรียกแบบฟังก์ชัน (ค่าอากิวเมนต์เป็นอะไรก็ได้) +console.log(typeof obj5); // "object" +``` + +### Boolean + +```js +var obj = new Boolean(true); +console.log(typeof obj); // "object" +var b = Boolean(true); +console.log(typeof b); // "boolean" +console.log(b); // true +console.log(obj == b); // true +console.log(obj === b); // false +``` + +```js +var false1 = Boolean(); // false +var false2 = Boolean(false); // false +var false3 = Boolean(0); // false +var false4 = Boolean(-0); // false +var false5 = Boolean(null); // false +var false6 = Boolean(''); // false +var false7 = Boolean(NaN); // false +var false8 = Boolean(undefined); // false +var true1 = Boolean(true); // true +var true2 = Boolean("Hi"); // true +var true3 = Boolean("true"); // true +var true4 = Boolean("false"); // true +var true5 = Boolean([]); // true +var true6 = Boolean({}); // true +var true7 = Boolean(true4); // true +``` + +### Number + +```js +var obj = new Number(1); +console.log(typeof obj); // "object" +var n1 = Number("1"); // รับค่าเป็นสตริงก็ได้เช่นกัน +var n2 = Number("a"); // "a" ไม่ใช่ตัวเลข จึงทำให้ตัวแปร n2 มีค่าเป็น NaN +console.log(typeof n1, typeof n2); // "number number" +console.log(n1, n2); // 1 NaN +console.log(obj == n1); // true +console.log(obj === n1); // false +``` + +### String + +```js +var str1 = new String("MyString"); +console.log(typeof str1); // "object" +console.log(str1 === "MyString"); // false +var str2 = String("MyString"); +console.log(typeof str2); // "string" +console.log(str2 === "MyString"); // true +console.log(str1 == str2); // true +console.log(str1 === str2); // false +``` + +### Array + +```js +var array1 = Array( 3 ); // เรียกแบบฟังก์ชัน +console.log(array1.length); // 3 +console.log(array1); // [ <3 empty slots> ] +var array2 = new Array(3); +console.log(array2.length); // 3 +console.log(array2); // [ <3 empty slots> ] +``` + +### Date + +```js +console.log(Date()); // "Fri Sep 08 2023 21:33:45 GMT+0700 (Indochina Time)" +console.log(new Date()); // "Fri Sep 08 2023 21:33:45 GMT+0700 (Indochina Time)" +``` + +```js +console.log(new Date("05-31-2023")) // "Wed May 31 2023 00:00:00 GMT+0700 (Indochina Time)" +console.log(new Date("09/30/2023")) // "Sat Sep 30 2023 00:00:00 GMT+0700 (Indochina Time)" +console.log( new Date("October 20, 2023 11:13:00") ); +// "Fri Oct 20 2023 11:13:00 GMT+0700 (Indochina Time)" +``` + +## เมธอดของสตริง + +```js +console.log( " Learning JavaScript ".trim() ); // แสดงผลเป็น "Learning JavaScript" +``` + +```js +console.log( "foo_foo_foo_".indexOf("foo") ); // 0 +console.log("foo_foo_foo_".lastIndexOf("foo") ); // 8 +``` + +```js +"use strict"; +var str = "MyString"; +console.log(str.length) // 8 +str.length = 0; // TypeError (ถ้าไม่ใช่โหมดสตริคท์จะไม่เกิด error แต่จะเพิกเฉย) +str.func = function(){ }; // TypeError (ถ้าไม่ใช่โหมดสตริคท์จะไม่เกิด error แต่จะเพิกเฉย) +``` + +## เมธอดของอาร์เรย์ + +```js +var array = ["a","b","c","d"]; +var str = array.join("->"); +console.log(str); // "a->b->c->d" +``` + +```js +var array = [ ]; +array.push("a","b","c","d"); // เพิ่มสมาชิกกี่ตัวก็ได้ +console.log(array); // [ 'a', 'b', 'c', 'd' ] +console.log(array.pop()); // "d" +console.log(array); // [ 'a', 'b', 'c' ] +``` + +```js +var array = ["a","b","c","d"]; +console.log(array.indexOf("c" )); // 2 +console.log(array.indexOf("e" )); // -1 +``` + +```js +var array = ["a","b","c","d"]; +var result = array.some( function (value, index, arrayObj) { + // value คือค่าสมาชิกของอาร์เรย์ + // index คืออินเด็กซ์ของอาร์เรย์ + // arrayObj คือ ["a", "b", "c", "d"] + return value == "c"; +} ); +console.log(result) // true +``` + +```js +var array = ["a","b","c","d"]; +array.forEach(function (value, index, arrayObj) { + // value คือค่าสมาชิกของอาร์เรย์ + // index คืออินเด็กซ์ของอาร์เรย์ + // arrayObj คือ ["a", "b", "c", "d"] + console.log("a[", index, "] = ", value); +}); +/* แสดงผลลัพธ์ +"a[ 0 ] = a" +"a[ 1 ] = b" +"a[ 2 ] = c" +"a[ 3 ] = d" */ +``` + +## ฟังก์ชันก็เป็นอ็อบเจ็กต์ + +```js +function foo() { + console.log(foo.x) // อ้างถึงพร๊อพเพอร์ตี้ x +} + +foo(); // undefined + +foo.x = 100; // เพิ่มเข้าไป เพื่อให้ a.x หาเจอ +console.log(foo.x) // 100 +foo(); // เรียกใช้ได้ โดยจะแสดง 100 ออกมา +foo["x"] = true // ใช้วงเล็บเหลี่ยมในการเข้าถึง x +foo["name"] = "My function"; // ใช้วงเล็บเหลี่ยมเพิ่มพร๊อพเพอร์ตี้ name เข้าไป +``` + +## เกร็ดความรู้ + +```js +function showData(person) { + console.log("Person is", person.name, ", age is", person.age ); +} +let person = { // บรรทัด a + name: "Somchai", + age: 23 +} +showData(person) // บรรทัด b +/* แสดงผลลัพธ์ +"Person is Somchai , age is 23" */ +``` + +```js +function showData(person) { + console.log("Person is", person.name, ", age is", person.age ); +} +showData({ // บรรทัด a + name: "Somchai", + age: 23 +}) +/* แสดงผลลัพธ์ +"Person is Somchai , age is 23" */ +``` + +```js +let obj = { first: "Jane", last: "Doe" }; +``` + +```js +let obj = { + first: "Jane", + last: "Doe" +}; +``` + +## ปูพื้นฐาน JSON + +### เมธอด JSON.parse() กับ JSON.stringify() + +```js +// ใช้ \ เชื่อมสตริงแต่ละบรรทัดเข้าดัวยกัน +var json = '{"bold": true,\ + "color": "red",\ + "size": 100\ +}'; +var obj = JSON.parse(json); // obj คืออ็อบเจ็กต์ที่ใช้แทนข้อมูลแบบ JSON +console.log(typeof obj); // "object" +console.log(obj); +// แสดงผลลัพธ์เป็น +// { bold: true, color: 'red', size: 100 } +``` + +* ตัวอย่าง จะทำการแปลงสตริงที่เขียนอยู่ในรูป JSON ซึ่งคราวนี้จะซับซ้อนหน่อยตรงที่คีย์ชื่อ "people" จะมีส่วน value เป็นอาร์เรย์ที่มีสมาชิกเป็นอ็อบเจ็กต์ (JSON) แล้วจะใช้เมธอด JSON.parse() แปลงให้กลายเป็นอ็อบเจ็กต์ที่อยู่ในรูปอ็อบเจ็กต์ของจาวาสคริปต์ + +```js +// ใช้ \ เชื่อมสตริงแต่ละบรรทัดเข้าดัวยกัน +var json = '{"people":[\ + {"firstName":"Somchai", "lastName":"Jaidee"},\ + {"firstName":"Mana", "lastName":"Dekdee"},\ + {"firstName":"Surat", "lastName":"Khonthai"}\ +]}'; +var obj = JSON.parse(json); // obj คืออ็อบเจ็กต์ที่ใช้แทนข้อมูลแบบ JSON +console.log(typeof obj); // "object" +console.log(obj); +/* แสดงผลลัพธ์เป็น +{ people: + [ { firstName: 'Somchai', lastName: 'Jaidee' }, + { firstName: 'Mana', lastName: 'Dekdee' }, + { firstName: 'Surat', lastName: 'Khonthai' } ] } */ +``` + +* ตัวอย่าง ใช้ JSON.stringify() จะแปลงอ็อบเจ็กต์ในจาวาสคริปต์ ให้เป็นสตริงที่อยู่ในรูป JSON + + +```js +var obj = { + bold: true, + color: "red", + size: 100 +}; +console.log(JSON.stringify(obj)); +// แสดงผลลัพธ์เป็น +// {"bold":true,"color":"red","size":100} +``` + +* ตัวอย่าง ใช้ JSON.stringify() จะแปลง value ที่เป็นอาร์เรย์ ให้เป็นสตริงที่อยู่ในรูป JSON + +```js +var obj = { + "cars": ["Toyota", "BMW", "Tesla"] +} +console.log(JSON.stringify(obj)); +// แสดงผลลัพธ์เป็น +// {"cars":["Toyota","BMW","Tesla"]} +``` + +```js +var obj = { + people: [ + { firstName: "Somchai", lastName: "Jaidee" }, + { firstName: "Mana", lastName: "Dekdee" }, + { firstName: "Surat", lastName: "Khonthai" } + ] , + age: Symbol("1") +}; +console.log(JSON.stringify(obj)); +/* แสดงผลลัพธ์เป็น +{"people":[{"firstName":"Somchai","lastName":"Jaidee"},{"firstName":"Mana", + "lastName":"Dekdee"},{"firstName":"Surat","lastName":"Khonthai"}]} */ +``` + +```js +var obj = { + people: [ + { firstName: "Somchai", lastName: "Jaidee" }, + { firstName: "Mana", lastName: "Dekdee" }, + { firstName: "Surat", lastName: "Khonthai" } + ] +}; +console.log(JSON.stringify(obj, null, 2)); // ระบุค่าอากิวเมนต์ตัวที่สาม +/* แสดงผลลัพธ์เป็น +{ + "people": [ + { + "firstName": "Somchai", + "lastName": "Jaidee" + }, + { + "firstName": "Mana", + "lastName": "Dekdee" + }, + { + "firstName": "Surat", + "lastName": "Khonthai" + } + ] +} */ +``` + +```js +var obj = { + name: "Somchai", + func: function() { }, + age: Symbol("25") +}; +console.log(JSON.stringify(obj)); +// แสดงผลลัพธ์ +// {"name":"Somchai"} +``` + +```js +var obj = { + name: "Somchai", + today: new Date() +}; +console.log(JSON.stringify(obj)); +// แสดงผลลัพธ์ +// {"name":"Somchai","today":"2023-02-06T02:10:39.018Z"} +``` diff --git a/examples_book/Chapter06.md b/examples_book/Chapter06.md new file mode 100644 index 0000000..91315a0 --- /dev/null +++ b/examples_book/Chapter06.md @@ -0,0 +1,854 @@ +# โค้ดบทที่ 6 ทบทวน Regex + +## Regular Expression + +* 1. สร้างโดยใช้ฟังก์ชันคอนสตรัคเตอร์ RegExp ตามตัวอย่าง + +```js +var myRegex = new RegExp("Hello+"); +console.log(myRegex.toString()); // /Hello+/ +``` + +* 2. สร้างโดยใช้เครื่องหมาย /…/ มาครอบข้อความ regex ตามตัวอย่าง +```js +var myRegex = /Hello+/; +// ข้อความ regex ก็คืออ็อบเจ็กต์ตัวหนึ่ง +// จึงสามารถแชร์ใช้งานเมธอด RegExp.prototype.toString() +console.log(/Hello+/.toString()); // /Hello+/ +``` + +### เมธอด test() กับ exec() + +ตัวอย่าง 6.1 การใช้งานเมธอด test() เพื่อค้นหาข้อความในสตริง +```js +console.log( /Hello+/.test("Hellooooo") ); // true +console.log( (new RegExp("Hello+")).test("Hellooooo") ); // true +``` + +ตัวอย่าง 6.2 เนื่องจากข้อความแบบ regex สามารถนำไปประยุกต์ใช้งานได้หลายกรณี และตัวอย่างต่อไปนี้จะเป็นการใช้ regex มาตรวจสอบข้อความว่าเป็นอีเมลหรือไม่ +```js +var myRegex = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i; +console.log(myRegex.test("xxxxxx.yyyyyy_zzzzz@abc.com")); // true +``` + +ตัวอย่าง 6.3 ข้อความแบบ regex สามารถนำไปใช้ตรวจสอบตัวเลข +```js +var myRegex = /^(\-?|\+?)\d*$/; +console.log(myRegex.test("-987")); // true +``` + +ตัวอย่าง 6.4 การใช้งานเมธอด exec() เพื่อค้นหาข้อความในสตริง +```js +var result = /(foo).(bar)/.exec("0123foo_bar"); +console.log(result.index); // 4 +console.log(result[0]); // "foo_bar" +console.log(result[1]); // "foo" +console.log(result[2]); // "bar" +console.log(result.input); // "0123foo_bar" +``` + +## รูปแบบการใช้งาน regex ต่างๆ + +### การระบุคำที่ต้องการค้นหา + +ตัวอย่าง 6.5 ใช้ /like/ เป็น regex เพื่อจับคู่ลำดับตัวอักษร "like" ในสตริง "I like u" ซึ่งเจอชุดตัวอักษร "like" ที่อินเด็กซ์ 2 +```js +var re = /like/; +console.log(re.exec("I like u")); // [ 'like', index: 2, input: 'I like u', groups: undefined ] +``` + +ตัวอย่าง 6.6 ใช้ /html|div|br/ เป็น regex เพื่อจับคู่ลำดับตัวอักษร "html" หรือ "div" หรือ "br" อย่างใดอย่างหนึ่ง ในสตริง "<div>" ซึ่งเจอชุดอักษร "div" ที่อินเด็กซ์ 1 +```js +var re = /html|div|br/; +console.log(re.exec("
")); // [ 'div', index: 1, input: '
', groups: undefined ] +``` + +### การระบุช่วงตัวอักษรที่จับคู่ + +ตัวอย่าง 6.7 ใช้ regex เป็น /[abc]/ หมายถึงจะจับคู่ตัวอักษร a หรือ b หรือ c โดยจะค้นหาตัวแรกที่พบเจอ ซึ่งจะเจออักษร "b" ในสตริง "feb" ที่อินเด็กซ์ 2 +```js +var re = /[abc]/; +console.log(re.exec("feb")); // [ 'b', index: 2, input: 'feb', groups: undefined ] +``` + +ตัวอย่าง 6.8 ใช้ regex เป็น /[7-9]/ หมายถึงจะจับคู่ตัวเลขในช่วง 7 ถึง 9 โดยจะค้นหาตัวแรกที่พบเจอ ซึ่งจะเจออักษรเลข "8" ในสตริง "2+8" ที่อินเด็กซ์ 2 +```js +var re = /[7-9]/; +console.log(re.exec("2+8")); // [ '8', index: 2, input: '2+8', groups: undefined ] +``` + +ตัวอย่าง 6.9 ใช้ regex เป็น /[a-c]/ หมายถึงจะจับคู่ตัวอักษรตัวพิมพ์เล็ก ตั้งแต่ a ถึง c โดยจะค้นหาตัวแรกที่พบเจอ ซึ่งจะเจออักษรเลข "a" ในสตริง "rat" ที่อินเด็กซ์ 1 +```js +var re = /[a-c]/; +console.log(re.exec("rat")); // [ 'a', index: 1, input: 'rat', groups: undefined ] +``` + +ตัวอย่าง 6.10 ใช้ regex เป็น /[A-C]/ หมายถึงจะจับคู่ตัวอักษรตัวพิมพ์ใหญ่ ตั้งแต่ A ถึง C โดยจะค้นหาตัวแรกที่พบเจอ ซึ่งจะเจออักษรเลข "C" ในสตริง "COM" ที่อินเด็กซ์ 0 +```js +var re = /[A-C]/; +console.log(re.exec("COM")); // [ 'C', index: 0, input: 'COM', groups: undefined ] +``` + +ตัวอย่าง 6.11 ใช้ regex เป็น /[a-cA-C]/ หมายถึงจะจับคู่ตัวอักษรตัวพิมพ์เล็ก ตั้งแต่ a ถึง c หรือตัวอักษรตัวพิมพ์ใหญ่ ตั้งแต่ A ถึง C โดยจะค้นหาตัวแรกที่พบเจอ ซึ่งจะเจออักษร "A" ในสตริง "AND" ที่อินเด็กซ์ 0 +```js +var re = /[a-cA-C]/; +console.log(re.exec("AND")); // [ 'A', index: 0, input: 'AND', groups: undefined ] +``` + +ตัวอย่าง 6.12 ใช้ regex เป็น /[^cat]/ หมายถึงจะจับคู่ตัวอักษร ที่ไม่ใช่ c และไม่ใช่ a และไม่ใช่ t โดยจะค้นหาตัวแรกที่พบเจอ ซึ่งจะเจออักษร "s" ในสตริง "cats" ที่อินเด็กซ์ 3 +```js +var re = /[^cat]/; +console.log(re.exec("cats")); // [ 's', index: 3, input: 'cats', groups: undefined ] +``` + +ตัวอย่าง 6.13 ใช้ regex เป็น /[^0-3]/ หมายถึงจะจับคู่ตัวอักษร ที่ไม่ใช่เลข 0 ถึง 3 โดยจะค้นหาตัวแรกที่พบเจอ ซึ่งจะเจออักษร "k" ในสตริง "20k" ที่อินเด็กซ์ 2 +```js +var re = /[^0-3]/; +console.log(re.exec("20k")); // [ 'k', index: 2, input: '20k', groups: undefined ] +``` + +ตัวอย่าง 6.14 ใช้ regex เป็น /[^w-z]/ หมายถึงจะจับคู่ตัวอักษร ที่ไม่ใช่ตัวอักษร w ถึง z โดยจะค้นหาตัวแรกที่พบเจอ ซึ่งจะเจอเลข 1 ในสตริง "xy12" ที่อินเด็กซ์ 2 +```js +var re = /[^w-z]/; +console.log(re.exec("xy12")); // [ '1', index: 2, input: 'xy12', groups: undefined ] +``` + +ตัวอย่าง 6.15 ใช้ regex เป็น /[^m-oM-O]/ หมายถึงจะจับคู่ตัวอักษร ที่ไม่ใช่ตัวอักษร m ถึง o และไม่ใช่ M ถึง O โดยจะค้นหาตัวแรกที่พบเจอ ซึ่งจะเจอเลข "!" ในสตริง "Moon!" ที่อินเด็กซ์ 4 +```js +var re = /[^m-oM-O]/; +console.log(re.exec("Moon!")); // [ '!', index: 4, input: 'Moon!', groups: undefined ] +``` + +ตัวอย่าง 6.16 เป็นการประยุกต์ใช้ regex ที่ดูซับซ้อนขึ้น ได้แก่ /[a-zB-Mxyz157]/ หมายถึงจะจับคู่ตัวอักษรระหว่าง a ถึง z หรือ B ถึง M หรือ x หรือ y หรือ z หรือ 1 หรือ 5 หรือ 7 โดยจะค้นหาตัวแรกที่พบเจอ ซึ่งจะเจออักษรเลข "5" ในสตริง "3-5" ที่อินเด็กซ์ 2 +```js +var re = /[a-zB-Mxyz157]/; +console.log(re.exec("3-5")); // [ '5', index: 2, input: '3-5', groups: undefined ] +``` + +### การระบุความถี่ของลำดับตัวอักษรที่จับคู่ + +ตัวอย่าง 6.17 ใช้ regex เป็น /3+/ หมายถึงจะจับคู่ลำดัับตัวเลข 3 ตั้งแต่ 1 ตัวขึ้นไป โดยจะเจออักษร "3" ในสตริง "123" ที่อินเด็กซ์ 2 +```js +var re = /3+/; +console.log(re.exec("123")); // [ '3', index: 2, input: '123', groups: undefined ] +``` + +ตัวอย่าง 6.18 ใช้ regex เป็น /p+/ หมายถึงจะจับคู่ลำดับตัวอักษร "p" ตั้งแต่ 1 ตัวขึ้นไป โดยจะเจออักษร "pp" ในสตริง "app" ที่อินเด็กซ์ 1 +```js +var re = /p+/; +console.log(re.exec("app")); // [ 'pp', index: 1, input: 'app', groups: undefined ] +``` + +ตัวอย่าง 6.19 ใช้ regex เป็น /3*/ หมายถึงจะจับคู่ลำดับตัวเลข 3 ตั้งแต่ศูนย์ตัวขึ้นไป ในตัวอย่างนี้ใช้ตริงตั้งต้นเป็น "12" ก็จะถือว่าเจอสตริงว่าง '' ที่อินเด็กซ์ 0 +```js +var re = /3*/; +console.log(re.exec("12")); // [ '', index: 0, input: '12', groups: undefined ] +``` + +ตัวอย่าง 6.20 ใช้ regex เป็น /b*/ หมายถึงจะจับคู่ลำดับตัวอักษร "b" ตั้งแต่ศูนย์ตัวขึ้นไป โดยจะเจออักษร "bb" ในสตริง "bbc" ที่อินเด็กซ์ 0 +```js +var re = /b*/; +console.log(re.exec("bbc")); // [ 'bb', index: 0, input: 'bbc', groups: undefined ] +``` + +ตัวอย่าง 6.21 ใช้ regex เป็น /p*/ หมายถึงจะจับคู่ลำดับตัวอักษร "p" ตั้งแต่ศูนย์ตัวขึ้นไป ในตัวอย่างนี้ใช้สตริงตั้งต้นเป็น "app" ก็จะถือว่าเจอสตริงว่าง '' ที่อินเด็กซ์ 0 เพราะเวลา regex จับคู่ในสตริง ก็จะมองจากตัวอักษรซ้ายมือสุดไปขวามือ พอเจอ "a" ก่อนตัวอักษรอื่นๆ จึงเข้าใจว่า p มีแค่ศูนย์ตัว จะไม่มองหา p ที่อยู่ในอินเด็กซ์ 1 กับ 2 ดังนั้นการใช้ * จึงต้องระวังให้ดี อาจทำให้เข้าใจผิดได้ +```js +var re = /p*/; +console.log(re.exec("app")); // [ '', index: 0, input: 'app', groups: undefined ] +``` + +ตัวอย่าง 6.22 ใช้ regex เป็น /b?/ หมายถึงจะจับคู่ลำดับตัวอักษร "b" ตั้งแต่ศูนย์ตัว ถึง 1 ตัว โดยจะเจออักษร "b" ในสตริง "bed" ที่อินเด็กซ์ 0 +```js +var re = /b?/; +console.log(re.exec("bed")); // [ 'b', index: 0, input: 'bed', groups: undefined ] +``` + +ตัวอย่าง 6.23 ใช้ regex เป็น /b?/ หมายถึงจะจับคู่ลำดับตัวอักษร "b" ตั้งแต่ศูนย์ตัว ถึง 1 ตัว โดยจะเจออักษร "b" ในสตริง "bbc" ที่อินเด็กซ์ 0 (เจอ "b" ตัวแรกแล้ว ก็จะไม่ค้นหา "b" ตัวถัดไป) +```js +var re = /b?/; +console.log(re.exec("bbc")); // [ 'b', index: 0, input: 'bbc', groups: undefined ] +``` + +ตัวอย่าง 6.24 ใช้ regex เป็น /p?/ หมายถึงจะจับคู่ลำดับตัวอักษร "p" ตั้งแต่ศูนย์ตัว ถึง 1 ตัว ตัวอย่างนี้ใช้สตริงตั้งต้นเป็น "app" ก็จะถือว่าเจอสตริงว่าง '' ที่อินเด็กซ์ 0 เพราะเวลา regex จับคู่ในสตริง ก็จะมองจากตัวอักษรซ้ายมือสุดไปขวามือ พอเจอ "a" ก่อนตัวอักษรอื่นๆ จึงเข้าใจว่า p มีแค่ศูนย์ตัว จะไม่มองหา p ที่อยู่ในอินเด็กซ์ 1 กับ 2 ดังนั้นการใช้ ? ก็เหมือน * จึงต้องระวังให้ดี อาจทำให้เข้าใจผิดได้ +```js +var re = /p?/; +console.log(re.exec("app")); // [ '', index: 0, input: 'app', groups: undefined ] +``` + +ตัวอย่าง 6.25 ใช้ regex เป็น /g{2}/ หมายถึงจะจับคู่ลำดับตัวอักษร "g" จำนวน 2 ตัว โดยจะเจออักษร "gg" ในสตริง "egg" ที่อินเด็กซ์ 1 +```js +var re = /g{2}/; +console.log(re.exec("egg")); // [ 'gg', index: 1, input: 'egg', groups: undefined ] +``` + +ตัวอย่าง 6.26 ใช้ regex เป็น /5{2}/ หมายถึงจะจับคู่ลำดับตัวเลข 5 จำนวน 2 ตัว โดยจะเจออักษร "55" ในสตริง "555" ที่อินเด็กซ์ 0 (เจอ 5 แค่สองตัว ไม่ใช่เจอ "555") +```js +var re = /5{2}/; +console.log(re.exec("555")); // [ '55', index: 0, input: '555', groups: undefined ] +``` + +ตัวอย่าง 6.27 ใช้ regex เป็น /e{1,3}/ หมายถึงจะจับคู่ลำดับตัวอักษร "e" จำนวนอย่างน้อย 1 ตัว และมากสุด 3 ตัว โดยจะเจออักษร "ee" ในสตริง "deep" ที่อินเด็กซ์ 1 (เจอ "e" สองตัว) +```js +var re = /e{1,3}/; +console.log(re.exec("deep")); // [ 'ee', index: 1, input: 'deep', groups: undefined ] +``` + +ตัวอย่าง 6.28 ใช้ regex เป็น /x{1,3}/ หมายถึงจะจับคู่ลำดับตัวอักษร "x" จำนวนอย่างน้อย 1 ตัว และมากสุด 3 ตัว โดยจะเจออักษร "xxx" ในสตริง "xxxx" ที่อินเด็กซ์ 0 (เจอ "x" แค่สามตัว ไม่ใช่เจอ "xxxx") +```js +var re = /x{1,3}/; +console.log(re.exec("xxxx")); // [ 'xxx', index: 0, input: 'xxxx', groups: undefined ] +``` + +ตัวอย่าง 6.29 ใช้ regex เป็น /a{2,}/ หมายถึงจะจับคู่ลำดับตัวอักษร "a" จำนวนอย่างน้อย 2 ตัว โดยจะเจออักษร "aaa" ในสตริง "zaaa" ที่อินเด็กซ์ 1 (เจอ "a" สามตัว) +```js +var re = /a{2,}/; +console.log(re.exec("zaaa")); // [ 'aaa', index: 1, input: 'zaaa', groups: undefined ] +``` + +### การจับคู่แบบ assertions + +ตัวอย่าง 6.30 ใช้ regex เป็น /^x/ หมายถึงจะจับคู่ตัวอักษร x จำนวน 1 ตัว ที่วางไว้ด้านหน้าสุด โดยจะเจอในสตริง "x-ray" ที่อินเด็กซ์ 0 +```js +var re = /^x/; +console.log(re.exec("x-ray")); // [ 'x', index: 0, input: 'x-ray', groups: undefined ] +``` +ตัวอย่าง 6.31 ใช้ regex เป็น /r$/ หมายถึงจะจับคู่ตัวอักษร r จำนวน 1 ตัว ที่วางไว้ด้านท้ายสุด โดยจะเจอในสตริง "car" ที่อินเด็กซ์ 2 +```js +var re = /r$/; +console.log(re.exec("car")); // [ 'r', index: 2, input: 'car', groups: undefined ] +``` + +ตัวอย่าง 6.32 ใช้ regex เป็น /\bfa/ หมายถึงจะจับคู่ชุดตัวอักษร "fa" ที่วางไว้ด้านหน้าสุดของคำใดคำหนึ่ง โดยจะเจอ "fa" ภายในคำ "father" ที่อินเด็กซ์ 2 (สตริงต้นทางคือ "A father is") +```js +var re = /\bfa/; +console.log(re.exec("A father is")); // [ 'fa', index: 2, input: 'A father is', groups: undefined ] +``` + +ตัวอย่าง 6.33 ใช้ regex เป็น /\bth/ หมายถึงจะจับคู่ชุดตัวอักษร "th" ที่วางไว้ด้านหน้าสุดของคำใดคำหนึ่ง ซึ่งปรากฏว่าไม่เจอชุดอักษรใดที่ตรงเงื่อนไข (สตริงต้นทางคือ "A father is") +```js +var re = /\bth/; +console.log(re.exec("A father is")); // null +``` + +ตัวอย่าง 6.34 ใช้ regex เป็น /er\b/ หมายถึงจะจับคู่ชุดตัวอักษร "er" ที่วางไว้ด้านหลังสุดของคำใดคำหนึ่ง โดยจะเจอ "er" ภายในคำ "father" ที่อินเด็กซ์ 6 (สตริงต้นทางคือ "A father is") +```js +var re = /er\b/; +console.log(re.exec("A father is")); // [ 'er', index: 6, input: 'A father is', groups: undefined ] +``` + +ตัวอย่าง 6.35 ใช้ regex เป็น /th\b/ หมายถึงจะจับคู่ชุดตัวอักษร "th" ที่วางไว้ด้านหลังสุดของคำใดคำหนึ่ง ซึ่งปรากฏว่าไม่เจอชุดอักษรใดที่ตรงเงื่อนไข (สตริงต้นทางคือ "A father is") +```js +var re = /th\b/; +console.log(re.exec("A father is")); // null +``` + +ตัวอย่าง 6.36 ใช้ regex เป็น /\Bfa/ หมายถึงจะจับคู่ชุดตัวอักษร "fa" ที่ไม่ได้วางไว้ด้านหน้าสุดของคำใดคำหนึ่ง ซึ่งปรากฏว่าไม่เจอชุดอักษรใดที่ตรงเงื่อนไข (สตริงต้นทางคือ "A father is") +```js +var re = /\Bfa/; +console.log(re.exec("A father is")); // null +``` + +ตัวอย่าง 6.37 ใช้ regex เป็น /\Bth/ หมายถึงจะจับคู่ชุดตัวอักษร "th" ที่ไม่ได้วางไว้ด้านหน้าสุดของคำใดคำหนึ่ง โดยจะเจอ "th" ภายในคำ "father" ที่อินเด็กซ์ 4 (สตริงต้นทางคือ "A father is") +```js +var re = /\Bth/; +console.log(re.exec("A father is")); // [ 'th', index: 4, input: 'A father is', groups: undefined ] +``` + +ตัวอย่าง 6.38 ใช้ regex เป็น /\Ber/ หมายถึงจะจับคู่ชุดตัวอักษร "er" ที่ไม่ได้วางไว้ด้านหน้าสุดของคำใดคำหนึ่ง โดยจะเจอ "er" ภายในคำ "father" ที่อินเด็กซ์ 6 (สตริงต้นทางคือ "A father is") +```js +var re = /\Ber/; +console.log(re.exec("A father is")); // [ 'er', index: 6, input: 'A father is', groups: undefined ] +``` + +ตัวอย่าง 6.39 ใช้ regex เป็น /er\B/ หมายถึงจะจับคู่ชุดตัวอักษร "er" ที่ไม่ได้วางไว้ด้านหลังสุดของคำใดคำหนึ่ง ซึ่งปรากฏว่าไม่เจอชุดอักษรใดที่ตรงเงื่อนไข (สตริงต้นทางคือ "A father is") +```js +var re = /er\B/; +console.log(re.exec("A father is")); // null +``` + +ตัวอย่าง 6.40 ใช้ regex เป็น /th\B/ หมายถึงจะจับคู่ชุดตัวอักษร "th" ที่ไม่ได้วางไว้ด้านหลังสุดของคำใดคำหนึ่ง โดยจะเจอ "th" ภายในคำ "father" ที่อินเด็กซ์ 4 (สตริงต้นทางคือ "A father is") + +```js +var re = /th\B/; +console.log(re.exec("A father is")); // [ 'th', index: 4, input: 'A father is', groups: undefined ] +``` + +ตัวอย่าง 6.41 ใช้ regex เป็น /fa\B/ หมายถึงจะจับคู่ชุดตัวอักษร "fa" ที่ไม่ได้วางไว้ด้านหลังสุดของคำใดคำหนึ่ง โดยจะเจอ "fa" ภายในคำ "father" ที่อินเด็กซ์ 2 (สตริงต้นทางคือ "A father is") +```js +var re = /fa\B/; +console.log(re.exec("A father is")); // [ 'fa', index: 2, input: 'A father is', groups: undefined ] +``` + +1) วิธี lookahead assertion จะมีรูปแบบ x(?=y) ดังตัวอย่าง +ตัวอย่าง 6.42 ใช้ regex เป็น /Java(?=Script)/ หมายถึงจะจับคู่คำว่า "Java" แต่ต้องตามด้วยคำว่า "Script" ด้วยรูปแบบ Java แล้วมีรูปแบบ (?=Script) แปะท้าย จึงเจอ "Java" ที่อินเด็กซ์ 10 +```js +let re = /Java(?=Script)/; +console.log(re.exec("This is a JavaScript book")); +// [ 'Java', index: 10, input: 'This is a JavaScript book', groups: undefined ] +``` + +ตัวอย่าง 6.43 ใช้ regex เป็น /[wW]atch(?=ed|ing)/ หมายถึงจะจับคู่คำว่า "watch" หรือ "Watch" แต่ต้องตามด้วย "ed" หรือ "ing" ด้วยรูปแบบ [wW]atch แล้วมีรูปแบบ (?=ed|ing) แปะท้าย จึงเจอ "watch" ที่อินเด็กซ์ 2 +```js +let re = /[wW]atch(?=ed|ing)/; +console.log(re.exec("I watched TV")); +// [ 'watch', index: 2, input: 'I watched TV', groups: undefined ] +``` +2) วิธี negative lookahead assertion จะมีรูปแบบ x(?!y) ดังตัวอย่าง +ตัวอย่าง 6.44 ใช้ regex เป็น /test_(?!js|txt)/ หมายถึงจะจับคู่คำว่า "test_" แล้วตามหลังด้วยรูปแบบ (?!js|txt) หรือก็คือต้องไม่ต่อท้ายด้วย "js" หรือ "txt" จึงเจอ "test_" ที่อินเด็กซ์ 17 +```js +let re = /test_(?!js|txt)/; +console.log(re.exec("test_js test_txt test_html")); +// [ 'test_', index: 17, input: 'test_js test_txt test_html', groups: undefined ] +``` + +1) วิธี lookbehind assertion จะมีรูปแบบ (?<=y)x ดังตัวอย่าง +ตัวอย่าง 6.45 ใช้ regex เป็น /(?<=Java)Script/ หมายถึงจะจับคู่คำว่า "Script" แต่ต้องขึ้นต้นตัวหน้าเป็น "Java" ด้วยรูปแบบ (?<=Java) และมีรูปแบบ Script แปะท้าย จึงเจอ "Script" ที่อินเด็กซ์ 14 +```js +let re = /(?<=Java)Script/; +console.log(re.exec("This is a JavaScript book")); +// [ 'Script', index: 14, input: 'This is a JavaScript book', groups: undefined ] +``` + +ตัวอย่าง 6.46 ใช้ regex เป็น /(?<=Java)[a-zA-Z\s]+/ หมายถึงจะจับคู่คำด้วยรูปแบบ [a-zA-Z\s]+ แต่ต้องขึ้นต้นตัวหน้าเป็น "Java" ด้วยรูปแบบ (?<=Java) และมีรูปแบบ [a-zA-Z\s]+ แปะท้าย จึงเจอ "Script book" ที่อินเด็กซ์ 14 + +```js +let re = /(?<=Java)[a-zA-Z\s]+/; +console.log(re.exec("This is a JavaScript book")); +// [ 'Script book', index: 14, input: 'This is a JavaScript book', groups: undefined ] +``` + +2) วิธี negative lookbehind assertion จะมีรูปแบบ (?<!y)x ดังตัวอย่าง +ตัวอย่าง 6.47 ใช้ regex เป็น /(?<!test).js/ หมายถึงจะจับคู่ด้วยคำว่า ".js" ด้วยรูปแบบ .js แต่ต้องไม่ขึ้นต้นด้วยคำว่า "test" ด้วยรูปแบบ (?<!test) จึงเจอคำว่า ".js" ที่อินเด็กซ์ 14 +```js +let re = /(?")); // [ ' ', index: 1, input: '< >', groups: undefined ] +``` + +ตัวอย่าง 6.49 ใช้ regex เป็น /\x41/ ซึ่งรหัส \x41 ก็คืออักษร "A" หมายถึงจะจับคู่อักษร "A" ในสตริง โดยจะค้นหาตัวแรกที่พบเจอ ซึ่งจะเจออักษร "A" ในสตริง "THAI" ที่อินเด็กซ์ 2 +```js +var re = /\x41/; +console.log(re.exec("THAI")); // [ 'A', index: 2, input: 'THAI', groups: undefined ] +``` + +ตัวอย่าง 6.50 ใช้ regex เป็น /\u0E14/ ซึ่งรหัส \u0E14 ก็คืออักษร "ด" หมายถึงจะจับคู่อักษร "ด" ในสตริง โดยจะค้นหาตัวแรกที่พบเจอ ซึ่งจะเจออักษร "ด" ในสตริง "แดง" ที่อินเด็กซ์ 1 +```js +var re = /\u0E14/; +console.log(re.exec("แดง")); // [ 'ด', index: 1, input: 'แดง', groups: undefined ] +``` + +ตัวอย่าง 6.51 ใช้ regex เป็น /\+/, /\*/,/\?/, /\\/, /\./ โดยต้องมีเครื่องหมาย \ วางนำหน้า เพื่อจับคู่อักษร "+", "*", "?", "\", "." ไม่เช่นนั้นจะจับคู่ไม่ได้ เพราะอักษรพวกนี้ถูกใช้เป็นสัญลักษณ์หนึ่งภายใน regex จึงจับคู่โดยตรงไม่ได้ +```js +console.log(/\+/.test("+")); // true +console.log(/\*/.test("*")); // true +console.log(/\?/.test("?")); // true +console.log(/\\/.test("\x5C")); // true -- x5C คือค่า ASCII ของ "\" +console.log(/\./.test(".")); // true +``` + +ตัวอย่าง 6.52 ใช้ regex เป็น /./ หมายถึงจะจับคู่ตัวอักษรใดๆ จำนวน 1 ตัว โดยจะค้นหาตัวแรกที่พบเจอ ซึ่งจะเจออักษร "d" ในสตริง "dog" ที่อินเด็กซ์ 0 +```js +var re = /./; +console.log(re.exec("dog")); // [ 'd', index: 0, input: 'dog', groups: undefined ] +``` + +ตัวอย่าง 6.53 ใช้ regex เป็น /.ox/ หมายถึงจะจับคู่ตัวอักษรที่ขึ้นต้นด้วยอะไรก็ได้ แต่ขอให้ตามด้วย ox โดยจะค้นหาคำแรกที่พบเจอ ซึ่งจะเจออักษร "box" ในสตริง "boxes" ที่อินเด็กซ์ 0 +```js +var re = /.ox/; +console.log(re.exec("boxes")); // [ 'box', index: 0, input: 'boxes', groups: undefined ] +``` + +ตัวอย่าง 6.54 ใช้ regex เป็น /\d/ หมายถึงจะจับคู่ตัวเลขใดๆ จำนวน 1 ตัว โดยจะค้นหาคำแรกที่พบเจอ ซึ่งจะเจออักษรเลข "8" ในสตริง "x86" ที่อินเด็กซ์ 1 (ไม่เจอเลข 6) +```js +var re = /\d/; +console.log(re.exec("x86")); // [ '8', index: 1, input: 'x86', groups: undefined ] +``` + +ตัวอย่าง 6.55 ใช้ regex เป็น /..\d/ หมายถึงจะจับคู่ลำดับตัวอักษรทั้งหมด 3 ตัว โดยสองตัวแรกเป็นตัวอักษรใดๆ ก็ได้ (ใช้จุดสองอันติดกัน) ส่วนตัวอักษรที่สาม จะเป็นตัวเลข (ใช้ \d) โดยจะค้นหาคำแรกที่พบเจอ ซึ่งจะเจอคำว่า "em1" ในสตริง "item1" ที่อินเด็กซ์ 2 +```js +var re = /..\d/; +console.log(re.exec("item1")); // [ 'em1', index: 2, input: 'item1', groups: undefined ] +``` + +ตัวอย่าง 6.56 ใช้ regex เป็น /\D/ หมายถึงจะจับคู่ตัวอักษรใดๆ จำนวน 1 ตัว ที่ไม่ใช่ตัวเลข โดยจะค้นหาคำแรกที่พบเจอ ซึ่งจะเจออักษรเลข "p" ในสตริง "100px" ที่อินเด็กซ์ 3 (ไม่เจออักษร "x") +```js +var re = /\D/; +console.log(re.exec("100px")); // [ 'p', index: 3, input: '100px', groups: undefined ] +``` + +ตัวอย่าง 6.57 ใช้regex เป็น /\w/ หมายถึงจะจับคู่ตัวอักษรใดๆ จำนวน 1 ตัว ได้แก่ ตัวอักษร a ถึง z หรือ A ถึง Z หรือ 0 ถึง 9 รวมทั้งตัวอักษร _ โดยจะค้นหาคำแรกที่พบเจอ ซึ่งในตัวอย่างนี้จะเจอหลายตัวอักษรที่อินเด็กซ์ 1 (ไม่จับคู่ "$") +```js +var re = /\w/; +console.log(re.exec("$a")); // [ 'a', index: 1, input: '$a', groups: undefined ] +console.log(re.exec("$T")); // [ 'T', index: 1, input: '$T', groups: undefined ] +console.log(re.exec("$7")); // [ '7', index: 1, input: '$7', groups: undefined ] +console.log(re.exec("$_")); // [ '_', index: 1, input: '$_', groups: undefined ] +``` + +ตัวอย่าง 6.58 ใช้ regex เป็น /\W/ หมายถึงจะจับคู่ตัวอักษรใดๆ จำนวน 1 ตัว ที่ไม่ใช่ตัวอักษร a ถึง z และไม่ใช่ A ถึง Z และไม่ใช่ 0 ถึง 9 รวมทั้งไม่ใช่ตัวอักษร _ โดยจะค้นหาคำแรกที่พบเจอ ซึ่งในตัวอย่างนี้ก็จะเจออักษร "$" ที่อินเด็กซ์ 1 +```js +var re = /\W/; +console.log(re.exec("a$")); // [ '$', index: 1, input: 'a$', groups: undefined ] +console.log(re.exec("T$")); // [ '$', index: 1, input: 'T$', groups: undefined ] +console.log(re.exec("7$")); // [ '$', index: 1, input: '7$', groups: undefined ] +console.log(re.exec("_$")); // [ '$', index: 1, input: '_$', groups: undefined ] +``` + +ตัวอย่าง 6.59 ใช้ regex เป็น /\s/ หมายถึงจะจับคู่ตัวอักษรช่องว่าง โดยจะค้นหาคำแรกที่พบเจอ ซึ่งจะเจอช่องว่าง " " ในสตริง "^ ^" ที่อินเด็กซ์ 1 +```js +var re = /\s/; +console.log(re.exec("^ ^")); // [ ' ', index: 1, input: '^ ^', groups: undefined ] +``` + +ตัวอย่าง 6.60 เราสามารถพิมพ์ช่องว่างเว้นวรรคภายใน regex โดยไม่ต้องใช้ \s ก็ได้ +```js +var re = / /; +console.log(re.exec("^ ^")); // [ ' ', index: 1, input: '^ ^', groups: undefined ] +``` + +ตัวอย่าง 6.61 ใช้ regex เป็น /\S/ หมายถึงจะจับคู่ตัวอักษรใดๆ ที่ไม่ใช่อักษรว่าง จำนวน 1 ตัว โดยจะค้นหาคำแรกที่พบเจอ ซึ่งจะเจออักษร "@" ในสตริง " @" ที่อินเด็กซ์ 1 (มีช่องว่างนำหน้า @) +```js +var re = /\S/; +console.log(re.exec(" @")); // [ '@', index: 1, input: ' @', groups: undefined ] +``` + +ตัวอย่าง 6.62 ประยุกต์ใช้ . ร่วมกับ * เพื่อจับคู่ตัวอักษรตั้งแต่ศูนย์ตัวขึ้นไป โดยต้องมี <p> ครอบเปิด และปิดท้ายด้วย </p> โดยในตัวอย่างนี้จะเจอข้อความ "<p>@test</p>" ที่อินเด็กซ์ 5 +```js +var re = /

.*<\/p>/; +console.log(re.exec("

@test

")); +// [ '

@test

', index: 5, input: '

@test

', groups: undefined ] +``` + +ตัวอย่าง 6.63 ประยุกต์ใช้ \d ร่วมกับ ^ และ \w ร่วมกับ {1,} เพื่อจับคู่สตริงที่ต้องขึ้นต้นด้วยตัวเลข แล้วตามด้วยอักษรที่อยู่ในช่วง a ถึง z หรือ A ถึง Z หรือ 0 ถึง 9 รวมทั้งตัวอักษร _ ที่มีตั้งแต่ 1 ตัวขึ้นไป โดยในตัวอย่างนี้จะเจอข้อความ "1_log" ที่อินเด็กซ์ 0 +```js +var re = /^\d\w{1,}/; +console.log(re.exec("1_log.txt")); // [ '1_log', index: 0, input: '1_log.txt', groups: undefined ] +``` + +### Unicode character properties + +ตัวอย่าง 6.64 ระบุพร็อพเพอร์ตี้ของยูนิโคดป็น Script +```js +var result = /\p{Script=Greek}+/u.test("μετά"); +console.log(result); // true +var result = /\p{Script=Thai}+/u.test("หนังสือไทย"); +console.log(result); // true +``` + +ตัวอย่าง 6.65 ระบุพร็อพเพอร์ตี้ของยูนิโคดป็น Uppercase_Letter +```js +var result = /\p{Uppercase_Letter}/u.test( "THAI" ); +console.log(result); // true +``` + +ตัวอย่างข้างต้นจะเสมือนเขียนซอร์สโค้ดดังนี้ +```js +var result = /\p{General_Category=Uppercase_Letter}/u.test( "THAI" ); +console.log(result); // true +``` + +ตัวอย่าง 6.66 การจับคู่โดยใช้ unicode property escapes แบบต่างๆ +```js +var str = "This is a book."; +var result = /\p{White_Space}/u.test( str ); +console.log(result); // true +var result = /\p{Lowercase_Letter}/u.test (str ); +console.log(result); // true +var result = /\p{Uppercase_Letter}/u.test (str ); +console.log(result); // true +``` + +### Capture groups + +ตัวอย่าง 6.67 ใช้ regex เป็น /(log)_(html)/ เพื่อทำการ capture groups โดยจะมี 2 กลุ่มย่อยที่ถูก catpure ได้แก่ (log) กับ (html) +```js +var result = /(log)_(html)/.exec("save log_html.txt"); +console.log(result.index); // 5 +console.log(result[0]); // "log_html" +console.log(result[1]); // "log" +console.log(result[2]); // "html" +console.log(result.input); // "save log_html.txt" +``` + +ตัวอย่าง 6.68 ใช้ regex เป็น /file{2}/ เปรียบเทียบกับ /(file){2}/ เพื่อค้นหาข้อความในสตริง +```js +var re = /file{2}/; +console.log(re.exec("filefile")); // null +console.log(re.exec("filee")); // [ 'filee', index: 0, input: 'filee', groups: undefined ] +var re = /(file){2}/; +console.log(re.exec("filefile")); // [ 'filefile', 'file', index: 0, input: 'filefile', groups: undefined ] +``` + +ตัวอย่าง 6.69 ใช้ regex เป็น /(?:<p>)abc(?:</p>)/ เพื่อทำการ capture group โดยจะไม่จำผลการ capture +```js +var re = /(?:

)abc(?:<\/p>)/; +let result = re.exec("

abc

"); +console.log(result[0]); // "

abc

" +console.log(result[1]); // undefined +console.log(result[2]); // undefined +console.log(result); // [ '

abc

', index: 0, input: '

abc

', groups: undefined ] +``` + +ตัวอย่าง 6.70 ใช้ regex เป็น /([a-z]+).js/ โดยไม่ได้ตั้งชื่อ group +```js +let re = /([a-z]+).js/; +console.log(re.exec("test index.js")); +// [ 'index.js', 'index', index: 5, input: 'test index.js', groups: undefined ] +``` + +ตัวอย่าง 6.71 ใช้ regex เป็น /(?[a-z]+).js/ +```js +let re = /(?[a-z]+).js/; +let matchObj = re.exec("test index.js") +console.log(matchObj); +/* แสดงผลลัพธ์ +[ + 'index.js', + 'index', + index: 5, + input: 'test index.js', + groups: { filename: 'index' } +] */ +console.log(matchObj .groups.filename); // "index" +``` + +ตัวอย่าง 6.72 ใช้ regex เป็น /bk(@)th(->)com\2/ โดยมีการ capture สองกลุ่มย่อย ได้แก่ (@) กับ (.>) แต่ให้สังเกตมีการใช้ \2 หมายถึงชุดตัวอักษรที่ถูกจับคู่ จะใช้ผลการ capture ก่อนหน้านี้ ด้วยวงเล็บอันที่สองก็คือ (.>) +```js +var re = /bk(@)th(.>)com\2/; +console.log(re.exec("bk@th=>com=>mail")); +// [ 'bk@th=>com=>', '@', '=>', index: 0, input: 'bk@th=>com=>mail', groups: undefined ] +``` + +ตัวอย่าง 6.73 ใช้ regex เป็น /talk(?ed|ing) & watch\k/ โดยมีการตั้งชื่อกลุ่มที่ได้จากการ capture เป็น "tense" และมีการอ้างถึงผลการ capture ก่อนหน้านี้ ด้วยการอ้างชื่อกลุ่ม ได้แก่ "tense" ด้วยรูปแบบ \k<tense> +```js +var re = /talk(?ed|ing) & watch\k/; +console.log(re.exec("talked & watched")); +// [ 'talked & watched', 'ed', index: 0, input: 'talked & watched', groups: { tense: 'ed' } ] +``` + +### เมธอดของสตริงที่ใช้งานร่วมกับ regex + +ตัวอย่าง 6.74 ลองพิจารณาการใช้เมธอดของสตริงร่วมกับ regex +```js +console.log("012Hellooooo".search(/Hello+/)); // 3 +var result = "Hellooooo Hello".match(/Hello+/g); +console.log(result); // [ 'Hellooooo', 'Hello' ] +console.log(result.length); // 2 +var str = "Hellooooo".replace(/Hello+/,"Bye"); +console.log(str); // "Bye" +var split = "1,2,3".split(/,/); +console.log(split); // [ '1', '2', '3' ] +``` + +### ค่าแฟล็ก + + + +ตัวอย่าง 6.75 ลองพิจารณาการตรวจสอบว่า ตอนนี้ regex ใช้งานแฟล็กอะไรบ้าง +```js +var regex = /Hello+/gi; +console.log(regex.lastIndex); // 0 +console.log(regex.source); // "Hello+" +console.log(regex.flags); // "gi" +console.log(regex.global); // true +console.log(regex.ignoreCase); // true +console.log(regex.multiline); // false +console.log(regex.unicode); // false +console.log(regex.sticky); // false +console.log(regex.dotAll); // false +console.log(regex.hasIndices); // false +``` + +### พร็อพเพอร์ตี้ flags + +ตัวอย่าง 6.76 การใช้งานพร็อพเพอร์ตี้ flags +```js +var myRegex = /foo/i; +console.log(myRegex.source); // "foo" +console.log(myRegex.flags); // "i" +``` + +### แฟล็ก i + +ตัวอย่าง 6.77 การใช้งานแฟล็ก i เพื่อค้นหาคำว่า "thai" โดยไม่สนใจตัวพิมพ์ใหญ่หรือเล็ก +```js +var re = /thai/i; +console.log("I' am THAI".search(re)); // 6 +``` + +### แฟล็ก m + +ตัวอย่าง 6.78 เป็นการใช้แฟล็ก m ร่วมกับ $ เพื่อหาคำลงท้ายในแต่ละบรรทัด +```js +var str = "I' am Thai.\nI live in Bangkok." +var re1 = /Thai.$/; +var re2 = /Thai.$/m; +console.log(str.search(re1)); // -1 -- หาไม่เจอ +console.log(str.search(re2)); // 6 +``` + +ตัวอย่าง 6.79 เป็นการใช้แฟล็ก m ร่วมกับ ^ เพื่อหาคำขึ้นต้นในแต่ละบรรทัด +```js +var str = "It's a good job.\nThe best show."; +var re1 = /^The/; +var re2 = /^The/m; +console.log(str.search(re1)); // -1 -- หาไม่เจอ +console.log(str.search(re2)); // 17 +``` + +ตัวอย่าง 6.80 ถ้ามีการใช้แฟล็ก m ค่าพร็อพเพอร์ตี้ multiline จะเป็น true +```js +let regex = /foo/m; +console.log(regex.multiline); // true +``` + + +### แฟล็ก g + +ตัวอย่าง 6.81 การจับคู่ในสตริงเมื่อปราศจากแฟล็ก g +```js +var re = /ok/; +console.log(re.exec("ok ok ok")); +// [ 'ok', index: 0, input: 'ok ok ok', groups: undefined ] +``` + +ตัวอย่าง 6.82 เป็นการใช้แฟล็ก g ร่วมกับเมธอด exec() +```js +var myRegex = /Hello+/g; +var str = "01Hello Hellooo89"; +var result = myRegex.exec(str); // ค้นหาครั้งแรก +console.log(result[0]); // "Hello" +console.log(result.index); // 2 +console.log(myRegex.lastIndex); // 7 +myRegex.exec(str); // ค้นหาครั้งที่ 2 +console.log(result[0]); // "Hello" +console.log(result.index); // 2 +console.log(myRegex.lastIndex); // 15 +myRegex.exec(str); // ค้นหาครั้งที่ 3 +console.log(result[0]); // "Hello" +console.log(result.index); // 2 +console.log(myRegex.lastIndex); // 0 +myRegex.exec(str); // ค้นหาครั้งที่ 4 +console.log(result[0]); // "Hello" +console.log(result.index); // 2 +console.log(myRegex.lastIndex); // 7 +``` + +ตัวอย่าง 6.83 อยากให้ลองพิจารณา regex ที่มีหน้าตาเหมือนกัน ได้แก่ /Hello+/ +```js +console.log( /Hello+/g === /Hello+/g); // false +/Hello+/g.exec("Hello Hello Hello"); // บรรทัด 2 +console.log(/Hello+/g.lastIndex); // 0 +``` + +ตัวอย่าง 6.84 เป็นการใช้แฟล็ก g ร่วมกับเมธอด match() ของสตริง +```js +var myRegex = /Hello+/g; +var result = "01Hello Hellooo89".match(myRegex); +console.log(result); // [ 'Hello', 'Hellooo' ] +console.log(myRegex.lastIndex) // 0 +``` + +### แฟล็ก u + +ตัวอย่าง 6.85 เป็นปัญหาเมื่อใช้ regex จับคู่ข้อความในสตริงที่เป็นภาษาจีน +```js +var str = "𠮷"; +console.log(str.length); // 2 +console.log(/^.$/.test(str)); // false +``` + +ตัวอย่าง 6.86 เป็นการใช้แฟล็ก u จับคู่ข้อความภายในสตริง +```js +var str = "𠮷"; +console.log(/^.$/u.test(str)); // true +``` + +ตัวอย่าง 6.87 ประยุกต์ใช้แฟล็ก u เพื่อนับจำนวนตัวอักษรที่เป็นภาษาชาติไหนก็ได้ +```js +var result1 = "𠮷กขคง𤭢".match(/[\s\S]/gu); +console.log(result1.length); // 6 +// ถ้าไม่ใช้แฟล็ก u จะนับตัวอักษรผิด +var result2 = "𠮷กขคง𤭢".match(/[\s\S]/g); +console.log(result2.length); // 8 +``` + +ตัวอย่าง 6.88 ถ้ามีการใช้แฟล็ก u ค่าพร็อพเพอร์ตี้ unicode จะเป็น true +```js +let regex = /foo/u; +console.log(regex.unicode); // true +``` + +### แฟล็ก y + +ตัวอย่าง 6.89 เปรียบเทียบการใช้แฟล็ก g, แฟล็ก y และไม่ได้ใช้แฟล็กใดๆ เลย +ตัวอย่าง 6.90 จากตัวอย่างก่อนหน้า ถ้าเรียกเมธอด exec() เป็นครั้งที่ 2 จะได้ผลลัพธ์ดังนี้ +```js +var str = "foo1_foo2_foo3"; // สตริงที่จะค้นหา +var regex = /foo\d_?/; // ไม่มีแฟล็ก +var regexG = /foo\d_?/g; // แฟล็ก g +var regexY = /foo\d_?/y; // แฟลก y +var result = regex.exec(str); +var resultG = regexG.exec(str); +var resultY = regexY.exec(str); +console.log(result[0]); // "foo1_" +console.log(resultG[0]); // "foo1_" +console.log(resultY[0]); // "foo1_" +console.log(regex.lastIndex); // 0 +console.log(regexG.lastIndex); // 5 +console.log(regexY.lastIndex); // 5 + +result = regex.exec(str); +resultG = regexG.exec(str), +resultY = regexY.exec(str); +console.log(result[0]); // "foo1_" +console.log(resultG[0]); // "foo2_" +console.log(resultY[0]); // "foo2_" +console.log(regex.lastIndex); // 0 +console.log(regexG.lastIndex); // 10 +console.log(regexY.lastIndex); // 10 +``` + +ตัวอย่าง 6.91 ทำการระบุ lastIndex ก่อนการจับคู่ในสตริง +```js +var str = "foo1_foo2_foo3"; +var regex = /foo\d_?/; // ไม่มีแฟล็ก +var regexG = /foo\d_?/g; // แฟล็ก g +var regexY = /foo\d_?/y; // แฟลก y +regex.lastIndex = 1; +regexG.lastIndex = 1; +regexY.lastIndex = 1; +var result = regex.exec(str); +var resultG = regexG.exec(str); +var resultY = regexY.exec(str); +console.log(result[0]); // "foo1_" +console.log(resultG[0]); // "foo2_" +console.log(resultY); // มีค่าเป็น null เพราะค้นหาไม่เจอข้อความ +``` + +ตัวอย่าง 6.92 เป็นการอ่านค่าจากพร็อพเพอร์ตี้ sticky +```js +var myRegex = /foo+/y; +console.log(myRegex.sticky); // true +myRegex.sticky = 1; // ไม่สามารถแก้ไขค่าได้ มีไว้อ่านอย่างเดียว ถ้าอยู่ในโหมดสตริคท์จะเกิด TypeError +console.log(myRegex.sticky); // true +``` + +### แฟล็ก s (dotAll) + +ตัวอย่าง 6.93 ปัญหาเมื่อ regex ไม่สามารถจับคู่ตัวอักษร "\n" ได้ +```js +let regex = /./; +let result = regex.test("\n"); +console.log(result); // false +``` + +ตัวอย่าง 6.94 เป็นการใช้แฟล็ก s เพื่อจับคู่ตัวอักษร "\n" +```js +let regex = /./s; +let result = regex.test("\n"); +console.log(result); // true +``` + +ตัวอย่าง 6.95 ถ้ามีการใช้แฟล็ก s ค่าพร็อพเพอร์ตี้ dotAll จะเป็น true +```js +let regex = /./s; +console.log(regex.dotAll); // true +``` + +### แฟล็ก d + +ตัวอย่าง 6.96 เป็นการใช้แฟล็ก d ร่วมกับเมธอด exec() โดยยังไม่ทำการ capture ใดๆ +```js +let matchObj = /bar/d.exec("foo bar"); +console.log(matchObj) +/* แสดงผลลัพธ์เป็น +[ + 'bar', + index: 4, + input: 'foo bar', + groups: undefined, + indices: [ [ 4, 7 ], groups: undefined ] +] */ +console.log(matchObj.indices[0]) // [ 4, 7 ] -- จะเป็นตำแหน่งของคำว่า "bar" +``` + +ตัวอย่าง 6.97 ทำการ capture พร้อมกับระบุแฟล็กเป็น d +```js +let matchObj = /(foo).(bar)/d.exec("0123foo_bar"); +console.log(matchObj); +/* แสดงผลลัพธ์ +[ + 'foo_bar', + 'foo', + 'bar', + index: 4, + input: '0123foo_bar', + groups: undefined, + indices: [ [ 4, 11 ], [ 4, 7 ], [ 8, 11 ], groups: undefined ] +] */ +console.log(matchObj.indices[0]) // [ 4, 11 ] -- จะเป็นตำแหน่งของคำว่า "foo_bar" +console.log(matchObj.indices[1]) // [ 4, 7 ] -- จะเป็นตำแหน่งของคำว่า "foo" +console.log(matchObj.indices[2]) // [ 8, 11 ] -- จะเป็นตำแหน่งของคำว่า "bar" +``` + +ตัวอย่าง 6.98 จากตัวอย่างก่อนหน้า เราสามารถ capture ด้วยการะบุชื่อ group และใช้แฟล็ก d ด้วยก็ได้ +```js +let matchObj = /(?foo).(?bar)/d.exec("0123foo_bar"); +console.log(matchObj); +/* แสดงผลลัพธ์ +[ + 'foo_bar', + 'foo', + 'bar', + index: 4, + input: '0123foo_bar', + groups: { first: 'foo', last: 'bar' }, + indices: [ + [ 4, 11 ], + [ 4, 7 ], + [ 8, 11 ], + groups: { first: [ 4, 7 ], last: [ 8, 11 ] } + ] +] */ +console.log(matchObj.indices.groups.first) // [ 4, 7 ] +console.log(matchObj.indices.groups.last) // [ 8, 11 ] +``` + +ตัวอย่าง 6.99 ถ้ามีการใช้แฟล็ก d ค่าพร็อพเพอร์ตี้ hasIndices จะเป็น true +```js +let regex = /bar/d; +console.log(regex.hasIndices); // true +``` + +### RegExp + +ตัวอย่าง 6.100 การสร้าง regex จาก RegExp +```js +var myRegex = /foo/g; +var regex2 = new RegExp(myRegex); +console.log(regex2.test("foo")); // true +console.log(myRegex === regex2); // false +``` + +ตัวอย่าง 6.101 การสร้าง regex จาก RegExp พร้อมกับระบุค่าแฟล็ก +```js +var myRegex = /foo/i; +var regex2 = new RegExp(myRegex, "g"); +console.log(myRegex.test("FOO")); // true (ไม่สนใจตัวพิมพ์ใหญ่พิมพ์เล็ก) +console.log(regex2.test("FOO")); // false +``` + +ตัวอย่าง 6.102 การสร้าง regex จากสตริง +```js +var myRegex = new RegExp("foo", "y"); // จะเสมือนเขียน var myRegex = /foo/y; +console.log(myRegex.exec("foo_abc")); // [ 'foo', index: 0, input: 'foo_abc', groups: undefined ] +``` \ No newline at end of file diff --git a/examples_book/Chapter07.md b/examples_book/Chapter07.md new file mode 100644 index 0000000..8b802a0 --- /dev/null +++ b/examples_book/Chapter07.md @@ -0,0 +1,668 @@ +# โค้ดบทที่ 7 ทบทวน HTML DOM + +## DOM คืออะไร +```html + + + + "My title" + + +

"My head 1"

+

"My head 2"

+ + +``` + +## การเข้าถึง element + +```html + + + + +
My div 1
+
My div 2
+ + +``` + +```html + + + + +
My div 1
+
My div 2
+ + + +``` + +### ตัวอย่างการใช้งานอ็อบเจ็กต์ที่พบเจอ + +```html + + + + +
+

My head 1

+
My div 2
+
+ + + + + + + +``` + + +```html + + + + +

My head 1

+

My paragraph 1

+

My paragraph 2

+

My paragraph 3

+

My paragraph 4

+ + + + + + + +``` + +### สิ่งที่ควรรู้ HTMLCollection + +หมายเหตุ ถ้าจะรันโค้ดชุดนี้ ต้องแก้ไขให้ let all_pTag มีการประกาศแค่ครั้งเดียวพอ +```html + + + + +

My First Paragraph 1

+

My First Paragraph 2

+

My First Paragraph 3

+

My First Paragraph 4

+ + + + + + + + + + + +``` + +### ตัวอย่างการเข้าถึง <from>...</form> + +หมายเหตุ ถ้าจะรันโค้ดชุดนี้ ต้องแก้ไขให้ let elem1, let elem2 มีการประกาศแค่ครั้งเดียวพอ + +```html + + + + +
+
+
+
+

+ +
+ + + + + + + + + +``` + +หมายเหตุ ถ้าจะรันโค้ดชุดนี้ ต้องแก้ไขให้ let loginForm มีการประกาศแค่ครั้งเดียวพอ + +```html + + + + +
+
+
+
+

+ +
+ + + + + + + + + +``` + +```html + + + + +
+
+
+
+

+ +
+ + + + + +``` + +## การเข้าถึงแอตทริบิวต์ของ element + +```html + + + + +

Hello World

+ + + + + + + + +``` + + +```html + + + + +

Hello World

+ + + + + + + + +``` + + +```html + + + + + + + + + + + +``` + +### การเพิ่มและลบ element +### ตัวอย่างเพิ่มและลบ <p>...</p> + +```html + + + + + + + + + + + +``` + +### ตัวอย่างเพิ่มและลบ <option>...</option> ใน select ของ HTML +```html + + + + + +

+ + +

+ + + + + + +``` + +## วิธีการแสดงเอาท์พุต + +### แสดงเอาท์พุตด้วยวิธีแรก + +```html + + + +

My First Web Page

+

My First Paragraph

+

+ + + +``` + +### แสดงเอาท์พุตด้วยวิธีที่สอง + +```html + + + +

My First Web Page

+

My first paragraph

+ + + +``` + + +```tab.html + + + + + + +

My First Web Page

+

My first paragraph

+ + + +``` + +หมายเหตุ ถ้าใช้ window.open() เมื่อนั้น document.write() จะไม่เขียนทับ +```html + + + + + + +

My First Web Page

+

My first paragraph

+ + + +``` + +### แสดงเอาท์พุตด้วยวิธีที่สาม + +```html + + + + + + +``` + +## Event คืออะไร + +```html + + + + + + +``` + +### วิธีแรก HTML Event Attributes + +```html + + + + + + +``` + + +```html + + + + + + + +``` + +```html + + + + + + + +``` + +### วิธีที่ 2 ใช้ HTML DOM กำหนดซอร์สโค้ดจัดการ event + +```html + + + + + + + +``` + +ตัวอย่างแรก แปะจาวาสคริปต์ไว้ที่แอตทริบิวต์ของ element ใน HTML ได้เลย +```html + + + + + + +``` + +ตัวอย่างที่สอง จับแยกซอร์สโค้ดจาวาสคริปต์กับ HTML ออกจากกัน ไม่ต้องเรียกจาวาสคริปต์ใน element ของ HTML ดังนี้ + +```html + + + + + + + +``` + +### วิธีที่ 3 ใช้ DOM EventListener + +```html + + + + + + + +``` + +```html + + + +

+ + + + +``` + + +```html + + + +

+ + + + +``` + +```html + + + +

+ + + + +``` + +## Event bubbling หรือ Event capturing + +```html + + + +
+
+ +
+
+ + +``` \ No newline at end of file diff --git a/examples_book/Chapter08.md b/examples_book/Chapter08.md new file mode 100644 index 0000000..06f22e4 --- /dev/null +++ b/examples_book/Chapter08.md @@ -0,0 +1,413 @@ +# โค้ดบทที่ 8 ฟีเจอร์ใหม่ของตัวเลข สตริง และ regex + +## ตัวเลข +### การแปลงสตริงเป็นตัวเลข + +```js +console.log(Number.parseInt("15")); // 15 +console.log(Number.parseFloat("123.5")); // 123.5 +console.log(Number.parseInt("abc")); // NaN (แปลงเป็นเลขจำนวนเต็มไม่ได้) +console.log(Number.parseFloat("abc")); // NaN (แปลงเป็นเลขทศนิยมไม่ได้) +``` + +```js +console.log(parseInt("15")); // 15 +console.log(parseFloat("123.5")); // 123.5 +console.log(typeof window.parseInt); // "function" (บนเว็บบราวเซอร์) +console.log(typeof window.parseFloat); // "function" (บนเว็บบราวเซอร์) +//console.log(typeof global.parseInt); // "function" (บน Node.js) +//console.log(typeof global.parseFloat); // "function" (บน Node.js) +``` + +### เลขฐาน 2 ฐาน 8 และฐาน 16 +```js +console.log(Number("100")); // 100 +console.log(Number("0b111")); // 7 +console.log(Number("0o45")); // 37 +console.log(Number("0x17")); // 23 +// ใช้ Number.parseInt() +console.log(Number.parseInt("100")); // 100 +console.log(Number.parseInt("0b111")); // 0 +console.log(Number.parseInt("0o45")); // 0 +console.log(Number.parseInt("0x17")); // 23 +``` + +### เมธอด Number.isFinite() กับ Number.isNaN() +```js +console.log(isFinite(2560)); // true +console.log(isFinite("2560")); // true +console.log(Number.isFinite(2560)); // true +console.log(Number.isFinite("2560")); // false +console.log(isNaN(NaN)); // true +console.log(isNaN("NaN")); // true +console.log(Number.isNaN(NaN) ); // true +console.log(Number.isNaN("NaN")); // false +console.log(typeof window.isFinite); // "function" (บนเว็บบราวเซอร์) +console.log(typeof window.isNaN); // "function" (บนเว็บบราวเซอร์) +//console.log(typeof global.isFinite); // "function" (บน Node.js) +//console.log(typeof global.isNaN); // "function" (บน Node.js) +``` + +### เมธอด Number.isInteger() กับ Number.isSafeInteger() +```js +console.log(Number.isInteger(34.6)); // false +console.log(Number.isInteger(34.0)); // true (เขียนเป็นเลข 34.0 ก็จริง แต่จะถูกจัดเก็บเป็นเลข 34) +console.log(Number.isInteger(34)); // true +``` + +```js +console.log(Math.pow(2,53)); // 9007199254740992 +console.log(Math.pow(2,53) + 1); // 9007199254740992 -- บรรทัด 2 +console.log(Math.pow(2,53) + 2); // 9007199254740994 -- บรรทัด 3 +console.log(Math.pow(2,53) + 3); // 9007199254740996 -- บรรทัด 4 +console.log(Math.pow(2,53) * 100); // 900719925474099200 -- บรรทัด 5 +``` + +```js +var upper = Number.MAX_SAFE_INTEGER; +console.log(Number.isSafeInteger(upper)); // true +console.log(Number.isInteger(upper)); // true +var outside_upper = upper + 1; +console.log(Number.isSafeInteger(outside_upper)); // false +console.log(Number.isInteger(outside_upper)); // true +var lower = Number.MIN_SAFE_INTEGER; +console.log(Number.isSafeInteger(lower)); // true +console.log(Number.isInteger(lower)); // true +var outside_lower = lower - 1; +console.log(Number.isSafeInteger(outside_lower)); // false +console.log(Number.isInteger(outside_lower)); // true +``` + +### ค่าคงที่ Number.EPSILON +```js +console.log(Number.EPSILON); // 2.220446049250313e-16 +``` +### BigInt + +```js +let max = Number.MAX_SAFE_INTEGER; +console.log(max); // 9007199254740991 +console.log(++max); // 9007199254740992 +console.log(++max); // 9007199254740992 +``` + +```js +let big = 9007199254740991n; +console.log(big) // 9007199254740991n +console.log(++big); // 9007199254740992n +console.log(++big); // 9007199254740993n +console.log(typeof big); // bigint +``` + +```js +let num = 1n; // น้อยกว่า MAX_SAFE_INTEGER +console.log(num); // 1n +``` + +```js +let num = 1000000000000n; +console.log(num.toString()); // "1000000000000" +``` + +### ใช้เครื่องหมาย Underscores (_) ในตัวเลข +```js +let num1 = 128_556_790; +let num2 = 880_000.71; +console.log(num1); // 128556790 +console.log(num2); // 880000.71 +``` + +```js +let num3 = 1000_000_000_000n; +console.log(num3.toString()); // "1000000000000" +``` + +```js +let num4 = 177_3; +console.log(num4); // 1773 +``` + +```js +let num5 = _177_3; +console.log(num5); // จะเกิด ReferenceError +``` + +```js +let num6= 1773_; +console.log(num6); // จะเกิด SyntaxError +``` + +## สตริง + +### เมธอด repeat() +```js +console.log( "JavaScript".repeat(3) ); // "JavaScriptJavaScriptJavaScript" +``` + +### เมธอด startsWith(), endsWith(), กับ includes() +```js +console.log("JavaScript".startsWith("Java")); // true (มีคำว่า "Java" อยู่ตำแหน่งแรก) +console.log("JavaScript".startsWith("world")); // false (ไม่มีคำว่า "world" อยู่ตำแหน่งแรก) +console.log("JavaScript".endsWith("Script")); // true (มีคำว่า "Script" อยู่ตำแหน่งสุดท้าย) +console.log("JavaScript".endsWith("Hello")); // false (ไม่มีคำว่า "Hello" อยู่ตำแหน่งสุดท้าย) +console.log("JavaScript".includes("va")); // true (มีคำว่า "va" อยู่ในสตริง) +console.log("JavaScript".includes("same")); // false (ไม่มีคำว่า "same" อยู่ในสตริง) +``` + +```js +console.log("JavaScript".startsWith("ri", 6)); // true +console.log("JavaScript".endsWith("va", 4)); // true +console.log("JavaScript".includes("ri", 2)); // true +``` + +### ยูนิโคด +```js +var a = "ABC"; +console.log(a.length); // 3 +console.log(a.codePointAt(0)); // 65 +console.log(a.codePointAt(1)); // 66 +console.log(a.codePointAt(2)); // 67 +console.log(a.codePointAt(3)); // undefined +``` + +```js +var thai = "กขค"; +console.log(thai.length); // 3 +console.log(thai.codePointAt(0).toString(16)); // e01 +console.log(thai.codePointAt(1).toString(16)); // e02 +console.log(thai.codePointAt(2).toString(16)); // e04 +``` + +```js +console.log(String.fromCodePoint(42)); // "*" +console.log(String.fromCodePoint(65, 66, 67)); // "ABC" +console.log(String.fromCodePoint(0xe01, 0xe02, 0xe04) ); // "กขค" +// สามารถส่งค่าอากิวเมนต์เป็นสตริงที่เขียนด้วยตัวเลข ก็สามารถทำได้เช่นกัน +console.log(String.fromCodePoint("97", "98", "99")); // "abc" +console.log(String.fromCodePoint("0xe07", "0xe08", "0xe09") ); // "งจฉ" +//String.fromCodePoint('_'); // RangeError +//String.fromCodePoint(Infinity); // RangeError +//String.fromCodePoint(-1); // RangeError +//String.fromCodePoint(NaN); // RangeError +``` + +```js +console.log("\u{e01}"); // "ก" +console.log("\u{e01}\u{e02}\u{e04}ABC"); // "กขคABC" +``` + +```js +console.log("\u0e01"); // "ก" +``` + +```js +console.log("\u20BB7"); // "₻7" +console.log("\u{20BB7}"); // "𠮷" +``` + +```js +console.log("a".length); // 1 +console.log("ก".length); // 1 +var char = "𤭢"; // "\u{24b62}" -- เป็นอักษรกลุ่ม CJK +console.log(char.length); // 2 +``` + +```js +console.log("\x41"); // "A" +console.log("\101"); // "A" - ทำไม่ได้ในโหมดสตริคท์ +``` + +### การตั้งชื่อด้วยตัวอักษรพิเศษยูนิโคด + +```js +var \u{e01} = 100; // จะเหมือนเขียน var ก = 100 +console.log(\u{e01}); // 100 +console.log("\u{e01}"); // "ก" +var ข = "JavaScript"; +console.log(ข); // "JavaScript" +``` + +```js +var \u0e01 = 100; // จะเหมือนเขียน var ก = 100 +console.log(\u0e01); // 100 +``` + +### เมธอด normalize() +```js +function toCodePoint(str) { // ฟังก์ชันแสดงค่า code point ของสตริงออกทางหน้าคอนโซล + var concat = ""; + for(var i = 0; i < str.length; i++ ) { + concat += "0x" + str.codePointAt(i).toString(16) + " "; + } + console.log(concat); +} +// U+1E9B: LATIN SMALL LETTER LONG S WITH DOT ABOVE +// U+0373: GREEK SMALL LETTER ARCHAIC SAMPI +var str = "\u{1E9B}\u{0373}"; +var s1= str.normalize("NFC"); +toCodePoint(s1); // 0x1e9b 0x373 +var s2= str.normalize("NFD"); +toCodePoint(s2); // 0x17f 0x307 0x373 +var s3 = str.normalize("NFKC"); +toCodePoint(s3); // 0x1e61 0x373 +var s4 = str.normalize("NFKD"); +toCodePoint(s4); // 0x73 0x307 0x373 +``` + + +### เมธอด padStart() + +```js +let str = "x"; +let newStr = str.padStart(5, "ab"); +console.log(newStr); // "ababx" +console.log(str); // "x" +``` + +```js +let str = "x"; +let newStr = str.padStart(4, "ab"); +console.log(newStr); // "abax" +``` + +```js +let newStr = "abc".padStart(10, "0123456789"); +console.log(newStr); // "0123456abc" +``` + +```js +let newStr = "abcd".padStart(2, "x"); +console.log(newStr); // "abcd" +``` + +```js +let newStr = "x".padStart(3); +console.log(newStr); // " x" +console.log(newStr.length); // 3 +``` + +```js +let newStr = "x".padStart(3, ' '); +console.log(newStr); // " x" +console.log(newStr.length); // 3 +``` + +### เมธอด padEnd() + +```js +// เติม ab จำนวน 2 ครั้งต่อท้าย x +let str1 = "x"; +let newStr1 = str1.padEnd(5, "ab"); +console.log(newStr1); // "xabab" +// เติม ab จำนวน 2 ครั้งต่อท้าย x แต่ครั้งที่สองจะตัดให้ a +let str2 = "x"; +let newStr2 = str2.padEnd(4, "ab"); +console.log(newStr2); // "xaba" +// เมื่อ fillString ยาวเท่ากับ maxLength +let newStr3 = "abc".padEnd(10, "0123456789"); +console.log(newStr3); // "abc0123456" +// เมื่อสตริงตัวตั้งต้นมีความยาวมากกว่าค่า maxLength +let newStr4 = "abcd".padEnd(2, "x"); +console.log(newStr4); // "abcd" +// เมื่อไม่ระบุ fillString +let newStr5 = "x".padEnd(3); +console.log(newStr5); // "x " +console.log(newStr5.length); // 3 +``` + +```js +let numStr = "12AF"; +let fillString = "0x"; +let maxLength = numStr.length + fillString.length; +console.log(numStr.padStart(maxLength, fillString)); // "0x12AF" +``` + +```js +let str1 = "OK"; +let str2 = "Javascript"; +let str3 = "Wow"; +let fillString = " "; +let maxLength = fillString.length; +console.log(maxLength); // 15 +console.log(str1.padStart(maxLength, fillString)); +console.log(str2.padStart(maxLength, fillString)); +console.log(str3.padStart(maxLength, fillString)); +/* แสดงผลลัพธ์ + OK + Javascript + Wow +*/ +``` + +```js +let filename = "test"; +let fillString = ".js"; +let maxLength = filename.length + fillString.length; +console.log(filename.padEnd(maxLength, fillString)); // "test.js" +``` + +```js +let numStr = "525"; +let fillString = ".00"; +let maxLength = numStr.length + fillString.length; +console.log(numStr.padEnd(maxLength, fillString)); // "525.00" +``` + +### เมธอด trimStart() กับ trimEnd() + +```js +let str1 = " ฉันรัก JavaScript"; +console.log(str1.trimStart()); // "ฉันรัก JavaScript" +let str2 = "ฉันรัก JavaScript "; +console.log(str2.trimEnd()); // "ฉันรัก JavaScript" +``` + +```js +let str = " ฉันรัก JavaScript "; +console.log(str.trim()); // "ฉันรัก JavaScript" +``` + +### เมธอด matchAll() + +```js +let str = "นายไก่ เลี้ยงแต่ไก่ ไม่ขายไข่ไก่"; +for(const c of str.matchAll("ไก่") ) { + console.log("เจอคำว่า", c[0], "ที่ตำแหน่ง", c.index ) +} +/* แสดงผลลัพธ์ +เจอคำว่า ไก่ ที่ตำแหน่ง 3 +เจอคำว่า ไก่ ที่ตำแหน่ง 16 +เจอคำว่า ไก่ ที่ตำแหน่ง 29 */ +``` + +```js +let str = "นายไก่ เลี้ยงแต่ไก่ ไม่ขายไข่ไก่"; +for(const c of str.matchAll(/ไก่/g) ) { + console.log("เจอคำว่า", c[0], "ที่ตำแหน่ง", c.index ) +} +/* แสดงผลลัพธ์ +เจอคำว่า ไก่ ที่ตำแหน่ง 3 +เจอคำว่า ไก่ ที่ตำแหน่ง 16 +เจอคำว่า ไก่ ที่ตำแหน่ง 29 */ +``` + +### เมธอด replaceAll() + +```js +let str = "สมชาย น้องสมปอง มีเพื่อนชื่อ สมชาย"; +let newStr = str.replace("สมชาย", "ประยุทธ์"); +console.log(newStr) // ประยุทธ์ น้องสมปอง มีเพื่อนชื่อ สมชาย +``` + +```js +let str = "สมชาย น้องสมปอง มีเพื่อนชื่อ สมชาย"; +let newStr = str.replace(/สมชาย/g, "ประยุทธ์"); +console.log(newStr) // ประยุทธ์ น้องสมปอง มีเพื่อนชื่อ ประยุทธ์ +``` + +```js +let str = "สมชาย น้องสมปอง มีเพื่อนชื่อ สมชาย"; +let newStr = str.replaceAll("สมชาย", "ประยุทธ์"); +console.log(newStr) // ประยุทธ์ น้องสมปอง มีเพื่อนชื่อ ประยุทธ์ +``` + diff --git a/examples_book/Chapter09.md b/examples_book/Chapter09.md new file mode 100644 index 0000000..8537cde --- /dev/null +++ b/examples_book/Chapter09.md @@ -0,0 +1,621 @@ +# โค้ดบทที่ 9 การประกาศตัวแปร และการกำหนดค่า + +## การประกาศตัวแปรแบบ let + +```js +function calculate(num) { + if (num > 10) { + let value = num*10; // ประกาศตัวแปรแบบ let + // ซอร์สโค้ดส่วนที่เหลือ + console.log(value); // มองเห็นตัวแปร value + } else { + // มองไม่เห็นตัวแปร value + } + // มองไม่เห็นตัวแปร value +} +``` + +### การใช้ตัวแปรแบบ var เปรียบเทียบกับ let + +```js +var a = 1; +console.log(a); // 1 +{ + var a = 2; + console.log(a); // 2 +} +console.log(a); // 2 +``` + +```js +var a = 1; +console.log(a); // 1 +{ + let a = 2; + console.log(a); // 2 +} +console.log(a); // 1 +``` + +### var กับ let ในประโยควนลูป + +```js +for(var i=0; i < 10; i++) { // วนลูป 10 ครั้ง + // ซอร์สโค้ด +} +// สามารถเข้าถึงตัวแปร i ที่ตอนนี้มีค่าเป็น 10 ได้ +console.log(i); // 10 +``` + +```js +for(let i=0; i < 10; i++) { // วนลูป 10 ครั้ง + // ซอร์สโค้ด +} +console.log(i); // จะเกิด ReferenceError เพราะมองไม่เห็นตัวแปร i +``` + +```js +var array = [ ]; +for(var i=0; i < 5; i++) { + // เพิ่มฟังก์ชันเข้าไป เพื่อให้เป็นสมาชิกของอาร์เรย์ + array.push( function () { console.log(i) } ); +} +array.forEach( function(printLog) { + // เรียกสมาชิกของอาร์เรย์ที่เป็นฟังก์ชัน ให้ทำงาน + printLog(); // จะแสดงค่าของตัวแปร i เป็นเลข 5 ทั้งหมดห้ารอบด้วยกัน +}); +/*แสดงผลลัพธ์เป็น +5 +5 +5 +5 +5 */ +``` + +```js +var array = [ ]; +for(var i=0; i< 5; i++) { + array.push( + function(item) { + // รีเทิร์นฟังก์ชันออกไป เพื่อให้เป็นสมาชิกของอาร์เรย์ + return function() { console.log(item); } ; + } ( i ) // ใช้เทคนิค IIFE + ); +} +array.forEach( function(printLog) { + printLog(); // รอบแรกแสดงค่าเป็น 0 รอบที่สองเป็น 1 รอบที่สามเป็น 2 …จนถึงรอบที่ห้าจะแสดงค่าเป็น 4 +}); +/*แสดงผลลัพธ์เป็น +0 +1 +2 +3 +4 */ +``` + +```js +var array = [ ]; +for(let i=0; i<5; i++) { // ประกาศตัวแปร i แบบ let + array.push( function () { console.log(i); } ); +} +array.forEach(function(printLog){ + printLog(); // รอบแรกแสดงค่าเป็น 0 รอบที่สองเป็น 1 รอบที่สามเป็น 2 …จนถึงรอบที่ห้าจะแสดงค่าเป็น 4 +}); +/*แสดงผลลัพธ์เป็น +0 +1 +2 +3 +4 */ +``` + + +### สรุปขอบเขตการมองเห็นตัวแปรแบบ let + +```js +console.log(count); // เกิด ReferenceError +let count = 89; // จะมองเห็นตัวแปร count ตั้งแต่จุดนี้เป็นต้นไป +if(true) { + console.log(count); // 89 +} +``` + +```js +var count = 89; +let count = 12; // เกิด SyntaxError เพราะประกาศชื่อตัวแปรซ้ำกัน +``` + +```js +var count = 89; +if(true) { + let count = 12; // จะไม่เกิด error + // จากนี้ไปจะมองเห็นตัวแปร count ที่ประกาศแบบ let เท่านั้น + console.log(count); // 12 +} +// มองเห็นและเข้าถึงตัวแปร count ที่อยู่นอกบล็อกของ if +console.log(count); // 89 +``` + +## ตัวแปรค่าคงที่ + +```js +const MAX_COUNT = 100; // ประกาศถูกต้องตามไวยากรณ์ +const MAX_VALUE; // เกิด SyntaxError เพราะไม่ได้กำหนดค่าตั้งต้นให้แต่แรก +const MESSAGE = "Hello"; // ประกาศถูกต้องตามไวยากรณ์ +MESSAGE = "Bye"; // เกิด TypeError เพราะไปแก้ไขตัวแปรค่าคงที่ภายหลังประกาศใช้งานแล้ว ซึ่งจะทำไม่ได้ +``` + +```js +console.log(count); // เกิด ReferenceError +const count = 89; // จะมองเห็นตัวแปร count ตั้งแต่จุดนี้เป็นต้นไป +if(true) { + console.log(count); // 89 +} +``` + +```js +var message = "foo"; +let count = 100; +// ประกาศตัวแปรค่าคงที่ +const message = "bar"; // เกิด SyntaxError เพราะประกาศตัวแปรชื่อซ้ำกัน +const count = 1; // เกิด SyntaxError เพราะประกาศตัวแปรชื่อซ้ำกัน +``` + +```js +var message = "foo"; +let count = 100; +if(true) { + const message = "bar"; + const count = 1; + // มองเห็นตัวแปร message และ count ที่ประกาศเป็นค่าคงที่เท่านั้น + console.log(message); // "bar" + console.log(count); // 1 +} +// มองเห็นและเข้าถึง message และ count ที่อยู่นอกบล็อกของ if +console.log(message); // "foo" +console.log(count); // 100 +``` + +### ข้อควรระวังเกี่ยวกับตัวแปรค่าคงที่ + +```js +const obj = { + value: 100 +}; +obj.value = 1; // สามารถแก้ไขค่าพร็อพเพอร์ตี้ภายในอ็อบเจ็กต์ได้ +console.log(obj.value); // 1 +obj = 10; // จะเกิด TypeError เพราะแก้ไขตัวแปรค่าคงที่ไม่ได้ +``` + +```js +for (const i=0; i < 10; i++) { // เกิด TypeError เพราะ i++ ไปแก้ไขตัวแปร i ซึ่งเป็นค่าคงที่ จะทำไม่ได้ + // ซอร์สโค้ด +} +``` + +```js +let obj = { + key1: true, + key2: true +}; +for (const key in obj) { + console.log(key); +} +/*แสดงผลลัพธ์เป็น +"key1" +"key2" */ +``` + +```js +for (const value of [1, 2]) { + console.log(value); +} +/*แสดงผลลัพธ์เป็น +1 +2 */ +``` + +## ดีสตรัคเตอร์ริ่ง + +```js +let f = { + color: "red", + size: "200", + icon: "small", + style: "normal", + lang: "thai" +} ; +// การแกะข้อมูลภายในอ็อบเจ็กต์ เพื่อไปกำหนดค่าให้กับตัวแปรทีละตัว จะดูยุ่งยากมาก +let color = f.color, size = f.size, icon = f.icon, style=f.style, lang= f.lang; +console.log(color, size, icon, style, lang); // "red 200 small normal thai" +``` + +```js +let f = ["red", "200", "small", "normal", "thai"]; +// การนำข้อมูลจากอาร์เรย์ เพื่อไปกำหนดค่าให้กับตัวแปรทีละตัว จะดูยุ่งยากมาก +let color = f[0], size = f[1], icon = f[2], style =f[3], lang=f[4]; +console.log(color, size, icon, style, lang); // "red 200 small normal thai" +``` + +### ดีสตรัคเตอร์ริ่งจากอ็อบเจ็กต์ + +```js +let font = { + color: "red", + size: 200 +} ; +let fontColor = font.color, fontSize = font.size; +``` + +```js +let font = { + color: "red", + size: 200 +} ; +// กำหนดค่าให้กับตัวแปร ด้วยวิธีดีสตรัคเตอร์ริ่ง +// จะเสมือนประกาศแบบนี้ +// let fontColor = font.color, fontSize = font.size; +let {color: fontColor, size: fontSize } = font; +// จะประกาศตัวแปรเป็นแบบ var หรือ const ก็ทำได้เช่นกัน +// var {color: fontColor, size: fontSize } = font; +// const {color: fontColor, size: fontSize } = font; +console.log(fontColor); // "red" จะมีค่าเท่ากับ font.color +console.log(fontSize); // 200 จะมีค่าเท่ากับ font.size +``` + +```js +let font = { + color: "red", + size: 200 +}; +// จะเสมือนประกาศแบบนี้ +// let color = font.color, size = font.size; +let {color, size} = font; // กำหนดค่าให้กับตัวแปร ด้วยวิธีดีสตรัคเตอร์ริ่งแบบย่อ +console.log(color); // "red +console.log(size); // 200 +``` + + +```js +let font = { + color: "red", + size: 200 +}; +let { color, size, style } = font; +console.log(color); // "red" +console.log(size); // 200 +console.log(style); // undefined +``` + +### อ็อบเจ็กต์ซ้อนอ็อบเจ็กต์ +```js +let font = { + color: "red", + size: 200, + text : { + name: "thai" + } +} ; +// จะเสมือนประกาศแบบนี้ +// let color = font.color, size = font.size, name = font.text.name; +let {color, size, text: {name} } = font; +console.log(color, size, name); // "red 200 thai" +``` + +## ดีสตรัคเตอร์ริ่งจากอาร์เรย์ + +```js +let font = [ "red", "bold", "thai"]; +// กำหนดค่าให้กับตัวแปร ด้วยวิธีดีสตรัคเตอร์ริ่ง +// จะเสมือนประกาศตัวแปรแบบนี้ +// let color = font[0], style = font[1]; +let [ color, style] = font; +// จะประกาศตัวแปรเป็นแบบ var หรือ const ก็ทำได้เช่นกัน +// var [color, style] = font; +// const [color, style] = font; +console.log(color); // "red" จะมีค่าเท่ากับ font[0] +console.log(style); // "bold" จะมีค่าเท่ากับ font[1] +``` + +```js +let font = [ "red", "bold", "thai"]; +let [ , style , ] = font; +console.log(style); // "bold" +``` + +### อาร์เรย์ซ้อนอาร์เรย์ + +```js +let font = [ "red", ["200", "thai"], "bold"]; +let [ color, [size, lang], style, option] = font; +console.log(color); // "red" +console.log(color === font[0]); // true +console.log(size); // "200" +console.log(size === font[1][0]); // true +console.log(lang); // "thai" +console.log(lang === font[1][1]); // true +console.log(style); // "bold" +console.log(style === font[2]); // true +console.log(option); // undefined +``` + +```js +let font = [ "red", ["200", "thai"], "bold"]; +let [ color, option , style] = font; +console.log(color, style); // "red bold" +console.log(option[0]); // "200" +console.log(option[1]); // "thai" +console.log(option === font[1]); // true (เพราะมันอ้างอิงไปที่อาร์เรย์ตำแหน่งเดียวกัน) +``` + +## ข้อควรรู้เพิ่มเติมของวิธีดีสตรัคเตอร์ริ่ง + +```js +let action = { + save: true, + undo: false +}; +let save, undo; +{save, undo} = action; // เกิด SyntaxError +``` + +```js +let action = { + save: true, + undo: false +}; +let save, undo; +({save, undo} = action); // ใส่วงเล็บครอบทั้งประโยคจะไม่เกิด error +console.log(save, undo); // true false +``` + +```js +let font = [ "red", "bold"]; +let color, style; +[color, style] = font; // ไม่เกิด error +console.log(color, style); // "red bold" +``` + +### การระบุค่าดีฟอลต์ให้กับตัวแปร + +```js +let { color, size = 200 } = {color:"red"} +console.log(color) // "red" +console.log(size) // 200 +``` + +```js +let [ , ,lang = "thai"] = [] +console.log(lang); // "thai" +``` + +### ข้อมูลผสมระหว่างอ็อบเจ็กต์และอาร์เรย์ + +```js +let action = { + save : "success", + undo : "none", + option : ["move", "stop", "slow"] +}; +// กำหนดค่าให้กับตัวแปรด้วยวิธีดีสตรัคเตอร์ริ่ง +let {save, undo, option: [ moveOption, stopOption]} = action; +console.log(save, undo, moveOption, stopOption); // "success none move stop" +``` + +```js +let action = { + save : "success", + undo : "none", + option : ["move", "stop", "slow"] +}; +let {save, undo, option} = action; // บรรทัด a +console.log(save, undo); // "success none" +console.log(option[0]); // "move" +console.log(option[1]); // "stop" +console.log(option[2]); // "slow" +console.log(option === action.option); // true (เพราะมันอ้างอิงไปที่อาร์เรย์ตำแหน่งเดียวกัน) +``` + +### การสลับข้อมูล + +```js +let a = 1, b =2 +let temp = a; // temp เป็นตัวแปรชั่วคราวที่ใช้เก็บค่าของ a เอาไว้ก่อน +a = b; +b = temp; +console.log(a); // 2 +console.log(b); // 1 +``` + +```js +let a = 1, b =2; +[b , a] = [a , b]; // ดีสตรัคเตอร์ริ่งจากอาร์เรย์ +console.log(a); // 2 +console.log(b); // 1 +``` + +```js +let a = 1, b = 2, c = 3, d = 4; +console.log(a, b, c, d); // 1 2 3 4 +[d, c, b ,a] = [a, b, c, d]; // ดีสตรัคเตอร์ริ่งจากอาร์เรย์ +console.log(a, b, c, d); // 4 3 2 1 +``` + +### รับค่าจากฟังก์ชัน + +```js +function myFunctin() { + return {a:1 ,b: 2}; +} +let {a, b} = myFunctin(); +console.log(a, b); // 1, 2 +``` + +```js +function myFunctin() { + return [1, 2] ; +} +let [a, b] = myFunctin(); +console.log(a, b); // 1 2 +``` + +### ข้อมูล JSON + +```js +// เป็นข้อมูล JSON ซึ่งเขียนด้วยเทมเพลตสตริง (บทที่ 11) +let jsonText = `{ + "file": "index.html", + "menu": [ + {"value": "New", "onclick": "createDoc"}, + {"value": "Open", "onclick": "openDoc"} + ] +}`; +let jsonObj = JSON.parse(jsonText); // อ็อบเจ็กต์ที่ใช้เป็นตัวแทนของ JSON +console.log(jsonObj); +/* แสดงผลลัพธ์เป็น +{ + file: 'index.html', + menu: [ + { value: 'New', onclick: 'createDoc' }, + { value: 'Open', onclick: 'openDoc' } + ] +} */ +let {file, menu:[ menu1, menu2] } = jsonObj; +console.log(file); // "index.html" +console.log(menu1.value); // "New" +console.log(menu1.onclick); // "createDoc" +console.log(menu2.value); // "Open" +console.log(menu2.onclick); // "openDoc" +``` + +### พร็อพเพอร์ตี้แบบเรสต์ + +```js +let obj = {foo: 1, bar: 2, zoo: 3}; +let {foo, ...rest} = obj; +console.log(foo); // 1 +console.log(rest) // { bar: 2, zoo: 3 } +``` + +```js +let obj = {foo: 1, bar: 2, zoo: 3}; +let {...rest, zoo} = obj; // ...rest วางไว้ด้านหน้าไม่ได้ จะเกิด SyntaxError +let {foo, ...rest1, ...rest2} = obj; // ...rest1 , ...rest2 ใช้ซ้ำกันไม่ได้ จะเกิด SyntaxError +``` + + +### Nullish Coalescing operator + + +```js +console.log(null ?? 555); // 555 +``` + +```js +console.log(undefined ?? 666); // 666 +``` + +```js +let x; // x = undefined +let a = x ?? 10; +console.log(a); // 10 +``` + +```js +let x; // undefined +let a = x || 10; // เมื่อยังไม่มีโอเปเรเตอร์ ? ให้ใช้งาน +console.log(a); // 10 +``` + +```js +let x = null; +let a = x || 10; // เมื่อยังไม่มีโอเปเรเตอร์ ? ให้ใช้งาน +console.log(a); // 10 +``` + +```js +let x = 5; +let a = x || 10; // เมื่อยังไม่มีโอเปเรเตอร์ ? ให้ใช้งาน +console.log(a); // 5 +``` + +```js +let x = 0; +let a = x || 10; // เกิดปัญหา เพราะ x มีค่าเป็น 0 ซึ่งเทียบเท่า false +console.log(a); // 10 +a = x ?? 10; // ไม่มีปัญหาเหมือน || +console.log(a); // 0 +``` + +```js +let x; // x = undefined +let a = x ?? 10; +console.log(a); // 10 +``` + +```js +let x; // x = undefined +let a; +if ( x==null || x == undefined) { + a = 10; // default +} else { + a = x; +} +console.log(a); // 10 +``` + +```js +let x; // x = undefined +let a = (x !== undefined && x !== null) ? x : 10; // ยุบเหลือบรรทัดเดียว +console.log(a); // 10 +``` + +### Logical Assignment Operator + +* ตัวอย่างโอเปอเรเตอร์ &&= + +```js +let x = true; +let y = 555; +x &&= y; // เสมือนเขียน x && (x=y); +console.log(x); // 555 +``` + +```js +let x = false; +let y = 555; +x &&= y; // เสมือนเขียน x && (x=y); +console.log(x); // false +``` + +* ตัวอย่างโอเปอเรเตอร์ ||= + +```js +let x = false; +let y = 666; +x ||= y; // เสมือนเขียน x || (x=y); +console.log(x) // 666 +``` + +```js +let x = true; +let y = 555; +x ||= y; // เสมือนเขียน x || (x=y); +console.log(x); // true +``` + +* ตัวอย่างโอเปอเรเตอร์ ??= + +```js +let x; // x = undefined +let y = 777 +x ??= y; // เสมือนเขียน x = x ?? (x=y); +console.log(x) // 777 +``` + +```js +let x=10; +let y = 777 +x ??= y; // เสมือนเขียน x = x ?? (x=y); +console.log(x) // 10 +``` diff --git a/examples_book/Chapter1.md b/examples_book/Chapter1.md deleted file mode 100644 index 3227e64..0000000 --- a/examples_book/Chapter1.md +++ /dev/null @@ -1,114 +0,0 @@ - -# บทที่ 1 แนะนำภาษาจาวาสคริปต์ - -## จาวาสคริปต์บนเว็บเบราเซอร์ -```html - - - - - -

Hello, world!

- - -``` - -```html - - - - - -

- - - -``` - -## จาวาสคริปต์ฝั่งเซิร์ฟเวอร์ -```js -var http = require('http'); -http.createServer(function (request, response) { - response.writeHead(200, {'Content-Type': 'text/plain'}); - response.end("Hello, world!"); -}).listen(8001, '127.0.0.1'); -console.log('Server running at http://127.0.0.1:8001/'); -``` - -## Traceur -```html - - - - - - - - - - -

- - - -``` - -## ตัวอย่าง Babel -```html - - - - - - - -

- - - -``` - -## ตัวอย่าง ES6 นอกเว็บเบราเซอร์ -```js -class Chat{ - constructor(message) { - this.message = message; - } - say(){ - console.log(this.message); - } -}; -let chat = new Chat("Hello, world!"); -chat.say(); -``` diff --git a/examples_book/Chapter10.md b/examples_book/Chapter10.md new file mode 100644 index 0000000..fc11461 --- /dev/null +++ b/examples_book/Chapter10.md @@ -0,0 +1,954 @@ +# โค้ดบทที่ 10 ฟังก์ชัน + +## นิพจน์ฟังก์ชัน + +```js +let f1 = function () { + // ซอร์สโค้ด +} +const f2 = function() { + // ซอร์สโค้ด +} +var f3 = function() { + // ซอร์สโค้ด +} +f1(); +f2(); +f3(); +``` + +## พารามิเตอร์แบบดีฟอลต์ + +```js +function sendMessage(ipaddress, message = "Hello" , callback = function() {}) { + // ถ้าไม่ส่งค่าอากิวเมนต์มาให้พารามิเตอร์ message จะมีค่าเป็น "Hello" + /* ถ้าไม่ส่งค่าอากิวเมนต์มาให้พารามิเตอร์ callback จะได้เป็นฟังก์ชันว่างที่ไม่ได้ทำงานอะไรเลย + แต่จะรีเทิร์นค่าเป็น undefined */ + console.log(ipaddress, message, callback() ); +} + +// ใช้ค่าดีฟอลต์ของพารามิเตอร์ message กับ callback +sendMessage("127.0.0.1"); // "127.0.0.1 Hello undefined" +// ใช้ค่าดีฟอลต์ของพารามิเตอร์ callback +sendMessage("127.0.0.1", "Good bye!"); // "127.0.0.1 Good bye! undefined" +// ไม่ได้ใช้ค่าดีฟอลต์อะไรเลย +sendMessage("127.0.0.1", "Good bye!", function() { + return "toDoSomething"; +}); // "127.0.0.1 Good bye! toDoSomething" +``` + +```js +function sendMessage(ipaddress, message = "Hello", callback = function() {}) { + console.log(ipaddress, message, callback() ); +} +// จะใช้ค่าดีฟอลต์ของพารามิเตอร์ callback เพียงตัวเดียวเท่านั้น +sendMessage("127.0.0.1", null, undefined); // "127.0.0.1 null undefined" +``` + +### ตำแหน่งการวางพารามิเตอร์แบบดีฟอลต์ + +```js +function sendMessage(ipaddress, message = "Hello" , callback) { + console.log(ipaddress, message, typeof callback); +} + +// จะใช้ค่าดีฟอลต์ของพารามิเตอร์ message เพียงตัวเดียวเท่านั้น +sendMessage("127.0.0.1"); // "127.0.0.1 Hello undefined" + +sendMessage("127.0.0.1", "Hello", function() {}); // "127.0.0.1 Hello function" +// พารามิเตอร์ message จะได้ค่าเป็น null +sendMessage("127.0.0.1",null, function() {}); // "127.0.0.1 null function" +// พารามิเตอร์ message จะใช้ค่าดีฟอลต์ +sendMessage("127.0.0.1", undefined, function() {}); // "127.0.0.1 Hello function" +``` + +```js +function sendMessage(ipaddress, callback, message = "Hello") { + console.log(ipaddress, message, typeof callback); +} +sendMessage("127.0.0.1", function() {}); // "127.0.0.1 Hello function" +``` + +### ประโยชน์ของพารามิเตอร์แบบดีฟอลต์ + +```js +function sendMessage(ipaddress, message , callback) { + message = message || "Hello"; + callback = callback || function() { return "callback";}; + console.log(ipaddress, message, callback()); +} +// message กับ callback จะใช้ค่าดีฟลอต์ +sendMessage("127.0.0.1"); // "127.0.0.1 Hello callback" +sendMessage("127.0.0.1", 0, null); // "127.0.0.1 Hello callback" +sendMessage("127.0.0.1", NaN, ''); // "127.0.0.1 Hello callback" +``` + +```js +let value = 1; +function getMessage() { + return "My_message_" + (value++); +} +function createCallback() { + return function() { + return "callback"; + }; +} +function sendMessage(message = getMessage(), callback = createCallback() ) { + console.log(message, callback()); +} +sendMessage(); // "My_message_1 callback" +sendMessage(); // "My_message_2 callback" +``` + +```js +function add(value) { + return value + 10; +} +function calculate(a, b = add(a), c = a * b) { + console.log(a, b, c) ; +} +calculate(1,1,1); // 1 1 1 +calculate(10); // 10 20 200 +calculate(20); // 20 30 600 +calculate(30); // 30 40 1200 +``` + +## พารามิเตอร์แบบเรสต์ + +```js +function iterateItem(item) { + console.log(item); // แสดงค่าอากิวเมนต์ตัวแรกออกมาก่อน + let result = 0; + let len = arguments.length; + for(let i=1; i { + return value; +}; +// เรียกใช้ฟังก์ชันได้เหมือนปกติธรรมดา +console.log(arrowFunc(122)); // 122 +``` + +* ตัวอย่างที่ 2 ถ้าบอดี้ของฟังก์ชันลูกศรมีนิพจน์เพียงตัวเดียว ก็ไม่ต้องมีเครื่องหมายปีกกาครอบตัวบอดี้ และค่าของนิพจน์ดังกล่าวจะถูกรีเทิร์นออกมา (ไม่ต้องมีคีย์เวิร์ด return) + +```js +// เหมือนในตัวอย่างที่ 1 แต่การเขียนจะสั้นและกระชับกว่า +// ไม่ต้องมีเครื่องหมายปีกกาครอบบอดี้ฟังก์ชัน รวมทั้งไม่ต้องเขียนประโยคคำสั่ง return +let arrowFunc = value => value; +console.log(arrowFunc(122)); // 122 +/* จะเสมือนเขียนเป็น +let arrowFunc = function(value) { + return value; +};*/ +let arrowFunc2 = value => console.log(value); +arrowFunc2(122); // 122 +/* จะเสมือนเขียนเป็น +let arrowFunc2 = function(value) { + return console.log(value); +};*/ +``` + +* ตัวอย่างที่ 3 ถ้าฟังก์ชันลูกศรไม่มีการประกาศพารามิเตอร์ ก็ต้องใส่วงเล็บเข้าไปแทน + +```js +// ฟังก์ชันลูกศรที่ไม่มีการประกาศพารามิเตอร์อะไรเลย +let arrowFunc = () => 122; +console.log(arrowFunc()); // 122 +/* จะเสมือนเขียนเป็น +let arrowFunc = function() { + return 122; +};*/ +``` + +* ตัวอย่างที่ 4 จากตัวอย่างหน้าก่อนนี้ ถ้าฟังก์ชันลูกศรไม่ได้ทำงานอะไรเลย (ตัวบอดี้ไม่มีซอร์สโค้ด) ก็ต้องใส่ {} ปิดท้าย + +```js +// ฟังก์ชันลูกศรที่ไม่มีพารามิเตอร์ และตัวบอดี้ของฟังก์ชันก็ว่างเปล่า +let arrowFunc = () => {}; +arrowFunc(); +// จะเสมือนเขียนเป็น +// var arrowFunc = function(){}; +``` + +* ตัวอย่างที่ 5 ถ้าฟังก์ชันลูกศรรีเทิร์นค่าเป็นอ็อบเจ็กต์ จะต้องใส่วงเล็บครอบอ็อบเจ็กต์เสมอ เพราะถ้าไม่ใส่จะเกิด error เนื่องจากมันจะสับสนกับเครื่องหมายปีกกาของตัวบอดี้ฟังก์ชัน + +```js +// ใส่เครื่องหมายวงเล็บ เพื่อครอบอ็อบเจ็กต์ที่ถูกรีเทิร์นออกมา +let getFont = () => ( { color: "red", size: 200 } ); +console.log(getFont()); // { color: 'red', size: 200 } +/* จะเสมือนเขียนเป็น +let getFont = function() { + return {color: "red", size: 200}; +};*/ +``` + +* ตัวอย่างที่ 6 ถ้าฟังก์ชันลูกศรมีพารามิเตอร์ตั้งแต่ 2 ตัวขึ้นไป จะต้องมีวงเล็บครอบพารามิเตอร์ และคั่นด้วยเครื่องหมายจุลภาค (,) + +```js +// มีวงเล็บครอบพารามิเตอร์เอาไว้ +let sum = (val1, val2, val3) => val1 + val2 + val3; +console.log(sum(1,2,3)); // 6 +/* จะเสมือนเขียนเป็น +let sum = function(val1, val2, val3) { + return val1 + val2 +val3; +};*/ +``` + +* ตัวอย่างที่ 7 ฟังก์ชันลูกศรสามารถประกาศใช้พารามิเตอร์แบบดีฟอลต์ + +```js +// ฟังก์ชันลูกศรที่ใช้พารามิเตอร์แบบดีฟอลต์ +let sum = (val1 = 1, val2 = 2, val3 = 3) => val1 + val2 + val3; +console.log(sum()); // 6 +/* จะเสมือนเขียนเป็น +let sum = function(val1 = 1, val2 = 2, val3 = 3) { + return val1 + val2 +val3; +};*/ +``` + +* ตัวอย่างที่ 8 ฟังก์ชันลูกศรสามารถประกาศใช้พารามิเตอร์แบบเรสต์ + +```js +// ฟังก์ชันลูกศรที่ใช้พารามิเตอร์แบบเรสต์ +let max = (...value) => Math.max(...value); +console.log(max(1, 2, 3, 6)); // 6 +/* จะเสมือนเขียน +let max = function(...value) { // พารามิเตอร์แบบเรสต์ + return Math.max(...value); // โอเปอเรเตอร์สเปรด +};*/ +``` + +### ฟังก์ชันลูกศรต่างจากฟังก์ชันธรรมดาอย่างไร + +```js +let arrowFunc = value => value; +console.log(typeof arrowFunc); // "function" +console.log(arrowFunc instanceof Function); // true +``` + +```js +let arrowFunc = () => {}; +console.log(arrowFunc.name); // จะแสดงชื่อ "arrowFunc" (ขึ้นอยู่กับจาวาสคริปต์เอ็นจิ้น) +``` + +### อ็อบเจ็กต์ arguments ในฟังก์ชันลูกศร + +```js +// เขียนแบบฟังก์ชันลูกศร +var arrowFunc = () => console.log(arguments); // ไม่สามารถใช้อ็อบเจ็กต์ arguments ได้ +arrowFunc(1, 2, 3); // ถ้ารันบนเว็บเบราเซอร์จะเกิด ReferenceError แต่บน Node.js ไม่เกิด error +// เขียนแบบฟังก์ชันธรรมดา +var arrowFunc2 = function() { + return console.log(arguments); // ฟังก์ชันธรรมดาสามารถใช้อ็อบเจ็กต์ arguments ได้ตามปกติ +}; +arrowFunc2(1, 2, 3); // [Arguments] { '0': 1, '1': 2, '2': 3 } +``` + +```js +function createArrow(value) { + // ฟังก์ชันลูกศรสามารถเรียกใช้ arguments ของฟังก์ชัน createArrow() + return () => arguments[0]; +} +let arrowFunc = createArrow(1); +console.log(arrowFunc()) // 1 +``` + +### เทคนิคการเขียน IIFE +```js +// เทคนิค IIFE กับฟังก์ชันลูกศร +var printItem = ( +(item) => function() { console.log(item); } +)("IIFE"); +printItem(); // "IIFE" +/* จะเสมือนใช้เทคนิค IIFE กับฟังก์ชันธรรมดา +var printItem = function(item) { + return function() { console.log(item); }; +}("IIFE"); +printItem(); // "IIFE" */ +``` + +### ฟังก์ชันคอลแบ็ค + +```js +var array = [1, 2, 3, 4]; +array.forEach( (value, index, arr) => arr[index] = value *2 ); +console.log(array); // [ 2, 4, 6, 8 ] +/* จะเสมือนใช้ฟังก์ชันคอลแบ็คแบบปกติ +var array = [1, 2, 3, 4]; +array.forEach(function(value, index, arr) { + return arr[index] = value * 2; +}); +console.log(array); // [ 2, 4, 6, 8 ] */ +``` + +### การใช้ this ในฟังก์ชันลูกศร + +```html + + + + + + + + + + + +``` + +* หมายเหตุ วิธีรันโค้ดนี้ให้ copy โค้ดจาวาสคริปต์ข้างล่าง ไปแปะไว้ในโครงสร้าง HTML ข้างบน แล้วถึงกดรันได้ + +```js +let obj ={ + value : "JavaScript", + printValue: function() { + console.log("Message:", this.value); // this จะชี้ไปยังอ็อบเจ็กต์ obj + }, + handle : function() { + console.log("Press a button"); + }, + init : function() { + let element = document.querySelector("#b1"); // ปุ่ม "Try it" + element.addEventListener("click", function(event) { + this.handle(); // this จะชี้ไปยังอ็อบเจ็กต์ obj + }.bind(this), false); // บรรทัด a -- this จะชี้ไปยังไปอ็อบเจ็กต์ obj + //}.bind(obj), false); // จะใช้บรรทัดนี้ก็ได้ มีความหมายเหมือนกัน + } +}; // สิ้นสุดการประกาศอ็อบเจ็กต์ +obj.printValue(); // "Message: JavaScript" +obj.init(); +``` + +* หมายเหตุ วิธีรันโค้ดนี้ให้ copy โค้ดจาวาสคริปต์ข้างล่าง ไปแปะไว้ในโครงสร้าง HTML ข้างบน แล้วถึงกดรันได้ + +```js +let obj ={ + value : "JavaScript", + printValue: function() { + console.log("Message:", this.value); // this จะชี้ไปยังอ็อบเจ็กต์ obj + }, + handle : function() { + console.log("Press a button"); + }, + init : function() { + let element = document.querySelector("#b1"); // ปุ่ม "Try it" + element.addEventListener("click", (event)=> this.handle()); //this จะชี้ไปยัง obj + } +}; // สิ้นสุดการประกาศอ็อบเจ็กต์ +obj.printValue(); // "Message: JavaScript" +obj.init(); +``` + +### เมธอด apply(), call() และ bind() + +```js +let sum = (val1, val2) => console.log(val1 + val2) ; +sum.apply(null, [5, 5] ); // 10 +sum.call(null, 5, 5); // 10 +let resultSum = sum.bind(null, 5, 5); +resultSum(); // 10 +``` + +```js +let objA = {value: "access objA"}; +let objB = { + value: "access objB", + myFunction() { + console.log("this.value in myFunction:", this.value); + // this ในฟังก์ชันลูกศร จะเห็นเหมือนกับที่ myFunction() มองเห็น + let arrowFunc = () => console.log("Arrow function:", this.value); + let func = function() { // this ในฟังก์ชันปกติ สามารถเปลี่ยนไปชี้อ็อบเจ็กตัวอื่นได้ + console.log("Normal function:", this.value); + } + arrowFunc.call(objA); // บรรทัด a –- ไม่สามารถเปลี่ยนค่า this ได้ + func.call(objA); // บรรทัด b -- สามารถเปลี่ยนค่า this ให้ชี้ไปยังอ็อบเจ็กต์ objA ได้ + } +} +objB.myFunction(); // บรรทัด c +/*แสดงผลลัพธ์ +"this.value in myFunction: access objB" +"Arrow function: access objB" +"Normal function: access objA" */ +objB.myFunction.call(objA); // บรรทัด d +/*แสดงผลลัพธ์ +"this.value in myFunction: access objA" +"Arrow function: access objA" +"Normal function: access objA" */ +``` + +## Tail call optimization + +```js +function foo(a) { + return a; // บรรทัด a +} +function bar(b) { + let c = b + 100; + return foo(c); // บรรทัด b +} +console.log( bar(30) ); // บรรทัด c แสดงผลลัพธ์เป็น 130 +``` + +### ตำแหน่ง Tail call + +* กรณีที่ 1 + +```js +function foo() { + bar(); // เรียกฟังก์ชันแบบนี้จะไม่ใช่ตำแหน่งสุดท้าย + // ถ้าเขียนเป็น return bar(); จะเป็นการเรียกฟังก์ชันในตำแหน่งสุดท้าย +} +``` + +```js +function foo() { + bar(); + return undefined; +} +``` + +* กรณีที่ 2 + +```js +function foo() { + return 1+ bar(); // เรียกฟังก์ชันแบบนี้จะไม่ใช่ตำแหน่งสุดท้าย +} +``` + +```js +function foo() { + let result = bar(); + return 1 + result; +} +``` + +* กรณีที่ 3 + +```js +function foo(condition) { + if(condition) { + return bar(); // บรรทัด a -- เรียกฟังก์ชันในตำแหน่งสุดท้าย + } else { + bar(); // บรรทัด b -- เรียกฟังก์ชันแบบนี้จะไม่ใช่ตำแหน่งสุดท้าย + } +} +``` + +### รีเคอร์ซีพ + +```js +function factorial(value) { + if (value <= 0) { + return 1; + } else { + return value * factorial(value-1); // บรรทัด a -- ไม่ใช่การเรียกฟังก์ชันในตำแหน่งสุดท้าย + } + } +console.log(factorial(4)); // จะได้ค่าเป็น 24 เพราะ 4! = 4 x 3 x 2 x 1 = 24 +// จะเกิด RangeError เพราะ stack frame โตมากเกินไป จนใช้หน่วยความจำหมด +console.log(factorial(200000)); +``` + +```js +// ต้องประกาศเพื่อทำ TCO แต่ถ้าเขียนบน Traceur หรือ Babel ซอร์สโค้ดจะเป็นสตริคท์โหมดโดยอัตโนมัติ +"use strict"; +function factorial(value) { + return callFac(1, value); +} +function callFac(temp, val) { + if (val <= 1) { + return temp; + } else { + return callFac(temp * val, val-1); // บรรทัด a -- เรียกฟังก์ชันในตำแหน่งสุดท้าย + } +} +console.log(factorial(4)); // 24 +console.log(factorial(200000)); // infinity +``` + +### นิพจน์อื่นที่เป็น Tail call + +* กรณีที่ 1 + +```js +let arrowFunc = param => param ? foo() : bar(); +``` + +```js +let arrowFunc = param => { + if(param) { + return foo(); // เรียกฟังก์ชันในตำแหน่งสุดท้าย + } else { + return bar(); // เรียกฟังก์ชันในตำแหน่งสุดท้าย + } +}; +``` +กรณีที่ 2 + +```js +let arrowFunc = () => (foo(), bar(), zoo()); +``` + +```js +let arrowFunc = () => { + foo(); + bar(); + return zoo(); // เรียกฟังก์ชันในตำแหน่งสุดท้าย +}; +``` + +กรณีที่ 3 + +```js +let arrowFunc = () => foo() || bar(); +``` + +```js +let arrowFunc = () => { + let temp = foo(); + if (temp) { + return temp; + } else { + return bar(); // เรียกฟังก์ชันในตำแหน่งสุดท้าย + } +}; +``` + +กรณีที่ 4 + +```js +let arrowFunc = () => foo() && bar(); +``` + +```js +let arrowFunc = () => { + let temp = foo(); + if (!temp) { + return temp; + } else { + return bar(); // เรียกฟังก์ชันในตำแหน่งสุดท้าย + } +}; +``` + +## การใช้คอมมา (,) + +```js +let arr = [ "red", "green", "blue", ]; +``` + +```js +let arr = [ "green", "blue", "red", ]; +``` + +```js +let obj = { + foo: 1, + bar: 2, +}; +``` + +```js +let obj = { + bar: 2, + foo: 1, +}; +``` + +```js +let obj = { foo: 1, bar: 2, }; +``` + +```js +let arr = [ + "green", + "blue", + "red", + ]; +``` + +```js +function foo(param1, param2, ) { + console.log(arguments); +} + +foo( "abc", "def", ); // [Arguments] { '0': 'abc', '1': 'def' } +``` + +```js +// ตอนประกาศฟังก์ชัน +function foo( + param2, + param1, +) { + console.log(arguments); +} +// ตอนเรียกฟังก์ชัน +foo( + "def", + "abc", +); +// แสดงผลลัพธ์ +// [Arguments] { '0': 'def', '1': 'abc' } +``` diff --git a/examples_book/Chapter11.md b/examples_book/Chapter11.md new file mode 100644 index 0000000..06e1538 --- /dev/null +++ b/examples_book/Chapter11.md @@ -0,0 +1,231 @@ +# โค้ดบทที่ 11 เทมเพลตสตริง + +## เทมเพลตสตริงคืออะไร + +```js +let msg = `JavaScript`; +console.log(msg); // "JavaScript" +console.log(msg.length); // 10 +console.log(typeof msg); // "string" +``` + +```js +let msg = `\`One\` "Two" ‘Three’`; +console.log(msg); // แสดงคำว่า `One` "Two" ‘Three’ +``` + +### เขียนสตริงหลายบรรทัด + +```js +let div = `
+

Hello world

+
`; +console.log(div); +/* แสดงผลลัพธ์เป็น +
+

Hello world

+
+*/ +``` + +```js +let div = "
\n\t

Hello world

\n
"; +console.log(div); +/* แสดงผลลัพธ์เป็น +
+

Hello world

+
+*/ +``` + +```js +let div = "
\n" + +"

Hello world

\n" + +"
"; +console.log(div); +/* แสดงผลลัพธ์เป็น +
+

Hello world

+
+*/ +``` + +```js +let div = ["
", "\t

Hello world

", "
"].join("\n"); +console.log(div); +/* แสดงผลลัพธ์เป็น +
+

Hello world

+
+*/ +``` + +```js +let div = "
\ +

Hello world

\ +
"; +console.log(div); +// แสดงผลลัพธ์เป็น +//

Hello world

+``` + +```js +let msg = `Hello\nworld`; +console.log(msg); +/* แสดงผลลัพธ์เป็น +Hello +world +*/ +``` + +### คอมเมนต์ในเทมเพลตสตริง + +```js +let msg = `First line // This is not a comment +/* +This is not a comment +*/ +Last line`; +console.log(msg); +``` + +* โค้ดข้างบนจะแสดงผลดังนี้ +```notrun +First line // This is not a comment +/* +This is not a comment +*/ +Last line +``` + +### ความยาวของเทมเพลตสตริง + +```js +let msg = ` +JavaScript + String`; +console.log(msg.length); // 20 +console.log(msg.trim().length); // 19 +``` + +## นิพจน์ในเทมเพลตสตริง +```notrun +${นิพจน์} +``` + +```js +let name = "Somchai"; +let msg = `My name is ${name}`; +console.log(msg) // "My name is Somchai" +``` + +```js +let a = 5, b = 10, c = 100; +console.log("Price $" + ((a*b).toFixed(2)) + ", not " + (c + a) ); +// แสดงผลลัพธ์เป็น +// "Price $50.00, not 105" +``` + +```js +let a = 5, b = 10, c = 100; +console.log(`Price $${(a*b).toFixed(2)}, not ${c + a}` ); +// แสดงผลลัพธ์เป็น +// "Price $50.00, not 105" +``` + +```js +function myFunction() { + let name = "Somchai"; +} +console.log(`My name is ${name}`); +// อาจเกิด ReferenceError หรือไม่มีค่า name (ขึ้นอยู่กับจาวาสคริปต์เอนจิน) +``` + +## การติดแท็ก +```notrun +let n = 1, a = 3, b = 6; +console.log(divTag`${n}) Hello world : ${a * b} items`); // บรรทัด 2 +``` + +```js +function divTag(strings, ...values) { + //console.log(strings[0]); // "" + //console.log(strings[1]); // ") Hello world world : " + //console.log(strings[2]); // " items" + //console.log(values[0]); // 1 (เป็นค่าของนิพจน์ ${n} ) + //console.log(values[1]); // 18 (เป็นค่าของนิพจน์ ${a * b} ) + let result = ""; + for (let i = 0; i < values.length; i++) { + result += strings [i]; + result += values [i]; + } + if(values.length < strings.length){ + result += strings[values.length]; // ต่อท้ายสตริงด้วยข้อความที่เหลือคือ " items" + } +// เมื่อโปรแกรมทำงานถึงตรงนี้ ค่าของตัวแปร result ก็คือสตริงตัวเดิมที่ถูกส่งเข้ามา +// ส่วนค่าที่รีเทิร์นออกมา จะเป็นสตริงตัวใหม่ที่มี
กับ
ครอบเปิดและปิดท้ายสตริงตัวเดิม + return `
${result}
`; +} +let n = 1, a = 3, b = 6; +console.log(divTag`${n}) Hello world : ${a * b} items`); +// แสดงผลลัพธ์เป็น +// "
1) Hello world : 18 items
" +``` + +```notrun +divTag`${n}) Hello world : ${a * b} items` +``` + +### String.raw +```js +console.log(`One\tTwo\nThree`); +/* เมื่อตัวอักษรพิเศษถูกประมวลผล ก็จะแสดงผลลัพธ์เป็น +"One Two +Three" +*/ +``` + +```js +console.log(String.raw `One\tTwo\nThree`); +// แสดงผลลัพธ์เป็น +// "One\tTwo\nThree" +``` + +```js +function rawTag(strings, ...values) { + let result = ""; + for (let i = 0; i < strings.length; i++) { + result += strings.raw[i]; // บรรทัด a + } + return result; +} +console.log(rawTag`One\tTwo\nThree`); +// แสดงผลลัพธ์เป็น +// "One\tTwo\nThree" +``` + +```js +console.log(`\u{004B}`); // ’K’ +console.log(`\u004B`); // ’K’ +console.log(`\x4B`); // ’K’ +``` + +```js +console.log("C:\unit\x-ray\12"); // เกิด SyntaxError +console.log(`C:\unit\x-ray\12`); // เกิด SyntaxError +``` + +```js +function windowPath(str) { + console.log(str[0]) // ถ้าใช้ \ ผิด ก็จะได้สตริงเป็น undefined + } +windowPath `C:\unit\x-ray\12`; // undefined +``` + +```js +function windowPath(str) { + return str.raw[0]; + } +let str = windowPath `C:\unit\x-ray\12`; +console.log(str) // C:\unit\x-ray\12 +``` \ No newline at end of file diff --git a/examples_book/Chapter12.md b/examples_book/Chapter12.md new file mode 100644 index 0000000..5fa53c3 --- /dev/null +++ b/examples_book/Chapter12.md @@ -0,0 +1,176 @@ +# โค้ดบทที่ 12 ซิมโบล + +## การใช้งานซิมโบล + +```js +let sym1 = Symbol(); // ไม่มี description +let sym2 = Symbol("example"); // มี description เป็น "example" +let sym3 = Symbol("example"); // มี description เป็น "example" +console.log(sym2 === sym3); // false +``` + +```js +let sym = Symbol("example"); +console.log(sym.toString()); // Symbol(example) +console.log(sym); // Symbol(example) +``` + +```js +console.log( typeof Symbol() ); // "symbol" +console.log( typeof Symbol("example") ); // "symbol" +``` + +```js +let uid = Symbol.for("uid"); +console.log( uid && true ); // true +console.log( !true ); // false +``` + +## ข้อควรระวังการใช้ซิมโบล + +```js +let bool = new Boolean(); // สามารถทำได้ +let str = new String(); // สามารถทำได้ +let num = new Number(); // สามารถทำได้ +let sym = new Symbol(); // จะเกิด TypeError +``` + +```js +let sym = Symbol("example"); +let symObj1 = Object(sym); +let symObj2 = new Object(sym); +console.log(typeof sym); // "symbol" +console.log(typeof symObj1); // "object" +console.log(typeof symObj2); // "object" +``` + +```js +let sym1 = Symbol("example1"); +let sym2 = Symbol("example2"); +console.log(sym1 == Object(sym1)); // true +console.log(sym2 == Object(sym1)); // false +``` + +## ใช้ซิมโบลเป็นคีย์ในอ็อบเจ็กต์ + +```js +let sym = Symbol("first symbol"); +let obj = { [sym]: 100 }; // ใช้ซิมโบลเป็นชื่อคีย์ในอ็อบเจ็กต์ +console.log(obj[sym]); // 100 +obj[sym] = 200; +console.log(obj[sym]); // 200 +let lastSym = Symbol("last symbol"); +Object.defineProperties(obj, { + [lastSym]: { // ใช้ซิมโบลเป็นชื่อคีย์ในอ็อบเจ็กต์ + value: 300, + writable: true + } +}); +console.log(obj[lastSym]); // 300 +Object.defineProperty(obj, sym, { writable: false }); // กำหนดให้พร็อพเพอร์ตี้ ไม่สามารถแก้ไขค่าได้ +obj[sym] = 1; // ถ้าอยู่ในโหมดสตริคท์ จะเกิด TypeError เพราะไม่สามารถแก้ไขค่าได้ +``` + +## การแชร์ซิมโบล + +```js +let uid = Symbol.for("uid"); // มีค่า description เป็น "uid" +console.log(uid); // Symbol(uid) +``` + +```js +let sym1 = Symbol.for("uid"); // ค้นหาซิมโบลครั้งแรก +let sym2 = Symbol.for("uid"); // ค้นหาซิมโบลครั้งที่สอง +console.log(sym1 === sym2); // true (เพราะ sym1 กับ sym2 คือซิมโบลตัวเดียวกัน) +console.log(sym1); // Symbol(uid) +// เนื่องจาก sym1 กับ sym2 คือซิมโบลตัวเดียวกัน จึงสามารถใช้แทนกันได้ +let obj = {[sym1]: 100}; +console.log(obj[sym2]); // 100 +``` + +```js +let sym1 = Symbol.for("uid"); +console.log(Symbol.keyFor(sym1)); // "uid" +let sym2 = Symbol("uid"); +console.log(Symbol.keyFor(sym2)); // undefined +``` + +```js +Symbol.for("dojo.uid"); +Symbol.for("jquery.uid"); +``` + +## ซิมโบลในประโยค for...in + +```js +let obj = { }; +obj.car = "100"; +obj["zoo"] = "200"; +obj[Symbol("foo")] = "foo"; +obj[Symbol("bar")] = "bar"; +for (let i in obj) { + console.log(i); +} +// ประโยค for จะแสดงผลลัพธ์ +// "car" +// "zoo" +console.log(Object.getOwnPropertyNames(obj)); // [ 'car', 'zoo' ] +console.log(Object.keys(obj)); // [ 'car', 'zoo' ] +``` + +```js +let obj = {}; +obj.car = "100"; +obj["zoo"] = "200"; +obj[Symbol("foo")] = "foo"; +obj[Symbol("bar")] = "bar"; +let keys = Object.getOwnPropertySymbols(obj); +console.log(keys); // [ Symbol(foo), Symbol(bar) ] +``` + +## JSON.stringify() + +```js +let obj = { +[Symbol("example")]: "100", +"bar" : "200" +} +// จะได้เป็นสตริงที่เขียนอยู่ในรูปแบบของ JSON +console.log(JSON.stringify(obj)); // {"bar":"200"} +``` + +## แปลงซิมโบลเป็นสตริง + +```js +let sym = Symbol("foo"); +++sym; // TypeError +sym + 0; // TypeError +``` + +```js +Symbol("foo") + "bar"; // TypeError +``` + +```js +let sym = Symbol("foo"); +console.log(sym.toString()); // Symbol(foo) +console.log(String(sym)); // Symbol(foo) +new String(sym); // TypeError +``` + +```js +let sym = Symbol("foo"); +console.log(`${sym.toString()}`); // Symbol(foo) +console.log(`${String(sym)}`); // Symbol(foo) +console.log(`${sym}`) // TypeError +``` + +## พร็อพเพอร์ตี้ description + +```js +let sym= Symbol("foo"); +console.log(sym) // Symbol(foo) +console.log(String(sym) === `Symbol(${"foo"})`); // true +console.log(sym.toString()); // Symbol(foo) +console.log(sym.description); // "foo" +``` \ No newline at end of file diff --git a/examples_book/Chapter13.md b/examples_book/Chapter13.md new file mode 100644 index 0000000..e35f0f7 --- /dev/null +++ b/examples_book/Chapter13.md @@ -0,0 +1,763 @@ +# โค้ดบทที่ 13 ฟีเจอร์ใหม่ของอ็อบเจ็กต์ + +## กำหนดค่าให้พร็อพเพอร์ตี้แบบย่อ + +```js +let color = "red"; +let size = 200; +let font = { + color: color, // คีย์กับข้อมูล ชื่อซ้ำกัน + size: size // คีย์กับข้อมูล ชื่อซ้ำกัน +}; +console.log(font.color); // "red" +console.log(font.size); // 200 +``` + +```js +let color = "red"; +let size = 200; +let font = { color, size}; // พร็อพเพอร์ตี้ของอ็อบเจ็กต์แบบย่อ +console.log(font.color); // "red" +console.log(font.size); // 200 +``` + +## การประกาศเมธอดแบบย่อ +```js +let obj = { + myFunction: function (param) { // ประกาศเมธอดเต็มรูปแบบ + console.log(param); + } +}; +obj.myFunction(200); // 200 +``` + +```js +let obj = { + myFunction(param) { // ประกาศเมธอดแบบย่อ + console.log(param); + } +} ; +obj.myFunction(200); // 200 +``` + +## ชื่อคีย์ซ้ำกัน + +```js +let font = { + color: "red", + color: "green", // เลือกใช้ตัวนี้ + myFunction() { + console.log("myFunction1"); + }, + myFunction() { // เลือกใช้ตัวนี้ + console.log("myFunction2"); + } +} ; +console.log(font.color); // "green" +font.myFunction(); // "myFunction2" +``` + +## ประกาศชื่อคีย์แบบวงเล็บเหลี่ยม + +```js +let name = "name" +let student = { + ["First " + name] : "Somchai", // ประกาศชื่อคีย์แบบวงเล็บเหลี่ยม + [ `Last ${name}`] : "Jaidee" // ประกาศชื่อคีย์แบบวงเล็บเหลี่ยม (ใช้เทมเพลตสตริง) +}; +console.log(student["First name"]); // "Somchai" +let lastName = `Last ${name}`; +console.log(student[lastName]); // "Jaidee" +``` + +```js +let key1 = {a:1}; +let key2 = {b:2}; +let obj = { + [key1] : 100, + [key2]: 200 // ใช้คีย์ตัวนี้ +} +console.log(obj[key1], obj[key2]); // 200 200 +console.log(obj[key1] === obj[key2]); // true +``` + + +```js +let name = "myFunction"; +let obj = { + [name]: function(param) { // ประกาศเมธอดโดยใช้วงเล็บเหลี่ยม + console.log(param); + } +}; +obj[name](200); // 200 +obj.myFunction(200); // 200 +``` + +```js +let name = "myFunction"; +let obj = { + [name](param) { // ประกาศเมธอดแบบย่อ + console.log(param); + } +}; +obj[name](200); // 200 +obj.myFunction(200); // 200 +``` + +## เมธอด Object.assign() + +```js +let obj = { x: 1, y: 2 }; +let cloned1 = Object.assign({}, obj); +let cloned2 = Object.assign({}, obj); +//สร้างอ็อบเจ็กต์ใหม่ได้เรื่อย ๆ ที่เหมือนกับอ็อบเจ็กต์ต้นแบบ obj ทุกประการ +console.log(cloned1); // { x: 1, y: 2 } +console.log(cloned2); // { x: 1, y: 2 } +console.log(cloned1 === cloned2); // false +``` + +```js +let obj1 = { x: 1 }; +let obj2 = { y: 2 }; +let obj3 = { z: 3 }; +let merged = Object.assign(obj1, obj2, obj3); +console.log(merged); // { x: 1, y: 2, z: 3 } +console.log(obj1); // { x: 1, y: 2, z: 3 } +console.log(merged === obj1); // true +console.log(merged === obj2); // false +console.log(merged === obj3); // false +``` + +```js +let person1 = {}; +let person2 = { + firstName: "Somchai", + lastName: "Jaidee" +}; +let person3 = { + lastName: "Dekdee" +}; +let person4 = { + lastName: "Rakdee" // ถ้าชื่อคีย์มันซ้ำกัน จะเลือกก็อปปี้จากอ็อบเจ็กต์ตัวล่าสุดเท่านั้น +}; +Object.assign(person1, person2, person3, person4); +console.log(person1.firstName); // "Somchai" +console.log(person1.lastName); // "Rakdee" +``` + +```js +let sym = Symbol("example"); +let name = "to do"; +let obj = { + myFunction(param) { + console.log(param); + }, + [ `${name}`]( ) { + console.log("to do something"); + }, + x : null, + y : undefined, + [sym] : 100 +}; +let cloned = Object.assign({}, obj); +cloned.myFunction(200); // 200 +cloned[`${name}`](); // "to do something" +console.log(cloned.x); // null +console.log(cloned.y); // undefined +console.log(cloned[sym]); // 100 +``` + +```js +let person = { + set firstName(param) { + this.fname = param; + }, + get firstName() { + return this.fname; + } +}; +person.firstName = "Somchai"; +let target = Object.assign({}, person); +let descriptor = Object.getOwnPropertyDescriptor(target, "firstName"); +console.log(descriptor.value); // "Somchai" +console.log(typeof descriptor.set); // undefined +console.log(typeof descriptor.get); // undefined +console.log(target.firstName); // "Somchai" +``` + +```js +let font = {} ; +Object.defineProperties (font, { + color: { + value : "red", + writable: false // ไม่สามารถก็อปปี้ได้ + }, + size: { + value : 200, + enumerable: false // ไม่สามารถก็อปปี้ได้ + } +}); +let obj = Object.assign({}, font); +console.log(typeof obj.color); // undefined +console.log(typeof obj.size); // undefined +``` + +```js +let car = { + drive() { + console.log("Drive a car") ; + } +}; +let taxi = Object.create(car); // กำหนดให้ taxi มีโปรโตไทป์เป็น car +taxi.drive(); // "Drive a car" +let driver = Object.assign({}, taxi); // driver โคลนนิ่งมาจาก taxi +console.log(typeof driver.drive); // undefined +``` + +## การเปลี่ยนโปรโตไทป์ +```js +let car = { + drive() { + console.log("Drive a car") ; + } +}; +let taxi = { + drive() { + console.log("Drive a taxi") ; + } +}; +let driver = Object.create(car); // บรรทัด a -- โปรโตไทป์คือ car +console.log( Object.getPrototypeOf(driver) === car); // true +driver.drive(); // "Drive a car" +Object.setPrototypeOf(driver, taxi); // บรรทัด b -- เปลี่ยนโปรโตไทป์เป็น taxi +console.log( Object.getPrototypeOf(driver) === taxi); // true +driver.drive(); // "Drive a taxi" +``` + +## '__proto__' + +```js +let car = { + drive() { + console.log("Drive a car") ; + } +}; +let taxi = { + drive() { + console.log("Drive a taxi") ; + } +}; +let driver ={ + __proto__ : car // โปรโตไทป์คือ car +} +console.log(driver.__proto__ === car); // true +driver.drive(); // "Drive a car" +driver.__proto__ = taxi; // เปลี่ยนโปรโตไทป์เป็น taxi +console.log(driver.__proto__ === taxi); // true +driver.drive(); // "Drive a taxi" +``` + +```js +let car = { + drive() { + console.log("Drive a car") ; + } +}; +let driver ={ + __proto__ : car, + __proto__ : car // SyntaxError +} +``` + +```js +let car = { + drive() { + console.log("Drive a car") ; + } +}; +let driver = { + ["__proto__"]: car +} +console.log(typeof driver.drive); // undefined +console.log(driver["__proto__"] === car); // true +``` + +## การใช้ supper + +```js +let car = { + speed: 100, + drive() { + return "Car speed: "; + } +}; +let driver = { + __proto__: car, + drive() { + return "Car speed: " + this.speed ; + } +}; +console.log(driver.speed); // 100 +console.log(driver.drive()); // "Car speed: 100" +``` + +```js +let car = { + speed: 100, + drive() { + return "Car speed: " ; + } +}; +let driver = { + __proto__: car, + drive() { + return super.drive() + super.speed ; // เรียกใช้งาน super +// อาจเรียกวิธีนี้แทนก็ได้ +// 1) return Object.getPrototypeOf(this).drive.call(this) + this.__proto__.speed; +// 2) return this.__proto__. drive.call(this) + this.__proto__.speed; + } // สิ้นสุดการประกาศ drive() +}; +console.log(driver.speed); // 100 +console.log(driver.drive()); // "Car speed: 100" +``` + +```js +let car = { + ["car speed"]: 100, + ["drive a car"] ( ) { + return "Car speed: " ; + } +}; +let driver = { + __proto__: car, + speed: 900, + drive() { + return super["drive a car"]() + super["car speed"] ; // เรียกใช้งาน super + } +}; +console.log(driver.speed); // 900 +console.log(driver.drive()); // "Car speed: 100" +``` + +```js +let car = { + drive() { + return "is driving"; + } +}; +let taxi = { + __proto__ : car, // โปรโตไทป์คือ car + drive() { + return super.drive() + " a taxi"; + } +}; +let driver ={ + __proto__ : taxi, // โปรโตไทป์คือ taxi + drive() { + return "Someone " + super.drive(); + } +} +console.log(car.drive()); // "is driving" +console.log(taxi.drive()); // "is driving a taxi" +console.log(driver.drive()); // "Someone is driving a taxi" +``` + +```js +let car = { + drive() { + return "Driving"; + } +}; +let taxi = { + __proto__ : car, // โปรโตไทป์คือ car + drive() { + return super.drive() + " a taxi"; // บรรทัด a + } +}; +let driver = { + __proto__ : taxi // โปรโตไทป์คือ taxi +} +console.log(car.drive()); // "Driving" +console.log(taxi.drive()); // "Driving a taxi" +console.log(driver.drive()); // "Driving a taxi" +``` + +```js +let car = { + drive() { + return "Driving"; + } +}; +let taxi = { + __proto__ : car, // โปรโตไทป์คือ car + drive() { + return Object.getPrototypeOf(this).drive.call(this) + " a taxi"; + } +}; +let driver ={ + __proto__ : taxi // โปรโตไทป์คือ taxi +} +console.log(car.drive()); // "Driving" +console.log(taxi.drive()); // "Driving a taxi" +console.log(driver.drive()); // RangeError: Maximum call stack size exceeded +``` + +### เบื้องหลังการใช้งาน super + +```js +function drive() { + console.log("Drive a taxi"); +} +``` + +```js +let car = { + drive() { + console.log("Drive a car"); + } +}; +``` + +```js +let car = { + drive() { + console.log("Drive a car. It has speed:", this.speed ) ; + } +}; +let driver = { + __proto__: car, + speed: 100, + drive() { // บรรทัด a + super.drive(); // บรรทัด b + // จะเหมือน Object.getPrototypeOf(driver).drive.call(this); + } // สิ้นสุดการประกาศ drive() +}; +driver.drive(); // "Drive a car. It has speed: 100" +``` + +```js +function startCar() { + console.log( super.drive()); // เกิด SyntaxError +} +``` + +```js +let car = { + drive() { + console.log("Drive a car. It has speed:", this.speed ) ; + } +}; +let driver = { + __proto__: car, + speed: 100, + drive() { + console.log("Drive a taxi") ; + } +}; +function startCar() { + return super.drive() + " :100"; // บรรทัด a - เกิด SyntaxError +} +driver.drive = startCar; // กำหนดค่าทับพร็อพเพอร์ตี้ driver.drive +driver.drive(); +``` + +```js +let car = { + drive() { + console.log("Drive a car. It has speed:", this.speed ) ; + } +}; +let driver = { + __proto__: car, + speed: 100, + drive: function() { // บรรทัด a -- ไม่ได้ประกาศเมธอดแบบย่อ + super.drive(); // บรรทัด b - เกิด SyntaxError + } // สิ้นสุดการประกาศ drive() +}; +driver.drive(); +``` + +## เมธอด Object.is() +```js +console.log(NaN === NaN); // false +console.log(-0 === +0); // true +``` + +```js +// เปรียบเทียบค่า NaN +console.log(NaN === NaN); // false +console.log(NaN === 0/0); // false +console.log(Object.is(NaN, NaN)); // true +console.log(Object.is(NaN, 0/0)); // true +// เปรียบเทียบเลข 0 +console.log(+0 === -0); // true +console.log(Object.is(+0, -0)); // false +// เปรียบเทียบตัวเลข +console.log(10 === 10); // true +console.log(10 === "10"); // false +console.log(Object.is(10, 10)); // true +console.log(Object.is(10, "10")); // false +// เปรียบเทียบอ็อบเจ็กต์ +console.log([] === []); // false +console.log({} === {}); // false +console.log(Object.is([], [])); // false +console.log(Object.is({}, {})); // false +var obj = { x: 1, y: 2 }; +console.log(obj === obj); // true +console.log(Object.is(obj, obj)); // true +// เปรียบเทียบค่า undefinded และ null +console.log(undefined === undefined); // true +console.log(null === null); // true +console.log(Object.is(undefined, undefined)); // true +console.log(Object.is(null, null)); // true +// เปรียบเทียบสตริง +console.log("Hello" === "Hello"); // true +console.log(Object.is("Hello", "Hello")); // true +// เปรียบเทียบบูลีน +console.log("" === false); // false +console.log(0 === false); // false +console.log(0 === ""); // false +console.log(false === false); // true +console.log(Object.is("", false)); // false +console.log(Object.is(0, false)); // false +console.log(Object.is(0, "")); // false +console.log(Object.is(false, false)); // true +``` +## เมธอด Object.values() + +```js +let obj = { foo: 1, bar: 2 }; +let array = Object.values(obj); +console.log(array); // [ 1, 2 ] +``` + +```js +let foo = Symbol("foo"); +let obj = { + [foo]: 1, + bar: 2, + }; +let array = Object.values(obj); +console.log(array); // [ 2 ] +``` + +```js +let obj = { foo: 1, bar: 2 }; +for (let v of Object.values(obj)) { // สกัดส่วนข้อมูลจากอ็อบเจ็กต์ obj ออกมา + console.log(v); +} +/* แสดงผลลัพธ์ +1 +2 */ +``` + +## เมธอด Object.entries() + +```js +let obj = { foo: 1, bar: 2 }; +let array = Object.entries(obj); +console.log(array); // [ [ 'foo', 1 ], [ 'bar', 2 ] ] +``` + +```js +let foo = Symbol("foo"); +let obj = { + [foo]: 1, // มีคีย์เป็นซิมโบล + bar: 2, + }; +let array = Object.entries(obj); +console.log(array); // [ [ 'bar', 2 ] ] +``` + +```js +let obj = { foo: 1, [Symbol("bar")]: 2 }; +// เข้าถึงคีย์ของ obj รวมทั้งคีย์ที่เป็นซิมโบลด้วย +console.log(Reflect.ownKeys(obj).map( (k)=>[k, obj[k]])); // [ [ 'foo', 1 ], [ Symbol(bar), 2 ] ] +// เข้าถึงคีย์ของ obj ที่เป็นซิมโบลอย่างเดียว +console.log(Object.getOwnPropertySymbols(obj).map( (k)=>[k, obj[k]])); // [ [ Symbol(bar), 2 ] ] +``` + +```js +let obj = { foo: 1, bar: 2 }; +for (let [k, v] of Object.entries(obj)) { // สกัดคีย์กับข้อมูลจากอ็อบเจ็กต์ obj ออกมา + console.log(`${k}: ${v}`); +} +/* แสดงผลลัพธ์ +foo: 1 +bar: 2 */ +``` + +```js +let obj = { foo: 1, bar: 2 }; +let map = new Map(Object.entries(obj)); +console.log(map); // Map(2) { 'foo' => 1, 'bar' => 2 } +``` + +## เมธอด Object.getOwnPropertyDescriptors() +```js +let obj = {"foo": 100, "bar": 200 }; +console.log( Object.getOwnPropertyDescriptor(obj,"foo")); // รีเทิร์น descriptor +// { value: 100, writable: true, enumerable: true, configurable: true } +console.log( Object.getOwnPropertyDescriptor(obj,"bar")); // รีเทิร์น descriptor +// { value: 200, writable: true, enumerable: true, configurable: true } +``` + +```js +let obj = { + foo: 1, + get bar() { return 2 }, + [Symbol("zoo")]: 3 +}; +console.log(Object.getOwnPropertyDescriptors(obj)); + +/* แสดงผลลัพธ์ +{ + foo: { value: 1, writable: true, enumerable: true, configurable: true }, + bar: { + get: [Function: get bar], + set: undefined, + enumerable: true, + configurable: true + }, + [Symbol(zoo)]: { value: 3, writable: true, enumerable: true, configurable: true } +} */ +``` + +## เมธอด Object.fromEntries() + +```js +let array = [["name", "somchai"], ["age", 65]]; +let obj = Object.fromEntries(array); +console.log(obj); // { name: 'somchai', age: 65 } +``` + +```js +let map = new Map([["name", "somchai"], ["age", 65]]); // สร้างแม็พขึ้นมาก่อน +console.log(map); // Map(2) { 'name' => 'somchai', 'age' => 65 } +let obj = Object.fromEntries(map) +console.log(obj); // { name: 'somchai', age: 65 } +``` + +## เมธอด Object.hasOwn() +```js +let foo = { + fooProp: 100, +}; +let bar = { + __proto__: foo, + barProp: 200, +}; + +console.log("fooProp" in bar); // true + +console.log( Object.hasOwn(foo, "fooProp") ); // true -- foo เป็นเจ้าของ "fooProp" +console.log( Object.hasOwn(bar, "fooProp") ); // false -- bar ไม่ใช่เจ้าของ "fooProp" + +console.log( foo.hasOwnProperty("fooProp") ); // true -- foo เป็นเจ้าของ "fooProp" +console.log( bar.hasOwnProperty("fooProp") ); // false -- bar ไม่ใช่เจ้าของ "fooProp" +``` + +## การกระจายพร็อพเพอร์ตี้ไปให้อีกอ็อบเจ็กต์ +```js +let obj1 = {a: "foo", b: "bar"}; +let obj2 = {...obj1 }; // ใช้โอเปอเรเตอร์สเปรด ประกาศสมาชิก +console.log(obj2) // { a: 'foo', b: 'bar' } +// แก้ไขอ็อบเจ็กต์ obj1 ก็ไม่กระทบต่อ obj2 +obj1.a = "zoo"; +console.log(obj1); // { a: 'zoo', b: 'bar' } +console.log(obj2); // { a: 'foo', b: 'bar' } +``` + +```js +let obj1 = {a: "foo", b: "bar"}; +let obj2 = {...obj1, c: "zoo"}; +console.log(obj2) // { a: 'foo', b: 'bar', c: 'zoo' } +``` + +```js +let obj1 = {a:"foo", b:"bar"}; +let obj2 = {x:"zoo", y:"car"}; +let obj3 = {...obj1, d:"car", ...obj2}; +console.log(obj3) // { a: 'foo', b: 'bar', d: 'car', x: 'zoo', y: 'car' } +``` + +```js +let obj1 = {a:"foo", b:"bar"}; +let obj2 = {a:"zoo", ...obj1}; +console.log(obj2) // { a: 'foo', b: 'bar' } +let obj3 = {...obj1, a:"zoo"}; +console.log(obj3) // { a: 'zoo', b: 'bar' } +``` + +```js +console.log( {...undefined}); // {} +console.log( {...null} ); // {} +console.log( {...123} ); // {} +console.log( {..."abc"} ); // { 0: 'a', 1: 'b', 2: 'c' } +console.log( {...["foo", "bar"]}); // { 0: 'foo', 1: 'bar' } +``` + +## Optional Chaining +```js +let data = { + parent : { + child: { + name: { + firstName: "สมชาย", + lastName : "ใจดี" + } + } + } +}; +console.log(data.parent.child.name.firstName); // "สมชาย" +console.log(data.parent.child.name.surname); // undefined +console.log(data.parent.child.NAME.firstName); // บรรทัด a - เกิด TypeError + +console.log(data.parent.child.NAME?.firstName); // undefined +console.log(data.parent.child.name?.firstName); // "สมชาย" + +console.log(data.parent.child.NAME?.["firstName"]); // undefined +console.log(data.parent.child.name?.["firstName"]); // "สมชาย" + +console.log(data.parent.child.name.surname?); // วาง ? ไว้ท้ายสุด เกิด SyntaxError ทำไม่ได้ +``` + +```js +function foo (x, y) { + return x*y; +} +let result1 = foo?.(2, 3); // - บรรทัด a +console.log(result1); // 6 +``` + +```js +let bar; //undefined +let result2 = bar?.(2, 3); +console.log(result2); // undefined +``` + +```js +let zoo = null; +let result3 = zoo?.(2, 3); +console.log(result3); // undefined +``` + +## globalThis +```js +function findGlobal () { + if (typeof global !== "undefined") { + return global; + } + if (typeof window !== "undefined") { + return window + }; + if (typeof self !== "undefined") { + return self + }; +}; +console.log(findGlobal()); +``` + +```js +console.log(globalThis); +``` \ No newline at end of file diff --git a/examples_book/Chapter14.md b/examples_book/Chapter14.md new file mode 100644 index 0000000..21b9aae --- /dev/null +++ b/examples_book/Chapter14.md @@ -0,0 +1,1830 @@ +# โค้ดบทที่ 14 คลาส + +## คลาส + +```js +class Car { + // สมาชิกภายในคลาส +} +``` + +```js +class Car { + // สมาชิกภายในคลาส +} +let car1 = new Car(); +let car2 = new Car(); +let car3 = new Car(); +// สร้างอ็อบเจ็กต์ได้เรื่อยๆ +``` + +## โอเปอเรเตอร์ instanceof + +```js +class Car { + // สมาชิกภายในคลาส +} +let car1 = new Car(); +let car2 = new Car(); +console.log(car1 instanceof Car); // true +console.log(car2 instanceof Car); // true +console.log(car1 === car2); // false +``` + +## สมาชิกคลาส + +### คอนสตรัคเตอร์ + +```js +class Car { + constructor(param) { + // ซอร์สโค้ดอื่นๆ + } +} +``` + +```js +class Car { + constructor(param) { + console.log(param); + } +} +let carObj = new Car("red"); // "red" +``` + +## ข้อมูลภายในอินสแตนซ์ + +```js +class Car { + constructor(param) { + this.color = param; + } +} +let carObj = new Car("red"); +console.log(carObj.color); // "red" (ไม่แนะนำให้เข้าถึงโดยตรงด้วยวิธีนี้) + +let c1 = new Car("red"); +let c2 = new Car("black"); +let c3 = new Car("white"); +console.log(c1.color); // "red" +console.log(c2.color); // "black" +console.log(c3.color); // "white" +``` + +```js +class Car { + color = "red"; // บรรทัด a -- ประกาศฟิวด์ color + speed = 100; // บรรทัด b -- ประกาศฟิวด์ speed + constructor() { + console.log(this.color); // "red" + console.log(this.speed); // 100 + } +} +new Car(); +// แสดงผลลัพธ์ +// "red" +// 100 +``` + +```js +class Car { + color; // undefined + var speed = 100; // ทำไม่ได้ เกิด SyntaxError + let weight = 50; // ทำไม่ได้ เกิด SyntaxError + const height = 1.5; // ทำไม่ได้ เกิด SyntaxError +} +``` + +```js +class Car { + intField(param) { + this.color = param; // บรรทัด a + } +} +let carObj = new Car(); +carObj.intField("red") // this.color ถูกสร้างขึ้นมา +console.log(carObj.color); // "red" (ไม่แนะนำให้เข้าถึงโดยตรงด้วยวิธีนี้) +``` + +### ประกาศเมธอด + +```js +class Car { + constructor (speed) { + this.speed = speed; + } + drive() { + console.log("Driving speed:", this.speed); + } + stop() { + console.log("Stop a car"); + } +} +let carObj = new Car(100); +carObj.drive(); // "Driving speed: 100" +carObj.stop(); // "Stop a car" +``` + +```js +class Car { + constructor (speed) { + this.speed = speed; + } + drive() { + console.log("Driving speed:", this.speed); + } + drive() { // เลือกใช้เมธอดตัวนี้ + console.log("Stop a car"); + } +} +let carObj = new Car(100); +carObj.drive(); // "Stop a car" +``` + +## เงื่อนไขการประกาศคลาส + +* ตัวอย่างที่ 1 + +```js +let car = new Car(); // จะเกิด ReferenceError เพราะมองไม่เห็น Car ที่ประกาศอยู่ข้างล่าง +class Car { // คลาสจะถูกมองเห็นตั้งแต่บรรทัดนี้เป็นต้นไป + // สมาชิกคลาส +} +``` + +* ตัวอย่างที่ 2 + +```js +class Car { +} +let car = Car(); // จะเกิด TypeError เพราะไม่ได้ใช้ new +``` + +* ตัวอย่างที่ 3 + +```js +class Car { + constructor (speed) { + this.speed = speed; + } + drive() { /*ซอร์สโค้ด*/ } +} +let car = new Car(100); +for(let c in car) { + console.log(c); +} +// แสดงผลลัพธ์เป็น +// "speed" +``` + +* ตัวอย่างที่ 4 + +```js +class Car { + constructor() { + Car = 100; // ไม่สามารถเปลี่ยนแปลง Car ได้เลย + } +} +Car = 100; // กำหนดค่าใหม่ให้กับตัวแปร Car จากข้างนอกคลาส จะสามารถทำได้ +``` + +## เบื้องหลังของคลาส + +```js +class Car { + constructor (speed) { + this.speed = speed; + } + drive() { + console.log("Driving speed:", this.speed); + } +} +let carObj = new Car(100); +carObj.drive(); // "Driving speed: 100" +console.log(typeof carObj); // "object" +console.log(carObj instanceof Car); // true +console.log(carObj instanceof Object); // true +console.log(carObj.drive === Car.prototype.drive); // true +console.log(typeof Car.prototype.drive); // "function" +// คลาส Car ก็คือฟังก์ชันคอนสตรัคเตอร์ที่ชื่อ Car +console.log(typeof Car); // "function" +console.log(Car.name); // "Car" +console.log(Car === Car.prototype.constructor); // true +console.log(Car.prototype.constructor.name); // "Car" +``` + +```js +let Car = function() { + "use strict"; + const Car = function(speed) { + if(typeof new.target === "undefined") { + throw new TypeError("Cannot call a class as a function"); + } + this.speed = speed; + }; + Object.defineProperty(Car.prototype, "drive", { + value: function() { + if(typeof new.target !== "undefined") { + throw new TypeError("Method cannot be called with new."); + } + console.log("Driving speed:", this.speed); + } + ,enumerable: false + ,writable: true + ,configurable: true + }); + return Car; +}(); // เทคนิค IIFE +let carObj = new Car(100); +carObj.drive(); // "Driving speed: 100" +``` + +## เพิ่มสมาชิกเข้าไปในคลาสทีหลัง + +```js +class Car { + constructor (speed) { + this.speed = speed; + } +} +Car.prototype.drive = function() { // เพิ่มเมธอดเข้าไปทีหลัง + console.log("Driving speed:", this.speed); +}; +let car = new Car(100); +car.drive(); +// แสดงผลลัพธ์ +// "Driving speed: 100" +for(let c in car) { + console.log(c); +} +// ประโยค for ...in จะแสดงผลลัพธ์ดังนี้ +// "speed" +// "drive" +``` + +```js +class Car { + constructor (speed) { + this.speed = speed; + } +} +Object.defineProperty(Car.prototype, "drive", { + value: function() { + console.log("Driving speed:", this.speed); + }, + enumerable: false, + writable: true, + configurable: true + }); +let car = new Car(100); +car.drive(); +// แสดงผลลัพธ์ +// "Driving speed: 100" +for(let c in car) { + console.log(c); +} +// ประโยค for ...in จะแสดงผลลัพธ์ดังนี้ +// "speed" +``` + +## นิพจน์คลาส + +```js +let Car = class { // คลาสไร้ชื่อ + // สมาชิกคลาส +}; +console.log(typeof Car); // "function" +let carObj = new Car(); +console.log(typeof carObj); // "object" +console.log(carObj instanceof Car); // true +``` + +```js +let Car2 = class Car1 { + // สมาชิกคลาส +}; +console.log(typeof Car2); // "function" +let carObj = new Car2(); +``` + +```js +let carObj = new Car(); // จะเกิด TypeError ได้ +var Car = class{ }; // บรรทัด 2 -- นิพจน์คลาสจะถูกมองเห็นตั้งแต่บรรทัดนี้เป็นต้นไป +/* จะเสมือนเขียนแบบนี้ +var Car; +let carObj = new Car(); +Car = class{ }; */ +``` + +```js +function myFunction(classExpr) { + let c = new classExpr(); // บรรทัด a + c.drive(100); +} +myFunction( class { // บรรทัด b -- คลาสไร้ชื่อ + drive(speed) { + console.log("Driving speed:", speed); + } +}); +// แสดงผลลัพธ์เป็น +// "Driving speed: 100" +``` + +```js +function myFunction(classExpr) { + return class { + drive(speed) { + console.log("Driving speed:", speed); + } + } +} +let Car = myFunction(); +let carObj = new Car(); +carObj.drive(100); // "Driving speed: 100" +``` + +```js +let carObj = new class { + constructor (speed) { + this.speed = speed; + } + drive() { + console.log("Driving speed:", this.speed); + } +}(100); +console.log(typeof carObj); // "object" +carObj.drive(100); // "Driving speed: 100" +``` + +## พร็อพเพอร์ตี้แอคเซสเซอร์ + +```js +class Car { + constructor () { + this.speedValue = 100; + } + get speed() { + return this.speedValue; + } + set speed(speedValue) { + this.speedValue = speedValue; + } +} +let carObj = new Car(100); +console.log(carObj.speed); // 100 +carObj.speed = 60; +console.log(carObj.speed); // 60 +console.log(carObj.speedValue); // 60 (เข้าถึงได้ แต่ไม่ควรเข้าถึงด้วยวิธีนี้ โดยตรง) + +let desc = Object.getOwnPropertyDescriptor(Car.prototype, "speed"); +console.log("get" in desc); // true +console.log("set" in desc); // true +console.log(desc.enumerable); // false +``` + +## สมาชิกที่เป็นสแตติก + +### ฟิวด์สแตติก + +```js +class Car { + static speed = 100; + color = "red"; +} +console.log(Car.speed); // 100 +let carObj = new Car(); +console.log(carObj.color); // "red" +console.log(typeof carObj.speed); // undefined +``` + +```js +class Car { + static speed = 100; // speed เป็นของคลาส + speed = Car.speed * 10; // บรรทัด a -- Car.speed * 10 = 1000 + drive() { + console.log("Driving speed:", Car.speed); // บรรทัด b + } +} +console.log(Car.speed); // 100 +let carObj = new Car(); +console.log(carObj.speed); // 1000 +carObj.drive(); // Driving speed: 100 +``` + +### เมธอดสแตติก + +```js +class Car { + constructor (speed) { // ห้ามมีคำว่า static นำหน้าคอนสตัคเตอร์ + this.speed = speed; + } + drive() { + console.log("Driving speed:", this.speed); + } + static stop() { // เมธอดสแตติก + console.log("Stop this car"); + } +} +// เมธอดสแตติก +Car.stop(); // "Stop this car" +let carObj = new Car(100); +carObj.drive(); // "Driving speed: 100" +console.log(typeof carObj.stop); // undefined +``` + +```js +class Car { + static constructor () { // จะกลายเป็นเมธอดสแตติกชื่อ constructor + console.log("constructor function"); + } +} +Car.constructor(); // "constructor function" +``` + +```js +class Car { + static set color(value) { + this.value = value; // จะเสมือนเขียนเป็น Car.value = value + } + static get color() { + return this.value; // จะเสมือนเขียนเป็น return Car.value; + } +} +Car.color = "red"; +console.log(Car.color); // "red" +console.log(Car.value); // "red" +let carObj = new Car(); +console.log(typeof carObj.color); // undefined +``` + +```js +class Car { + constructor (speed) { + this.speed = speed; + } + drive() { + console.log("Driving speed:", this.speed); + } + static set color(value) { // เมธอด setter + this.value = value; + } +} +let carObj = new Car(100); +console.log(carObj.value); // undefined +console.log(carObj.speed); // 100 +carObj.drive(); // "Driving speed: 100" +Car.color= "red"; +console.log(Car.value); // "red" +console.log(Car.speed); // undefined +``` + +```js +class Car { + static speed = 100; + static reduce(val) { + // หลีกเลี่ยงใช้ this + // return this.speed / val; + return Car.speed / val; // บรรทัด a + } + static drive() { + // หลีกเลี่ยงใช้ this + console.log("Driving speed:", Car.reduce(10) ); // บรรทัด b + } +} +Car.drive(); // "Driving speed: 10" +``` + +```js +class Car { + static speed = 100; + static drive(value) { + console.log("Driving speed:", value); + } +} +let carObj = new Car(); +for (let prop in carObj) { + console.log(prop) // จะไม่เข้ามาทำงานในประโยค for +} + +for (let prop in Car) { + console.log(prop) +} +// แสดงผลลัพธ์ +// "speed" +``` + +## การใช้วงเล็บเหลี่ยมในคลาส + +```js +let name1= "speed"; +let name2 = "drive"; +let name3 = "stop"; +let name4 = Symbol("reduce"); +class Car { + ["constructor"] (speedValue) { // กลายเป็นเมธอดตัวหนึ่ง ไม่ใช่คอนสตรัคเตอร์ + this.speedValue = speedValue; + console.log("Not a constructor: speed =", this.speedValue); + } + set [name1](speedValue) { // เมธอด setter + this.speedValue = speedValue; + } + get [name1]() { // เมธอด getter + return this. speedValue; + } + [name2]() { // เมธอดที่ไม่ใช่สแตติก + console.log("Driving speed:", this.speedValue); + } + static [name3]() { // เมธอดสแตติก + console.log("Stop this car"); + } + [name4]() { // ชื่อเมธอดเป็นซิมโบล + console.log("Reduce speed"); + } + [2+2]() { // ชื่อเมธอดเป็นนิพจน์ 2+2 = 4 + console.log("Start this car"); + } +} +let carObj = new Car(100); +console.log(carObj.speedValue); // undefined +carObj.constructor(100); // "Not a constructor: speed = 100" +console.log(carObj.speedValue); // 100 +carObj.speed = 60; +console.log(carObj.speed); // 60 +carObj.drive(); // "Driving speed: 60" +Car.stop(); // "Stop this car" +// หรือจะเรียกเมธอดผ่านวงเล็บเหลี่ยมก็ได้ +carObj[name4](); // "Reduce speed" +carObj[3+1](); // "Start this car" +``` + +```js +let speed= "speed"; +let reduce = Symbol("reduce"); +class Car { + [speed] = 100; + ["drive"] = "Driving this car"; + [reduce] = 1; + [2+2] = 20.5; +} +let carObj = new Car(); +console.log( carObj[speed] ); // 100 +console.log( carObj["drive"] ); // "Driving this car" +console.log( carObj[reduce] ); // 1 +console.log( carObj[3+1] ); // 20.5 +``` + +## การสืบทอดคลาส + +```js +class Calculation { + constructor (a, b) { + this.a = a; + this.b = b; + } + multiply() { + return this.a * this.b; + } +} +class Division extends Calculation { // บรรทัด a -- Division สืบทอดมาจาก Calculation + constructor (a, b) { + super(a, b); // บรรทัด b -- เรียกใช้คอนสตรัคเตอร์ของ Calculation + // สามารถกำหนดค่าให้กับ this.a และ this.b ที่อยู่ในคลาสแม่ได้โดยตรง + // แต่การทำเช่นนี้จะไม่ปลอดภัย + // this.a = a; // ไม่ควรทำ + // this.b = b; // ไม่ควรทำ + } + divide() { + return this.a / this.b; + } +} +let div = new Division(20,10); +console.log(div.multiply()); // 200 +console.log(div.divide()); // 2 +console.log(div.a, div.b); // 20 10 (ไม่ควรเข้าถึงข้อมูลอินสแตนซ์โดยตรง ด้วยวิธีนี้) +console.log(div instanceof Division); // true +console.log(div instanceof Calculation); // true +console.log(div instanceof Object); // true +// เบื้องหลังจะมีการทำ prototype chain +console.log(Object.getPrototypeOf(div) === Division.prototype); // true +console.log(Object.getPrototypeOf(Division.prototype) === Calculation.prototype); // true +``` + +```js +function Calculation(a, b) { + this.a = a; + this.b = b; +} +Calculation.prototype.multiply = function() { + return this.a * this.b; +} +function Division (a, b) { + Calculation.call(this, a, b); // เรียกใช้ฟังก์ชันคอนสตรัคเตอร์ Calculation +} +Division.prototype = Object.create(Calculation.prototype, { // prototype chain + constructor: { + value: Division, + enumerable: false, + writable: true, + configurable: true + } +}); +Division.prototype.divide = function() { + return this.a / this.b; +} +let div = new Division(20,10); +console.log(div.multiply()); // 200 +console.log(div.divide()); // 2 +console.log(div.a, div.b); // 20 10 +console.log(div instanceof Division); // true +console.log(div instanceof Calculation); // true +``` + +### คอนสตรัคเตอร์ดีฟอลต์ของคลาสลูก + + +```js +class Calculation { + constructor (a, b) { + this.a = a; + this.b = b; + console.log("Calculation:", a , b); + } +} +class Division extends Calculation { + // ไม่มีคอนสตรัคเตอร์ + // แต่จาวาสคริปต์ จะสร้างคอนสตรัคเตอร์ที่เป็นดีฟอลต์มาให้ +} +let div = new Division(100 , 200); // "Calculation: 100 200" +// จะเสมือนมีคอนสตรัคเตอร์ที่เป็นค่าดีฟอลต์มาให้ดังนี้ +/* class Division extends Calculation { + constructor (...args) { + super(...args); + } +} */ +``` + +```js +class Calculation { +} +class Division extends Calculation { +} +``` + +```js +class Calculation { +} +class Division extends Calculation { + constructor() { + // ปราศจากการเรียกใช้ super() + } +} +new Division(); // เกิด ReferenceError +``` + +## การสืบทอดคลาสหลายระดับชั้น + +```js +class Animal { + constructor(name) { + this.name = name; + console.log("Animal constructor"); // บรรทัด a + } + showName() { + console.log("Animal is", this.name); + } + static sleep() { + console.log("This animal is sleeping"); + } +} +class Quadruped extends Animal { + constructor(name) { + super(name); + console.log("Quadruped constructor"); // บรรทัด b + } + showColor() { + console.log(this.name, "is red"); + } +} +class Dog extends Quadruped { + constructor(name) { + super(name); + console.log("Dog constructor"); // บรรทัด c + } + run() { + console.log(this.name, "is running"); + } +} +let dogObj = new Dog("Pit bull"); +// คอนสตรัคเตอร์จะทำงานก่อนรันเมธอด ด้วยการแสดงผลลัพธ์ +//"Animal constructor" +//"Quadruped constructor" +//"Dog constructor" +console.log(dogObj instanceof Dog); // true +console.log(dogObj instanceof Quadruped); // true +console.log(dogObj instanceof Animal); // true +console.log(dogObj instanceof Object); // true +dogObj.showName(); // "Animal is Pit bull" +dogObj.showColor(); // "Pit bull is red" +dogObj.run(); // "Pit bull is running" +Dog.sleep(); // "This animal is sleeping" +console.log(typeof dogObj.sleep); // undefined +``` + +## โอเวอร์ไรด์เมธอดของคลาสแม่ + +```js +class Calculation { + constructor (a, b) { + this.a = a; + this.b = b; + } + multiply() { + return this.a * this.b; + } +} +class Multiplying extends Calculation { + constructor (a, b) { + super(a, b); + } + multiply() { // โอเวอร์ไรด์เมธอด multiply() ของคลาสแม่ + return "The result is " + super.multiply(); + } +} +let m = new Multiplying(20,10); +console.log(m.multiply()); // "The result is 200" +``` + +```js +class Calculation { + constructor (a, b) { + this.a = a; + this.b = b; + } + multiply() { + return this.a * this.b; + } +} +let name = "multiply"; +class Multiplying extends Calculation { + constructor (a, b) { + super(a, b); + } + [name]() { + return "The result is " + super.multiply(); + // หรือจะเขียนเป็น return "The result is " + super[name](); + } +} +let m = new Multiplying(20,10); +console.log(m[name]()); // "The result is 200" +console.log(m.multiply()); // "The result is 200" +``` + +```js +class Animal { + constructor (name) { + this.name = name; // บรรทัด a + } + set animalName(name) { + this.name = name; + } + get animalName() { + return this.name; + } +} +class Dog extends Animal { + constructor (name) { + super(name); // บรรทัด b + } + showName() { + console.log(this.name); // "A dog" + console.log(super.name); // undefined + // เข้าถึงพร็อพเพอร์ตี้แอคเซสเซอร์ของคลาสแม่ ผ่านทาง super + super.animalName="Pit bull dog"; + console.log(super.animalName); // "Pitbull dog" + } +} +let dogObj = new Dog("A dog"); +dogObj.showName(); +/* แสดงผลลัพธ์เป็น +"A dog" +undefined +"Pit bull dog" */ +``` + +## สืบทอดคลาสแบบนิพจน์ + +```js +function getClass() { + let c = class Calculation { + constructor (a, b) { + this.a = a; + this.b = b; + } + multiply() { + return this.a * this.b; + } // สิ้นสุดการประกาศเมธอด multiply + } // สิ้นสุดการประกาศคลาส Calculation + return c; // รีเทิร์นคลาสออกไป +} +class Multiplying extends getClass() { // สืบทอดคลาสแบบนิพจน์ + constructor (a, b) { + super(a, b); + } +} +let m = new Multiplying(20, 10); +console.log(m.multiply()); // 200 +``` + +```js +function Calculation (a,b) { + this.a = a; + this.b = b; +} +Calculation.prototype.multiply = function() { + return this.a * this.b; +}; +class Multiplying extends Calculation { + constructor (a, b) { + super(a, b); + } +} +let m = new Multiplying(20, 10); +console.log(m.multiply()); // 200 +``` + +## การสืบทอดคลาสมากกว่า 1 ตัว + +```js +let MultiplyingObj = { + multiply() { + return this.a * this.b; + } +}; +let DivisionObj = { + divide() { + return this.a / this.b; + } +}; +function getClass (...args) { + let merged = function() {}; // ฟังก์ชันคอนสตรัคเตอร์ + // เมธอดของ MultiplyingObj กับ DivisionObj จะมารวมอยู่ที่ merged.prototype + Object.assign(merged.prototype, ...args); // บรรทัด a + return merged; // รีเทิร์น merged ซึ่งทำหน้าที่เป็นฟังก์ชันคอนสตรัคเตอร์ +} +class MyCalc extends getClass(MultiplyingObj, DivisionObj) { + // สืบทอดมาจาก MultiplyingObj กับ DivisionObj + constructor (a, b) { + super(); + // ไม่สามารถเรียก super(a,b); + this.a = a; + this.b = b; + } +} +let calc = new MyCalc(20,10); +console.log(calc.multiply()); // 200 +console.log(calc.divide()); // 2 +``` + +## สืบทอดคลาสจากที่มีอยู่แล้วในจาวาสคริปต์ + +```js +class ArrayExt extends Array { + get(index) { + return this[index]; + } +} +let array = new ArrayExt(); +array[0] = 1; +array[1] = 2; +console.log(array.get(0)); // 1 +console.log(array.get(1)); // 2 +``` + +```js +class ArrayExt extends Array { + constructor(length){ + super(length); + } +} +let a1 = ArrayExt.of("one", "two", "three"); +console.log(a1 instanceof ArrayExt ); // true +console.log(a1 instanceof Array ); // true +console.log(a1.length); // 3 +let a2 = ArrayExt.from(["one", "two", "three"]); +console.log(a2 instanceof ArrayExt ); // true +console.log(a2 instanceof Array ); // true +console.log(a2.length); // 3 +``` + +## คอมโพสิชั่น + +```js +class Calculation { + constructor (a, b) { + this.a = a; + this.b = b; + } + execute() { + return this.a * this.b; + } +} +class Multiplying { + constructor (calcObj) { + if(calcObj instanceof Calculation) { + this.calcObj = calcObj; //บรรทัด a -- อ้างถึงอ็อบเจ็กต์ที่เป็นอินสแตนซ์ของ Calculation + } + } + multiply() { + return "This value is " + this.calcObj.execute(); // บรรทัด b + } +} +let c1 = new Calculation(10, 10); +let m1 = new Multiplying(c1); +console.log(m1.multiply()); // "This value is 100" +let c2 = new Calculation(20, 20); +let m2 = new Multiplying(c2); +console.log(m2.multiply()); // "This value is 400" +``` + +## new.target + +```js +class Calculation { + constructor () { + console.log(new.target === Calculation); // true + // new.target คือคลาส Calculation + console.log(new.target); // "class Calculation" + console.log(Calculation); // "class Calculation" + } +} +new Calculation(); +// แสดงผลลัพธ์ +// true +// "class Calculation" (แต่ละจาวาสคริปต์รันไทม์แสดงผลไม่เหมือนกัน) +// "class Calculation" (แต่ละจาวาสคริปต์รันไทม์แสดงผลไม่เหมือนกัน) +``` + +```js +class Calculation { + constructor () { + // ถ้าคลาสลูกเรียกคอนสตรัคเตอร์ของแม่ ค่าของ new.target จะมีค่าเท่ากับ undefined + console.log("new.target in Calculaton:", new.target === Calculation); + } +} +class Multiplying extends Calculation { + constructor () { + super(); + console.log("new.target in Multiplying:", new.target === Multiplying); + } +} +new Multiplying(); +// แสดงผลลัพธ์ +// "new.target in Calculaton: false" +// "new.target in Multiplying: true" +``` + +## สมาชิกแบบ private + +```js +class Car { + speed = 100; +} +let carObj = new Car(); +carObj.speed = 200 // บรรทัด a -- ไม่ควรเข้าถึง speed โดยตรง +carObj.speed = -10 // บรรทัด b -- ไม่ควรเข้าถึง speed โดยตรง +``` + +### ฟิวด์ที่เป็น private + +```js +class Car { + #speed = 100; // เมื่ออินสแตนซ์ถูกสร้างขึึ้นมา ฟิวด์ #speed จะมีค่าเป็น 100 + /*static { + console.log(#speed in new Car()); // true + }*/ +} +let carObj = new Car(); +console.log(carObj.speed); // undefined +console.log(carObj.#speed); // เกิด SyntaxError +``` + +```js +class Car { + #speed; // ไม่จำเป็นต้องกำหนดค่าเริ่มต้นก็ได้ + constuctor() { + this.#speed = 100; // กำหนดค่าให้ทีหลัง + } +} +let carObj = new Car(); // เมื่ออินสแตนซ์ถูกสร้างขึึ้นมา ฟิวด์ #myField จะมีค่าเป็น 100 +``` + +```js +class Car { + constuctor() { + this.speed = 100; // บรรทัด a -- ทำงานได้ปกติ + this.#value = 1; // บรรทัด b -- เกิด SyntaxError + } +} +``` + +### เมธอดที่เป็น private + +```js +class Car { + #drive() { // บรรทัด a + console.log("Driving this car"); + } + /*static { + console.log( #drive in new Car()); // true + }*/ +} +let carObj = new Car(); +carObj.#drive(); // เกิด SyntaxError +``` + +### ฟิวด์สแตติกที่เป็น private + +```js +class Car { + static #speed = 100; + /*static { + console.log( #speed in Car ); // true + }*/ +} +console.log(Car.speed); // undefined +console.log(Car.#speed ); // SyntaxError +``` + +### เมธอดสแตติกที่เป็น private + +```js +class Car { + static #drive() { + console.log("Driving this car"); + } + /*static { + console.log( #drive in Car ); // true + }*/ +} +Car.#drive(); // เกิด SyntaxError +``` + +```js +class Car { + #speedValue; // ไม่จำเป็นต้องประกาศค่าเริ่มต้นก็ได้ + get #speed() { + return this.#speedValue; + } + set #speed(value) { + this.#speedValue = value; + } +} +``` + +```js +class MyClass { + #syncMethod() { } + get #accessor() { } + set #accessor(value) { } + * #syncGenerator() { } + async #asyncMethod() { } + async * #asyncGenerator() { } +} +``` + +```js +class MyClass { + static #syncMethod() { } + static get #accessor() { } + static set #accessor(value) { } + static * #syncGenerator() { } + static async #asyncMethod() { } + static async * #asyncGenerator() { } +} +``` + +### ตัวอย่างการเข้าถึงสมาชิกที่เป็น private + +* ตัวอย่าง การเข้าถึงฟิวด์ที่เป็น private ภายในอินสแตนซ์ + +```js +class Car { + #speed = 100; + drive() { + console.log("Driving speed:", this.#speed); // บรรทัด a + } +} +let objCar = new Car(); +objCar.drive(); // "Driving speed: 100" +``` + +* ตัวอย่าง การเข้าถึงเมธอดที่เป็น private ภายในอินสแตนซ์ + +```js +class Car { + #getSpeed() { + return 100; + } + drive() { + console.log("Driving speed:", this.#getSpeed()); // บรรทัด a + } +} +let objCar = new Car(); +objCar.drive(); // "Driving speed: 100" +``` + +* ตัวอย่าง การเข้าถึงฟิวด์ที่เป็น private จากคอนสตรัคเตอร์ และ getter กับ setter ภายในอินสแตนซ์ + +```js +class Car { + #speed ; // บรรทัด a + constructor(speed) { + this.#speed = speed; // บรรทัด b + } + get speed() { + return this.#speed; // บรรทัด c + } + set speed(speed) { + this.#speed = speed; // บรรทัด d + } +} +let objCar = new Car(100); +objCar.speed = 5; +console.log(objCar.speed); // 5 +``` + +* ตัวอย่าง การเข้าถึงฟิวด์สแตติกที่เป็น private จากภายในบอดี้ของคลาส + +```js +class Car { + static #speed = 100; + drive() { + console.log("Driving speed:", Car.#speed); // บรรทัด a + } +} +let objCar = new Car(); +objCar.drive(); // Driving speed: 100 +``` + +* ตัวอย่าง การเข้าถึงเมธอดแตติกที่เป็น private จากภายในบอดี้ของคลาส + +```js +class Car { + static #getSpeed() { + return 100; + } + drive() { + console.log("Driving speed:", Car.#getSpeed()); // บรรทัด a + } +} +let objCar = new Car(); +objCar.drive(); // "Driving speed: 100" +``` + +* สรุป สมาชิกใดๆ ที่จะเข้าถึงสมาชิกที่เป็น private ก็ขอให้ประกาศอยู่ภายใต้บอดี้ของคลาสเดียวกันก็สามารถเข้าถึงได้หมดเลย ดังตัวอย่าง + +```js +class Car { + #speed = 10; + speedValue = this.#speed * 10; // บรรทัด a +} +let objCar = new Car(); +console.log(objCar.speedValue); // 100 +``` + +```js +class MyClass { + #myField; + constructor(value) { + this.#myField = value; + } + showValue(inst) { + console.log("Show value:", inst.#myField ); + } +} +let inst1 = new MyClass(1); +let inst2 = new MyClass(100); +inst1.showValue(inst2); // "Show value: 100" +``` + +### การสืบทอดสมาชิกที่เป็น private + +```js +class SuperClass { + #superField = 1; +} +class MyClass extends SuperClass { + showMsg() { + console.log("Result:", this.#superField); // บรรทัด a -- เกิด SyntaxError + } +} +``` + +```js +class SuperClass { + #superField = 1; + get superField() { + return this.#superField; + } +} +class MyClass extends SuperClass { + showMsg() { + console.log("Result:", super.superField); // บรรทัด a + } +} +let inst = new MyClass(); +inst.showMsg(); // "Result: 1" +``` + +### ข้อควรรู้ชื่อ private ในอินสแตนซ์ + +```js +class Car { + #speed = 100; + color = "red"; + showSpeed() { + console.log( this.#speed ); + } + showColor() { + console.log( this.color ); + } +} +/*let carObj = new Car() +carObj.showSpeed(); // 100 +carObj.showColor(); // "red"*/ +``` + +```js +let Car; +{ // ขอบเขตของคลาส + const speed = Symbol(); + Car = class { + __PrivateElements__ = new Map([ + [speed, 100], + ]); + color = "red" + showSpeed() { + console.log( this.__PrivateElements__.get(speed) ); + } + showColor() { + console.log( this.color ); + } + } +} +``` + +### ชื่อฟิวด์ที่เป็น private เมื่อยู่คนละคลาส จะไม่ชนกัน + +```js +class ClassA { + #myField = 1; +} +class ClassB { + #myField = 100; +} +``` + +```js +class SuperClass { + #myField = 1; // บรรทัด a + get myField() { + return this.#myField; + } +} +class MyClass extends SuperClass { + #myField = 2; // บรรทัด b + showMsg() { + console.log("Result:", super.myField + this.#myField); // บรรทัด c + } +} +let inst = new MyClass(); +inst.showMsg(); // "Result: 3" +``` + +### ชื่อที่เป็น private กับ public จะไม่ชนกัน + +```js +class MyClass { + myField; + #myField; + myMethod(){ } + #myMethod(){ } +} +``` + +### ลำดับการสร้างฟิวด์ เมื่อมีการสืบทอด + +```js +class MyClass { + constructor() { + console.log("MyClass constructor"); // บรรทัด a + } + pubField = console.log("pubField"); // บรรทัด b + #privateField = console.log("privateField"); // บรรทัด c +} +new MyClass(); +// แสดงผลลัพธ์ +// "pubField" +// "privateField" +// "MyClass constructor" +``` + +```js +class SuperClass { + superField = console.log("superField"); // บรรทัด a + constructor() { + console.log("SuperClass constructor"); // บรรทัด b + } + } +class MyClass extends SuperClass { + myField = console.log("myField"); // บรรทัด c + constructor() { + super(); + console.log("MyClass constructor"); // บรรทัด d + } + } +new MyClass(); +// แสดงผลลัพธ์ +// "superField" +// "SuperClass constructor" +// "myField" +// "MyClass constructor" +``` + +```js +class SuperClass { + #superField = console.log("#superField"); // บรรทัด a + constructor() { + console.log("SuperClass constructor"); // บรรทัด b + } + } +class MyClass extends SuperClass { + #myField = console.log("#myField"); // บรรทัด c + constructor() { + super(); + console.log("MyClass constructor"); // บรรทัด d + } + } +new MyClass(); +// แสดงผลลัพธ์ +// "#superField" +// "SuperClass constructor" +// "#myField" +// "MyClass constructor" +``` + +## บล็อกสแตติก + +```js +class Car { + static { // บรรทัด a + console.log("Driving this car"); // บรรทัด b + } +} +// แสดงผลลัพธ์ +// "Driving this car" +``` + +* ตัวอย่าง การใช้สแตติกบล็อกเข้าถึงฟิวด์ของอินสแตนซ์ที่เป็น private + +```js +class Car { + #speed; + static { + let objCar = new Car(); + objCar.#speed = 100; + } +} +``` + +* ตัวอย่าง การใช้สแตติกบล็อกเข้าถึงฟิวด์ของอินสแตนซ์ที่เป็น public + +```js +class Car { + speed=1; + static { + let objCar = new Car(); + objCar.speed = 100; + } +} +``` + +* ตัวอย่าง การใช้สแตติกบล็อกเข้าถึงฟิวด์สแตติกของคลาสที่เป็น private + +```js +class Car { + static #speed; + static { + Car.#speed = 100; + } +} +``` + +* ตัวอย่าง การใช้สแตติกบล็อกเข้าถึงฟิวด์สแตติกของคลาสที่เป็น public + +```js +class Car { + static speed; + static { + Car.speed = 100; + } +} +``` + +```js +class Car { + static speed = 100; + static drive() { + console.log("Driving this car"); + } + static { + console.log(this.speed); // บรรทัด a + this.drive(); // บรรทัด b + } +} +// แสดงผลลัพธ์ +// 100 +// "Driving this car" +``` + +```js +class Car { + static #speed = 100; + static #drive() { + console.log("Driving this car"); + } + static { + console.log(this.#speed); // บรรทัด a + this.#drive() ; // บรรทัด b + } +} +// แสดงผลลัพธ์ +// 100 +// "Driving this car" +``` + +```js +class Car { + static speed = 100; + static { + console.log(this.speed); // บรรทัด a + console.log(this === Car); // บรรทัด b -- true + } +} +// แสดงผลลัพธ์ +// 100 +// true +``` + +```js +class Car { + static { + console.log(Car.speed); // บรรทัด a -- มองไม่เห็น จะได้เป็น undefined + } + static speed = 100; // บรรทัด b +} +// แสดงผลลัพธ์ +// undefined +``` + +```js +class Car { + static { + console.log("static"); // บรรทัด a + } + static speed = console.log("speed"); // บรรทัด b + static color = console.log("color"); // บรรทัด c +} +// แสดงผลลัพธ์ +// "static" +// "speed" +// "color" +``` + +```js +class MyClass { + static { + console.log("Line a"); // บรรทัด a + } + static { + console.log("Line b"); // บรรทัด b + } +} +// แสดงผลลัพธ์ +// "Line a" +// "Line b" +``` + +```js +class MyClass { + static myFiled1 = console.log("myFiled1"); // บรรทัด a + static { + console.log("Static line b"); // บรรทัด b + } + static myFiled2 = console.log("myFiled2"); // บรรทัด c + static { + console.log("Static line d"); // บรรทัด d + } +} +// แสดงผลลัพธ์ +// "myFiled1" +// "Static line b" +// "myFiled2" +// "Static line d" +``` + +```js +class SuberClass { + static superFiled = console.log("superFiled"); // บรรทัด a + static { + console.log("Static line b"); // บรรทัด b + } +} +class MyClass extends SuberClass { + static myFiled = console.log("myFiled"); // บรรทัด c + static { + console.log("Static line d"); // บรรทัด d + } +} +// แสดงผลลัพธ์ +// "superFiled" +// "Static line b" +// "myFiled" +// "Static line d" +``` + +```js +class SuperClass { + static #myFiled = 1; + static showMsg() { + console.log( this.#myFiled); // บรรทัด a + } +} +class MyClass extends SuperClass { + // สืบทอด showMsg() มาด้วย +} +MyClass.showMsg(); // TypeError +``` + +```js +class SuperClass { + static #myFiled = 1; + static showMsg() { + console.log( SuperClass.#myFiled); // บรรทัด a + } +} +class MyClass extends SuperClass { + // สืบทอด showMsg() มาด้วย +} +MyClass.showMsg(); // 1 +``` + +## เบื้องหลังสมาชิกของอินสแตนซ์ + +```js +class MyClass { + #privateField = 1 + #privateMethod() { } + get #privateFieldValue() { } + set #privateFieldValue(field) { } + pubField = 2; + publicMethod() { } + get pubFieldValue() { } + set pubFieldValue(field) { } +} +let inst = new MyClass(); +console.log(Object.keys(inst)) // [ 'pubField' ] +``` + +```js +class MyClass { + static #privateField = 2 + static #privateMethod() { } + static get #privateFieldValue() { } + static set #privateFieldValue(field) { } + static pubField = 1; // มองเห็นเป็นชื่อคีย์ pubField + static publicMethod() { } + static get pubFieldValue() { } + static set pubFieldValue(field) { } +} +console.log(Object.keys(MyClass)) // [ 'pubField' ] +``` + +### การแชร์เมธอดระหว่างอินสแตนซ์ + +```js +class Car { + #drive() { } + stop() { } + static { + let car1 = new Car(); + let car2= new Car(); + console.log( car1.#drive === car2.#drive ); // true -- บรรทัด a + console.log( car1.stop === car2.stop ); // true -- บรรทัด b + } +} +// แสดงผลลัพธ์ +// true +// true +``` + +```js +class Car { + get #speed() { return 100; } + set #speed(value) { } + get color() { return "red"; } + set color(value) { } + static { + let car1 = new Car(); + let car2= new Car(); + console.log( car1.#speed === car2.#speed ); // true -- บรรทัด a + console.log( car1.color === car2.color ); // true -- บรรทัด b + } +} +// แสดงผลลัพธ์ +// true +// true +``` + +```js +class Car { + #drive() { } + static { + let carObj = new Car(); + console.log( #drive in carObj ); // true -- บรรทัด a + console.log( #drive in Car.prototype ); // false -- บรรทัด b + console.log( #drive in Car ); // false + } +} +// แสดงผลลัพธ์ +// true +// false +// false +``` + +```js +class Car { + get #speed() { return 100; } + set #speed(value) { } + static { + let carObj = new Car(); + console.log( #speed in carObj ); // true -- บรรทัด a + console.log( #speed in Car.prototype ); // false -- บรรทัด b + console.log( #speed in Car ); // false + } +} +// แสดงผลลัพธ์ +// true +// false +// false +``` + +```js +class Car { + drive() { } + get speed() { return 100; } + set speed(value) { } +} +let objCar = new Car(); +console.log(objCar.drive == Car.prototype.drive); // true -- บรรทัด a +console.log(objCar.speed == Car.prototype.speed); // true -- บรรทัด b +``` + +## ตรวจสอบสมาชิกที่เป็น private + +* ตัวอย่าง ตรวจสอบฟิวด์ที่เป็น private ด้วยโอเปอเรเตอร์ in + +```js +class Car { + #speed = 100; + static check(target) { + console.log( #speed in target ); // บรรทัด a + } +} +let objCar = new Car(); +Car.check(objCar); // true +Car.check(Car); // false +``` + +* ตัวอย่าง ตรวจสอบเมธอดที่เป็น private ด้วยโอเปอเรเตอร์ in + +```js +class Car { + #drive() { + console.log("Driving this car"); + } + static check(target) { + console.log( #drive in target ); // บรรทัด a + } +} +let objCar = new Car(); +Car.check(objCar); // true +Car.check(Car); // false +``` + +*ตัวอย่าง ตรวจสอบฟิวด์สแตติกที่เป็น private ด้วยโอเปอเรเตอร์ in + +```js +class Car { + static #speed = 100; + static check(target) { + console.log( #speed in target ); // บรรทัด a + } +} +let objCar = new Car(); +Car.check(objCar); // false +Car.check(Car); // true +``` + +* ตัวอย่าง ตรวจสอบเมธอดสแตติกที่เป็น private ด้วยโอเปอเรเตอร์ in + +```js +class Car { + static #drive() { + console.log("Driving this car"); + } + static check(target) { + console.log( #drive in target ); // บรรทัด a + } +} +let objCar = new Car(); +Car.check(objCar); // false +Car.check(Car); // true +``` + +## แอ็บสแตรคท์คลาส + +```js +class Calculation { + constructor() { + if(new.target === Calculation) { + throw new Error("Abstract class cannot be instantiated.") + } + } + execute() { + // ไม่มีซอร์สโค้ด ต้องให้คลาสอื่นมา extends เพื่อไปใช้งานต่อ + } +} +new Calculation(); // จะโยน error ออกมา + +class Multiplying extends Calculation { + constructor (a, b) { + super(); + this.a = a; + this.b = b + } + execute() { + return this.a * this.b; + } +} +let m = new Multiplying(2, 2); +console.log(m.execute()); // 4 +``` + +## Polymorphism + +```js +class Calculation { + constructor (a, b) { + if (new.target === Calculation) { + throw new Error("Abstract class cannot be instantiated.") + } + this.a = a; + this.b = b + } + execute() { } // เมธอดเปล่าๆ ยังไม่มีการทำงานอะไร +} +class Multiplying extends Calculation { + constructor (a, b) { + super(a, b); + } + execute() { + return this.a * this.b ; + } +} +class Division extends Calculation { + constructor (a, b) { + super(a, b); + } + execute() { + return this.a / this.b ; + } +} +class Subtraction extends Calculation { + constructor (a, b) { + super(a, b); + } + execute() { + return this.a - this.b ; + } +} +function calc(calcObj) { + if(calcObj instanceof Calculation) { + // ผลการทำงานจะขึ้นอยู่กับอ็อบเจ็กต์ที่ส่งเข้ามา ว่ามันเป็นอะไร + console.log(calcObj.execute()); + } +} +calc(new Multiplying(20, 10) ); // 200 +calc(new Division(20, 10) ); // 2 +calc(new Subtraction(20, 10) ); // 10 +``` \ No newline at end of file diff --git a/examples_book/Chapter15.md b/examples_book/Chapter15.md new file mode 100644 index 0000000..b240272 --- /dev/null +++ b/examples_book/Chapter15.md @@ -0,0 +1,785 @@ +# โค้ดบทที่ 15 คอลเลคชั่น + +## อาเรย์ + +### Array.of() + +```js +let array = Array.of( 1, 2, 3 ); +console.log(array.length); // 3 +console.log(array); // [ 1, 2, 3 ] +console.log(array[0], array[1], array[2]); // 1 2 3 +// จะเสมือนสร้างอาร์เรย์โดยใช้วงเล็บเหลี่ยม +// let array = [1, 2, 3]; +``` + +### Array.from() + +```js +let a = [1, 2, 3]; +let array1 = Array.from(a); +console.log(array1); // [ 1, 2, 3 ] +let str = "456"; +let array2 = Array.from(str); +console.log(array2); // [ '4', '5', '6' ] +let set = new Set([7, 8, 9]); +let array3 = Array.from(set); +console.log(array3); // [ 7, 8, 9 ] +``` + +```js +let obj = { + length: 4, + 1: "foo", + 2: "bar" +}; +let array = Array.from(obj); +console.log(obj.length); // 4 +console.log(array); // [ undefined, 'foo', 'bar', undefined ] +``` + +```js +let a = Array.from( { length: 4 } ); // มีสมาชิกทั้งหมด 4 ตัว ที่มีค่าเป็น undefined +console.log(a); // [ undefined, undefined, undefined, undefined ] +``` + +```js +let b = Array( 4 ); +console.log(b); // [ <4 empty items> ] +let c = Array.apply( null, { length: 4 } ); +console.log(c); // [ undefined, undefined, undefined, undefined ] +``` + +```js +let obj = { + length: 4, + 1: "foo", + 2: "bar" +}; +let toUpper = function (value,index) { // ฟังก์ชันคอลแบ็ค + if (typeof value == "string") { + return value.toUpperCase(); // รีเทิร์นสตริงตัวพิมพ์ใหญ่ + } else { + return "index_" + index; // รีเทิร์นข้อความที่ขึ้นต้นด้วยคำว่า "index_" แล้วตามด้วยอินเด็กซ์ + } +}; +let array = Array.from( obj, toUpper ); +console.log(array); +// จะแปลงจาก [ undefined, 'foo', 'bar', undefined ] +// ให้กลายมาเป็น [ 'index_0', 'FOO', 'BAR', 'index_3' ] +``` + +```js +let obj = { + length: 4, + 1: "foo", + 2: "bar" +}; +let array = Array.from( obj, function (value,index) { + // console.log(this === obj); // true + return this[index]; +},obj); // อากิวเมนต์ตัวที่สาม +console.log(array); // [ undefined, 'foo', 'bar', undefined ] +``` + +### copyWithin() + +```js +let a1 = [0, 1, 2, 3, 4, 5]; +a1.copyWithin( 3, 0 ); +console.log(a1); // [ 0, 1, 2, 0, 1, 2 ] +let a2 = [0, 1, 2, 3, 4, 5]; +a2.copyWithin( 3, 0, 2 ); +console.log(a2); // [ 0, 1, 2, 0, 1, 5 ] +``` + +```js +let a3 = [0, 1, 2, 3, 4, 5]; +a3.copyWithin( 0, -2 ); +console.log(a3) // [ 4, 5, 2, 3, 4, 5 ] +let a4 = [0, 1, 2, 3, 4, 5]; +a4.copyWithin( 0, -3, -1); +console.log(a4); // [ 3, 4, 2, 3, 4, 5 ] +``` + +### fill() + +```js +let array = Array.of("a", "b", "c", "d"); +console.log(array); // [ 'a', 'b', 'c', 'd' ] +array.fill( 1 ); +console.log(array); // [ 1, 1, 1, 1 ] +``` + +```js +let array = [ null, null, null, null ,null].fill( 10, 1 ); +console.log(array); // [ null, 10, 10, 10, 10 ] +``` + +```js +let array = [ null, null, null, null ,null].fill( 10, 1, 4 ); +console.log(array); // [ null, 10, 10, 10, null ] +``` + +### find() + +```js +let a = ["red", "green", "blue", "yellow"]; +function search(value, index, array) { // ฟังก์ชันคอลแบ็ค + //console.log(array); // [ 'red', 'green', 'blue', 'yellow' ] + return value == "blue"; +} +let result = a.find( search ); +console.log(result); // 'blue' +``` + +```js +let a = ["red", "green", "blue", "yellow"]; +let result = a.find( function (value, index, array) { + //console.log(array); // [ 'red', 'green', 'blue', 'yellow' ] + //console.log(this === a); // true + return this[index] == "blue"; +}, a); // อากิวเมนต์ตัวที่สอง +console.log(result); // 'blue' +``` + +### findIndex() + +```js +let a = ["red", "green", "blue", "yellow"]; +function search (value ,index, array) { // ฟังก์ชันคอลแบ็ค + // console.log(array); // [ 'red', 'green', 'blue', 'yellow' ] + return value == "blue"; +}; +let result = a.findIndex( search ); +console.log(result); // 2 +``` + +```js +let a = ["red", "green", "blue", "yellow"]; +let result = a.findIndex( function (value, index, array){ + // console.log(array); // [ 'red', 'green', 'blue', 'yellow' ] + // console.log(this === a); // true + return this[index] == "blue"; +}, a); // อากิวเมนต์ตัวที่สอง +console.log(result); // 2 +``` + +### includes() + +```js +let array = ["A", "B", "C"]; // ประกาศอาร์เรย์ +console.log(array.includes("A")); // true +console.log(array.includes("Z")); // false +``` + +```js +let array = ["A", "B", "C"]; // ประกาศอาร์เรย์ +// เริ่มค้นหา "B" จากอินเด็กซ์คือ 2 ซึ่งจะพบว่าหาไม่เจอ +console.log(array.includes("B", 2)); // false +// แต่ถ้าเปลี่ยนมาเริ่มค้นหาจากอินเด็กซ์เป็น 1 ก็จะหา "B" เจอ +console.log(array.includes("B", 1)); // true +``` + +```js +let array = [0, NaN, 1]; +console.log(array.indexOf(NaN)); // -1 เพราะไม่เจอสมาชิกที่ต้องการ +console.log(array.includes(NaN)); // true +``` + +หมายเหตุ -0 จะเว้นวรรคแล้วตามด้วยเครื่องหมาย , (เหตุผลเพราะต้องการ fixbug ในหน้าเพจนี้ ไม่เกี่ยวกับหนังสือ) +```js +let array = [-0 , NaN, 1]; +console.log(array.indexOf(+0)); // 0 เพราะเจอค่า -0 อยู่ในอาร์เรย์ที่ตำแหน่งอินเด็กซ์ 0 +console.log(array.includes(+0)); // true +``` + +### flat() + +หมายเหตุ โค้ดต่อไปนี้จะเป็นการประกาศตัวแปร arr1 ขึ้นมา จึงต้องก็อปปี้ไปรันในโค้ดถัดๆ ไป ภายในในหัวข้อ flat()ด้วย มิฉะนั้นจะหาตัวแปร arr1 ไม่เจอ +```js +let arr1 = [1, 2, 3, [4, 5, 6, [7, 8, 9, [10, 11, 12]]]]; +``` + +```js +let arr2 = arr1.flat(); // arr2 มีสมาชิกซ้อนกัน 3 ระดับ +console.log(arr2); // [ 1, 2, 3, 4, 5, 6, [ 7, 8, 9, [ 10, 11, 12 ] ] ] +let arr3 = arr2.flat(); // arr2 มีสมาชิกซ้อนกัน 2 ระดับ +console.log(arr3); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9, [ 10, 11, 12 ] ] +``` + +```js +let arr4 = arr1.flat().flat().flat(); // จับยืดออก 3 ครั้ง +console.log(arr4); +/* แสดงผลลัพธ์ +[ + 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10, + 11, 12 +] */ +``` + +```js +let arr5 = arr1.flat(3); // จับยืดออก 3 ครั้ง +console.log(arr5); +/* แสดงผลลัพธ์ +[ + 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10, + 11, 12 +] */ +``` + +```js +let arr6 = arr1.flat(Infinity); // คลี่ออกมาหมด +console.log(arr6); +/* แสดงผลลัพธ์ +[ + 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10, + 11, 12 +] */ +``` + +### flatMap() + +```js +let arr = [1, 2, 3, 4, 5]; +let newArr1 = arr.map( function(x) { + return [x, x * 2]; +}); +console.log(newArr1); // [ [ 1, 2 ], [ 2, 4 ], [ 3, 6 ], [ 4, 8 ], [ 5, 10 ] ] +``` + +```js +let arr = [1, 2, 3, 4, 5]; +let newArr2 = arr.flatMap( function(x) { + return [x, x * 2]; +}); +console.log(newArr2); +/* แสดงผลลัพธ์ +[ + 1, 2, 2, 4, 3, + 6, 4, 8, 5, 10 +] */ +``` + +### เสริมเพิ่มเติม + +```js +let array = [1, 2, 3, 4]; +array.forEach( (value, index, arrayObj) => console.log(`a[${index}] = ${value}`) ); +/*แสดงผลลัพธ์ +"a[0] = 1" +"a[1] = 2" +"a[2] = 3" +"a[3] = 4" */ +``` + +```js +let array = [1, 2, 3, 4]; +array.forEach( value => console.log(value)); // ส่ง value เป็นค่าอากิวเมนต์ตัวแรก +/*แสดงผลลัพธ์ +1 +2 +3 +4 */ +``` + +```js +let array = [1, 2, 3, 4, 5]; +let result = array.every( value => value > 0 ); // สมาชิกของอาร์เรย์ต้องมากกว่า 0 +console.log(result) // true +``` + +```js +let array = [1, 2, 3, 4, 5]; +let result = array.some( (value) => value == 3 ); // รีเทิร์น boolean +console.log(result) // true +result = array.find( (value) => value == 3 ); // รีเทิร์นสมาชิก +console.log(result) // 3 +result = array.findIndex( (value) => value == 3 ); // รีเทิร์นอินเด็กซ์ +console.log(result) // 2 +``` + +```js +let array = [1, 2, 3, 4, 5]; +let result = array.filter( value => value > 2 ); // เลือกเฉพาะสมาชิกที่มีค่ามากกว่า 2 +console.log(result) // [ 3, 4, 5 ] +``` + +```js +let array = [1, 2, 3, 4, 5]; +let result = array.reduce( (acc, value) => acc + value ); // บวกรวมสมาชิกทุกตัว +console.log(result) // 15 +``` + +```js +let arr = [1, 2, 3, 4, 5]; +let newArr1 = arr.map( value => 2 * value ); // แปลงสมาขิกทุกตัวด้วยการคูณสอง +console.log(newArr1); // [ 2, 4, 6, 8, 10 ] +``` + +```js +let arr = [1, 2, 3, 4, 5]; +let result = arr.flatMap(value => [value, value * 2]); +console.log(result); +/* แสดงผลลัพธ์ +[ + 1, 2, 2, 4, 3, + 6, 4, 8, 5, 10 +] */ +``` + +```js +let arr = [1, 2, 3, 4, 5]; +let result = Array.from( arr, value => 2 * value ); // แปลงสมาขิกทุกตัวด้วยการคูณสอง +console.log(result); // [ 2, 4, 6, 8, 10 ] +``` + +## เซท + +```js +let set = new Set(); +console.log(set.size); // 0 +``` + +```js +let set = new Set(); +set.add( 1 ); +set.add("2"); +set.add({id : 1}); +console.log(set); // Set(3) { 1, '2', { id: 1 } } +``` + +```js +let set = new Set(); +set.add( 1 ); +set.add("1"); +set.add( true ); +set.add( true ); +set.add( null ); +set.add( null ); +set.add( undefined ); +set.add( undefined ); +set.add({}); // บรรทัด a +set.add({}) // บรรทัด b +console.log(set); // Set(7) { 1, '1', true, null, undefined, {}, {} } +console.log(set.size); // 7 +``` + +```js +let set = new Set(); +set.add(1).add(2).add(3); // เรียกเมธอด add() ต่อเนื่องกันได้ +console.log(set); // Set(3) { 1, 2, 3 } +``` + +### คอนสตรัคเตอร์ของเซท + +```js +let set1 = new Set( ["a", "b", "c"] ); +console.log(set1); // Set(3) { 'a', 'b', 'c' } +let set2 = new Set( "def" ); +console.log(set2); // Set(3) { 'd', 'e', 'f' } +``` + +```js +let set1 = new Set( ["a", "b", "c"] ); +let set2 = new Set( set1); // สร้าง set2 จาก set1 ที่มีอยู่ก่อนแล้ว +console.log(set2); // Set(3) { 'a', 'b', 'c' } +``` + +### วิธีลบสมาชิกของเซท + +```js +let set = new Set(["a", "b", "c"]); +console.log(set); // Set(3) { 'a', 'b', 'c' } +console.log(set.delete("a")); // true +console.log(set); // Set(2) { 'b', 'c' } +set.clear(); +console.log(set.size); // 0 +``` + +### เมธอด has() + +```js +let set = new Set(); +let b = {b: 2}; +set.add(b); +set.add(0); +console.log(set.has(b)); // true +console.log(set.has(0)); // true +console.log(set.has(-0)); // true +console.log(Object.is(0,-0)); // false +``` + +### เมธอด forEach() + +```js +function log(value1, value2, setObj) { + // console.log(setObj); // Set { 'a', 'b', 'c' } + // console.log(this === set); // true + console.log(`[${value1}] = ${value2}`); +} +let set = new Set( ["a", "b", "c"] ); +set.forEach( log, set); // ระบุค่าอากิวเมนต์ตัวที่สองเป็น set +/*แสดงผลลัพธ์เป็น +"[a] = a" +"[b] = b" +"[c] = c" */ +``` + +```js +let set = new Set( ["a", "b", "c"] ); +set.forEach( value => console.log(value) ); // ระบุค่าอากิวเมนต์เป็นฟังก์ชันลูกศร +/*แสดงผลลัพธ์เป็น +a +b +c */ +``` + +### ข้อควรระวังเมื่อใช้อ็อบเจ็กต์ในเซท + +```js +let set = new Set(["a", true, 1]); +console.log(set.has("a")); // true +console.log(set.has(true)); // true +console.log(set.has(1)); // true +set.delete("a"); +set.delete(true); +set.delete(1); +console.log(set.size); // 0 +``` + +```js +let set = new Set(); +set.add({a: 1}); // -- บรรทัด a +console.log(set.delete({a:1})); // false -- บรรทัด b +let b = {b: 2}; +set.add(b) // -- บรรทัด c +console.log(set.delete(b)); // true -- บรรทัด d +let c = {c:3}; +set.add(c); +console.log(set.has({c:3})); // false -- บรรทัด e +console.log(set.has(c)); // true -- บรรทัด f +let d = [ ]; +set.add(d); +console.log(set.has([ ])); // false -- บรรทัด g +console.log(set.has(d)); // true -- บรรทัด h +``` + +# WeakSet + +```js +let a = { x: 1}, b = { y: 2}; +a = null; // อ็อบเจ็กต์ {x: 1} จะรอให้ GC มาเรียกคืนหน่วยความจำ +b = null; // อ็อบเจ็กต์ {y: 2} จะรอให้ GC มาเรียกคืนหน่วยความจำ +``` + +```js +let set = new Set(); +let a = { x: 1 }, b = { y: 2 }; +set.add(a).add(b); +a = null; // อ็อบเจ็กต์ { x: 1 } ยังไม่ถูก GC มาเรียกคืนหน่วยความจำ +b = null; // อ็อบเจ็กต์ { y: 2 } ยังไม่ถูก GC มาเรียกคืนหน่วยความจำ +console.log(set); // Set(2) { { x: 1 }, { y: 2 } } +``` + +```js +let wset = new WeakSet(); +let a = { x: 1 }, b = { y: 2 }; +wset.add( a ); +wset.add( b ); +console.log(wset.has(a)); // true +console.log(wset.has(b)); // true +a = null; // อ็อบเจ็กต์ { x: 1 } จะรอให้ GC มาเรียกคืนหน่วยความจำ +b = null; // อ็อบเจ็กต์ { y: 2 } จะรอให้ GC มาเรียกคืนหน่วยความจำ +console.log(wset.has(a)); // false +console.log(wset.has(b)); // false +``` + +## แม็พ + +```js +let map = new Map(); +console.log(map.size) // 0 +``` + +```js +let map = new Map(); +map.set("1", "Hello"); +map.set("1", "Hi" ); // เลือกใช้สมาชิกตัวนี้ +map.set( {id:2}, "World"); // บรรทัด a +map.set( {id:2}, "World"); // บรรทัด b +map.set( null, "You"); +map.set( null, "We"); // เลือกใช้สมาชิกตัวนี้ +map.set( undefined, "Good"); +map.set( undefined, "Bye"); // เลือกใช้สมาชิกตัวนี้ +console.log(map); +/*แสดงผลลัพธ์เป็น +Map(5) { '1' => 'Hi', { id: 2 } => 'World', { id: 2 } => 'World', null => 'We', undefined => 'Bye' } +*/ +``` + +```js +let map = new Map(); +map.set(1, "a").set(2, "b").set(3, "c"); +console.log(map); // Map(3) { 1 => 'a', 2 => 'b', 3 => 'c' } +``` + +### คอนสตรัคเตอร์ของแม็พ + +```js +let map = new Map( [ [1, "a"] , [2, "b"] ]); +console.log(map); // Map(2) { 1 => 'a', 2 => 'b' } +``` + +```js +let map1 = new Map(); +map1.set( 1, "a"); +map1.set( 2, "b"); +let map2 = new Map(map1); +console.log(map2); // Map(2) { 1 => 'a', 2 => 'b' } +``` + +### วิธีลบสมาชิกของแม็พ + +```js +let map = new Map(); +map.set( 1, "a"); +map.set( 2, "b"); +map.set( 3, "c"); +console.log(map); // Map(3) { 1 => 'a', 2 => 'b', 3 => 'c' } +console.log(map.delete(1)); // true +console.log(map); // Map(2) { 2 => 'b', 3 => 'c' } +map.clear(); +console.log(map.size); // 0 +``` + +### เมธอด get() และ has() + +```js +let map = new Map(); +map.set( 1, "a"); +map.set( 2, "b"); +console.log(map.get( 1 )); // "a" +console.log(map.get( 2 )); // "b" +``` + +```js +let map = new Map(); +map.set( 0, "a" ); +map.set( 1, "b" ); +console.log(map.has(0)); // true +console.log(map.has(-0)); // true +console.log(map.has(1)); // true +``` + +### เมธอด forEach() + +```js +function log(value, key, mapObj) { + // console.log(mapObj); // Map(2) { 1 => 'a', 2 => 'b' } + // console.log(this === map); // true + console.log(`[${key}] = ${value}`); +} +let map = new Map( [ [1, "a"] , [2, "b"] ]); +map.forEach( log, map); //ระบุค่าอากิวเมนต์ตัวที่สองเป็น map +/*แสดงผลลัพธ์เป็น +"[1] = a" +"[2] = b" */ +``` + +```js +let map = new Map( [ [1, "a"] , [2, "b"] ]); +map.forEach( value => console.log(value) ); // ระบุค่าอากิวเมนต์เป็นฟังก์ชันลูกศร +/*แสดงผลลัพธ์เป็น +a +b */ +``` + +### ข้อควรระวังเมื่อใช้อ็อบเจ็กต์ในแม็พ + +```js +let map = new Map(); +let obj = { a:1 }; // --บรรทัด a +map.set( obj, "HI"); +console.log(map.delete({a:1})); // false --บรรทัด b +console.log(map.has({a:1})); // false --บรรทัด c +map.set([ ],"Bye"); // --บรรทัด d +console.log(map.has([ ])); // false --บรรทัด f +``` + +### การสร้างแม็พด้วยปีกกา + +```js +let map = { }; // จำลองการสร้างแม็พ +let a = { x: 1 }, b = { y: 2 }; +map[a] = "foo"; // กำหนดให้ a เป็นค่าคีย์ +map[b] = "bar"; // กำหนดให้ b เป็นค่าคีย์ +console.log(map[a]); // "bar" +console.log(map[b]); // "bar" +``` + +## WeakMap + +```js +let wmap = new WeakMap(); +let a = { x: 1 }, b = { y: 2}; +wmap.set( a, "foo" ); +wmap.set( b, "bar" ); +console.log(wmap.get(a)); // "foo" +console.log(wmap.get(b)); // "bar" +a = null; // { x: 1 } จะรอให้ GC มาเรียกคืนหน่วยความจำ +b = null; // { y: 1 } จะรอให้ GC มาเรียกคืนหน่วยความจำ +console.log(wmap.get(a)); // undefined +console.log(wmap.get(b)); // undefined +``` + +```js +let wmap = new WeakMap(); +let a = { x: 1 }, b = { y: 2}; +wmap.set(a,b); +b=null +console.log(wmap.get(a)); // { y: 2 } +``` + +## อาร์เรย์ระดับบิต + +```js +let buffer = new ArrayBuffer(32); // ระบุความยาว 32 ไบต์ (256 บิต) +console.log(buffer.byteLength); // 32 +``` + +```js +let buffer = new ArrayBuffer( 32 ); // ระบุความยาว 32 ไบต์ (256 บิต) +console.log(buffer.byteLength); // 32 +let uint16 = new Uint16Array( buffer ); +console.log(uint16.length); // สมาชิก 16 ตัว +``` + +```js +let buffer = new ArrayBuffer( 2 ); // 2 ไบต์ (16 บิต) +let uint16 = new Uint16Array( buffer ); +console.log(uint16.length); // 1 +uint16[0] = 0b0001110000001111; // 7183 (เลขฐานสิบ) +console.log(uint16[0] == 7183); // true +``` + +```js +let buffer = new ArrayBuffer( 2 ); // 2 ไบต์ (16 บิต) +let uint16 = new Uint16Array(buffer); // มีสมาชิกตัวเดียวขนาด 16 บิต +let uint8 = new Uint8Array(buffer); // มีสมาชิกสองตัว ตัวละ 8 บิต +uint16[0] = 0x105b; // 4187 (เลขฐานสิบ) +console.log(uint8[0] == 0x5b); // true +console.log(uint8[1] == 0x10); // true +[uint8[1], uint8[0] ] = [uint8[0], uint8[1]] // สลับข้อมูล (ดีสตรัคเตอร์ริ่ง) +console.log(uint8[0] == 0x10); // true +console.log(uint8[1] == 0x5b); // true +console.log(uint16[0]); // 23312 +``` + +### อาร์เรย์ระดับบิตอื่น ๆ + +```js +let buffer = new ArrayBuffer(2); // 2 ไบต์ (16 บิต) +let a = new Int16Array(buffer); // มองเห็น 16 บิตของบัฟเฟอร์ +let b = new Int8Array(buffer, 0, 1); // มองเห็น 8 บิตล่างของบัฟเฟอร์ +let c = new Int8Array(buffer, 1, 1); // มองเห็น 8 บิตบนของบัฟเฟอร์ +let d = new Int8Array(buffer,1); // เข้าถึงบัฟเฟอร์ตั้งแต่ offset มีค่าเป็น 1 เป็นต้นไป +a[0] = 0x105b; // 4187 (เลขฐานสิบ) +console.log(b[0] == 0x5b); // true +console.log(c[0] == 0x10); // true +console.log(d[0] == 0x10); // true +``` + +```js +let a = new Float32Array(2); +console.log(a.length); // 2 +console.log(a[0], a[1]); // 0 0 +let b = new Float64Array(a); +console.log(b.length); // 2 +console.log(b[0], b[1]); // 0 0 +let c = new Int32Array([100, 200]); +console.log(c[0], c[1]); // 100 200 +let likeArray = { + length: 2, + 0: 300, + 1: 400 +} +let d = new Uint32Array(likeArray); +console.log(d[0], d[1]); // 300 400 +``` + +### เมธอดของอาร์เรย์ระดับบิต + +```js +let int32 = new Int32Array( 3 ); +int32 [0] = 1; +int32 [1] = 2; +int32 [2] = 3; +let result = int32.map( function(i) { + return i*i; +} ); +console.log(result); // Int32Array(3) [ 1, 4, 9 ] +let join = int32.join( "," ); +console.log(join); // "1,2,3" +``` + +```js +var array = [ 10, 1, 5 ]; +array.sort(); +console.log(array); // [ 1, 10, 5 ] +let uint8 = new Uint8Array([1,10, 5]); +uint8.sort(); +console.log(uint8); // Uint8Array(3) [ 1, 5, 10 ] +``` + +```js +let uint8 = new Uint8Array( 2 ); +uint8[0] = 20; +uint8[1] = 40; +let result = uint8.map( function(i) { + return i * i; +} ); +console.log(result[0]); // 144 (จริง ๆ ควรได้ค่า 400) +console.log(result[1]); // 64 (จริง ๆ ควรได้ค่า 1600) +``` + +```js +let uint8 = new Uint8Array( 2 ); +uint8[0] = 20; +uint8[1] = 40; +let result1 = Uint8Array.from(uint8, function(i) { + return i * i; +} ); +console.log(result1); // Uint8Array(2) [ 144, 64 ] +let result2 = Array.from(uint8, function(i) { + return i * i; +} ); +console.log(result2); // [ 400, 1600 ] +``` + +### includes() + +```js +let uint8 = new Uint8Array([1, 2, 3, 4, 5]); +console.log(uint8.includes(1)); // true +console.log(uint8.includes(5)); // true +console.log(uint8.includes(10)); // false +``` + +## เมธอด at() + +```js +let array = ["a", "b", "c", "d", "e"]; +console.log(array.at(3)); // "d" +console.log(array.at(-3)); // "c" +``` + +```js +let str = "abcde"; +console.log(str.at(3)); // "d" +console.log(str.at(-3)); // "c" +``` + diff --git a/examples_book/Chapter16.md b/examples_book/Chapter16.md new file mode 100644 index 0000000..dba7b88 --- /dev/null +++ b/examples_book/Chapter16.md @@ -0,0 +1,832 @@ +# โค้ดบทที่ 16 อิเทอเรเตอร์ และเจนเนอเรเตอร์ + +## อินเทอเรเตอร์ + +### ตัวอย่างการสร้างอิเทอเรเตอร์ + +```js +function createIterator(array) { + let iterator = { + i : 0 // ทุกครั้งที่เรียกใช้ next() ค่าของ i จะบวกเพิ่มเป็นหนึ่งทุกครั้ง + ,next : function() { + // เมื่อเข้าถึงสมาชิกในอาร์เรย์ครบทุกตัว done จะมีค่าเป็น true + let done = (this.i >= array.length); + // เข้าถึงสมาชิกในอาร์เรย์ + let value = !done ? array[this.i++] : undefined; + return { value, done }; + } // สิ้นสุดการประกาศฟังก์ขั่น + }; + return iterator; +} +let iterator = createIterator([1, 5, 10]); +console.log(iterator.next()); // { value: 1, done: false } +console.log(iterator.next()); // { value: 5, done: false } +console.log(iterator.next()); // { value: 10, done: false } +console.log(iterator.next()); // { value: undefined, done: true } +console.log(iterator.next()); // { value: undefined, done: true } +``` + +## อิเทอเรเตอร์ที่มีอยู่แล้วในภาษา + +```js +let array = [1, 5]; +let iterator = array[Symbol.iterator](); +console.log(iterator.next()); // { value: 1, done: false } +console.log(iterator.next()); // { value: 5, done: false } +console.log(iterator.next()); // { value: undefined, done: true } +``` + +```js +let say = "hi"; +let iterator = say[Symbol.iterator](); +console.log(iterator.next()); // { value: 'h', done: false } +console.log(iterator.next()); // { value: 'i', done: false } +console.log(iterator.next()); // { value: undefined, done: true } +``` + +```js +console.log( typeof String.prototype[Symbol.iterator] ); // "function" +console.log( typeof Array.prototype[Symbol.iterator] ); // "function" +console.log( typeof Set.prototype[Symbol.iterator] ); // "function" +console.log( typeof Map.prototype[Symbol.iterator] ); // "function" +console.log( typeof Uint8Array.prototype[Symbol.iterator] ); // "function" +console.log( typeof WeakSet.prototype[Symbol.iterator] ); // "undefined" +console.log( typeof WeakMap.prototype[Symbol.iterator] ); // "undefined" +``` + +### อ็อบเจ็กต์ที่วนซ้ำได้ + +```js +for(let i of [1, 5, 10]) { + console.log(i) +} +/* แสดงผลลัพธ์ +1 +5 +10 */ +/* อาจเหมือนเขียนประโยคนี้ +let iterator = [1, 5, 10][Symbol.iterator](); +let result; +while( (result=iterator.next()) && result.done == false) { + let i = result.value; + console.log(i); +}*/ +``` + +### สตริงในประโยค for + +```js +let str = "ab𠮷"; +console.log(str.length); // 4 +for (let i=0; i < str.length; i++) { + console.log(str[i]); +} +/* แสดงผลลัพธ์ของประโยค for +"a" +"b" +(blank) +(blank) */ +``` + +```js +let str = "ab𠮷"; +for (let i of str) { + console.log(i); +} +/* แสดงผลลัพธ์ +"a" +"b" +"𠮷" */ +``` + +### เมธอด values(), keys และ entries() + +```js +let array = [5, 10]; +let set= new Set(["red", "green"]); +let map = new Map(); +map.set("firstname", "Somchai"); +map.set("lastname", "Jaidee"); +for (let value of array.values()) { + console.log(value); +} +for (let value of set.values ()) { + console.log(value); +} +for (let value of map.values ()) { + console.log(value); +} +/* จะแสดงผลลัพธ์ดังนี้ +เมื่อใช้ array.values() จะแสดงผลลัพธ์เป็น +5 +10 +เมื่อใช้ set.values() จะแสดงผลลัพธ์เป็น +"red" +"green" +เมื่อใช้ map.values() จะแสดงผลลัพธ์เป็น +"Somchai" +"Jaidee" */ +``` + +```js +let array = [5, 10]; +let set= new Set(["red", "green"]); +let map = new Map(); +map.set("firstname", "Somchai"); +map.set("lastname", "Jaidee"); +for (let key of array.keys()) { + console.log(key); +} +for (let key of set.keys ()) { + console.log(key); +} +for (let key of map.keys ()) { + console.log(key); +} +/* จะแสดงผลลัพธ์ดังนี้ +เมื่อใช้ array.keys() จะแสดงผลลัพธ์เป็น +0 +1 +เมื่อใช้ set.keys() จะแสดงผลลัพธ์เป็น +"red" +"green" +เมื่อใช้ map.keys() จะแสดงผลลัพธ์เป็น +"firstname" +"lastname" */ +``` + +```js +let array = [5, 10]; +let set= new Set(["red", "green"]); +let map = new Map(); +map.set("firstname", "Somchai"); +map.set("lastname", "Jaidee"); +for (let entry of array.entries()) { + console.log(entry); +} +for (let entry of set.entries()) { + console.log(entry); +} +for (let entry of map.entries()) { + console.log(entry); +} +/* จะแสดงผลลัพธ์ดังนี้ +เมื่อใช้ array.entries() จะแสดงผลลัพธ์เป็น +[ 0, 5 ] +[ 1, 10 ] +เมื่อใช้ set.entries() จะแสดงผลลัพธ์เป็น +[ 'red', 'red' ] +[ 'green', 'green' ] +เมื่อใช้ map.entries() จะแสดงผลลัพธ์เป็น +[ 'firstname', 'Somchai' ] +[ 'lastname', 'Jaidee' ] */ +``` + +```js +let array = [5, 10]; +let set= new Set(["red", "green"]); +let map = new Map(); +map.set("firstname", "Somchai"); +map.set("lastname", "Jaidee"); +for (let value of array) { // จะเหมือนกับการใช้ array.values() + console.log(value); +} +for (let value of set) { // จะเหมือนกับการใช้ set.values() + console.log(value); +} +for (let entry of map) { // จะเหมือนกับการใช้ map.entries() + console.log(entry); +} +/* จะแสดงผลลัพธ์ดังนี้ +5 +10 +"red" +"green" +[ 'firstname', 'Somchai' ] +[ 'lastname', 'Jaidee' ] */ +``` + +### โอเปอเรเตอร์สเปรด + +* ตัวอย่างที่ 1 จะแสดงการใช้งานโอเปอเรเตอร์สเปรดกับสตริง เพื่อนำไปกำหนดค่าให้เป็นสมาชิกของอาร์เรย์ + +```js +let array = [..."abc"]; // จะเหมือนเขียนเป็น let array = ["a", "b", "c"]; +console.log(array[0], array[1], array[2]); // "a b c" +``` + +* ตัวอย่างที่ 2 เมื่อใช้โอเปอเรเตอร์สเปรดกับเซท มันจะไปเรียกเมธอด @@iterator ของเซท ให้สร้างอิเทอเรเตอร์ขึ้นมา เพื่อใช้เข้าถึงและแตกสมาชิกของเซทออกมา + +```js +let set = new Set(["a", "b", "c"]) ; +let array = [...set]; +console.log(array[0], array[1], array[2]); // "a b c" +``` + +```js +let set = new Set(["a", "b", "b", "c", "c",]); // Set(3) { 'a', 'b', 'c' } +let keys = [ ...set.keys() ], + values = [ ...set.values() ], + entries = [ ...set.entries() ]; +console.log(keys); // [ 'a', 'b', 'c' ] +console.log(values); // [ 'a', 'b', 'c' ] +console.log(entries[0][0]); // "a" +console.log(entries[0][1]); // "a" +console.log(entries[1][0]); // "b" +console.log(entries[1][1]); // "b" +console.log(entries[2][0]); // "c" +console.log(entries[2][1]); // "c" +``` + +* ตัวอย่างที่ 3 จะแสดงการใช้งานโอเปอเรเตอร์สเปรดกับแม็พ เพื่อนำไปกำหนดค่าให้เป็นสมาชิกของอาร์เรย์ + +```js +let map = new Map([ [1,"a"] , [2, "b"] ]); +let keys = [ ...map.keys() ], + values = [ ...map.values() ], + entries = [ ...map.entries() ]; +console.log(keys); // [1, 2] +console.log(values); // [ 'a', 'b' ] +console.log(entries[0][0]); // 1 +console.log(entries[0][1]); // "a" +console.log(entries[1][0]); // 2 +console.log(entries[1][1]); // "b" +console.log( [...map]); // [ [ 1, 'a' ], [ 2, 'b' ] ] +``` + +* ตัวอย่างที่ 4 จะแสดงการใช้โอเปอเรเตอร์สเปรด กับอาร์เรย์ + +```js +let array = [ ]; +array.length = 3; +array[1] = "a"; +console.log(...array.keys()); // 0 1 2 +console.log(...array.values()); // undefined a undefined +console.log(...array.entries()); // [ 0, undefined ] [ 1, 'a' ] [ 2, undefined ] +console.log(...array); // undefined a undefined +``` + +* ตัวอย่างที่ 5 จะแสดงการใช้โอเปอเรเตอร์สเปรด กับอาร์เรย์ระดับบิต +```js +let uint8 = new Uint8Array( 2 ); +uint8[0] = 10 ; +uint8[1] = 20 ; +console.log(...uint8.keys()); // 0 1 +console.log(...uint8.values()); // 10 20 +console.log(...uint8.entries()); // [ 0, 10 ] [ 1, 20 ] +console.log(...uint8); // 10 20 +``` + +* ตัวอย่างที่ 6 จะแสดงการใช้งานโอเปอรเตอร์สเปรดกับคอลเลคชั่น เพื่อแตกค่าออกมาก่อน แล้วจึงส่งมันให้เป็นค่าอากิวเมนต์แก่ฟังก์ชัน +```js +let set = new Set([1, 2, 3]); +let array = [10, 20, 30]; +function plus(a, b, c) { + console.log(a + b + c); +} +plus(...set); // 6 +plus(...array); // 60 +``` + +### ยูเนียน อินเตอร์เซคชัน ผลต่างของเซต + +* ยูเนียน +```js +let a = new Set([1, 2, 3, 4, 5]); // Set(5) { 1, 2, 3, 4, 5 } +let b = new Set([3, 4, 5, 6, 7]); // Set(5) { 3, 4, 5, 6, 7 } +let union = new Set([...a, ...b]); +console.log(...union); // 1 2 3 4 5 6 7 +``` + +* อินเตอร์เซคชั่น +```js +let a = new Set([1, 2, 3, 4, 5]); // Set(5) { 1, 2, 3, 4, 5 } +let b = new Set([3, 4, 5, 6, 7]); // Set(5) { 3, 4, 5, 6, 7 } +let temp = [...a]; // แตกสมาชิกของเซทออกมา ให้กลายมาเป็นอาร์เรย์ชั่วคราว +let intersect = new Set(temp.filter( item => b.has(item) ) ); +// หรือจะเขียนสั้น ๆ ได้เป็น +// let intersect = new Set([...a].filter(item => b.has(item) ) ); +console.log(...intersect); // 3 4 5 +``` + +* ผลต่างของเซต +```js +let a = new Set([1, 2, 3, 4, 5]); // Set(5) { 1, 2, 3, 4, 5 } +let b = new Set([3, 4, 5, 6, 7]); // Set(5) { 3, 4, 5, 6, 7 } +let temp = [...a]; // แตกสมาชิกของเซทออกมา ให้กลายมาเป็นอาร์เรย์ชั่วคราว +let diff = new Set(temp.filter(item => !b.has(item) ) ); +// หรือจะเขียนสั้น ๆ ได้เป็น +// let diff = new Set([...a].filter(item => !b.has(item) ) ); +console.log(...diff); // 1 2 +``` + +### ดีสตรัคเตอร์ริ่ง + +* ตัวอย่างที่ 1 ลองพิจารณาการใช้งานเซท กับวิธีดีสตรัคเตอร์ริ่ง + +```js +let [a, b, c] = new Set(["a", "b", "c"]); +console.log(a, b, c) // "a b c" +``` + +* ตัวอย่างที่ 2 ลองพิจารณาการใช้งานแม็พ กับวิธีดีสตรัคเตอร์ริ่ง + +```js +let map = new Map(); +map.set("firstname", "Somchai"); +map.set("lastname", "Jaidee"); +//let map = new Map( [ ["firstname","Somchai"] , ["lastname", "Jaidee"] ] ); +let [a, b] = map; +console.log(a); // [ 'firstname', 'Somchai' ] +console.log(b); // [ 'lastname', 'Jaidee' ] +let [ [key1, value1] , [key2, value2]] = map; +console.log(key1,key2); // "firstname lastname" +console.log(value1,value2); // "Somchai Jaidee" +``` + +* ตัวอย่างที่ 3 ลองพิจารณาการใช้งานอาร์เรย์ระดับบิต ด้วยการใช้โอเปอเรเตอร์สเปรด ร่วมกับวิธีดีสตรัคเตอร์ริ่ง + +```js +let uint8 = new Uint8Array( 2 ); +[...uint8] = [10, 20]; +console.log(uint8[0], uint8[1]); // 10 20 +``` + +* ตัวอย่างที่ 4 ลองพิจารณาการนำข้อมูลจากคอลเลคชั่นมากำหนดค่าให้กับอาร์เรย์ ด้วยการใช้โอเปอเรเตอร์สเปรด ร่วมกับวิธีดีสตรัคเตอร์ริ่ง +```js +let a1 = [], a2 = []; +let set = new Set([10, 20]); +[...a1] = [...set]; +console.log(a1[0], a1[1]); // 10 20 +let map = new Map( [ ["firstname","Somchai"] , ["lastname", "Jaidee"] ] ); +[...a2] = [...map]; +console.log(a2[0]); // [ 'firstname', 'Somchai' ] +console.log(a2[1]); // [ 'lastname', 'Jaidee' ] +console.log(a2[0][0], a2[0][1]); // "firstname Somchai" +console.log(a2[1][0], a2[1][1]); // "lastname Jaidee" +``` + +* ตัวอย่างที่ 5 ลองพิจารณาการใช้งานเมธอด entries() ของคอลเลคชั่น ในประโยค for …of + +```js +let array = [5, 10]; +let set= new Set(["red", "green"]); +let map = new Map( [["firstname","Somchai"] , ["lastname", "Jaidee"] ]); +let uint8 = new Uint8Array( 2 ); +[...uint8] = [10, 20]; +for(let [key,value] of array.entries()) { + console.log(key, value); +} +for(let [key,value] of set.entries()) { + console.log(key, value); +} +for(let [key,value] of map.entries()) { + console.log(key, value); +} +for(let [key,value] of uint8.entries()) { + console.log(key, value); +} + +/* +เมื่อใช้ array.entries() จะแสดงผลลัพธ์เป็น +0 5 +1 10 +เมื่อใช้ set.entries() จะแสดงผลลัพธ์เป็น +"red red" +"green green" +เมื่อใช้ map.entries() จะแสดงผลลัพธ์เป็น +"firstname Somchai" +"lastname Jaidee" +เมื่อใช้ uint8.entries() จะแสดงผลลัพธ์เป็น +0 10 +1 20 +*/ +``` + +* ตัวอย่างที่ 6 ลองพิจารณาการใช้วิธีดีสตรัคเตอร์ริ่ง มารับค่าจากการรีเทิร์นของฟังก์ชัน +```js +function myFunction() { + return new Set(["red", "green"]); +} +let [a, b] = myFunction(); +console.log(a, b); // "red green" +``` + +### พารามิเตอร์แบบดีสตรัคเตอร์ + +```js +function createGrade(firstName, lastName,[gender="Male", + age=18 ,subject="Math", gpa=0]) { + console.log(firstName, lastName, gender, age, subject, gpa); +} +let set = new Set(["Female", "21", "Science", "3.44"]); +let map = new Map([["k1","Male"], ["k2","19"], ["k3","Math"], ["k4","3.20"]]); +createGrade("Somchai", "Jaidee", set); // "Somchai Jaidee Female 21 Science 3.44" +createGrade("Mana", "Dekdee", map.values());// "Mana Dekdee Male 19 Math 3.20" +``` + +### อ็อบเจ็กต์ที่วนซ้ำได้แบบอื่นๆ + +```html + + + + + + +
    +
  • One
  • +
  • Two
  • +
  • Three
  • +
+ + + + +``` + +### วิธีสร้างอ็อบเจ็กต์ที่วนซ้ำได้ + + +```js +let obj = { array: [1, 5, 10] }; +obj[Symbol.iterator] = function () { // บรรทัดที่ 2 + return this.array[Symbol.iterator](); // บรรทัดที่ 3 + // return this.array.values(); // หรือจะเขียนแบบนี้ก็สามารถทำได้ +}; +for(let i of obj) { + console.log(i); +} +/* แสดงผลลัพธ์ +1 +5 +10 */ +``` + +```js +let obj = { + array: [1, 5, 10], + [Symbol.iterator] () { + return this.array[Symbol.iterator](); + // return this.array.values(); + } +}; +``` + +```js +class Iterable { + constructor(array) { + this.array = array; + } + [Symbol.iterator] () { + return this.array[Symbol.iterator](); + // return this.array.values(); + } +} +let obj = new Iterable([1, 5, 10]); +for (let i of obj) { + console.log(i); +} +/* แสดงผลลัพธ์เป็น +1 +5 +10 */ +``` + +```js +String.prototype[Symbol.iterator] = function() { + let item = this; + let iterator = { + i : 0 // ทุกครั้งที่เรียกใช้ next() ค่าของ i จะบวกเพิ่มเป็นหนึ่งทุกครั้ง + ,next() { + // เมื่อเข้าถึงสมาชิกในอาร์เรย์ครบทุกตัว done จะมีค่าเป็น true + let done = (this.i >= item.length); + // เปลี่ยนเป็นตัวอักษรตัวใหญ่ + let value = !done ? item[this.i++].toUpperCase() : undefined; + return { value, done }; + } // สิ้นสุดการประกาศเมธอด next() + }; + return iterator; +} +for(let i of "abc") { + console.log(i); +} +/* แสดงผลลัพธ์เป็น +"A" +"B" +"C" */ +``` + +## เจนเนอเรเตอร์ + +```js +function * genIterator() { + // ซอร์สโค้ด + yield 1; + // ซอร์สโค้ด + yield 5; + // ซอร์สโค้ด + yield 10; + // ซอร์สโค้ด +} +let iterator = genIterator(); +console.log(iterator.next()); // { value: 1, done: false } +console.log(iterator.next()); // { value: 5, done: false } +console.log(iterator.next()); // { value: 10, done: false } +console.log(iterator.next()); // { value: undefined, done: true } +``` + +```js +function * genIterator(array) { + for (let i=0; i < array.length; i++) { + // ซอร์สโค้ดก่อนหน้า + yield array[i]; // เมื่อประโยคนี้ทำงานเสร็จ ก็จะหยุดรอการเรียก iterator.next() ครั้งต่อไป + // ซอร์สโค้ดตามหลัง + } +} +let iterator = genIterator ([1, 5, 10]); +console.log(iterator.next()); // { value: 1, done: false } +console.log(iterator.next()); // { value: 5, done: false } +console.log(iterator.next()); // { value: 10, done: false } +console.log(iterator.next()); // { value: undefined, done: true } +``` + +```js +function * genIterator(item) { + for (let i=0; i < item.length; i++) { + yield item[i]; + } +} +let iterator = genIterator([1, 5, 10]); +for (let i of iterator) { // อิเทอเรเตอร์ของเจนเนอเรเตอร์สามารถอยู่ในประโยค for …of ได้ + console.log(i); +} +/* แสดงผลลัพธ์เป็น +1 +5 +10 */ +``` + + +### ประโยค return ในเจนเนอเรเตอร์ + +```js +function * genIterator () { + yield 1; + yield 5; + return 45; // จบการทำงานแค่บรรทัดนี้ + yield 10; // เส้นทางการทำงานจะไม่มาถึงบรรทัดนี้ +} +let iterator = genIterator (); +console.log(iterator.next()); // { value: 1, done: false } +console.log(iterator.next()); // { value: 5, done: false } +console.log(iterator.next()); // { value: 45, done: true } +console.log(iterator.next()); // { value: undefined, done: true } +console.log(iterator.next()); // { value: undefined, done: true } +``` + +```js +function * genIterator () { + yield 1; + yield 5; + return; // จบการทำงานแค่บรรทัดนี้ + yield 10; +} +let iterator = genIterator (); +console.log(iterator.next()); // { value: 1, done: false } +console.log(iterator.next()); // { value: 5, done: false } +console.log(iterator.next()); // { value: undefined, done: true } +``` + +### ประโยคคำสั่ง yield *นิพจน์ + +```js +function * g() { + yield 2; + yield 3; +} +function * genIterator() { + yield 1; // บรรทัด a + yield* g(); // บรรทัด b + yield* "45"; // บรรทัด c + yield* Array.from(arguments); // บรรทัด d + let item = new Set([8, 9]); + yield* item.values(); // บรรทัด e +} +let iterator = genIterator(6, 7); // บรรทัด f +console.log(iterator.next()); // { value: 1, done: false } +console.log(iterator.next()); // { value: 2, done: false } +console.log(iterator.next()); // { value: 3, done: false } +console.log(iterator.next()); // { value: '4', done: false } +console.log(iterator.next()); // { value: '5', done: false } +console.log(iterator.next()); // { value: 6, done: false } +console.log(iterator.next()); // { value: 7, done: false } +console.log(iterator.next()); // { value: 8, done: false } +console.log(iterator.next()); // { value: 9, done: false } +console.log(iterator.next()); // { value: undefined, done: true } +``` + +```js +function * f() { + yield 1; // บรรทัด a + return 2; // บรรทัด b +} +function * g(count) { + for(let i=0; i<2 ; i++) { + yield count + i; // บรรทัด c + } +} +function * genIterator() { + let result = yield *f(); // บรรทัด d + yield result + 10; // บรรทัด e + yield *g(result); // บรรทัด f +} +let iterator = genIterator(); +console.log(iterator.next()); // { value: 1, done: false } +console.log(iterator.next()); // { value: 12, done: false } +console.log(iterator.next()); // { value: 2, done: false } +console.log(iterator.next()); // { value: 3, done: false } +console.log(iterator.next()); // { value: undefined, done: true } +``` + +### ส่งค่าอากิวเมนต์ไปให้เมธอด next() + +```js +function * genIterator() { + let a = yield 1; + let b = yield a + 3; + yield b + 7; +} +let iterator = genIterator(); +console.log(iterator.next()); // { value: 1, done: false } +console.log(iterator.next(2)); // { value: 5, done: false } +console.log(iterator.next(3)); // { value: 10, done: false } +console.log(iterator.next(4)); // { value: undefined, done: true } +``` + +### เมธอด throw() + +```js +function * genIterator() { + let a = yield 1; + let b = yield a + 3; + yield b + 7; +} +let iterator = genIterator (); +console.log(iterator.next()); // { value: 1, done: false } +console.log(iterator.next(2)); // { value: 5, done: false } +console.log(iterator.throw(new Error(''))); // error +``` + +```js +function * genIterator() { + let a = yield 1; + let b; + try { + b = yield a + 3; + } catch(ex) { + b = 3; + } + yield b + 7; +} +let iterator = genIterator(); +console.log(iterator.next()); // { value: 1, done: false } +console.log(iterator.next(2)); // { value: 5, done: false } +console.log(iterator.throw(new Error(''))); // { value: 10, done: false } +console.log(iterator.next()); // { value: undefined, done: true } +``` + +### นิพจน์เจนเนอเรเตอร์ + +```js +let genIterator = function *(item) { // นิพจน์เจนเนอเรเตอร์ + for (let i=0; i < item.length; i++) { + yield item[i]; + } +} +for (let i of genIterator([1, 5, 10]) ) { + console.log(i); +} +/* แสดงผลลัพธ์เป็น +1 +5 +10 */ +``` +### ประกาศเจนเนอเรเตอร์ในอ็อบเจ็กต์ + +```js +let obj = { }; +obj.genIterator = function *(item) { // ประกาศเจนเนอเรเตอร์ + for (let i=0; i < item.length; i++) { + yield item[i]; + } +}; +for(let i of obj.genIterator( [1, 5, 10] ) ) { + console.log(i); +} +/* แสดงผลลัพธ์ +1 +5 +10 */ +``` + +```js +let obj = { + genIterator: function *(item) { // ประกาศเจนเนอเรเตอร์ + for (let i=0; i < item.length; i++) { + yield item[i]; + } + } +}; +``` + +```js +let obj = { + *genIterator (item) { // ประกาศเจนเนอเรเตอร์แบบย่อ + for (let i=0; i < item.length; i++) { + yield item[i]; + } + } +}; +``` + +### ประกาศเจนเนอเรเตอร์ในคลาส + +```js +class IteratorClass { + *genIterator (item) { // ประกาศเจนเนอเรเตอร์ + for (let i=0; i < item.length; i++) { + yield item[i]; + } + } +} +let myClass = new IteratorClass(); +for(let i of myClass.genIterator([1, 5, 10]) ) { + console.log(i); +} +/* แสดงผลลัพธ์ +1 +5 +10 */ +``` + +### สร้างอ็อบเจ็กต์ที่วนซ้ำได้ด้วยเจนเนอเรเตอร์ + +```js +let obj = { item: [1, 5, 10] }; +obj[Symbol.iterator] = function *() { // ประกาศเจนเนอเรเตอร์ + yield *this.item; +}; +for(let i of obj) { + console.log(i); +} +/* แสดงผลลัพธ์เป็น +1 +5 +10 */ +``` + +```js +let obj = { + item: [1, 5, 10], + [Symbol.iterator] : function *() { // ประกาศเจนเนอเรเตอร์ + yield *this.item; + } + }; +``` + +```js +let obj = { + item: [1, 5, 10], + *[Symbol.iterator]() { // ประกาศเจนเนอเรเตอร์แบบย่อ + yield *this.item; + } + }; +``` + +```js +class GenIterable { + constructor(item) { + this.item = item; + } + *[Symbol.iterator]() { // ประกาศเจนเนอเรเตอร์ + yield *this.item; + } +} +let obj = new GenIterable([1, 5, 10]); +for(let i of obj) { + console.log(i); +} +/* แสดงผลลัพธ์ +1 +5 +10 */ +``` diff --git a/examples_book/Chapter17.md b/examples_book/Chapter17.md new file mode 100644 index 0000000..1587972 --- /dev/null +++ b/examples_book/Chapter17.md @@ -0,0 +1,365 @@ +# โค้ดบทที่ 17 เมต้าโปรแกรมมิ่ง + +## เมต้าโปรแกรมมิ่ง + +### ฟังก์ชัน eval() + +```js +var function1 = "function myFunction (a, b) { console.log(a*b); }"; +var function2 = "function myFunction (a, b) { console.log(a/b); }"; +var randomNumber = Math.random() >= 0.5; // true หรือ false +var code = randomNumber ? function1 : function2; +eval(code); +myFunction(10, 5); // 50 หรือ 2 -- บรรทัด a +/* จะเสมือนเขียน +function myFunction(a, b) { + console.log(a*b); +}*/ +/* หรืออาจะเขียนเป็น +function myFunction(a, b) { + console.log(a/b); +}*/ +``` + +### Function() + +```js +var body1 = "console.log(a*b)"; +var body2 = "console.log(a/b)"; +var randomNumber = Math.random() >= 0.5; // true หรือ false +var myBody = randomNumber ? body1 : body2; +var myFunction = Function("a","b", myBody); +myFunction(10, 5); // 50 หรือ 2 +/* จะเสมือนเขียน +function myFunction(a, b) { + console.log(a*b); +}*/ +/* หรืออาจะเขียนเป็น +function myFunction(a, b) { + console.log(a/b); +}*/ +``` + +## Reflect + +```js +let obj = { }; +obj.a = 100; +console.log(obj.a); // 100 +``` + +```js +let obj = { }; +Reflect.set(obj, "a", 100); // เหมือนเขียนประโยค obj.a = 100; +console.log( Reflect.get(obj, "a") ); // 100 -- เหมือนเขียนประโยค console.log(obj.a); +``` + +## พร็อกซี่ + +```js +let obj = { a: 100 }; +let handler = { }; // บรรทัด 2 +let proxyObj = new Proxy( obj, handler); // บรรทัด 3 +console.log(proxyObj.a); // 100 (บรรทัด 4) +console.log(obj.a); // 100 (บรรทัด 5) +``` + +```js +// ห่ออาร์เรย์ +let proxyArray = new Proxy( [1, 2, 3], {}); // บรรทัด a +console.log(proxyArray[1]); // 2 +console.log(...proxyArray); // 1 2 3 +// ห่อฟังก์ชันลูกศร +let proxyFunc = new Proxy( ()=> console.log("myFunction") , {} ); // บรรทัด b +proxyFunc(); // "myFunction" +// ห่อพร็อกซี่ +let proxyObj1 = new Proxy(proxyArray, {}); // บรรทัด c +console.log(...proxyObj1); // 1 2 3 +// ห่อพร็อกซี่ +let proxyObj2 = new Proxy(proxyFunc, {}); // บรรทัด d +proxyObj2(); // "myFunction" +``` + +### Trap + +```js +let obj = { a: 100 }; +let trapLogic = { + get(targetObj,key, proxy) { + console.log(`key: ${key}`); + // targetObj ในตัวอย่างนี้คือ obj + // proxy ในตัวอย่างนี้คือ proxyObj + return Reflect.get(targetObj, key); + // เนื่องจาก Reflect บางเว็บบราวเซอร์อาจไม่ทำงาน จึงอาจเขียนแบบนี้แทนก็ได้ + // return targetObj[key]; + } + }; +let proxyObj = new Proxy( obj, trapLogic); +console.log(proxyObj.a); // บรรทัด a +// แสดงผลลัพธ์เป็น +// "key: a" +// 100 +console.log(obj.a); // 100 +``` + +### ตัวอย่างการใช้งานพร็อกซี่ + +* ตัวอย่างที่ 1 จะแสดงการดัดแปลงพฤติกรรมของอ็อบเจ็กต์ ด้วยการใช้พร็อกซี่ + +```js +let obj = { foo: 1 }; +let trapLogic = { + set(targetObj, key, value, proxy) { // กำหนดค่าให้กับพร็อพเพอร์ตี้ + if(Reflect.has( targetObj, key )) { + return Reflect.set( targetObj, key, value ); + } else { + throw `Can't write property: ${key}`; + } + } + ,get(targetObj, key, proxy) { // อ่านค่าพร็อพเพอร์ตี้ + if(Reflect.has( targetObj, key )) { + return Reflect.get(targetObj, key); + } else { + throw `Can't read property: ${key}`; + } + } + ,deleteProperty(targetObj, key){ // ลบพร็อพเพอร์ตี้ + if(Reflect.has( targetObj, key )) { + return Reflect.deleteProperty(targetObj, key); + } else { + throw `Can't delete property: ${key}`; + } + } +} +let proxyObj = new Proxy(obj, trapLogic); +proxyObj.foo = 100; +console.log(proxyObj.foo); // 100 +// console.log(delete proxyObj.foo); // true +proxyObj.a = 1; // error: Can't write property: a +console.log(proxyObj.b); // error: Can't read property: b +delete proxyObj.c ; // error: Can't delete property: c +``` + +* ตัวอย่างที่ 2 จะแสดงการตรวจสอบค่าอากิวเมนต์ที่ส่งไปให้พารามิเตอร์ของฟังก์ชัน รวมทั้งค่ารีเทิร์นจากฟังก์ชันด้วย + +```js +let validation = { // handler + apply(targetObj, thisObj, argumentList) { // ดักจับตอนฟังก์ชันเป้าหมายถูกเรียกใช้งาน + if( argumentList.length == 0 ) { // ไม่มีค่าอากิวเมนต์ส่งมาให้ฟังก์ชัน + throw new Error("Must send arguments to the function"); + } + argumentList.forEach( function(value, index, thisObj) { + if(typeof value != "number") { + // ตรวจสอบค่าอากิวเมนต์ที่ส่งให้ฟังก์ชัน มันเป็นตัวเลขหรือไม่ ? + throw new Error("All arguments must be numbers"); + } // สิ้นสุด if + }); // สิ้นสุด argumentList.forEach() + // เรียกใช้งานฟังก์ชันเป้าหมาย + let result = Reflect.apply(targetObj, thisObj, argumentList); + // ตรวจสอบค่าที่รีเทิร์นจากฟังก์ชัน มันอยู่ในช่วง safe integer หรือไม่ ? + if( Number.isSafeInteger(result) == false) { + throw new Error("The result is not safe integer"); + }; + return result; + } // สิ้นสุด apply +} // สิ้นสุดการประกาศอ็อบเจ็กต์ validation + +function sum(param1, param2) { // หาผลบวก + return param1 + param2; +} +function multiply(param1, param2) { // หาผลคูณ + return param1 * param2; +} + +let proxySum = new Proxy(sum, validation); +let proxyMultiply = new Proxy(multiply, validation); +// เรียกฟังก์ชัน โดยส่งค่าอากิวเมนต์ไปให้ ที่เป็นตัวเลข +// แล้วรีเทิร์นค่าไม่เกิน "safe integer" ก็จะทำงานได้ตามปกติ +console.log(proxySum(2, 3 )); // 5 = 2 + 3 +console.log(proxyMultiply(2, 3)); // 6 = 2 * 3 + +// ไม่มีค่าอากิวเมนต์ส่งไปให้ฟังก์ชัน ก็จะเกิด error +proxySum(); // Error: Must send arguments to the function +proxyMultiply(); // Error: Must send arguments to the function + +// เมื่อส่งค่าอากิวเมนต์ที่ไม่ใช่ตัวเลข ก็จะเกิด error +proxySum(2, "3"); // Error: All arguments must be numbers +proxyMultiply(2, "3"); // Error: All arguments must be numbers + +// ค่าที่รีเทิร์นออกจากฟังก์ชัน ถ้าเกินช่วง safe integer ก็จะเกิด error +let maxNum = Number.MAX_SAFE_INTEGER + 1; +proxySum(maxNum, maxNum); // Error: The result is not safe integer +proxyMultiply(maxNum, maxNum); // Error: The result is not safe integer +``` + +* ตัวอย่างที่ 3 จะแสดงกลไกการป้องกันเวลากำหนดโปรโตไทป์ให้กับอ็อบเจ็กต์ + +```js +let people = { }; + +let preventObj = { // handler + // ดักจับตอนกำหนดโปรโตไทป์ให้กับอ็อบเจ็กต์เป้าหมาย + setPrototypeOf(targetObj, prototype) { + if(prototype !== people) { // ตรวจสอบว่าได้กำหนดโปรโตไทป์เป็น people หรือไม่ + throw new Error("Prototype must be people object only"); + } + console.log("Set the people prototype"); + return Reflect.setPrototypeOf(targetObj, prototype); + } // สิ้นสุด setPrototypeOf +}// สิ้นสุดประกาศอ็อบเจ้กต์ + +let man = { age: 17 }; +let woman = { age: 20 }; +let proxyMan = new Proxy(man, preventObj); +let proxyWoman = new Proxy(woman, preventObj); + +// กำหนดโปรโตไทป์เป็น people ก็จะทำงานได้ตามปกติ +proxyMan.__proto__ = people; // "Set the people prototype" +proxyWoman.__proto__ = people; // "Set the people prototype" +console.log( man.__proto__ === people ); // true +console.log( woman.__proto__ === people ); // true + +// เมื่อกำหนดโปรโตไทป์ที่ไม่ใช่ people ก็จะเกิด error +let car = { speed: 100 }; // กำหนดให้เป็นโปรโตไทป์ของ man กับ woman +proxyMan.__proto__ = car; // Error: Prototype must be people object only +proxyWoman.__proto__ = car; // Error: Prototype must be people object only +``` + +* ตัวอย่างที่ 4 จะแสดงกลไกป้องกันการกำหนดสมาชิกคนละชนิดในอาร์เรย์ +```js +let checkType = { + set(targetObj, key, value, proxy) { + if( targetObj.length == 0) { // ถ้าเป็นอาร์เรย์ว่าง + return Reflect.set( targetObj, key, value ); + } + let val0 = Reflect.get(targetObj, 0); // เข้าถึงสมาชิกตัวแรกของอาร์เรย์ (อินเด็กซ์ 0) + if( typeof val0 == typeof value ) { // ข้อมูลสมาชิกที่กำหนดเข้ามาเป็นชนิดเดียวกัน + return Reflect.set( targetObj, key, value ); + } else { // ถ้าเป็นคนข้อมูลคนละชนิดกัน ก็จะเกิด error + throw new Error(`Can't add this type: ${typeof value}`); + } + } // สิ้นสุดการประกาศ set +} + +let myArray = [ 0, 1, 2, 3, 4]; +let proxyArray = new Proxy(myArray, checkType); + +// เพิ่มสมาชิกตัวที่ 5 (มีชนิดข้อมูลเป็นตัวเลข) +proxyArray[5] = 5; +console.log(proxyArray); // [ 0, 1, 2, 3, 4, 5 ] +console.log(myArray); // [ 0, 1, 2, 3, 4, 5 ] + +// เพิ่มสมาชิกตัวที่ 6 แต่เป็นสตริง จะเกิด error ขึ้นได้ +proxyArray[6] = "6"; // Error: Can't add this type: string +``` + +* ตัวอย่างที่ 5 จะแสดงวิธีที่ทำให้อ็อบเจ๊กที่เคยสร้างแล้วจะถูก cached เก็บไว้ จะได้ไม่ต้องสร้างซ้ำ ๆ กันหลายรอบ +```js +class Image{ + constructor(imageName){ + this.imageName = imageName; + // สมมติว่าไปโหลดไปมาจากฐานข้อมูล + // ซึ่งอาจเสียเวลานานด้วย + // loadImageFromDB(imageName); + } +} + +let createImage = { // handler + imageMap: new Map(), // เอาไว้เก็บอ็อบเจ็กต์ (cached) + construct(targetObj, thisArugment, newTarget) { // ดักจับเมื่อสร้างอ็อบเจ็กต์เป้าหมาย + imageName = thisArugment[0]; + let map = this.imageMap; + if( map.has(imageName)) { + console.log("Clone an Object from cached"); + // โคลนนิ่งอ็อบเจ๊กที่เก็บไว้ใน imageMap มาใส่อ็อบเจ็กต์เปล่า แล้วรีเทิร์นออกไป + return Object.assign({},map.get(imageName)); + } + // ถ้าไม่มีเก็บไว้ในแม็พ + console.log("Create new Object"); + let newObj = Reflect.construct(targetObj, thisArugment, newTarget); + map.set(imageName, newObj); // เก็บไว้ในแม็พ + return newObj + } // สิ้นสุด construct +} // สิ้นสุดประกาศอ็อบเจ็กต์ + +let proxyImage = new Proxy(Image, createImage); + +// เมื่อสร้างอ็อบเจ็กต์ด้วยการเรียกใช้ new +let catImage1 = new proxyImage("cat"); // Create new Object + +// เมื่อระบุค่าอากิวเมนต์ "cat" ซ้ำอีกครั้ง ก็จะเป็นการโคลนิ่งอ็อบเจ็กต์ที่มีอยู่แล้ว +let catImage2 = new proxyImage("cat"); // Clone an Object from cached +console.log( catImage1 === catImage2) // false +``` + +### ข้อควรระวังเมื่อใช้ trap + +```js +let obj = {a: 100}; +let trapLogic = { + getOwnPropertyDescriptor(targetObj, key) { + console.log(`getOwnPropertyDescriptor: ${key}`); // บรรทัด a + return Reflect.getOwnPropertyDescriptor(targetObj, key); + }, + defineProperty(targetObj, key, descriptor) { // บรรทัด b + console.log(`defineProperty: ${key}`); + return Reflect.defineProperty(targetObj, key, descriptor); + } + /* ,set(targetObj, key, value, proxy) { // บรรทัด c + console.log(`set property: ${key} = ${value}`); + return Reflect.set( targetObj, key, value ); + }*/ +}; +let proxyObj = new Proxy( obj, trapLogic); +proxyObj.a = 100; +/* แสดงผลลัพธ์เป็น +"getOwnPropertyDescriptor: a" +"defineProperty: a" +*/ +/* แต่ถ้าเอาคอมเมนต์ของบรรทัด c ออกไป จะเปลี่ยนไปแสดงผลลัพธ์เป็น +"set property: a = 100" +*/ +``` + +### วิธียกเลิกพร็อกซี่ + +```js +let trapLogic = { + set(target,key,value, proxy) { + console.log(`set property: ${key} = ${value}`); + return true; + } +}; +let {proxy: myProxy, revoke: revokeFunc} = Proxy.revocable({}, trapLogic); // บรรทัด a +myProxy.a = 100; // "set property: a = 100" -- บรรทัด b +revokeFunc(); // หยุดการทำงานของพร็อกซี่ +console.log(myProxy.a); // TypeError -- บรรทัด c +``` + +## ลำดับของพร็อพเพอร์ตี้ + +```js +let parent = { parentKey: 1 }; +let obj = { + __proto__: parent // กำหนดโปรโตไทป์เป็น parent +}; +console.log(obj.parentKey); // 1 +obj.z = 100; +obj.y = 200; +obj [Symbol("c")] = "symbol c"; +obj [Symbol("a")] = "symbol a"; +obj[3.14] = 400; +obj[-10] = 300 +obj[null] = 500; +obj[undefined] = 600; +obj[true] = 700; +obj[{}] = 800; +obj[10] = "foo"; +obj[0] = "bar"; +console.log(Reflect.ownKeys(obj)); +// [ '0', '10', 'z', 'y', '3.14', '-10', 'null', 'undefined', 'true', '[object Object]', Symbol(c), Symbol(a) ] +console.log(Object.getOwnPropertyNames(obj)); +// [ '0', '10', 'z', 'y', '3.14', '-10', 'null', 'undefined', 'true', '[object Object]' ] +console.log(Object.getOwnPropertySymbols(obj)); // [ Symbol(c), Symbol(a) ] +``` + diff --git a/examples_book/Chapter18.md b/examples_book/Chapter18.md new file mode 100644 index 0000000..32d572c --- /dev/null +++ b/examples_book/Chapter18.md @@ -0,0 +1,890 @@ +# โค้ดบทที่ 18 พรอมิส + +## พรอมิสคืออะไร + +```js +let promise = new Promise( function (resolve, reject) { + console.log(typeof resolve); // บรรทัด a + console.log(typeof reject); // บรรทัด b + console.log("Promise"); // บรรทัด c + } ); +console.log("Last statement"); // บรรทัด d +/* แสดงผลลัพธ์เป็น +"function" +"function" +"Promise" +"Last statement" */ +``` + +## เมธอด then() + +```js +function asynCode(resolve, reject) { + setTimeout( function() { // บรรทัด a + /* ซอร์สโค้ดอื่น ๆ */ + //... + resolve("success"); // บรรทัด b -- แจ้งว่าพรอมิสทำงานสำเร็จ + }, 1000); // ดีเลย์ 1 วินาที +}; +let promise = new Promise(asynCode); // บรรทัด c +promise.then( + function (value) { // บรรทัด d -- มอนิเตอร์พรอมิส กรณีที่มันทำงานสำเร็จ + console.log("Promise:", value); + } +); +console.log("Last statement"); // บรรทัด e +/* แสดงผลลัพธ์เป็น +"Last statement" +"Promise: success" */ +``` + +```js +let asynCode = function (resolve, reject) { + console.log("Start a job"); + let condition; + /* ซอร์สโค้ดอะซิงโครนัส */ + //... + if (condition) { + resolve("success"); // บรรทัด a -- แจ้งว่าพรอมิสทำงานสำเร็จ + } else { + reject("failed"); // บรรทัด b -- แจ้งว่าพรอมิสทำงานไม่สำเร็จ + } +}; +let promise = new Promise(asynCode); +promise.then( + function (value) { // บรรทัด c -- มอนิเตอร์พรอมิส กรณีที่มันทำงานสำเร็จ + console.log("Promise:",value); + }, function (reason) { // บรรทัด d -- มอนิเตอร์พรอมิส กรณีที่มันทำงานไม่สำเร็จ + console.log("Promise:", reason); + } +); +console.log("Last statement"); +// แสดงผลลัพธ์เป็น +// "Start a job" +// "Last statement" +// "Promise: success" หรือ "Promise: failed" +``` + +## เมธอด cath() + +```js +let promise = new Promise( function (resolve, reject) { + // เมื่อทำงานไม่สำเร็จ + console.log("Start a job"); + reject("failed"); // บรรทัด a +}); +promise.catch( function (reason){ // บรรทัด b + console.log("Promise:", reason); +}); +console.log("Last statement"); +/* แสดงผลลัพธ์เป็น +"Start a job" +"Last statement" +"Promise: failed" */ +``` + +```js +let promise = new Promise( function(resolve, reject) { + console.log("Start a job"); + throw new Error("Error in promise"); // บรรทัด a +}); +promise.catch( function(error) { // บรรทัด b + console.log(error.message); +} ); +console.log("Last statement"); +/* แสดงผลลัพธ์เป็น +"Start a job" +"Last statement" +"Error in promise" */ +``` + +```js +let promise = new Promise( function(resolve, reject) { + try{ + console.log("Start a job"); + throw new Error("Error in promise"); + } catch(error) { + reject(error); + } +}); +promise.catch( function(error) { + console.log(error.message); +} ); +console.log("Last statement"); +/* แสดงผลลัพธ์เป็น +"Start a job" +"Last statement" +"Error in promise" */ +``` + +## ตำแหน่งการวาง then() กับ cath() + +```js +let promise = new Promise( function(resolve, reject ) { + // resolve("success"); + // reject("failed"); +}); +console.log("Line 1"); // บรรทัดที่ 1 +//... +console.log("Line 2000"); // บรรทัดที่ 2000 +promise.catch( function(reason) { // จะวาง catch() ไว้หลังจากสร้างพรอมิสตำแหน่งใดก็ได้ + console.log("Promise:", reason); +} ); +//... +console.log("Line 5000"); // บรรทัดที่ 5000 +promise.then( function(value) { // จะวาง then() ไว้หลังจากสร้างพรอมิสตำแหน่งใดก็ได้ + console.log("Promise:", value); +} ); +``` + +```js +let promise = new Promise( function(resolve, reject) { + console.log("Start a job"); + resolve("success"); +}); +promise.then( function(value) { + console.log("Outer Promise:", value); + promise.then( function (value) { + console.log("Inner Promise:", value); + }); +} ); +console.log("Last statement"); +/* แสดงผลลัพธ์เป็น +"Start a job" +"Last statement" +"Outer Promise: success" +"Inner Promise: success" */ +``` + +```js +let promise = new Promise( function(resolve, reject) { + console.log("Start a job"); + reject("failed"); +}); +promise.catch( function(reason) { + console.log("Outer promise:", reason); + promise.catch( function (reason) { + console.log("Inner promise:", reason); + }); +} ); +console.log("Last statement"); +/* แสดงผลลัพธ์เป็น +"Start a job" +"Last statement" +"Outer promise: failed" +"Inner promise: failed" */ +``` + +## เรียก then() แบบต่อเนื่อง + +```js +let p1 = new Promise( function(resolve, reject) { + resolve("success"); // บรรทัด a +}); +let p2 = p1.then( function(value) { + console.log("Promise:", value); // บรรทัด b +}); +p2.then( function() { // บรรทัด c + console.log("Finish"); +}); +/* แสดงผลลัพธ์เป็น +"Promise: success" +"Finish" */ +``` + +```js +let promise = new Promise(function(resolve, reject) { + resolve("success"); +}); +promise.then(function(value) { // then() ตัวแรก + console.log("Promise:", value); +}).then(function() { // then() ตัวที่สอง ฟังก์ชันคอลแบ็คจะไม่มีพารามิเตอร์ + console.log("then1: finish"); +}). then(function() { // then() ตัวที่สาม ฟังก์ชันคอลแบ็คจะไม่มีพารามิเตอร์ + console.log("then2: finish"); +}); +/* แสดงผลลัพธ์เป็น +"Promise: success" +"then1: finish" +"then2: finish" */ +``` + +## ดักจับ error แบบต่อเนื่อง + +```js +let promise = new Promise (function(resolve, reject) { + throw new Error("error1"); // บรรทัด a +}); +promise.catch( function(error) { // catch() ตัวแรก + console.log("catch1:", error.message); + throw new Error("error2"); +}).catch( function(error) { // catch() ตัวที่สอง + console.log("catch2:", error.message); + throw new Error("error3"); +}).catch( function(error) { // catch() ตัวที่สาม + console.log("catch3:", error.message); +}); +/* แสดงผลลัพธ์ +"catch1: error1 +"catch2: error2" +"catch3: error3" */ +``` + +```js +let promise = new Promise(function(resolve, reject) { + throw new Error("Error"); +}); +promise.catch( function(error) { // ฟังก์ชันชั่นคอลแบ็คของ catch() ตัวแรก จะทำงาน + console.log(error.message); +}).catch( function() { // ฟังก์ชันชั่นคอลแบ็คของ catch() ตัวที่สอง ไม่ถูกเรียกให้ทำงาน + console.log("Last error"); +}); +// แสดงผลลัพธ์ +// "Error" +``` + +```js +let promise = new Promise(function(resolve, reject) { + throw new Error("Error"); +}); +promise.catch( function(error) { + console.log(error.message); +}).then( function(value) { + console.log("Last then"); +}); +/* แสดงผลลัพธ์ +"Error" +"Last then" */ +``` + +## ประโยค return + +```js +let promise = new Promise(function(resolve, reject) { + resolve("success"); +}); +promise.then(function(value) { // then() ตัวแรก + console.log("then1:", value); + return 2; // ส่ง 2 ไปให้กับฟังก์ชันคอลแบ็คของ then() ตัวถัดไป +}).then(function(value) { // ประกาศฟังก์ชันคอลแบ็คให้มีพารามิเตอร์ + console.log("then2:", value); + return 3; // ส่ง 3 ไปให้กับฟังก์ชันคอลแบ็คของ then() ตัวถัดไป +}). then(function(value) { // ประกาศฟังก์ชันคอลแบ็คให้มีพารามิเตอร์ + console.log("then3:", value); +}); +/* แสดงผลลัพธ์เป็น +"then1: success" +"then2: 2" +"then3: 3" */ +``` + +## การใช้ then() ร่วมกับ catch() + + +```js +let promise = new Promise( function(resolve, reject) { // บรรทัด a + reject("Promise:"); +}); +promise.then( function(value) { + console.log(value, "success"); // ไม่ถูกเรียกให้ทำงาน +}).catch( function(reason) { + console.log(reason, "failed"); // ถูกเรียกให้ทำงาน +}); +// แสดงผลลัพธ์ +// "Promise: failed" +``` + +```js +let promise = new Promise( function(resolve, reject) { + resolve("Promise:"); +}); +promise.catch( function(value) { + console.log(value, "failed"); // ไม่ถูกเรียกให้ทำงาน +}).then( function(reason) { + console.log(reason, "success"); // ถูกเรียกให้ทำงาน +}); +// แสดงผลลัพธ์ +// "Promise: success" +``` + +```js +let promise = new Promise(function(resolve, reject) { + throw new Error("Error"); +}); +promise.catch(function(error) { + console.log(error.message); + return 1; // สามารถส่ง 1 ไปให้ฟังก์ชันคอลแบ็คของ then() ตัวถัดไปได้ +}).then(function(value) { + console.log("then:", value); + return 2; // ไม่สามารถส่ง 2 ไปให้ฟังก์ชันคอลแบ็คของ catch() ตัวถัดไปได้ +}).catch(function(error) { + console.log("catch:", error); // ไม่ถูกเรียกให้ทำงาน +}); +/* แสดงผลลัพธ์ +"Error" +"then: 1" */ +``` + +```js +let promise = new Promise(function(resolve, reject) { + throw new Error("Error"); +}); +promise.catch( function(error) { + console.log(error.message); + return 1; +}).then( function(value) { + console.log("then:", value); + throw 2; +}).catch( function(value) { + console.log("catch:", value); // ถูกเรียกให้ทำงานได้ +}); +/* แสดงผลลัพธ์ +"Error" +"then: 1" +"catch: 2" */ +``` + +## รีเทิร์นพรอมิส + +```js +let p1 = new Promise( function(resolve, reject) { + resolve("promise1"); +}); +let p2 = new Promise( function(resolve, reject) { + resolve("promise2"); // บรรทัด a +}); +let p3 = p1.then( function(value) { + console.log("First then:", value); + return p2; // บรรทัด b +}); +p3.then( function(value) { // บรรทัด c + console.log("Second then:", value); +}); +/* แสดงผลลัพธ์ +"First then: promise1" +"Second then: promise2" */ +``` + +```js +let p1 = new Promise( function(resolve, reject) { + resolve("promise1"); +}); +let p2 = new Promise( function(resolve, reject) { + resolve("promise2"); +}); +p1.then( function(value) { + console.log("First then:", value); + return p2; +}).then( function(value) { + console.log("Second then:", value); +}); +/* แสดงผลลัพธ์ +"First then: promise1" +"Second then: promise2" */ +``` + +```js +let p1 = new Promise( function(resolve, reject) { + resolve("success"); +}); +let p2 = new Promise( function(resolve, reject) { + reject("failed"); +}); +p1.then( function(value) { + console.log("then:",value); + return p2; +}).catch( function(value) { // บรรทัด a + console.log("catch:", value); +}); +/* แสดงผลลัพธ์ +"then: success" +"catch: failed" */ +``` + +```js +let p1 = new Promise( function(resolve, reject) { + console.log("Promise1"); + resolve("Success1"); +}); +p1.then( function(value) { + console.log("p1.then:", value); + let p2 = new Promise( function(resolve, reject) { // บรรทัด a + console.log("Promise2"); + resolve("Success2"); + }); + return p2; +}).then(function(value) { + console.log("p2.then:", value); +}); +/* แสดงผลลัพธ์ +"Promise1" +"p1.then: Success1" +"Promise2" +"p2.then: Success2" */ +``` + +## การสร้างพรอมิสที่มีสถานะ settled + +```js +let promise = Promise.resolve("Promise is fulfilled"); +/*ไม่ต้องเสียเวลาเขียนแบบนี้ +let promise = new Promise(function(resolve, reject) { + resolve("Promise is fulfilled"); +}); */ +promise.then(function(value) { + console.log(value); +}); +// แสดงผลลัพธ์เป็น +// "Promise is fulfilled" +``` + +```js +let promise = Promise.reject("Promise is rejected"); +/*ไม่ต้องเสียเวลาเขียนแบบนี้ +let promise = new Promise(function(resolve, reject) { + reject("Promise is rejected"); +}); */ +promise.catch(function(reason) { + console.log(reason); +}); +// แสดงผลลัพธ์เป็น +// "Promise is rejected" +``` + +```js +let thenable = { + then(resolve, reject) { + resolve("fulfilled"); + } +}; +``` + +```js +let thenable = { + then (resolve, reject) { + console.log("thenable"); + resolve("fulfilled"); // บรรทัด a -- จะส่งค่า "fulfilled" ไปให้ promise.then() + } +}; +let promise = Promise.resolve(thenable); +promise.then(function(value) { + console.log("then:", value); // บรรทัด b +}); +/* แสดงผลลัพธ์เป็น +"thenable" +"then: fulfilled" */ +``` + +## เมธอด finally() + +```js +Promise.resolve("fulfilled") // บรรทัด a +.then( result => console.log("then:", result) ) // บรรทัด b +.catch( result => console.log("catch:", result) ) +.finally( () => console.log("finally") ); // บรรทัด c +/* แสดงผลลัพธ์ +"then: fulfilled" +"finally" */ +``` + +```js +Promise.reject("rejected") // บรรทัด a +.then( result => console.log("then:", result) ) +.catch( result => console.log("catch:", result) ) // บรรทัด b +.finally( () => console.log("finally") ); // บรรทัด c +/* แสดงผลลัพธ์ +"catch: rejected" +"finally" */ +``` + +```js +Promise.resolve("fulfilled") // บรรทัด a +.finally( () => console.log("finally") ) // บรรทัด b +.then( result => console.log("then:", result) ) // บรรทัด c +.catch( result => console.log("catch:", result) ); +/* +"finally" +"then: fulfilled" */ +``` + +```js +Promise.reject("rejected") // บรรทัด a +.finally( () => console.log("finally") ) // บรรทัด b +.then( result => console.log("then:", result) ) +.catch( result => console.log("catch:", result) ); // บรรทัด c +/* +"finally" +"catch: rejected" */ +``` + +```js +Promise.reject("rejected") // บรรทัด a +.finally( () => { + throw "error from finally"; // บรรทัด b +} ) +.catch( error => console.log(error) ); // บรรทัด c +// แสดงข้อความ +// "error from finally" +``` + +```js +Promise.resolve("fulfilled") +.finally( ( ) => { return 1 } ) // เขียนย่อเป็น .finally( ( ) => 1 ) ก็ได้ +.then( value => console.log(value)); +// แสดงผลลัพธ์ +// "fulfilled" +``` + +```js +Promise.resolve("fulfilled") +.finally( () => console.log("finally") ) // มีแค่ finally() อย่างเดียว +// แสดงผลลัพธ์ +// "finally" +``` + +## Promise.all() + +```js +let p1 = Promise.resolve("Promise1"); +let p2 = Promise.resolve("Promise2"); +let p3 = Promise.resolve("Promise3"); +let p4 = Promise.all([p1, p2, p3]); // มอนิเตอร์ p1, p2 และ p3 ที่มีสถานะเป็น fulfilled ทุกตัว +p4.then(function(value) { + console.log(value); +}); +// แสดงผลลัพธ์เป็น +// [ 'Promise1', 'Promise2', 'Promise3' ] +``` + +```js +let p1 = new Promise(function(resolve, reject) { + resolve("Promise1:"); +}); +let p2 = new Promise(function(resolve, reject) { + setTimeout( function(){ + reject("Promise2:"); + }, 1000); // ดีเลย์ไป 1 วินาที +}); +let p3 = Promise.reject("Promise3:"); +let p4 = Promise.all([p1, p2, p3]); // มอนิเตอร์เฉพาะ p3 เพราะมีสถานะเป็น rejected ก่อนตัวอื่น +p4.then( + function(value) { console.log(value, "success"); } + ,function(value) { console.log(value, "failed"); } +); +// แสดงผลลัพธ์เป็น +// "Promise3: failed" +``` + +## Promise.race() + +```js +let p1 = new Promise( function(resolve, reject) { + setTimeout( function() { + resolve("Promise1:"); + }, 500); // ดีเลย์ 0.5 วินาที +}); +let p2 = new Promise( function(resolve, reject) { + setTimeout( function() { + resolve("Promise2:"); + }, 1000); // ดีเลย์ 1 วินาที +}); +let p3 = Promise.reject("Promise3:"); +let p4 = Promise.race([p1, p2, p3]); // มอนิเตอร์เฉพาะพรอมิส p3 เพราะมีสถานะเป็น settled ก่อนตัวอื่น +p4.then( + function(value) { console.log(value, "success"); } + ,function(value) { console.log(value, "failed"); } +); +// แสดงผลลัพธ์เป็น +// "Promise3: failed" +``` + +## Promise.allSettled() + +```js +let p1 = Promise.resolve(123); +let p2 = Promise.reject(456); +Promise.allSettled( [p1, p2] ); +``` + +```js +let p1 = Promise.resolve(123); +let p2 = Promise.reject(456); +Promise.allSettled( [p1, p2] ) // allSettled() รีเทิร์นพรอมิสที่เป็น fulfilled +.then( arr => console.log(arr) ); // ค่า fulfillment และ rejection ที่ถูกส่งมาเป็นอาร์เรย์ + +/* แสดงผลลัพธ์ +[ + { status: 'fulfilled', value: 123 }, + { status: 'rejected', reason: 456 } +] */ +``` + +```js +let p1 = Promise.resolve(123); +let p2 = new Promise( resolve => { + throw "Error" // บรรทัด a +}); +Promise.allSettled( [p1, p2] ) // บรรทัด b +.then( arr => console.log(arr) ) // บรรทัด c +.catch( err => console.log(err) ); // บรรทัด d -- ไม่ถูกเรียกให้ทำงาน + +/* แสดงผลลัพธ์ +[ + { status: 'fulfilled', value: 123 }, + { status: 'rejected', reason: 'Error' } +] */ +``` + +## Promise.any() กับ AggregateError + +```js +let p1 = Promise.resolve(123); +let p2 = Promise.resolve(456); +let p3 = Promise.reject(789); +Promise.any([p1, p2, p3]); + +Promise.any([p1, p2, p3]) // p1 เป็น fulfilled ก่อน p2 +.then(value=> console.log(value) ); // จะส่งค่า 123 ของ p1 มาให้ then() +// แสดงผลลัพธ์ +// 123 +``` + +```js +let p1 = new Promise( (resolve) => setTimeout(resolve, 3000) ); // หลับไป 3 วินาที +let p2 = Promise.resolve(456); // ทำงานเสร็จก่อน p1 +let p3 = Promise.reject(789); +Promise.any([p1, p2, p3]) // p2 เป็น fulfilled ก่อนตัวอื่น +.then(value=> console.log(value) ); // จะส่งค่า 456 ของ p2 มาให้ then() +// แสดงผลลัพธ์ +// 456 +``` + +```js +let p1 = Promise.reject("Error 1"); +let p2 = Promise.reject("Error 2"); +let p3 = Promise.reject("Error 3"); +Promise.any([p1, p2, p3]) +.catch(aggregateError => console.log(aggregateError)); +/* แสดงผลลัพธ์ +[AggregateError: All promises were rejected] { + [errors]: [ 'Error 1', 'Error 2', 'Error 3' ] +} */ + +Promise.any([p1, p2, p3]) +.catch(aggregateError => console.log(aggregateError.errors)); // เข้าถึงพร็อพเพอร์ตี้ errors +// แสดงผลลัพธ์ +// [ 'Error 1', 'Error 2', 'Error 3' ] +``` + +## การประยุกต์ใช้งานพรอมิส + +* ตัวอย่างที่ 1 +หมายเหตุ ตัวอย่างนี้ต้องรันไฟล์ ajax.mjs ที่เครื่องตัวเองขึ้นมาด้วย เพื่อรองรับ request ที่เข้ามา +แต่ถ้าไม่สะดวก็ให้เปลี่ยน url จาก http://127.0.01:8001/messagepromise +ไปเป็น https://patanasongsivilai.com/example/ajax.php + +และตัวอย่างข้างล่างจะแสดงผลลัพธ์หน้าคอนโซลของเว็บเบราเซอร์ด้วย +{"echo": "test promise"} + +```html + + + + + +

+

+ + + +``` + +* ตัวอย่างที่ 2 + +* [ไฟล์ json.php ประกอบเนื้อหา Fetch API](json.php) + +```js +fetch("https://patanasongsivilai.com/example/json.php") // บรรทัด a +.then( function (res) { // บรรทัด b + return res.text(); // บรรทัด c + } +) +.then( function (txt) { // บรรทัด d + console.log(txt); // บรรทัด e + } +); +// แสดงผลลัพธ์ +// {"name":"Somchai","age":30,"city":"Bangkok"} +``` + +หมายเหตุ ตัวอย่าง html ถัดไป ต้องรันไฟล์ ajax.mjs ที่เครื่องตัวเองขึ้นมาด้วย เพื่อรองรับ request ที่เข้ามา +แต่ถ้าไม่สะดวก็ให้เปลี่ยน url จาก http://127.0.01:8001/message?msg=test promise +ไปเป็น https://patanasongsivilai.com/example/ajax.php?msg=test promise + + +```html + + + + + +

+

+ + + +``` + +* ตัวอย่างที่ 3 (ต้องรันบน Node.js เท่านั้น) + +* [ไฟล์ json.txt](Chapter18/json.txt) +ตัวอย่างไฟล์ json.txt อยู่ที่ https://raw.githubusercontent.com/adminho/javascript/master/examples_book/Chapter18/json.txt + +```js +function readJSONFile(fileName){ + function asynCode(resolve,reject) { + let fs = require("fs"); // ใช้อ่านไฟล์ + fs.readFile(fileName, function(err, text) { // บรรทัด a + if (err) { + reject(err.message); // บรรทัด b + } else { + let obj = JSON.parse(text); + let json = JSON.stringify(obj, null, 2); + resolve(json); // บรรทัด c + } // สิ้นสุดประโยค if + }); + } // สิ้นสุดการประกาศฟังก์ชัน asynCode + return new Promise(asynCode); // บรรทัด d +} // สิ้นสุดการประกาศฟังก์ชัน readJSONFile +let reader = readJSONFile("json.txt"); // อ่านไฟล์ json.text +console.log("Read a file"); +function handle(value) { console.log(value); } +function errorHandle(errMsg) { console.log("Error message:", errMsg); } +reader.then(handle, errorHandle) // บรรทัด e +``` + +* ตัวอย่างที่ 4 + +```js +let iterator = generator(); +iterator.next(); // เริ่มทำงาน +function *generator() { + for(let i=0; i<5; i++) { + let result = yield runService(i); // บรรทัด a + console.log("Message:" , result); + } +} +function runService(data) { + function aysnCode(resolve, reject) { + console.log("to do something:", data); // บรรทัด b + // การทำงานแบบอะซิงโครนัสอื่น ๆ + // ... + setTimeout(function() { + resolve(data); // บรรทัด c + } + ,1000); // ดีเลย์ 1 วินาที + } + let promise = new Promise(aysnCode); + promise.then(function(value) { // บรรทัด d + let result = `service ${value} is success`; + iterator.next(result); + }); +} + +/* แสดงผลลัพธ์ +to do something: 0 +Message: service 0 is success +to do something: 1 +Message: service 1 is success +to do something: 2 +Message: service 2 is success +to do something: 3 +Message: service 3 is success +to do something: 4 +Message: service 4 is success */ +``` + +## สไตล์การเขียนด้วยฟังก์ชันคอลแบ็ค + +```js +Promise.resolve(123) +.then( function(value) { // บรรทัด a -- ฟังก์ชันคอลแบ็คแบบไร้ชื่อ ขึ้นต้นด้วย function + console.log(value); + } +); +// แสดงผลลัพธ์ +// 123 +Promise.reject("Error") +.catch(function (value) { + console.log(value); // บรรทัด b -- ฟังก์ชันคอลแบ็คแบบไร้ชื่อ ขึ้นต้นด้วย function + } +); +// แสดงผลลัพธ์ +// "Error" +``` + +```js +Promise.resolve(123) +.then( value => console.log(value)); // บรรทัด a -- ฟังก์ชันคอลแบ็คเป็นฟังก์ชันลูกศร +// แสดงผลลัพธ์ +// 123 +Promise.reject("Error") +.catch( value => console.log(value)); // บรรทัด b - ฟังก์ชันคอลแบ็คเป็นฟังก์ชันลูกศร +// แสดงผลลัพธ์ +// "Error" +``` + +```js +let promise = new Promise(function(resolve, reject) { + console.log("Line a ...to do something") // บรรทัด a +}); +console.log("I done already"); // บรรทัดนี้ถูกเรียกให้ทำงาน +promise.then( value => console.log(value)) // บรรทัด b +.catch( value => console.log(value)); // บรรทัด c +/* แสดงผลลัพธ์เป็น +"Line a ...to do something" +"I done already" */ +``` \ No newline at end of file diff --git a/examples_book/Chapter18/json.txt b/examples_book/Chapter18/json.txt new file mode 100644 index 0000000..00459a6 --- /dev/null +++ b/examples_book/Chapter18/json.txt @@ -0,0 +1,4 @@ +{"Font":[ + {"color":"red", "lang":"thai"}, + {"color":"blue", "lang":"eng"} +]} diff --git a/examples_book/Chapter19.md b/examples_book/Chapter19.md new file mode 100644 index 0000000..b7dafa3 --- /dev/null +++ b/examples_book/Chapter19.md @@ -0,0 +1,627 @@ +# โค้ดบทที่ 19 การใช้งาน async กับ await + +## ประกาศฟังก์ชันด้วย async + +* กรณีที่ฟังก์ชันแบบ async รีเทิร์นพรอมิสที่เป็น fulfilled + +```js +async function asyncFunc() { + return 123; // บรรทัดที่ 2 +} +``` + +* เสมือนเขียนดังนี้ +```js +function asyncFunc() { // เสมือนเขียนฟังก์ชันปกติธรรมดา + return new Promise( resolve => { // จะรีเทิร์นพรอมิส + resolve(123); // จากประโยค return 123 เสมือนเรียก resolve(123) + }); +} +``` + +```js +async function asyncFunc() { + return 123; // 123 คือค่าที่ส่งไปให้ฟังก์ชันคอลแบ็คของ then() +} +asyncFunc() // บรรทัด a +.then(x => console.log(x)); // 123 +``` + +```js +async function asyncFunc() { + console.log("aysnc function"); + // บรรทัด 3 - จะเสมือนมีประโยค return undefined; +} +``` + +```js +function asyncFunc() { // เสมือนเขียนฟังก์ชันปกติธรรมดา + return new Promise( resolve => { // จะรีเทิร์นพรอมิส + console.log("aysnc function"); // โค้ดถูกห่ออยู่ในพรอมิสที่สร้างขึ้นมา + resolve(undefined); // จะเสมือนมี resolve(undefined) ต่อท้าย + }); +} +``` + +```js +async function asyncFunc() { + console.log("aysnc function"); + // บรรทัด 3 - จะเสมือนมีประโยค return undefined; +} +asyncFunc() // บรรทัด a -- เรียกฟังก์ชันให้ทำงาน +.then(x => console.log(x)); // undefined +/* แสดงผลลัพธ์ +"aysnc function" +undefined */ +``` + +* กรณีที่ฟังก์ชันแบบ async รีเทิร์นพรอมิสที่เป็น rejected + +```js +async function asyncFunc() { + throw new Error("Problem!"); // error ที่ส่งไปให้ฟังก์ชันคอลแบ็คของ catch +} +``` + +* เสมือนเขียนดังนี้ + +```js +function asyncFunc() { + return new Promise( (resolve, reject) => { + reject( new Error("Problem!") ) ; + }); +} +``` + +```js +async function asyncFunc() { + throw new Error("Problem!"); // error ที่ส่งไปให้ฟังก์ชันคอลแบ็คของ catch +} +asyncFunc() +.catch(err => console.log(err)); // Error: Problem! +``` + +```js +async function asyncFunc() { + console.log("aysnc function()"); // บรรทัด a -- ซิงโครนัส + return 123; // บรรทัด b +} +console.log("Start"); // บรรทัด c -- ซิงโครนัส +asyncFunc(). // บรรทัด d +then(x => console.log(`Resolved: ${x}`)); // บรรทัด e - อะซิงโครนัส +console.log("End"); // บรรทัด f - ซิงโครนัส +/* แสดงผลลัพธ์ +"Start" +"aysnc function()" +"End" +"Resolved: 123" */ +``` + +* เมื่อฟังก์ชันแบบ async รีเทิร์นพรอมิสออกมาโดยตรง + +```js +async function asyncFunc() { + return Promise.resolve(123); // บรรทัด a -- ส่งพรอมิสที่เป็น fulfilled ออกมา +} +asyncFunc() // บรรทัด b +.then(x => console.log(x)) // 123 +``` + +```js +async function asyncFunc() { + return Promise.reject(new Error('Problem!')); // บรรทัด a ส่งพรอมิสที่เป็น rejected ออกมา +} +asyncFunc() // บรรทัด b +.catch(err => console.error(err)); // Error: Problem! +``` + +* สรุป การรีเทิร์นของฟังก์ชันแบบ asyn ก็ใช้หลักเกณฑ์เดียวกันกับการ return ของพรอมิส + +```js +async function asyncFunc() { + return otherAsyncFunc(); +} +``` + +```js +async function asyncFunc() { + return await otherAsyncFunc(); +} +``` + +## การใช้งาน await + +```js +async function asyncFunc() { + let result = await new Promise(resolve => resolve(123)); // บรรทัด a + console.log(result); // บรรทัด b +} +asyncFunc(); // 123 +``` + +```js +async function asyncFunc() { + return new Promise( resolve => resolve(123) ) + .then( result => console.log(result) ); +} +asyncFunc(); // 123 +``` + +```js +async function asyncFunc() { + return await new Promise(resolve => resolve(123)); // return นำหน้า await +} +``` + +```js +async function asyncFunc() { + // เสมือนเขียน + let result = await new Promise(resolve => resolve(123)); // result = 123 + return result; // รีเทิร์นพรอมิส พร้อมส่งค่า result = 123 ออกไป +} + +asyncFunc() +.then( value => console.log(value)) +// แสดงผลลัพธ์ +// 123 +``` + +```js +async function asyncFunc() { + try { + let result = await new Promise((resolve, reject) => reject("Error!")); // บรรทัด a + } catch (err) { + console.log(err); // บรรทัด b + } +} +asyncFunc(); // "Error!" +``` + +```js +async function asyncFunc() { + return new Promise((resolve, reject) => reject("Error!")) + .catch(err => console.log(err)); +} +asyncFunc(); // "Error!" +``` + +### ขอฝากเตือน + +```js +async function asyncFunc() { + try { + let result = await new Promise((resolve, reject) => { }); // บรรทัด a + console.log(result); // บรรทัด b + } catch (err) { + console.log(err); // บรรทัด c + } +} +asyncFunc(); +``` + +### ข้อควรระวังในการใช้ await + +```js +function myFunc() { // ไม่มี async นำหน้า + let result = await Promise.resolve("Success!"); // SyntaxError +} +``` + +```run.module +let result = await Promise.resolve("Success!"); +console.log(result); // "Success!" +``` + +```run.module +try { + await Promise.reject("Error!"); +} catch (error) { + console.log(error); // "Error!" +} +``` + +```run.module +let result = await 123; +console.log(result) // 123 +``` + +```js +(async () => { +let result = await 123; +console.log(result) // 123 +})(); +``` + +```js +async function asyncFunc() { + function innerFunc() { + return await Promise.resolve("Success!"); // await อยู่ใต้ innerFunc() ไม่ได้ + } + innerFunc() + .then(value => console.log(value)) +} +asyncFunc(); +``` + +```js +async function asyncFunc() { + async function innerFunc() { + return await Promise.resolve("Success!"); // await สามารถอยู่ใต้ innerFunc() ได้ + } + innerFunc() + .then(value => console.log(value)) +} +asyncFunc(); // "Success!" +``` + +```js +async function foo() { +} +async function foo() { // ประกาศชื่อ foo ซ้ำกันไม่ได้ +} +``` + +หมายเหตุ otherAsyncFunc(ms) ถูกนำไปใช้ในหลายๆ ตัวอย่าง + +```js +async function otherAsyncFunc(ms) { + return new Promise (resolve => + setTimeout( () => { + console.log("call otherAsyncFunc"); // บรรทัด a + resolve("Time out"); // บรรทัด b + } + , 1000) // กำหนดระยะเวลาในการ timeout ไป 1000 ms หรือ 1 วินาที + ); +} +``` + +หมายเหตุ ถ้าจะรันโค้ดนี้ให้ก็อปปี้การประกาศ otherAsyncFunc() มาด้วย + +```js +async function asyncFunc() { + console.log("Start"); // บรรทัด a + otherAsyncFunc(); // บรรทัด b + console.log("End"); // บรรทัด c +} +asyncFunc(); +/* แสดงผลลัพธ์ +"Start" +"End" +"call otherAsyncFunc" */ +``` + +หมายเหตุ ถ้าจะรันโค้ดนี้ให้ก็อปปี้การประกาศ otherAsyncFunc() มาด้วย + +```js +async function asyncFunc() { + console.log("Start"); // บรรทัด a + await otherAsyncFunc(); // บรรทัด b + console.log("End"); // บรรทัด c +} +asyncFunc(); +/* แสดงผลลัพธ์ +"Start" +"call otherAsyncFunc" +"End" */ +``` + +หมายเหตุ ถ้าจะรันโค้ดนี้ให้ก็อปปี้การประกาศ otherAsyncFunc() มาด้วย + +```js +async function asyncFunc() { + console.log("Start"); // บรรทัด a + let result = await otherAsyncFunc(); // บรรทัด a + console.log(result); // บรรทัด b +} +asyncFunc(); +/* แสดงผลลัพธ์ +"Start" +"call otherAsyncFunc" +"Time out" */ +``` + +* การใช้ awiat แบบเรียงต่อเนื่องกัน + + +หมายเหตุ otherAsyncFunc(ms) ถูกนำไปใช้ในหลายๆ ตัวอย่าง + +```js +async function otherAsyncFunc(ms) { // ส่งค่าอากิวเมนต์เข้ามา + return new Promise ( + resolve => setTimeout( + () => resolve(`Time out: ${ms} ms`, ms ) + ,ms ) // กำหนด timeout หน่วยเป็นมิลลิวินาที (millisecond) + ); +} +``` + +หมายเหตุ ถ้าจะรันโค้ดนี้ให้ก็อปปี้การประกาศ otherAsyncFunc() มาด้วย + +```js +async function asyncFunc() { + let result1 = await otherAsyncFunc(1000); // บรรทัด a -- รอพรอมมิสทำงานก่อน + console.log(result1); + let result2 = await otherAsyncFunc(2000); // บรรทัด b -- รอพรอมมิสทำงานก่อน + console.log(result2); +} +asyncFunc(); // บรรทัด c +/* แสดงผลลัพธ์ +Time out: 1000 ms +Time out: 2000 ms */ +``` + +หมายเหตุ ถ้าจะรันโค้ดนี้ให้ก็อปปี้การประกาศ otherAsyncFunc() มาด้วย + +```js +function asyncFunc() { + return otherAsyncFunc(1000) // ทำงานก่อน + .then(result1 => { + console.log(result1); + return otherAsyncFunc(2000); // ทำงานทีหลัง + }) + .then(result2 => { + console.log(result2); + }); +} +asyncFunc(); // เรียกให้ทำงาน +/* แสดงผลลัพธ์ +Time out: 1000 ms +Time out: 2000 ms */ +``` + +* ประยุกต์ใช้งาน await ร่วมกับ Promise.all() เพื่อให้ฟังก์ชันทำงานคู่ขนาน + +หมายเหตุ ถ้าจะรันโค้ดนี้ให้ก็อปปี้การประกาศ otherAsyncFunc() มาด้วย +```js +async function asyncFunc() { + let [result1, result2] = await Promise.all([ + otherAsyncFunc(1000), // บรรทัด a -- ทำงานคู่ขนานกัน + otherAsyncFunc(2000), // บรรทัด b -- ทำงานคู่ขนานกัน + ]); + console.log(`${result1}, ${result2}`); // บรรทัด c +} +asyncFunc(); // เรียกให้ทำงาน +// Time out: 1000 ms, Time out: 2000 ms +``` + +หมายเหตุ ถ้าจะรันโค้ดนี้ให้ก็อปปี้การประกาศ otherAsyncFunc() มาด้วย + +```js +function asyncFunc() { + return Promise.all([ + otherAsyncFunc(1000), + otherAsyncFunc(2000), + ]) + .then( ([result1, result2]) => { + console.log(`${result1}, ${result2}`); + }); +} +asyncFunc(); // เรียกให้ทำงาน +// Time out: 1000 ms, Time out: 2000 ms +``` + +* ตัวอย่างเปลี่ยนจากการเรียกเมธอด then() ต่อเนื่อง มาเป็น await จะสะดวกกว่า +* [ไฟล์ json.php](json.php) + +```js +fetch("https://patanasongsivilai.com/example/json.php") // บรรทัด a +.then( res => res.text()) // บรรทัด b +.then( txt => console.log(txt) ); // บรรทัด c +console.log("Hello"); // บรรทัด d +// แสดงผลลัพธ์ +// "Hello" +// {"name":"Somchai","age":30,"city":"Bangkok"} +``` + +```run.module +let res = await fetch("https://patanasongsivilai.com/example/json.php") // บรรทัด a +let txt = await res.text() // บรรทัด b +console.log(txt) // บรรทัด c +console.log("Hello"); // บรรทัด d +// แสดงผลลัพธ์ +// {"name":"Somchai","age":30,"city":"Bangkok"} +// "Hello" +``` + +```run.module +async function otherAsyncFunc(ms) { // ส่งค่าอากิวเมนต์เข้ามา + return new Promise ( + resolve => setTimeout( + () => resolve(`Time out: ${ms} ms`, ms ) + ,ms ) // กำหนด timeout หน่วยเป็นมิลลิวินาที (millisecond) + ); +} +async function func1() { + let time = await otherAsyncFunc(1000); + let t = await "func1-> " + time; + console.log(t); +} +async function func2() { + let time = await otherAsyncFunc(100); + let t = await "func2-> " + time; + console.log(t); +} +func1(); // บรรทัด a +func2(); // บรรทัด b +/* แสดงผลลัพธ์ +"func2-> Time out: 100 ms" +"func1-> Time out: 1000 ms" */ +``` + +นำตัวอย่างก่อนหน้ามาใช้ด้วยการ เติม await นำหน้า (อย่าลืมก็อปปี้โค้ด func1() กับ func2() มาใช้งานด้วย) +```run.module +await func1(); +await func2(); +/* แสดงผลลัพธ์ +"func1-> Time out: 1000 ms" +"func2-> Time out: 100 ms" */ +``` + +## Asynchronous iteration + +```js +let asyncIterable = [1, 5, 10]; + +asyncIterable[Symbol.asyncIterator]=createAsyncIterator; + +function createAsyncIterator() { + let array = this; // ในตัวอย่างนี้ this ชี้ไปยังอาร์เรย์ [1, 5, 10] + let i = 0; // เอาไว้นับจำนวนรอบเข้าถึงสมาชิกในอาร์เรย์ + return { + // ทุกครั้งที่เรียกใช้ next() ค่าของ i จะบวกเพิ่มเป็นหนึ่งทุกครั้ง + next : function() { + return new Promise( (resolve, reject) => { + // เมื่อเข้าถึงสมาชิกในอาร์เรย์ครบทุกตัว done จะมีค่าเป็น true + let done = (i >= array.length); + // เข้าถึงสมาชิกในอาร์เรย์ + let value = !done ? array[i++] : undefined; + // อ็อบเจ็กต์จะมีพร็อพเพอร์ตี้ value กับ done + let iteratorResult = { value, done }; + resolve(iteratorResult); // บรรทัด a + }); // สิ้นสุดการประกาศพรอมิส + } // สิ้นสุดการประกาศฟังก์ชัน next() + }; +} + +let asyncIterator = asyncIterable[Symbol.asyncIterator](); + +asyncIterator.next() // เรียก nex() ครั้งที่ 1 +.then(function(iteratorResult ) { + console.log(iteratorResult); // { value: 1, done: false } + return asyncIterator.next(); // เรียก nex() ครั้งที่ 2 +}) +.then(function(iteratorResult ) { + console.log(iteratorResult); // { value: 5, done: false } + return asyncIterator.next(); // เรียก nex() ครั้งที่ 3 +}) +.then(function(iteratorResult ) { + console.log(iteratorResult); // { value: 10, done: false } + return asyncIterator.next(); // เรียก nex() ครั้งที่ 4 ไม่มีสมาชิกให้เข้าถึง +}) +.then(function(iteratorResult ) { + console.log(iteratorResult); +}) +/* แสดงผลลัพธ์ +{ value: 1, done: false } +{ value: 5, done: false } +{ value: 10, done: false } +{ value: undefined, done: true } +*/ +``` + +ตัวอย่างนี้ต้องก็อปปี้การประกาศ asyncIterable จากตัวอย่างก่อนมาใช้งาน +```run.module +let asyncIterator2 = asyncIterable[Symbol.asyncIterator](); +let obj1 = await asyncIterator2.next(); +let obj2 = await asyncIterator2.next(); +let obj3 = await asyncIterator2.next(); +let obj4 = await asyncIterator2.next(); +console.log(obj1); // { value: 1, done: false } +console.log(obj2); // { value: 5, done: false } +console.log(obj3); // { value: 10, done: false } +console.log(obj4); // { value: undefined, done: true } +``` + +## ประโยคคำสั่ง for ...await ...of + +ตัวอย่างนี้ต้องก็อปปี้การประกาศ asyncIterable จากตัวอย่างก่อนมาใช้งาน +```run.module +for await (const x of asyncIterable) { + console.log(x); +} +/* แสดงผลลัพธ์ +1 +5 +10 */ +``` + +```run.module +for await (const x of [1, 2, 3]) { + console.log(x); +} +``` + +```run.module +let arr = [Promise.resolve("foo"), Promise.resolve("bar")]; +for await (const item of arr) { + console.log(item); +} +/* แสดงผลลัพธ์ +"foo" +"bar" */ +``` + +## Asynchronous generators + +รูปแบบโครงสร้างของเจนเนอเรเตอร์แบบอะซิงโครนัส จะเป็นดังนี้ + +```notrun +async function* asynGenerator() { + // ส่วนอินพุต + let x = await myPromise; + for await (const y of asyncIterable) { + // ··· + } + // ส่วนเอาท์พุต + yield someValue; + yield* otherAsyncGen(); +} +``` + +ตัวอย่างโค้ด +```run.module +async function* asynGenerator () { + yield 1; // บรรทัด a + yield 2; // บรรทัด b + yield 3; // บรรทัด c + } +let asyncIterable = asynGenerator(); // บรรทัด d +let asyncIterator = asyncIterable[Symbol.asyncIterator](); // บรรทัด e +console.log(await asyncIterator.next()); // { value: 1, done: false } +console.log(await asyncIterator.next()); // { value: 2, done: false } +console.log(await asyncIterator.next()); // { value: 3, done: false } +console.log(await asyncIterator.next()); // { value: undefined, done: true } + +for await(const i of asynGenerator()) { + console.log(i) // บรรทัด a +} +/* แสดงผลลัพธ์ +1 +2 +3 */ +``` + +```run.module +async function* asynGenerator () { + yield Promise.resolve(1); // บรรทัด a + yield Promise.resolve(2); // บรรทัด b + yield Promise.resolve(3); // บรรทัด c +} +for await(const i of asynGenerator()) { // บรรทัด d + console.log(i) // บรรทัด e + } +/* แสดงผลลัพธ์ +1 +2 +3 */ +``` + +```run.module +async function* otherAsynGenerator() { // บรรทัด a + yield Promise.resolve(2); + yield Promise.resolve(3); +} +async function* asynGenerator() { + yield Promise.resolve(1); + yield *otherAsynGenerator(); // บรรทัด b + yield Promise.resolve(4); // บรรทัด c +} +for await(const i of asynGenerator()) { // บรรทัด d + console.log(i) + } +/* แสดงผลลัพธ์ +1 +2 +3 +4 */ +``` diff --git a/examples_book/Chapter2.md b/examples_book/Chapter2.md deleted file mode 100644 index aca2fca..0000000 --- a/examples_book/Chapter2.md +++ /dev/null @@ -1,758 +0,0 @@ -# บทที่ 2 ทบทวนเบื้องต้น - -## คอมเมนต์ -```js -var x = 10; //This is an example. -``` - -```js -/* This is an example -ECMAScript 6 is very easy*/ -var x = 10; -``` - -## console.log() -```html - - - - -

Hello, world!

- - - -``` - -```js -console.log("Hello world", 122, 333.333); // "Hello world 122 333.333" -``` - -## debugger -```js -console.log("line 1"); -debugger; // บรรทัดนี้จะเป็นตำแหน่งของ breakpoint เพื่อหยุดการทำงานชั่วขณะ ตอนดีบั๊กโปรแกรม -console.log("line 2"); -``` - -## การตั้งชื่อ -```js -var _ = 100; // ตั้งชื่อแบบนี้ได้ -var $ = 100; // ตั้งชื่อแบบนี้ได้ -var _a =100; // ตั้งชื่อแบบนี้ได้ -var $a =100; // ตั้งชื่อแบบนี้ได้ -var Abc = 100; // ตั้งชื่อแบบนี้ได้ -var abc = 100; // ตั้งชื่อแบบนี้ได้ แต่ทว่าตัวแปร Abc กับ abc จะถือว่าคนละชื่อกัน -var a0123 = 100; // ตั้งชื่อแบบนี้ได้ -var 9b = 100; // ทำไม่ได้ มันจะเกิด SyntaxError เพราะมีเลข 9 นำหน้าชื่อตัวแปร -``` - -## การประกาศตัวแปร -```js -var x = 100; -``` - -```js -var x = 1, y = 2, z = 3; // ประกาศตัวแปร x, y และ z ให้อยู่ในบรรทัดเดียวกัน -``` - -```js -var x= 1, y = x; -/* จะเสมือนเขียนซอร์สโค้ดแบบนี้ -var x = 1; -var y = x; -*/ -``` - -```js -var x; -console.log(x); // undefined -``` - -## การประกาศตัวแปรโดยไม่มี var -```js -x = 1; -``` - -```js -console.log(x); // ReferenceError -``` - -## ไดนามิกไทป์ -```js -var foo = 42; // เริ่มต้นตัวแปร foo จะมีชนิดข้อมูลเป็นตัวเลข -foo = "bar"; // foo เปลี่ยนมาเก็บข้อมูลเป็นสตริง -foo = true; // foo เปลี่ยนมาเก็บข้อมูลเป็นบูลีน -``` - - -### null กับ undefined -```js -console.log(null === undefined) // false -console.log(null == undefined) // true -console.log(typeof null) // "object" -console.log(typeof undefined) // "undefined" -``` - -## ตัวเลข -```js -console.log(100, 0, -0, -300, 3.14, -78.222); // 100 0 -0 -300 3.14 -78.222 -``` - -```js -console.log(200e5); // 200 x 105 = 20000000 -console.log(2E-5); // 2 x 10-5 = 0.00002 -``` - -```js -console.log(Math.sqrt(-1)); // NaN (ไม่สามารถถอดรากที่สองของ -1) -console.log(0/0); // NaN ( 0 หาร 0 ไม่มีนิยาม) -console.log(parseInt("Hi")); // NaN (ไม่สามารถแปลง "Hi" เป็นตัวเลขได้) -``` - -```js -console.log( Infinity * Infinity); // Infinity -console.log( Infinity / Infinity); // NaN -console.log(-344 * Infinity); // -Infinity -console.log(3 / Infinity); // 0 -``` - -```js -console.log(Number.MAX_VALUE); // 1.7976931348623157e+308 เป็นค่าโดยประมาณ -console.log(Number.MAX_VALUE + 100); // 1.7976931348623157e+308 เป็นค่าโดยประมาณ (บรรทัด 2) -console.log(Number.MAX_VALUE * 10); // Infinity (บรรทัด 3) -console.log(Number.MAX_VALUE * -10); // -Infinity (บรรทัด 4) -``` - -```js -console.log(Number.MIN_VALUE); //5e-324 (เป็นค่าโดยประมาณ) -``` - -## สตริง -```js -"Learning JavaScript" -'Learning JavaScript' -``` - -```js -// ใช้ \" อยู่ภายในสตริง ส่วนเครื่องหมาย ' สามารถเขียนอยู่ในสตริงได้เลย -console.log("...\"Learning\" 'JavaScript'..."); // ..."Learning" 'JavaScript'... -// ใช้ \' อยู่ภายในสตริง ส่วนเครื่องหมาย " สามารถเขียนอยู่ในสตริงได้เลย -console.log('..."Learning" \'JavaScript\'...'); // ..."Learning" 'JavaScript'... -``` - -```js -var str= "line1\ -line2\ -line3"; -console.log(str); // line1line2line3 -``` - -```js -console.log("1234567890".length); // 10 -``` - -## โอเปอเรเตอร์ทางคณิตศาสตร์ -```js -console.log(true + 0); // ได้ผลลัพธ์เป็น 1 เพราะ true จะถือว่ามีค่าเป็น 1 -console.log(false + false); // ได้ผลลัพธ์เป็น 0 เพราะ false จะถือว่ามีค่าเป็น 0 -console.log(100 - true); // 99 -console.log(true * 30); // 30 -console.log(true / 2); // 0.5 -console.log(true %10); // 1 -console.log(+true); // 1 -console.log(-false); // -0 -var a = false, b=true; -console.log(++a); // 1 -console.log(--b); // 0 -``` - -```js -console.log(NaN + 30); // NaN -console.log(NaN - 30); // NaN -console.log(NaN * 30); // NaN -console.log(NaN / 30); // NaN -console.log(NaN % 30); // NaN -console.log(+NaN); // NaN -console.log(-NaN); // NaN -var x = NaN; -console.log(++x); // NaN -console.log(--x); // NaN -``` - -```js -console.log(Infinity * 0); // NaN -console.log(Infinity * 1); // Infinity -console.log(Infinity * Infinity); // Infinity -console.log(Infinity * -Infinity); // -Infinity -console.log(Infinity / 0); // Infinity -``` - -## โอเปอเรเตอร์ที่ใช้กำหนดค่าให้กับตัวแปร -```js -var a = 20; -a +=true; // true มีค่าเป็น 1 -console.log(a); // 21 -a *=NaN; -console.log(a); // NaN -``` - -## โอเปเรอเตอร์แบบตรรกะ -```js -console.log(true || true); // true (เงื่อนไข short circuit) -console.log(true || false); // true (เงื่อนไข short circuit) -console.log(false || true); // true -console.log(false || false); // false -console.log(true && true); // true -console.log(true && false); // false -console.log(false && true); // false (เงื่อนไข short circuit) -console.log(false && false); // false (เงื่อนไข short circuit) -console.log(!true); // false -console.log(!false); // true -``` - -## โอเปอเรเตอร์ระหว่างบิต -```js -var a = 12; // 1100 (เลขฐานสอง) -var b = 5; // 0101 (เลขฐานสอง) -var c = a & b; // 0100 (เลขฐานสอง) -console.log(c.toString(2)); // 100 (เลขฐานสอง) -console.log(c); // 4 -console.log(12 & 5); // 4 -``` - -```js -var a = 12; // 1100 -var b = 5; // 0101 -var c = a | b; // 1101 -console.log(c.toString(2)); // 1101 -console.log(c); // 13 -console.log(12 | 5); // 13 -``` - -```js -var a =12; // 1100 -var b= 5; // 0101 -var c = a ^ b; // 1001 -console.log(c.toString(2)); // 1001 -console.log(c); // 9 -console.log(12 ^ 5); // 9 -``` - -```js -var a= 9; // 00000000000000000000000000001001 -var b = ~a; // 11111111111111111111111111110110 (1’s Complement) -console.log(b); // -10 -console.log(~9); // -10 -``` - -```js -var a = 9; // 00000000000000000000000000001001 -var c = a << 2; // เลื่อนบิตจากขวามือไปทางซ้ายมือ 2 ตำแหน่ง - // 00000000000000000000000000100100 -console.log(c); // 36 -console.log(9 << 2); // 36 -``` - -```js -var a = 9; // 00000000000000000000000000001001 -var c = a >> 2; // เลื่อนบิตจากซ้ายมือไปทางขวามือ 2 ตำแหน่ง - // 00000000000000000000000000000010 -console.log(c); // 2 -console.log(9 >> 2); // 2 -``` - -```js --9; // 11111111111111111111111111110111 (เลขฐานสอง) --9 >> 2; // 11111111111111111111111111111101 (เลขฐานสอง) = -3 (เลขฐานสิบ) -``` - -```js -var a = 9; // 00000000000000000000000000001001 -var c = a >>> 2; // เลื่อนบิตจากซ้ายมือไปทางขวามือ 2 ตำแหน่ง พร้อมเติมเลข 0 ที่บิตด้านหน้าสุด - // 00000000000000000000000000000010 -console.log(c); // 2 -console.log(9 >>> 2); // 2 -``` - -## โอเปอเรเตอร์ typeof -```js -console.log(typeof true); // "boolean" -console.log(typeof false); // "boolean" -console.log(typeof -0.13); // "number" -console.log(typeof NaN); // "number" -console.log(typeof Infinity); // "number" -console.log(typeof undefined); // "undefined" -console.log(typeof ''); // "string" -console.log(typeof "Hi"); // "string" -console.log(typeof (typeof 100) ); // "string" -console.log(typeof null ); // "object" -console.log(typeof {x: 1, y: 2}); // "object" -console.log(typeof [1, 2]); // "object" -console.log(typeof function(){}); // "function" -console.log(typeof Math.sqrt); // "function" -console.log(typeof class C {}); // "function" -console.log(typeof Symbol()); // "symbol" -``` - -## โอเปอเรเตอร์วงเล็บ -```js -var a = 1 + 2 * 3 + 5; -// จะเสมือนเขียนเป็น var a = 1 + (2 * 3) + 5; -console.log(a); // 12 -``` - -```js -var a = (1 + 2) * (3 + 5); -console.log(a); // 24 -``` - -## โอเปอเรเตอร์คอมม่า -```js -var a = 1, b = 2; -var x = (1+34, a+=2, b*=10, b+1); -console.log(x); // 21 -``` - -## โอเปอเรเตอร์ void -```js -var a = void 12; -console.log(a); // undefined -console.log(Math.ceil(4.4)); // 5 -console.log(void Math.ceil(4.4)); // undefined -var b = 1; -console.log(void (++b)); // undefined -console.log(b); // แสดงค่าออกมาเป็น 2 เพราะตัวแปร b ถูกบวกเพิ่มไป 1 ค่า -``` - -## อาร์เรย์ -```js -[1, 1, 1, true, "Array"]; // อาร์เรย์ -``` - -```js -var a = ["a", "b", "c", "d", "e"]; -console.log(typeof a); // "object" -console.log(a[0], a[1], a[2], a[3], a [4]); // "a b c d e" -``` - -```js -var array = []; // ประกาศเป็นอาร์เรย์ว่าง -array[0] = 1; -array[1] = 2; -``` - -```js -var array = [1, 2, 3, 4, 5]; -console.log(array.length); // 5 -``` - -```js -var array = [1, 2, 3, 4, 5]; -console.log(array.length); // 5 -array.length = 7; // เพิ่มขนาดอาร์เรย์จาก 5 เป็น 7 -console.log(array); // [ 1, 2, 3, 4, 5, <2 empty slots> ] -console.log(array.length); // 7 -array[9] = 100; -console.log(array); // [ 1, 2, 3, 4, 5, <4 empty slots>, 100 ] -console.log(array.length); // 10 -``` - -## การประกาศฟังก์ชั่น -```js -function function_Name (พารามิเตอร์1, พารามิเตอร์2, …, พารามิเตอร์N) -{ -// ซอร์สโค้ดภายในบอดี้ของฟังก์ชั่น -return [value]; -// จะมีหรือไม่มีประโยค return ก็ได้ -// ส่วนค่า value หลังคำว่า return จะมีหรือไม่มีก็ได้ -} -``` - -```js -function calculate(param1, param2){ -return param1 * param2; -} -``` - -```js -var result = calculate(10, 2); -console.log(result); // 20 -``` - -```js -function calculate(){ -return 20; -} -var result = calculate(); -console.log(result); // 20 -``` - -## ประโยค return -```js -function myFunction (){ - return 1; - console.log("myFunction");// บรรทัดนี้เส้นทางการทำงานของโปรแกรมจะมาไม่ถึง -} -var result = myFunction(); -console.log(result); // 1 -``` - -```js -function myFunction (){ - return; -} -var result = myFunction(); -console.log(result); // undefined -``` - -```js -function myFunction (){ -console.log("myFunction"); -// จะเสมือนมีประโยค return undefined; วางไว้ตำแหน่งสุดท้าย ก่อนฟังก์ชั่นจบการทำงาน -} -var result = myFunction(); // "myFunction" -console.log(result); // undefined -``` - -## ฟังก์ชั่นไร้ชื่อ -```js -function (param1,param2){ -return param1 * param2; -} -``` - -## นิพจน์ฟังก์ชั่น -```js -var calculate = function (param1,param2){ -return param1 * param2; -} -console.log(calculate(10, 2)); // 20 -calculate = 100; // ตัวแปร calculation สามารถแก้ไขให้เป็นค่าอื่นได้ -console.log(calculate); // 100 -``` - -```js -var calculate = function calc2(param1,param2){ -return param1 * param2; -} -console.log(calculate(10,2)); // 20 -``` - -## ฟังก์ชั่นคอลแบ็ค -```js -function sayHi(){ -console.log("Hi"); -} -function sayBye(){ -console.log("Bye"); -} - -function say(func){ - func(); // เรียกฟังก์ชั่นให้ทำงาน -} -say(sayHi); // "Hi" -say(sayBye); // "Bye" -``` - -```js -function say(func){ - console.log("Say..."); -function sayHi(){ - console.log("Hi"); -} -return sayHi; // รีเทิร์นฟังก์ชั่น -} -var hi = say(); // "Say..." -hi(); // "Hi" -``` - -```js -function say(func){ - console.log("Say..."); - return function(){ // รีเทิร์นฟังก์ชั่นไร้ชื่อ - console.log("Hi"); -} -} // สิ้นสุดการประกาศฟังก์ชั่น -var hi = say(); // "Say..." -hi(); // "Hi" -``` - -## อ็อบเจ็กต์ arguments -```js -function myFunction(param1, param2){ -console.log(param1, param2); -} -myFunction(); // undefined undefined -myFunction(100); // 100 undefined -myFunction(100,200); // 100 200 -myFunction(100,200,300,400); // 100 200 -``` - -```js -function myFunction (param1, param2){ -console.log(arguments); -} -myFunction(100,200,300,400); // [100, 200, 300, 400] -``` - -```js -function myFunction(param1,param2){ // ฟังก์ชั่นนี้ไม่เคยถูกเรียกใช้ - console.log("function1 value:", param1, param2); -} -myFunction(100, 200); // เรียกใช้ฟังก์ชั่นที่ประกาศอยู่ด้านล่าง -function myFunction(param){ // จะโอเวอร์ไรด์ทับฟังก์ชั่นที่ประกาศไว้ก่อนหน้านี้ - console.log("function2 value:", param); -} -myFunction(100); -myFunction(100, 200); -/* แสดงผลลัพธ์เป็น -"function2 value: 100" -"function2 value: 100" -"function2 value: 100" */ -``` - -## ขอบเขตการมองเห็นของตัวแปร -```js -if(true){ -var a = 1; // a มีขอบเขตการมองเห็นแบบโกลบอล -} -{ -var b = 2; // b มีขอบเขตการมองเห็นแบบโกลบอล -} -console.log(a, b); // 1 2 -``` - -```html - - - - - - - -``` - -```js -console.log(NaN, undefined, Infinity); // NaN undefined Infinity -console.log(window.NaN, window.undefined, window.Infinity); // NaN undefined Infinity -// ถ้ารันอยู่ใน Node.js -// console.log(global.NaN, global.undefined, global.Infinity); -// NaN undefined Infinity -``` - -## ฟังก์ชั่นซ้อนฟังก์ชั่น -```js -function outerFunc() { - var value = 0; - function innerFunc() { - console.log(++value); - } - return innerFunc; -}; -var func1 = outerFunc(); // บรรทัด a -func1(); // 1 -func1(); // 2 - -var func2 = outerFunc(); // บรรทัด b -func2(); // 1 -func2(); // 2 -``` - -## Hoist -```js -var value = 100; -``` - -```js -function myFunction(num){ - // สามารถมองเห็นตัวแปร value - console.log(value); // undefined - - if(num > 10) { - var value = num*10; // ประกาศตัวแปร value ที่ตรงนี้ แต่มองเห็นได้ทั่วฟังก์ชั่น - /* ซอร์สโค้ด */ - } else { - // ถ้าเงื่อนไขประโยค if เป็นเท็จ ก็จะเข้ามาทำงานที่ else - // ซึ่งจะเห็นตัวแปร value มีค่าเป็น undefined - console.log(value); // undefined - } -// สามารถมองเห็นตัวแปร value ได้ หลังจากประโยค if …else ทำงานเสร็จสิ้น - console.log(value); -} -``` - -```js -function myFunction(num){ - var value; // ประกาศตัวแปร value โดยไม่มีค่าเริ่มต้น จึงทำให้มีค่าเป็น undefined -console.log(value); // undefined - if(num > 10) { - value = num*10; // บรรทัดนี้เป็นเพียงการกำหนดค่าให้กับตัวแปร value - /* ซอร์สโค้ด */ - } else { - console.log(value); // undefined - } - console.log(value); -} -``` - -```js -// สามารถมองเห็นตัวแปร value -console.log(value); // undefined -if(true) { -var value = 100; // ประกาศตัวแปรแบบ var -} -console.log(value); // 100 -``` - -```js -var value; // ประกาศตัวแปร value โดยไม่มีค่าเริ่มต้น จึงทำให้มีค่าเป็น undefined -console.log(value); // undefined -if(true) { - value = 100; // บรรทัดนี้เป็นเพียงการกำหนดค่าให้กับตัวแปร value -} -console.log(value); // 100 -``` - -```js -// มองเห็นฟังก์ชั่นก่อนการประกาศใช้งาน -myFunction(); // "Hoisted" -function myFunction(){ -console.log("Hoisted"); -} - -myFunction(); // "Hoisted" -``` - -```js -function outerFunc() { - innerFunc(); // มองเห็นฟังก์ชั่นก่อนการประกาศใช้งาน - function innerFunc() { - console.log("inner function"); - } -}; -outerFunc(); // "inner function" -console.log(typeof innerFunc); // undefined -``` - -## สตริคท์โหมด -```js -"use strict"; // ประกาศโหมดสตริคท์ ด้วยการเขียนไว้ที่ตอนต้นของไฟล์ -var x = 1; -``` - -```js -function myFunction() { - "use strict"; // เฉพาะฟังก์ชั่นนี้จะอยู่ในโหมดสตริคท์ - var x = 1; -} -``` - -```js -"use strict"; -x = 1; // เกิด error เพราะไม่ได้ประกาศตัวแปรแบบ var ถ้าอยู่ดี ๆ จะมากำหนดค่าให้ทันทีแบบนี้จะทำไม่ได้ -``` - -```js -"use strict"; -function x(a, a) {}; // เกิด error เพราะประกาศพารามิเตอร์ ที่มีชื่อ a ซ้ำกัน -``` - -```js -"use strict"; -var x = 1; -delete x; // เกิด error ไม่สามารถลบตัวแปรได้ -``` - -```js -"use strict"; -delete Object.prototype; //เกิด error เพราะพร็อพเพอร์ตี้ตัวนี้ห้ามลบ -``` - -```js -"use strict"; -var x = 010; // เกิด error ไม่สามารถประกาศแบบนี้ได้ -var y = \010; // เกิด error ไม่สามารถประกาศแบบนี้ได้ -``` - -```js -"use strict"; -var obj = {}; -Object.defineProperty(obj, "x", {value:0, writable:false}); -obj.x = 1; // เกิด error เนื่องจากมันเป็นพร็อพเพอร์ตี้ที่อ่านค่าได้อย่างเดียว -``` - -```js -"use strict"; -var obj = {get x() {return 0} }; -obj.x = 1; // เกิด error ไม่สามารถกำหนดค่าให้กับ x ได้ -``` - -```js -"use strict"; -var obj = {}; -Object.preventExtensions(obj); -obj.a= 1; // เกิด error ไม่สามารถเพิ่มพร็อพเพอร์ตี้เข้าไปในอ็อบเจ็กต์ได้ -``` - -```js -"use strict"; -function f() { return this; } -console.log(f()); // undefined -``` - -```js -"use strict"; -var eval = 1; // เกิด error ไม่สามารถใช้ชื่อ eval เป็นตัวแปร -``` - -```js -"use strict"; -var arguments = 1; // เกิด error ไม่สามารถใช้ชื่อ arguments เป็นตัวแปร -``` - -```js -"use strict"; -with (Math){ a = cos(1)}; // เกิด error ไม่สามารถใช้ประโยคคำสั่ง with ได้ -``` - -```js -"use strict"; -eval("var x = 1;"); // ประกาศตัวแปร x ด้วย eval() -x = 2; // เกิด error -// แบบนี้จะไม่เกิด error -// eval("var x = 1; x = 2;"); // ไม่เกิด error -``` - -```js -"use strict"; -var implements =1; // เกิด error เพราะ implements คือคำสงวนในโหมดสตริคท์ -``` - -```js -"use strict"; -if(true) { -function myFunction1(){ } // ขอบเขตแบบโลบอล -} - -{ -function myFunction2(){ } // ขอบเขตแบบโลบอล - -} -console.log(typeof myFunction1); // undefined (ถ้าไม่ใช่โหมดสตริคท์จะแสดงค่าเป็น "function") -console.log(typeof myFunction2); // undefined (ถ้าไม่ใช่โหมดสตริคท์จะแสดงค่าเป็น "function") -``` - - - - diff --git a/examples_book/Chapter20.md b/examples_book/Chapter20.md new file mode 100644 index 0000000..a6a3fe0 --- /dev/null +++ b/examples_book/Chapter20.md @@ -0,0 +1,475 @@ +# โค้ดบทที่ 20 มอดูล + +## มอดูลคืออะไร + +```js +// -------------- ไฟล์ main.js -------------------- +import "./lib/mylib.js"; // ไม่ได้โหลดอะไรเข้ามา +``` + +```module +// -------------- ไฟล์ lib/mylib.js --------------- +export var a = 1; +export var b = 2; +``` + +```run.module +// -------------- ไฟล์ main.js -------------------- +import { a, b } from "./lib/mylib.js"; +console.log(a); // 1 +console.log(b); // 2 +``` + +## เอ็กซ์พอร์ตด้วยการระบุชื่อ + +```module +// -------------- ไฟล์ lib.js -------------------- +export var a = 1; +export let b = 2; +export const MY_CONST = 100; +export function myFunction() { + // ซอร์สโค้ด +} +export function * myGenerator() { + // ซอร์สโค้ด +} +export class MyClass { + // ซอร์สโค้ด +} +``` + +```module +// -------------- ไฟล์ example.js -------------------- +const CONST_VALUE = 100; // บรรทัด a -- จะไม่ถูกเอ็กซ์พอร์ตออกไป +export function square(x) { // บรรทัด b + return x * x; +} +export function calculate(x, y) { // บรรทัด c + return (square(x) + square(y)) * CONST_VALUE; +} +function multiply(num1, num2) { // บรรทัด d + return num1 * num2; +} +export multiply; // บรรทัด e -- เอ็กซ์พอร์ตภายหลัง +``` + +```module +// -------------- ไฟล์ example.js -------------------- +const CONST_VALUE = 100; +function square(x) { + return x * x; +} +function calculate(x, y) { + return (square(x) + square(y)) * CONST_VALUE; +} +function multiply(num1, num2) { + return num1 * num2; +} +export { square, calculate, multiply }; // เขียนคำว่า export ที่บรรทัดสุดท้ายที่เดียว +``` + +```js +let condition = true; +if (condition) { + export condition; // SyntaxError +} +``` + +## วิธีโหลดมอดูล + +```module +// -------------- ไฟล์ example.js -------------- +const CONST_VALUE = 100; +function square(x) { + return x * x; +} +function calculate(x, y) { + return (square(x) + square(y)) * CONST_VALUE; +} +function multiply(num1, num2) { + return num1 * num2; +} +export { square, calculate, multiply }; +``` + +```run.module +// -------------- ไฟล์ main.js ----------------- +import { square, calculate } from "./example.js"; +console.log(square(2)); // 4 +console.log(calculate(2, 2)); // 800 +square =1; // บรรทัด a -- จะเกิด TypeError +calculate= 2; // บรรทัด b -- จะเกิด TypeError +let square = 1; // บรรทัด c -- จะเกิด SyntaxError +let calculate = 2 ; // บรรทัด d -- จะเกิด SyntaxError +``` + +----- + +```module +// -------------- ไฟล์ msg.js ----------------- +export var message = "Hi"; +export function setMessage(msg) { + message = msg; +} +``` + +```run.module +// -------------- ไฟล์ main.js -------------- +import { message, setMessage } from "./msg.js"; +console.log(message); // "Hi" +setMessage("Bye"); // บรรทัด a +console.log(message); // "Bye" +message = "Good morning"; // บรรทัด b -- จะเกิด TypeError +``` + +```run.module +// ไฟล์ main.js +import * as lib from "./example.js"; +console.log(lib.square(2)); // 4 +console.log(lib.calculate(2, 2)); // 800 +``` + +```run.module +// -------------- ไฟล์ main.js -------------- +import { square } from "./example.js"; +import { calculate } from "./example.js"; +import { multiply } from "./example.js"; +``` + +เพื่อให้เข้าใจถึงหลักการทำงานเวลามอดูลถูกโหลดเข้ามา สมมติว่ามีไฟล์มอดูล say.js ดังนี้ +```module +// -------------- ไฟล์ say.js ----------------- +export const msg = "Hello"; // บรรทัด a +console.log(msg); // บรรทัด b +``` + +```run.module +// -------------- ไฟล์ main.js -------------- +import { msg } from "./say.js"; +// แสดงผลลัพธ์ +// "Hello" +``` + +```run.module +// -------------- ไฟล์ main.js -------------- +import * as say1 from "./say.js"; +import * as say2 from "./say.js"; +import * as say3 from "./say.js"; +// แสดงผลลัพธ์ +// "Hello" +``` + +```run.module +// -------------- ไฟล์ main.js -------------- +import { msg } from "./say.js"; +import { msg } from "./say.js"; // เกิด SyntaxError เพราะประกาศชื่อ msg ซ้ำกัน 2 ครั้ง +``` + +```run.module +// -------------- ไฟล์ main.js -------------- +import { msg as msg1 } from "./say.js"; +import { msg as msg2 } from "./say.js"; +// แสดงผลลัพธ์ +// "Hello" +``` + +```run.module +// -------------- ไฟล์ main.js -------------- +console.log("Hi") // บรรทัด a +import { msg } from "./say.js"; // บรรทัด b +// แสดงผลลัพธ์ +// "Hello" +// "Hi" +``` + +```run.module +// -------------- ไฟล์ main.js -------------- +{ + import { msg } from "./say.js"; // SyntaxError +} +``` + +## เปลี่ยนชื่อสิ่งที่ต้องการโหลด และเอ็กซ์พอร์ต + +```run.module +// -------------- ไฟล์ main.js -------------- +import { square, calculate as calc } from "./example.js"; +console.log(square(2)); // 4 +console.log(calc(2, 2)); // 800 +``` + +```module +// -------------- ไฟล์ example.js -------------- +const CONST_VALUE = 100; +export function square(x) { + return x * x; +} +export function calculate(x, y) { + return (square(x) + square(y)) * CONST_VALUE; +} +function multiply(num1, num2) { + return num1 * num2; +} +export { square as sqr, calculate as calc, multiply as mul }; // บรรทัด a +``` + +```run.module +// -------------- ไฟล์ main.js ----------------- +import { sqr, calc, mul } from "./example.js"; +console.log(sqr(2)); // 4 +console.log(calc(2, 2)); // 800 +console.log(mul(2,2)); // 4 +``` + +## เอ็กซ์พอร์ตแบบดีฟอลต์ + +```module +// -------------- ไฟล์ MyFunc.js -------------- +export default function () { /*…*/ } +``` + +```module +// -------------- ไฟล์ MyClass.js -------------- +export default class { /*…*/ } +``` + +```module +// -------------- ไฟล์ MyValue.js -------------- +export default 123; +``` + +```module +// -------------- ไฟล์ MyArrow.js -------------- +export default param => param; +``` + +```run.module +// -------------- ไฟล์ main.js -------------- +import func from "./MyFunc.js"; +import _class from "./MyClass.js"; +import value from "./MyValue.js"; +import arrowFunc from "./MyArrow.js"; +``` + +---- + +```module +// -------------- ไฟล์ mydefault.js -------------- +export var value = 100; +export function square(x) { + return x * x; +} +export default function(num1, num2) { // ฟังก์ชันไร้ชื่อ + return num1 + num2; +} +``` + +```run.module +// -------------- ไฟล์ main.js ------------------- +import multiply, { value, square } from "./mydefault.js"; +console.log(value); // 100 +console.log(square(2,2)); // 4 +console.log(multiply(2,2)); // 4 +``` + +## เอ็กซ์พอร์ตซ้ำจากมอดูลอื่น + +```module +// -------------- ไฟล์ mylib.js -------------- +var foo = 1; +var bar = 2; +export { foo, bar }; +``` + +```module +// -------------- ไฟล์ example.js -------------- +export { foo, bar } from "./mylib.js"; +``` + +```module +// -------------- ไฟล์ example.js -------------- +import { foo, bar } from "./mylib.js"; +export { foo, bar }; +``` + +```module +// -------------- ไฟล์ example.js -------------- +export { foo as foo1, bar } from "./mylib.js"; +``` + +```module +// -------------- ไฟล์ example.js -------------- +export * from "./mylib.js"; +``` + +หมายเหตุ โค้ดสำหรับเทสในหัวข้อนี้ (ไม่มีในหนังสือ) + +```run.module +// -------------- ไฟล์ main.js -------------- +import { foo, bar } from "./example.js"; +console.log(foo, bar); +``` + +## Namespace re-exporting + +```js +export * as ns from "http"; +``` + +* ลองพิจารณาตัวอย่างไฟล์ 3 อัน ดังต่อไปนี้ +* 1) ไฟล์ name.js จะเป็นมอดูลง่ายๆ เก็บตัวแปร firstname กับ lastname + +```module +// -------------- ไฟล์ name.js ------------------ +const firstname = "Somchai"; +const lastname = "Jaidee"; +export { firstname, lastname }; // ส่งออก firstname กับ lastname +``` + +* 2) ไฟล์ student.js เป็นมอดูลอย่างง่าย ข้างในทำการ export ส่งออกตัวแปร age และจะนำเข้ามอดูล name.js พร้อม export ส่องออกมอดูลออกมาเป็นชื่อ person (เปลี่ยนชื่อ namespace) + +```module +// -------------- ไฟล์ student.js ------------------ +const age = 25; +export {age}; // ส่งออก age +export * as person from "./name.js" // ส่งออก person +``` + +* 3) ไฟล์ myprogram.js เอาไว้รันทดสอบโปรแกรม โดยจะนำเข้ามอดูล student.js ดังตัวอย่าง + +```run.module +// -------------- ไฟล์ myprogram.js ------------------ +import { age, person } from "./student.js" +console.log(age); // 25 +console.log(person.firstname); // "Somchai" +console.log(person.lastname); // "Jaidee" +``` + +## อ็อบเจ็กต์โกลบอล + +```module +// -------------- ไฟล์ example.js -------------- +Object.prototype.say = function(msg) { + console.log(msg); +}; +``` + +```run.module +// -------------- ไฟล์ main.js ------------------ +import "./example.js"; +let a = { }; +a.say("I love JavaScript"); // "I love JavaScript" +``` + +## ลองใช้งานมอดูลกันจริงๆ + +### วิธีใช้งานมอดูลบนเว็บเบราเซอร์ + +หมายเหตุ ตัวอย่างหน้า HTML นี้ ต้องมีมอดูล mylib.js ข้างล่างเสียก่อน ไม่เช่นนั้นตัวอย่างหน้าเว็บจะไม่เจอมอดูล + +เช่น mylib.js อยู่ในโฟลเดอร์ test_module เราก็อ้างถึงพาธ "./test_modulejs/mylib.js" + +```tab.html + + + + + + + +``` + +หมายเหตุ mylib.js สมมติอยู่ในโฟลเดอร์ test_modulejs + +```module +// -------------- ไฟล์ mylib.js ------------------ +export const msg = "I love JavaScript"; +``` + +```notrun + +``` + +### วิธีใช้งานมอดูลบน Node.js + +หมายเหตุ หลายตัวอย่างต่อไปนี้ต้องทดสอบบน Node.js เท่านั้น เพราะบนเว็บเบราเซอร์ยังไม่รองรับ + +```js +// -------------- ไฟล์ mylib.mjs ------------------ +export const msg = "I love JavaScript"; +``` + +```js +// -------------- ไฟล์ myapp.mjs ------------------ +import { msg } from "./mylib.mjs"; +console.log(msg); +``` + +```module +// -------------- ไฟล์ mylib.js ------------------ +export const msg = "I love JavaScript"; +``` + +```run.module +// -------------- ไฟล์ myapp.js ------------------ +import { msg } from "./mylib.js"; +console.log(msg); +``` + +ตัวอย่างไฟล์ package.json + +```notrun +{ "type": "module" } +``` + +```notrun +{ + "name": "my-awesome-package", + "version": "1.0.0" + ,"type": "module" +} +``` + +### เสริมเพิ่มเติม + +หมายเหตุ โค้ดต่อไปนี้ให้รันบน Node.js + +```js +var http = require("http"); +var fs = require("fs"); +var url = require("url"); +``` + +```js +import * as http from "http"; +import * as fs from "fs"; +import * as url from "url"; +``` + +## โอเปอเรเตอร์ import() + +หมายเหตุ โค้ดต่อไปนี้ให้รันบน Node.js + +```js +import("http") +.then((httpModule) => { + console.log(httpModule.maxHeaderSize); // 16384 +}); +``` + +```js +const httpModule = await import("http"); +console.log(httpModule.maxHeaderSize); // 16384 +``` + +## import.meta + +```run.module +console.log(import.meta.url); // "file:///c:/javascript/mymodule.js" +``` \ No newline at end of file diff --git a/examples_book/Chapter3.md b/examples_book/Chapter3.md deleted file mode 100644 index a7f4daf..0000000 --- a/examples_book/Chapter3.md +++ /dev/null @@ -1,296 +0,0 @@ - -# บทที่ 3 ทบทวนประโยคคำสั่งเบื้องต้น - -## บล็อก -```js -{ - var a = 2; // ประโยคคำสั่ง 1 - a++; // ประโยคคำสั่ง 2 - console.log(a); // ประโยคคำสั่ง 3 -} -``` - -```js -if (true) { - var a = 2; // ประโยคคำสั่ง 1 - a++; // ประโยคคำสั่ง 2 - console.log(a); // ประโยคคำสั่ง 3 -} -``` - -## ประโยคคำสั่ง while -```js -var i = 0; -while (i < 3) { - console.log(i); - i++; -} -/* แสดงผลลัพธ์ -0 -1 -2 */ -``` - -## ประโยคคำสั่ง do …while -```js -var i = 0; -do{ - console.log(i); - i++; -} while (i < 3); -/* แสดงผลลัพธ์ -0 -1 -2 */ -``` - -## ประโยคคำสั่ง for -```js -for(var i=0; i<3; i++){ - console.log(i); -} -/* แสดงผลลัพธ์ -0 -1 -2 */ -``` - -## ประโยคคำสั่ง continue -```js -for(var i=0; i<3; i++){ - if(i<=1){ - continue; // ข้ามประโยคคำสั่งที่เหลือ ไปทำงานรอบถัดไปแทน - } - console.log(i); -} -// แสดงผลลัพธ์ -// 2 -``` - -## ประโยคคำสั่ง break -```js -for(var i=0; i<3; i++){ - if(i==2){ - break; - } - console.log(i); -} -/* แสดงผลลัพธ์ -0 -1 */ -``` - -## ประโยคคำสั่ง label -```js -outer: for(var i=0; i < 2; i++) { // บรรทัด a - console.log("i: ", i); - for (var j=0; j < 3; j++ ) { - if ( j == 1){ - continue outer; // บรรทัด b - } - console.log("j: ", j); - }// สิ้นสุดประโยคคำสั่ง for -} // สิ้นสุดประโยคคำสตั่ง for ที่ได้ติดฉลาก outer: for -/* แสดงผลลัพธ์ -"i: 0" -"j: 0" -"i: 1" -"j: 0" */ -``` - -```js -outer: while(true){ // บรรทัด a - for (var i=0; i < 3; i++ ) { - if ( i == 1){ - break outer; // บรรทัด b - } - console.log("i: ", i); - }// สิ้นสุดประโยคคำสั่ง for -}// สิ้นสุดประโยคคำสั่ง while -// แสดงผลลัพธ์ -// "i: 0" -outer: while(true){ // บรรทัด a - for (var i=0; i < 3; i++ ) { - if ( i == 1){ - break outer; // บรรทัด b - } - console.log("i: ", i); - }// สิ้นสุดประโยคคำสั่ง for -}// สิ้นสุดประโยคคำสั่ง while -// แสดงผลลัพธ์ -// "i: 0" -``` - -## ประโยคคำสั่ง if -```js -// var i = 1; -// var i = 2; -// var i = 3; - -if(i == 1) -console.log("if statement"); -else if(i == 2) - console.log("else if statement"); -else - console.log("else statement"); -``` - -## ประโยคคำสั่ง switch -```js -// var i = 1; -// var i = 2; -// var i = 3; -switch (i) { - case 1: - console.log("case1"); // ถ้า i มีค่าเป็น 1 ก็จะมาทำประโยคนี้ - break; // ออกจากประโยค switch - case 2: - console.log("case2"); // ถ้า i มีค่าเป็น 2 ก็จะมาทำประโยคนี้ - break; // ออกจากประโยค switch - default: - console.log("case_default"); // ถ้า i ไม่ใช่ 1 กับ 2 ก็จะมาทำประโยคนี้ - -} -``` - -```js -switch (1) { - case 1: - console.log("case1"); - case 2: - console.log("case2"); - default: - console.log("case_default"); -} -/* แสดงผลลัพธ์เป็น -"case1" -"case2" -"case_default" */ -``` - -```js -switch (1) { - case 1: - case 2: - default: - console.log("case_default"); -} -// แสดงผลลัพธ์เป็น -// "case_default" -``` - -## โอเปอเรเตอร์แบบเงื่อนไข -```js -// var condition = true; -// var condition = false; -var value = condition ? "foo" : "bar"; -console.log(value); // "foo" หรือ "bar" -``` - -```js -// var condition = true; -// var condition = false; -var value; -if(condition){ -value = "foo"; -} else { - value = "bar"; -} -console.log(value); // "foo" หรือ "bar" -``` - -## ปการจัดการความผิดพลาด -```js -throw "Error"; // โยน exception เป็นชนิดข้อมูลสตริง -throw 100; // โยน exception เป็นชนิดข้อมูลตัวเลข -throw true; // โยน exception เป็นชนิดข้อมูลบูลีน -throw new Object(); // โยน exception เป็นชนิดข้อมูลอ็อบเจ็กต์ -throw new Error("Error") // โยน exception เป็นชนิดข้อมูลอ็อบเจ็กต์ -``` - -```js -console.log(x); // ReferenceError -``` - -```js -new Error(["ข้อความ error"]) -``` - -```js -throw new SyntaxError ("Syntax error"); -``` - -## ประโยคคำสั่งจัดการความผิดพลาด -```js -try { - console.log(x); // บรรทัด a - x++; // บรรทัด b -} catch (e) { - console.log(typeof e); // "object" - console.log(e.message); // "x is not defined" - console.log(e.name); // "ReferenceError" -} -console.log("Last statement"); // บรรทัด c -/* แสดงผลลัพธ์เป็น -"object" -"x is not defined" -"ReferenceError" -"Last statement" */ -``` - -```js -try { -throw 42; // โยน exception ออกมาเอง -console.log(42); // เส้นทางการทำงานของโปรแกรมจะมาไม่ถึง -} catch (e) { - console.log(e); // 42 -} -console.log("Last statment"); -/* แสดงผลลัพธ์ -42 -"Last statment" */ -``` - -```js -try { -console.log(x); // บรรทัด a -} catch (e){ - console.log(e.message); // "x is not defined" -} finally { - console.log("finally"); -} -console.log("Last statement"); -/* แสดงผลลัพธ์ -"x is not defined" -"finally"; -"Last statement" */ -``` - -```js -try { -console.log("no error"); -} catch (e){ - console.error(e.message); -} finally{ - console.log("finally"); -} -console.log("Last statement "); -/* แสดงผลลัพธ์ -"no error" -"finally"; -"Last statement" */ -``` - -```js -function foo(){ -try { - return "foo"; -} finally{ - console.log("finally"); -} -} -console.log(foo()); -/* แสดงผลลัพธ์ -"finally"; -"foo"; */ -``` diff --git a/examples_book/Chapter4.md b/examples_book/Chapter4.md deleted file mode 100644 index ce43c80..0000000 --- a/examples_book/Chapter4.md +++ /dev/null @@ -1,838 +0,0 @@ -# บทที่ 4 ทบทวนอ็อบเจ็กต์ - -## วิธีสร้างอ็อบเจ็กต์อย่างง่าย - -```js -var font = {}; -``` - -```js -var font = { - color: "red" // คีย์ชื่อ color : ข้อมูลจะเป็นสตริง "red" - ,myFunction: function (param){ // คีย์ชื่อ myFunction : ข้อมูลจะเป็นเมธอด (ฟังก์ชั่น) - // ซอร์สโค้ดของเมธอด -} -,option: { // คีย์ชื่อ option : ข้อมูลจะเป็นอ็อบเจ็กต์ - value: 1 -} -}; -``` - - -## การเข้าถึงพร็อพเพอร์ตี้ -```js -var obj = { - a: 1 // กำหนดให้ a มีค่าตั้งต้นเป็น 1 -,myFunction : function() { - console.log("call myFunction"); -} -}; -obj.a = 100; // กำหนดให้ obj.a มีค่าเป็น 100 -console.log(obj.a); // 100 -console.log(typeof obj.myFunction); // "function" -obj.myFunction(); // "call myFunction" -``` - -## การใช้วงเล็บเหลี่ยม -```js -var student = { -"First name": "Somchai" -,"Last name": "Jaidee" -,"Who are you": function(){ - console.log("I’m a student"); -} -,nickname: "Tom" -}; -console.log(student["First name"]); // "Somchai" -var lastName = "Last name"; -console.log(student[lastName]); // "Jaidee" -student["Who are you"](); // "I’m a student" -console.log(student.nickname); // "Tom" -console.log(student["nickname"]); // "Tom" -``` - -```js -var obj = { -1: 1 -,true: 2 -,null : 3 -,undefined: 4 -}; -console.log(obj[1 + 0]); // 1 -console.log(obj[true && true]); // 2 -console.log(obj[null]); // 3 -console.log(obj[undefined]); // 4 -``` - -```js -var obj = { -{}: 1 // เกิด error ไม่สามารถใช้อ็อบเจ็กต์เป็นคีย์ได้โดยตรง -}; -``` - -## เพิ่มพร็อพเพอร์ตี้เข้าไปทีหลัง -```js -var obj = {}; -obj.a = 1; // เพิ่มพร็อพเพอร์ตี้ที่เป็นตัวแปร a -obj[1]=100; // เพิ่มพร็อพเพอร์ตี้ที่มีคีย์เป็นตัวเลข 1 -obj["property name"]= 200; // เพิ่มพร็อพเพอร์ตี้ที่มีคีย์เป็นสตริง "property name" -obj.myFunction = function(){ // เพิ่มพร็อพเพอร์ตี้ที่เป็นเมธอด -console.log("to do something"); -}; - -console.log(obj.a); // 1 -console.log(obj[1]); // 100 -console.log(obj["property name"]); // 200 -obj.myFunction(); // "to do something" -``` - -```js -var obj = { }; -var key = { }; -obj[key] = 100; // มีคีย์เป็นอ็อบเจ็กต์ว่าง -console.log(obj[key]); // 100 -``` - -## การส่งค่าให้ตัวแปร -```js -function myFunction(param1, param2){ - param1.a = 3; // pass by reference - param2 = 200; // pass by value -} -var obj = {a:1, b:2}, value = 100; -myFunction(obj, value); -console.log(obj.a); // 3 -console.log(value); // 100 -``` - -```js -var obj1 = {a:1, b:2}; -var obj2 = obj1; // บรรทัด a -- pass by reference -obj2.a = 3; // บรรทัด b -console.log(obj1.a); // 3 -var value1 = 1; -var value2 = value1; // บรรทัด d -- pass by value -value2 = 3; // บรรทัด e -console.log(value1); // 1 -``` - -## การเปรียบเทียบความเท่ากัน -```js -console.log({a:1} == {a:1}); // false -console.log({a:1} === {a:1}); // false -``` - -```js -var a = 1, b = 1; -console.log(a == b); // true -console.log(a === b); // true -``` - -## this -```js -var obj = { - a: 1 - ,foo: function(){ - return 2; - } - ,bar: function(){ - console.log(this.a); -} -,zoo: function(){ - console.log(this.foo()); -} -}; -obj.bar(); // 1 -obj.zoo(); // 2 -``` - -```js -var obj = { - foo: function (){ - this.a = 1; // เพิ่มตัวแปร a เข้าไปในอ็อบเจ็กต์ - console.log(this.a ); -} -,bar: function(){ - console.log(this.a); -} -}; - -obj.foo(); // 1 -obj.bar(); // 1 -console.log(obj.a); // 1 -``` - -## การผูก this ไว้กับอ็อบเจ็กต์ -```js -var obj1 = {}; -var obj2 ={ -a: 1 -,bar : function(){ - console.log("this.a =", this.a); - obj1.foo = function(){ - console.log("this.a =", this.a); - } // สิ้นสุดการประกาศฟังก์ชั่น foo() - } // สิ้นสุดการประกาศฟังก์ชั่น bar() -}; -obj2.bar(); // "this.a = 1" -obj1.foo(); // "this.a = undefined" -``` - -## this ในฟังก์ชั่น -```js -//"use strict"; -function myFunction() { - return this; -} -var obj = myFunction(); -console.log(typeof obj); -// แสดงผลลัพธ์เป็น -// undefined (ถ้าเป็นโหมดสตริคท์) -// "object" (ถ้าไม่ใช่โหมดสตริคท์) -``` - -## เมธอด call() apply() และ bind() -```js -var obj1 = { - value: 20 -}; -var obj2 = { -myFunction: function(param1, param2){ - var value = this.value; // this จะชี้ไปยัง obj1 - console.log(param1, param2, value); -} -} -obj2.myFunction(1, 10); // 1 10 undefined -obj2.myFunction.call(obj1, 1, 10); // 1 10 20 -obj2.myFunction.apply(obj1, [1, 10]); // 1 10 20 -var f = obj2.myFunction.bind(obj1, 1, 10); -f(); // 1 10 20 -``` - -## พร็อพเพอร์ตี้แอคเซสเซอร์ -```js -var font = { color: "red" } ; -font.color = "blue" ; -``` - -```js -var font = { - set color(param){ // ประกาศเมธอด setter โดยมีพารามิเตอร์ ได้เพียงตัวเดียว - this.col = param; // กำหนดค่าให้กับข้อมูลภายในอ็อบเจ็กต์ -} -} ; -font.color = "blue"; // แก้ไขค่าได้ -console.log(font.color) // undefined -``` - -```js -var font = { - col: "red" - ,get color(){ // ประกาศเมธอด getter โดยไม่ต้องมีพารามิเตอร์ - return this.col; // รีเทิร์นข้อมูลภายในอ็อบเจ็กต์ออกไป -} -}; -console.log(font.color); // "red" -font.color = "blue"; // ไม่มีผลอะไรเกิดขึ้น หรือจะเกิด TypeError ในโหมดสตริคท์ -console.log(font.color); // "red" -``` - -```js -var font = { -col: "red" - ,set color(param){ - this.col = param; -} -,get color(){ - return this.col; -} -} ; -console.log(font.color); // "red" -font.color = "blue"; -console.log(font.color); // "blue" -``` - -## โอเปอเรเตอร์ delete -```js -var obj = {x:1 ,y:2} ; -console.log(delete obj.x); // true -console.log(delete obj["y"]); // true -console.log(obj); // {} -var a = 1; -console.log(delete a); // false หรือเกิด SyntaxError ในโหมดสตริคท์ -``` - -```js -console.log(delete Number.MAX_VALUE); // false หรือเกิด TypeError โหมดสตริคต์ -``` - -```js -var a = [1, "Hi"]; -console.log(a.length); // 2 -console.log(delete a[0]); // true -console.log(delete a[1]); // true -console.log(a[0]); // undefined -console.log(a[1]); // undefined -console.log(a.length); // 2 -``` - -## Descriptor -```js -var obj1 = {}; -Object.defineProperty(obj1, "foo", { // อ็อบเจ็กต์ descriptor - value: 100 - ,writable: true -}); -console.log(obj1.foo); // 100 -console.log(Object.getOwnPropertyDescriptor(obj1,"foo")); // รีเทิร์น descriptor -// { value: 100, writable: true, enumerable: false, configurable: false } -var obj2 = {}; -Object.defineProperties(obj2, { - "foo": { // อ็อบเจ็กต์ descriptor - value: "fooValue", - writable: true - } - ,"bar": { // อ็อบเจ็กต์ descriptor - value: "barValue", - writable: false - } - // พร็อพเพอร์ตี้อื่น ๆ -}); -console.log(obj2.foo, obj2.bar); // "fooValue barValue" -console.log(Object.getOwnPropertyDescriptor(obj2,"foo")); // รีเทิร์น descriptor -// { value: "fooValue", writable: true, enumerable: false, configurable: false } -console.log(Object.getOwnPropertyDescriptor(obj2,"bar")); // รีเทิร์น descriptor -// { value: "barValue", writable: false, enumerable: false, configurable: false } -``` - -## ฟังก์ชั่นคอนสตรัคเตอร์ -```js -function Car(color) { -this.color = color; -return true; -} -var redCar = new Car("red"); -var blueCar = new Car("blue"); -//… สร้างอ็อบเจ็กต์ใหม่ได้เรื่อยด้วยโอเปอเรเตอร์ new -console.log(redCar.color); // "red" -console.log(blueCar.color); // "blue" -``` - -```js -function Car(color) { -this.color = color; // ถ้าเป็นโหมดสตริคท์จะเกิด error ขึ้นได้ -return true; -} -var blueCar = Car("blue"); // เป็นการเรียกฟังก์ชั่นธรรดา -console.log(blueCar); // true -``` - -```js -function Car(color) { -console.log("constructor"); -} -var redCar = new Car(); // "constructor" -var blueCar = new Car; // "constructor" -``` - -## เมธอด Object.create() -```js -var car = { - drive: function(){ console.log("driving a car") ; } -} -var redCar = Object.create(car); -var blueCar = Object.create(car, -{ // เพิ่มพร็อพเพอร์ตี้เข้าไป ด้วยการระบุ descriptor - foo: { writable: true, configurable: true, value: "fooValue" } // descriptor - ,bar:{ writable: true, configurable: true, value: "barValue" } // descriptor -} -); -// สร้างอ็อบเจ็กต์ใหม่ได้เรื่อย ๆ ด้วย Object.create() -// … -redCar.drive(); // "driving a car" -blueCar.drive(); // "driving a car" -console.log(blueCar.foo); // "fooValue" -console.log(blueCar.bar); // "barValue" -``` - -```js -var car = { } -var redCar = Object.create(car); -var blueCar = Object.create(car); -car.drive = function() { - console.log("driving a car"); -} -redCar.drive(); // "driving a car" -blueCar.drive(); // "driving a car" -``` - -```js -console.log( Object.getPrototypeOf(redCar) === car ); // true (เพราะโปรโตไทป์คือ car) -console.log( Object.getPrototypeOf(blueCar) === car ); // true (เพราะโปรโตไทป์คือ car) -``` - -```js -var obj1 = Object.create(null); // ไม่มีโปรโตไทป์ -console.log(obj1); // {} -console.log(Object.getPrototypeOf(obj1)); // null - -var obj2 = Object.create(Object.prototype); // จะเหมือนสร้างอ็อบเจ็กต์ด้วยวิธีนี้ var obj = {} -console.log(obj2); // {} -console.log(Object.getPrototypeOf(obj2) === Object.prototype); // true -``` - -## prototype -```js -function Car(color) { -this.color = color; -} -var redCar = new Car("red"); -console.log( Object.getPrototypeOf(redCar) === Car.prototype); // true -``` - -```js -function Car(color) { -this.color = color; -} -Car.prototype.drive = function() { -console.log("Drive a", this.color, "car"); // this จะชี้ไปยังอ็อบเจ็กต์ที่ถูกสร้างขึ้นมา -}; -var redCar = new Car("red"); -redCar.drive(); // " Drive a red car" -var blueCar = new Car("blue"); -blueCar.drive(); // " Drive a blue car" -console.log( Object.getPrototypeOf(redCar) === Car.prototype); // true -console.log( Object.getPrototypeOf(blueCar) === Car.prototype); // true -``` - -```js -function Car(color) { -this.color = color; -} -Car.drive = function() { -console.log("driving a car"); -}; -Car.drive(); // "driving a car" -var redCar = new Car("red"); -console.log(typeof redCar.drive); // undefined -``` - -## การสืบทอดสมาชิกหลายระดับชั้น -```js -var extend = { -drive: function(){ - console.log("Drive a", this.color, "car"); -} -} -function Car(color) { -this.color = color; -} -Car.prototype = extend; // บรรทัด a -Car.prototype.stop = function(){ // บรรทัด b -console.log("Stop a", this.color, "car"); -}; -var redCar = new Car("red"); -console.log( Object.getPrototypeOf(redCar) === extend); // true -redCar.drive(); // "Drive a red car" -redCar.stop(); // "Stop a red car" -``` - -```js -function Car(color) {} -Car.prototype.drive = function() { -console.log("Drive a", this.color, "car"); // this ชี้ไปยังอ็อบเจ็กต์ที่ถูกสร้างขึ้นมา -}; -function RedCar(color){ - this.color = color; -} -RedCar.prototype = Object.create(Car.prototype); // บรรทัด a -console.log( Object.getPrototypeOf(RedCar.prototype) === Car.prototype); // true -RedCar.prototype.stop = function(){ -console.log("Stop a", this.color, "car"); // this ชี้ไปยังอ็อบเจ็กต์ที่ถูกสร้างขึ้นมา -} -var redCar = new RedCar("red"); -console.log( Object.getPrototypeOf(redCar) === RedCar.prototype); // true -redCar.drive(); // "Drive a red car" -redCar.stop(); // "Stop a red car" -``` - -## โอเปอรเตอร์ instanceof -```js -function Foo() { } -var obj = new Foo(); -console.log(obj instanceof Foo); // true -console.log(Object.getPrototypeOf(obj) === Foo.prototype); // true -``` - -```js -function Foo() { } -var obj = new Foo(); -console.log(obj instanceof Foo); // true -console.log(Object.getPrototypeOf(obj) === Foo.prototype); // true - -console.log(obj instanceof Object); // true -console.log(Foo.prototype instanceof Object); // true -console.log(Object.getPrototypeOf(Foo.prototype) === Object.prototype); // true -console.log(Object.getPrototypeOf(obj) === Object.prototype); // false -``` - -## prototype ที่มีอยู่ในภาษา -```js -console.log( Object.getPrototypeOf(function(){}) === Function.prototype); // true -console.log( Object.getPrototypeOf([]) === Array.prototype); // true -console.log( Object.getPrototypeOf({}) === Object.prototype); // true -console.log( Object.getPrototypeOf('') === String.prototype); // true -console.log( Object.getPrototypeOf(true) === Boolean.prototype); // true -console.log( Object.getPrototypeOf(1) === Number.prototype); // true -console.log(function(){} instanceof Function); // true -console.log([] instanceof Array); // true -console.log({} instanceof Object); // true -console.log(''instanceof String); // false -console.log(true instanceof Boolean); // false -console.log(1 instanceof Number); // false -``` - -```js -// ฟังก์ชั่น -Function.prototype.sayMsg = function(msg) { - console.log("Function say:", msg); -}; -function myFunction(){} -myFunction.sayMsg("Hello");// " Function say: Hello" -// อาร์เรย์ -Array.prototype.sayMsg = function(msg) { - console.log("Array say:", msg); -}; -[].sayMsg("Hello"); // "Array say: Hello" -// อ็อบเจ็กต์ -Object.prototype.sayMsg = function(msg) { - console.log("Object say:", msg); -}; -var obj = {}; -obj.sayMsg("Hello"); // "Object say: Hello" -({}).sayMsg("Hello"); // "Object say: Hello" -// สตริง -String.prototype.sayMsg = function(msg) { - console.log("String say:", msg); -}; -"123".sayMsg("Hello"); // "String say: Hello" -// บูลีน -Boolean.prototype.sayMsg = function(msg) { - console.log("Boolean say:", msg); -} -true.sayMsg("Hello"); // "Boolean say: Hello" -// ตัวเลข -Number.prototype.sayMsg = function(msg) { - console.log("Number say:", msg); -} -var num = 123; -num.sayMsg("Hello"); // "Number say: Hello" -(123).sayMsg("Hello"); // "Number say: Hello" -``` - -## โอเปอเรเตอร์ in -```js -var obj = {x: 1, y: 2}; -console.log("x" in obj); // true -console.log("xyz" in obj); // false (อ็อบเจ็กต์ไม่มีพร็อพเพอร์ตี้ xyz) -var a = ["a", "b", "c"]; -console.log(0 in a); // true (อาร์เรย์นี้มีอินเด็กซ์ 0) -console.log(5 in a); // false (อาร์เรย์ไม่มีอินเด็กซ์ 5) -console.log("1" in a); // true (อาร์เรย์นี้มีอินเด็กซ์ 1) -console.log("length" in a); // true (อาร์เรย์จะมี length เป็นพร็อพเพอร์ตี้) -``` - -```js -var parent = {x:1}; -var obj = Object.create(parent); -console.log("x" in obj); // true -``` - -## ประโยคคำสั่ง for …in -```js -function Font() { - this.color = "red"; - this.size = 200; -} -var coordinate = {x: 1, y: 1, z: 1} -Font.prototype = Object.create(coordinate); -Font.prototype.myFunction = function(){}; -var font = new Font(); -font[1] = "fontValue"; -for(var prop in font) { -console.log(prop); -} -/* แสดงผลลัพธ์เป็น -"1" -"color " -"size " -"myFunction" -"x " -"y " -"z " */ -``` - -```js -function Font() { - this.color = "red"; - this.size = 200; -} -var coordinate = {x: 1, y: 1, z: 1} -Font.prototype = Object.create(coordinate); -Font.prototype.myFunction = function(){}; -var font = new Font(); -font[1] = "fontValue"; -for(var prop in font) { - if(font.hasOwnProperty( prop ) ) { // เข้าถึงคีย์ที่อยู่ในอ็อบเจ็กต์เท่านั้น - console.log("font." + prop, "=", font[prop]); -} -} -/* แสดงผลลัพธ์เป็น -"font.1 = fontValue" -"font.color = red" -"font.size = 200" */ -``` - -## Object.preventExtensions() กับ Object.isExtensions() -```js -var obj1 = {}; -var obj2 = Object.preventExtensions(obj1); -console.log(obj1 === obj2); // true - -obj1.a = 1; // จะเพิกเฉย แต่ถ้าอยู่ในโหมดสตริคท์จะเกิด TypeError - -var obj3 = Object.create(obj1); -console.log(obj3); // {} -obj3.a = 1; // เพิ่มพร็อพเพอร์ตี้เข้าไปได้ -console.log(Object.getPrototypeOf(obj3) === obj1); // true -``` - -```js -var obj = {}; -console.log(Object.isExtensible(obj)); // true -Object.preventExtensions(obj); -console.log(Object.isExtensible(obj)); // false -``` - -## Object -```js -var obj1 = new Object(1234); -var obj2 = new Object(); // อ็อบเจ็กต์ว่าง -var obj3 = new Object(undefined); // อ็อบเจ็กต์ว่าง -var obj4 = new Object(null); // อ็อบเจ็กต์ว่าง -var obj5 = Object(1); // เรียกแบบฟังก์ชั่น (ค่าอากิวเมนต์เป็นอะไรก็ได้) -console.log(typeof obj5); // "object" -``` - -## Boolean -```js -var obj = new Boolean(true); -console.log(typeof obj); // "object" -var b = Boolean(true); -console.log(typeof b); // "boolean" -console.log(b); // true -console.log(obj == b); // true -console.log(obj === b); // false -``` - -```js -var false1 = Boolean(); // false -var false2 = Boolean(false); // false -var false3 = Boolean(0); // false -var false4 = Boolean(-0); // false -var false5 = Boolean(null); // false -var false6 = Boolean(''); // false -var false7 = Boolean(NaN); // false -var false8 = Boolean(undefined); // false -var true1 = Boolean(true); // true -var true2 = Boolean("Hi"); // true -var true3 = Boolean("true"); // true -var true4 = Boolean("false"); // true -var true5 = Boolean([]); // true -var true6 = Boolean({}); // true -var true7 = Boolean(true4); // true -``` - -## Number -```js -var obj = new Number(1); -console.log(typeof obj); // "object" -var n1 = Number("1"); // รับค่าเป็นสตริงก็ได้เช่นกัน -var n2 = Number("a"); // "a" ไม่ใช่ตัวเลข จึงทำให้ตัวแปร n2 มีค่าเป็น NaN -console.log(typeof n1, typeof n2); // "number number" -console.log(n1, n2); // 1 NaN -console.log(obj == n1); // true -console.log(obj === n1); // false -``` - -## String -```js -var str1 = new String("MyString"); -console.log(typeof str1); // "object" -console.log(str1 === "MyString"); // false -var str2 = String("MyString"); -console.log(typeof str2); // "string" -console.log(str2 === "MyString"); // true -console.log(str1 == str2); // true -``` - -## Array -```js -var array1 = Array( 3 ); // เรียกแบบฟังก์ชั่น -console.log(array1.length); // 3 -console.log(array1); // [ <3 empty slots> ] -var array2 = new Array(3); -console.log(array2.length); // 3 -console.log(array2); // [ <3 empty slots> ] -``` - -## เมธอดของสตริง -```js -console.log( " Learning JavaScript ".trim() ); // แสดงผลเป็น "Learning JavaScript" -``` - -```js -console.log( "foo_foo_foo_".indexOf("foo") ); // 0 -console.log("foo_foo_foo_".lastIndexOf("foo") ); // 8 -``` - -```js -"use strict"; -var str = "MyString"; -console.log(str.length) // 8 -str.length = 0; // TypeError (ถ้าไม่ใช่โหมดสตริคท์จะไม่เกิด error แต่จะเพิกเฉย) -str.func = function(){}; // TypeError (ถ้าไม่ใช่โหมดสตริคท์จะไม่เกิด error แต่จะเพิกเฉย) -``` - -## เมธอดของอาร์เรย์ -```js -var array = ["a","b","c","d"]; -var str = array.join("->"); -console.log(str); // "a->b->c->d" -``` - -```js -var array = []; -array.push("a","b","c","d"); // เพิ่มสมาชิกกี่ตัวก็ได้ -console.log(array); // ["a", "b", "c", "d"] -console.log(array.pop()); // "d" -console.log(array); // ["a", "b", "c"] -``` - -```js -var array = ["a","b","c","d"]; -console.log(array.indexOf("c" )); // 2 -console.log(array.indexOf("e" )); // -1 -``` - -```js -var array = ["a","b","c","d"]; -var result = array.some( function matcher(value, index, arrayObj){ - // value คือค่าสมาชิกของอาร์เรย์ - // index คืออินเด็กซ์ของอาร์เรย์ - // arrayObj คือ ["a", "b", "c", "d"] - return value == "c"; -} ); -console.log(result) // true -``` - -```js -var array = ["a","b","c","d"]; -array.forEach(function (value, index, arrayObj) { - // value คือค่าสมาชิกของอาร์เรย์ - // index คืออินเด็กซ์ของอาร์เรย์ - // arrayObj คือ ["a", "b", "c", "d"] - console.log("a[", index, "] = ", value); -}); -/* แสดงผลลัพธ์ -"a[ 0 ] = a" -"a[ 1 ] = b" -"a[ 2 ] = c" -"a[ 3 ] = d" */ -``` - -## Regular Expression -```js -var myRegex = new RegExp("Hello+"); -console.log(myRegex.toString()); // /Hello+/ -``` - -```js -var myRegex = /Hello+/; -// ข้อความ regex ก็คืออ็อบเจ็กต์ตัวหนึ่ง -// จึงสามารถแชร์ใช้งานเมธอด RegExp.prototype.toString() -console.log(/Hello+/.toString()); // /Hello+/ -``` - -## เมธอด test() กับ exec() -```js -console.log( /Hello+/.test("Hellooooo") ); // true -console.log( (new RegExp("Hello+")).test("Hellooooo") ); // true -``` - -```js -var result = /(foo).(bar)/.exec("0123foo_bar"); -console.log(result.index); // 4 -console.log(result[0]); // "foo_bar" -console.log(result[1]); // "foo" -console.log(result[2]); // "bar" -console.log(result.input); // "0123foo_bar" -``` - -## สตริงกับ regex -```js -console.log("012Hellooooo".search(/Hello+/)); // 3 -var result = "Hellooooo Hello".match(/Hello+/g); -console.log(result); // ["Hellooooo", "Hello"] -console.log(result.length); // 2 - -var str = "Hellooooo".replace(/Hello+/,"Bye"); -console.log(str); // "Bye" -var split = "1,2,3".split(/,/); -console.log(split); // ["1", "2", "3"] -``` - -## ค่าแฟล็ก -```js -var regex = /Hello+/gi; -console.log(regex.lastIndex); // 0 -console.log(regex.source); // "Hello+" -console.log(regex.global); // true -console.log(regex.ignoreCase); // true -console.log(regex.multiline); // false -``` - -```js -var myRegex = /Hello+/g; -var str = "01Hello Hellooo89"; -var result = myRegex.exec(str); // ค้นหาครั้งแรก -console.log(result[0]); // "Hello" -console.log(myRegex.lastIndex); // 7 -myRegex.exec(str); // ค้นหาครั้งที่ 2 -console.log(result[0]); // "Hello" -console.log(myRegex.lastIndex); // 15 -myRegex.exec(str); // ค้นหาครั้งที่ 3 -console.log(result[0]); // "Hello" -console.log(myRegex.lastIndex); // 0 -myRegex.exec(str); // ค้นหาครั้งที่ 4 -console.log(result[0]); // "Hello" -console.log(myRegex.lastIndex); // 7 -``` - -```js -console.log( /Hello+/g === /Hello+/g); // false -/Hello+/g.exec("Hello Hello Hello"); // บรรทัด 2 -console.log(/Hello+/g.lastIndex); // 0 -- บรรทัด 3 -``` - -## ตัวอย่างการใช้ regex -```js -var myRegex = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i; -console.log(myRegex.test("xxxxxx.yyyyyy_zzzzz@abc.com")); // true -``` - -```js -var myRegex = /^(\-?|\+?)\d*$/; -console.log(myRegex.test("-987")); // true -``` - diff --git a/examples_book/Chapter5.md b/examples_book/Chapter5.md deleted file mode 100644 index 239655f..0000000 --- a/examples_book/Chapter5.md +++ /dev/null @@ -1,297 +0,0 @@ -# บทที่ 5 ฟีเจอร์ใหม่ของตัวเลข สตริง และ regex - -## การแปลงสตริงเป็นตัวเลข -```js -console.log(Number.parseInt("15")); // 15 -console.log(Number.parseFloat("123.5")); // 123.5 -console.log(Number.parseInt("abc")); // NaN (แปลงเป็นเลขจำนวนเต็มไม่ได้) -console.log(Number.parseFloat("abc")); // NaN (แปลงเป็นเลขทศนิยมไม่ได้) -``` - -```js -console.log(parseInt("15")); // 15 -console.log(parseFloat("123.5")); // 123.5 -console.log(typeof window.parseInt); // "function" (บนเว็บบราวเซอร์) -console.log(typeof window.parseFloat); // "function" (บนเว็บบราวเซอร์) -//console.log(typeof global.parseInt); // "function" (บน Node.js) -//console.log(typeof global.parseFloat); // "function" (บน Node.js) -``` - -## เลขฐาน 2 ฐาน 8 และฐาน 16 -```js -console.log(Number("100")); // 100 -console.log(Number("0b111")); // 7 -console.log(Number("0o45")); // 37 -console.log(Number("0x17")); // 23 -// ใช้ Number.parseInt() -console.log(Number.parseInt("100")); // 100 -console.log(Number.parseInt("0b111")); // 0 -console.log(Number.parseInt("0o45")); // 0 -console.log(Number.parseInt("0x17")); // 23 -``` - -## เมธอด Number.isFinite() กับ Number.isNaN() -```js -console.log(isFinite(2560)); // true -console.log(isFinite("2560")); // true -console.log(Number.isFinite(2560)); // true -console.log(Number.isFinite("2560")); // false -console.log(isNaN(NaN)); // true -console.log(isNaN("NaN")); // true -console.log(Number.isNaN(NaN) ); // true -console.log(Number.isNaN("NaN")); // false -console.log(typeof window.isFinite); // "function" (บนเว็บบราวเซอร์) -console.log(typeof window.isNaN); // "function" (บนเว็บบราวเซอร์) -//console.log(typeof global.isFinite); // "function" (บน Node.js) -//console.log(typeof global.isNaN); // "function" (บน Node.js) -``` - -## เมธอด Number.isInteger() กับ Number.isSafeInteger() -```js -console.log(Number.isInteger(34.6)); // false -console.log(Number.isInteger(34.0)); // true (เขียนเป็นเลข 34.0 ก็จริง แต่จะถูกจัดเก็บเป็นเลข 34) -console.log(Number.isInteger(34)); // true -``` - -```js -console.log(Math.pow(2,53)); // 9007199254740992 -console.log(Math.pow(2,53) + 1); // 9007199254740992 -- บรรทัด 2 -console.log(Math.pow(2,53) + 2); // 9007199254740994 -- บรรทัด 3 -console.log(Math.pow(2,53) + 3); // 9007199254740996 -- บรรทัด 4 -console.log(Math.pow(2,53) * 100); // 900719925474099200 -- บรรทัด 5 -``` - -```js -var upper = Number.MAX_SAFE_INTEGER; -console.log(Number.isSafeInteger(upper)); // true -console.log(Number.isInteger(upper)); // true -var outside_upper = upper + 1; -console.log(Number.isSafeInteger(outside_upper)); // false -console.log(Number.isInteger(outside_upper)); // true -var lower = Number.MIN_SAFE_INTEGER; -console.log(Number.isSafeInteger(lower)); // true -console.log(Number.isInteger(lower)); // true -var outside_lower = lower - 1; -console.log(Number.isSafeInteger(outside_lower)); // false -console.log(Number.isInteger(outside_lower)); // true -``` - -## ค่าคงที่ Number.EPSILON -```js -console.log(Number.EPSILON); // 2.220446049250313e-16 -``` - -## เมธอด repeat() -```js -console.log( "JavaScript".repeat(3) ); // "JavaScriptJavaScriptJavaScript" -``` - -## เมธอด startsWith(), endsWith(), กับ includes() -```js -console.log("JavaScript".startsWith("Java")); // true (มีคำว่า "Java" อยู่ตำแหน่งแรก) -console.log("JavaScript".startsWith("world")); // false (ไม่มีคำว่า "world" อยู่ตำแหน่งแรก) -console.log("JavaScript".endsWith("Script")); // true (มีคำว่า "Script" อยู่ตำแหน่งสุดท้าย) -console.log("JavaScript".endsWith("Hello")); // false (ไม่มีคำว่า "Hello" อยู่ตำแหน่งสุดท้าย) -console.log("JavaScript".includes("va")); // true (มีคำว่า "va" อยู่ในสตริง) -console.log("JavaScript".includes("same")); // false (ไม่มีคำว่า "same" อยู่ในสตริง) -``` - -```js -console.log("JavaScript".startsWith("ri", 6)); // true -console.log("JavaScript".endsWith("va", 4)); // true -console.log("JavaScript".includes("ri", 2)); // true -``` - -## ยูนิโคด -```js -var a = "ABC"; -console.log(a.length); // 3 -console.log(a.codePointAt(0)); // 65 -console.log(a.codePointAt(1)); // 66 -console.log(a.codePointAt(2)); // 67 -console.log(a.codePointAt(3)); // undefined -``` - -```js -var thai = "กขค"; -console.log(thai.length); // 3 -console.log(thai.codePointAt(0).toString(16)); // e01 -console.log(thai.codePointAt(1).toString(16)); // e02 -console.log(thai.codePointAt(2).toString(16)); // e04 -``` - -```js -console.log(String.fromCodePoint(42)); // "*" -console.log(String.fromCodePoint(65, 66, 67)); // "ABC" -console.log(String.fromCodePoint(0xe01, 0xe02, 0xe04) ); // "กขค" -// สามารถส่งค่าอากิวเมนต์เป็นสตริงที่เขียนด้วยตัวเลข ก็สามารถทำได้เช่นกัน -console.log(String.fromCodePoint("97", "98", "99")); // "abc" -console.log(String.fromCodePoint("0xe07", "0xe08", "0xe09") ); // "งจฉ" -//String.fromCodePoint('_'); // RangeError -//String.fromCodePoint(Infinity); // RangeError -//String.fromCodePoint(-1); // RangeError -//String.fromCodePoint(NaN); // RangeError -``` - -```js -console.log("\u{e01}"); // "ก" -console.log("\u{e01}\u{e02}\u{e04}ABC"); // "กขคABC" -``` - -```js -console.log("\u0e01"); // "ก" -``` - -```js -console.log("\u20BB7"); // " 7" -console.log("\u{20BB7}"); // "𠮷" -``` - -```js -console.log("a".length); // 1 -console.log("ก".length); // 1 -var char = "𤭢"; // "\u{24b62}" -- เป็นอักษรกลุ่ม CJK -console.log(char.length); // 2 -``` - -## การตั้งชื่อด้วยตัวอักษรพิเศษยูนิโคด -```js -var \u{e01} = 100; // จะเหมือนเขียน var ก = 100 -console.log(\u{e01}); // 100 -console.log("\u{e01}"); // "ก" -var ข = "JavaScript"; -console.log(ข); // "JavaScript" -``` - -```js -var \u0e01 = 100; // จะเหมือนเขียน var ก = 100 -console.log(\u0e01); // 100 -``` - -## เมธอด normalize() -```js -function toCodePoint(str){ // ฟังก์ชั่นแสดงค่า code point ของสตริงออกทางหน้าคอนโซล - var concat = ""; - for(var i = 0; i < str.length; i++ ){ - concat += "0x" + str.codePointAt(i).toString(16) + " "; - } - console.log(concat); -} - -// U+1E9B: LATIN SMALL LETTER LONG S WITH DOT ABOVE -// U+0373: GREEK SMALL LETTER ARCHAIC SAMPI -var str = "\u{1E9B}\u{0373}"; -var s1= str.normalize("NFC"); -toCodePoint(s1); // 0x1e9b 0x373 - -var s2= str.normalize("NFD"); -toCodePoint(s2); // 0x17f 0x307 0x373 - -var s3 = str.normalize("NFKC"); -toCodePoint(s3); // 0x1e61 0x373 - -var s4 = str.normalize("NFKD"); -toCodePoint(s4); // 0x73 0x307 0x373 -``` - -## แฟล็ก u -```js -var str = "𠮷"; -console.log(str.length); // 2 -console.log(/^.$/.test(str)); // false -``` - -```js -var str = "𠮷"; -console.log(/^.$/u.test(str)); // true -``` - -```js -var result1 = "𠮷กขคง𤭢".match(/[\s\S]/gu); -console.log(result1.length); // 6 - -// ถ้าไม่ใช้แฟล็ก u จะนับตัวอักษรผิด -var result2 = "𠮷กขคง𤭢".match(/[\s\S]/g); -console.log(result2.length); // 8 -``` - -## แฟล็ก y -```js -var str = "foo1_foo2_foo3"; // สตริงที่จะค้นหา -var regex = /foo\d_?/; // ไม่มีแฟล็ก -var regexG = /foo\d_?/g; // แฟล็ก g -var regexY = /foo\d_?/y; // แฟลก y -var result = regex.exec(str); -var resultG = regexG.exec(str); -var resultY = regexY.exec(str); -console.log(result[0]); // "foo1_" -console.log(resultG[0]); // "foo1_" -console.log(resultY[0]); // "foo1_" -console.log(regex.lastIndex); // 0 -console.log(regexG.lastIndex); // 5 -console.log(regexY.lastIndex); // 5 -``` - -```js -result = regex.exec(str); -resultG = regexG.exec(str), -resultY = regexY.exec(str); -console.log(result[0]); // "foo1_" -console.log(resultG[0]); // "foo2_" -console.log(resultY[0]); // "foo2_" -console.log(regex.lastIndex); // 0 -console.log(regexG.lastIndex); // 10 -console.log(regexY.lastIndex); // 10 -``` - -```js -var str = "foo1_foo2_foo3"; -var regex = /foo\d_?/; -var regexG = /foo\d_?/g; // แฟล็ก g -var regexY = /foo\d_?/y; // แฟลก y - -regex.lastIndex = 1; -regexG.lastIndex = 1; -regexY.lastIndex = 1; -var result = regex.exec(str); -var resultG = regexG.exec(str); -var resultY = regexY.exec(str); - -console.log(result[0]); // "foo1_" -console.log(resultG[0]); // "foo2_" -console.log(resultY); // มีค่าเป็น null เพราะค้นหาไม่เจอข้อความ -``` - -```js -var myRegex = /foo+/y; -console.log(myRegex.sticky); // true -myRegex.sticky = 1; // TypeError ไม่สามารถแก้ไขค่าได้ มีไว้อ่านอย่างเดียว -``` - -## RegExp -```js -var myRegex = /foo/g; -var regex2 = new RegExp(myRegex); -console.log(regex2.test("foo")); // true -console.log(myRegex === regex2); // false -``` - -```js -var myRegex = /foo/i; -var regex2 = new RegExp(myRegex, "g"); -console.log(myRegex.test("FOO")); // true (ไม่สนใจตัวพิมพ์ใหญ่พิมพ์เล็ก) -console.log(regex2.test("FOO")); // false -``` - -```js -var myRegex = new RegExp("foo", "y"); // จะเสมือนเขียน var myRegex = /foo/y; -console.log(myRegex.exec("foo_abc")); // ["foo"] -``` - -## พร็อพเพอร์ตี้ flags -```js -var myRegex = /foo/i; -console.log(myRegex.source); // "foo" -console.log(myRegex.flags); // "i" -``` - diff --git a/examples_book/Chapter6.md b/examples_book/Chapter6.md deleted file mode 100644 index 9f8a140..0000000 --- a/examples_book/Chapter6.md +++ /dev/null @@ -1,478 +0,0 @@ - -# บทที่ 6 การประกาศตัวแปร และการกำหนดค่า - -## การประกาศตัวแปรแบบ let -```js -function calculate(num){ - if (num > 10) { - let value = num*10; // ประกาศตัวแปรแบบ let - // ซอร์สโค้ดส่วนที่เหลือ - console.log(value); // มองเห็นตัวแปร value - } else { - // มองไม่เห็นตัวแปร value - } - // มองไม่เห็นตัวแปร value -} -``` - -## การใช้ตัวแปรแบบ var เปรียบเทียบกับ let -```js -var a = 1; -console.log(a); // 1 -{ - var a = 2; - console.log(a); // 2 -} -console.log(a); // 2 -``` - -```js -var a = 1; -console.log(a); // 1 -{ - let a = 2; - console.log(a); // 2 -} -console.log(a); // 1 -``` - -## var กับ let ในประโยควนลูป -```js -for(var i=0; i < 10; i++) { // วนลูป 10 ครั้ง -// ซอร์สโค้ด -} -// สามารถเข้าถึงตัวแปร i ที่ตอนนี้มีค่าเป็น 10 ได้ -console.log(i); // 10 -``` - -```js -for(let i=0; i < 10; i++) { // วนลูป 10 ครั้ง -// ซอร์สโค้ด -} -console.log(i); // จะเกิด ReferenceError เพราะมองไม่เห็นตัวแปร i -``` - -```js -var array = []; -for(var i=0; i < 5; i++){ - // เพิ่มฟังก์ชั่นเข้าไป เพื่อให้เป็นสมาชิกของอาร์เรย์ - array.push( function () { console.log(i) } ); -} -array.forEach( function(printLog) { - // เรียกสมาชิกของอาร์เรย์ที่เป็นฟังก์ชั่น ให้ทำงาน - printLog(); // จะแสดงค่าของตัวแปร i เป็นเลข 5 ทั้งหมดห้ารอบด้วยกัน -}); -/*แสดงผลลัพธ์เป็น -5 -5 -5 -5 -5 -*/ -``` - -```js -var array = []; -for(var i=0; i< 5; i++){ - array.push( - function(item) { - // รีเทิร์นฟังก์ชั่นออกไป เพื่อให้เป็นสมาชิกของอาร์เรย์ - return function() { console.log(item); } ; - }( i ) // ใช้เทคนิค IIFE -); -} -array.forEach( function(printLog) { - printLog(); // รอบแรกแสดงค่าเป็น 0 รอบที่สองเป็น 1 รอบที่สามเป็น 2 …จนถึงรอบที่ห้าจะแสดงค่าเป็น 4 -}); -/*แสดงผลลัพธ์เป็น -0 -1 -2 -3 -4 -*/ -``` - -```js -var array = []; -for(let i=0; i<5; i++){ // ประกาศตัวแปร i แบบ let - array.push( function () { console.log(i); } ); -} -array.forEach(function(printLog){ - printLog(); // รอบแรกแสดงค่าเป็น 0 รอบที่สองเป็น 1 รอบที่สามเป็น 2 …จนถึงรอบที่ห้าจะแสดงค่าเป็น 4 -}); -/*แสดงผลลัพธ์เป็น -0 -1 -2 -3 -4 -*/ -``` - -## สรุปขอบเขตการมองเห็นตัวแปรแบบ let -```js -console.log(count); // undefined หรือเกิด ReferenceError (ขึ้นอยู่กับจาวาสคริปต์เอ็นจิ้น) -let count = 89; // จะมองเห็นตัวแปร count ตั้งแต่จุดนี้เป็นต้นไป -if(true){ -console.log(count); // 89 -} -``` - -```js -var count = 89; -let count = 12; // เกิด error เพราะประกาศชื่อตัวแปรซ้ำกัน -``` - -```js -var count = 89; -if(true) { -let count = 12; // จะไม่เกิด error -// บรรทัดต่อจากนี้ไป สามารถมองเห็นตัวแปร count ที่ประกาศแบบ let เท่านั้น -console.log(count); // 12 -} -// มองเห็นและเข้าถึงตัวแปร count ที่อยู่นอกบล็อกของ if -console.log(count); // 89 -``` - -## ตัวแปรค่าคงที่ -```js -const MAX_COUNT = 100; // ประกาศถูกต้องตามไวยากรณ์ -const MAX_VALUE; // เกิด error เพราะไม่ได้กำหนดค่าตั้งต้นให้แต่แรก -const MESSAGE = "Hello"; // ประกาศถูกต้องตามไวยากรณ์ -MESSAGE = "Bye"; // เกิด error เพราะไปแก้ไขตัวแปรค่าคงที่ภายหลังประกาศใช้งานแล้ว ซึ่งจะทำไม่ได้ -``` - -```js -console.log(count); // undefined หรือเกิด ReferenceError (ขึ้นอยู่กับจาวาสคริปต์เอ็นจิ้น) -const count = 89; // จะมองเห็นตัวแปร count ตั้งแต่จุดนี้เป็นต้นไป -if(true){ -console.log(count); // 89 -} -``` - -```js -var message = "foo"; -let count = 100; -// ประกาศตัวแปรค่าคงที่ -const message = "bar"; // เกิด error เพราะประกาศตัวแปรชื่อซ้ำกัน -const count = 1; // เกิด error เพราะประกาศตัวแปรชื่อซ้ำกัน -``` - -```js -var message = "foo"; -let count = 100; -if(true) { - const message = "bar"; -const count = 1; -// มองเห็นตัวแปร message และ count ที่ประกาศเป็นค่าคงที่เท่านั้น -console.log(message); // "bar" -console.log(count); // 1 -} -// มองเห็นและเข้าถึง message และ count ที่อยู่นอกบล็อกของ if -console.log(message); // "foo" -console.log(count); // 100 -``` - -## ข้อควรระวังเกี่ยวกับตัวแปรค่าคงที่ -```js -const obj = { - value: 100 -}; -obj.value = 1; // สามารถแก้ไขค่าพร็อพเพอร์ตี้ภายในอ็อบเจ็กต์ได้ -console.log(obj.value); // 1 -obj = 10; // จะเกิด error เพราะแก้ไขตัวแปรค่าคงที่ไม่ได้ -``` - -```js -for (const i=0; i < 10; i++) { // จะ error เพราะนิพจน์ i++ ได้ไปแก้ไขตัวแปร i ซึ่งเป็นค่าคงที่ มันจะทำไม่ได้ -// ซอร์สโค้ด -} -``` - -```js -let obj = { - key1: true, - key2: true, -}; -for (const key in obj) { - console.log(key); -} -/*แสดงผลลัพธ์เป็น -"key1" -"key2" -*/ -``` - -```js -for (const value of [1, 2]) { - console.log(value); -} -/*แสดงผลลัพธ์เป็น -1 -2 -*/ -``` - -## ดีสตรัคเตอร์ริ่ง -```js -let f = { - color: "red" -,size: "200" -,icon: "small" -,style: "normal" -, lang: "thai" -} ; -// การแกะข้อมูลภายในอ็อบเจ็กต์ เพื่อไปกำหนดค่าให้กับตัวแปรทีละตัว จะดูยุ่งยากมาก -let color = f.color, size = f.size, icon = f.icon, style=f.style, lang= f.lang; -console.log(color, size, icon, style, lang); // "red 200 small normal thai" -``` - -```js -let f = ["red", "200", "small", "normal", "thai"]; -// การนำข้อมูลจากอาร์เรย์ เพื่อไปกำหนดค่าให้กับตัวแปรทีละตัว จะดูยุ่งยากมาก -let color = f[0], size = f[1], icon = f[2], style =f[3], lang=f[4]; -console.log(color, size, icon, style, lang); // "red 200 small normal thai" -``` - -## ดีสตรัคเตอร์ริ่งจากอ็อบเจ็กต์ -```js -let font = { - color: "red" -,size: 200 -} ; -let fontColor = font.color, fontSize = font.size; -``` - -```js -let font = { - color: "red" -,size: 200 -} ; -// กำหนดค่าให้กับตัวแปร ด้วยวิธีดีสตรัคเตอร์ริ่ง -// จะเสมือนประกาศแบบนี้ -// let fontColor = font.color, fontSize = font.size; -let {color: fontColor, size: fontSize } = font; -// จะประกาศตัวแปรเป็นแบบ var หรือ const ก็ทำได้เช่นกัน -// var {color: fontColor, size: fontSize } = font; -// const {color: fontColor, size: fontSize } = font; -console.log(fontColor); // "red" จะมีค่าเท่ากับ font.color -console.log(fontSize); // 200 จะมีค่าเท่ากับ font.size -``` - -```js -let font = { - color: "red" -,size: 200 -}; -// จะเสมือนประกาศแบบนี้ -// let color = font.color, size = font.size; -let {color, size} = font; // กำหนดค่าให้กับตัวแปร ด้วยวิธีดีสตรัคเตอร์ริ่งแบบย่อ -console.log(color); // "red -console.log(size); // 200 -``` - -```js -let font = { - color: "red" -,size: 200 -}; -let { color, size, style } = font; -console.log(color); // "red" -console.log(size); // 200 -console.log(style); // undefined -``` - -## อ็อบเจ็กต์ซ้อนอ็อบเจ็กต์ -```js -let font = { - color: "red" -,size: 200 -,text : { - name: "thai" -} -} ; -// จะเสมือนประกาศแบบนี้ -// let color = font.color, size = font.size, name = font.text.name; -let {color, size, text: {name} } = font; -console.log(color, size, name); // "red 200 thai" -``` - -## ดีสตรัคเตอร์ริ่งจากอาร์เรย์ -```js -let font = [ "red", "bold", "thai"]; -// กำหนดค่าให้กับตัวแปร ด้วยวิธีดีสตรัคเตอร์ริ่ง -// จะเสมือนประกาศตัวแปรแบบนี้ -// let color = font[0], style = font[1]; -let [ color, style] = font; -// จะประกาศตัวแปรเป็นแบบ var หรือ const ก็ทำได้เช่นกัน -// var [color, style] = font; -// const [color, style] = font; -console.log(color); // "red" จะมีค่าเท่ากับ font[0] -console.log(style); // "bold" จะมีค่าเท่ากับ font[1] -``` - -```js -let font = [ "red", "bold", "thai"]; -let [ , style , ] = font; -console.log(style); // "bold" -``` - -## อาร์เรย์ซ้อนอาร์เรย์ -```js -let font = [ "red", ["200", "thai"], "bold"]; -let [ color, [size, lang], style, option] = font; -console.log(color); // "red" -console.log(color === font[0]); // true -console.log(size); // "200" -console.log(size === font[1][0]); // true -console.log(lang); // "thai" -console.log(lang === font[1][1]); // true -console.log(style); // "bold" -console.log(style === font[2]); // true -console.log(option); // undefined -``` - -```js -let font = [ "red", ["200", "thai"], "bold"]; -let [ color, option , style] = font; -console.log(color, style); // "red bold" -console.log(option[0]); // "200" -console.log(option[1]); // "thai" -console.log(option === font[1]); // true (เพราะมันอ้างอิงไปที่อาร์เรย์ตำแหน่งเดียวกัน) -``` - -## ข้อควรรู้เพิ่มเติมของวิธีดีสตรัคเตอร์ริ่ง -```js -let action = { - save: true -,undo: false -}; -let save, undo; -{save, undo} = action; // เกิด error -``` - -```js -let action = { - save: true -,undo: false -}; -let save, undo; -({save, undo} = action); // ใส่วงเล็บครอบทั้งประโยคจะไม่เกิด error -console.log(save, undo); // true false -``` - -```js -let font = [ "red", "bold"]; -let color, style; -[color, style] = font; // ไม่เกิด error -console.log(color, style); // "red bold" -``` - -## การระบุค่าดีฟอลต์ให้กับตัวแปร -```js -let { color, size = 200 } = {color:"red"} -console.log(color) // "red" -console.log(size) // 200 -``` - -```js -let [ , ,lang = "thai"] = [] -console.log(lang); // "thai" -``` - -## ข้อมูลผสมระหว่างอ็อบเจ็กต์และอาร์เรย์ -```js -let action = { - save : "success", - undo : "none", - option : ["move", "stop", "slow"] -}; -// กำหนดค่าให้กับตัวแปรด้วยวิธีดีสตรัคเตอร์ริ่ง -let {save, undo, option: [ moveOption, stopOption]} = action; -console.log(save, undo, moveOption, stopOption); // "success none move stop" -``` - -```js -let action = { - save : "succes", - undo : "none", - option : ["move", "stop", "slow"] -}; - -let {save, undo, option} = action; // บรรทัด a -console.log(save, undo); // "success none" -console.log(option[0]); // "move" -console.log(option[1]); // "stop" -console.log(option[2]); // "slow" -console.log(option === action.option); // true (เพราะมันอ้างอิงไปที่อาร์เรย์ตำแหน่งเดียวกัน) -``` - -## การสลับข้อมูล -```js -let a = 1, b =2 -let temp = a; // temp เป็นตัวแปรชั่วคราวที่ใช้เก็บค่าของ a เอาไว้ก่อน -a = b; -b = temp; -console.log(a); // 2 -console.log(b); // 1 -``` - -```js -let a = 1, b =2; -[b , a] = [a , b]; // ดีสตรัคเตอร์ริ่งจากอาร์เรย์ -console.log(a); // 2 -console.log(b); // 1 -``` - -```js -let a = 1, b = 2, c = 3, d = 4; -console.log(a, b, c, d); // 1 2 3 4 -[d, c, b ,a] = [a, b, c, d]; // ดีสตรัคเตอร์ริ่งจากอาร์เรย์ -console.log(a, b, c, d); // 4 3 2 1 -``` - - -## รับค่าจากฟังก์ชั่น -```js -function myFunctin(){ - return {a:1 ,b: 2}; -} -let {a, b} = myFunctin(); -console.log(a, b); // 1, 2 -``` - -```js -function myFunctin(){ - return [1, 2] ; -} -let [a, b] = myFunctin(); -console.log(a, b); // 1 2 -``` - -## ข้อมูล JSON -```js -// เป็นข้อมูล JSON ซึ่งเขียนด้วยเทมเพลตสตริง (บทที่ 8) -let jsonText = `{ - "file": "index.html", - "menu": [ - {"value": "New", "onclick": "createDoc"}, - {"value": "Open", "onclick": "openDoc"} - ] -}`; - -let jsonObj = JSON.parse(jsonText); // อ็อบเจ็กต์ที่ใช้เป็นตัวแทนของ JSON -console.log(jsonObj); -/* แสดงผลลัพธ์เป็น -{ file: "index.html", - menu: - [ { value: "New", onclick: "createDoc" }, - { value: "Open", onclick: "openDoc" } ] } -*/ -let {file, menu:[ menu1, menu2] } = jsonObj; -console.log(file); // "index.html" -console.log(menu1.value); // "New" -console.log(menu1.onclick); // "createDoc" -console.log(menu2.value); // "Open" -console.log(menu2.onclick); // "openDoc" -``` diff --git a/examples_book/Chapter7.md b/examples_book/Chapter7.md deleted file mode 100644 index d5f92ac..0000000 --- a/examples_book/Chapter7.md +++ /dev/null @@ -1,890 +0,0 @@ - -# บทที่ 7 ฟังก์ชั่น - -## นิพจน์ฟังก์ชั่น -```js -let f1 = function (){ - // ซอร์สโค้ด -} -const f2 = function (){ - // ซอร์สโค้ด -} -var f3 = function (){ - // ซอร์สโค้ด -} - -f1(); -f2(); -f3(); -``` - -## พารามิเตอร์แบบดีฟอลต์ -```js -function sendMessage(ipaddress, message = "Hello" , callback = function() {}) { -// ถ้าไม่ส่งค่าอากิวเมนต์มาให้พารามิเตอร์ message จะมีค่าเป็น "Hello" -/* ถ้าไม่ส่งค่าอากิวเมนต์มาให้พารามิเตอร์ callback จะได้เป็นฟังก์ชั่นว่างที่ไม่ได้ทำงานอะไรเลย - แต่จะรีเทิร์นค่าเป็น undefined */ -console.log(ipaddress, message, callback() ); -} -``` - -```js -// ใช้ค่าดีฟอลต์ของพารามิเตอร์ message กับ callback -sendMessage("127.0.0.1"); // "127.0.0.1 Hello undefined" -// ใช้ค่าดีฟอลต์ของพารามิเตอร์ callback -sendMessage("127.0.0.1", "Good bye!"); // "127.0.0.1 Good bye! undefined" -// ไม่ได้ใช้ค่าดีฟอลต์อะไรเลย -sendMessage("127.0.0.1", "Good bye!", function() { - return "toDoSomething"; -}); -``` - -```js -function sendMessage(ipaddress, message = "Hello", callback = function() {}) { -console.log(ipaddress, message, callback() ); -} -// จะใช้ค่าดีฟอลต์ของพารามิเตอร์ callback เพียงตัวเดียวเท่านั้น -sendMessage("127.0.0.1", null, undefined); // "127.0.0.1 null undefined" -``` - -## ตำแหน่งการวางพารามิเตอร์แบบดีฟอลต์ -```js -function sendMessage(ipaddress, message = "Hello" , callback) { -console.log(ipaddress, message, typeof callback); -} -``` - -```js -// จะใช้ค่าดีฟอลต์ของพารามิเตอร์ message เพียงตัวเดียวเท่านั้น -sendMessage("127.0.0.1"); // "127.0.0.1 Hello undefined" -```js - -```js -sendMessage("127.0.0.1", "Hello", function() {}); // "127.0.0.1 Hello function" -// พารามิเตอร์ message จะได้ค่าเป็น null -sendMessage("127.0.0.1",null, function() {}); // "127.0.0.1 null function" -// พารามิเตอร์ message จะใช้ค่าดีฟอลต์ -sendMessage("127.0.0.1", undefined, function() {}); // "127.0.0.1 Hello function" -``` - -```js -function sendMessage(ipaddress, callback, message = "Hello") { -console.log(ipaddress, message, typeof callback); -} -sendMessage("127.0.0.1", function() {}); // "127.0.0.1 Hello function" -``` - -## ประโยชน์ของพารามิเตอร์แบบดีฟอลต์ -```js -function sendMessage(ipaddress, message , callback) { -message = message || "Hello"; -callback = callback || function() { return "callback";}; -console.log(ipaddress, message, callback()); -} -// message กับ callback จะใช้ค่าดีฟลอต์ -sendMessage("127.0.0.1"); // "127.0.0.1 Hello callback" -sendMessage("127.0.0.1", 0, null); // "127.0.0.1 Hello callback" -sendMessage("127.0.0.1", NaN, ''); // "127.0.0.1 Hello callback" -``` - -```js -let value = 1; -function getMessage() { - return "My_message_" + (value++); -} -function createCallback() { - return function() { - return "callback"; - }; -} -function sendMessage(message = getMessage(), callback = createCallback() ) { -console.log(message, callback()); -} -sendMessage(); // "My_message_1 callback" -sendMessage(); // "My_message_2 callback" -``` - -```js -function add(value) { - return value + 10; -} - -function calculate(a, b = add(a), c = a * b) { - console.log(a, b, c) ; -} - -calculate(1,1,1); // 1 1 1 -calculate(10); // 10 20 200 -calculate(20); // 20 30 600 -calculate(30); // 30 40 1200 -``` - -## พารามิเตอร์แบบเรสต์ -```js -function iterateItem(item){ - console.log(item); // แสดงค่าอากิวเมนต์ตัวแรกออกมาก่อน - let result = 0; -let len = arguments.length; - for(let i=1; i { -return value; -}; -// เรียกใช้ฟังก์ชั่นได้เหมือนปกติธรรมดา -console.log(arrowFunc(122)); // 122 -``` - -### ตัวอย่าง 2 -```js -// เหมือนในตัวอย่างที่ 1 แต่การเขียนจะสั้นและกระชับกว่า -// ไม่ต้องมีเครื่องหมายปีกกาครอบบอดี้ฟังก์ชั่น รวมทั้งไม่ต้องเขียนประโยคคำสั่ง return -let arrowFunc = value => value; -console.log(arrowFunc(122)); // 122 -/* จะเสมือนเขียนเป็น -let arrowFunc = function(value){ - return value; -};*/ -let arrowFunc2 = value => console.log(value); -arrowFunc2(122); // 122 -/* จะเสมือนเขียนเป็น -let arrowFunc2 = function(value){ - return console.log(value); -};*/ -``` - -### ตัวอย่าง 3 -```js -// ฟังก์ชั่นลูกศรที่ไม่มีการประกาศพารามิเตอร์อะไรเลย -let arrowFunc = () => 122; -console.log(arrowFunc()); // 122 -/* จะเสมือนเขียนเป็น -let arrowFunc = function(){ - return 122; -};*/ -``` - -### ตัวอย่าง 4 -```js -// ฟังก์ชั่นลูกศรที่ไม่มีพารามิเตอร์ และตัวบอดี้ของฟังก์ชั่นก็ว่างเปล่า -let arrowFunc = () => {}; -arrowFunc(); -// จะเสมือนเขียนเป็น -// var arrowFunc = function(){}; -``` - -### ตัวอย่าง 5 -```js -// ใส่เครื่องหมายวงเล็บ เพื่อครอบอ็อบเจ็กต์ที่ถูกรีเทิร์นออกมา -let getFont = () => ( { color: "red", size: 200 } ); -console.log(getFont()); // {color: "red", size: 200} -/* จะเสมือนเขียนเป็น -let getFont = function(){ - return {color: "red", size: 200}; -};*/ -``` - -### ตัวอย่าง 6 -```js -// มีวงเล็บครอบพารามิเตอร์เอาไว้ -let sum = (val1, val2, val3) => val1 + val2 + val3; -console.log(sum(1,2,3)); // 6 -/* จะเสมือนเขียนเป็น -let sum = function(val1, val2, val3){ - return val1 + val2 +val3; -};*/ -``` - -### ตัวอย่าง 7 -```js -// ฟังก์ชั่นลูกศรที่ใช้พารามิเตอร์แบบดีฟอลต์ -let sum = (val1 = 1, val2 = 2, val3 = 3) => val1 + val2 + val3; -console.log(sum()); // 6 -/* จะเสมือนเขียนเป็น -let sum = function(val1 = 1, val2 = 2, val3 = 3){ - return val1 + val2 +val3; -};*/ -``` - - -### ตัวอย่าง 8 -```js -// ฟังก์ชั่นลูกศรที่ใช้พารามิเตอร์แบบเรสต์ -let max = (...value) => Math.max(...value); -console.log(max(1, 2, 3, 6)); // 6 -/* จะเสมือนเขียน -let max = function(...value){ // พารามิเตอร์แบบเรสต์ - return Math.max(...value); // โอเปอเรเตอร์สเปรด -};*/ -``` - - -## ฟังก์ชั่นลูกศรต่างจากฟังก์ชั่นธรรมดาอย่างไร -```js -let arrowFunc = value => value; -console.log(typeof arrowFunc); // "function" -console.log(arrowFunc instanceof Function); // true -``` - - - -```js -let arrowFunc = () => {}; -console.log(arrowFunc.name); // จะแสดงชื่อ "arrowFunc" (ขึ้นอยู่กับจาวาสคริปต์เอ็นจิ้น) -``` - -## อ็อบเจ็กต์ arguments ในฟังก์ชั่นลูกศร -```js -// เขียนแบบฟังก์ชั่นลูกศร -var arrowFunc = () => console.log(arguments); // ไม่สามารถใช้อ็อบเจ็กต์ arguments ได้ -arrowFunc(1, 2, 3); // [] (ถ้าใช้ Traceur กับ Babel ตัว arguments จะเป็นอาร์เรย์ว่าง) -// เขียนแบบฟังก์ชั่นธรรมดา -var arrowFunc2 = function(){ - return console.log(arguments); // ฟังก์ชั่นธรรมดาสามารถใช้อ็อบเจ็กต์ arguments ได้ตามปกติ -}; -arrowFunc2(1, 2, 3); // [1, 2, 3] -``` - -```js -function createArrow(value) { -// ฟังก์ชั่นลูกศรสามารถเรียกใช้ arguments ของฟังก์ชั่น createArrow() - return () => arguments[0]; -} -let arrowFunc = createArrow(1); -console.log(arrowFunc()) // 1 -``` - -## เทคนิคการเขียน IIFE -```js -// เทคนิค IIFE กับฟังก์ชั่นลูกศร -var printItem = ( -(item) => function() { console.log(item); } -)("IIFE"); -printItem(); // "IIFE" -/* จะเสมือนใช้เทคนิค IIFE กับฟังก์ชั่นธรรมดา -var printItem = function(item) { - return function() { console.log(item); }; -}("IIFE"); -printItem(); // "IIFE" -*/ -``` - -## ฟังก์ชั่นคอลแบ็ค -```js -var array = [1, 2, 3, 4]; -array.forEach( (value, index, arr) => arr[index] = value *2 ); -console.log(array); // [2, 4, 6, 8] -/* จะเสมือนใช้ฟังก์ชั่นคอลแบ็คแบบปกติ -var array = [1, 2, 3, 4]; -array.forEach(function(value, index, arr) { - return arr[index] = value * 2; -}); -console.log(array); // [2, 4, 6, 8] -*/ -``` - -## การใช้ this ในฟังก์ชั่นลูกศร -```js - - - - - - - - - - - - - - - -``` - - -```js -let obj ={ - value : "JavaScript" - ,printValue: function(){ - console.log("Message:", this.value); // this จะชี้ไปยังอ็อบเจ็กต์ obj - } - ,handle : function (){ - console.log("Press a button"); - } - ,init : function(){ - let element = document.querySelector("#b1"); // ปุ่ม "Try it" - element.addEventListener("click", function(event){ - this.handle(); // this จะชี้ไปยังอ็อบเจ็กต์ obj - }.bind(this), false); // บรรทัด a -- this จะชี้ไปยังไปอ็อบเจ็กต์ obj - //}.bind(obj), false); // จะใช้บรรทัดนี้ก็ได้ มีความหมายเหมือนกัน - } -}; // สิ้นสุดการประกาศอ็อบเจ็กต์ - -obj.printValue(); // "Message: JavaScript" -obj.init(); -``` - - -```js -let obj ={ - value : "JavaScript" - ,printValue: function(){ - console.log("Message:", this.value); // this จะชี้ไปยังอ็อบเจ็กต์ obj - } - ,handle : function (){ - console.log("Press a button"); - } - ,init : function(){ - let element = document.querySelector("#b1"); // ปุ่ม "Try it" - element.addEventListener("click", (event)=> this.handle()); //this จะชี้ไปยัง obj - } -}; // สิ้นสุดการประกาศอ็อบเจ็กต์ -obj.printValue(); // "Message: JavaScript" -obj.init(); -``` - - -## เมธอด apply(), call() และ bind() -```js -let sum = (val1, val2) => console.log(val1 + val2) ; -sum.apply(null, [5, 5] ); // 10 -sum.call(null, 5, 5); // 10 -let resultSum = sum.bind(null, 5, 5); -resultSum(); // 10 -``` - -```js -let objA = {value: "access objA"}; -let objB = { - value: "access objB" - ,myFunction() { - console.log("this.value in myFunction:", this.value); - // this ในฟังก์ชั่นลูกศร จะเห็นเหมือนกับที่ myFunction() มองเห็น - let arrowFunc = () => console.log("Arrow function:", this.value) ; - - let func = function(){ // this ในฟังก์ชั่นปกติ สามารถเปลี่ยนไปชี้อ็อบเจ็กตัวอื่นได้ - console.log("Normal function:", this.value); - } - arrowFunc.call(objA); // บรรทัด a –- ไม่สามารถเปลี่ยนค่า this ได้ - func.call(objA); // บรรทัด b -- สามารถเปลี่ยนค่า this ให้ชี้ไปยังอ็อบเจ็กต์ objA ได้ - } -} -objB.myFunction(); // บรรทัด c -/*แสดงผลลัพธ์ -"this.value in myFunction: access objB" -"Arrow function: access objB" -"Normal function: access objA" -*/ -objB.myFunction.call(objA); // บรรทัด d -/*แสดงผลลัพธ์ -"this.value in myFunction: access objA" -"Arrow function: access objA" -"Normal function: access objA" -*/ -``` - -## Tail call optimization -```js -function foo(a) { - return a; // บรรทัด a -} -function bar(b) { - let c = b + 100; - return foo(c); // บรรทัด b -} -console.log( bar(30) ); // บรรทัด c แสดงผลลัพธ์เป็น 130 -``` - -## ตำแหน่ง Tail call -### กรณีที่ 1 -```js -function foo() { - bar(); // เรียกฟังก์ชั่นแบบนี้จะไม่ใช่ตำแหน่งสุดท้าย - // ถ้าเขียนเป็น return bar(); จะเป็นการเรียกฟังก์ชั่นในตำแหน่งสุดท้าย -} -``` -จะเสมือนเขียน - -```js -function foo() { - bar(); - return undefined; -} -``` - -### กรณีที่ 2 -```js -function foo() { - return 1+ bar(); // เรียกฟังก์ชั่นแบบนี้จะไม่ใช่ตำแหน่งสุดท้าย -} -``` - -จะเสมือนเขียน - -```js -function foo() { - let result = bar(); - return 1 + result; -} - - -กรณีที่ 3 - -```js -function foo(condition) { - if(condition){ - return bar(); // บรรทัด a -- เรียกฟังก์ชั่นในตำแหน่งสุดท้าย - } else { - bar(); // บรรทัด b -- เรียกฟังก์ชั่นแบบนี้จะไม่ใช่ตำแหน่งสุดท้าย - } - } -``` - -## รีเคอร์ซีพ -```js -function factorial(value) { - if (value <= 0) { - return 1; - } else { - return value * factorial(value-1); // บรรทัด a -- ไม่ใช่การเรียกฟังก์ชั่นในตำแหน่งสุดท้าย - } - } -console.log(factorial(4)); // จะได้ค่าเป็น 24 เพราะ 4! = 4 x 3 x 2 x 1 = 24 -// จะเกิด RangeError เพราะ stack frame โตเกินไป จนใช้หน่วยความจำหมด -console.log(factorial(200000)); -``` - -```js -// ต้องประกาศเพื่อทำ TCO แต่ถ้าเขียนบน Traceur หรือ Babel ซอร์สโค้ดจะเป็นสตริคท์โหมดโดยอัตโนมัติ -"use strict"; -function factorial(value) { - return callFac(1, value); -} -function callFac(temp, val) { - if (val <= 1) { - return temp; - } else { - return callFac(temp * val, val-1); // บรรทัด a -- เรียกฟังก์ชั่นในตำแหน่งสุดท้าย - } -} -console.log(factorial(4)); // 24 -console.log(factorial(200000)); // infinity -``` - - -## นิพจน์อื่นที่เป็น Tail call -### กรณีที่ 1 -```js -let arrowFunc = param => param ? foo() : bar(); -``` - -จะเสมือนเขียน - -```js -let arrowFunc = param => { - if(param){ - return foo(); // เรียกฟังก์ชั่นในตำแหน่งสุดท้าย -}else{ - return bar(); // เรียกฟังก์ชั่นในตำแหน่งสุดท้าย -} -}; -``` - - -## กรณีที่ 2 -```js -let arrowFunc = () => (foo(), bar(), zoo()); -``` - -จะเสมือนเขียน - -```js -let arrowFunc = () => { - foo(); -bar(); - return zoo(); // เรียกฟังก์ชั่นในตำแหน่งสุดท้าย -}; -``` - -## กรณีที่ 3 -```js -let arrowFunc = () => foo() || bar(); -``` - -จะเสมือนเขียน - -```js -let arrowFunc = () => { - let temp = foo(); - if (temp) { - return temp; - } else { - return bar(); // เรียกฟังก์ชั่นในตำแหน่งสุดท้าย - } -}; -``` - -## กรณีที่ 4 -```js -let arrowFunc = () => foo() && bar(); -``` - - -```js -let arrowFunc = () => { - let temp = foo(); - if (!temp) { - return temp; - } else { - return bar(); // เรียกฟังก์ชั่นในตำแหน่งสุดท้าย - } -}; -``` diff --git a/examples_book/Chapter8.md b/examples_book/Chapter8.md deleted file mode 100644 index 8b13789..0000000 --- a/examples_book/Chapter8.md +++ /dev/null @@ -1 +0,0 @@ - diff --git a/examples_book/README.md b/examples_book/README.md index 92efc03..580d594 100644 --- a/examples_book/README.md +++ b/examples_book/README.md @@ -1,13 +1,35 @@ -# [โค้ดตัวอย่างจากหนังสือทั้งหมด]( https://www.se-ed.com/product/พัฒนาเว็บแอปพลิเคชั่นด้วย-JavaScript.aspx?no=9786160825394) -![cover_6.PNG](https://github.com/adminho/javascript/blob/master/images/cover_6.PNG) +# [โค้ดตัวอย่างจากหนังสือทั้งหมด]([https://www.mebmarket.com/web/index.php?action=BookDetails&data=YToyOntzOjc6InVzZXJfaWQiO3M6NzoiMTcyNTQ4MyI7czo3OiJib29rX2lkIjtzOjY6IjE1Njg1NCI7fQ) +![cover_new.PNG](https://github.com/adminho/javascript/blob/master/images/cover_new.png) -* [บทที่ 1](Chapter1.md) -* [บทที่ 2](Chapter2.md) -* [บทที่ 3](Chapter3.md) -* [บทที่ 4](Chapter4.md) -* [บทที่ 5](Chapter5.md) -* [บทที่ 6](Chapter6.md) -* [บทที่ 7](Chapter7.md) +[สามารถสั่งซื้อได้ที่เว็บ MEB (ขายเป็นอีบุ๊ก)](https://www.mebmarket.com/web/index.php?action=BookDetails&data=YToyOntzOjc6InVzZXJfaWQiO3M6NzoiMTcyNTQ4MyI7czo3OiJib29rX2lkIjtzOjY6IjE1Njg1NCI7fQ) +[ ส่วนเล่มที่วางขายใน shopee ตอนนี้เลิกทำแล้ว เพราะต้นทุนแพงเกินไปทำไม่ไหว](https://shopee.co.th/product/159315996/18595064435/) +* บทที่ 1 แนะนำจาวาสคริปต์ +* [บทที่ 2 รันจาวาสคริปต์อย่างง่าย](Chapter02.md) +* [บทที่ 3 ทบทวนมาตรฐานเก่า](Chapter03.md) +* [บทที่ 4 ทบทวนประโยคคำสั่งเบื้องต้น](Chapter04.md) +* [บทที่ 5 ทบทวนอ็อบเจ็กต์](Chapter05.md) +* [บทที่ 6 ทบทวน Regex](Chapter06.md) +* [บทที่ 7 ทบทวน HTML DOM](Chapter07.md) +* [บทที่ 8 ฟีเจอร์ใหม่ของตัวเลข สตริง และ regex](Chapter08.md) +* [บทที่ 9 การประกาศตัวแปร และการกำหนดค่า](Chapter09.md) +* [บทที่ 10 ฟังก์ชั่น](Chapter10.md) +* [บทที่ 11 เทมเพลตสตริง](Chapter11.md) +* [บทที่ 12 ซิมโบล](Chapter12.md) +* [บทที่ 13 ฟีเจอร์ใหม่ของอ็อบเจ็กต์](Chapter13.md) +* [บทที่ 14 คลาส](Chapter14.md) +* [บทที่ 15 คอลเลคชั่น](Chapter15.md) +* [บทที่ 16 อิเทอเรเตอร์ และเจนเนอเรเตอร์](Chapter16.md) +* [บทที่ 17 เมต้าโปรแกรมมิ่ง](Chapter17.md) +* [บทที่ 18 พรอมิส](Chapter18.md) +* [บทที่ 19 การใช้งาน async กับ await](Chapter19.md) +* [บทที่ 20 มอดูล](Chapter20.md) +* [ภาคผนวก ข](Appendix_B) +* [ภาคพนวก ง](Appendix_G.md) + +### หมายเหตุ [สามารถทดลองรันโค้ดตัวอย่างในหนังสือได้ (ควรเปิดบนคอม ไม่แนะนำให้เปิดบนมือถือ หรือแท็บเล็ต)](https://patanasongsivilai.com/myblog/main.php?id=756) + +#### ปล. เล่มนี้ตามรูปข้างล่างเนื้อหาเก่าแล้ว (ปัจจุบันไม่มีตีพิมพ์เพิ่ม) +![cover_big.jpg](https://github.com/adminho/javascript/blob/master/images/cover_big.jpg) diff --git a/examples_book/ajax.php b/examples_book/ajax.php new file mode 100644 index 0000000..a6ae76a --- /dev/null +++ b/examples_book/ajax.php @@ -0,0 +1,12 @@ + \ No newline at end of file diff --git a/examples_book/free_ebook/Readme.md b/examples_book/free_ebook/Readme.md new file mode 100644 index 0000000..c89ef47 --- /dev/null +++ b/examples_book/free_ebook/Readme.md @@ -0,0 +1,13 @@ +# เอกสารแจกฟรี (อ่านบนเว็บ MEB) + +* [ปูพื้นฐาน Node.js ฉบับย่อ เข้าใจ MongoDB]( +https://www.mebmarket.com/web/index.php?action=BookDetails&data=YToyOntzOjc6InVzZXJfaWQiO3M6NzoiMTcyNTQ4MyI7czo3OiJib29rX2lkIjtzOjY6IjI0OTQwOCI7fQ) + + + +* [พื้นฐาน React - ขออนุญาตแก้ไขลิงค์ รวมทั้งเนื้อหาในหนังสือ เดี่ยวเปิดให้อ่านสักประมาณต้นเดือน มกราคม 2567 ]() + +เลยให้ลิงก์ชั่วคราวไปก่อน +[อ่าน React ชั่วคราว](https://www.mebmarket.com/web/index.php?action=BookDetails&data=YToyOntzOjc6InVzZXJfaWQiO3M6NzoiMTcyNTQ4MyI7czo3OiJib29rX2lkIjtzOjY6IjI0ODU5NiI7fQ) + + diff --git a/examples_book/json.php b/examples_book/json.php new file mode 100644 index 0000000..13c071a --- /dev/null +++ b/examples_book/json.php @@ -0,0 +1,15 @@ +name = "Somchai"; +$myObj->age = 30; +$myObj->city = "Bangkok"; + +$myJSON = json_encode($myObj); + +echo $myJSON; +?> \ No newline at end of file diff --git a/images/chap01/quotes.png b/images/chap01/quotes.png index 1d6ec08..6d991aa 100644 Binary files a/images/chap01/quotes.png and b/images/chap01/quotes.png differ diff --git a/images/cover_new.png b/images/cover_new.png new file mode 100644 index 0000000..1c9883a Binary files /dev/null and b/images/cover_new.png differ