Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
看了數(shù)千個項目后,發(fā)現(xiàn)了 10 個最常見的 JavaScript 錯誤。我們會告訴你什么原因?qū)е铝诉@些錯誤,以及如何防止這些錯誤發(fā)生。如果你能夠避免落入這些 “陷阱”,你將會成為一個更好的開發(fā)者。
如果你是一個 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> ); } }
兩個重要的流程:
這很容易解決。最簡單的方法:在構(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)錯誤的示例。
這是在 Safari 中讀取屬性或調(diào)用未定義對象上的方法時發(fā)生的錯誤。您可以在 Safari Developer Console 中輕松測試。這與第一點中提到的 Chrome 的錯誤基本相同,但 Safari 使用了不同的錯誤消息提示語。
這是在 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>
當未捕獲的 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í)行。
這是您在調(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();
當您調(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í)行上面的代碼會導致以下錯誤:
“Uncaught TypeError:this.clearBoard is not a function”。
原因應(yīng)該是清楚的,即執(zhí)行上下文不理解導致的指向錯誤。
當你調(diào)用一個不終止的遞歸函數(shù)就會發(fā)生這種錯誤。您可以在 Chrome 開發(fā)者控制臺中進行測試。
此外,如果您將值傳遞給超出范圍的函數(shù),也可能會發(fā)生這種情況。
許多函數(shù)只接受其輸入值的特定范圍的數(shù)字。 例如:
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!
這是 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);
當我們嘗試訪問一個未定義的變量時,它總是返回 undefined,我們不能獲取或設(shè)置任何未定義的屬性。 在這種情況下會將拋出 “Uncaught TypeError: Cannot set property”。
當您嘗試訪問未定義的變量或超出當前作用域的變量時,會引發(fā)此錯誤。 您可以在 Chrome 瀏覽器中測試。
如果在使用 event 時遇到此錯誤,請確保使用傳入的事件對象作為參數(shù)。像 IE 這樣的舊瀏覽器提供了一個全局變量事件,但并不是所有瀏覽器都支持。
document.addEventListener("mousemove", function (event) { console.log(event); })
我們看到上面的 10 個最常見的錯誤,其實所涉及的知識點并不難。當你認真讀過《你不知道的 JavaScript》上卷后,這些錯誤基本就不會再出現(xiàn)了。當然,歸根結(jié)底還是要有扎實的 javascript 基礎(chǔ),理解底層原理和實現(xiàn)。
么是JS延遲加載?
JS延遲加載,也就是等頁面加載完成之后再加載JavaScript文件
為什么讓JS實現(xiàn)延遲加載?
js的延遲加載有助于提高頁面的加載速度。
Js延遲加載的方式有哪些?一般有以下幾種方式:
·defer屬性
·async屬性
·動態(tài)創(chuàng)建DOM方式
·使用jQuery的getScript方法
·使用setTimeout延遲方法
·讓JS最后加載
HTML 4.01為<script>標簽定義了defer屬性。標簽定義了defer屬性元素中設(shè)置defer屬性,等于告訴瀏覽器立即下載,但延遲執(zhí)行標簽定義了defer屬性。
用途:表明腳本在執(zhí)行時不會影響頁面的構(gòu)造。也就是說,腳本會被延遲到整個頁面都解析完畢之后再執(zhí)行在<script>元素中設(shè)置defer屬性,等于告訴瀏覽器立即下載,但延遲執(zhí)行
<!DOCTYPE html>
<html>
<head>
<script src="test1.js" defer="defer"></script>
<script src="test2.js" defer="defer"></script>
</head>
<body>
<!--這里放內(nèi)容-->
</body>
</html>
說明:雖然<script>元素放在了<head>元素中,但包含的腳本將延遲瀏覽器遇到</html>標簽后再執(zhí)行HTML5規(guī)范要求腳本按照它們出現(xiàn)的先后順序執(zhí)行。在現(xiàn)實當中,延遲腳本并不一定會按照順序執(zhí)行defer屬性只適用于外部腳本文件。支持HTML5的實現(xiàn)會忽略嵌入腳本設(shè)置的defer屬性
HTML5 為<script>標簽定義了async屬性。與defer屬性類似,都用于改變處理腳本的行為。同樣,只適用于外部腳本文件。標簽定義了async屬性。與defer屬性類似,都用于改變處理腳本的行為。同樣,只適用于外部腳本文件。
目的:不讓頁面等待腳本下載和執(zhí)行,從而異步加載頁面其他內(nèi)容。異步腳本一定會在頁面 load 事件前執(zhí)行。不能保證腳本會按順序執(zhí)行
<!DOCTYPE html>
<html>
<head>
<script src="test1.js" async></script>
<script src="test2.js" async></script>
</head>
<body>
<!--這里放內(nèi)容-->
</body>
</html>
async和defer一樣,都不會阻塞其他資源下載,所以不會影響頁面的加載。
缺點:不能控制加載的順序
//這些代碼應(yīng)被放置在</ body>標簽前(接近HTML文件底部)
<script type="text/javascript">
function downloadJSAtOnload() {
varelement=document .createElement("script");
element.src="defer.js";
document.body.appendChild(element);
}
if (window. addEventListener)
window.addEventListener("load" ,downloadJSAtOnload, false);
else if (window.attachEvent)
window.attachEvent("onload", downloadJSAtOnload) ;
else
window. onload=downloadJSAtOnload;
</script>
$.getScript("outer.js" , function(){ //回調(diào)函數(shù),成功獲取文件后執(zhí)行的函數(shù)
console.log(“腳本加載完成")
});
<script type="text/javascript" >
function A(){
$.post("/1ord/1ogin" ,{name:username,pwd:password},function(){
alert("Hello");
});
}
$(function (){
setTimeout('A()', 1000); //延遲1秒
})
</script>
把js外部引入的文件放到頁面底部,來讓js最后引入,從而加快頁面加載速度例如引入外部js腳本文件時,如果放入html的head中,則頁面加載前該js腳本就會被加載入頁面,而放入body中,則會按照頁面從上倒下的加載順序來運行JavaScript的代碼。所以我們可以把js外部引入的文件放到頁面底部,來讓js最后引入,從而加快頁面加載速度。
上述方法2也會偶爾讓你收到Google頁面速度測試工具的“延遲加載javascript”警告。所以這里的解決方案將是來自Google幫助頁面的推薦方案。
//這些代碼應(yīng)被放置在</body>標簽前(接近HTML文件底部)
<script type="text/javascript">
function downloadJSAtonload() {
var element=document.createElement("script");
element.src="defer.js";
document.body.appendChild(element);
}
if (window.addEventListener)
window.addEventListener("load", downloadJSAtOnload, false);
else if (window.attachEvent )
window.attachEvent("onload", downloadJSAtonload);
else window.onload=downloadJSAtOnload;
</script>
這段代碼意思等到整個文檔加載完后,再加載外部文件“defer.js”。
使用此段代碼的步驟:
6.1)復制上面代碼
6.2)粘貼代碼到HTML的標簽前 (靠近HTML文件底部)
6.3)修改“defer.js”為你的外部JS文件名
6.4)確保文件路徑是正確的。例如:如果你僅輸入“defer.js”,那么“defer.js”文件一定與HTML文件在同一文件夾下。
注意:
這段代碼直到文檔加載完才會加載指定的外部js文件。因此,不應(yīng)該把那些頁面正常加載需要依賴的javascript代碼放在這里。而應(yīng)該將JavaScript代碼分成兩組。一組是因頁面需要而立即加載的javascript代碼,另外一組是在頁面加載后進行操作的javascript代碼(例如添加click事件。
本已經(jīng)過原作者 Viduni Wickramarachchi 授權(quán)翻譯。
你是否經(jīng)歷過JavaScript中的某些值比較沒有得到預期結(jié)果的情況?
看下面的情況:
即使[]==0結(jié)果為真,if[]條件也沒有根據(jù)結(jié)果執(zhí)行。有沒有想過為什么會這樣?
本文主要說明這些值比較的工作原理以及影響它們的因素。在深入解釋之前,大家要熟悉一個概念:類型轉(zhuǎn)換。
這也稱為類型強制。對于不熟悉此概念的人來說,它只是將值從一種數(shù)據(jù)類型自動轉(zhuǎn)換為另一種數(shù)據(jù)類型。
看個例子,大家會更清楚明白。
在此示例中,定義的兩個變量具有兩種類型;字符串和數(shù)字。但是,當我們使用 ==(非嚴格比較)進行比較時,結(jié)果為true。原因是當我們使用==比較這兩個時,JavaScript 會自動嘗試將String類型轉(zhuǎn)換為Number類型以產(chǎn)生結(jié)果。這是一種強制轉(zhuǎn)換。
JavaScript中有多種強制類型。
在上述情況下,類型轉(zhuǎn)換沒有害處。但是,在許多情況下,類型強制會導致問題。
我們看下面例子。
在這里,JavaScript已將Number類型轉(zhuǎn)換為String。這與相等比較中發(fā)生的情況相反。我們預期的結(jié)果是450。但是,我們得到了String輸出。
現(xiàn)在,我們對類型轉(zhuǎn)換以及為什么要避免使用類型轉(zhuǎn)換有了清晰的了解,讓我們看看如何避免類型轉(zhuǎn)換。這是本文最重要的部分。因此,請坐下來,喝咖啡并集中精力
如果你需要對用戶輸入或任何其他值使用數(shù)學運算,則在執(zhí)行該運算之前,自己進行一次顯式轉(zhuǎn)換會更安全。這樣,可以避免任何意外行為。
如果需要連接兩個數(shù)字,則使用模板文字會更安全。特別是不確定值的類型。
也可以使用顯式轉(zhuǎn)換來導出相同的結(jié)果。
前面我們看到,當使用==時,JavaScript 會執(zhí)行隱式類型轉(zhuǎn)換,這會導致不一致的結(jié)果。因此,在我們的生產(chǎn)代碼中使用它是不安全的。
為了得出預期的結(jié)果,應(yīng)該始終使用===進行比較。三等號隱含地表示:
我可以同時了解變量的值和類型
因此,如果將數(shù)字和字符串與值進行比較,結(jié)果將是false,因為它也會考慮變量的類型。
這是獲得預期一致結(jié)果的更安全的方法。
在JavaScript中,數(shù)據(jù)類型有兩種變體。
到目前為止,我們已經(jīng)討論了原始數(shù)據(jù)類型的類型轉(zhuǎn)換。我提供的第一個示例涉及非原始數(shù)據(jù)類型,例如數(shù)組。
所有非原始數(shù)據(jù)類型都有一個名為.toPrimitive()的內(nèi)置函數(shù)。比較非原始值和原始值時,此函數(shù)會自動將非原始類型轉(zhuǎn)換為原始類型。在我們看過的第一個示例中,當使用此函數(shù)進行非嚴格比較時,空數(shù)組將轉(zhuǎn)換為空字符串。確切地說,用于執(zhí)行此轉(zhuǎn)換的確切函數(shù)是toString()。因此,空數(shù)組(將轉(zhuǎn)換為空字符串)等于0。
正如我們前面所看到的,當在if條件中檢查空數(shù)組時,將執(zhí)行條件中的行。但是,如果空數(shù)組隱式轉(zhuǎn)換為0怎么辦?
這是在單獨的JavaScript條件下進行的: 真值和虛值 。除了true以外,JavaScript 將大部分有值的視為真值,除了少數(shù)值。例如,0,-0,""被視為虛值。由于空數(shù)組不被認為是虛值,當在條件中檢查它時,它將作為真值執(zhí)行。(這里不會發(fā)生類型轉(zhuǎn)換,空數(shù)組保留為數(shù)組,這是類型轉(zhuǎn)換不一致的另一個例子。)
JavaScript作為一種松散類型語言,執(zhí)行隱式類型轉(zhuǎn)換。這會導致不一致和意想不到的結(jié)果。因此,我們應(yīng)該在任何時候都避免這種類型轉(zhuǎn)換。如果不確定值的類型,可以使用typeof檢查。檢查類型可以讓我們更好地理解應(yīng)該如何進行轉(zhuǎn)換。
~完,我是刷碗智,我要去刷碗了,骨的白~
作者:Viduni Wickramarachchi 譯者:前端小智 來源:stackabuse
原文:https://blog.bitc.io/how-to-avoid-javascript-type-conversions-29e1258f37d8
*請認真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。