事件委託技術能讓你避免對特定的每一個節點添加事件監聽器;相反,事件監聽器是被添加到它們的父元素上。事件監聽器會分析從子元素冒泡上來的事件,找到是哪一個子元素的事件。javascript
優勢:html
this 永遠指向函數運行時所在的對象,而不是函數被建立時所在的對象。前端
原型繼承的基礎是原型鏈查找。java
原型鏈查找基本概念:
每個函數 F 都有一個原型對象(prototype)F.prototype
每個函數均可以經過 new 關鍵字化身成爲一個類構造函數,new F 會產生一個對象 O
在調用對象的某個屬性或者方法,好比 http://O.xxx 的時候,會首先查找對象自身是否有這個方法或者屬性,若是沒找到就會去對象的構造函數的原型對象中查找(注意有兩個定語),也就是查找 O 的構造函數 F 的原型對象 http://F.prototype.xxx
F.prototype 也是一個對象,查找 http://F.prototype.xxx 的時候會重複第 3 步的過程web
這裏只是聲明一個叫foo的function,直接用()執行這樣是不成功的,想要變成IIFE就要把聲明變成表達式,就能夠當即執行了,能夠這樣(function foo(){})()或者(function foo(){}()),這就是用括號把定義強轉成表達式,固然還有其餘方法,關鍵就是聲明不能夠執行,表達式才能夠執行。面試
undefined:未定義,在變量沒有賦值的時候的值即爲undefined。「缺乏值」,就是此處應該有一個值,可是尚未定義。
underclared:即爲被污染的命名,訪問沒有被聲明的變量,會拋出異常,終止執行。嘗試訪問一個undeclared的變量,瀏覽器會報錯,JS執行會中斷。
null:是一個空的對象引用。「沒有對象」,即該處不該該有值ajax
區別:
undefined和null在if語句中,都會被自動轉爲false,相等運算符甚至直接報告二者相等。typeof undefined會返回undefined ,而typeof null 總返回 object(typeof有六種可能:「number」、「string」、「boolean」、「object」、「function」、「undefined」)算法
false == undefined;//false false == null;//false null == undefined;//true
該如何檢測它們?typescript
var obj; obj ===undefined; //檢測undfined 方法一 typeof obj === ‘undefined’;//檢測undefined方法2 obj = null; obj === null;//來檢測null typeof null;//‘object’
定義:閉包就是能夠讀取到其餘函數內部變量的函數。
閉包的用途:express
注意:
匿名函數能夠用做回調函數執行,能夠防止全局變量污染。
在 JS 框架中常使用匿名函數來避免全局變量的污染。
$.(「input」).each(function(e){this.val(‘OK’)});
(function(){})();
$(document).ready(function(){ });
$(function() {})
原生對象:獨立於宿主環境的 ECMAScript 實現提供的對象。爲array obj regexp date function等能夠new實例化的對象。
內置對象:爲gload Math 等,開發者沒必要明確實例化內置對象,它已被實例化了。相似於isNaN()、parseInt()和parseFloat()方法等,看起來都是函數,而實際上,它們都是Global對象的方法。具體能夠參考 JavaScript 全局對象
宿主對象:即由 ECMAScript 實現的宿主環境(操做系統和瀏覽器)提供的對象。全部的BOM和DOM對象都是宿主對象。由於其對於不一樣的「宿主」環境所展現的內容不一樣(這就是兼容性和特性檢測的原因)。ECMAScript官方未定義的對象都屬於宿主對象。
第一個爲函數聲明,第二個將函數person()返回值賦值給person,第三個經過Person()的構造器建立了一個對象讓person變量引用該對象;
call和apply都是調用一個對象的一個方法,以另外一個對象替換當前對象。它們都屬於Function.prototype的一個方法,因此每一個function實例都有call和apply屬性。這兩個方法能夠用來代替另外一個對象調用一個方法,可將一個函數的對象上下文從初始的上下文改變爲由 thisObj 指定的新對象。
區別:
二者傳遞的參數不一樣,雖然函數第一個參數都是要傳入給當前對象的對象,可是,apply的第二個參數是一個參數數組,將多個參數組合成爲一個數組傳入;而call第二個參數則是直接的參數列表。
Function.prototype.bind()其實就是函數綁定。函數的接收者取決於他是如何被調用,能夠經過調用.bind()給函數綁定做用域上下文(this的值),即函數的接收者。
var foo = { x: 3} var bar = function(){console.log( this.x);} bar(); // undefinedvar boundFunc = bar.bind(foo);//隱式看做是在foo做用域裏調用bar方法 boundFunc(); // 3
.bind()建立了一個函數,當這個函數在被調用的時候,它的 this 關鍵詞會被設置成被傳入的值(這裏指調用bind()時傳入的參數)也就是咱們傳入想要的上下文。 簡單的用法: 關於 Function.prototype.bind() 內部,這裏有個很是簡單的例子:
Function.prototype.bind = function (scope) { var fn = this; return function () { return fn.apply(scope);//使用call效果同樣 }; }
document.write()方法能夠用在兩個方面:
記住,在載入頁面後,瀏覽器輸出流自動關閉。在此以後,任何一個對當前頁面進行操做的document.write()方法將打開—個新的輸出流,它將清除當前頁面內容(包括源文檔的任何變量或值)。所以,假如但願用腳本生成的HTML替換當前頁面,就必須把HTML內容鏈接起來賦給一個變量,使用一個document.write()方法完成寫操做。沒必要清除文檔並打開一個新數據流,一個document.write()調用就可完成全部的操做。
關於document.write()方法還有一點要說明的是它的相關方法document.close()。腳本向窗口(無論是本窗口或其餘窗口)寫完內容後,必須關閉輸出流。在延時腳本的最後一個document.write()方法後面,必須確保含有document.close()方法,不這樣作就不能顯示圖像和表單。而且,任何後面調用的document.write()方法只會把內容追加到頁面後,而不會清除現有內容來寫入新值。爲了演示document.write()方法,咱們提供了同一個應用程序的兩個版本。
大多數生成的廣告代碼依舊使用 document.write(),雖然這種用法會讓人很不爽。
檢測瀏覽器的特殊名稱和版本(用戶代理檢測)即瀏覽器UA字符串嗅探。瀏覽器嗅探技術能夠快捷的將代碼進行分支,以便針對不一樣的瀏覽器應用不一樣的指令;針對特定瀏覽器的特定版本,超出範圍以外都是不可靠的
優點:能夠刷新局部頁面,而不用總體頁面都刷新
缺點:用戶禁用javascript的狀況
工做原理:JSONP動態建立script標籤,回調函數。Ajax是頁面無刷新請求數據操做,動態添加一個<script>標籤,而script標籤的src屬性是沒有跨域的限制的。這樣說來,這種跨域方式其實與ajax XmlHttpRequest協議無關了。
當GET請求從被調用頁面返回時,能夠返回一段JavaScript代碼,這段代碼會自動調用主頁面中的一個callback函數。
優勢:不受同源策略的影響,它的兼容性更好,在更加古老的瀏覽器中均可以運行,不須要XMLHttpRequest或ActiveX的支持;而且在請求完畢後能夠經過調用callback的方式回傳結果
缺點:只支持GET請求而不支持POST等其它類型的HTTP請求;它只支持跨域HTTP請求這種狀況,不能解決不一樣域的兩個頁面之間如何進行JavaScript調用的問題。
在JavaScript代碼運行以前實際上是有一個編譯階段的。編譯以後纔是從上到下,一行一行解釋執行。變量提高就發生在編譯階段,它把變量和函數的聲明提高至做用域的頂端。(編譯階段的工做之一就是將變量與其做用域進行關聯)。
變量提高須要注意兩點:
函數聲明:
從目標元素開始,往頂層元素傳播。途中若是有節點綁定了相應的事件處理函數,這些函數都會被依次觸發。若是想阻止事件起泡,可使用e.stopPropagation()(Firefox)或者e.cancelBubble=true(IE)來組織事件的冒泡傳播
DOM元素的attribute和property二者是不一樣的東西。attribute翻譯爲「特性」,property翻譯爲「屬性」。
attribute是一個特性節點,每一個DOM元素都有一個對應的attributes屬性來存放全部的attribute節點,attributes是一個類數組的容器,說得準確點就是NameNodeMap,不繼承於Array.prototype,不能直接調用Array的方法。attributes的每一個數字索引以名值對(name=」value」)的形式存放了一個attribute節點。
property就是一個屬性,若是把DOM元素當作是一個普通的Object對象,那麼property就是一個以名值對(name=」value」)的形式存放在Object中的屬性。要添加和刪除property和普通的對象相似。
不少attribute節點還有一個相對應的property屬性,好比上面的div元素的id和class既是attribute,也有對應的property,無論使用哪一種方法均可以訪問和修改。
總之,attribute節點都是在HTML代碼中可見的,而property只是一個普通的名值對屬性
由於你不知道哪一天瀏覽器或javascript自己就會實現這個方法,並且和你擴展的實現有不一致的表現。到時候你的javascript代碼可能已經在無數個頁面中執行了數年,而瀏覽器的實現致使全部使用擴展原型的代碼都崩潰了。
須要給Array原型添加一個distinct的方法,最好檢查是否存在同名的方法,避免自定義方法覆蓋原生方法:
Arrray.prototype.distinct = Arrray.prototype.distinct || function(){/…../}
ready 表示文檔的 DOM 已經加載完成(不包含圖片、視頻等資源);load 表示整個網頁加載完成。能夠看出,ready 事件發生在 load 事件以前。
若是兩邊的操做數具備一致的類型且擁有相同的值時,=== 返回 true,!== 返回 false。
同源策略限制了一個源(origin)中加載文本或腳本與來自其它源(origin)中資源的交互方式。
同源策略出於安全,不容許源 A 的腳本讀取(read)源 B 的資源的內容,但卻容許執行(execute)源 B 的資源。這個概念也有些拗口。簡單說,有一個頁面調用了 Google CDN 提供的 jQuery,以及其它 CDN 上的 Bootstrap JS、CSS 代碼,雖然它們與個人博客不一樣源,但我能夠用它們來操做這個頁面,並應用樣式,這是執行的概念。
將此方法添加至 Array.prototype 實現,代碼以下:
Array.prototype.duplicator = function(){ var l = this.length,i; for(i=0;i<l;i++){ this.push(this[i]) } }
一個運算符若是有一個操做數,爲一元運算符,兩個爲二元,三個爲三元運算符,三元表達式則爲一個三元運算表達式!
ECMAScript5中引入的嚴格模式,經過讓JavaScript運行環境對一些開發過程當中最多見和不易發現的錯誤作出和當前不一樣的處理,來讓開發者擁有一個」更好」的JavaScript語言。
好處:
好處具體體現:
壞處:一樣的代碼,在「嚴格模式」中,可能會有不同的運行結果;一些在「正常模式」下能夠運行的語句,在「嚴格模式」下將不能運行
總結:啓用JavaScript嚴格模式,它能幫你發現代碼中不曾注意到的錯誤。不要在全局環境中啓用,但你能儘可能多的使用IIFE(當即執行函數表達式)來把嚴格模式做用到多個函數範圍內。一開始,你會遇到以前不曾碰到過的錯誤提示,這是正常的。當啓用嚴格模式後,請確保在支持的瀏覽器中作了測試,以發現新的潛在問題。必定不要僅僅在代碼中添加一行」use strict」就假定餘下的代碼能正常工做。
for (var i = 1; i <= 30; i++) { if (i % 3 === 0) { if (i % 5 === 0) { alert('fizzbuzz' + i); continue; } alert('fizz' + i); continue; } else if (i % 5 === 0) { if (i % 3 === 0) { alert('fizzbuzz' + i); continue; } alert('buzz' + i); continue; } }
它的意思是: 儘可能少在全局做用域定義變量。
目的:減小名稱衝突 利於模塊化
要等到等頁面徹底加載後(全部圖像、javascript文件、CSS等外部文件)。替代:把script標籤放到最後面。
單頁應用是一種特殊的web應用,它將全部的活動侷限於一個web頁面中,僅在該Web頁面初始化時加載相應的HTML、JavaScript 和 CSS。
優勢:
缺點:
優勢:易讀性改善
以Typescript爲例子:
typescript是javascript的強類型版本,在編譯期去掉類型和特有語法,生成純粹的javascript代碼。TypeScript 是 JavaScript 的超集,這意味着他支持全部的 JavaScript 語法。並在此之上對 JavaScript 添加了一些擴展,如 class / interface / module 等。這樣會大大提高代碼的可閱讀性。
優勢:
缺點:
javascript中的原始值(undefined、null、布爾值、數字和字符串)與對象(包括數組和函數)有着根本區別。原始值是不可更改的:任何方法都沒法更改(或「突變」)一個原始值。對數字和布爾值來講顯然如此—-改變數字的值自己就說不通,而對字符串來講就不那麼明顯了,由於字符串看起來像由字符組成的數組,咱們指望能夠經過指定索引來假改字符串中的字符。實際上,javascript是禁止這樣作的。字符串中全部的方法看上去返回了一個修改後的字符串,實際上返回的是一個新的字符串值。
區別:
優勢:
*由於不能修改一個不變對象的狀態,因此能夠避免由此引發的沒必要要的程序錯誤;一個不變的對象要比一個可變的對象更加容易維護。
缺點:
可使用const 修飾變量不可變
同步式:當計算機調度線程進行I/O操做命令後,因爲文件的讀寫或者網絡通訊須要較長的操做時間,操做系統爲了充分利用cpu,此時會暫停到當前的I/O線程對CPU的控制(故又稱同步式爲阻塞式I/O),把cup資源然給其餘的線程資源,當I/O線程完成了操做時,此時操做系統會恢復此時的I/O線程,從而當前I/O線程從新得到了cup的的控制權,繼續完成其餘操做。
異步式:異步式IO又稱非阻塞式I/O,異步式與同步式不一樣的是,當線程進行IO操做時,操做系統並非暫停當前的線程操做,而是執行完I/O指令後,操做系統繼續讓當前線程執行下一條指令,當I/O操做完成後,會經過事件(event)通知I/O線程,而線程在接收到通知後,會處理響應事件。
主線程從「任務隊列」中讀取事件,這個過程是循環不斷的,因此整個的這種運行機制又稱爲Event Loop(事件循環)。
所謂「回調函數」(callback),就是那些會被主線程掛起來的代碼。異步任務必須指定回調函數,當異步任務從「任務隊列」回到執行棧,回調函數就會執行。
「任務隊列」是一個先進先出的數據結構,排在前面的事件,優先返回主線程。主線程的讀取過程基本上是自動的,只要執行棧一清空,「任務隊列」上第一位的事件就自動返回主線程。可是,因爲存在後文提到的「定時器」功能,主線程要檢查一下執行時間,某些事件必需要在規定的時間返回主線程。
第一個未函數聲明,第二個爲函數定義表達式(函數定義表達式foo,變量聲明提早單賦值並未提早)
(function(){ var a = b = 3; })(); console.log("a defined? " + (typeof a !== 'undefined')); console.log("b defined? " + (typeof b !== 'undefined'));
輸出: a defined? false b defined? true
這是一個愈來愈廣泛的作法,被許多流行的JavaScript庫(jQuery,Node.js等)採用。這種技術建立了一個圍繞文件所有內容的閉包,也許是最重要的是,建立了一個私有的命名空間,從而有助於避免不一樣JavaScript模塊和庫之間潛在的名稱衝突。
這種技術的另外一個特色是,容許一個易於引用的(假設更短的)別名用於全局變量。這一般用於,例如,jQuery插件中。jQuery容許你使用jQuery.noConflict(),來禁用 $ 引用到jQuery命名空間。在完成這項工做以後,你的代碼仍然可使用$ 利用這種閉包技術,以下所示:
(function($) { /* jQuery plugin code referencing $ */ } )(jQuery);
use strict 是一種在JavaScript代碼運行時自動實行更嚴格解析和錯誤處理的方法。那些被忽略或默默失敗了的代碼錯誤,會產生錯誤或拋出異常。一般而言,這是一個很好的作法。
嚴格模式的一些主要優勢包括:
function foo1(){ return { bar: "hello" }; }function foo2(){ return { bar: "hello" }; }
返回不相同: foo1 returns:Object {bar: "hello"}foo2 returns:undefined
分號在JavaScript中是一個可選項(儘管省略它們一般是很是糟糕的形式)。其結果就是,當碰到 foo2()中包含 return語句的代碼行(代碼行上沒有其餘任何代碼),分號會當即自動插入到返回語句以後。也不會拋出錯誤,由於代碼的其他部分是徹底有效的,即便它沒有獲得調用或作任何事情(至關於它就是是一個未使用的代碼塊,定義了等同於字符串 "hello"的屬性 bar)。
這種行爲也支持放置左括號於JavaScript代碼行的末尾,而不是新代碼行開頭的約定。正如這裏所示,這不只僅只是JavaScript中的一個風格偏好。
NaN 屬性表明一個「不是數字」的值。這個特殊的值是由於運算不能執行而致使的,不能執行的緣由要麼是由於其中的運算對象之一非數字(例如, "abc" / 4),要麼是由於運算的結果非數字(例如,除數爲零)。
NaN的特色:
測試數字爲NaN的方法:
console.log(0.1 + 0.2);console.log(0.1 + 0.2 == 0.3);
JavaScript中的數字和浮點精度的處理相同,所以,可能不會老是產生預期的結果。
以上所提供的例子就是一個演示了這個問題的典型例子。但出人意料的是,它會輸出:
0.30000000000000004false
ECMAScript 6 以前沒有提供相似 Number.isInteger 的方法。在ECMAScript規格說明中,整數只概念上存在:即,數字值老是存儲爲浮點值。
方法:
雖然這個以 parseInt函數爲基礎的方法在 x 取許多值時都能工做良好,但一旦 x 取值至關大的時候,就會沒法正常工做。問題在於 parseInt() 在解析數字以前強制其第一個參數到字符串。所以,一旦數目變得足夠大,它的字符串就會表達爲指數形式(例如, 1e+21)。所以,parseInt() 函數就會去解析 1e+21,但當到達 e字符串的時候,就會中止解析,所以只會返回值 1。注意:
String(1000000000000000000000)'1e+21'> parseInt(1000000000000000000000, 10)1> parseInt(1000000000000000000000, 10) === 1000000000000000000000false
(function() { console.log(1); setTimeout(function(){console.log(2)}, 1000); setTimeout(function(){console.log(3)}, 0); console.log(4); })();
序號以下:
1 4 3 2
比較明顯而易見的那部分:
1 和 4之因此放在前面,是由於它們是經過簡單調用 console.log() 而沒有任何延遲輸出的
2 之因此放在 3的後面,是由於 2 是延遲了1000毫秒(即,1秒)以後輸出的,而 3 是延遲了0毫秒以後輸出的。
好的。可是,既然 3 是0毫秒延遲以後輸出的,那麼是否意味着它是當即輸出的呢?若是是的話,那麼它是否是應該在 4 以前輸出,既然 4 是在第二行輸出的?
要回答這個問題,你須要正確理解JavaScript的事件和時間設置。
瀏覽器有一個事件循環,會檢查事件隊列和處理未完成的事件。例如,若是時間發生在後臺(例如,腳本的 onload 事件)時,瀏覽器正忙(例如,處理一個 onclick),那麼事件會添加到隊列中。當onclick處理程序完成後,檢查隊列,而後處理該事件(例如,執行 onload 腳本)。
一樣的, setTimeout() 也會把其引用的函數的執行放到事件隊列中,若是瀏覽器正忙的話。
當setTimeout()的第二個參數爲0的時候,它的意思是「儘快」執行指定的函數。具體而言,函數的執行會放置在事件隊列的下一個計時器開始。可是請注意,這不是當即執行:函數不會被執行除非下一個計時器開始。這就是爲何在上述的例子中,調用 console.log(4) 發生在調用 console.log(3) 以前(由於調用 console.log(3) 是經過setTimeout被調用的,所以會稍微延遲)。
下面這個函數在 str 是迴文結構的時候返回true,不然,返回false。
function isPalindrome(str) { str = str.replace(/\W/g, '').toLowerCase(); return (str == str.split('').reverse().join('')); }
例如:
console.log(isPalindrome("level")); // logs 'true'console.log(isPalindrome("levels")); // logs 'false'console.log(isPalindrome("A car, a man, a maraca")); // logs 'true'
console.log(sum(2,3)); // Outputs 5 console.log(sum(2)(3)); // Outputs 5
(至少)有兩種方法能夠作到:
方法1:
function sum(x) { if (arguments.length == 2) { return arguments[0] + arguments[1]; }else { return function(y) { return x + y; }; } }
方法2:
function sum(x, y) { if (y !== undefined) { return x + y; } else { return function(y) { return x + y; }; } }
for (var i = 0; i < 5; i++) { var btn = document.createElement('button'); btn.appendChild(document.createTextNode('Button ' + i)); btn.addEventListener('click', function(){ console.log(i); }); document.body.appendChild(btn); }
(a)當用戶點擊「Button 4」的時候會輸出什麼到控制檯,爲何?
不管用戶點擊什麼按鈕,數字5將總會輸出到控制檯。這是由於,當 onclick 方法被調用(對於任何按鈕)的時候, for 循環已經結束,變量 i 已經得到了5的值。
(b)提供一個或多個備用的可按預期工做的實現方案?
要讓代碼工做的關鍵是,經過傳遞到一個新建立的函數對象,在每次傳遞經過 for 循環時,捕捉到 i 值。下面是三種可能實現的方法:
方法一
for (var i = 0; i < 5; i++) {
var btn = document.createElement('button'); btn.appendChild(document.createTextNode('Button ' + i)); btn.addEventListener('click', (function(i) { return function() { console.log(i); }; })(i)); document.body.appendChild(btn);
}
方法二: 封裝所有調用到在新匿名函數中的 btn.addEventListener
for (var i = 0; i < 5; i++) {
var btn = document.createElement('button'); btn.appendChild(document.createTextNode('Button ' + i)); (function (i) { btn.addEventListener('click', function() { console.log(i); }); })(i); document.body.appendChild(btn);
}
方法三: 調用數組對象的本地 forEach 方法來替代 for 循環
['a', 'b', 'c', 'd', 'e'].forEach(function (value, i) {
var btn = document.createElement('button'); btn.appendChild(document.createTextNode('Button ' + i)); btn.addEventListener('click', function() { console.log(i); }); document.body.appendChild(btn);
});
var arr1 = "john".split(''); var arr2 = arr1.reverse(); var arr3 = "jones".split(''); arr2.push(arr3); console.log("array 1: length=" + arr1.length + " last=" + arr1.slice(-1)); console.log("array 2: length=" + arr2.length + " last=" + arr2.slice(-1));
輸出結果是:
"array 1: length=5 last=j,o,n,e,s""array 2: length=5 last=j,o,n,e,s"
調用數組對象的 reverse() 方法並不僅返回反順序的陣列,它也反轉了數組自己的順序(即,在這種狀況下,指的是 arr1)。
reverse() 方法返回一個到數組自己的引用(在這種狀況下即,arr1)。其結果爲,arr2 僅僅是一個到 arr1的引用(而不是副本)。所以,當對 arr2作了任何事情(即當咱們調用 arr2.push(arr3);)時,arr1 也會受到影響,由於 arr1 和 arr2 引用的是同一個對象。
注意:
傳遞數組到另外一個數組的 push() 方法會讓整個數組做爲單個元素映射到數組的末端。其結果是,語句 arr2.push(arr3); 在其總體中添加 arr3 做爲一個單一的元素到 arr2 的末端(也就是說,它並無鏈接兩個數組,鏈接數組是 concat() 方法的目的)。
和Python同樣,JavaScript標榜數組方法調用中的負數下標,例如 slice() 可做爲引用數組末尾元素的方法:例如,-1下標表示數組中的最後一個元素,等等。
console.log(1 + "2" + "2"); console.log(1 + +"2" + "2"); console.log(1 + -"1" + "2"); console.log(+"1" + "1" + "2"); console.log( "A" - "B" + "2"); console.log( "A" - "B" + 2);
上面的代碼將輸出如下內容到控制檯:
"122""32""02""112""NaN2"NaN
根本緣由是,JavaScript(ECMAScript)是一種弱類型語言,它可對值進行自動類型轉換,以適應正在執行的操做。讓咱們經過上面的例子來講明這是如何作到的。
例1:1 + "2" + "2" 輸出:"122" 說明: 1 + "2" 是執行的第一個操做。因爲其中一個運算對象("2")是字符串,JavaScript會假設它須要執行字符串鏈接,所以,會將 1 的類型轉換爲 "1", 1 + "2"結果就是 "12"。而後, "12" + "2" 就是 "122"。
例2: 1 + +"2" + "2" 輸出: "32" 說明:根據運算的順序,要執行的第一個運算是 +"2"(第一個 "2" 前面的額外 + 被視爲一元運算符)。所以,JavaScript將 "2" 的類型轉換爲數字,而後應用一元 + 號(即,將其視爲一個正數)。其結果是,接下來的運算就是 1 + 2 ,這固然是 3。而後咱們須要在一個數字和一個字符串之間進行運算(即, 3 和 "2"),一樣的,JavaScript會將數值類型轉換爲字符串,並執行字符串的鏈接,產生 "32"。
例3: 1 + -"1" + "2" 輸出: "02" 說明:這裏的解釋和前一個例子相同,除了此處的一元運算符是 - 而不是 +。先是 "1" 變爲 1,而後當應用 - 時又變爲了 -1 ,而後將其與 1相加,結果爲 0,再將其轉換爲字符串,鏈接最後的 "2" 運算對象,獲得 "02"。
例4: +"1" + "1" + "2" 輸出: "112" 說明:雖然第一個運算對象 "1"由於前綴的一元 + 運算符類型轉換爲數值,但又當即轉換回字符串,當鏈接到第二個運算對象 "1" 的時候,而後又和最後的運算對象"2" 鏈接,產生了字符串 "112"。
例5: "A" - "B" + "2" 輸出: "NaN2" 說明:因爲運算符 - 不能被應用於字符串,而且 "A" 和 "B" 都不能轉換成數值,所以,"A" - "B"的結果是 NaN,而後再和字符串 "2" 鏈接,獲得 "NaN2" 。
例6: "A" - "B" + 2 輸出: NaN 說明:參見前一個例子, "A" - "B" 結果爲 NaN。可是,應用任何運算符到NaN與其餘任何的數字運算對象,結果仍然是 NaN。
var list = readHugeList(); var nextListItem = function() { var item = list.pop(); if (item) { // process the list item... nextListItem(); } };
潛在的堆棧溢出能夠經過修改nextListItem 函數避免:
var list = readHugeList(); var nextListItem = function() { var item = list.pop(); if (item) { // process the list item... setTimeout( nextListItem, 0); } };
堆棧溢出之因此會被消除,是由於事件循環操縱了遞歸,而不是調用堆棧。當 nextListItem 運行時,若是 item不爲空,timeout函數(nextListItem)就會被推到事件隊列,該函數退出,所以就清空調用堆棧。當事件隊列運行其timeout事件,且進行到下一個 item 時,定時器被設置爲再次調用 extListItem。所以,該方法從頭至尾都沒有直接的遞歸調用,因此不管迭代次數的多少,調用堆棧保持清空的狀態。
閉包是一個能夠訪問外部(封閉)函數做用域鏈中的變量的內部函數。
閉包能夠訪問三種範圍中的變量:這三個範圍具體爲:
下面是一個簡單的例子:
var globalVar = "xyz"; (function outerFunc(outerArg) { var outerVar = 'a'; (function innerFunc(innerArg) { var innerVar = 'b'; console.log( "outerArg = " + outerArg + "\n" + "innerArg = " + innerArg + "\n" + "outerVar = " + outerVar + "\n" + "innerVar = " + innerVar + "\n" + "globalVar = " + globalVar); })(456); })(123);
在上面的例子中,來自於 innerFunc, outerFunc和全局命名空間的變量都在 innerFunc的範圍內。所以,上面的代碼將輸出以下:
outerArg = 123innerArg = 456outerVar = ainnerVar = bglobalVar = xyz
for (var i = 0; i < 5; i++) { setTimeout(function() { console.log(i); }, i * 1000 ); }
解釋你的答案。閉包在這裏能起什麼做用?
上面的代碼不會按預期顯示值0,1,2,3,和4,而是會顯示5,5,5,5,和5。
緣由是在循環中執行的每一個函數將整個循環完成以後被執行,所以,將會引用存儲在 i中的最後一個值,那就是5。
閉包能夠經過爲每次迭代建立一個惟一的範圍,存儲範圍內變量的每一個惟一的值,來防止這個問題,以下:
for (var i = 0; i < 5; i++) { (function(x) { setTimeout(function() { console.log(x); }, x * 1000 ); })(i); }
這就會按預期輸出0,1,2,3,和4到控制檯。
console.log("0 || 1 = "+(0 || 1)); console.log("1 || 2 = "+(1 || 2)); console.log("0 && 1 = "+(0 && 1)); console.log("1 && 2 = "+(1 && 2));
該代碼將輸出:
0 || 1 = 11 || 2 = 10 && 1 = 01 && 2 = 2
在JavaScript中, || 和 &&都是邏輯運算符,用於在從左至右計算時,返回第一個可徹底肯定的「邏輯值」。
或( || )運算符。在形如 X||Y的表達式中,首先計算X 並將其解釋執行爲一個布爾值。若是這個布爾值true,那麼返回true(1),再也不計算 Y,由於「或」的條件已經知足。若是這個布爾值爲false,那麼咱們仍然不能知道 X||Y是真是假,直到咱們計算 Y,而且也把它解釋執行爲一個布爾值。
所以, 0 || 1 的計算結果爲true(1),同理計算1 || 2。
與( &&)運算符。在形如 X&&Y的表達式中,首先計算 X並將其解釋執行爲一個布爾值。若是這個布爾值爲 false,那麼返回 false(0),再也不計算 Y,由於「與」的條件已經失敗。若是這個布爾值爲true,可是,咱們仍然不知道 X&&Y 是真是假,直到咱們去計算 Y,而且也把它解釋執行爲一個布爾值。
不過,關於 &&運算符有趣的地方在於,當一個表達式計算爲「true」的時候,那麼就返回表達式自己。這很好,雖然它在邏輯表達式方面計算爲「真」,但若是你但願的話也可用於返回該值。這就解釋了爲何,有些使人奇怪的是, 1 && 2返回 2(而不是你覺得的可能返回 true 或 1)。
console.log(false == '0') console.log(false === '0')
代碼將輸出:
true false
在JavaScript中,有兩種等式運算符。三個等於運算符 === 的做用相似傳統的等於運算符:若是兩側的表達式有着相同的類型和相同的值,那麼計算結果爲true。而雙等於運算符,會只強制比較它們的值。所以,整體上而言,使用 ===而不是 ==的作法更好。 !==vs !=亦是同理。
var a={}, b={key:'b'}, c={key:'c'}; a[b]=123; a[c]=456; console.log(a[b]);
這段代碼將輸出 456(而不是 123)
緣由爲:當設置對象屬性時,JavaScript會暗中字符串化參數值。在這種狀況下,因爲 b 和 c都是對象,所以它們都將被轉換爲"[object Object]"。結果就是, a[b]和a[c]均至關於a["[object Object]"] ,並能夠互換使用。所以,設置或引用 a[c]和設置或引用 a[b]徹底相同。
console.log((function f(n){return ((n > 1) ? n * f(n-1) : n)})(10));
代碼將輸出10!的值(即10!或3628800)。
緣由是:
命名函數 f()遞歸地調用自己,當調用 f(1)的時候,只簡單地返回1。下面就是它的調用過程:
f(1): returns n, which is 1f(2): returns 2 f(1), which is 2f(3): returns 3 f(2), which is 6f(4): returns 4 f(3), which is 24f(5): returns 5 f(4), which is 120f(6): returns 6 f(5), which is 720f(7): returns 7 f(6), which is 5040f(8): returns 8 f(7), which is 40320f(9): returns 9 f(8), which is 362880f(10): returns 10 * f(9), which is 3628800
(function(x) { return (function(y) { console.log(x); })(2) })(1);
控制檯將輸出 1,即便歷來沒有在函數內部設置過x的值。緣由是:
閉包是一個函數,連同在閉包建立的時候,其範圍內的全部變量或函數一塊兒。在JavaScript中,閉包是做爲一個「內部函數」實施的:即,另外一個函數主體內定義的函數。閉包的一個重要特徵是,內部函數仍然有權訪問外部函數的變量。
所以,在本例中,因爲 x未在函數內部中定義,所以在外部函數範圍中搜索定義的變量 x,且被發現具備1的值。
var hero = { _name: 'John Doe', getSecretIdentity: function (){ return this._name; } }; var stoleSecretIdentity = hero.getSecretIdentity; console.log(stoleSecretIdentity()); console.log(hero.getSecretIdentity());
代碼將輸出:
undefinedJohn Doe
第一個 console.log之因此輸出 undefined,是由於咱們正在從 hero對象提取方法,因此調用了全局上下文中(即窗口對象)的 stoleSecretIdentity(),而在此全局上下文中, _name屬性不存在。
其中一種修復stoleSecretIdentity() 函數的方法以下:
var stoleSecretIdentity = hero.getSecretIdentity.bind(hero);
此函數的參數爲:
DOM元素
回調函數(將DOM元素做爲其參數)
訪問樹(DOM)的全部元素是經典的深度優先搜索算法應用。下面是一個示範的解決方案:
function Traverse(p_element,p_callback) { p_callback(p_element); var list = p_element.children; for (var i = 0; i < list.length; i++) { Traverse(list[i],p_callback); // recursive call } }