棧:函數調用造成了一個棧幀。javascript
function foo(b) {
var a = 10;
return a + b + 11;
}
function bar(x) {
var y = 3;
return foo(x * y);
}
console.log(bar(7));
複製代碼
當調用bar時,建立了第一個幀 ,幀中包含了bar的參數和局部變量。當bar調用foo時,第二個幀就被建立,並被壓到第一個幀之上,幀中包含了foo的參數和局部變量。當foo返回時,最上層的幀就被彈出棧(剩下bar函數的調用幀 )。當bar返回的時候,棧就空了。
html
堆:對象被分配在一個堆中,即用以表示一個大部分非結構化的內存區域。java
隊列:一個 JavaScript 運行時包含了一個待處理的消息隊列。每個消息都有一個爲了處理這個消息相關聯的函數。web
在事件循環時,runtime (運行時)老是從最早進入隊列的一個消息開始處理隊列中的消息。正因如此,這個消息就會被移出隊列,並將其做爲輸入參數調用與之關聯的函數。爲了使用這個函數,調用一個函數老是會爲其創造一個新的棧幀( stack frame),一如既往。 函數的處理會一直進行直到執行棧再次爲空;而後事件循環(event loop)將會處理隊列中的下一個消息(若是還有的話)。
跨域
事件循環:之因此稱爲事件循環,是由於它常常被用於相似以下的方式來實現:
數組
while (queue.waitForMessage()) {
queue.processNextMessage();
}複製代碼
"執行至完成"
瀏覽器
每個消息完整的執行後,其它消息纔會被執行。當你分析你的程序時,這點提供了一些優秀的特性,包括每當一個函數運行時,它就不能被搶佔,而且在其餘代碼運行以前徹底運行(且能夠修改此函數操做的數據)。這與C語言不一樣,例如,若是函數在線程中運行,則能夠在任何位置終止而後在另外一個線程中運行其餘代碼。緩存
這個模型的一個缺點在於當一個消息須要太長時間才能完成,Web應用沒法處理用戶的交互,例如點擊或滾動。瀏覽器用「程序須要過長時間運行」的對話框來緩解這個問題。一個很好的作法是使消息處理縮短,若是可能,將一個消息裁剪成幾個消息。bash
添加消息
服務器
在瀏覽器裏,當一個事件出現且有一個事件監聽器被綁定時,消息會被隨時添加。若是沒有事件監聽器,事件會丟失。因此點擊一個附帶點擊事件處理函數的元素會添加一個消息。其它事件亦然。
調用 setTimeout
函數會在一個時間段過去後在隊列中添加一個消息。這個時間段做爲函數的第二個參數被傳入。若是隊列中沒有其它消息,消息會被立刻處理。可是,若是有其它消息,setTimeout
消息必須等待其它消息處理完。所以第二個參數僅僅表示最少的時間 而非確切的時間。
零延遲
零延遲並非意味着回調會當即執行。在零延遲調用 setTimeout 時,其並非過了給定的時間間隔後就立刻執行回調函數。其等待的時間基於隊列里正在等待的消息數量。在下面的例子中,"this is just a message" 將會在回調 (callback) 得到處理以前輸出到控制檯,這是由於延遲是要求運行時 (runtime) 處理請求所需的最小時間,但不是有所保證的時間。
(function() {
console.log('this is the start');
setTimeout(function cb() {
console.log('this is a msg from call back');
});
console.log('this is just a message');
setTimeout(function cb1() {
console.log('this is a msg from call back1');
}, 0);
console.log('this is the end');
})();
// "this is the start"
// "this is just a message"
// "this is the end"
// note that function return, which is undefined, happens here
// "this is a msg from call back"
// "this is a msg from call back1"
複製代碼
多個運行時互相通訊
一個 web worker 或者一個跨域的iframe都有本身的棧,堆和消息隊列。兩個不一樣的運行時只能經過 postMessage方法進行通訊。若是後者偵聽到message事件,則此方法會向其餘運行時添加消息。
永不阻塞
事件循環模型的一個很是有趣的特性是 JavaScript,與許多其餘語言不一樣,它永不阻塞。 處理 I/O 一般經過事件和回調來執行,因此當一個應用正等待IndexedDB查詢返回或者一個 XHR 請求返回時,它仍然能夠處理其它事情,如用戶輸入。 例外是存在的,如 alert或者同步 XHR,但應該儘可能避免使用它們。
上面的內容摘自MDN,看起來是否是有點官方,其實所謂的事件循環就是javascript的主線程重複從消息隊列中取消息、執行的過程。
Websocket實現了瀏覽器與服務器全雙工通訊,能更好的節省服務器資源和帶寬並達到實時通信的目的。它與HTTP同樣經過已創建的TCP鏈接來傳輸數據。websocket是一個持久化鏈接的協議,一個典型的websocket鏈接:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
複製代碼
熟悉http協議的可以看出來,它比http協議多了幾個東西,以下就是重點,用於告知服務器咱們客戶端使用的是websocket協議,不是那老套的http協議。
Upgrade: websocket
Connection: Upgrade
複製代碼
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
複製代碼
首先,Sec-WebSocket-Key 是一個Base64 encode的值,這個是瀏覽器隨機生成的,用於驗證服務器,要求服務端必須返回一個對應加密的Sec-WebSocket-Accept應答,不然客戶端會拋出Error during WebSocket handshake錯誤,並關閉鏈接。Sec_WebSocket-Protocol 是一個用戶定義的字符串,用來區分同URL下,不一樣的服務所須要的協議。Sec-WebSocket-Version 是告訴服務器所使用的Websocket Draft(協議版本),而後服務器會返回下列東西,表示已經接受到請求, 成功創建Websocket,後續服務器與瀏覽器之間的通信不須要向http協議那樣重複發送httpHeader。
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
複製代碼
1.undefined:undefined
2.null:object
3.string:string
4.number:number
5.boolean:boolean
6.function:function
7.object:object
8.array:object(Array:function)
9.NaN:number
1.instanceof :
var ary = [2,3];
console.log(ary instanceof Array)//true;
複製代碼
2.原型鏈方法
var ary = [2,4];
console.log(ary.__proto__.constructor==Array);//true
console.log(ary.constructor==Array)//true 這兩段代碼是同樣的
複製代碼
上述兩種方法都是有侷限性的:
instanceof 和constructor 判斷的變量,必須在當前頁面聲明的,好比,一個頁面(父頁面)有一個框架,框架中引用了一個頁面(子頁面),在子頁面中聲明瞭一個arr,並將其賦值給父頁面的一個變量,這時判斷該變量,Array == arr.constructor;會返回false;緣由就是Array是引用型數據,1.在傳遞過程當中,僅僅是引用地址的傳遞。 2.每一個頁面的Array原生對象所引用的地址是不同的,在子頁面聲明的array,所對應的構造函數,是子頁面的Array對象;父頁面來進行判斷,使用的Array並不等於子頁面的Array,從而形成原型鏈的斷裂。
3. Object.prototype.toString.call()
採用Object.prototype.toString.call()
算是最穩定也是通用的一個方法,Object.prototype.toString()
返回一個表示該對象的字符串,每一個對象都有一個toString()方法,當該對象被表示爲一個文本值時,或者一個對象以預期的字符串方式引用時自動調用。默認狀況下,toString()方法被每一個Object對象繼承。若是此方法在自定義對象中未被覆蓋,toString() 返回 "[object type]",其中type是對象的類型。
var arr = [2,3];
function isArray(o){
return Object.prototype.toString.call(o)=='[object Array]';
}
console.log(isArray(arr));
複製代碼
場景:記錄一個員工一個月的總加班時間,咱們首先想到的可能會是這樣實現:
var sumTime = 0;
function overTime(time){
return sumTime += time;
}
overTime(3.0);
overTime(7);
overTime(4.5);
......
console.log(overTime());複製代碼
逐次累加,這樣看起來一點毛病都沒有,可是當數據量很大時,就會影響性能了,那麼這時柯里化就能夠解決咱們的問題了。咱們能夠不用每次都疊加,只須要將每次加班的時間保存起來,到最後再疊加就ok了。
var overTime = (function(){ var args = []; return function () { if(arguments.length === 0){ var time = 0; for(var i = 0;len = args.length,i<len;i++){ time += args[i]; } return time; }else{ [].push.apply(args, arguments); } }})()
overTime(3.0);overTime(4.5);overTime(7.0);console.log(overTime());//14.5複製代碼
看到這裏你應該有點理解什麼叫函數柯里化了,柯里化(英語:Currying),又譯爲卡瑞化或加里化,是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,而且返回接受餘下的參數並且返回結果的新函數的技術。