JS 基礎
js基礎數據類型和引用類型分別是什麼
1.基本數據類型(自身不可拆分的):Undefined、Null、Boolean、Number、String javascript
2.引用數據類型(對象):Object (Array,Date,RegExp,Function) html
ES6基本數據類型多了個symbol前端
JavaScript 的 typeof 返回那些數據類型
object number function boolean undefined stringjava
typeof null; // object
typeof isNaN; // function
typeof isNaN(123); //boolean
typeof []; // object
Array.isArray(); // false
toString.call([]); // [object Array]
var arr = [];
arr.constructor; // ƒ Array() { [native code] }
顯示轉換(強制類型轉換) 強制類型轉換和隱式類型轉換?
js 提供瞭如下幾種轉型函數:node
轉換的類型
函數
數值類型
Number(mix),parseInt(string,radix),parseFloat(string);
字符串類型
toString(radix),String(mix)
布爾類型
Boolean(mix)
Number(mix) 函數,能夠將任意類型的參數 mix 轉換爲數值類型,規則爲
若是是布爾值,true
和 false
分別被轉換爲 1 和 0
若是是數字值,返回自己
若是是 null
,返回 0
若是是 undefined
,返回 NaN
若是是字符串,遵循如下規則:
若是字符串中只包含數字,則將其轉換爲十進制(忽略前導0,前面正負號有效)
若是字符串中包含有效的浮點格式,則將其轉換爲對應的浮點數值(忽略前導0,前面正負號有效)
若是字符串中包含有效的十六進制格式,則轉換爲相同大小的十進制整數值
若是字符串是空的(不包含任何字符),則將其轉換爲 0
若是字符串中包含上述格式以後的字符,則將其轉換爲 NaN
若是是對象,則調用對象的 valueOf()
方法,而後按照前面的規則進行轉換返回的值,若是是轉換結果是 NaN
,則調用對象的 toString()
方法,而後再一次按照前面的規則進行返回的字符串值的轉換
下表是對象的 valueOf() 的返回值jquery
對象
返回值
Array
數組的元素被轉換爲字符串,這些字符串由逗號分隔,鏈接在一塊兒。其操做與 Array.toString 和 Array.join 方法相同。
Boolean
Boolean 值。
Date
存儲的時間是從 1970 年 1 月 1 日午夜開始計的毫秒數 UTC。
Function
函數自己。
Number
數字值。
Object
對象自己。這是默認狀況。
String
字符串值。
因爲 Number()函數在轉換字符串時原理比較複雜,且不夠合理,所以在處理字符串時,更經常使用的是 parseInt()
函數web
parstInt(string,radix) 函數,將字符串轉換爲整數類型的數值,其規則爲
忽略前面字符串前面的空格,直至找到第一個非空格字符
若是第一個字符不是數字字符或者負號,就會返回 NaN
(也就是遇到空字符會返回 NaN)
若是第一個字符是數字字符,會繼續解析第二個字符,知道解析完全部後續的字符或者是遇到一個非數字字符
若是字符串中第一個字符是數字字符,也可以識別各類進制
最好在 第二個參數指定轉換的基數(進制),就不會有所歧義。
parseFloat(string)函數,將字符串轉換爲浮點數類型的數值。
與parseInt()函數相似,parseFloat()也是從第一個字符(位置0)開始解析每一個字符。並且也是一直解析到字符串末尾,或者解析到碰見一個無效的浮點數字字符爲止。也就是說,字符串中的第一個小數點是有效的,而第二個小數點就是無效的了,所以它後面的字符串將被忽略。面試
toString(radix)
除 undefined
和 null
以外的全部類型的值都具備 toString()
方法,其做用是返回對象的字符串表示。正則表達式
多數狀況下,調用toString()方法沒必要傳遞參數。可是,在調用數值的toString()方法時,能夠傳遞一個參數:輸出數值的基數。默認狀況下,toString()方法以十進制格式返回數值的字符串表示。shell
對象
操做
Array
將 Array 的元素轉換爲字符串。結果字符串由逗號分隔,且鏈接起來。
Boolean
若是 Boolean 值是 true,則返回 「true」。不然,返回 「false」。
Date
返回日期的文字表示法。
Error
返回一個包含相關錯誤信息的字符串。
Function
返回以下格式的字符串,其中 functionname 是被調用 toString 方法函數的名稱:function functionname( ) { [native code] }
Number
返回數字的文字表示。
String
返回 String 對象的值。
默認
返回 「[object objectname]」,其中 objectname 是對象類型的名稱。
在不知道要轉換的值是否是null或undefined的狀況下,還可使用轉型函數String(),這個函數可以將任何類型的值轉換爲字符串。
String(mix)函數,將任何類型的值轉換爲字符串,其規則爲:
若是有toString()方法,則調用該方法(不傳遞radix參數)並返回結果
若是是null,返回」null」
若是是undefined,返回」undefined」
Boolean(mix)函數,將任何類型的值轉換爲布爾值。
如下值會被轉換爲false
:false、」」、0、NaN、null、undefined,其他任何值都會被轉換爲true
。
隱式轉換(非強制轉換類型)
在某些狀況下,即便咱們不提供顯示轉換,Javascript也會進行自動類型轉換,主要狀況有:
用於檢測是否爲非數值的函數:isNaN(mix)
isNaN()
函數,經測試發現,該函數會嘗試將參數值用 Number()
進行轉換,若是結果爲「非數值」則返回 true
,不然返回 false
。
遞增遞減操做符(包括前置和後置)、一元正負符號操做符(通過對比發現,其規則與Number()規則基本相同)
若是是包含有效數字字符的字符串,先將其轉換爲數字值(轉換規則同 Number()
),再執行加減1的操做,字符串變量變爲數值變量。
若是是不包含有效數字字符的字符串,將變量的值設置爲 NaN
,字符串變量變成數值變量。
若是是布爾值 false
,先將其轉換爲0再執行加減1的操做,布爾值變量編程數值變量。
若是是布爾值 true
,先將其轉換爲1再執行加減1的操做,布爾值變量變成數值變量。
若是是浮點數值,執行加減1的操做。
若是是對象,先調用對象的 valueOf()
方法,而後對該返回值應用前面的規則。若是結果是 NaN
,則調用 toString()
方法後再應用前面的規則。對象變量變成數值變量。
加法運算操做符
加號運算操做符在Javascript也用於字符串鏈接符,因此加號操做符的規則分兩種狀況:
若是兩個操做值都是數值,其規則爲:
若是一個操做數爲 NaN
,則結果爲 NaN
若是是 Infinity+Infinity
,結果是 Infinity
若是是 -Infinity+(-Infinity)
,結果是 -Infinity
若是是 Infinity+(-Infinity)
,結果是 NaN
若是是 +0+(+0)
,結果爲 +0
若是是 (-0)+(-0)
,結果爲 -0
若是是 (+0)+(-0)
,結果爲 +0
若是有一個操做值爲字符串,則:
若是兩個操做值都是字符串,則將它們拼接起來 若是隻有一個操做值爲字符串,則將另外操做值轉換爲字符串,而後拼接起來
若是一個操做數是對象、數值或者布爾值,則調用toString()方法取得字符串值,而後再應用前面的字符串規則。
對於undefined和null,分別調用String()顯式轉換爲字符串。
能夠看出,加法運算中,若是有一個操做值爲字符串類型,則將另外一個操做值轉換爲字符串,最後鏈接起來。
乘除、減號運算符、取模運算符
這些操做符針對的是運算,因此他們具備共同性:若是操做值之一不是數值,則被隱式調用Number()
函數進行轉換。具體每一種運算的詳細規則請參考ECMAScript中的定義。
邏輯操做符(!、&&、||)
邏輯非(!)操做符首先經過Boolean()函數將它的操做值轉換爲布爾值,而後求反。
邏輯與(&&)操做符,若是一個操做值不是布爾值時,遵循如下規則進行轉換:
若是第一個操做數經 Boolean()
轉換後爲 true
,則返回第二個操做值,不然返回第一個值(不是 Boolean()
轉換後的值)
若是有一個操做值爲 null
,返回 null
若是有一個操做值爲 NaN
,返回 NaN
若是有一個操做值爲 undefined
,返回 undefined
邏輯或(||)操做符,若是一個操做值不是布爾值,遵循如下規則
若是第一個操做值經 Boolean()
轉換後爲 false
,則返回第二個操做值,不然返回第一個操做值(不是 Boolean()
轉換後的值)
對於 undefined
、null
和 NaN
的處理規則與邏輯與(&&)相同
關係操做符(<, >, <=, >=)
與上述操做符同樣,關係操做符的操做值也能夠是任意類型的,因此使用非數值類型參與比較時也須要系統進行隱式類型轉換:
若是兩個操做值都是數值,則進行數值比較
若是兩個操做值都是字符串,則比較字符串對應的字符編碼值
若是隻有一個操做值是數值,則將另外一個操做值轉換爲數值,進行數值比較
若是一個操做數是對象,則調用 valueOf()
方法(若是對象沒有 valueOf()
方法則調用 toString()
方法),獲得的結果按照前面的規則執行比較
若是一個操做值是布爾值,則將其轉換爲數值,再進行比較
注:NaN
是很是特殊的值,它不和任何類型的值相等,包括它本身,同時它與任何類型的值比較大小時都返回 false
。
相等操做符(==)
相等操做符會對操做值進行隱式轉換後進行比較:
若是一個操做值爲布爾值,則在比較以前先將其轉換爲數值
若是一個操做值爲字符串,另外一個操做值爲數值,則經過Number()函數將字符串轉換爲數值
若是一個操做值是對象,另外一個不是,則調用對象的 valueOf()
方法,獲得的結果按照前面的規則進行比較
null
與 undefined
是相等的
若是一個操做值爲 NaN
,則相等比較返回 false
若是兩個操做值都是對象,則比較它們是否是指向同一個對象
split()、join()的區別
前者是切割成數組的形式
後者是將數組轉換爲字符串
數組方法pop/push/unshift/shift
數組方法
描述
pop()
刪除原數組最後一項,並返回刪除元素的值;若是數組爲空則返回undefined
push()
將參數添加到原數組末尾,並返回數組的長度
unshift()
將參數添加到原數組開頭,並返回數組的長度
shift()
刪除原數組第一項,並返回刪除元素的值;若是數組爲空則返回undefined
事件綁定和普通事件有什麼區別
普通事件中的onclick是DOM0級事件只支持單個事件,會被其餘onclick事件覆蓋,而事件綁定中的addEventListener是DOM2級事件能夠添加多個事件而不用擔憂被覆蓋
普通添加事件的方法:
var btn = document.getElementById("hello");
btn.onclick = function(){
alert(1);
}
btn.onclick = function(){
alert(2);
}
事件綁定方式添加事件: 執行上面的代碼只會alert 2
var btn = document.getElementById("hello");
btn.addEventListener("click",function(){
alert(1);
},false);
btn.addEventListener("click",function(){
alert(2);
},false);
IE 和 DOM 事件流有什麼區別 執行上面的代碼會先alert 1 再 alert 2
事件
HTML元素事件是瀏覽器內在自動產生的,當有事件發生時html元素會向外界(這裏主要指元素事件的訂閱者)發出各類事件,如click,onmouseover,onmouseout等等。
DOM事件流
DOM(文檔對象模型)結構是一個樹型結構,當一個HTML元素產生一個事件時,該事件會在元素結點與根結點之間的路徑傳播,路徑所通過的結點都會收到該事件,這個傳播過程可稱爲DOM事件流。
冒泡型事件(Bubbling)
這是IE瀏覽器對事件模型的實現。冒泡,顧名思義,事件像個水中的氣泡同樣一直往上冒,直到頂端。從DOM樹型結構上理解,就是事件由葉子結點沿祖先結點一直向上傳遞直到根結點;從瀏覽器界面視圖HTML元素排列層次上理解就是事件由具備從屬關係的最肯定的目標元素一直傳遞到最不肯定的目標元素.
捕獲型事件(Capturing)
Netscape Navigator的實現,它與冒泡型恰好相反,由DOM樹最頂層元素一直到最精確的元素,直觀上的理解應該如同冒泡型,事件傳遞應該由最肯定的元素,即事件產生元素開始。
DOM標準事件模型
由於兩個不一樣的模型都有其優勢和解釋,DOM標準支持捕獲型與冒泡型,能夠說是它們二者的結合體。它能夠在一個DOM元素上綁定多個事件處理器,而且在處理函數內部,this關鍵字仍然指向被綁定的DOM元素,另外處理函數參數列表的第一個位置傳遞事件event對象。
首先是捕獲式傳遞事件,接着是冒泡式傳遞,因此,若是一個處理函數既註冊了捕獲型事件的監聽,又註冊冒泡型事件監聽,那麼在DOM事件模型中它就會被調用兩次。
實例 :
<body>
<div>
<button>點擊這裏</button>
</div>
</body>
捕獲:body
-> div
-> button
(Netscape事件流)冒泡:button
-> div
-> body
(IE 事件流)
DOM: body
-> div
-> button
-> button
-> div
-> body
(先捕獲後冒泡)
事件偵聽函數的區別
// IE使用:
[Object].attachEvent("name_of_event_handler", fnHandler); //綁定函數
[Object].detachEvent("name_of_event_handler", fnHandler); //移除綁定
// DOM使用:
[Object].addEventListener("name_of_event", fnHandler, bCapture); //綁定函數
[Object].removeEventListener("name_of_event", fnHandler, bCapture); //移除綁定
取消事件傳遞是指,中止捕獲型事件或冒泡型事件的進一步傳遞。如何取消瀏覽器事件的傳遞與事件傳遞後瀏覽器的默認處理
事件傳遞後的默認處理是指,一般瀏覽器在事件傳遞並處理完後會執行與該事件關聯的默認動做(若是存在這樣的動做)。例如,若是表單中input type 屬性是 「submit」,點擊後在事件傳播完瀏覽器就就自動提交表單。又例如,input 元素的 keydown 事件發生並處理後,瀏覽器默認會將用戶鍵入的字符自動追加到 input 元素的值中。
要取消瀏覽器的事件傳遞,IE與DOM標準又有所不一樣。
在IE下,經過設置 event
對象的 cancelBubble
爲 true
便可。
function someHandle() {
window.event.cancelBubble = true;
}
function someHandle(event) { event.stopPropagation(); }
DOM標準經過調用 event
對象的 stopPropagation()
方法便可。
因些,跨瀏覽器的中止事件傳遞的方法是:
1 2 3 4 5 6
copy function someHandle (event ) { event = event || window .event; if (event.stopPropagation) event.stopPropagation(); else event.cancelBubble = true ; }
取消事件傳遞後的默認處理,IE與DOM標準又不所不一樣。
在IE下,經過設置 event
對象的 returnValue
爲 false
便可。
1 2 3
copy function someHandle () { window .event.returnValue = false ; }
DOM標準經過調用 event
對象的 preventDefault()
方法便可。
1 2 3
copy function someHandle (event ) { event.preventDefault(); }
因些,跨瀏覽器的取消事件傳遞後的默認處理方法是:
1 2 3 4 5 6
copy function **` `someHandle (event ) { event = event || window .event; if (event.preventDefault) event.preventDefault(); else event.returnValue = false ; }
IE 和標準下有哪些兼容性的寫法
1 2 3
copy var ev = ev || window .eventdocument .documentElement.clinetWidth || document .body.clientWidthvar target = ev.srcElement || ev.target
call 和 apply 的區別
call 和 apply 相同點: 都是爲了用一個本不屬於一個對象的方法,讓這個對象去執行
基本使用
call()
1
copy function .call (obj[,arg1[, arg2[, [,.argN]]]]] )
調用call
的對象必須是個函數function
call
的第一個參數將會是function改變上下文後指向的對象.若是不傳,將會默認是全局對象window
第二個參數開始能夠接收任意個參數,這些參數將會做爲function的參數傳入function
調用call
的方法會當即執行
apply()
1
copy function .apply (obj[,argArray] )
與call
方法的使用基本一致,可是隻接收兩個參數,其中第二個參數必須是一個數組 或者類數組 ,這也是這兩個方法很重要的一個區別
數組與類數組小科普
數組咱們都知道是什麼,它的特徵都有哪些呢?
能夠經過角標調用,如 array[0]
具備長度屬性length
能夠經過 for 循環和forEach
方法進行遍歷
類數組顧名思義,具有的特徵應該與數組基本相同,那麼能夠知道,一個形以下面這個對象的對象就是一個類數組
1 2 3 4 5 6
copy var arrayLike = { 0: 'item1', 1: 'item2', 2: 'item3', length: 3 }
類數組arrayLike
能夠經過角標進行調用,具備length
屬性,同時也能夠經過 for 循環進行遍歷
咱們常用的獲取dom節點的方法返回的就是一個類數組,在一個方法中使用 arguments
關鍵字獲取到的該方法的全部參數也是一個類數組
可是類數組卻不能經過forEach
進行遍歷,由於forEach
是數組原型鏈上的方法,類數組畢竟不是數組,因此沒法使用
不一樣點
call
方法從第二個參數開始能夠接收任意個參數,每一個參數會映射到相應位置的func的參數上,能夠經過參數名調用,可是若是將全部的參數做爲數組傳入,它們會做爲一個總體映射到func對應的第一個參數上,以後參數都爲空
1 2 3 4 5 6 7
copy function func (a,b,c) {} func.call(obj, 1,2,3) // function接收到的參數其實是 1,2,3 func.call(obj, [1,2,3]) // function接收到的參數其實是 [1,2,3],undefined,undefined
apply
方法最多隻有兩個參數,第二個參數接收數組或者類數組,可是都會被轉換成類數組傳入func中,而且會被映射到func對應的參數上
1 2 3 4 5 6 7 8 9 10
copy func.apply(obj, [1,2,3]) // function接收到的參數其實是 1,2,3 func.apply(obj, { 0: 1, 1: 2, 2: 3, length: 3 }) // function接收到的參數其實是 1,2,3
b 繼承 a 的方法
方法一:對象冒充
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
copy function Parent (username ) { this .username = username; this .hello = function () { console .log(this .username); } } function Child (username,password ) { this .method = Parent; this .method(username); delete this .method; this .password = password; this .world = function () { console .log(this .password); } } const parent = new Parent('hello parent' );const child = new Child('hello child' ,'123456' );console .log(child);parent.hello(); child.hello(); child.world();
方法二:call()
call 方法是 Function 類中的方法 call 方法的第一個參數的值賦值給類(即方法)中出現的 this call 方法的第二個參數開始依次賦值給類(即方法)所接受的參數
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
copy function Parent (username ) { this .username = username; this .hello = function () { console .log(this .username); } } function Child (username,password ) { Parent.call(this ,username); this .password = password; this .world = function () { console .log(this .password); } } const parent = new Parent('hello parent' );const child = new Child('hello child' ,'123456' );parent.hello(); child.hello(); child.world();
方法三:apply()
apply方法接受2個參數
第一個參數與call方法的第一個參數同樣,即賦值給類(即方法)中出現的this
第二個參數爲數組類型,這個數組中的每一個元素依次賦值給類(即方法)所接受的參數
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
copy function Parent (username ) { this .username = username; this .hello = function () { console .log(this .username); } } function Child (username,password ) { Parent.apply(this ,new Array (username)); this .password = password; this .world = function () { console .log(this .password); } } const parent = new Parent('hello parent' );const child = new Child('hello child' ,'123456' );parent.hello(); child.hello(); child.world();
方法四:原型鏈
即子類經過 prototype 將全部在父類中經過 prototype 追加的屬性和方法都追加到 Child ,從而實現繼承
1 2 3 4 5 6 7 8 9 10 11 12 13 14
copy function Parent () {}Parent.prototype.hello = "hello" ; Parent.prototype.sayHello = function () { console .log(this .hello); } function Child () {}Child.prototype = new Parent(); Child.prototype.world = "world" ; Child.prototype.sayWorld = function () { console .log(this .world); } const child = new Child();child.sayHello(); child.sayWorld();
方法五:混合方式,call()+ 原型鏈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
copy function Parent (hello ) { this .hello = hello; } Parent.prototype.sayHello = function () { console .log(this .hello); } function Child (hello,world ) { Parent.call(this ,hello); this .world = world; } Child.prototype = new Parent(); Child.prototype.sayWorld = function () { console .log(this .world); } const child = new Child("hello" ,"world" );child.sayHello(); child.sayWorld();
JavaScript this 指針、閉包、做用域
js 中的this 指針
在函數執行時,this 老是指向調用該函數的對象。要判斷 this 的指向,其實就是判斷 this 所在的函數屬於誰。
在《javaScript語言精粹》這本書中,把 this 出現的場景分爲四類,簡單的說就是:
1)有對象就指向調用對象
1 2 3 4 5 6
copy var myObject = { value : 123 }myObject.getValue = function () { console .log(this .value); console .log(this ); } myObject.getValue();
2)沒調用對象就指向全局對象
1 2 3 4 5 6 7 8 9 10 11 12
copy var myObject = { value : 123 }myObject.getValue = function () { var foo = function () { console .log(this .value); console .log(this ); } foo(); return this .value; } console .log(myObject.getValue());
3) 用new構造就指向新對象
1 2 3 4 5 6
copy var someClass = function () { this .value = 123 ; } var myCreate = new someClass();console .log(myCreate.value);
4)經過 apply 或 call 或 bind 來改變 this 的指向
1 2 3 4 5 6 7 8 9
copy var myObject = { value : 123 };var foo = function () { console .log(this ); } foo(); foo.apply(myObject); foo.call(myObject); var newFoo = foo.bind(myObject);newFoo();
閉包
閉包英文是 Closure ,簡而言之,閉包就是
函數的局部集合,只是這些局部變量在函數返回後會繼續存在
函數的「堆棧」在函數返回後並不釋放,能夠理解爲這些函數堆棧並不在棧上分配而是在堆上分配
當在一個函數內部定義另一個函數就會產生閉包
做爲局部變量均可以被函數內的代碼訪問,這個和靜態語言是沒有差異的,閉包的差異在於局部變量能夠在函數執行結束後仍然被函數外的代碼訪問,這意味着函數必須返回一個指向閉包的「引用」,或將這個「引用」賦值給某個外部變量,才能保證閉包中局部變量被外部代碼訪問,固然包含這個引用的實體應該是一個對象。可是ES並無提供相關的成員和方法來訪問包中的局部變量,可是在ES中,函數對象中定義的內部函數是能夠直接訪問外部函數的局部變量,經過這種機制,能夠用以下方式完成對閉包的訪問。
1 2 3 4 5 6 7 8 9 10
copy function greeting (name ) { var text = "Hello " + name; return function () { console .log(text); } } var sayHello = greeting('Closure' );sayHello();
在 ECMAscript 的腳本函數運行時,每一個函數關聯都有一個執行上下文場景(Exection Context),這個執行上下文包括三個部分
文法環境(The LexicalEnvironment)
變量環境(The VariableEnvironment)
this綁定
其中第三點this綁定與閉包無關,不在本文中討論。文法環境中用於解析函數執行過程使用到的變量標識符。咱們能夠將文法環境想象成一個對象,該對象包含了兩個重要組件,環境記錄(Enviroment Recode),和外部引用(指針)。環境記錄包含包含了函數內部聲明的局部變量和參數變量,外部引用指向了外部函數對象的上下文執行場景。全局的上下文場景中此引用值爲NULL。這樣的數據結構就構成了一個單向的鏈表,每一個引用都指向外層的上下文場景。
例如上面咱們例子的閉包模型應該是這樣,sayHello函數在最下層,上層是函數greeting,最外層是全局場景。以下圖:
所以當sayHello被調用的時候,sayHello會經過上下文場景找到局部變量text的值,所以在屏幕的對話框中顯示出」Hello Closure」
針對一些例子來幫助你們更加深刻的理解閉包,下面共有5個樣例,例子來自於JavaScript Closures For Dummies( 鏡像 ) 。
例子1:閉包中局部變量是引用而非拷貝
1 2 3 4 5 6 7 8 9 10
copy function say667 () { var num = 666 ; var sayConsole = function () { console .log(num); } num++; return sayConsole; } var sayConsole = say667();sayConsole();
例子2:多個函數綁定同一個閉包,由於他們定義在同一個函數內。
1 2 3 4 5 6 7 8 9 10 11 12
copy function setupSomeGlobals () { var num = 666 ; gConsoleNumber = function () { console .log(num); } gIncreaseNumber = function () { num++; } gSetNumber = function (x ) { num = x; } } setupSomeGlobals(); gConsoleNumber(); gIncreaseNumber(); gConsoleNumber(); gSetNumber(12 ); gConsoleNumber();
例子3:當在一個循環中賦值函數時,這些函數將綁定一樣的閉包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
copy function buildList (list ) { var result = []; for (var i = 0 ; i < list.length; i++){ var item = 'item' + list[i]; result.push(function () { console .log(item+' ' +list[i]); }) } return result; } function testList () { var fnList = buildList([1 ,2 ,3 ]); for (var j = 0 ; j < fnList.length; j++){ fnList[j](); } } testList();
testList的執行結果是彈出item3 undefined窗口三次,由於這三個函數綁定了同一個閉包,並且item的值爲最後計算的結果,可是當i跳出循環時i值爲4,因此list[4]的結果爲undefined.
例子4:外部函數全部局部變量都在閉包內,即便這個變量聲明在內部函數定義以後。
1 2 3 4 5 6 7 8 9
copy function sayAlice () { var sayConsole = function () { console .log(alice); } var alice = "Hello Alice" ; return sayConsole; } var helloAlice=sayAlice();helloAlice();
執行結果輸出」Hello Alice」的窗口。即便局部變量聲明在函數sayAlert以後,局部變量仍然能夠被訪問到。
例子5:每次函數調用的時候建立一個新的閉包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
copy function newClosure (someNum,someRef ) { var num = someNum; var anArray = [1 ,2 ,3 ]; var ref = someRef; return function (x ) { num += x; anArray.push(num); console .log('num: ' + num +'\nanArray ' + anArray.toString() +'\nref.someVar ' + ref.someVar); } } closure1=newClosure(40 ,{someVar :'closure 1' }); closure2=newClosure(1000 ,{someVar :'closure 2' }); closure1(5 ); closure2(-10 );
閉包的缺點:
(1)因爲閉包會使得函數中的變量都被保存在內存中,內存消耗很大,因此不能濫用閉包,不然會形成網頁的性能問題,在IE中可能致使內存泄露。解決方法是,在退出函數以前,將不使用的局部變量所有刪除。
(2)閉包會在父函數外部,改變父函數內部變量的值。因此,若是你把父函數看成對象(object)使用,把閉包看成它的公用方法(Public Method),把內部變量看成它的私有屬性(private value),這時必定要當心,不要隨便改變父函數內部變量的值。
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13
copy function Cars () { this .name = "Benz" ; this .color = ["white" ,"black" ]; } Cars.prototype.sayColor = function () { var outer = this ; return function () { return outer.color }; }; var instance = new Cars();console .log(instance.sayColor()())
改造:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
copy function Cars () { this .name = "Benz" ; this .color = ["white" ,"black" ]; } Cars.prototype.sayColor = function () { var outerColor = this .color; return function () { return outerColor; }; outColor = null ; }; var instance = new Cars();console .log(instance.sayColor()())
做用域
在JS當中一個變量的做用域(scope)是程序中定義這個變量的區域。變量分爲兩類:全局(global)的和局部的。其中全局變量的做用域是全局性的,即在JavaScript代碼中,它到處都有定義。而在函數以內聲明的變量,就只在函數體內部有定義。它們是局部變量,做用域是局部性的。函數的參數也是局部變量,它們只在函數體內部有定義。
咱們能夠藉助JavaScript的做用域鏈(scope chain)更好地瞭解變量的做用域。每一個JavaScript執行環境都有一個和它關聯在一塊兒的做用域鏈。這個做用域鏈是一個對象列表或對象鏈。當JavaScript代碼須要查詢變量x的值時(這個過程叫作變量解析(variable name resolution)),它就開始查看該鏈的第一個對象。若是那個對象有一個名爲x的屬性,那麼就採用那個屬性的值。若是第一個對象沒有名爲x的屬性,JavaScript就會繼續查詢鏈中的第二個對象。若是第二個對象仍然沒有名爲x的屬性,那麼就繼續查詢下一個對象,以此類推。若是查詢到最後(指頂層代碼中)不存在這個屬性,那麼這個變量的值就是未定義的。
1 2 3 4 5 6 7 8 9 10
copy var a,b;(function () { alert(a); alert(b); var a = b = 3 ; alert(a); alert(b); })(); alert(a); alert(b);
以上代碼至關於
1 2 3 4 5 6 7 8 9 10
copy var a,b;(function () { alert(a); alert(b); var a = 3 ; b = 3 ; alert(a); alert(b); })(); alert(a);
事件委託是什麼?
概述
什麼叫作事件委託,別名叫事件代理,JavaScript 高級程序設計上講。事件委託就是利用事件冒泡,只指定一個事件處理程序,就能夠管理某一類型的全部事件。
實際例子:
有三個同事預計會在週一收到快遞,爲簽收快遞,有兩種方法:一是三我的在公司門口等快遞,二是委託給前臺的小姐代爲簽收。現實生活中,咱們大多采用委託的方案(公司也不會容忍那麼多人站在門口)。前臺小姐收到快遞後,會判斷收件人是誰,按照收件人的要求籤收,甚至是代付。這種方案還有一個好處就是,即便公司來了不少新員工(無論多少),前臺小姐也會在收到寄給新員工們的快遞後覈實代爲簽收。
這裏有2層意思: 第1、如今委託前臺的小姐是能夠代爲簽收的,即程序中的現有的 DOM 節點是有事件的。
第2、新員工也能夠被前臺小姐代爲簽收,即程序中新添加的 DOM 節點也是有事件的。
爲何要使用事件委託
通常來講,DOM 須要有事件處理程序,就會直接給它設處理程序,可是若是是不少 DOM 須要添加處理事件呢?例如咱們有100個 li,每一個 li 都有相同的 click 點擊事件,可能咱們會用到 for 循環,來遍歷全部 li ,而後給它們添加事件,那麼會存在什麼樣的問題?
在 JavsScript 中,添加到頁面上的事件處理程序數量將直接影響到總體運行性能,由於須要不斷地與 DOM 進行交互,訪問 DOM 的次數越多,引發瀏覽器重繪與重排的次數也就越多,就會延長整個頁面的交互就緒時間,這就是爲何性能優化的主要思想之一就是減小 DOM 操做的緣由、若是要用到事件委託,就會將全部的操做都放在 js 程序裏面,與 DOM 的操做就只須要交互一次,這樣就能夠大大減小與 DOM 的交互次數,提升性能。
每一個函數都是一個對象,是對象就會佔用內存,對象越多,內存佔用率就越大,天然性能就越差了(內存不夠用,是硬傷,哈哈),好比上面的100個li,就要佔用100個內存空間,若是是1000個,10000個呢,那隻能說呵呵了,若是用事件委託,那麼咱們就能夠只對它的父級(若是隻有一個父級)這一個對象進行操做,這樣咱們就須要一個內存空間就夠了,是否是省了不少,天然性能就會更好。
事件委託的原理
事件委託是利用事件的冒泡原理來實現的,何爲事件冒泡?就是事件從最深的節點開始執行,而後逐步向上傳播事件,例子:
頁面上有一個節點樹,div>ul>li>a,好比給最裏面的 a 加一個 click 點擊事件,那麼這個事件就會一層一層的往外執行,執行順序 a>li>ul>div,有這麼一個機制,那麼咱們給最外面的 div 加點擊事件,那麼裏面的 ul,li,a 作點擊事件的時候,都會冒泡到最外層的 div 上面,都會觸發,這就是事件委託,委託他們父級代爲執行事件。
事件委託怎麼實現
1 2 3 4 5 6
copy <ul id ="ul" > <li > 111</li > <li > 222</li > <li > 333</li > <li > 444</li > </ul >
實現功能是點擊li,彈出123:
1 2 3 4 5 6 7 8 9
copy window .onload = function () { var oUl = document .getElementById('ul' ); var aLi = oUl.getElementsByTagName('li' ); for (var i = 0 ; i < aLi.length; i++){ aLi[i].onclick = function () { alert(123 ); } } }
上面的代碼的意思很簡單,相信不少人都是這麼實現的,咱們看看有多少次的dom操做,首先要找到ul,而後遍歷li,而後點擊li的時候,又要找一次目標的li的位置,才能執行最後的操做,每次點擊都要找一次li;
那麼咱們用事件委託的方式作又會怎麼樣呢?
1 2 3 4 5 6
copy window .onload = function () { var oUl = document .getElementById('ul' ); oUl.onclick = function () { alert(123 ); } }
這裏用父級ul作事件處理,當li被點擊時,因爲冒泡原理,事件就會冒泡到ul上,由於ul上有點擊事件,因此事件就會觸發,固然,這裏當點擊ul的時候,也是會觸發的,那麼問題就來了,若是我想讓事件代理的效果跟直接給節點的事件效果同樣怎麼辦,好比說只有點擊li纔會觸發,不怕,咱們有絕招:
Event對象提供了一個屬性叫target,能夠返回事件的目標節點,咱們成爲事件源,也就是說,target就能夠表示爲當前的事件操做的dom,可是不是真正操做dom,固然,這個是有兼容性的,標準瀏覽器用ev.target,IE瀏覽器用event.srcElement,此時只是獲取了當前節點的位置,並不知道是什麼節點名稱,這裏咱們用nodeName來獲取具體是什麼標籤名,這個返回的是一個大寫的,咱們須要轉成小寫再作比較(習慣問題):
1 2 3 4 5 6 7 8 9 10
copy window .onload = function () { var oUl = document .getElementById("ul" ); oUl.onclick = function (ev ) { var ev = ev || window .event; var target = ev.target || ev.srcElement; if (target.nodeName.toLowerCase() == "li" ){ alert(target.innerHTML); } } }
這樣改下就只有點擊li會觸發事件了,且每次只執行一次dom操做,若是li數量不少的話,將大大減小dom的操做,優化的性能可想而知!
上面的例子是說li操做的是一樣的效果,要是每一個li被點擊的效果都不同,那麼用事件委託還有用嗎?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
copy var Add = document.getElementById("add"); var Remove = document.getElementById("remove"); var Move = document.getElementById("move"); var Select = document.getElementById("select"); Add.onclick = function(){ alert('添加'); }; Remove.onclick = function(){ alert('刪除'); }; Move.onclick = function(){ alert('移動'); }; Select.onclick = function(){ alert('選擇'); }
上面實現的效果我就很少說了,很簡單,4個按鈕,點擊每個作不一樣的操做,那麼至少須要4次dom操做,若是用事件委託,能進行優化嗎?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
copy var oBox = document .getElementById("box" );oBox.onclick = function (ev ) { var ev = ev || window .event; var target = ev.target || ev.srcElement; if (target.nodeName.toLowerCase() == 'input' ){ switch (target.id) { case 'add' : alert('添加' ); break ; case 'remove' : alert('刪除' ); break ; case 'move' : alert('移動' ); break ; case 'select' : alert('選擇' ); break ; default : alert('業務錯誤' ); break ; } } }
用事件委託就能夠只用一次dom操做就能完成全部的效果,比上面的性能確定是要好一些的
如今講的都是document加載完成的現有dom節點下的操做,那麼若是是新增的節點,新增的節點會有事件嗎?也就是說,一個新員工來了,他能收到快遞嗎?
1 2 3 4 5 6 7
copy <input type ="button" name ="" id ="btn" value ="添加" /> <ul id ="ul1" > <li > 111</li > <li > 222</li > <li > 333</li > <li > 444</li > </ul >
如今是移入li,li變紅,移出li,li變白,這麼一個效果,而後點擊按鈕,能夠向ul中添加一個li子節點
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
copy window .onload = function () { var oBtn = document .getElementById("btn" ); var oUl = document .getElementById("ul" ); var aLi = oUl.getElementsByTagName("li" ); var num = 4 ; for (var i = 0 ; i < aLi.length; i++){ aLi[i].onmouseover = function () { this .style.background = 'red' ; } aLi[i].onmouseout = function () { this .style.background = '#fff' ; } } oBtn.onclick = function () { num++; var oLi = document .createElement('li' ); oLi.innerHTML = 111 * num; oUl.appendChild(oLi); } }
這是通常的作法,可是你會發現,新增的li是沒有事件的,說明添加子節點的時候,事件沒有一塊兒添加進去,這不是咱們想要的結果,那怎麼作呢?通常的解決方案會是這樣,將for循環用一個函數包起來,命名爲mHover,以下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
copy window .onload = function () { var oBtn = document .getElementById("btn" ); var oUl = document .getElementById("ul" ); var aLi = oUl.getElementsByTagName("li" ); var num = 4 ; function mHover () { for (var i = 0 ; i < aLi.length; i++){ aLi[i].onmouseover = function () { this .style.background = 'red' ; } aLi[i].onmouseout = function () { this .style.background = '#fff' ; } } } mHover(); oBtn.onclick = function () { num++; var oLi = document .createElement('li' ); oLi.innerHTML = 111 * num; oUl.appendChild(oLi); mHover(); } }
雖然功能實現了,看着還挺好,但實際上無疑是又增長了一個dom操做,在優化性能方面是不可取的,那麼有事件委託的方式,能作到優化嗎?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
copy window .onload = function () { var oBtn = document .getElementById("btn" ); var oUl = document .getElementById("ul" ); var aLi = oUl.getElementsByTagName("li" ); var num = 4 ; oUl.onmouseover = function () { var ev = ev || window .event; var target = ev.target || ev.srcElement; if (target.nodeName.toLowerCase() == 'li' ){ target.style.background = 'red' ; } } oUl.onmouseout = function () { var ev = ev || window .event; var target = ev.target || ev.srcElement; if (target.nodeName.toLowerCase() == 'li' ){ target.style.background = '#fff' ; } } oBtn.onclick = function () { num++; var oLi = document .createElement('li' ); oLi.innerHTML = 111 * num; oUl.appendChild(oLi); } }
另一個思考的問題
如今給一個場景 ul > li > div > p,div佔滿li,p佔滿div,仍是給ul綁定時間,須要判斷點擊的是否是li(假設li裏面的結構是不固定的),那麼e.target就多是p,也有多是div,這種狀況你會怎麼處理呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14
copy <ul id ="test" > <li > <p > 11111111111</p > </li > <li > <div > 22222222 </div > </li > <li > <span > 3333333333</span > </li > <li > 4444444</li > </ul >
如上列表,有4個li,裏面的內容各不相同,點擊li,event對象確定是當前點擊的對象,怎麼指定到li上,下面我直接給解決方案:
1 2 3 4 5 6 7 8 9 10 11
copy var oUl = document .getElementById('test' );oUl.addEventListener('click' ,function (ev ) { var target = ev.target; while (target !== oUl) { if (target.tagName.toLowerCase() == 'li' ){ alert(target.innerHTML); break ; } target = target.parentNode; } });
如何阻止事件冒泡和默認事件
在解答這個問題以前,先來看看事件的執行順序
1 2 3 4 5
copy <div > <ul > <li > 冒泡/捕獲</li > </ul > </div >
實例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
copy var html = document .documentElement;var body = document .body;var div = body.querySelector('div' );var ul = body.querySelector('ul' );var li = body.querySelector('li' );ul.addEventListener('click' ,captureCallback,true ); li.addEventListener('click' ,captureCallback,true ); div.addEventListener('click' ,captureCallback,true ); body.addEventListener('click' ,captureCallback,true ); html.addEventListener('click' ,captureCallback,true ); ul.addEventListener('click' ,bubblingCallback,false ); li.addEventListener('click' ,bubblingCallback,false ); div.addEventListener('click' ,bubblingCallback,false ); body.addEventListener('click' ,bubblingCallback,false ); html.addEventListener('click' ,bubblingCallback,false ); function captureCallback (e ) { var target = e.currentTarget; console .log(target.tagName); } function bubblingCallback (e ) { var target = e.currentTarget; console .log(target.tagName); }
點擊 html 中的冒泡/捕獲,能夠獲得如下結果
1 2 3 4 5 6 7 8 9 10
copy capturing&bubbling phase.html:36 HTML capturing&bubbling phase.html:36 BODY capturing&bubbling phase.html:36 DIV capturing&bubbling phase.html:36 UL capturing&bubbling phase.html:36 LI capturing&bubbling phase.html:40 LI capturing&bubbling phase.html:40 UL capturing&bubbling phase.html:40 DIV capturing&bubbling phase.html:40 BODY capturing&bubbling phase.html:40 HTML
總結就是先捕獲,後冒泡,捕獲是從上到下,冒泡是從下到上。(形象說法:捕獲像石頭沉入海底,冒泡像氣球冒出水面)
去掉 函數 bubblingCallback
中的 e.stopPropagation()
的註釋,則只會進行捕獲,輸出
1 2 3 4 5 6
copy capturing&bubbling phase.html:40 HTML capturing&bubbling phase.html:40 BODY capturing&bubbling phase.html:40 DIV capturing&bubbling phase.html:40 UL capturing&bubbling phase.html:40 LI capturing&bubbling phase.html:45 LI
而取消 函數 captureCallback
中的 e.stopPropagation()
註釋,則只捕獲到 最上面的標籤
1
copy capturing&bubbling phase.html:40 HTML
經過上面,咱們能夠得知, event.stopPropagation()
,能夠阻止 捕獲和冒泡階段當前事件的進一步傳播。
規則:
在冒泡事件和捕獲事件同時存在的狀況下,捕獲事件優先級高一點
在同一個元素的綁定事件中,冒泡和捕獲沒有次序之分,遵循Javascript的執行順序。
在元素上同時綁定捕獲事件和冒泡事件,若是經過此元素的子級元素觸發,則優先觸發捕獲事件,若不經過此元素的子級元素觸發,則按照Javascript執行順序觸發。
事件不一樣瀏覽器處理函數
element.addEventListener(type, listener[, useCapture]); // IE6~8不支持(捕獲和冒泡經過useCapture,默認false)
element.attachEvent(’on’ + type, listener); // IE6~10,IE11不支持(只執行冒泡事件)
element[’on’ + type] = function(){} // 全部瀏覽器(默認執行冒泡事件)
-事件不一樣瀏覽器處理函數.png)
W3C 中定義了 3個事件階段,依次是 捕獲階段、目標階段、冒泡階段。事件對象按照上圖的傳播路徑依次完成這些階段。若是某個階段不支持或事件對象的傳播被終止,那麼該階段就會被跳過。舉個例子,若是Event.bubbles
屬性被設置爲false
,那麼冒泡階段就會被跳過。若是Event.stopPropagation()
在事件派發前被調用,那麼全部的階段都會被跳過。
捕獲 階段:在事件對象到達事件目標以前,事件對象必須從 window 通過目標的祖先節點傳播到事件目標。這個階段被咱們稱爲捕獲階段,在這個階段註冊的事件監聽器在事件到達其目標前必須先處理事件。
目標 階段:事件對象到達其事件目標,這個階段被咱們稱之爲目標階段。一旦事件對象達到事件目標,該階段的事件監聽器就要對它進行處理。若是一個事件對象類型被標誌爲不能冒泡。那麼對應的事件對象在到此階段時就會終止傳播。
冒泡 階段:事件對象以一個與捕獲階段相反的方向從事件目標傳播通過其祖先節點傳播到 window ,這個階段被稱之爲冒泡階段,在此階段註冊的事件監聽器會對應的冒泡事件進行處理
在一個事件完成了全部階段的傳播路徑後,它的Event.currentTarget
會被設置爲null
而且Event.eventPhase
會被設爲0。Event
的全部其餘屬性都不會改變(包括指向事件目標的Event.target
屬性)
跨瀏覽器的事件處理函數
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
copy var EventUtil = { addHandler: function (element, type, handler ) { if (element.addEventListener) { element.addEventListener(type, handler, false ); } else if (element.attachEvent) { element.attachEvent('on' + type, handler); } else { element['on' + type] = handler; } }, removeHandler: function (element, type, handler ) { if (element.removeEventListener) { element.removeEventListener(type, handler, false ); } else if (element.detachEvent) { element.detachEvent('on' + type, handler); } else { element['on' + type] = null ; } } };
跨瀏覽器的事件對象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
copy var EventUtil = { getEvent: function (e ) { return e ? e : window .e; }, getTarget: function (e ) { return e.target || e.srcElement; }, preventDefault: function (e ) { if (e.preventDefault) { e.preventDefault(); } else { e.returnValue = false ; } }, stopPropagation: function (e ) { if (e.stopPropagation) { e.stopPropagation() } else { e.cancelBubble = true ; } } }
js冒泡和捕獲是事件的兩種行爲,使用event.stopPropagation()起到阻止捕獲和冒泡階段中當前事件的進一步傳播。使用event.preventDefault()能夠取消默認事件。
防止冒泡和捕獲
w3c的方法是e.stopPropagation(),IE則是使用e.cancelBubble = true
topPropagation也是事件對象(Event)的一個方法,做用是阻止目標元素的冒泡事件,可是會不阻止默認行爲。什麼是冒泡事件?如在一個按鈕是綁定一個」click」事件,那麼」click」事件會依次在它的父級元素中被觸發 。stopPropagation就是阻止目標元素的事件冒泡到父級元素。如:
1 2 3 4 5
copy <div id ='div' onclick ='alert("div");' > <ul onclick ='alert("ul");' > <li onclick ='alert("li");' > test</li > </ul > </div >
單擊時,會依次觸發alert(「li」),alert(「ul」),alert(「div」),這就是事件冒泡。
阻止冒泡
1
copy window .event? window .event.cancelBubble = true : e.stopPropagation();
取消默認事件
w3c的方法是e.preventDefault(),IE則是使用e.returnValue = false
preventDefault它是事件對象(Event)的一個方法,做用是取消一個目標元素的默認行爲。既然是說默認行爲,固然是元素必須有默認行爲才能被取消,若是元素自己就沒有默認行爲,調用固然就無效了。什麼元素有默認行爲呢?如連接 <a>
,提交按鈕 <input type="submit">
等。當Event 對象的 cancelable爲false時,表示沒有默認行爲,這時即便有默認行爲,調用preventDefault也是不會起做用的。
咱們都知道,連接 <a>
的默認動做就是跳轉到指定頁面,下面就以它爲例,阻止它的跳轉:
1 2 3 4 5 6 7 8 9
copy var a = document .getElementById("test" );a.onclick =function (e ) { if (e.preventDefault){ e.preventDefault(); }else { window .event.returnValue == false ; } }
return false
javascript的return false只會阻止默認行爲,而是用jQuery 的話則既阻止默認行爲又防止對象冒泡
下面這個使用原生js,只會阻止默認行爲,不會中止冒泡
1 2 3 4 5 6 7 8 9 10 11
copy <div id ='div' onclick ='alert("div");' > <ul onclick ='alert("ul");' > <li id ='ul-a' onclick ='alert("li");' > <a href ="http://caibaojian.com/" id ="testB" > caibaojian.com</a > </li > </ul > </div > <script > var a = document .getElementById("testB" ); a.onclick = function () { return false ; }; </script >
總結使用方法
當須要中止冒泡行爲時,可使用
1 2 3 4 5 6 7 8 9
copy function stopBubble (e ) { if ( e && e.stopPropagation ) e.stopPropagation(); else window .event.cancelBubble = true ; }
當須要阻止默認行爲時,可使用
1 2 3 4 5 6 7 8 9 10
copy function stopDefault ( e ) { if ( e && e.preventDefault ) e.preventDefault(); else window .event.returnValue = false ; return false ; }
事件注意點
event表明事件的狀態,例如觸發event對象的元素、鼠標的位置及狀態、按下的鍵等等;
event對象只在事件發生的過程當中纔有效。
firefox裏的event跟IE裏的不一樣,IE裏的是全局變量,隨時可用;firefox裏的要用參數引導才能用,是運行時的臨時變量。
在IE/Opera中是window.event,在Firefox中是event;而事件的對象,在IE中是window.event.srcElement,在Firefox中是event.target,Opera中二者均可用。
1 2 3 4
copy function a (e ) { var e = (e) ? e : ((window .event) ? window .event : null ); var e = e || window .event; }
查找 添加 刪除 替換 插入到某個節點的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
copy document .getElementById('id' ); document .getElementsByClassName('class' ); document .getElementsByTagName('div' ); document .createDocumentFragment(); document .createElement(); document .createTextNode(); var oDiv = document .createElement('div' );document .body.appendChild(oDiv) var oP = createElement('p' ); document .body.insertBefore(oP,oDiv); document .body.removeChild(oP); var oSpan = document .createElement('span' );document .body.replaceChild(oSpan,oBox);
javaScript 的本地對象,內置對象和宿主對象
內部對象
js中的內部對象包括Array、Boolean、Date、Function、Global、Math、Number、Object、RegExp、String以及各類錯誤類對象,包括Error、EvalError、RangeError、ReferenceError、SyntaxError和TypeError。
本地對象
Array、Boolean、Date、Function、Number、Object、RegExp、String、Error、EvalError、RangeError、ReferenceError、SyntaxError和TypeError 等 new 能夠實例化
內置對象
其中Global和Math這兩個對象又被稱爲「內置對象」,這兩個對象在腳本程序初始化時被建立,沒必要實例化這兩個對象。
宿主對象
宿主環境:通常宿主環境由外殼程序建立與維護,只要能提供js引擎執行的環境均可稱之爲外殼程序。如:web瀏覽器,一些桌面應用系統等。即由web瀏覽器或是這些桌面應用系統早就的環境即宿主環境。
那麼宿主就是瀏覽器自帶的 document , window 等
==
和 ===
的不一樣
前者會自動轉換類型,然後者不會。
比較過程:
雙等號==:
若是兩個值類型相同,再進行三個等號(===)的比較
若是兩個值類型不一樣,也有可能相等,需根據如下規則進行類型轉換再比較
若是一個是 null,一個是 undefined 那麼相等
若是一個是字符串,一個是數值,把字符串轉換成數值以後再進行比較
三等號 === :
若是類型不一樣,就必定不相等
若是兩個都是數值,而且是同一個值,那麼相等;若是其中至少一個是 NaN,那麼不相等(判斷一個值是不是 NaN,只能使用 isNaN() 來判斷)
若是兩個都是字符串,每一個位置的字符都同樣,那麼相等,不然不相等
若是兩個值都是 true,或是 false,那麼相等
若是兩個值都引用同一個對象或者是函數,那麼相等,不然不相等
若是兩個值都是 null,或者是 undefined 那麼相等。
javaScript 的同源策略
同源策略的含義:腳本只能讀取和所屬文檔來源相同的窗口和文檔的屬性。
同源指的是主機名、協議和端口號的組合
-同源策略.png)
同源策略帶來的限制
cookie、LocalStorage 和 IndexDB沒法讀取
DOM 沒法獲取
AJAX請求沒法發送
主流跨域請求解決方案
一、JSONP 實現跨域
爲了便於客戶端使用數據,逐漸造成了一種非正式傳輸協議。人們把它稱做JSONP。該協議的一個要點就是容許用戶傳遞一個callback參數給服務端,而後服務端返回數據時會將這個callback參數做爲函數名來包裹住JSON數據,這樣客戶端就能夠隨意定製本身的函數來自動處理返回數據了。
jsonp的核心是動態添加 script標籤 來調用服務器提供的js腳本。
說說JSON和JSONP
JSONP實現原理?
JS 跨域請求資源會被限制。可是在頁面中,script 標籤跨域時,倒是沒有限制的(frame,img同理)。
咱們經過,script的src屬性,請求服務器,並經過參數(如:?callback=foo,foo爲本地一個執行的方法)告訴服務器返回指定格式的JS腳本,並將數據封裝在此腳本中。
服務器再配合客戶端返回一段腳本 (如: foo({「id」: 123, 「name」 : 張三, 「age」: 17}); ),其實返回的就是 一個客戶端本地的一個 可執行的方法的方法名, 並將要返回的 數據封裝在了參數 裏。
請求到資源後,本地就會執行此方法,經過對參數的處理,也就獲取到了咱們所要的數據。
JSONP的侷限性
JSONP 方式,當然方便強大。可是他的侷限性在於,它沒法完成POST請求。便是咱們將type改成post,在發送請求時,依然會是以Get 的方式。
二、CORS跨域
CORS原理
CORS(Cross-Origin-Resource Sharing,跨院資源共享)是一種容許多種資源(圖片,Css文字,Javascript等)在一個Web頁面請求域以外的另外一個域的資源的機制。 跨域資源共享這種機制讓Web應用服務器支持跨站訪問控制,從而使得安全的進行跨站數據傳輸成爲了可能。
經過這種機制設置一系列的響應頭 ,這些響應頭容許 瀏覽器與服務器進行交流,實現資源共享。
各語言設置響應頭的方法
CORS 解決方案相對於JSONP 更加靈活,並且支持POST請求,是跨域的根源性解決方案
三、代理層
JSONP 和CORS 是主流的 跨域問題 的解決方案。除了他們吶,還有一種解決方案,就是代理層 。簡要說一下
JS 調用本源的後臺的方法(這樣就不存在跨域的問題),而經過後臺(任何具備網絡訪問功能的後臺語言,ASP.NET ,JAVA,PHP等)去跨域請求資源,然後將結果返回至前臺。
另外也能夠看看
同源策略
JavaScript的同源策略 Redirect 1
編寫一個數組去重的方法
var arr = [1,1,3,4,2,4,7] => [1,2,4,2,7]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
copy function DuplicateRemoval (arr ) { for (var i = 0 ; i < arr.length-1 ; i++){ for (var j = i + 1 ; j < arr.length; j++){ if (arr[i] == arr[j]){ arr.splice(j,1 ); j--; } } } return arr; } var arr=[1 ,1 ,3 ,4 ,2 ,4 ,7 ];console .log(DuplicateRemoval(arr));function DuplicateRemoval (arr ) { for (var i = 0 ; i < arr.length; i++){ if (arr.indexOf(arr[i]) !== i){ arr.splice(i,1 ); i-- } } return arr; } var arr=[1 ,1 ,3 ,4 ,2 ,4 ,7 ];console .log(DuplicateRemoval(arr)); var arr=[1 ,1 ,3 ,4 ,2 ,4 ,7 ];var result = arr.filter( (element, index, self ) => self.indexOf(element) === index );console .log(result);var arr=[1 ,1 ,3 ,4 ,2 ,4 ,7 ];function Add (arr ) { var result = []; for (var i = 0 ; i < arr.length; i++){ if (arr.indexOf(arr[i]) === i){ result.push(arr[i]); } } return result; } console .log(Add(arr));var arr=[1 ,1 ,3 ,4 ,2 ,4 ,7 ];console .log([...new Set (arr)]);
JavaScript 的數據類型都有什麼
基本數據類型:String,Boolean,number,undefined,object,Null
引用數據類型:Object(Array,Date,RegExp,Function)
類型檢測
typeof
typeof
運算精度只能是基礎類型也就是 number
, string
, undefined
, boolean
, object
,要注意的是 null
和數組使用 typeof
運算符獲得的也是 object
1 2 3 4 5 6 7 8
copy console .log(typeof 123 ); console .log(typeof 'type' ); console .log(typeof null ); console .log(typeof undefined ); console .log(typeof true ); console .log(typeof []); console .log(typeof {}); console .log(typeof function () {});
instanceof
用於判斷一個變量是否某個對象的實例,或用於判斷一個變量是否某個對象的實例
instanceof
運算符能夠精確到是哪種類型的引用,但 instanceof
也有一個缺陷就是對於直接賦值的數字,字符串,布爾值以及數組是不能將其識別爲Number
,String
,Boolean
,Array
。
1 2 3 4 5 6
copy console .log(123 instanceof Number ); console .log('type' instanceof String ); console .log(true instanceof Boolean ); console .log([] instanceof Array ); console .log({} instanceof Object ); console .log(function () {} instanceof Function );
toString.call()
1 2 3 4 5 6 7 8
copy console .log(toString.call(123 ) ); console .log(toString.call('type' )); console .log(toString.call(null )); console .log(toString.call(undefined )); console .log(toString.call(true )); console .log(toString.call([])); console .log(toString.call({})); console .log(toString.call(function () {}));
constructor
構造函數的屬性 用來判別對建立實例對象的函數的引用,另外 constructor 能夠被改寫
1 2 3 4 5 6 7 8 9 10 11 12 13
copy var number = 123 ,string = 'type' , boolean = true , arr = [], obj = {}, fn = function () {}; console .log( number.constructor); console .log( string.constructor); console .log( boolean.constructor); console .log( arr.constructor); console .log( obj.constructor); console .log( fn.constructor);
也能夠用 constructor.name
就會返回函數名
Object.prototype.toString.call()
這個方法其實跟上面 toString()
是同樣的,只是上面那個方法有可能會被重寫。因此用這個比較穩妥,全部對象都包含有一個內部屬性[[Class]] ,這個屬性不能直接訪問,可是咱們能夠經過Object.prototype.toString.call()
1 2 3 4 5 6 7 8
copy console .log(Object .prototype.toString.call(123 ) ); console .log(Object .prototype.toString.call('type' )); console .log(Object .prototype.toString.call(null )); console .log(Object .prototype.toString.call(undefined )); console .log(Object .prototype.toString.call(true )); console .log(Object .prototype.toString.call([])); console .log(Object .prototype.toString.call({})); console .log(Object .prototype.toString.call(function () {}));
須要注意的是IE6/7/8中 Object.prototype.toString.apply(null)返回「[object Object]」。
js 中 innerText/value/innerHTML三個屬性的區別
1 2 3 4
copy <div value = "3" id ="target3" > <input type ="text" value ="1" id ="target1" > <span id ="target2" value ="2" > span</span > </div >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
copy var a = document .getElementById("target1" );var b = document .getElementById("target2" );var c = document .getElementById("target3" );console .log('----------' );console .log(a.value); console .log(a.innerHTML);console .log(a.innerText); console .log('----------' );console .log(b.value); console .log(b.innerHTML); console .log(b.innerText); console .log('----------' );console .log(c.value); console .log(c.innerHTML); console .log(c.innerText);
總結:
innerText
是標籤內的文本,輸出的是字符串,它能夠得到某個節點下面的全部標籤內的文本
innerHtml
能夠得到某個DOM 節點內部的 HTML 代碼
value
是表單元素的屬性,得到是表單元素裏面的值
但願獲取到頁面中全部的 checkbox 怎麼作?( ( 不使用第三方框架) )
html:
1 2 3
copy <input type ="checkbox" > <input type ="checkbox" > <input type ="checkbox" >
1 2 3 4 5 6 7 8 9 10 11
copy window .onload = function () { var domList = document .getElementsByTagName('input' ); var checkBoxList = []; var len = domList.length; while (len--){ if (domList[len].type = 'checkbox' ){ checkBoxList.push(domList[len]); } } console .log(checkBoxList); }
當一個 Dom節點被點擊時候,咱們但願可以執行一個函數,應該怎麼作?
1 2
copy <div onlick = "test()" >
1 2 3 4
copy xxx.onclick = test addEventListener(xxx, 'click' , test)
Javascript 的事件流模型都有什麼?
「事件冒泡」:事件開始由最具體的元素接受,而後逐級向上傳播
「事件捕捉」:事件由最不具體的節點先接收,而後逐級向下,一直到最具體的
「DOM 事件流」:三個階段,事件捕捉,目標階段。事件冒泡。
已知有字符串 foo=」get-element-by-id」,寫一個 function 將其轉化成駝峯表示法」——getElementById」。
1 2 3 4 5 6 7 8 9 10
copy var foo = "get-element-by-id" ;function combo (msg ) { var arr = msg.split('-' ); for (var i=1 ;i < arr.length; i++){ arr[i] = arr[i].charAt(0 ).toUpperCase()+arr[i].substr(1 ,arr[i].length-1 ); } msg = arr.join('' ); return msg; } console .log(combo(foo));
輸出今天的日期,以 YYYY-MM-DD 的方式
1 2 3 4 5 6 7
copy var date = new Date ();var year = date.getFullYear();var month = date.getMonth() + 1 ;month = month < 10 ? '0' + month : month; var day = date.getDate();day = day < 10 ? '0' + day : day; console .log([year,month,day].join('-' ))
將字符串 」 <tr><td>{${$name}</td></tr>
」 中的 {$id}
替換 成 10 , {$name}
替換成 Tony (使用正則表達式)
1 2
copy var str = '<tr><td>{$id}</td><td>{$name}</td></tr>' ;str.replace(/{\$id}/g ,10 ).replace(/{\$name}/ ,'Tony' )
爲了保證頁面輸出安全,咱們常常須要對一些特殊的字符進行轉義,請寫一個函數 escapeHtml ,將 <
, >
, &
, 「
進行轉義
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
copy function escapeHtml (str ) { return str.replace(/[<>」&]/g ,function (match ) { switch (match) { case "<" : return '<' ; break ; case ">" : return '>' break ; case "\」" : return '"' break ; case "&" : return '&' break ; } }); }
用 js 實現隨機選擇 10 -100 之間的 10 個數字,並存入一個數組,並排序
1 2 3 4 5 6 7 8 9 10 11
copy var result = [];function getRandom (start,end ) { var choice = end - start + 1 ; return Math .floor(Math .random() * choice + start); } for (var i = 0 ; i < 10 ; i++){ var arr = getRandom(10 ,100 ); result.push(arr); } console .log(result.sort( (x,y ) => x - y ));
把兩個數組合並,並刪除第二個元素
1 2 3 4
copy var arr1 = ['a' ,'b' ,'c' ];var arr2 = ['d' ,'e' ,'f' ];var result = arr1.concat(arr2);result.splice(1 ,1 );
請寫一段 JS 程序提取URL中的各個 GET 參數( 參數名和參數個數不肯定 ) , 將其按key-value 形式返回到一個 json 結構中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
copy const url = 'http://item.taobao.com/item.htm?a=1&b=2&c=&d=xxx&e' ;function serilizeUrl (url ) { const objUrl = {}; if (/\?/ .test(url)){ const urlStr = url.substring(url.indexOf('?' ) + 1 ); const urlArr = urlStr.split('&' ); for (let i = 0 ; i < urlArr.length; i++){ const urlItem = urlArr[i]; const item = urlItem.split('=' ); objUrl[item[0 ]] = item[1 ]; } return objUrl; } return null ; } console .log(serilizeUrl(url));
正則構造函數與正則表示字面量
Javascript中的正則表達式也是對象,咱們可使用兩種方法建立正則表達式:
使用new RegExp()構造函數
使用正則表達字面量
先說結果,使用正則表達字面量的效率更高 。
下面的示例代碼演示了兩種可用於建立正則表達式以匹配反斜槓的方法:
1 2 3 4 5 6 7 8 9
copy var re = /\\/gm ;var reg = new RegExp ('\\\\' ,'gm' );var foo = 'abc\\123' ;console .log(re.test(foo)); console .log(reg.test(foo));
如上面的代碼中能夠看到,使用正則表達式字面量表示法時式子顯得更加簡短,並且不用按照相似類(class-like)的構造函數方式思考。
其次,在當使用構造函數的時候,在這裏要使用四個反斜槓才能匹配單個反斜槓。這使得正則表達式模式顯得更長,更加難以閱讀和修改。正確來講,當使用RegExp()構造函數的時候,不只須要轉義引號(即\」表示」),而且一般還須要雙反斜槓(即\表示一個\)。
使用new RegExp()的緣由之一在於,某些場景中沒法事先肯定模式,而只能在運行時以字符串方式建立。
寫一個函數,清除字符串先後的空格
使用自帶接口 trim(),考慮兼容性:
1 2 3 4 5 6 7 8
copy if (String .prototype.trim){ String .prototype.trim = function () { return this .replace(/^\s+/ ,"" ).replace(/\s+$/ ,"" ); } } var str = '\t\n test string ' .trim();console .log(str);
JavaScript 中 callee 和 caller 的做用?
caller
返回一個函數的引用,這個函數調用了當前的函數
callee
返回正在執行的函數自己的引用,它是arguments的一個屬性
caller
使用這個屬性要注意:
這個屬性只有當函數執行時纔有用
若是在js 程序中,函數由頂級調用,則返回 null
1 2 3 4
copy function fn () { console .log(fn.caller); } fn();
包裹一層
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
copy function a () { fn(); function fn () { console .log(fn.caller); } } a();
結果爲 a 函數。
再包裹一層
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
copy function a () { b(); function b () { fn(); function fn () { console .log(fn.caller); } } } a();
結果爲 b 函數
callee
要注意:
這個屬性只有函數執行時纔有效
它有一個 length
屬性,能夠用來得到形參的個數,所以能夠用來比較形參和實參個數是否一致,即比較 arguments.length
是否等於 argument.callee.length
它能夠用來傳遞匿名函數
1 2 3 4 5 6 7 8 9 10 11 12 13
copy function a () { console .log(arguments .callee); } function b () { a(); } b();
a 在 b 中被調用了,可是它返回了 a 自己的引用。
下面是一個經典的階乘函數
1 2 3 4 5 6 7 8
copy function factorial (num ) { if (num <= 1 ){ return 1 ; }else { return num * factorial(num - 1 ) } } console .log(factorial(120 ));
爲了不函數名修改致使內部有報錯,能夠修改爲如下函數
1 2 3 4 5 6 7
copy function factorial (num ) { if (num <= 1 ){ return 1 ; }else { return num * arguments .callee(num - 1 ) } }
得到形參和實參個數
1 2 3 4 5
copy function a (num1,num2,num3 ) { console .log(arguments .length); console .log(arguments .callee.length); } a(1 )
兔子問題
若是一對兔子每個月生一對兔子,一對新生兔子,從第二個月起就開始生兔子,假設每對兔子都是一雌一雄,試問一對兔子,第 N 個 月能繁殖多少成對多少對兔子(使用 callee 完成)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
copy function rabbit (num ) { if ( num === 1 ){ return 1 ; }else if (num === 2 ){ return 1 ; }else { if (result[num]){ return result[num]; }else { result[num] = arguments .callee(num-1 ) + arguments .callee(num-2 ) return result[num]; } } }
參考連接:
JS類型轉換(強制和自動的規則)
JavaScript 數據類型轉換(顯式與隱式)
DOM標準與IE的html元素事件模型區別
ie和dom事件流的區別
前端面試題——call與apply方法的異同
JavaScript中B繼承A的方法 Me丶微笑
Javascript 中 做用域、閉包與 this 指針
理解JAVASCRIPT的閉包
深刻理解JS中的變量做用域
解決js函數閉包內存泄露問題的辦法
js中的事件委託或是事件代理詳解
JavaScript捕獲和冒泡探討
JS阻止冒泡和取消默認事件(默認行爲)
javascript 原生方法對dom節點的操做,建立、添加、刪除、替換、插入、複製、移動等操做
JavaScript中本地對象、內置對象和宿主對象
js中==和===區別
同源策略與JS跨域請求(圖文實例詳解)
Js數組去重方法總結
javascript 六種數據類型(一)
Javascript正則構造函數與正則表達字面量&&經常使用正則表達式
js中的 caller與callee用法小實例
caller和callee的區別 ( 轉載) http://laibh.top/前端面試題目彙總摘錄-JS 基礎篇.html