#JavaScript Coding Style
<tr>
<td>2013.08.08</td>
<td>1.1</td>
<td>
* 1.7.5 新增物件屬性風格說明<br/>
* 1.7.6 新增陣列元素風格說明<br/>
* 增加4.3函式參數風格說明<br/>
</td>
<td>Hank Kuo</td>
</tr>
<tr>
<td>2013.07.11</td>
<td>1.0</td>
<td>
* 1.3改為'建議' <br/>
* 增加1.6.3 全域變數說明 <br/>
* 1.7.2增加perseInt()使用規則說明<br/>
* 1.7.3增加簡易檢測變數與參數說明<br/>* 修改2.1多行注解規則<br/>
* 修改3.4 assign變數風格(=前後需空一格) <br/>
* 修改4.1變數宣告風格(每個變數都要有var) <br/>
* 新增4.1.1變數宣告位置說明<br/> * 4.2函式宣告新增匿名函式說明
</td>
<td>Hank Kuo</td>
</tr>
<tr>
<td>2013.07.03</td>
<td>1.0 beta2</td>
<td>
* 文件改用markdown撰寫, 增加變數, 函式, 運算符, 錯誤處理, 類型檢測等風格
</td>
<td>Hank Kuo</td>
</tr>
<tr>
<td>2012.12.06</td>
<td>1.0 beta1</td>
<td>
* 建立文件, 基本程式風格建立
</td>
<td>Mars Peng</td>
</tr>
時間 | 版本 | 說明 | 編輯人 |
---|---|---|---|
2013.11.25 | 1.11 | * 新增章節8:嚴格模式 | Hank Kuo |
#####程式碼的縮排採用四個空格字元
, 不可使用tab character
因為不同系統對於 tab character 的解釋不一致, 造成用不同編輯器打開文件看到的結果不一樣。
#####程式碼的結尾一定要加上分號(;)
JavaScript有自動分號插入(Automatic Semicolon Insertion, ASI)機制, 即使省略分號大部份情形下也可以正常工作, 但是 ASI 的分號插入機制非常複雜, 因此一定要加上分號, 避免發生下列情形.
// 原始程式碼
function getData() {
return
{
title: 'Hiiir JavaScript Coding Style',
author: 'Mars, Hank'
}
}
// 分析器會轉譯成
function getData() {
return;
{
title: 'Hiiir JavaScript Coding Style',
author: Mars, Hank'
};
}
這樣會導致 getData() 回傳值為 undefined.
callMyFunction(this, window, 'this is a string', 'another string', 123,
myObject);
// ASI機制會在某些條件下在行結束的位置插入分號, 因此總是將一個運算符號置於行尾, 可避免ASI插入錯誤的分號
if (isYear && isDate && day == 30 && isOldYear &&
isNoPlans) {
doSomething();
}
var result = someValue + anotherValue + myValue + hisValue + herValue +
defaultValue;
- 在每個條件判斷語句之前(if…else, for)添加空行
- 在方法(function)之間
- 在方法中的區域變數(local variable)和第一條語句之間
- 在多行或單行注解之前
- 在方法內的邏輯片段之間插入空行, 提高可讀性
function doSomething() {
var myArr,
anotherVal;
if (myArr.length > 0) {
for (var i, len=myArr.length; i < len; i++) {
// 單行注解
/*
*多行注解
*/
var logicSection = 10;
logicSection1 = logicSection + i;
logicSection2 = logicSection + i * 100;
}
}
}
function doAnotherThing() {
// doAnotherThing
}
- 用
小駝峰命名法
(Camel Case) - 命名前綴為
名詞
(Noun)
- 用
小駝峰命名法
(Camel Case) - 命名前綴為
動詞
(Verb)
表1.6.1 函數命名前綴詞參考表
動詞 | 含義 |
---|---|
can | 函數回傳一個布林值 |
has | 函數回傳一個布林值 |
is | 函數回傳一個布林値 |
get | 函數回傳一個非布林値 |
set | 函數用來保存一個值 |
var myData = 10;
function getPlusVal(val) {
return val + val;
}
- 用
大寫字母
和底線
來命名 - 必須宣告於檔案開頭,並且注解標示
// const
var MAX_COUNT = 10;
if (count < MAX_COUNT) {
doSomething();
}
- 用
小寫g
和底線
開頭,後面採用一般變數命名方式(小駝峰法) - 必須宣告於檔案開頭,並且注解標示
// global
var g_myVariable = 100;
將函式跟建構函式用不同的命名方法,可以幫助我們快速定位問題,
你會知道用大駝峰命名法命名的函數前面一定會有 new 關鍵字
, 進而避免函式誤用的情況.
function Person(name) {
this.name = name;
}
Person.prototype.sayName = function() {
alert(this.name);
}
var me = new Person('Hank');
- 使用
單引號('')
括住字串 - 多行字串用
字串連接符號(+)
連結 - 禁止使用建構函式(Constructor)
var myString = 'This is my long string,' +
'it is just a example.'
// 不好的做法: 使用建構函式
var myString = new String('Hank');
- 禁止使用
8進位
寫法 - 禁止使用建構函式(Constructor)
- 使用
parseInt()
將字串轉為整數時,一定要加上基數
參數
// 好的寫法
var num = 10;
// 不好的寫法: 使用8進位寫法
var num = 010;
// 不好的寫法: 使用建構函式
var num = new Number(10);
// 好的寫法,有加上基數參數,這行代表將字串'10'轉成10進位整數
var num = parseInt('10', 10);
// 不好的寫法,沒有加上基數參數
var num = parseInt('10');
- 不可沒有小數部分
- 不可沒有整數部分
// 整數
var count = 10;
// 浮點數
var price = 20.0;
var price = 20.00;
// 不好的做法: 使用建構函式
var price = new Number(20);
// 不好的做法: 浮點數沒有小數部分
var price = 20.;
// 不好的做法: 浮點數沒有整數部分
var price = .5;
- 布林値
true
跟false
所有字元必須小寫
, 因為其他混合大小寫形式的 true 跟 false 都不是布林値 - 禁止使用建構函式(Constructor)
// 布林値
var isBool = true;
// 不好的做法: 布林値字元有大寫
var isBool = True;
// 不好的做法: 使用建構函式
var isBool = new Boolean(true);
- null所有字元
必須小寫
, 因為其他混合大小寫形式的 null 都不是 null - 判斷物件是否為null, 請用
嚴格相等(===)
與嚴格不相等(!==)
符號
- 當做一個未來可能成為物件的變數的初始值
- 當函式的期望參數是物件時, 可當作參數傳入
- 當函式的期望回傳值為物件時, 可當作回傳值傳出
- 用 null 來檢測是否傳入了某個參數
- 用 null 來檢測變數
function doSomething(object) {
...
}
// 好的寫法:當做一個未來可能成為物件的變數的初始值
var person = null;
// 好的寫法:當函式的期望參數是物件時, 可當作參數傳入
doSomething(null);
// 好的寫法:當函式的期望回傳值為物件時, 可當作回傳值傳出
function getPerson() {
if (condition) {
return new Person('hank');
}
else {
return null;
}
}
// 不好的寫法: 用 null 來檢測是否傳入了某個參數
function doSomething(arg1, arg2) {
if (arg2 !== null) {
doSomething();
}
}
// 不好的寫法: 用 null 來檢測未初始化的變數
var person;
if (person !== null) {
doSomething();
}
#####簡易檢測變數與參數方法
- 直接用
if
判斷變數或參數 - 無論是
null
還是undefined
, 都會當作 false 處理 - 但是請注意,若變數的值為
0
, 也會當作 false 處理
var person;
var personNull = null;
var personZero = 0;
if (person) { // false
doSomething();
}
if (personNull) { // false
doSomething();
}
if (personZero) { // false
doSomething();
}
- 因為 undefined 常常會跟 typeof 返回值 'undefined' 混淆.
- 這樣可以確保只有在變數未宣告時 typeof 會回傳 "undefined"
- null == undefined // true, 但是這兩個值的用途大不相同
// 使用foo會有錯誤, 因為foo尚未聲明
var person;
alert(typeof person); // "undefined"
alert(typeof foo); // "undefined"
// 請用null作為可能成為物件的變數的初始值, 不要用undefined
var person = null;
// 不好的寫法
var person2 = undefined;
// null跟undefined的typeof回傳值不同, 這樣兩者就可以區分開了
alert(typeof person); // "object"
alert(typeof person2); // "undefined"
- 用物件實字(
{}
)(object literals)建立 Object 物件 - 物件屬性在冒號(
:
)後需空一格
// 好的寫法: 物件實字寫法
var person = {
name: 'hank',
age: 28,
skills: 'php, javascript'
};
// 不好的寫法: 物件屬性冒號後沒空一格
var person = {
name:'hank',
age:28,
};
// 不好的寫法: 使用Object()
var person = new Object();
person.name = 'hank';
person.age = '28';
person.skills = 'php, javascript';
- 用陣列實字(
[]
)(array literals)建立Array物件 - 陣列各元素逗號後面需
空一格
// 好的寫法: 使用陣列實字建立陣列
var colors = ['red', 'blue', 'yellow', 'green'];
// 不好的寫法: 使用Array()建立陣列
var colors = new Array('red', 'blue', 'yellow', 'green');
// 不好的寫法: 陣列各元素逗號後面沒空一格
var colors = ['red','blue','yellow','green'];
- 獨佔一行的注解, 在注解之前要有
一個空行
, 並跟下一行程式碼對齊
- 在程式碼尾部的注解, 需和程式碼之間
空一格(space)
, 注解+程式碼不得超過單行程式碼長度限制
if (condition) {
// 獨佔一行的注解, 在注解之前要有一個空行, 並跟下一行程式碼對齊
doSomething();
}
if (condition) {
doSomething(); // 在程式碼尾部的注解, 需和程式碼之間空一格(space)
}
- 多行注解之前要有
一個空行
, 星號之後要空一格
, 並跟下一行程式碼對齊
- 程式碼尾部的注解, 不要用多行注解
- 可用來
注解掉大段程式碼
或用來寫程式碼說明
if (condition) {
/*
* 多行注解之前要有一個空行,並跟下一行程式碼對齊,
* 程式碼尾部的注解,不要用多行注解
*/
doSomething();
/*
* 可用來`注解掉大段程式碼`,
* 或用來寫`程式碼說明`
*/
/*
if (condition) {
doSomething();
}
*/
- 當程式碼所提供的
信息不足以解釋其中的功能與含義
, 必須透過注解提供額外訊息時 - 當程式碼
難以理解
時 - 當程式碼可能被
誤認為是錯誤
時
// 改這個值, 系統就爆炸了(注解提供額外訊息)
var count = 10;
// 當程式碼難以理解時
/*
* 當兩個時間差小於1小時, 就顯示差了分鐘,
* 當兩個時間差小於1天, 就顯示差了幾小時,
* 當兩個時間差小於1週, 就顯示差了幾天,
* 如果這些條件都不成立, 就直接顯示日期
*/
if (diff < ONE_HOUR) {
output = Math.floor(diff / ONE_MIN) + ' m';
}
else if (diff < ONE_DAY) {
output = Math.floor(diff / ONE_HOUR) + ' hr';
}
else if (diff < ONE_WEEK) {
output = Math.floor(diff / ONE_DAY) + ' day';
}
else {
output = dateSplit[0][0] + '-' + dateSplit[0][1] + '-' + dateSplit[0][2];
}
// 當程式碼可能被誤認為是錯誤時
while (element && (element = element[axis])) { // 這邊是賦値操作無誤
if ((all || element[TAG_NAME]) && (!fn || fn(element)) {
return element;
}
}
- TODO: 說明
程式碼尚未完成
, 並寫出下一步要做的事情 - HACK: 說明
程式碼用了非正規途徑解決某些問題
, 要寫出為何使用hack的原因,這也表明未來可能有更好的解決方法 - XXX: 說明
程式碼是有問題
的要儘快修復 - FIXME: 說明
程式碼是有問題
的要儘快修復, 重要性僅次於XXX - REVIEW: 說明
程式碼如果要有任何的改動都需要進行評估與審核
// TODO: 區域函式add尚未完成,請實作
var myToolKit = {
add: function(val1, val2) {
}
};
/*
* HACK: 這邊有針對IE8做特別處理,不然會破版
* 計劃等IE8淘汰過後移除該部分
*/
function myFunc() {
// 程式碼
}
// XXX: 這個函式根本是壞的!
function myFunc() {
// 程式碼
}
// FIXME: 如果輸入null會回傳錯誤,請修復
function myFunc() {
// 程式碼
}
// REVIEW: 我覺得應該有更好的做法
function myFunc() {
// 程式碼
}
- if…else
- for
- while
- do…while…
- try…catch…finally
- 將左大括號放置在語句中第一句程式碼的尾端
- 在左圓括號之前和右圓括號之後各空一格
- 第二個以後的條件判斷關鍵字開頭要放置在上一個右大括弧後空一格
if (codition) {
doSomething();
} else if(condition) {
doSomethingElse();
} else {
doSomethingElseElse();
}
- 每條 case 語句相對於
switch
關鍵字縮排4個空格 - 第二條 case 語句開始,每條 case 語句前後各有
一個空行
- 一般情況, case 末尾一定要
搭配 break 關鍵字,除非為連續執行 case(fall through)
- 若特殊情況下,需要連續執行 case, 必須在在 case 之間加上注解
/* fall through */
- ES2015, 如果 case 裡有變數, 請用大括弧避免變數污染
- 無論任何情況, 都
不要省略 default
- 不要將已知情況寫至 default, 例如有3個狀態 a,b,c. 而 b,c 為同一個邏輯,
不要只寫 a case 而把 b,c 寫至 default
switch (condition) {
case 1:
doSomething();
break;
case 2:
case 3:
doSomething();
break;
case 4:
doSomething();
/* fall through */
case 5: {
let myVar = 'hello';
doSomething(myVar);
break;
}
default:
break;
}
- 宣告區若有多個變數, 每個變數
各自用 var 宣告並換行
- 變數之間若是
賦値符號(=)或運算符號(<, >, ==, !=)
, 符號左右需空一格 - 在運算區塊
不要直接使用陣列 length 屬性
, 應該在宣告區將 length 儲存起來使用 - 避免使用
continue
關鍵字, 可用條件判斷式取代
var values = [1, 2, 3, 4, 5, 6, 7];
var i;
var len;
for (i = 0, len = values.length; i < len; i++) {
if (i != 2) {
doSomething(i);
}
}
// 同等於
for (i = 0; len = values.length; i < len; i++) {
if (i == 2) {
continue;
}
doSomething(i);
}
- 總是使用
hasOwnProperty()
來為 for-in 過濾出物件屬性, 除非想查找原型(protoype)屬性 - 若要查找原型(prototype)屬性, 必需要加上注解說明
- for-in 是用來遍歷物件屬性, 禁止使用 for-in 來遍歷陣列(Array)
var person = {
name: 'hank',
age: 28
},
prop;
// 總是使用 `hasOwnProperty()` 來為 for-in 過濾出物件屬性, 除非想查找原型(protoype)屬性
for (prop in person) {
if (person.hasOwnProperty(prop)) {
alert('Property name is ' + prop);
alert('Property value is ' + person[prop]);
}
}
// 若要查找原型(prototype)屬性, 必需要加上注解說明
for (prop in person) { // 包含對prototype的遍歷
alert('Property name is ' + prop);
alert('Property value is ' + person[prop]);
}
// 禁止使用 for-in 來遍歷陣列(Array)
var values = [1, 2, 3, 4, 5];
var i;
for (i in values) {
doSomething(i);
}
- 無論是全域變數或區域變數進行宣告, 變數名稱前面一定要加上
var
關鍵字 - 總是將變數宣告作為函式內的第一條語句
- 多個變數宣告時, 每個變數前面加上
var
關鍵字, 變數需對齊, 沒有初始值的變數要放在變數語句的尾部
var g_someValue = 100;
function doSomething() {
var values = [1, 2, 3, 4, 5, 6, 7];
var baseValue = 10;
var result = value + value;
var i;
var len;
for (i = 0, len = values.length; i < len; i++) {
result += i;
}
}
- 變數必須宣告在使用它的
程式碼上方
- 若有多個程式碼區塊使用到同一變數, 宣告在最前面程式碼區塊的上方
function doSomething() {
// 變數可給程式碼區塊 1, 2, 3 使用
var myVar1 = 1;
var myVar2 = 2;
var sum1;
// 程式碼區塊 1
sum1 = myVar1 + myVar2;
// 變數可給程式碼區塊 2, 3 使用
var myVar3 = 3;
var myVar4 = 4;
var sum2;
// 程式碼區塊 2
sum2 = myVar1 + myVar2 + myVar3;
// 變數可給程式碼區塊 3 使用
var myVar5 = 5;
var myVar6 = 6;
var sum3;
// 程式碼區塊 3
sum3 = myVar1 + myVar3 + myVar5 + myVar6;
}
- 函式名稱和左括號之間沒有空格, 函數右括號
(
與左大括號{
要空一格 - 函式參數之間用
逗號加空一格
分開, 參數與函式左右括號之間沒有空格
- 立即函式要用一對原括號包裹起來
- 函式內部的
局部函式要緊接著變數宣告之後宣告
, 並且使用函式表達式
- 如果函式
不會重複使用
, 可使用匿名函式
函式宣告不可
在條件判斷語句中使用函式表達式可以
在條件判斷語句中使用
// 函式宣告
function doSomething(arr, someVal, isFunc) {
var value = 10,
var i;
var len;
// 局部函式要緊接著變數宣告之後宣告, 並且使用函式表達式
var doSomethingElse = function(obj) {
// 程式碼邏輯
}
for (i = 0, len = arr.length; i < len; i++) {
doSomethingElse(arr[i]);
}
}
// 函式表達式
var doSomething = function(arr, someVal, isFunc) {
// 程式碼邏輯
}
// 立即函式宣告
var value = (function(value) {
return inputValue + 10;
}(inputValue));
// 匿名函式
setTimeout(function() {
doSomething();
}, 100);
// 匿名立即函式(宣告並立即執行)
(function(value) {
doSomething();
}(inputValue));
// 不好的寫法: 函數名稱與左括號之間有空格
var myFunc = function() {
doSomething();
}
// 不好的寫法: 不要在條件判斷語句中宣告函式
if (condition) {
function doSomething() {
alert('hello');
}
}
else {
function doSomething() {
alert('Yeh!');
}
}
- callback function一律放在參數列的
最後面
- 如果參數有
兩個以上callback function
, 請使用函式表達式
,而避免直接使用匿名函式 - 若有大量參數需傳遞給函式, 或是參數數量可能會有多次變動, 請使用
物件
封裝參數
// 好的寫法: callback function 放在參數列的最後面
myFunc(val1, val2, function() {
doSomething();
});
// 好的寫法: 參數有兩個以上callback function, 使用函式表達式
var callback1 = function() {
doSomething1();
}
var callback2 = function() {
doSomething2();
}
myFunc(val1, val2, callback1, callback2);
// 好的寫法: 若有大量參數需傳遞給函式, 或是參數數量可能會有多次變動, 使用物件封裝參數
var myFunc = function(profile) {
alert(profile.name);
alert(profile.gender);
alert(profile.phone);
alert(profile.age);
alert(profile.job);
}
myFunc({
name: 'hank',
gender: 'male',
phone: '0227119900'
age: 28,
job: engineer
});
// 不好的寫法: callback function 沒放在參數列的最後面
myFunc(function() {
doSomething();
}, val1, val2);
// 不好的寫法: 參數有兩個以上 callback function, 使用匿名函式
myFunc(val1, val2, function() {
doSomething1();
}, function() {
doSomething2();
});
// 不好的寫法, 大量參數沒用物件封裝
var myFunc = function(name, gender, phone, age, job) {
doSomething();
}
禁止使用 eval()
, 除了別無他法的情況下, eval() 容易產生 XSS 安全性問題- setInterval() 跟 setTimeout() 的內容
禁止使用字串, 要用函數
- 禁止使用建構式(Constructor)
// 好的寫法
setInterval(function(){
alert('Hello');
}, 500);
// 不好的寫法: 內容使用字串
setTimeout('alert("Hello");', 500);
// 不好的寫法: 使用建構式
var func = new Function('alert("Hello!");');
- 不可將
'use strict' 關鍵字用在全局作用域中
,以避免所有程式碼都用嚴格模式運作, 導致其他未照嚴格模式規範的程式碼發生錯誤
// 好的寫法
function doSomething() {
'use strict'
// 程式碼邏輯
}
// 好的寫法
(function() {
'use strict'
function doSomething() {
// 程式碼邏輯
}
function doSomethingElse() {
// 程式碼邏輯
}
}());
// 不好的寫法
'use strict'
function doSomething() {
// 程式碼邏輯
}
- 如果(=)右側是有比較語句的表達式, 要用圓括號()包裹
// 好的寫法
var flag = (i < count);
// 不好的寫法
var flag = i < count;
- 檢測類型總是使用(
===
)與(!==
),請參考7.1 - 若無特別需要避免有隱含型別轉換的狀況,可用(
==
)和(!=
);
var checkValue = 2;
var myValue = '2';
// 使用==或!=會自動將字串'2'轉為數字2
if (checkValue == myValue) { // true
...
}
if (checkValue === myValue) { // false
...
}
- 僅用在
賦値操作
, 不可當作if使用 - (?)與(:)符號
左右需個空一格
// 好的寫法
var value = condition ? value1 : value2;
// 不好的寫法
condition ? myFun1() : myFun2();
- Error: 基本錯誤物件
- EvalError:
eval()發生錯誤
時拋出 - RangeError:
數值超出範圍
時拋出(經常發生) - ReferenceError:
找不到物件
時拋出 - SyntaxError:
語法錯誤的程式碼傳入eval()
時拋出 - TypeError:
變數被賦値意外的類型或呼叫不存在的方法
時(經常發生) - URIError: encodeURI(), encodeURIComponent(), decodeURI() 或decodeURIComponent()等傳遞
非法格式
時拋出
new eval(); // EvalError,FireFox 4+和IE8會拋出TypeError
var items = Array(-100); // RangeError
var obj = myObj; // ReferenceError
eval('a ++ b'); // SyntaxError
var a = new '1234'; // TypeError
alert('name' in true); //TypeError
Function.prototype.toString.call('name') //TypeError
function CustomError(message) {
this.name = 'myCustomError';
this.message = message;
}
CustomError.prototype = new Error();
function doSomething(obj) {
if (obj === null) {
throw new CustomError('Oops!');
}
}
try {
doSomething(null);
}
catch(error) {
alert(error.name + ' ' + error.message); // myCustomError Oops!
}
throw
負責引發異常- 總是使用
Error
物件或其子類別物件拋出錯誤
// 好的寫法
throw new Error('Something went wrong!');
// 不好的寫法
throw new 'Something went wrong!';
throw true;
throw 123;
throw new Date();
catch
負責捕捉異常catch
區塊不要留空- 可用
finally
區塊,無論程式碼有無錯誤發生,finally區塊內的程式碼總是會被執行 - 小心使用
finally
區塊,只要有finally
區塊, try 跟 catch 區塊的return 都會被忽略
// 好的寫法
try {
doSomething();
}
catch(error) {
handleError(ex);
}
// 好的寫法
try {
// 程式碼
}
catch(error) {
// 捕捉錯誤
}
finally {
// 無論try區塊有無錯誤發生,finally區塊總是會被執行
}
// 不好的寫法: catch區塊留空
try {
doSomething();
}
catch(error) {
}
// try-catch, throw應用範例
function getLastElement(array) {
if (array.length > 0) {
return array[array.length - 1];
}
else {
throw new Error('Cannot not take the last element of an empty array.');
}
}
try {
getLastElement([]);
}
catch(error) {
alert(error.message);
}
- String, Number, Boolean, undefined用
typeof
關鍵字 - null用
===
和!==
,禁止使用typeof檢測null
, 因為會返回'object' - typeof String 回傳 'string'
- typeof Number 回傳 'number'
- typeof Boolean 回傳 'boolean'
- typeof undefined 回傳 'undefined'
var myString = 'hello world!';
var myNumber = 10;
var myBoolean = true;
var myNull = null;
// String
if (typeof myString === 'string') {
doSomething();
}
// Number
if (typeof myNumber === 'number') {
doSomething();
}
// Boolean
if (typeof myBoolean === 'boolean' && myBoolean) {
doSomething();
}
// undefined
if (typeof myUndefined === 'undefined') {
doSomething();
}
// null
if (myNull === null) {
doSomething();
}
- 用 'instanceof' 關鍵字
- 'instanceof' 不僅會檢查物件的建構函式(Constuctor), 也會檢查該物件的原型(Prototype), 因此避免使用
myObject instanceof Object
來檢測物件是否屬於某個特定物件
var myDate = new Date();
var myError = new Error('help!');
var myPerson = new Person('Hank');
function Person(name) {
this.name = name;
}
// 好的做法
if (myDate instanceof Date) { // true
doSomething();
}
// 好的做法
if (myError instanceof Error) { // true
doSomething();
}
// 好的做法
if (myPerson instanceof Person) { // true
doSomething();
}
// 不好的做法: 用 Object 檢測物件
if (myPerson instanceof Object) { // true
}
- 使用
typeof
關鍵字 - 注意,在 IE8 以下的瀏覽器,使用
typeof
檢測 DOM 的函式(例如 document.getElementById),會回傳object
而不是function
, 因此要用in
來檢測
function myFunc() {
// 程式碼
}
if (typeof myFunc === 'function') { // true
doSomething();
}
// IE8以下瀏覽器DOM函式檢測方法
if ('getElementById' in document) {
doSomething();
}
- 可用 Kangax 解決方案
- ECMAScript5 可用 Array.isArray()
- IE9+, Chrome, FireFox 4+, Safari 5+, Opera 10.5+ 都有 Array.isArray()
var myArr = [1, 2, 3, 4, 5];
// Kangax 解決方案
function isArray(value) {
return Object.prototype.toString.call(value) === '[object Array]';
}
if (isArray[myArr]) {
doSomething();
}
- 用
in
來檢測 - 如果只想檢查物件某個屬性是否存在(略過 prototype 屬性),可使用
hasOwnProperty()
方法 - IE8 以下的 DOM 物件並非繼承自 Object, 因此不包含 hasOwnProperty()這個方法, 因此請使用
in
運算符
var person = {
name: 'hank',
age: 28
};
// 用 in 來檢測
if ('name' in person) {
doSomething();
}
// 對於非 DOM 物件的屬性檢測方法
if (person.hasOwnProperty('name')) {
doSomething();
}
// 如果不確定是否為 DOM 物件
if ('hasOwnProperty' in person && person.hasOwnProperty('name')) {
doSomething();
}
- 可以在檔案、程式或函式的開頭加上
"use strict"
; 宣告 Strict 模式 - Strict 模式宣告的範圍取決於宣告本身的內容. 如果在全域內容中 (在函式範圍之外) 宣告, 則程式中的所有程式碼都會套用 Strict 模式; 如果是在函式宣告,則函式中的所有程式碼都會套用 Strict 模式
// 在'use strict'以下的所有程式碼都會套用 Strict 模式
'use strict';
function testFunction(){
var testvar = 4;
return testvar;
}
// ReferenceError: assignment to undeclared variable testvar
testvar = 5;
/*
只有 testFunction 內的程式碼才會套用 Strict 模式.
函式以外的變數宣告不會導致語法錯誤, 但是函式中的宣告則會.
*/
function testFunction(){
'use strict';
// ReferenceError: assignment to undeclared variable testvar
testvar = 4;
return testvar;
}
testvar = 5;
- 不可使用未宣告的變數
'use strict';
// ReferenceError: assignment to undeclared variable testvar
testvar = 4;
- 不可將值寫入到唯讀屬性
'use strict';
var testObj = Object.defineProperties({}, {
prop1: {
value: 10,
writable: false // by default
},
prop2: {
get: function () {
}
}
});
// TypeError: "prop1" is read-only
testObj.prop1 = 20;
testObj.prop2 = 30;
- 當物件設為無法擴充時, 為物件新增屬性會發生錯誤
'use strict';
var testObj = new Object();
Object.preventExtensions(testObj);
// TypeError: ({}) is not extensible
testObj.name = "Bob";
- 無法刪除宣告的變數(沒嚴格模式時, 也不會刪除, 但不會拋出錯誤)
- 無法刪除 configurable 設為 false 的屬性
var testvar = 15;
function testFunc() {};
// SyntaxError: applying the 'delete' operator to an unqualified name is deprecated
delete testvar;
delete testFunc;
Object.defineProperty(testObj, "testvar", {
value: 10,
configurable: false
});
// TypeError: property "testvar" is non-configurable and can't be deleted
delete testObj.testvar;
- 不可多次定義同一屬性
'use strict';
// SyntaxError: property name prop1 appears more than once in object literal
var testObj = {
prop1: 10,
prop2: 15,
prop1: 20
};
- 在函式不可多次定義同一參數
'use strict';
// SyntaxError: duplicate formal argument param1
function testFunc(param1, param1) {
return 1;
};
以下關鍵字皆不可當作變數宣告使用:
- implements
- interface
- let
- package
- private
- protected
- public
- static
- yield
'use strict';
// SyntaxError: implements is a reserved identifier
var implements = 10;
- 不可將八進位值指派給變數
- 不可使用八進位溢出字元(經測試沒用嚴格模式也會發生錯誤)
'use strict';
// SyntaxError: octal literals and octal escape sequences are deprecated
var testoctal = 010;
// SyntaxError: illegal character
var testescape = \010;
- 如果this的值為
null
或undefined
,就無法轉換成全域物件
'use strict';
function testFunc() {
return this;
}
var testvar = testFunc();
alert(testvar); // undefined, 若非嚴格模式在瀏覽器執行則是[object Window])
'use strict';
// SyntaxError: redefining eval is deprecated
var eval = 10;
'use strict';
// ReferenceError: assignment to undeclared variable testvar
eval("var testvar = 10");
testvar = 15;
- 在 Strict 模式中,函式宣告不可以巢狀在陳述式或區塊內部。 它們只可以出現在最上層或直接位於函式主體內。
'use strict';
var arr = [1, 2, 3, 4, 5];
var index = null;
for (index in arr) {
// SyntaxError: in strict mode code, functions may be declared only at top level or
immediately within another function
function myFunc() {};
}
'use strict';
// SyntaxError: redefining arguments is deprecated
var arguments = 10;
'use strict';
function test(testInt) {
if (testInt-- == 0) {
return;
}
// TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on
strict mode functions or the arguments objects for calls to them
arguments.callee(testInt--);
}
'use strict';
// SyntaxError: strict mode code may not contain 'with' statements
with (Math) {
x = cos(3);
y = tan(7);
}