數的各種執行方式
① 普通函數調用(包括匿名函數自調用)
② 構造函數執行 new 函數
③ 作為對象的成員方法執行
④ 通過call和apply執行
call和apply可以明顯控制變量污染的風險。
實例1:
<script type="text/javascript">
//call方式執行函數
function say(){
document.write("在"+this.addr+'演講');
}
var dog = {addr:'德國',color:'black'};
//dog對象去調用say()函數執行
//通過"call()"實現
//函數.call(函數內部this的指引,形參1, 形參2, 形參3...);
say.call(dog);//在德國演講
var cat = {addr:'波斯灣'}
say.call(cat);//在波斯灣演講
//dog.express = say;
//dog.express = "abc";//同名的express把同名的方法污染了
//dog.express();
</script>
實例2:
<script type="text/javascript">
//call方式執行函數
function say(){
document.write("在"+this.addr+'演講');
}
var dog = {addr:'德國',color:'black'};
//dog對象去調用say()函數執行
//通過"call()"實現
//函數.call(函數內部this的指引, 形參1, 形參2, 形參3...);
say.call(dog);//在德國演講
var cat = {addr:'波斯灣'}
say.call(cat);//在波斯灣演講
dog.express = say;
dog.express(); //顯示:在德國演講
dog.express = "abc";//同名的express把同名的方法污染了
document.write(dog.express);//顯示:abc
dog.express(); //沒有結果
</script>
實例3:
<script type="text/javascript">
//call方式執行函數
function run(time,addr){
document.write(this.name+"時間:"+time+",在"+addr+"跑步");
}
var tiger = {name:'東北虎',weight:220}
run.call(tiger); //東北虎時間:undefined,在undefined跑步
run.call(tiger,'早上','深山老林');//東北虎時間:早上,在深山老林跑步
var cat = {name:'kitty'}
run.apply(cat,['傍晚','屋后']);//kitty時間:傍晚,在屋后跑步
</script>
注意:call和apply的區別在于語法格式區別
實例3:
JavaScript在我們日常開發中扮演著至關重要的角色,大部分時間都與它打交道,因此對這門語言的了解再多也不為過。
雖然有很多關于JavaScript的書籍和資料,但絕大多數都從JavaScript語言本身的角度去分析其語法和語義,很少有與JavaScript具體執行過程相關的分析資料。因此,我邀請大家一起從瀏覽器的角度來看一下一段JavaScript代碼到底是如何執行的。
以 Google 瀏覽器為例,JavaScript 代碼由 V8 引擎負責解釋執行。代碼的執行總體上經歷以下三個步驟:
整個流程圖如下:
為什么要將代碼解析成AST呢,直接用原代碼不行嗎?
答案肯定是不行,因為瀏覽器并不能理解我們所寫的代碼。這個理由就像將html需要解析為DOM一樣,瀏覽器并不能理解我們所編寫的代碼,需要將我們寫的代碼編譯成瀏覽器能夠理解的結構,也就是AST。
AST是一種非常重要的數據結構,許多著名的項目都在使用它。
比如Babel,Babel被用于代碼轉換,將es6的代碼轉換為es5的代碼,用于解決兼容性問題,Babel的工作原理就是將es6源碼解析為AST,再將es6的AST轉化為es5的AST,最后利用es5的AST生成源碼。
類似的還有ESlint,ESlint是用來檢查js代碼規范的插件,它的檢查流程也是將源碼解析成AST,再利用AST來檢查代碼的規范問題。
將源碼解析成AST通常需要兩個步驟:
詞法分析又稱為分詞(tokenize),是將一行行代碼拆分為一個個token。如下圖:
語法分析又稱解析,將上一步生成的token數據根據語法規則轉化為AST。在轉化過程中,如果不符合語法規則,會終止轉化并拋出一個語法錯誤。轉化成的AST如下圖:
可以通過 AST可視化網站:https://astexplorer.net/ 來體驗成生成AST。
除了生成AST,該階段還會創建代碼塊的執行上下文。其實上述的編譯過程都發生在v8引擎內部,對于我們開發者來說完全是黑盒的,但是執行上下文這個概念卻是至關重要的,因為它是代碼運行的基本環境。
生成AST后,接著解釋器 Ignition 就會將AST轉化為字節碼并解釋執行字節碼。
為什么又要將AST解析成字節碼呢,上文不是說過瀏覽器可以理解AST嗎?答案是解決內存占用的問題。
其實一開始v8引擎并不會將AST轉化為字節碼,而是直接將AST轉化為機器碼并執行。雖然執行效率特別高,但是同時暴露出來了嚴重的內存占用問題。因為v8引擎要消耗大量的內存來存放轉化后的機器碼。為了解決內存占用的問題,v8團隊引入了字節碼,花了大量的時間,完成了當前的這套架構。
解釋下字節碼是什么,字節碼是介于AST和機器碼之間的一種代碼,字節碼需要解釋器將其轉化為機器碼之后才能執行。為了更好的去理解字節碼,下面是源碼、字節碼、機器碼的一個對比圖:
可以看出,字節碼對比機器碼占用的空間要小得多。所以使用字節碼可以減少系統的內存使用。
對于一段第一次被執行的字節碼,解釋器 Ignition 會逐條解釋并執行。在執行的過程中,如果發現熱點代碼(Hotspot),比如說一段被重復執行多次的代碼,后臺的 編譯器 TurboFan 就會將該段熱點字節碼編譯成機器碼并保存起,當下次在執行到該段熱點代碼時,只需要執行編譯后的機器碼就行,這樣就節提升了大量的執行效率。執行流程如下圖所示:
這種字節碼配合解釋器和編譯器的技術正是最近很火的技術————即時編譯(JIT) 。
至此本文分析完了 JavaScript 代碼執行的整個階段。在宏觀角度上 JavaScript 代碼會經歷如下步驟:
整個JavaScript代碼執行是慢啟動,越執行越快。這種字節碼配合解釋器和編譯器的技術叫做即時編譯(JIT)。v8引擎也是基于這種技術來實現對內存占用和執行效率的調控。
最后在JavaScript代碼執行過程中,最重要的部分就是執行上下文了,它是代碼運行時的基本環境。
來源:微信公眾號:大轉轉FE
出處:https://mp.weixin.qq.com/s/ZFtxVyERBkCgSadQqYxijQ
了執行Javascript,需要在HTML文件內以特定的方式書寫JavaScript的代碼,JavaScript的書寫方法有多種,其執行的流程也各不相同:
此種嵌入方法無法操作<script>之后的DOM元素。因為<script>之后的DOM元素還未構造,因此在<script>標簽內就無法取得位于其后的DOM元素。
此種嵌入方法可以指定defer、async屬性。defer可以推遲執行,async可以異步執行。
此種嵌入方法在頁面讀取完后再對其執行,所以可以對所有的DOM元素操作。
<body onload="alert('hello')">
window.onload = function(){alert('hello');};
當window.onload事件觸發時,頁面上所有的DOM、樣式表、腳本、圖片、flash都已經加載完成了。
//window.onload不能同時編寫多個。
//以下代碼無法正確執行,結果只輸出第二個。
window.onload = function(){
alert("test1");
};
window.onload = function(){
alert("test2");
};
//$(document).ready()能同時編寫多個
//結果兩次都輸出
$(document).ready(function(){
alert("Hello World");
});
$(document).ready(function(){
alert("Hello again");
});
window.onload和body中onload也有些許區別:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.0.js"></script>
<script language="javascript">
window.onload = haha;
function haha(){console.log("window.onload");}
if(document.addEventListener){
function DOMContentLoaded(){
console.log("DOMContentLoaded");
}
document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
}</script>
</head>
<body onload="console.log('bodyonload');">
<div id="div1">a</div>
</body>
</html>
在IE10和FireFox下,結果為 :
"DOMContentLoaded"
"bodyonload"
說明body中的onload會覆蓋window.onload
在chrome下,結果為:
DOMContentLoaded
window.onload
bodyonload
然后,如果把javascript代碼移到最下面,結果又會是什么樣呢?
chrome和IE10、FireFox的結果竟然是一樣的:
DOMContentLoaded
window.onload
IE 10、Fire Fox可以理解,window.on load和body中的 on load 誰在下面就是誰覆蓋誰,只會執行后面的那個。
onload方法可能需要等待時間,而本方法可以在完成HTML解析后發生的事件,減少等待時間。
在chrome、IE10和FireFox中,執行結果是:DOMContentLoaded然后才是onload的輸出。所以說一般情況下,DOMContentLoaded事件要在window.onload之前執行,當DOM樹構建完成的時候就會執行DOMContentLoaded事件。
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
<script type="text/javascript" src="jquery2.js"></script>
<script language="javascript">
window.onload = haha;
function haha(){console.log(document.getElementById("div1"));}
if(document.addEventListener){
function DOMContentLoaded(){
console.log("DOMContentLoaded");
}
document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
}
</script>
</head>
<body>
<div id="div1">a</div>
</body>
</html>
如果你是個jQuery使用者,你可能會經常使用$(document).ready();或者$(function(){}),這都是使用了DOMContentLoaded事件
5.1 使用原生js方法
動態創建script標簽,并指定script的src屬性
function loadJs(url, callback) {
var script = document.createElement('script');
script.type = "text/javascript";
if (typeof(callback) != "undefined") {
if (script.readyState) {
script.onreadystatechange = function() {
if (script.readyState == "loaded" || script.readyState == "complete") {
script.onreadystatechange = null;
callback();
}
}
} else {
script.onload = function() {
callback();
}
}
}
script.src = url;
document.body.appendChild(script);
}
loadJs("test.js", function() {
alert('done');
});
還可以使用同樣的原理動態加載css文件,只不過插入的的父節點是head標簽。
5.2 使用document.write/writeln()方式
該種方式可以實現js文件的動態加載,原理就是在重寫文檔流,這種方式會導致整個頁面重繪。
document.writeln("<script src=\"http://lib.sinaapp.com/js/jquery/1.6/jquery.min.js\"></script>");
需要注意的是特殊字符的轉義。
5.3 使用jQuery
使用getScript(url,callback)方法實現動態加載js文件
$.getScript('test.js',function(){
alert('done');
});
-End-
*請認真填寫需求信息,我們會在24小時內與您取得聯系。