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