前端javascript代碼編寫中,有一個不錯的工具叫JSLint,能夠檢查代碼規範化,壓縮JS,CSS等,可是他的語法規範檢查我的以爲太「苛刻」了,會提示各類各樣的問題修改建議,有時候提示的信息咱們看的莫名其妙,這裏,先轉載一下攜程UED的一個技術文章(原文連接ued.ctrip.com/blog/?p=273…),看看JSLint的錯誤提示都是什麼意思:javascript
一直覺得檢查JS語法錯誤非jslint不可,不過使用起來老是以爲過重量級了一點點。php
後來無心中發現了一個叫jshint的東東。html
首先介紹一下,jshint和jslint的差異在哪裏。前端
摘自官網的一段內容java
JSHint is a fork of Douglas Crockford’s JSLint that is designed to be more flexible than the original. Our goal is to make a tool that helps you to find errors in your JavaScript code and to enforce your favorite coding style.node
We realize that people use different styles and conventions, and we want our tool to adjust to them. JSHint will never enforce one particular convention.jquery
大概的意思就是,JSHint比起JSLint而言,會更加輕量級一些。它可以找出代碼中的語法錯誤,而且建議更好的一種編碼風格。固然,它也不是強制性的非要你根據它規定的編碼風格來作。由於它提供了一系列的配置,你能夠隨時關掉某些你以爲沒必要要的錯誤提示。這個我後面會介紹到。ajax
那麼如何使用jsHint檢查錯誤呢?用起來很是簡單哦~正則表達式
var result = JSHINT(source, options);複製代碼
先解釋一下參數和返回值:express
第一個參數source : 必選項。表示須要檢查的代碼,js或者json,能夠傳一個字符串或者一個數組。若是傳字符串,須要用’\r’或者’\n’來分隔一行一行的代碼;若是傳數組,則每個數組元素表示一行的代碼。
第二個參數option : 可選項。表示代碼檢查的配置項。大部分的都是bool類型的,也有一部分,例如predef,能夠是一個array,含有全局變量或者全局方法;或者是一個object,key是全局變量或者方法,value是一個bool值,表示是否被定義過。
返回值:若是代碼沒有問題,JSHINT會返回一個true;不然返回false。
詳細的說明
1. 關於第一個參數
因爲只能傳入一個字符串或者數組。可是若是須要根據一個js的連接來檢查此文件呢?
我嘗試使用ajax方法獲取js的內容(雖然是能夠跨域的),可是若是頁面是gb2312的取回來就會亂碼,那麼用jshint頁面檢查的話,讀到中文部分就會報錯」unsafe charater」。嘗試使用二進制的responseBody進行轉碼,可是沒有找到是適合的js轉碼方法。
後來,我寫了一個php的中轉頁面,用file_get_contents的方法讀取文件,使用mb_detect_encoding檢測頁面編碼
<?php
mb_detect_order("GB2312,GBK,UTF-8,ASCII");
$url = $_REQUEST["url"];
$str = file_get_contents($url); if(!isset($url) || !$str){
echo "";
}else{
$getcontent = iconv(mb_detect_encoding($str), "utf-8", $str);
echo $getcontent;
}
?>複製代碼
固然,也有同窗提到能夠將js文件獲取下來,存到本地。讀取的時候判斷編碼來讀取,也是一種方法。不過我沒有嘗試過。
2. 關於第二個參數
我以爲這個纔是JSHINT的精髓,由於每個配置項均可以定義你要check的深度和廣度。
下面就把這一系列的配置項列出來(偶本身翻譯的)。如下的這些是官網上面的option。
prop | description |
asi | 是否使用自動插入分號 |
bitwise | 若是是true,則禁止使用位運算符 |
boss | 若是是true,則容許在if/for/while的條件中使用=作賦值操做 |
curly | 若是是true,則要求在if/while的模塊時使用TAB結構 |
debug | 若是是true,則容許使用debugger的語句 |
eqeqeq | 若是是true,則要求在全部的比較時使用===和!== |
eqnull | 若是是true,則容許使用== null |
evil | 若是是true,則容許使用eval方法 |
forin | 若是是true,則不容許for in在沒有hasOwnProperty時使用 |
immed | 若是是true,則要求「當即調用」(immediate invocations)必須使用括號包起來 |
laxbreak | 若是是true,則不檢查換行,那麼自動插入分號的選項必須開啓。 |
maxerr | 默認是50。 表示多少錯誤時,jshint中止分析代碼 |
newcap | 若是是true,則構造函數必須大寫 |
noarg | 若是是true,則不容許使用arguments.caller和arguments.callee |
noempty | 若是是true,則不容許使用空函數 |
nonew | 若是是true,則不容許不作賦值的構造函數,例如new UIWindow(); |
nomen | 若是是true,則不容許在名稱首部和尾部加下劃線 |
onevar | 若是是true,則在一個函數中只能出現一次var |
passfail | 若是是true,則在遇到第一個錯誤的時候就終止 |
plusplus | 若是是true,則不容許使用++或者- -的操做 |
regexp | 若是是true,則正則中不容許使用.或者[^…] |
undef | 若是是ture,則全部的局部變量必須先聲明以後才能使用 |
sub | 若是是true,則容許使用各類寫法獲取屬性(通常使用.來獲取一個對象的屬性值) |
strict | 若是是true,則須要使用strict的用法, 詳見ejohn.org/blog/ecmasc… |
white | 若是是true,則須要嚴格使用空格用法。 |
固然,千萬別覺得JSHINT就只有這些配置項,在我使用的過程當中,發現不少配置項就須要去讀它的源代碼才能發現。
好比,當我發現他會報錯個人某個自定義函數$animate沒有定義的時候,我嘗試用/*global $animate*/來聲明,可是沒有效果。
因而我跟蹤代碼,發現了以下的代碼
function assume() {
if (option.couch)
combine(predefined, couch);
if (option.rhino)
combine(predefined, rhino);
if (option.prototypejs)
combine(predefined, prototypejs);
if (option.node)
combine(predefined, node);
if (option.devel)
combine(predefined, devel);
if (option.dojo)
combine(predefined, dojo);
if (option.browser)
combine(predefined, browser);
if (option.jquery)
combine(predefined, jquery);
if (option.mootools)
combine(predefined, mootools);
if (option.wsh)
combine(predefined, wsh);
if (option.globalstrict && option.strict !== false)
option.strict = true;
}複製代碼
當時我在這個後面加了一段代碼,準備擴展一個predef的項。
後來才發現,原來JSHINT自己就有一個predef的配置,就像上面說的一個數組或一個object便可。
if(option.predef){
for(var i=0,l=option.predef.length; i<l; i++){
predefined[option.predef[i]] = true;
}
}複製代碼
固然,細心的同窗必定發現了,裏面也能夠聲明代碼運行的環境。例如jquery/dojo等。
又好比JSHINT檢查的時候,針對某些字符會報」unsafe character」的錯誤,可是若是有些字符恰巧就是咱們須要的怎麼辦呢?
跟蹤代碼,發現檢查unsafe的正則以下:
cx = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/複製代碼
咱們能夠自定義一下cx
3. 返回值
當返回false的時候,咱們如何知道代碼哪裏出問題了呢?
有如下幾個方法:
1. JSHINT.errors
JSHINT.errors是一個object,有如下值:
{
line: 錯誤的行數,
charater: 錯誤的字符數,
reason: 問題詳細描述信息,
evidence: 出錯的代碼,
raw: 本來的描述信息,
a: the first detail,
b: the second detail,
c: the third detail,
d: the fourth detail
}複製代碼
2. JSHINT.report(limited)
參數limited若是爲true,則表示report僅僅輸出錯誤(errors)
返回一個相似report的最終結果。能夠被放置在html中。
3. JSHINT.data()
返回一個object格式的數據結果, 有如下值:
{
errors:[
{
line: 錯誤的行數[number],
charater: 錯誤的字符數[number],
reason: 問題詳細描述信息[string],
evidence: 出錯的代碼[string]
}
],
functions: [
name: 函數名稱[STRING],
line: 錯誤的行數[NUMBER],
last: NUMBER,
param: [
參數[STRING]
],
closure: [
閉包[STRING]
],
var: [
STRING
],
exception: [
STRING
],
outer: [
STRING
],
unused: [
STRING
],
global: [
STRING
],
label: [
STRING
]
],
globals: [
STRING
],
member: {
STRING: NUMBER
},
unuseds: [
{
name: STRING,
line: NUMBER
}
],
implieds: [
{
name: STRING,
line: NUMBER
}
],
urls: [
STRING
],
json: 是不是json的數據[BOOLEAN]複製代碼
使用下來,jshint對代碼的檢查很是不錯的。
可是以爲jshint有些地方能夠改進,例如全部的報錯信息都是分散在四面八方。這裏一句warning(「xx」),那裏一句warning(「yy」)。不像wikipedia有一個統一管理message的地方,並且有語言版本的選擇。
無奈之下,我只能增長了一下message的翻譯,而且小改了一下warning的函數
globalMsg = {
「Missing semicolon.」 : 「缺乏分號.」,
「Use the function form of \」use strict\」.」 : 「使用標準化定義function.」,
「Unexpected space after ‘-’.」 : 「在’-'後面不該出現空格.」, 「Expected a JSON value.」 : 「請傳入一個json的值.」, 「Mixed spaces and tabs.」: 「空格和TAB重複.」, 「Unsafe character.」 : 「不安全的字符.」, 「Line too long.」: 「本行中的字符超過設定的最大長度.」, 「Trailing whitespace.」: 「本行末尾有過多無用空格.」, 「Script URL.」 : 「腳本URL.」, 「Unexpected {a} in ‘{b}’.」 : 「在 ‘{b}’ 中不應出現 {a}.」, 「Unexpected ‘{a}’.」 : 「不應在此出現’{a}’.」, 「Strings must use doublequote.」 : 「字符串須要用雙引號」, 「Unnecessary escapement.」 : 「不須要轉義」, 「Control character in string: {a}.」 : 「在字符串中出現了Control的字符」, 「Avoid \\’.」 : 「避免 \\」, 「Avoid \\v.」 : 「避免 \\v」, 「Avoid \\x-.」 : 「避免 \\x-」, 「Bad escapement.」 : 「錯誤的轉義字符」, 「Bad number ‘{a}’.」 : 「錯誤的數字 ‘{a}’」, 「Missing space after ‘{a}’.」 : 「在’{a}’以後缺乏空格」, 「Don’t use extra leading zeros ‘{a}’.」 : 「不要再’{a}’的前面用多餘的0″, 「Avoid 0x-. ‘{a}’.」 : 「避免使用 0x-. ‘{a}’.」, 「A trailing decimal point can be confused with a dot ‘{a}’.」 : 「在’{a}’中使用點尾隨小數點」, 「Unexpected comment.」 : 「不應在此處出現註釋」, 「Unescaped ‘{a}’.」 : 「沒有轉義 ‘{a}’」, 「Unexpected control character in regular expression.」 : 「在正則表達式中出現了control字符」, 「Unexpected escaped character ‘{a}’ in regular expression.」 : 「在正則表達式中出現了沒有轉義的字符 ‘{a}’」, 「Expected ‘{a}’ and instead saw ‘{b}’.」 : 「應該用 ‘{a}’代替’{b}’」, 「Spaces are hard to count. Use {{a}}.」 : 「空格難以統計,請使用 {{a}}」, 「Insecure ‘{a}’.」 : 「不安全的 ‘{a}’」, 「Empty class.」 : 「空的class」, 「Expected a number and instead saw ‘{a}’.」:「應該用數字代替’{a}’」, 「‘{a}’ should not be greater than ‘{b}’.」:「‘{a}’不該該比’{b}’大」, 「‘hasOwnProperty’ is a really bad name.」: 「‘hasOwnProperty’是關鍵字」, 「‘{a}’ was used before it was defined.」:「‘{a}’未定義就已經使用了.」, 「‘{a}’ is already defined.」:「‘{a}’被重複定義」, 「A dot following a number can be confused with a decimal point.」:「數字後面的一個點會被誤認爲是十進制的小數點」, 「Confusing minusses」 : 「容易混淆的負數表達-」, 「Confusing plusses.」 : 「容易混淆的正數表達+」, 「Unmatched ‘{a}’.」 : 「沒法匹配的’{a}’」, 「Expected ‘{a}’ to match ‘{b}’ from line {c} and instead saw ‘{d}’.」:「在行{c}中須要用’{a}’和’{b}’匹配,用來代替’{d}’」, 「Unexpected early end of program.」:「程序不可預期的提早終止」, 「A leading decimal point can be confused with a dot: ‘.{a}’.」:「‘{a}’前的點容易混淆成小數點」, 「Use the array literal notation [].」:「使用數組的符號 []「, 「Expected an operator and instead saw ‘{a}’.」:「須要用一個符號來代替’{a}’」, 「Unexpected space after ‘{a}’.」:「在’{a}’以後不能出現空格」, 「Unexpected space before ‘{a}’.」:「在’{a}’以前不能出現空格」, 「Bad line breaking before ‘{a}’.」:「在’{a}’以前錯誤的換行」, 「Expected ‘{a}’ to have an indentation at {b} instead at {c}.」:「‘{a}’須要在{c}而不是{b}處縮進」, 「Line breaking error ‘{a}’.」:「換行錯誤 ‘{a}’」, 「Unexpected use of ‘{a}’.」:「此處不能用’{a}’」, 「Bad operand.」:「錯誤的操做數」, 「Use the isNaN function to compare with NaN.」:「使用isNaN來與NaN比較」, 「Confusing use of ‘{a}’.」:「容易混淆的’{a}’的使用」, 「Read only.」:「只讀的屬性」, 「‘{a}’ is a function.」:「‘{a}’是一個函數」, ‘Bad assignment.’:「錯誤的賦值」, 「Do not assign to the exception parameter.」:「不要給額外的參數賦值」, 「Expected an identifier in an assignment and instead saw a function invocation.」:「在賦值的語句中須要有一個標識符,而不是一個方法的調用」, 「Expected an identifier and instead saw ‘{a}’ (a reserved word).」:「須要有一個標識符,而不是’{a}’(保留字符)」, 「Missing name in function declaration.」:「在方法聲明中缺乏名稱」, 「Expected an identifier and instead saw ‘{a}’.」:「須要有一個標識符,而不是’{a}’」, 「Inner functions should be listed at the top of the outer function.」:「內部函數的聲明應該放在此函數的頂部。」, 「Unreachable ‘{a}’ after ‘{b}’.」:「在’{b}’以後沒法獲取’{a}’」, 「Unnecessary semicolon.」:「沒必要要的分號」, 「Label ‘{a}’ on {b} statement.」:「將’{a}’放在{b}的聲明中」, 「Label ‘{a}’ looks like a javascript url.」:「‘{a}’看上去像一個js的連接」, 「Expected an assignment or function call and instead saw an expression」:「須要一個賦值或者一個函數調用,而不是一個表達式.」, 「Do not use ‘new’ for side effects.」:「不要用’new’語句.」, 「Unnecessary \」use strict\」.」:「沒必要要的\」use strict\」.」, 「Missing \」use strict\」 statement.」:「缺乏\」use strict\」的聲明」, 「Empty block.」:「空的模塊」, 「Unexpected /*member ‘{a}’.」:「不該出現 /*元素 ‘{a}’.」, 「‘{a}’ is a statement label.」:「‘{a}’是一個聲明」, 「‘{a}’ used out of scope.」:「‘{a}’使用超出範圍」, 「‘{a}’ is not allowed.」:「不容許使用’{a}’」, 「‘{a}’ is not defined.」:「‘{a}’沒有被定義」, 「Use ‘{a}’ to compare with ‘{b}’.」:「使用’{a}’與’{b}’相比」, 「Variables should not be deleted.」:「變量須要被刪除」, 「Use the object literal notation {}.」:「使用對象的文字符號 {}」, 「Do not use {a} as a constructor.」:「不要使用{a}做爲一個構造對象」, 「The Function constructor is eval.」:「The Function constructor is eval.」, 「A constructor name should start with an uppercase letter.」:「一個構造對象的名稱必須用大寫字母開頭.」, 「Bad constructor.」:「錯誤的構造對象」, 「Weird construction. Delete ‘new’.」:「構造對象有誤,請刪除’new’」, 「Missing ‘()’ invoking a constructor.」:「缺乏括號()」, 「Avoid arguments.{a}.」:「避免參數.{a}.」, 「document.write can be a form of eval.」:「document.write是eval的一種形式」, ‘eval is evil.’:「儘可能不要使用eval」, 「Math is not a function.」:「Math不是一個函數」, 「Missing ‘new’ prefix when invoking a constructor.」:「此處缺乏了’new’」, 「Missing radix parameter.」:「缺乏參數」, 「Implied eval is evil. Pass a function instead of a string.」:「傳遞一個函數,而不是一個字符串」, 「Bad invocation.」:「錯誤的調用」, 「['{a}'] is better written in dot notation.」:「['{a}']最好用點.的方式」, 「Extra comma.」:「多餘的逗號」, 「Don’t make functions within a loop.」:「不要用循環的方式建立函數」, 「Unexpected parameter ‘{a}’ in get {b} function.」:「在{b}方法中不應用到參數’{a}’」, 「Duplicate member ‘{a}’.」:「重複的’{a}’」, 「Expected to see a statement and instead saw a block.」:「此處應該是語句聲明.」, 「Too many var statements.」:「過多var的聲明」, 「Redefinition of ‘{a}’.」:「‘{a}’被重複定義」, 「It is not necessary to initialize ‘{a}’ to ‘undefined’.」:「無需將’{a}’初始化爲’undefined’」, 「Expected a conditional expression and instead saw an assignment.」:「此處須要一個表達式,而不是賦值語句」, 「Expected a ‘break’ statement before ‘case’.」:「在’case’以前須要有’break’.」, 「Expected a ‘break’ statement before ‘default’.」:「在’default’以前須要有’break’.」, 「This ‘switch’ should be an ‘if’.」:「此處’switch’應該是’if’.」, 「All ‘debugger’ statements should be removed.」:「請刪除’debugger’的語句」, 「‘{a}’ is not a statement label.」:「‘{a}’不是一個聲明標籤.」, 「Expected an assignment or function call and instead saw an expression.」:「須要一個語句或者一個函數調用,而不是一個表達式」, 「Function declarations should not be placed in blocks. Use a function expression or move the statement to the top of the outer function.」:「函數的聲明不能放在相似if的塊中,須要放在外部函數的頂部.」 },複製代碼
warning函數增長了一個m = globalMsg[m] || m;
function warning(m, t, a, b, c, d) {
var ch, l, w;
t = t || nexttoken;
if (t.id === ‘(end)’) { // `~
t = token;
}
l = t.line || 0;
ch = t.from || 0;
m = globalMsg[m] || m;
w = {
id: ‘(error)’,
raw: m,
evidence: lines[l - 1] || 」,
line: l,
character: ch,
a: a,
b: b,
c: c,
d: d
};
w.reason = m.supplant(w);
JSHINT.errors.push(w);
if (option.passfail) {
quit(‘Stopping. ‘, l, ch);
}
warnings += 1;
if (warnings >= option.maxerr) {
quit(「Too many errors.」, l, ch);
}
return w;
}複製代碼
若是你須要一個輕量級的語法檢查工具,那麼jshint仍是一個蠻駕輕就熟的工具。若是可以更深刻的讀一下JSHINT的代碼,收穫應該不小。
針對本身項目中遇到的一些提示,作一些舉例說明:
1 [W099]:Mixed spaces and tabs
這個錯誤是最多見的,意思是在同一行中,空格和Tab縮進混合使用了,修改很簡單,通常是刪除Tab縮進,所有改成空格。爲了方便,咱們能夠把編輯器的Tab縮進設置成2個或4個空格,來代替原有的縮進。
2 [W030]:Expected an assignment or function call and instead saw an expression
這個錯誤提示的很詭異,我是用以下代碼提示的這個錯誤 index-1 <0 ? index = 0:index = index - 1; 這是一個逗號表達式,可是JSLInt認爲這裏不該該用表達式,而必須是一個函數,因此,若是很是在意這個錯誤,就改成if else 語句吧
3 [W041]:Use '===' to compare with ...
這個錯誤是說,咱們要是用全等來代替等於,若是表達式兩邊的數據類型是一致的話,建議使用全等來判斷
4 [W033]:Missing semicolon
缺乏分號;這個通常都是本身忘記寫了吧,可是有一個須要注意的是,對於只有一句的結構,後面也須要寫分號。例如:if(index<0) {index=tcount-1} 這句代碼,正確寫法是if(index<0) {index=tcount-1;},我是常常忘記這裏寫分號,汗...
其餘還有一些錯誤提示就對照一下改吧,要培養本身良好的代碼風格和書寫習慣。