本文轉(zhuǎn)載自公眾號(hào):前端工匠(微信號(hào):frontJS)
本文給大家?guī)砹?0個(gè)經(jīng)典的JavaScript錯(cuò)誤,為了便于閱讀,每個(gè)錯(cuò)誤都被縮短了,讓我們更深入地研究每一個(gè)問題,以確定是什么導(dǎo)致了這些問題,以及如何避免產(chǎn)生這些問題。
1. Uncaught TypeError: Cannot read property
如果你是一個(gè) JavaScript 開發(fā)人員,你可能已經(jīng)看到過這個(gè)錯(cuò)誤。當(dāng)你讀取屬性或在未定義對(duì)象上調(diào)用方法時(shí),Chrome 中就會(huì)發(fā)生這種情況。你可以在 Chrome 開發(fā)者控制臺(tái)中輕松進(jìn)行測(cè)試。
發(fā)生這種情況的原因有很多,但常見的原因是渲染 UI 組件時(shí)狀態(tài)初始化不當(dāng)。讓我們來看一個(gè)在現(xiàn)實(shí)應(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>
);
}
}
這里有兩件重要的事情要意識(shí)到:
- 組件的狀態(tài)(例如
this.state
)以undefined
狀態(tài)開始使用。 - 當(dāng)你異步獲取數(shù)據(jù)時(shí),無論數(shù)據(jù)是在構(gòu)造函數(shù)
componentWillMount
還是componentDidMount
中獲取,組件都將在數(shù)據(jù)加載之前至少渲染一次。當(dāng)Quiz第一次渲染時(shí),this.state.items
是undefined
。這反過來又意味著ItemList會(huì)得到未定義的items,你會(huì)在控制臺(tái)中得到一個(gè)錯(cuò)誤——"UncaughtTypeError: Cannot read property 'map' of undefined "的錯(cuò)誤。
這很容易解決,最簡(jiǎn)單的方法:在構(gòu)造函數(shù)中使用合理的默認(rèn)值初始化狀態(tài)。
class Quiz extends Component {
// 添加了這個(gè):
constructor(props) {
super(props);
this.state = {
items: [] // 默認(rèn)值
};
}
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)用程序中的實(shí)際代碼可能會(huì)有不同,但我希望已經(jīng)給了你足夠的線索,讓你在你的應(yīng)用程序中修復(fù)或避免這個(gè)問題。如果沒有,請(qǐng)繼續(xù)閱讀,因?yàn)槲覍⒃谙旅娼榻B有關(guān)相關(guān)錯(cuò)誤的更多示例。
2. TypeError: ‘undefined’ is not an object (evaluating
這是在 Safari
中讀取屬性或調(diào)用undefined
對(duì)象上的方法時(shí)發(fā)生的錯(cuò)誤,你可以在Safari
開發(fā)者控制臺(tái)中非常輕松地進(jìn)行測(cè)試。這基本上與上述針對(duì) Chrome 的錯(cuò)誤相同,但 Safari 使用了不同的錯(cuò)誤消息。
3. TypeError: null is not an object (evaluating
這是在 Safari 中讀取屬性或調(diào)用null
對(duì)象上的方法時(shí)發(fā)生的錯(cuò)誤,你可以在 Safari 開發(fā)者控制臺(tái)中非常輕松地進(jìn)行測(cè)試。
有趣的是,在JavaScript
中,null
和undefined
不相同,這就是為什么我們看到兩個(gè)不同的錯(cuò)誤消息的原因。undefined通常是尚未分配的變量,而null表示該值為空白。 要驗(yàn)證它們是否相等,請(qǐng)嘗試使用嚴(yán)格相等運(yùn)算符。
在實(shí)際示例中可能發(fā)生這種錯(cuò)誤的一種方式是,在加載元素之前嘗試在JavaScript
中使用DOM
元素,這是因?yàn)?code>DOM API對(duì)于空白的對(duì)象引用返回null
。
任何執(zhí)行和處理DOM
元素的JS
代碼都應(yīng)在創(chuàng)建DOM
元素后執(zhí)行。JS
代碼按照HTML
格式從上到下進(jìn)行解釋,所以,如果在DOM
元素之前有一個(gè)標(biāo)簽,那么在瀏覽器解析HTML
頁面的時(shí)候,腳本標(biāo)簽內(nèi)的JS
代碼就會(huì)被執(zhí)行。如果在加載腳本之前尚未創(chuàng)建DOM
元素,則會(huì)出現(xiàn)此錯(cuò)誤。
在此示例中,我們可以通過添加事件偵聽器來解決該問題,該事件偵聽器將在頁面準(zhǔn)備就緒時(shí)通知我們。一旦觸發(fā)了 addEventListener
,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
當(dāng)未捕獲的JavaScript
錯(cuò)誤違反跨源策略跨越域邊界時(shí),將發(fā)生腳本錯(cuò)誤。例如,如果你將你的JavaScript
代碼托管在CDN
上,任何未被捕獲的錯(cuò)誤(冒泡到window.onerror處理程序中的錯(cuò)誤,而不是在try-catch中被捕獲的錯(cuò)誤)都會(huì)被報(bào)告為 "Script error",而不是包含有用的信息。這是一種瀏覽器安全措施,旨在防止跨域傳遞數(shù)據(jù),否則該域?qū)o法通信。
要獲取真實(shí)的錯(cuò)誤消息,請(qǐng)執(zhí)行以下操作。
發(fā)送Access-Control-Allow-Origin標(biāo)頭
將 Access-Control-Allow-Origin
標(biāo)頭設(shè)置為 *
表示可以從任何域正確訪問資源。你可以根據(jù)需要將 *
替換為您的域:例如,Access-Control-Allow-Origin:www.example.com
。但是,處理多個(gè)域比較復(fù)雜,如果使用 CDN 可能會(huì)出現(xiàn)緩存問題,那么可能不值得花費(fèi)精力。
以下是一些有關(guān)如何在各種環(huán)境中設(shè)置此標(biāo)頭的示例:
Apache
在將提供JavaScript
文件的文件夾中,創(chuàng)建一個(gè)具有以下內(nèi)容的 .htaccess
文件:
Header add Access-Control-Allow-Origin "*"
Nginx
將add_header
指令添加到提供JavaScript
文件的location
塊中:
location ~ ^/assets/ {
add_header Access-Control-Allow-Origin *;
}
HAProxy
將以下內(nèi)容添加到提供JavaScript
文件的asset
后端:
rspadd Access-Control-Allow-Origin:\ *
在腳本標(biāo)簽上設(shè)置crossorigin =“ anonymous”
在你的HTML
源代碼中,對(duì)于您設(shè)置了 Access-Control-Allow-Origin
標(biāo)頭的每個(gè)腳本,在script
標(biāo)記上設(shè)置crossorigin="anonymous"
。在script
標(biāo)記上添加 crossorigin
屬性之前,請(qǐng)確保已驗(yàn)證是否已為腳本文件發(fā)送了標(biāo)頭。在Firefox
中,如果存在 crossorigin
屬性,但沒有 Access-Control-Allow-Origin
標(biāo)頭,則不會(huì)執(zhí)行腳本。
5. TypeError: Object doesn’t support property
這是在 IE 中發(fā)生的錯(cuò)誤,當(dāng)您調(diào)用undefined
的方法時(shí),你可以在 IE 開發(fā)人員控制臺(tái)中對(duì)此進(jìn)行測(cè)試。
這等效于 Chrome 中的錯(cuò)誤“ TypeError:‘undefined’ is not a function”。是的,對(duì)于相同的邏輯錯(cuò)誤,不同的瀏覽器可能具有不同的錯(cuò)誤消息。
這是 IE 在采用JavaScript
命名空間的 Web 應(yīng)用程序中常見的問題,在這種情況下,99.9%的問題是 IE 無法將當(dāng)前名稱空間中的方法綁定到 this
關(guān)鍵字。
例如,如果你的JS命名空間 Rollbar
使用 isAwesome
方法。通常,如果你在 Rollbar
名稱空間中,則可以使用以下語法調(diào)用 isAwesome
方法:
this.isAwesome();
Chrome,F(xiàn)irefox 和 Opera 將很樂意接受此語法。另一方面,IE 則不會(huì)。因此,在使用 JS
命名空間時(shí),最安全的方法是用實(shí)際的命名空間作為前綴。
Rollbar.isAwesome();
6. TypeError: ‘undefined’ is not a function
這是在 Chrome 中發(fā)生的錯(cuò)誤,當(dāng)你調(diào)用undefined
的函數(shù)時(shí)。你可以在 Chrome 開發(fā)者控制臺(tái)和Mozilla Firefox
開發(fā)者控制臺(tái)中對(duì)此進(jìn)行測(cè)試。
隨著這些年來JavaScript
的編碼技術(shù)和設(shè)計(jì)模式越來越復(fù)雜,在回調(diào)和閉包中的自引用作用域也相應(yīng)地增多,這也是相當(dāng)常見的這種或那種混亂的根源。
考慮以下示例代碼片段:
function clearBoard(){
alert("Cleared");
}
document.addEventListener("click", function(){
this.clearBoard(); // 這個(gè) "this" 是什么?
});
如果執(zhí)行上述代碼,然后單擊該頁面,則會(huì)導(dǎo)致以下錯(cuò)誤“ Uncaught TypeError:this.clearBoard not a function”。原因是正在執(zhí)行的匿名函數(shù)是在文檔的上下文中,而 clearBoard
是在 window
中定義的。
傳統(tǒng)的、與舊瀏覽器兼容的解決方案是簡(jiǎn)單地將對(duì)它的引用保存在一個(gè)變量中,然后閉包可以繼承這個(gè)變量。例如:
var self = this;
document.addEventListener("click", function(){
self.clearBoard();
});
另外,在較新的瀏覽器中,可以使用 bind()
方法傳遞正確的引用:
document.addEventListener("click",this.clearBoard.bind(this));
7. Uncaught RangeError: Maximum call stack
這是 Chrome 瀏覽器在幾種情況下出現(xiàn)的錯(cuò)誤,一種是調(diào)用不終止的遞歸函數(shù)。你可以在 Chrome 開發(fā)者控制臺(tái)中對(duì)此進(jìn)行測(cè)試。
如果將值傳遞給超出范圍的函數(shù),也可能會(huì)發(fā)生這種情況。許多函數(shù)的輸入值僅接受特定范圍的數(shù)字,例如,Number.toExponential(digits)
和 Number.toFixed(digits)
接受0到20之間的數(shù)字,而Number.toFixed(digits)
接受1到21之間的數(shù)字。
var a = newArray(4294967295); //OK
var b = newArray(-1); //range error
var num = 2.555555;
document.writeln(num.toExponential(4)); //OK
document.writeln(num.toExponential(-2)); //range error!
num = 2.9999;
document.writeln(num.toFixed(2)); //OK
document.writeln(num.toFixed(25)); //range error!
num = 2.3456;
document.writeln(num.toPrecision(1)); //OK
document.writeln(num.toPrecision(22)); //range error!
8. TypeError: Cannot read property ‘length’
這是 Chrome 瀏覽器中發(fā)生的錯(cuò)誤,因?yàn)樽x取undefined
的變量的length
屬性,你可以在 Chrome 開發(fā)者控制臺(tái)中進(jìn)行測(cè)試。
通常情況下,你可以在數(shù)組上找到定義的長(zhǎng)度,但如果數(shù)組沒有初始化或者變量名被隱藏在其他上下文中,你可能會(huì)遇到這個(gè)錯(cuò)誤。通過以下示例讓我們了解此錯(cuò)誤。
var testArray= ["Test"];
function testFunction(testArray) {
for (var i = 0; i < testArray.length; i++) {
console.log(testArray[i]);
}
}
testFunction();
當(dāng)你聲明一個(gè)帶參數(shù)的函數(shù)時(shí),這些參數(shù)就變成了局部參數(shù)。這意味著即使你具有名稱為 testArray
的變量,函數(shù)內(nèi)具有相同名稱的參數(shù)仍將被視為局部參數(shù)。
你可以通過兩種方式解決問題:
刪除函數(shù)聲明語句中的參數(shù)(事實(shí)證明,你想訪問那些在函數(shù)外部聲明的變量,因此你不需要為函數(shù)使用參數(shù))
var testArray = ["Test"];
/* 前置條件:在函數(shù)外部定義testArray */
function testFunction(/* No params */) {
for (var i = 0; i < testArray.length; i++) {
console.log(testArray[i]);
}
}
testFunction()
調(diào)用函數(shù),將我們聲明的數(shù)組傳遞給它。
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
當(dāng)我們嘗試訪問未定義的變量時(shí),它總是返回 undefined
,我們無法獲取或設(shè)置任何 undefined
屬性。在這種情況下,應(yīng)用程序?qū)⒁l(fā)“ Uncaught TypeError:Cannot set property”。
例如,在 Chrome 瀏覽器中:
如果 test
對(duì)象不存在,則錯(cuò)誤將引發(fā)“ Uncaught TypeError:Cannot set property”。
10. ReferenceError: event is not defined
當(dāng)您嘗試訪問undefined
或超出當(dāng)前范圍的變量時(shí),將引發(fā)此錯(cuò)誤。你可以在Chrome瀏覽器中非常輕松地對(duì)其進(jìn)行測(cè)試。
如果你在使用事件處理系統(tǒng)時(shí)收到這個(gè)錯(cuò)誤,請(qǐng)確保你使用傳入的事件對(duì)象作為參數(shù)。IE 等較舊的瀏覽器會(huì)提供全局變量事件,而 Chrome 會(huì)自動(dòng)將事件變量附加到處理程序。 Firefox 不會(huì)自動(dòng)添加它。jQuery 之類的庫試圖規(guī)范這種行為,盡管如此,最好還是使用傳遞給事件處理程序函數(shù)的方法。
document.addEventListener("mousemove", function (event) {
console.log(event);
})
總結(jié)
事實(shí)證明,其中許多都是null
或undefined
的錯(cuò)誤。如果使用嚴(yán)格的編譯器選項(xiàng),像Typescript
這樣的靜態(tài)類型檢查系統(tǒng)可以幫助你避免使用它們。它可以警告你,如果一個(gè)類型是預(yù)期的,但還沒有被定義。
以上就是W3Cschool編程獅
關(guān)于JavaScript錯(cuò)誤的10大經(jīng)典問題的相關(guān)介紹了,希望對(duì)大家有所幫助。