前言
看了數(shù)千個項目后,發(fā)現(xiàn)了 10 個最常見的 JAVAScript 錯誤。我們會告訴你什么原因?qū)е铝诉@些錯誤,以及如何防止這些錯誤發(fā)生。如果你能夠避免落入這些 “陷阱”,你將會成為一個更好的開發(fā)者。
一、Uncaught TypeError: Cannot read property
如果你是一個 JavaScript 開發(fā)人員,可能你看到這個錯誤的次數(shù)比你敢承認的要多。當你讀取一個未定義的對象的屬性或調(diào)用其方法時,這個錯誤會在 Chrome 中出現(xiàn)。 您可以很容易的在 Chrome 開發(fā)者控制臺中進行測試。
發(fā)生這種情況的原因很多,但常見的一種是在渲染 UI 組件時對于狀態(tài)的初始化操作不當。
我們來看一個在真實應(yīng)用程序中發(fā)生的例子:我們選擇 React,但該情況也同樣適用于 Angular、Vue 或任何其他框架。
class Quiz extends Component { componentWillMount() { axIOS.get('/thedata').then(res => { this.setState({items: res.data}); }); } render() { return ( <ul> {this.state.items.map(item => <li key={item.id}>{item.name}</li> )} </ul> ); } }
兩個重要的流程:
- 組件的狀態(tài)(例如 this.state)開始于 undefined。
- 當異步獲取數(shù)據(jù)時,不管它是在構(gòu)造函數(shù) componentWillMount 還是 componentDidMount 中獲取的,組件在數(shù)據(jù)加載之前至少會呈現(xiàn)一次,當 Quiz 第一次呈現(xiàn)時,this.state.items 是 undefined。
這很容易解決。最簡單的方法:在構(gòu)造函數(shù)中初始化 state。
class Quiz extends Component { // Added this: constructor(props) { super(props); // Assign state itself, and a default value for items this.state = { items: [] }; } componentWillMount() { axios.get('/thedata').then(res => { this.setState({items: res.data}); }); } render() { return ( <ul> {this.state.items.map(item => <li key={item.id}>{item.name}</li> )} </ul> ); } }
在你的應(yīng)用程序中的具體代碼可能是不同的,但我們希望我們已經(jīng)給你足夠的線索,以解決或避免在你的應(yīng)用程序中出現(xiàn)的這個問題。如果還沒有,請繼續(xù)閱讀,因為我們將在下面覆蓋更多相關(guān)錯誤的示例。
2、 TypeError: ‘undefined’ is not an object
這是在 Safari 中讀取屬性或調(diào)用未定義對象上的方法時發(fā)生的錯誤。您可以在 Safari Developer Console 中輕松測試。這與第一點中提到的 Chrome 的錯誤基本相同,但 Safari 使用了不同的錯誤消息提示語。
3、 TypeError: null is not an object
這是在 Safari 中讀取屬性或調(diào)用空對象上的方法時發(fā)生的錯誤。 您可以在 Safari Developer Console 中輕松測試。
有趣的是,在 JavaScript 中,null 和 undefined 是并不同,這就是為什么我們看到的是兩個不同的錯誤信息。
undefined 通常是一個尚未分配的變量,而 null 表示該值為空。 要驗證它們不相等,請嘗試使用嚴格的相等運算符 ===
在我們工作中,這種錯誤可能發(fā)生的一種場景是:如果在加載元素之前嘗試在 JavaScript 中使用元素。 因為 DOM API 對于空白的對象引用返回值為 null。
任何執(zhí)行和處理 DOM 元素的 JS 代碼都應(yīng)該在創(chuàng)建 DOM 元素之后執(zhí)行。
JS 代碼按照 html 中的規(guī)定從上到下進行解釋。 所以,如果 DOM 元素之前有一個標簽,腳本標簽內(nèi)的 JS 代碼將在瀏覽器解析 HTML 頁面時執(zhí)行。 如果在加載腳本之前尚未創(chuàng)建 DOM 元素,則會出現(xiàn)此錯誤。
在這個例子中,我們可以通過添加一個事件監(jiān)聽器來解決這個問題,這個監(jiān)聽器會在頁面準備好的時候通知我們。 一旦 addEventListener 被觸發(fā),init() 方法就可以使用 DOM 元素。
<script> function init() { var myButton = document.getElementById("myButton"); var myTextfield = document.getElementById("myTextfield"); myButton.onclick = function() { var userName = myTextfield.value; } } document.addEventListener('readystatechange', function() { if (document.readyState === "complete") { init(); } }); </script> <form> <input type="text" id="myTextfield" placeholder="Type your name" /> <input type="button" id="myButton" value="Go" /> </form>
4、 (unknown): Script error
當未捕獲的 JavaScript 錯誤(通過 window.onerror 處理程序引發(fā)的錯誤,而不是捕獲在 try-catch 中)被瀏覽器的跨域策略限制時,會產(chǎn)生這類的腳本錯誤。 例如,如果您將您的 JavaScript 代碼托管在 CDN 上,則任何未被捕獲的錯誤將被報告為“腳本錯誤” 而不是包含有用的堆棧信息。這是一種瀏覽器安全措施,旨在防止跨域傳遞數(shù)據(jù),否則將不允許進行通信。
要獲得真正的錯誤消息,請執(zhí)行以下操作:
1. 設(shè)置 ‘Access-Control-Allow-Origin’ 頭部
將 Access-Control-Allow-Origin 標頭設(shè)置為 * 表示可以從任何域正確訪問資源。
在 Nginx 中設(shè)置如下:
將 add_header 指令添加到提供 JavaScript 文件的位置塊中:
location ~ ^/assets/ { add_header Access-Control-Allow-Origin *; }
2. 在 <script> 中設(shè)置 crossorigin="anonymous"
在您的 HTML 代碼中,對于您設(shè)置了 Access-Control-Allow-Origin 的每個腳本,在 script 標簽上設(shè)置 crossorigin =“anonymous”。在腳本標記中添加 crossorigin 屬性之前,請確保驗證上述 header 正確發(fā)送。
在 Firefox 中,如果存在crossorigin屬性,但Access-Control-Allow-Origin頭不存在,則腳本將不會執(zhí)行。
5、 TypeError: Object doesn’t support property
這是您在調(diào)用未定義的方法時發(fā)生在 IE 中的錯誤。 您可以在 IE 開發(fā)者控制臺中進行測試。
這相當于 Chrome 中的 “TypeError:”undefined“ is not a function” 錯誤。
是的,對于相同的邏輯錯誤,不同的瀏覽器可能具有不同的錯誤消息。
對于使用 JavaScript 命名空間的 Web 應(yīng)用程序,這是一個 IE 瀏覽器的常見的問題。 在這種情況下,99.9% 的原因是 IE 無法將當前名稱空間內(nèi)的方法綁定到 this 關(guān)鍵字。
例如:如果你 JS 中有一個命名空間 Rollbar 以及方法 isAwesome。 通常,如果您在 Rollbar 命名空間內(nèi),則可以使用以下語法調(diào)用 isAwesome 方法:
this.isAwesome();
Chrome,F(xiàn)irefox 和 Opera 會欣然接受這個語法。 但是 IE 卻不會。 因此,使用 JS 命名空間時最安全的選擇是始終以實際名稱空間作為前綴。
Rollbar.isAwesome();
6、 TypeError: ‘undefined’ is not a function
當您調(diào)用未定義的函數(shù)時,這是 Chrome 中產(chǎn)生的錯誤。 您可以在 Chrome 開發(fā)人員控制臺和 Mozilla Firefox 開發(fā)人員控制臺中進行測試。
function clearBoard(){ alert("Cleared"); } document.addEventListener("click", function(){ this.clearBoard(); // what is “this” ? });
執(zhí)行上面的代碼會導(dǎo)致以下錯誤:
“Uncaught TypeError:this.clearBoard is not a function”。
原因應(yīng)該是清楚的,即執(zhí)行上下文不理解導(dǎo)致的指向錯誤。
7、 Uncaught RangeError
當你調(diào)用一個不終止的遞歸函數(shù)就會發(fā)生這種錯誤。您可以在 Chrome 開發(fā)者控制臺中進行測試。
此外,如果您將值傳遞給超出范圍的函數(shù),也可能會發(fā)生這種情況。
許多函數(shù)只接受其輸入值的特定范圍的數(shù)字。 例如:
- toExponential(digits) 和 toFixed(digits) 接受 0 到 100
- toPrecision(digits) 接受 1 到 100
var num = 2.555555; console.log(num.toExponential(4)); //OK console.log(num.toExponential(-2)); //range error! console.log(num.toFixed(2)); //OK console.log(num.toFixed(105)); //range error! console.log(num.toPrecision(1)); //OK console.log(num.toPrecision(0)); //range error!
8、 TypeError: Cannot read property ‘length’
這是 Chrome 中發(fā)生的錯誤,因為讀取未定義變量的長度屬性。 您可以在 Chrome 開發(fā)者控制臺中進行測試。
您通常會在數(shù)組中找到定義的長度,但是如果數(shù)組未初始化或者變量在另一個上下文中,則可能會遇到此錯誤。讓我們用下面的例子來理解這個錯誤。
var testArray = ["Test"]; function testFunction(testArray) { for (var i = 0; i < testArray.length; i++) { console.log(testArray[i]); } } testFunction();
執(zhí)行以上代碼會報錯:
Cannot read property 'length' of undefined
有兩種方法可以解決這個問題:
var testArray = ["Test"]; /* Precondition: defined testArray outside of a function */ function testFunction(/* No params */) { for (var i = 0; i < testArray.length; i++) { console.log(testArray[i]); } } testFunction();
或
var testArray = ["Test"]; function testFunction(testArray) { for (var i = 0; i < testArray.length; i++) { console.log(testArray[i]); } } testFunction(testArray);
9、 Uncaught TypeError: Cannot set property
當我們嘗試訪問一個未定義的變量時,它總是返回 undefined,我們不能獲取或設(shè)置任何未定義的屬性。 在這種情況下會將拋出 “Uncaught TypeError: Cannot set property”。
10. ReferenceError: event is not defined
當您嘗試訪問未定義的變量或超出當前作用域的變量時,會引發(fā)此錯誤。 您可以在 Chrome 瀏覽器中測試。
如果在使用 event 時遇到此錯誤,請確保使用傳入的事件對象作為參數(shù)。像 IE 這樣的舊瀏覽器提供了一個全局變量事件,但并不是所有瀏覽器都支持。
document.addEventListener("mousemove", function (event) { console.log(event); })
總結(jié)
我們看到上面的 10 個最常見的錯誤,其實所涉及的知識點并不難。當你認真讀過《你不知道的 JavaScript》上卷后,這些錯誤基本就不會再出現(xiàn)了。當然,歸根結(jié)底還是要有扎實的 javascript 基礎(chǔ),理解底層原理和實現(xiàn)。