JavaScript class
JavaScript ไม่มี OOP ในลักษณะเดียวกับภาษา Java แต่จะใช้ลักษณะ Prototype-based
// Constructor
var MyObject = function(x){
this.x = x;
};
// Method
MyObject.prototype.getX = function(){
return this.x;
};
// การใช้งาน
var instance = new MyObject("Hello, world");
console.log(instance.getX());
// ไม่มี visibility
console.log(instance.x);
ข้อดีของระบบนี้คือสามารถที่จะต่อเติมคลาสได้ตลอดเวลา
MyObject.prototype.logX = function(){
console.log(this.x);
};
instance.logX();
เมื่อรันโค้ดนี้ต่อจากโค้ดที่แล้วจะเห็นว่า MyObject
ทุกตัวจะมีเมธอด logX
ขึ้นมาทันที การใช้งานอย่างหนึ่งของความสามารถนี้คือการเข้าไปแก้ไข object ของระบบให้มีความสามารถมากขึ้น ซึ่งใช้ในไลบรารีบางตัว เช่น PrototypeJS
// https://github.com/sstephenson/prototype/blob/master/src/prototype/lang/array.js#L222
Array.prototype.first = function(){
return this[0];
};
console.log(["a", "b", "c"].first());
(ทั้งนี้มีข้อถกเถียงกันว่าการแก้ไข object ระบบดีหรือไม่ เพราะถ้าใช้หลายๆ ไลบรารีอาจจะเกิดการชนกันได้ง่าย)
Other style of inheritance
บางทีอาจจะเห็นการเขียนคลาสในลักษณะนี้
var Person = function(name){
this.name = name;
this.sayName = function(){
console.log(this.name);
}
};
var instance = new Person("Madoka");
instance.sayName();
ปัญหาของวิธีนี้คือเมธอด sayName
จะถูกสร้างใหม่ทุกครั้งที่มีการสร้างคลาส Person
ทำให้ช้ากว่าวิธีอื่นๆ
Inheritance
เนื่องด้วยว่า JavaScript ไม่มีคลาสแท้ๆ จึงมีผู้คิดค้น Inheritance ใน JavaScript โดยอาศัยการ copy prototype
ของ object นั้นๆ มาใส่ในออพเจ็กท์ใหม่
วิธีนี้โค้ดค่อนข้างยาวพอสมควร ศึกษาได้จากบล็อคของ John Resig (ผู้พัฒนา jQuery)
var Being = Class.extend({});
Being.prototype.breath = function(){
};
var Human = Being.extend();
Human.prototype.eat = function(){
};
Human.prototype.breed = function(){
};
เพื่อความสะดวก ไลบรารีนี้ยอมให้เราส่งเมธอดไปในรูปแบบของ hash ได้
Hash
ใน JavaScript มีตัวแปรชนิดหนึ่งเรียกว่า Object แต่เพื่อไม่ให้งงขอเรียกว่า hash แล้วกัน
ทุกอย่างใน JavaScript เป็น object หมด รวมถึง primitive types ด้วย เช่นใช้ 2..toString()
ก็ได้ (สังเกตว่าการใช้เมธอดกับตัวเลขมี . สองครั้ง เพราะถ้ามีจุดเดียวมันจะนึกว่ากำลังจะระบุทศนิยม) แต่ในทีนี้เราจะมาพูดถึง object พื้นฐานกัน
Hash มีหน้าตาแบบนี้
var x = {
"key": "value",
"key2": "value2"
};
โดย key เป็น string และ value เป็นอะไรก็ได้ รวมถึง hash ซ้อน
เมื่อเรามี hash แล้วและรู้ key สามารถหาค่าได้
console.log(x.key);
console.log(x["key"]);
ทั้งสองแบบนี้ได้ผลลัพท์เหมือนกัน แบบแรกสั้นกว่า แบบหลังสามารถระบุ key ด้วยตัวแปรได้
Inheritance (cont)
กลับมาที่ไลบรารีของเรา เราสามารถสร้างคลาสแบบเดียวกันได้ด้วยโค้ดนี้
var Being = Class.extend({
breath: function(){
}
});
var Human = Being.extend({
eat: function(){
},
breed: function(){
}
});
นั่นคือ ใช้แฮชที่มีชื่อเป็นชื่อเมธอด และค่าเป็นฟังก์ชั่นนั่นเอง
ข้อสังเกตคือในแฮชจะต้องมีลูกน้ำคั่นระหว่างสมาชิกเสมอ ถ้าตกไปก็จะเกิด error ขึ้นได้ ข้อแนะนำคือเราสามารถทิ้งลูกน้ำเกินไว้ได้โดยไม่ถือเป็น error
var Human = Being.extend({
eat: function(){
},
breed: function(){
}, // ใส่ลูกน้ำทิ้งไว้เลย
});
JSHint
เนื่องจาก JavaScript นั้นไม่มีการตรวจสอบโค้ดทำให้อาจจะมีข้อผิดพลาดเกิดขึ้นได้ง่าย เราสามารถใช้โปรแกรม JSHint ช่วยตรวจได้
Online
สามารถใช้ JSHint แบบออนไลน์ได้ที่ jshint.com
Terminal
สามารถติดตั้ง JSHint ได้โดยติดตั้ง node.js ก่อน แล้วติดตั้งผ่าน npm
การติดตั้ง Node.JS (Debian/Ubuntu)
sudo apt-get install nodejs
การติดตั้ง Node.JS (Arch Linux)
sudo pacman -S nodejs
การติดตั้ง Node.JS (Mac)
ให้ติดตั้งโปรแกรม Homebrew ก่อน ซึ่งจำเป็นจะต้องติดตั้ง XCode ก่อน แล้วรันคำสั่ง
brew install nodejs
การติดตั้ง JSHint
sudo npm install -g jshint
สามารถรันได้ด้วยการสั่ง jshint file.js
Sublime Text
ต้องติดตั้งให้ใช้งานใน Terminal ได้ก่อนจึงจะใช้วิธีนี้ได้
สามารถติดตั้งปลั๊กอินได้ที่ Sublime-JSHint และเรียกใช้โดยการกดปุ่ม Ctrl/Cmd+Shift+J
ความสามารถ
JSHint สามารถเตือนเรื่องดังต่อไปนี้ได้ โดยสามารถเปิดปิดได้ตามต้องการ
- การตั้งชื่อตัวแปรต้องเป็น
variableName
หรือVARIABLE_NAME
เท่านั้น - บังคับให้ใส่ปีกกาเสมอ (เช่น หลัง if หรือ while)
- ห้ามไม่ให้แก้ไข
prototype
ของ object ที่มีอยู่แล้วในระบบ - ชื่อ Constructor ต้องขึ้นด้วยตัวใหญ่เสมอ
- ห้ามใช้
new
โดยไม่บันทึกผลลงตัวแปร - บังคับให้ใช้ single quotes หรือ double quotes อย่างใดอย่างหนึ่งเท่านั้น
- แจ้งเตือนเมื่อมีการใช้ตัวแปรที่ไม่มีการประกาศไว้ (ถ้ามาจากไลบรารีอื่นๆ ที่หัวไฟล์จะต้องประกาศ
/* globals A, B, C */
เพื่้อบอกว่าตัวแปร A, B, C มีการประกาศแล้วที่ไฟล์อื่น) - แจ้งเตือนเมื่อมีการประกาศตัวแปรแล้วไม่ได้ใช้