爲了可讀性,錯誤名稱進行了必定的簡寫。讓咱們深刻了解每一個錯誤發生的緣由以及解決方法。ios
若是你是一名JavaScript開發人員,你可能已經記不清楚多少次看到這個錯誤了。當你讀取一個undefined對象的屬性或是調用其上的方法時,就會出現這個錯誤。你能夠再Chrome Console中進行測試。
致使這個問題的緣由有許多,最多見的是渲染UI組件時對state不恰當的初始化。讓咱們看一個真實APP中可能出現該狀況的例子。咱們選擇了React,可是這樣的不良初始化也適用於Angular,Vue或是其它的框架。git
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> ); } }
這裏要注意兩件重要的事情:面試
componentWillMount
或是componentDidMount
。當Quiz
第一次渲染的時候,this.state.items
爲undefined。所以,item列表得到的值爲undefined,所以會報錯"Uncaught TypeError: Cannot read property ‘map’ of undefined"
。這個問題很容易解決。最簡單的方法是,在構造器裏面將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> ); } }
這和你的項目中的代碼不必定徹底相同,可是咱們但願給你提供一個解決或是避免該問題的思路。axios
這是一個在Safari中在undefined對象上訪問屬性或方法時報的錯。你能夠在Safari的控制檯上進行測試。這個錯誤和以前在Chrome中出現的錯誤是相同,只是報錯信息不一樣。設計模式
這是在Safari中在訪問null對象上的屬性或方法時報的錯。跨域
有趣的是,在JavaScript中,null和undefined是不一樣的,因此咱們看到了兩個不一樣的報錯信息。Undefined一般是指一個還沒有賦值的變量,而null是指該變量的值爲空。要想判斷兩者不等,應當使用嚴格的相等操做符:瀏覽器
在真實世界中,這種錯誤可能出現的緣由之一是你試圖在元素加載完成以前訪問DOM元素。對於空白的對象引用,DOM API會返回null。緩存
任何對DOM元素進行處理的JS代碼都應該都在DOM元素建立完成以後進行。JS代碼按照HTML中的規定按從上到下的順序進行解釋。因此,若是在DOM元素以前存在標籤,則腳本標籤內的JS代碼將在瀏覽器解析HTML頁面時執行。若是在加載腳本以前還沒有建立相關的DOM元素,就會出現此錯誤。安全
在這個例子中,咱們經過添加一個事件監聽器通知咱們頁面已經完成加載,來解決這個問題。一旦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>
當未捕獲的JavaScript錯誤跨越違法跨域策略的域邊界時,會發生腳本錯誤。好比,若是你將你的JavaScript代碼託管到CDN上,任何未被捕捉的錯誤(沒有被try-catch塊捕獲,被冒泡至window.onerror處理器的錯誤)將會被簡單的報告爲Script Error
,不包含任何有用的信息。這是瀏覽器的一種安全措施,旨在防止跨域傳遞數據。
要想得到真正的報錯信息,作如下幾步:
將Access-Control-Allow-Origin
設置爲.
來標記該資源從任何域均可以正常訪問。若是須要的話,也能夠將其設置爲本身的域名:好比,Access-Control-Allow-Origin: www.example.com
。可是,處理多個域會變的棘手,並且若是你是出於緩存的問題而使用CDN,那麼這樣子的代價可能不值得。詳情參考這裏
這裏給出一些在不一樣的環境中設置header的例子:
Apache
在你存放JavaScript的文件夾中添加一個.htacess文件,包含如下內容:
Header add Access-Control-Allow-Origin "*"
Nginx
將add_header指令添加到爲JavaScript文件提供服務的位置塊:
location ~ ^/assets/ { add_header Access-Control-Allow-Origin *; }
HAProxy
將如下內容添加到提供JavaScript的asset backend
rspadd Access-Control-Allow-Origin:\ *
crossorigin="annonymous"
屬性在HTML中,對於每個設置了Access-Control-Allow-Origin
頭的腳本,在腳本的標籤上添加crossorigin="anonymous"
屬性。在將crossorigin
屬性添加到腳本以前,請確保驗證是否爲腳本文件設置了header。在火狐瀏覽器中,若是設置了crossorigin
屬性可是沒有設置Access-Control-Allow-Origin
頭,該腳本不會執行。
這是在IE瀏覽器中報的錯,當你試圖調用一個undefined對象的方法時:
這等價於Chrome中的TypeError: ‘undefined’ is not a function
錯誤。是的,不一樣的瀏覽器對相同的錯誤會產生不一樣的報錯信息。
對於使用JavaScript命名空間的Web程序,在IE上運行時常常會遇到這個錯誤。當這個錯誤出現時,99.9%的狀況是由於IE不能將當前的命名空間的方法綁定到this
關鍵字上。好比,假設你有一個JS命名空間Rollbar
,其下有一個方法isAwesome()
。一般在Rollbar
命名空間下你會用以下的語法調用isAwesome
方法:
this.isAwesome();
Chrome,Firfox和Opera都會愉快的接受這個語法。可是,IE並不會。所以,使用JS命名空間時最安全的選擇是始終以實際的命名空間做爲前綴。
Rollbar.isAwesome();
這是當你在Chrome中試圖調用undefined的方法時出現的錯誤。
隨着JavaScript的編程技巧和設計模式在這幾年來愈來愈複雜,在回調和閉包中自我引用範圍的擴散也相應的增長,致使對this
出現困惑。
看下面這段代碼:
function testFunction() { this.clearLocalStorage(); this.timer = setTimeout(function() { this.clearBoard(); // what is "this"? }, 0); };
運行上面的代碼會出現"Uncaught TypeError: undefined is not a function."
報錯。緣由是當你試圖調用setTimeout()
方法時,你實際上在調用window.setTimeout()
方法。所以,一個匿名的函數傳入到setTimeout()
方法中,該函數的上下文其實是window對象,而window對象沒有clearBoard()方法。
一個傳統的,瀏覽器兼容的方案是將引用this
存儲到一個變量中,該引用可以被閉包繼承,以下:
function testFunction () { this.clearLocalStorage(); var self = this; // save reference to 'this', while it's still this! this.timer = setTimeout(function(){ self.clearBoard(); }, 0); };
在新版本的瀏覽器中,你可使用bind()
方法來傳遞引用:
function testFunction () { this.clearLocalStorage(); this.timer = setTimeout(this.reset.bind(this), 0); // bind to 'this' }; function reset(){ this.clearBoard(); //back in the context of the right 'this'! };
這是在Chrome中出現的一種錯誤。狀況之一是當你調用了一個沒有終止的遞歸方法:
當你向方法傳了一個超越規定範圍的值也可能會出現這個報錯。不少方法只接受特定範圍的值做爲輸入。好比,Number.toExponential(digits)
和Number.toFixed(digits)
只接受從0到20的數字,而Number.toPrecision(digits)
則接受1到21的數字。
var a = new Array(4294967295); //OK var b = new Array(-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!
這是在Chrome中讀取一個undefined對象的length屬性時報的錯。
你一般能夠在array中找到length屬性,可是你也可能在array尚未初始化或是變量名被隱藏在另外一個上下文中時遇到這個錯誤。讓咱們用下面這個例子理解一下這個報錯:
var testArray= ["Test"]; function testFunction(testArray) { for (var i = 0; i < testArray.length; i++) { console.log(testArray[i]); } } testFunction();
當你在方法中聲明參數時,這些參數成爲了局部變量。這意味着即便你有名爲testArray
的全局變量,方法中相同名稱的參數仍是會被當作局部變量。
你有兩種方法來結局這個問題:
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);
當咱們試圖訪問一個undefined的變量時,一般會返回undefined
,而咱們不能獲取或是設置undefined的屬性。這時候,應用就會拋出「Uncaught TypeError cannot set property of undefined.」
報錯。
若是test
對象不存在,也會拋出「Uncaught TypeError cannot set property of undefined.」
。
當你試圖訪問的變量爲undifined或是不在當前做用域範圍內時,會拋出這個錯誤:
若是你在使用事件處理系統時遇到這個報錯,請確保你將事件對象做爲參數傳入了處理方法中。老的瀏覽器器如IE會提供一個全局的事件變量,而Chrome會自動將事件變量附屬到handler上。Firfox不會自動添加它。而相似jQuery之類的庫則試圖規範化這個行爲。總之,你最好將event做爲採納數傳入事件處理方法中:
document.addEventListener("mousemove", function (event) { console.log(event); })
看來大多數的錯誤都是null或是undefined相關的錯誤。若是你在使用編譯器的嚴格模式選項,一個良好的類型檢查系統如Typescript
可以幫助你避免這些問題。它會在一個預期類型沒有被定義時警告你。即使沒有Typescript, 它也能幫助咱們使用防護性編程,在調用對象以前檢查對象是不是undefined。
咱們但願你可以學到一些新的內容,而且在將來可以避免這些錯誤,也可能這個指南幫你解決了一些頭疼的問題。不管如何,即使是最佳實踐,在編碼過程當中仍是會出現意料以外的錯誤。瞭解影響用戶使用的錯誤而且擁有能夠快速解決問題的工具是很重要的。
想要了解更多開發技術,面試教程以及互聯網公司內推,歡迎關注個人微信公衆號!將會不按期的發放福利哦~