javascript 是一門單線程的語言,在同一個時間只能做完成一件任務,如果有多個任務,就必須排隊,前面一個任務完成,再去執行后面的任務。作為瀏覽器端的腳本語言,javascript 的主要功能是用來和用戶交互以及操作 dom。假設 javascript 不是單線程語言,在一個線程里我們給某個 dom 節點增加內容的時候,另一個線程同時正在刪除這個 dom 節點的內容,則會造成混亂。
由于 js 單線程的設計,假設 js 程序的執行都是同步。如果執行一些耗時較長的程序,例如 ajax 請求,在請求開始至請求響應的這段時間內,當前的工作線程一直是空閑狀態, ajax 請求后面的 js 代碼只能等待請求結束后執行,因此會導致 js 阻塞的問題。
javascript 單線程指的是瀏覽器中負責解釋和執行 javascript 代碼的只有一個線程,即為 js 引擎線程,但是瀏覽器的渲染進程是提供多個線程的,如下:
為解決上述類似上述 js 阻塞的問題,js 引入了同步和異步的概念。
“同步”就是后一個任務等待前一個任務結束后再去執行。
“異步”與同步不同,每一個異步任務都有一個或多個回調函數。webapi 會在其相應的時機里將回調函數添加進入消息隊列中,不直接執行,然后再去執行后面的任務。直至當前同步任務執行完畢后,再把消息隊列中的消息添加進入執行棧進行執行。
異步任務在瀏覽器中一般是以下:
“棧”是一種數據結構,是一種線性表。特點為 LIFO,即先進后出 (last in, first out)。
利用數組的 push 和 shift 可以實現壓棧和出棧的操作。
在代碼運行的過程中,函數的調用會形成一個由若干幀組成的棧。
function foo(b) {
let a=10;
return a + b + 11;
}
function bar(x) {
let y=3;
return foo(x * y);
}
console.log(bar(7))
上面代碼最終會在控制臺打印42,下面梳理一下它的執行順序。
對象被分配在堆中,堆是一個用來表示一大塊(通常是非結構化的)內存區域的計算機術語。
首先,stack 是有結構的,每個區塊按照一定次序存放,可以明確知道每個區塊的大小;heap 是沒有結構的,數據可以任意存放。因此,
stack 的尋址速度要快于 heap。
其次,每個線程分配一個 stack,每個進程分配一個 heap,也就是說,stack 是線程獨占的,heap 是線程共用的。
此外,stack 創建的時候,大小是確定的,數據從超過這個大小,就發生 stack overflow 錯誤,而 heap 的大小是不確定的,
需要的話可以不斷增加。
public void Method1()
{
int i=4;
int y=2;
class1 cls1=new class1();
}
上面代碼這三個變量和一個對象實例在內存中的存放方式如下。
從上圖可以看到,i、y和cls1都存放在stack,因為它們占用內存空間都是確定的,而且本身也屬于局部變量。但是,cls1指向的對象實例存放在heap,因為它的大小不確定。作為一條規則可以記住,所有的對象都存放在heap。
接下來的問題是,當Method1方法運行結束,會發生什么事?
回答是整個stack被清空,i、y和cls1這三個變量消失,因為它們是局部變量,區塊一旦運行結束,就沒必要再存在了。而heap之中的那個對象實例繼續存在,直到系統的垃圾清理機制(garbage collector)將這塊內存回收。因此,一般來說,內存泄漏都發生在heap,即某些內存空間不再被使用了,卻因為種種原因,沒有被系統回收。
隊列是一種數據結構,也是一種特殊的線性表。特點為 FIFO,即先進先出(first in, first out)
利用數組的 push 和 pop 可實現入隊和出隊的操作。
事件循環和事件隊列的維護是由事件觸發線程控制的。
事件觸發線程線程同樣是由瀏覽器渲染引擎提供的,它會維護一個事件隊列。
js 引擎遇到上文所列的異步任務后,會交個相應的線程去維護異步任務,等待某個時機,然后由事件觸發線程將異步任務對應的回調函數加入到事件隊列中,事件隊列中的函數等待被執行。
js 引擎在執行過程中,遇到同步任務,會將任務直接壓入執行棧中執行,當執行棧為空(即 js 引擎線程空閑), 事件觸發線程 會從事件隊列中取出一個任務(即異步任務的回調函數)放入執行在棧中執行。
執行完了之后,執行棧再次為空,事件觸發線程會重復上一步的操作,再從事件隊列中取出一個消息,這種機制就被稱為 事件循環 (Event Loop)機制。
為了更好地理解Event Loop,請看下圖(轉引自Philip Roberts的演講《Help, I'm stuck in an event-loop》)。
例子代碼:
console.log('script start')
setTimeout(()=> {
console.log('timer 1 over')
}, 1000)
setTimeout(()=> {
console.log('timer 2 over')
}, 0)
console.log('script end')
// script start
// script end
// timer 2 over
// timer 1 over
模擬 js 引擎對其執行過程:
此時,執行棧為空,js 引擎線程空閑。便從事件隊列中讀取任務,此時隊列如下:
注意點:
上面,timer 2 的延時為 0ms,HTML5標準規定 setTimeout 第二個參數不得小于4(不同瀏覽器最小值會不一樣),不足會自動增加,所以 "timer 2 over" 還是會在 "script end" 之后。
就算延時為0ms,只是 time 2 的回調函數會立即加入事件隊列而已,回調的執行還是得等到執行棧為空時執行。
在 ES6 新增 Promise 處理異步后,js 執行引擎的處理過程又發生了新的變化。
看代碼:
console.log('script start')
setTimeout(function() {
console.log('timer over')
}, 0)
Promise.resolve().then(function() {
console.log('promise1')
}).then(function() {
console.log('promise2')
})
console.log('script end')
// script start
// script end
// promise1
// promise2
// timer over
這里又新增了兩個新的概念, macrotask (宏任務)和 microtask (微任務)。
所有的任務都劃分到宏任務和微任務下:
js 引擎首先執行主代碼塊。
執行棧每次執行的代碼就是一個宏任務,包括任務隊列(宏任務隊列)中的。執行棧中的任務執行完畢后,js 引擎會從宏任務隊列中去添加任務到執行棧中,即同樣是事件循環的機制。
當在執行宏任務遇到微任務 Promise.then 時,會創建一個微任務,并加入到微任務隊列中的隊尾。
微任務是在宏任務執行的時候創建的,而在下一個宏任務執行之前,瀏覽器會對頁面重新渲染(task >> render >> task(任務隊列中讀取))。 同時,在上一個宏任務執行完成后,頁面渲染之前,會執行當前微任務隊列中的所有微任務。
所以上述代碼的執行過程就可以解釋了。
js 引擎執行 promise.then 時,promise1、promise2 被認為是兩個微任務按照代碼的先后順序被加入到微任務隊列中,script end執行后,棧空。
此時當前宏任務(script 主代碼塊)執行完畢,并不從當前宏任務隊列中讀取任務。而是立馬清空當前宏任務所產生的微任務隊列。將兩個微任務依次放入執行棧中執行。執行完畢,打印 promise1、promise2。棧空。 此時,第一輪事件循環結束。
緊接著,再去讀取宏任務隊列中的任務,time over 被打印。棧空。
因此,宏任務和微任務的執行機制如下:
因為,async 和 await 本質上還是基于 Promise 的封裝,而 Promise 是屬于微任務的一種。所以使用 await 關鍵字與 Promise.then 效果類似:
setTimeout(_=> console.log(4))
async function main() {
console.log(1)
await Promise.resolve()
console.log(3)
}
main()
console.log(2)
// 1
// 2
// 3
// 4
async 函數在 await 之前的代碼都是同步執行的, 可以理解為 await 之前的代碼都屬于 new Promise 時傳入的代碼,await 之后的所有代碼都是 Promise.then 中的回調,即在微任務隊列中。
參考:
原文作者:大芒果哇
原文地址:https://www.cnblogs.com/shenggao/p/13799566.html
者:櫻桃小丸子兒
來源:https://www.jianshu.com/p/2f7eb1ad7174
1、Html5與html4相比,各有何優缺點? 怎樣處理html5新標簽的兼容性問題?
html5余html4的異同請看以下的鏈接
html5與html4的異同
兼容性問題
IE8/IE7/IE6支持通過document.createElement方法產生的標簽,可以利用這一特性讓這些瀏覽器支持HTML5新標簽,瀏覽器支持新標簽后,還需要添加標簽默認的樣式。當然也可以直接使用成熟的框架、比如html5shim。
<!--[if lt IE 9]> <script> src="http://html5shim.googlecode.com/svn/trunk/html5.js"</script> <![endif]-->
1、JS如何使頁面跳轉?怎么引入一個外部JS文件?
①直接在head標簽內寫入js代碼,如下
<Script Language="JavaScript">js 語句</Script>
②引入寫好的js文件,使用語句
<script language="JavaScript" src="test.js"></script>
也是直接放入到head標簽里頭,也有的是放在</body>前面。
2、輸入框的驗證用什么事件?
change(fn)
3、undefined與null有何異同?
null是一個表示"無"的對象,轉為數值時為0;undefined是一個表示"無"的原始值,轉為數值時為NaN。
undefined:
(1)變量被聲明了,但沒有賦值時,就等于undefined。
(2) 調用函數時,應該提供的參數沒有提供,該參數等于undefined。
(3)對象沒有賦值的屬性,該屬性的值為undefined。
(4)函數沒有返回值時,默認返回undefined。
null:
(1) 作為函數的參數,表示該函數的參數不是對象。
(2) 作為對象原型鏈的終點。
它們都表示空,轉換為boolean后都為false,但是null代表一個對象變量已經被初始化,但未裝入對象;undefined表示未初始化變量
4、===與==有何異同?
相同點:都是判定兩個值是否相等
不同點:==不會判斷類型,而===會判斷類型
5、如何判斷一個變量的值是否為數字?以及有哪些手段判斷變量值的數據類型?
全局函數isNaN可以判斷一個變量的值是否為數字。
可以使用運算符type、instanceof判斷變量值的數據類型。
6、什么是Bom什么是Dom?你如何理解Dom?
鏈接標記target與Dom,Bom
7、Array的join、push、splice、slice各有何用途,splice與slice有何異同?
join:使用指定間隔符連接所有元素為字符串
push:在尾部添加元素并維護array實例的length
splice與slice都是截取一部分元素。不同的在于:slice返回截取后的新實例,splice在原array實例上操作,更詳細的請見下文鏈接。
JS中數組對象詳解
8、如何阻止表單提交?
在onsubmit事件中返回false
9、如何動態操作表格?
可以像普通dom一樣操作,但是因為表格的dom比較復雜,所以我通常是使用table的insertRow、deleteRow及tr對象的insetCell、deleteCell操作。
10、String.match與RegExp.exec有何區別?
match只會返回沒有分組的全部匹配結果或者有分組的第一次匹配結果;
而exec可以利用循環返回全部匹配結果。
11、為驗證手機號寫一個正則。
function checkSubmitMobil() { if ($("#phoneNum").val()=="") { alert("手機號碼不能為空!"); //$("#moileMsg").html("<font color='red'>手機號碼不能為空!</font>"); $("#mobile").focus(); return false; } if (!$("#phoneNum").val().match(/^(?:13\d|15\d|18\d)\d{5}(\d{3}|\*{3})$/)) { alert("手機號碼格式不正確!"); //$("#moileMsg").html("<font color='red'>手機號碼格式不正確!請重新輸入!</font>"); $("#phoneNum").focus(); return false; } return true; }
12、正則的i標記與g標記各有何用途?
i:不區分大小寫;
g:全局匹配。
13、為String添加trim()方法。
String.prototype.trim=function() { return this.replace(/^ +| +$/g,""); }
14、簡述COOKIE。在JS中如何操作Cookie?
簡述cookie,在JS中如何操作cookie
15、談談javascript數組排序方法sort()的使用,重點介紹sort()參數的使用及其內部機制。
JS數組排序方法sort()的使用
16、談談innerHTML outerHTML innerText之間的區別。
①innerHTML是w3c的html dom定義的方法,而后兩者是IE獨有的方法;
②innerHTML代表一個元素節點內由所有子節點,不包括當前節點組成的html代碼;
③outerHTML代表一個元素節點內由所有子節點和當前節點組成的html代碼;
④innerText代表一個元素節點內由所有子文本節點內容組成的文本;
17、在JavaScript中定時調用函數 foo() 如何寫?
setTimeout( foo, 1000 //這里設置延時數 );
18、setTimeout與setInterval有何區別?
①setTimeout和setInterval的語法相同。它們都有兩個參數,一個是將要執行的代碼字符串,還有一個是以毫秒為單位的時間間隔,當過了那個時間段之后就將執行那段代碼。
②不過這兩個函數還是有區別的,setInterval在執行完一次代碼之后,經過了那個固定的時間間隔,它還會自動重復執行代碼,而setTimeout只執行一次那段代碼。
19、你在js中用過array嗎?如果用過,array中添加數據用什么方法?
在尾部添加使用push();
在頭部添加使用unshift();
在任意位置添加使用splice(),但要注意把它的刪除個數設置為0;
array詳細介紹請看下文鏈接
JS中數組對象詳解
20、簡述javascript的優缺點。
優點:簡單易用,與Java有類似的語法,可以使用任何文本編輯工具編寫,只需要瀏覽器就可執行程序,并且事先不用編譯,逐行執行,無需進行嚴格的變量聲明,而且內置大量現成對象,編寫少量程序可以完成目標;
缺點:不適合開發大型應用程序;
21、Javascript有哪些內置對象?
只有Math和Global(在瀏覽器環境中,Global就是Window)
22、列舉Javascript的本地對象。
Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError
23、javascript的typeof返回哪些數據類型
object number function boolean undefind string
24、例舉3種強制類型轉換和2種隱式類型轉換?
強制(parseInt,parseFloat,number)
隱式(==–===)
25、IE和DOM事件流的區別
①執行順序不一樣、
②參數不一樣
③事件加不加on
④this指向問題
26、事件綁定和普通事件有什么區別
①事件綁定就是針對dom元素的事件,綁定在dom元素上
②普通事件即為非針對dom元素的事件
27、事件委托是什么
利用事件冒泡的原理,讓自己的所觸發的事件,由他的父元素代替執行!
通俗的講,事件就是onclick,onmouseover,onmouseout,等就是事件,委托呢,就是讓別人來做,這個事件本來是加在某些元素上的,然而你卻加到別人身上來做,完成這個事件。
例子請看以下鏈接
JS中的事件委托
28、閉包是什么,有什么特性,對頁面有什么影響
什么是閉包
“官方”的解釋:所謂“閉包”,指的是一個擁有許多變量和綁定了這些變量的環境的表達式(通常是一個函數),因而這些變量也是該表達式的一部分。
在 Scala、Scheme、Common Lisp、Smalltalk、Groovy、JavaScript、Ruby、 Python、Go、Lua、objective c、swift 以及Java(Java8及以上)等語言中都能找到對閉包不同程度的支持。
通俗的講就是函數a的內部函數b,被函數a外部的一個變量引用的時候,就創建了一個閉包。
閉包的特性:
①.封閉性:外界無法訪問閉包內部的數據,如果在閉包內聲明變量,外界是無法訪問的,除非閉包主動向外界提供訪問接口;
②.持久性:一般的函數,調用完畢之后,系統自動注銷函數,而對于閉包來說,在外部函數被調用之后,閉包結構依然保存在;
對頁面的影響
使用閉包會占有內存資源,過多的使用閉包會導致內存溢出等。
詳細請看以下推薦鏈接
深入理解JavaScript的閉包特性 如何給循環中的對象添加事件
29、javascript的本地對象,內置對象和宿主對象
①本地對象為array obj regexp等可以new實例化
②內置對象為gload Math 等不可以實例化的
③宿主為瀏覽器自帶的document,window 等
30、編寫一個數組去重的方法
思路:
1.創建一個新的數組存放結果
2.創建一個空對象
3.for循環時,每次取出一個元素與對象進行對比,如果這個元素不重復,則把它存放到結果數組中,同時把這個元素的內容作為對象的一個屬性,并賦值為1,存入到第2步建立的對象中。
說明:至于如何對比,就是每次從原數組中取出一個元素,然后到對象中去訪問這個屬性,如果能訪問到值,則說明重復。
代碼如下:
Array.prototype.unique3=function(){ var res=[]; var json={}; for(var i=0; i < this.length; i++){ if(!json[this[i]]){ res.push(this[i]); json[this[i]]=1; } } return res; } var arr=[112,112,34,'你好',112,112,34,'你好','str','str1']; alert(arr.unique3());
31、this對象的理解
①this總是指向函數的直接調用者(而非間接調用者);
②如果有new關鍵字,this指向new出來的那個對象;
③在事件中,this指向觸發這個事件的對象,特殊的是,IE中的attachEvent中的this總是指向全局對象Window;
32、eval是做什么的?
①它的功能是把對應的字符串解析成JS代碼并運行;
②應該避免使用eval,不安全,非常耗性能(2次,一次解析成js語句,一次執行)。
③由JSON字符串轉換為JSON對象的時候可以用eval,var obj=eval('('+ str +')');
33、new操作符具體干了什么呢?
①創建一個空對象,并且 this 變量引用該對象,同時還繼承了該函數的原型。
②屬性和方法被加入到 this 引用的對象中。
③新創建的對象由 this 所引用,并且最后隱式的返回 this 。
34、call() 和 apply() 的區別和作用?
①apply()函數有兩個參數:第一個參數是上下文,第二個參數是參數組成的數組。如果上下文是null,則使用全局對象代替。
如:function.apply(this,[1,2,3]);
②call()的第一個參數是上下文,后續是實例傳入的參數序列。
如:function.call(this,1,2,3);
如何獲取UA
JS代碼
function whatBrowser() { document.Browser.Name.value=navigator.appName; document.Browser.Version.value=navigator.appVersion; document.Browser.Code.value=navigator.appCodeName; document.Browser.Agent.value=navigator.userAgent; }
35、請解釋一下 JavaScript 的同源策略
概念:同源策略是客戶端腳本(尤其是Javascript)的重要的安全度量標準。它最早出自Netscape Navigator2.0,其目的是防止某個文檔或腳本從多個不同源裝載。
這里的同源策略指的是:協議,域名,端口相同,同源策略是一種安全協議。指一段腳本只能讀取來自同一來源的窗口和文檔的屬性。
為什么要有同源限制?
我們舉例說明:比如一個黑客程序,他利用Iframe把真正的銀行登錄頁面嵌到他的頁面上,當你使用真實的用戶名,密碼登錄時,他的頁面就可以通過Javascript讀取到你的表單中input中的內容,這樣用戶名,密碼就輕松到手了。
36、請描述一下 cookies,sessionStorage 和 localStorage 的區別?
cookie在瀏覽器和服務器間來回傳遞。 sessionStorage和localStorage不會
sessionStorage和localStorage的存儲空間更大;
sessionStorage和localStorage有更多豐富易用的接口;
sessionStorage和localStorage各自獨立的存儲空間;
小結
在互聯網上,圖像和鏈接則是通過URL唯一確定信息資源的位置。URL分為絕對URL和相對URL。通過使用<img />標記在瀏覽器中顯示一張圖像。超文本具有的鏈接能力,可層層鏈接相關文件,這種具有超鏈接能力的操作,稱為超鏈接。鏈接文檔中的特定位置也稱為錨點,定義錨點和超鏈接都使用<a>標記。圖像地圖是帶有可點擊區域的圖像,每個區域是一個相關的超級鏈接。video和audio這兩個HTML5新增加的元素,它們分別用來處理視頻與音頻數據,使得多媒體播放再也不需要安裝插件了。
習題
1.audio元素中src屬性的作用是(C)
A.提供播放、暫停和音量控件 B.循環播放
C.制定要播放音頻的URL D.插入一段替換內容
*請認真填寫需求信息,我們會在24小時內與您取得聯系。