基本(值)類型css
String,Number,boolean,undefined,null
html
對象(引用)類型vue
Object
:任意對象程序員
Function
:一種特別的對象(能夠執行)es6
Array
:一種特別的對象(數值下標,內部數據是有序的)ajax
typeof
返回數據類型的字符串表達跨域
能夠判斷undefine
d / 數值 / 字符串/布爾值瀏覽器
不能判斷null
與object
緩存
instanceof
返回布爾值markdown
===
能夠判斷undefined
,null
undefined
與null
的區別?
undefined
表明定義未賦值
null
定義並賦值了,只是值爲null
何時給變量賦值爲null
呢?
初始賦值,代表將要賦值爲對象。
結束前,被垃圾回收器回收
嚴格區別變量類型與數據類型?
數據的類型
基本類型
對象類型
變量的類型(變量內存值的類型)
基本類型:保存基本類型的數據
引用類型:保存的是地址值
什麼是數據?
存儲在內存中表明特定信息的,本質上是0101...
數據的特色:可傳遞,可運算
一切皆數據
內存中全部操做的目標:
數據
算術運算
邏輯運算
賦值
運行函數
什麼是內存?
內存條通電之後產生的可存儲數據的空間。(臨時的)
內存的產生和死亡:內存條(電路板)-->產生內存空間-->存儲數據-->斷電-->內存空間和數據都消失
一塊小內存的兩個數據
內部存儲的數據
地址值
內存分類
棧:全局變量和局部變量(空間較小)
堆:對象(空間較大)
什麼是變量?
可變化的量,由變量名和變量值組成
每一個變量都對應的一塊小內存,變量名用來查找對應的內存,變量值就是內存中保存的數據。
內存,數據,變量三者之間的關係
內存用來存儲數據的空間
變量是內存的標識
問題:var a = xxx
,a
內存中保存的是什麼?
xxx是基本數據,保存的是這個數據
xxx是對象,保存的是對象的地址值
xxx是一個變量,保存xxx的內存內容(多是基本數據,也多是地址值)
關於引用變量賦值問題
n個引用變量指向同一個對象,經過一個變量修改對象內部數據,另外一個變量看到的是修改以後的數據。
n個引用變量指向同一個對象,讓其中一個引用變量指向另外一個對象,另外一個引用變量依然指向前一個對象。
問題:在js調用函數時傳遞變量參數時,是值傳遞仍是引用傳遞?
理解1:都是值(基本值/地址值)傳遞
理解2:多是值傳遞,也多是引用傳遞(地址值)
問題:js引擎如何管理內存?
內存生命週期
分配小內存空間,獲得它的使用權
存儲數據,能夠反覆進行操做
釋放小內存空間
釋放內存
局部變量:函數執行完自動釋放(棧空間)
對象:成爲垃圾對象-->垃圾回收器回收(堆空間)
什麼是對象?
多個數據的封裝體
用來保存多個數據的容器
一個對象表明現實世界中的一個事物
爲何要用對象?
對象的組成
屬性:屬性名(字符串)和屬性值(任意類型)組成。
方法:一種特別的屬性(屬性值是函數)
如何訪問對象內部數據?
.屬性名:編碼簡單,有時不能用
['屬性名']:編碼麻煩,能通用
問題:何時必須使用['屬性名']的方式?
屬性名包含特殊字符:-,空格
變量名不肯定
什麼是函數?
實現特定功能的n條語句的封裝體
只有函數是能夠執行的,其餘類型的數據不能執行
爲何要用函數?
提升代碼複用
便於閱讀交流
如何定義函數?
函數聲明
表達式
如何調用(執行)函數?
test()
:直接調用
obj.test()
:經過對象調用
new test()
:new調用
test.call/apply(obj)
:臨時讓test
成爲obj
的方法進行調用
什麼函數纔是回調函數?
本身定義的
本身沒有調用
但最終執行了(在某個時刻或某個條件下)
常見的回調函數?
dom事件回調函數 this ==>發生事件的dom元素
定時器回調函數 this==>window
ajax請求回調函數
聲明週期回調函數
IIFE
的理解
全稱:Immediately-Invoked Function Expression 當即調用函數表達式
也能夠叫:匿名函數自調用
IIFE
的做用
隱藏實現
不會污染外部(全局)命名空間
用它來編碼js模塊
this
是什麼?
任何函數本質上都是經過某個對象來調用的,若是沒有直接指定的就是window
全部函數內部都有一個變量this
它的值是調用函數的當前對象
如何肯定this
的值?
test():window
p.test():p
new test():新建立的對象
p.call(obj):obj
js一條語句的後面能夠不加分號
是否加分號是編碼風格問題,沒有應不該該,只有喜不喜歡
在下面2種狀況下不加分號會有問題
小括號開頭的前一條語句
中方括號開頭的前一條語句
解決辦法:在行首加分號
例子:vue.js庫
每一個函數都有一個prototype
屬性,他默認指向一個Object
空對象(稱爲:原型對象)
原型對象中有一個屬性constructor
,他指向函數對象
給原型對象添加屬性(通常都是方法)
做用:函數的全部實例對象自動擁有原型中的屬性(方法)
每一個函數function
都有一個prototype
,即顯式原型
每一個實例對象都有一個__proto__
,可稱爲隱式原型
對象的隱式原型的值爲其對應構造函數的顯式原型的值
總結:
函數的prototype
屬性:在定義函數時自動添加的,默認值是一個空Object
對象。
對象的__proto__
屬性:建立對象時自動添加的,默認值爲構造函數的prototype
屬性值。
程序員能直接操做顯式原型,但不能直接操做隱式原型(es6以前)
訪問一個對象的屬性時,
訪問一個對象的屬性時,如今自身屬性中查找,找到返回。若是沒有,再沿着__proto__這條鏈向上查找,找到返回。若是最終沒有找到,返回undefined
別名:隱式原型鏈
做用:查找對象的屬性(方法)
__proto__
都是同樣的函數的顯式原型指向的對象默認是空Object
實例對象(但Object
不知足)
全部函數都是Function
的實例(包括Function
)
Object
的原型對象是原型鏈的盡頭,Object.__proto__ =null
讀取對象的屬性值時:會自動到原型鏈中查找
設置對象的屬性值時:不會查找原型鏈,若是當前對象中沒有此屬性,直接天界此屬性並設置其值。
方法通常定義在原型中,屬性通常經過構造函數定義在對象自己上。
表達式「A instanceof B
若是B函數的顯式原型對象在A對象的原型鏈上,返回true
,不然返回false
經過var定義(聲明)的變量,在定義語句以前就能夠訪問到
值:undefined
經過function
聲明的函數,在以前就能夠直接調用(var定義的不行)
值:函數定義(對象)
全局代碼
函數(局部)代碼
在執行全局代碼前將window肯定爲全局執行上下文
對全局數據進行預處理
var定義的全局變量==>undefined
,添加爲window
的屬性
function
聲明的全局函數==>賦值(fun),添加爲window
的方法
this==>賦值(window)
開始執行全局代碼
在調用函數,準備執行函數體以前,建立對應的函數執行上下文對象(虛擬的,存在於棧中)
對局部數據進行預處理
形參變量==>賦值(實參)==>添加爲執行上下文的屬性
arguments
==>賦值(實參列表),添加爲執行上下文的屬性
var
定義的局部變量==>undefined
,添加爲執行上下文的屬性
function
聲明的函數==>賦值(fun),添加爲執行上下文的方法
this==>賦值(調用函數的對象)
開始執行函數體代碼
在全局代碼執行前,js引擎就會建立一個棧來存儲管理全部的執行上下文對象
在全局執行上下文(window)肯定後,將其添加到棧中(壓棧)
在函數執行上下文建立後,將其添加到棧中(壓棧)
在當前函數執行完後,將棧頂的對象移除(出棧)
當全部的代碼執行完後,棧中只剩下爲window
先執行變量提高,再執行函數提高
遞歸調用:在函數內部調用本身
就是一塊「地盤」,一個代碼段所在的區域
它是靜態的(相對於上下文對象),在編寫代碼時就肯定了
分類
全局做用域
函數做用域
沒有塊做用域(es6有了)
做用
全局做用域以外,每一個函數都會建立本身的做用域,做用域在函數定義時就已經肯定了,而不是在函數調用時。
全局執行上下文環境是在全局做用域肯定以後,js代碼立刻執行前建立。
函數執行上下文環境是在調用函數時,函數體代碼執行以前建立。
做用域是靜態的,只要函數定義好了就一直存在,且不會變化。
上下文環境是動態的,調用函數時建立,函數調用結束時上下文環境就會自動釋放。
聯繫
上下文環境(對象)是從屬於所在的做用域
全局上下文環境==>全局做用域
函數上下文環境==>對應的函數使用域
多個上下級關係的做用域造成的鏈,它的方向是從下向上的(從內向外)
查找變量時就是沿着做用域鏈來查找的
在當前做用域下的執行上下文中查找對應的屬性,若是還找不到就拋出找不到的異常。
如何產生閉包?
當一個嵌套的內部(子)函數引用了嵌套的外部(子)函數的變量(函數)時,就產生了閉包
閉包究竟是什麼?
使用Chrome調試查看
理解一:閉包是嵌套的內部函數(絕大多部分人)
理解二:包含被引用變量(函數)的對象(極少數人)
注意:閉包存在於嵌套的內部函數中
產生閉包的條件?
函數嵌套
內部函數引用了外部函數的數據(變量/函數)
常見的閉包
將函數做爲另外一個函數的返回值
將函數做爲實參傳遞給另外一個函數調用
閉包的做用
使用函數內部的變量在函數執行完後,仍然存活在內存中(延長了局部變量的生命週期)
讓函數外部均可以操做(讀寫)到函數內部的數據(變量/函數)
問題:
通常是不存在,存在於閉包中的變量纔可能存在
不能,可是能夠經過閉包讓外部操做它。
閉包的生命週期
產生:在嵌套內部函數定義執行完時就產生了(不是在調用)
死亡:在嵌套的內部函數成爲垃圾對象時
閉包的應用:定義js模塊
具備特定功能的js文件
將全部的數據和功能都封裝在一個函數內部(私有的)
只向外暴露一個包含n個方法的對象或函數
模塊的使用者,只須要經過模塊暴露的對象調用方法來實現對應的功能。
9. 閉包的缺點及解決
缺點
函數執行完後,函數內的局部變量沒有釋放,佔用內存時間會變長
容易形成內存泄露
解決
能不用閉包就不用閉包
及時釋放
一種程序運行出現的錯誤
當程序運行須要的內存超過了剩餘的內存時,就會拋出內存溢出的錯誤
佔用的內存沒有及時釋放
內存泄露積累多了就會容易致使內存溢出
常見的內存泄漏:
意外的全局變量
沒有及時清理的計時器或回調函數
閉包
Object
構造函數模式
套路:先建立空Object
對象,再動態添加屬性/方法
適用場景:起始時不肯定對象內部數據
問題:語句太多
對象字面量模式
套路:使用{ }建立對象,同時指定屬性/方法
適用場景:起始時對象內部數據時肯定的
問題:若是建立多個對象,有重複代碼
工廠模式
套路:經過工廠函數動態建立對象並返回
適用場景:須要建立多個對象
問題:對象沒有一個具體的類型,都是Object類型
自定義構造函數模式
套路:自定義構造函數,經過new建立對象
適用場景:須要建立多個類型肯定的對象
問題:每一個對象都有相同的數據,浪費內存
構造函數+原型的組合模式
套路:自定義構造函數,屬性在函數中初始化,方法添加到原型上
適用場景:須要建立多個類型肯定的對象
套路
定義父類型構造函數
給父類型的原型添加方法
定義子類型的構造函數
建立父類型的對象賦值給子類型的原型
將子類型原型的構造屬性設置爲子類型
給子類型原型添加方法
建立子類型的對象:能夠調用父類型的方法
關鍵
function Parent(){ }
Parent.prototype.test = function(){ };
function Child( ){ }
Child.prototype = new Parent( );//子類型的原型指向父類型實例
Child.prototype.constructor = Child
var child = new Child( );
借用構造函數繼承(假的):獲得屬性
複製代碼
套路:
定義父類型構造函數
定義子類型構造函數
在子類型構造函數中調用父類型構造
關鍵:
function Parent(xxx){ this.xxx = xxx}
Parent.prototype.test = function( ){ };
function Child(xxx,yyy){
Parent.call(this,xxx)// 借用構造函數 this.Parent(xxx)
}
var child =new Child('a','b'); //child.xxx爲'a',但child沒有test()
複製代碼
原型鏈+借用構造函數的組合繼承
利用原型鏈實現對父類型對象的方法繼承
利用super()借用父類型構建函數初始化相同屬性
function Parent(xxx){this.xxx=xxx}
Parent.prototype.test = function(){};
function Child(xxx,yyy){
Parent.call(this,xxx);//借用構造函數 this.Parent(xxx)
}
Child.prototype = new Parent();//獲得test()
var child = new Child();//child.xxx爲'a',也有test()
複製代碼
建立一個空對象
給對象設置__proto__
,值爲構造函數對象的prototype
屬性值,this.__proto__=Fn.prototype
執行構造函數體(給對象添加屬性/方法)
線程池:保存多個線程對象的容器,實現線程對象的反覆利用
多線程
優勢:能有效提高CPU的利用率,建立多線程開銷
缺點:線程間切換開銷,死鎖與狀態同步問題
js是單線程仍是多線程?
js是單線程運行的
但使用H5中的Web Workers
能夠多線程運行
瀏覽器內核由不少模塊組成
主線程
js引擎模塊:負責js程序的編譯與運行
html
,css文檔解析模塊:負責頁面文本的解析
DOM/CSS
模塊:負責dom/ css
在內存中的相關處理
佈局和渲染模塊:負責頁面的佈局和效果的繪製(內存中的對象)
分線程
定時器模塊:負責定時器的管理
事件響應模塊:負責事件的管理
網絡請求模塊:負責ajax
請求
定時器真是定時執行的嗎?
定時器並不能保證真正定時執行
通常會延遲一丁點(能夠接受),也有可能延遲很長時間(不能接受)
定時器回調函數是在分線程執行的嗎?
定時器是如何實現的?
alert
會暫停當前的主線程的執行,同時暫停計時,點擊肯定後,恢復程序執行和計時
alert
是window
的方法,在分線程不能調用
如何證實js執行時單線程的?
setTimeout()
的回調函數是在主線程執行的
定時器回調函數只有在運行棧中的代碼所有執行完後纔有可能執行
爲何js要用單線程模式,而不用多線程模式?
js的單線程和它的用途有關
做爲瀏覽器腳本語言,js的主要用途是魚用戶互動,以及操做dom
這決定了他只能是單線程,不然會帶來很複雜的同步問題
代碼的分類
初始化代碼(同步代碼):包含綁定dom
事件監聽,設置定時器,發送ajax
請求的代碼
回調代碼(異步代碼):處理回調邏輯
js引擎執行代碼的基本流程
先執行初始化代碼:包含一些特別的代碼 回調函數(異步執行)
設置定時器
綁定監聽
發送ajax請求
後面在某個時刻纔會執行回調代碼
模型的2個重要組成部分:
事件管理模塊
回調隊列(起到了一個緩存的做用)
模型的運轉流程
執行初始化代碼,將事件回調函數交給對象模塊管理
當事件發生時,管理模塊會將回調函數及其數據添加到回調隊列中
只有當初始化代碼執行完後(可能要必定時間),纔會遍歷讀取回調隊列中的回調函數執行
事件輪詢(event loop
):從任務隊列中循環取出回調函數放入執行棧處理中
H5規範提供了js分線程的實現,取名爲:Web Workers
相關API
Worker
:構造函數,加載分線程執行的js文件
Worker.prototype.onmessage
:用於接收另外一個線程的回調函數
Worker.prototype.postMessage
:向另外一個線程發送消息
worker.onMessage=function(event){event.data}
:用來接受另外一個線程發送過來的數據的回調
不足
worker
內代碼不能操做DOM
(更新UI)
不能跨域加載js
不是每一個瀏覽器都支持這個新特性