const a = [1, 2, 3, 4, 5]; // Implement this a.multiply(); console.log(a); // [1, 2, 3, 4, 5, 1, 4, 9, 16, 25]
false
。 解釋一下爲何會這樣:// false 0.2 + 0.1 === 0.3
提示:只有兩種類型 – 主要數據類型和引用類型(對象)。 有 6 種主要類型。javascript
檢索並計算屬於同一教室中每一個學生的平均分數,其中一些ID爲75。每一個學生能夠在一年內參加一門或多門課程。 如下 API 可用於檢索所需數據。php
// GET LIST OF ALL THE STUDENTS GET /api/students Response: [{ "id": 1, "name": "John", "classroomId": 75 }] // GET COURSES FOR GIVEN A STUDENT GET /api/courses?filter=studentId eq 1 Response: [{ "id": "history", "studentId": 1 }, { "id": "algebra", "studentId": 1 },] // GET EVALUATION FOR EACH COURSE GET /api/evaluation/history?filter=studentId eq 1 Response: { "id": 200, "score": 50, "totalScore": 100 }
編寫一個接受教室 ID 的函數,您將根據該函數計算該教室中每一個學生的平均值。該函數的最終輸出應該是具備平均分數的學生列表:css
[ { "id": 1, "name": "John", "average": 70.5 }, { "id": 3, "name": "Lois", "average": 67 }, ]
使用普通的 callbacks ,promises ,observables,generator 或 async-wait 編寫所需的函數。嘗試使用至少 3 種不一樣的技術解決這個問題。html
提示:ES Proxy 容許您攔截對任何對象屬性或方法的調用。首先,每當更改底層綁定對象時,都應更新 DOM 。前端
您是否熟悉 Elixir,Clojure,Java 等其餘編程語言中使用的任何其餘併發模型?vue
提示:查找事件循環,任務隊列,調用棧,堆等。html5
new
關鍵字在 JavaScript 中有什麼做用?提示:在 JavaScript 中,new
是用於實例化對象的運算符。 這裏的目的是瞭解知識廣度和記憶狀況。java
另外,請注意 [[Construct]]
和 [[Call]]
。web
提示:有四種模式,函數調用,方法調用,.call()
和 .apply()
。ajax
提示:好比 2018 的 BigInt,partial 函數,pipeline 操做符 等。
這是一個神話嗎?它是否遭受了誤傳?是否有一些有用的用例?
若是咱們將如下對象轉換爲 JSON 字符串,會發生什麼?
const a = { key1: Symbol(), key2: 10 } // What will happen? console.log(JSON.stringify(a));
ES6對數組添加了一些新的方法,另外還添加了TypedArray類型,這種類型支持對內存的操做,ArrayBuffer
和C語言內存分配同樣,分配一塊內存塊。下面從如下幾個方面來看看ES6數組的變化:
Array.of()
, Array.from()
;find()
, findIndex()
, fill()
, copyWithin()
;ArrayBuffer
;1.Array.of()
ES6新添加了這個靜態方法,用於數組構造器實例化數組。咱們知道數組實例化通常能夠經過構造器或者數組字面量的形式來聲明。ES5經過構造器聲明數組會出現一個比較模糊的地方,好比:
var arr = new Array(1, 2, 3); // 表示聲明一個數組元素爲[1, 2, 3]的數組 arr.length; // 3 arr[0]; // 1 arr[1]; // 2 arr[2]; // 3 var arr = new Array(2); // 2表示長度 // 而這樣則表示一個數組長度爲2的數組,而數組元素未聲明 arr.length; // 2 arr; // [undefined, undefined]
而Array.of()
則消除了這種模糊,凡是向方法中添加數字,都表示數組元素,而不是長度
var arr = Array.of(1, 2); arr; // [1, 2] var arr = Array.of(2); // 2表示元素 arr; // [2]
2.Array.from()
ES6以前要將一個array-like
對象轉換成數組,咱們通常是利用slice
方法,好比
function doSomething() { // 將arguments類數組對象轉換成數組 var args = Array.prototype.slice.call(arguments); // 或者 [].slice.call(arguments) // ... }
ES6經過靜態方法 Array.from()
能夠將 類數組對象 或者 可迭代的對象 轉換成一個數組,其語法爲:
Array.from(arraylike[, callback] [, thisArg])
上面的例子能夠寫爲:
function doSomething() { var args = Array.from(arguments); // ... }
將可迭代的對象轉變爲數組:
var set = new Set([1, 2, 2, 4, 5]); // Set {1, 2, 4, 5} var arr = Array.from(set); // [1, 2, 4, 5]
後面添加回調函數, 若是回調函數屬於一個對象, 則能夠添加第3個可選參數,指出this
上下文:
let helper = { diff: 1, add(value) { return value + this.diff; } } function translate() { // 第2個參數爲callback, 第3個參數爲上下文 return Array.from(arguments, helper.add, helper); } translate(1, 2, 3); // [2, 3, 4]
1.find(),findIndex()
之前咱們查看數組中是否存在某個值或者某個值的位置時,通常使用indexOf()
, lastIndexOf()
,ES6添加了find(), findIndex()來添加條件查找。這兩個方法和map(),forEach()同樣添加一個回調函數,有選擇性的添加thisArg
指定上下文。
find找到了就返回第一個知足條件的,未找到返回undefined, findIndex返回索引位置,未找到返回 -1:
var arr = [1, 2, 19, 16]; arr.find(v => v > 10 ); // 返回 19 arr.findIndex(v => v > 10); // 返回 2
find(), findIndex()用於查找一個數組元素知足某個條件而不是值,要根據值查找建議使用indexOf(), lastIndexOf().
2.fill(), copyWithin()
這兩個方法其實爲了操做Typed Array對象使用的,可是爲了保持一致性,也添加給普通數組了。看下語法:
fill(value[,startFillPostion = 0 ] [, endFillPostion = arr.length])
copyWithin(StartToBeCopiedPos[,StartCopyPos = 0] [,EndCopyPos = arr.length])
先看fill:
var arr = [1, 2, 3, 4]; // 不指定開始和結束填充的位置 arr.fill(5); // arr: [5, 5, 5, 5] // 指定開始 arr.fill(5, 2); // arr: [1, 2, 5, 5] // 都指定,不包含結束位置 arr.fill(5, 0, 2)// arr: [5, 5, 3, 4] // 固然起始和結尾也能夠爲負數,至關於加上數組長度 arr.fill(5, -3); // arr: [1, 5, 5, 5] // 至關於 arr.fill(5, -3+4)
copyWith: 比較繞, 它是指複製自身內容到指定的位置:
var arr = [1, 10, 15, 29, 18]; // 只有一個參數表示被複制的索引,另外2個參數則默認從0開始到結束 arr.copyWithin(2); // arr [1, 10, 1, 10, 15] // 2個參數,指定自身複製的位置 arr.copyWithin(3, 1); // arr [1, 10, 15, 10, 15] // 3個參數都指定 arr.copyWithin(2, 0, 1); // arr [1, 10, 1, 29, 18] // 0-1只有一個數 "1", 全部索引位置爲2的 "15" 被替換成 "1"
上面例子咱們能夠發現,是有這些方法都會改變數組自身
AarryBuffer是指分配內存中的一塊位置用於操做,至關於C語言中的malloc(),對內存塊進行二進制操做,會極大的提高性能,知足一些特別的接口要求。
先了解一下內存分配的基本語法:
var buffer = new ArrayBuffer(bytes);
好比:分配10個字節(byte)
var buffer = new ArrayBuffer(10);
內存的大小肯定以後是不能修改的,能夠改變內部內容
屬性: byteLength
, slice()
slice方法是從已經存在的內存塊中複製一段,添加都新的內存塊中
var buffer = new ArrayBuffer(10); var buffer2 = buffer.slice(3, 5); // 將buffer中的3, 4字節內容拷貝到新的內存塊中 console.log(buffer2.byteLength); // 2
光有內存塊,而不進行操做也是沒有用的,javascript經過視圖的方式對內存塊進行讀寫,存在兩種視圖:
1.共同屬性
這兩種視圖擁有一些共同的屬性:
buffer
: 表示指向的內存塊;2.DataView
DataView構造器可以添加三個參數:new DataView(buffer[, byteOffset][, byteLength])
var buffer = new ArrayBuffer(10); // 指向整個內存塊 var dataView1 = new DataView(buffer); dataView1.buffer === buffer; // true dataView1.byteOffset; // 0 dataView1.byteLength; // 10 // 表示 字節 5, 6上的視圖 var dataView2 = new DataView(buffer, 5, 2); dataView2.buffer === buffer; // true dataView2.byteOffset; // 5 dataView2.byteLength; // 2
3.TypedArray
TypedArray本質上是一個抽象類,他表示9中特定類型: Int8Array
, Uint8Array
, Int16Array
, Uint16Array
, Int32Array
, Uint32Array
, Float32Array
,
Float64Array
,還有一種只針對Canvas顏色值的 Uint8ClampedArray
若是咱們在調用 makeAPIRequest
函數時必須使用 timeout
的默認值,那麼正確的語法是什麼?
function makeAPIRequest(url, timeout = 2000, headers) { // Some code to fetch data }
提示:截至 2018 年,沒有。
Angular 1.x 基於雙向數據綁定,而 React,Vue,Elm 等基於單向數據流架構。
單向數據綁定,帶來單向數據流。。
指的是咱們先把模板寫好,而後把模板和數據(數據可能來自後臺)整合到一塊兒造成HTML代碼,而後把這段HTML代碼插入到文檔流裏面。適用於總體項目,並於追溯。
雙向數據綁定,帶來雙向數據流。AngularJS2添加了單向數據綁定
數據模型(Module)和視圖(View)之間的雙向綁定。不管數據改變,或是用戶操做,都能帶來互相的變更,自動更新。適用於項目細節,如:UI控件中(一般是類表單操做)。
MVC 擁有大約 50 年的悠久歷史,並已演變爲 MVP,MVVM 和 MV *。二者之間的相互關係是什麼?若是 MVC 是架構模式,那麼單向數據流是什麼?這些競爭模式是否能解決一樣的問題?
提示:經典 MVC 是適用於桌面應用程序的 Smalltalk MVC。在 Web 應用中,至少有兩個不一樣的數據 MVC 週期。
提示:Currying(柯里化),point-free 函數,partial 函數應用,高階函數,純函數,獨立反作用,record 類型(聯合,代數數據類型)等。
提示:沒有正確答案。但粗略地說,函數式編程是關於小型編碼,編寫純函數和響應式編程是大型編碼,即模塊之間的數據流,鏈接以 FP 風格編寫的組件。 FRP – 功能響應式編程( Functional Reactive Programming)是另外一個不一樣但相關的概念。
不可變結構是否有任何性能影響? JS 生態系統中哪些庫提供了不可變的數據結構?這些庫的優勢和缺點是什麼?
提示:線程安全(咱們真的須要在瀏覽器 JavaScript 中擔憂嗎?),無反作用的函數,更好的狀態管理等。
提示:Structural 與 Nominal 類型系統,類型穩健性,工具/生態系統支持,正確性超過方便。
列出在實現不一樣模塊系統之間互操做所涉及的一些複雜性問題(主要對 ES 模塊和 CommonJS 互操做感興趣)
列出 HTTP/2 與其上一個版本的基本區別。
討論概念,含義,用途等。
提示:Observable 是惰性的,基於推送的複數值構造,同時具備 async/sync 調度程序。
提示:及早求值(eager evaluation),尷尬的取消機制,用 then()
方法假裝 map()
和 flatMap()
等。
具體談談,如下每種狀況下會發生什麼:
<audio>
或 <video>
。它會致使任何錯誤嗎?一、告訴瀏覽器使用什麼樣的html或xhtml規範來解析html文檔
二、對瀏覽器的渲染模式產生影響;不一樣的渲染模式會影響到瀏覽器對於 CSS 代碼甚至 JavaScript 腳本的解析,因此Doctype是很是關鍵的,尤爲是在 IE 系列瀏覽器中,由DOCTYPE 所決定的 HTML 頁面的渲染模式相當重要。
兩種渲染模式:
BackCompat:標準兼容模式未開啓(或叫怪異模式[Quirks mode]、混雜模式)
CSS1Compat:標準兼容模式已開啓(或叫嚴格模式[Standards mode/Strict mode])
選擇什麼樣的DOCTYPE
如上例所示,XHTML 1.0中有3種DTD(文檔類型定義)聲明能夠選擇:過渡的(Transitional)、嚴格的(Strict)和框架的(Frameset)。這裏分別介紹以下。
1.過渡的
一種要求不很嚴格的DTD,容許在頁面中使用HTML4.01的標識(符合xhtml語法標準)。過渡的DTD的寫法以下:
代碼以下:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2.嚴格的
一種要求嚴格的DTD,不容許使用任何表現層的標識和屬性,例如<br/>等。嚴格的DTD的寫法以下:
代碼以下:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3.框架的
一種專門針對框架頁面所使用的DTD,當頁面中含有框架元素時,就要採用這種DTD。框架的DTD的寫法以下:
代碼以下:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
使用嚴格的DTD來製做頁面,固然是最理想的方式。可是,對於沒有深刻了解Web標準的網頁設計者,比較合適的是使用過渡的DTD。由於這種DTD還容許使用表現層的標識、元素和屬性,比較適合大多數網頁製做人員。
HTML 4.01 中的 doctype 須要對 DTD 進行引用,由於 HTML 4.01 基於 SGML。
HTML 5 不基於 SGML,所以不須要對 DTD 進行引用,可是須要 doctype 來規範瀏覽器的行爲(html 5簡化了這種聲明,意在告訴瀏覽器使用統一的標準便可)
提示:BOM,DOM,ECMAScript 和 JavaScript 都是不一樣的東西。
BOM和瀏覽器關係密切,DOM和HTML文檔有關。BOM是Browser Object Mode的縮寫,及對瀏覽器對象模型,用來獲取或設置瀏覽器的屬性、行爲。DOM是Document Object Model 的縮寫,即文檔對象模型,用來獲取或設置文檔中標籤的屬性。BOM沒有相關標準。DOM是W3C的標準。BOM的最根本對象是window。DOM最根本對象是document(其實是window.document)。因爲DOM的操做對象是文檔(Document),因此dom和瀏覽器沒有直接關係。
HTML DOM 模型被構造爲對象的樹。
經過可編程的對象模型,JavaScript 得到了足夠的能力來建立動態的 HTML。
JavaScript 可以改變頁面中的全部 HTML 元素
JavaScript 可以改變頁面中的全部 HTML 屬性
JavaScript 可以改變頁面中的全部 CSS 樣式
JavaScript 可以對頁面中的全部事件作出反應
以下圖所示,咱們有三個 div 元素。每一個 div 都有一個與之關聯的點擊處理程序。處理程序執行如下任務:
hello outer
打印到控制檯。hello inner
打印到控制檯。編寫一段代碼來分配這些任務,以便在單擊 innermost div 時始終打印如下序列?
hello inner
→ hello innermost
→ hello outer
提示:事件捕獲和事件冒泡
咱們知道JavaScript與HTML之間的交互是經過事件實現的。事件最先是在IE3和Netscape Navigator 2中出現的,當時是做爲分擔服務器運算負載的一種手段。 通俗地理解,事件就是用戶或瀏覽器自身執行的某種操做。而事件處理程序即爲響應某個事件的函數。抽出主幹,即事件處理程序爲函數。 咱們又把事件處理程序稱爲事件偵聽器。 事件處理程序是以"on"開頭的,所以對於事件on的時間處理程序即爲onclick。時間處理程序在JavaScript中大體有五種,下面會根據這五種不一樣的時間處理程序分爲5部分來介紹。
什麼使HTML事件處理程序呢?顯然,經過名字就能夠猜到,它是卸載HTML中的函數(事件處理程序)。初學者大多用到的事件處理程序即爲HTML事件處理程序。下面舉例:
例1:
<button onclick="alert('success')">點我</button>
這條代碼即爲事件處理程序,點擊button後,會彈出彈框,顯示success。
特色:HTML事件處理程序中Javascript代碼做爲了onclick特性的值,所以,咱們不能在JavaScript代碼中使用未經轉義的HTML語法字符,如&(和號)、""(雙引號)、<(小於號)、>(大於號)等等。因此這個例子中字符串我使用了單引號而沒有使用雙引號。看下面在JavaScript代碼中使用了未經轉義的HTML語法字符。
例2:
<button onclick="alert("success")">點我</button>
這時,我在success外使用了HTML語法字符""(雙引號),這時不會彈出窗口,而是報錯語法錯誤。可是我若是仍是但願使用雙引號呢? 這時就要用"實體來代替HTML中的語法字符。以下例所示:
例3:
<button onclick="alert("success")">點我</button>
<!-- 正常彈出窗口-->
這個例子中咱們在JavaScript代碼中使用了HTML實體而沒有使用HTML語法字符,這時就不會報錯了。
例4:
1
2
3
4
5
6
7
8
|
< button onclick="show()">點我</ button >
<!-- 正常彈出窗口-->
< script >
function show(){
alert("success");
}
</ script >
|
這個例子中咱們調用函數,而把函數定義放在了script中,這樣也是能夠的。由於:事件處理程序中的代碼在執行時,有權訪問到全局做用域中的任何代碼。這句話怎麼理解呢? 實際上,咱們能夠在chrome中觀察button標籤的做用域鏈。以下所示:
接下來咱們再看看script所在的做用域,以下圖所示:
能夠看到script標籤就在全局做用域。
也就是說目前button中的HTML事件處理函數在做用域鏈的最前端,而Script在全局做用域,因此「事件處理程序中的代碼在執行時,有權訪問到全局做用域中的任何代碼。」這句話就不難理解了。
例5:
<button onclick="alert(event.type)">點我</button>
這時瀏覽器彈出窗口顯示:click。這個例子是什麼意思呢?注意到我並無在event.type外加單引號,說明這並非字符串。實際上,event是局部對象--在觸發DOM上的某個事件時,會產生一個事件對象event,這個對象包含着全部與事件有關的信息。而這裏是彈出了對象了類型,即爲click。
HTML事件處理程序的三個缺點(重點):
1. 時差問題。 由於用戶可能在HTML元素一出現就開始觸發相應事件,可是有可能該事件的腳本(如例4中show()函數的函數定義在script中)尚未加載完成,此時不具有執行條件,故報錯。
解決方法:將HTML事件處理程序封裝在一個try-catch塊中,以便錯誤不會浮出水面。
<input type="button" value="click me" onclick="try{show();}catch(ex){}">
2.這樣擴展事件實例程序的做用域鏈在不一樣的瀏覽器中會致使不一樣的結果(例4中我是在chrome中查看的做用域鏈,其餘瀏覽器不必定是這樣的,請注意)。不一樣JavaScript引擎遵循的標識符解析規則略有差別,頗有可能會在訪問非限定對象成員時出錯。
3.HTML和JavaScript代碼緊密耦合。 結果是:若是要更換事件處理程序,就必須改動兩個地方--HTML代碼和JavaScript代碼。
那麼怎麼解決上面的問題呢? DOM0級事件處理程序是一個不錯的選擇!
DOM0級事件處理程序用的也很是廣泛。之因此成爲DOM0級,我認爲是當時尚未出DOM標準,而IE和Netscape Navigator二者使用的時間處理程序(不知是否合理,望批評指正)。 總之,咱們先看看下面的例子吧。
例6:
<button id="button">點我</button>
<script>
var button=document.getElementById("button");
button.onclick=function(){
alert("clicked");
}
</script>
即咱們先在script中取得元素的引用,而後再將一個函數賦值給onclick事件處理程序。 以前介紹過,事件處理程序即爲函數,而button.onclick這種形式即函數做爲了對象的方法。那麼對象的方法即事件處理程序是在元素(對象)的做用域中運行而非在全局做用域中運行的,由於方法是屬於對象的。(注意:例4中事件處理程序是在全局做用域中運行的)。 若是這個函數中存在this關鍵字,那麼this就會指向這個對象。下面咱們在瀏覽器中證實事件處理程序是在元素的做用域中運行。
咱們看到alert("clicked");確實是在button中運行的。
咱們還能夠經過下面的方式刪除經過DOM0級方法指定的事件處理程序。
button.onclick=null;
經過上面的分析咱們能夠知道DOM0級事件處理程序是很是不錯的,它解決了HTML事件處理程序的三個缺點:時差問題、做用域鏈致使的不一樣瀏覽器表現不一致問題和HTML和JavaScript緊密耦合問題。
可是,DOM0級事件處理程序並非完美的,它一樣有兩個缺點:
對於第二個問題後面會講到,第一個問題舉例以下:
<button id="button">點我</button>
<script>
var button=document.getElementById("button");
button.onclick=function(){
alert("clicked");
}
button.onclick=function(){
alert("again");
}
雖然我對同一個元素設置了兩個事件處理程序,可是最終的結果是:只有第二個事件有效(覆蓋了第一個事件)。固然,人類是聰明的動物,DOM2級事件很好的解決了這個問題!
DOM2級事件處理程序定義了兩個方法:
在博文的開頭我就提到了事件處理程序即事件偵聽器。這兩個方法都接收三個參數:
下面經過兩個例子加深理解:
例7:
<button id="button">點我</button>
<script>
var button=document.getElementById("button");
button.addEventListener("click",function(){
alert(this.id);
},false);
button.addEventListener("click",function(){
alert("another event");
},false);
</script>
結果:第一次彈出窗口:button。
第二次彈出窗口:another event。
結論:經過DOM2級事件處理程序,咱們能夠爲同一個元素添加兩個或更多的事件。事件根據順序依次觸發。且this一樣指向當前元素,故函數在元素的做用域中執行。
this分析:和前面的DOM0級事件處理程序同樣,這裏的addEventListener一樣也能夠看做對象的方法,不一樣之初在於,DOM0級的方法須要另一個函數來賦值,而這裏的方法是DOM2級規範預約義的。
removeEventListener()這個刪除事件處理程序的方法值得注意的是:使用addEventListener()來添加的事件處理程序只能經過它來移除,且須要傳入相同的參數。
例8:
<button id="button">點我</button>
<script>
var button=document.getElementById("button");
button.addEventListener("click",function(){
alert(this.id);
},false);
button.removeEventListener("click",function(){
alert("another event");
},false);
上述代碼貌似能夠移除click的事件處理程序,可是經過實驗證實是不能夠的,緣由是:事件處理程序爲匿名函數時沒法移除。看下面的成功移除的例子:
例9:
<button id="button">點我</button>
<script>
var button=document.getElementById("button");
function handler(){
alert(this.id);
}
button.addEventListener("click",handler,false);
button.removeEventListener("click",handler,false);
</script>
成功移除!
注意:1.傳入方法的handler沒有(),是由於這裏都只是定義函數,而不是調用,須要注意。
2.這兩個方法的第三個參數都是false,即事件處理程序添加到冒泡階段。通常不使用true,由於低版本的IE不支持捕獲階段。
DOM2級事件處理程序成功地解決了前面全部事件處理程序的問題,堪稱perfect!!!! 然而老是特立獨行的IE瀏覽器又有新花樣,它也有本身的一套事件處理程序,下面咱們就來看看吧。
IE事件處理程序中有相似與DOM2級事件處理程序的兩個方法:
它們都接收兩個參數:
之因此沒有和DOM2級事件處理程序中相似的第三個參數,是由於IE8及更早版本只支持冒泡事件流。
注意:
1.IE事件處理程序中attachEvent()的事件處理程序的做用域和DOM0與DOM2不一樣,她的做用域是在全局做用域中。所以,不一樣於DOM0和DOM2中this指向元素,IE中的this指向window。
2.一樣,咱們能夠使用attachEvent()來給同一個元素添加多個事件處理程序。可是與DOM2不一樣,事件觸發的順序不是添加的順序而是添加順序的相反順序。
3.一樣地,經過attachEvent()添加的事件處理程序必須經過detachEvent()方法移除,一樣的,不能使用匿名函數。
4.支持IE事件處理程序的瀏覽器不僅有IE瀏覽器,還有Opera瀏覽器。
實際上,這一部分視爲了跨瀏覽器使用,將前面的幾部分結合起來就能夠了。
這一部分須要建立兩個方法:
這兩個方法接收相同的三個參數:
這兩個方法的構造狀況以下:
var EventUtil={
addHandler:function(element,type,handler){
if(element.addEventListener){
element.addEventListener(type,handler,false);//注意:這裏默認使用了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);//注意:這裏默認使用了false(冒泡)
}else if(element.detachEvent){
element.detachEvent("on"+type,handler);
}else{
element["on"+type]=null;
}
}
};
即先判斷DOM2級事件處理程序,再判斷IE事件處理程序,最後使用DOM0級事件處理程序。
例10:經過這個例子來使用上面構造的方法。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>跨瀏覽器事件處理程序</title>
</head>
<body>
<button id="button">點我</button>
<script>
var EventUtil={
addHandler:function(element,type,handler){
if(element.addEventListener){
element.addEventListener(type,handler,false);//注意:這裏默認使用了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);//注意:這裏默認使用了false(冒泡)
}else if(element.detachEvent){
element.detachEvent("on"+type,handler);
}else{
element["on"+type]=null;
}
}
};
function handler(){
alert("clicked");
}
var button=document.getElementById("button");
EventUtil.addHandler(button,"click",handler);
</script>
</body>
</html>
最後瀏覽器成功彈出「clicked」。
提示:XMLHttpRequest2(streaming),fetch(non-streaming),File API
1、XMLHttpRequest2(streaming)
XMLHttpRequest是一個瀏覽器接口,使得Javascript能夠進行HTTP(S)通訊。
最先,微軟在IE 5引進了這個接口。由於它太有用,其餘瀏覽器也模仿部署了,ajax操做所以得以誕生。
可是,這個接口一直沒有標準化,每家瀏覽器的實現或多或少有點不一樣。HTML 5的概念造成後,W3C開始考慮標準化這個接口。2008年2月,就提出了XMLHttpRequest Level 2 草案。
這個XMLHttpRequest的新版本,提出了不少有用的新功能,將大大推進互聯網革新。本文就對這個新版本進行詳細介紹。
1、老版本的XMLHttpRequest對象
在介紹新版本以前,咱們先回顧一下老版本的用法。
首先,新建一個XMLHttpRequest的實例。
var xhr = new XMLHttpRequest();
而後,向遠程主機發出一個HTTP請求。
xhr.open('GET', 'example.php');
xhr.send();
接着,就等待遠程主機作出迴應。這時須要監控XMLHttpRequest對象的狀態變化,指定回調函數。
xhr.onreadystatechange = function(){
if ( xhr.readyState == 4 && xhr.status == 200 ) {
alert( xhr.responseText );
} else {
alert( xhr.statusText );
}
};
上面的代碼包含了老版本XMLHttpRequest對象的主要屬性:
* xhr.readyState:XMLHttpRequest對象的狀態,等於4表示數據已經接收完畢。
* xhr.status:服務器返回的狀態碼,等於200表示一切正常。
* xhr.responseText:服務器返回的文本數據
* xhr.responseXML:服務器返回的XML格式的數據
* xhr.statusText:服務器返回的狀態文本。
2、老版本的缺點
老版本的XMLHttpRequest對象有如下幾個缺點:
* 只支持文本數據的傳送,沒法用來讀取和上傳二進制文件。
* 傳送和接收數據時,沒有進度信息,只能提示有沒有完成。
* 受到"同域限制"(Same Origin Policy),只能向同一域名的服務器請求數據。
3、新版本的功能
新版本的XMLHttpRequest對象,針對老版本的缺點,作出了大幅改進。
* 能夠設置HTTP請求的時限。
* 能夠使用FormData對象管理表單數據。
* 能夠上傳文件。
* 能夠請求不一樣域名下的數據(跨域請求)。
* 能夠獲取服務器端的二進制數據。
* 能夠得到數據傳輸的進度信息。
下面,我就一一介紹這些新功能。
4、HTTP請求的時限
有時,ajax操做很耗時,並且沒法預知要花多少時間。若是網速很慢,用戶可能要等好久。
新版本的XMLHttpRequest對象,增長了timeout屬性,能夠設置HTTP請求的時限。
xhr.timeout = 3000;
上面的語句,將最長等待時間設爲3000毫秒。過了這個時限,就自動中止HTTP請求。與之配套的還有一個timeout事件,用來指定回調函數。
xhr.ontimeout = function(event){
alert('請求超時!');
}
目前,Opera、Firefox和IE 10支持該屬性,IE 8和IE 9的這個屬性屬於XDomainRequest對象,而Chrome和Safari還不支持。
5、FormData對象
ajax操做每每用來傳遞表單數據。爲了方便表單處理,HTML 5新增了一個FormData對象,能夠模擬表單。
首先,新建一個FormData對象。
var formData = new FormData();
而後,爲它添加表單項。
formData.append('username', '張三');
formData.append('id', 123456);
最後,直接傳送這個FormData對象。這與提交網頁表單的效果,徹底同樣。
xhr.send(formData);
FormData對象也能夠用來獲取網頁表單的值。
var form = document.getElementById('myform');
var formData = new FormData(form);
formData.append('secret', '123456'); // 添加一個表單項
xhr.open('POST', form.action);
xhr.send(formData);
6、上傳文件
新版XMLHttpRequest對象,不只能夠發送文本信息,還能夠上傳文件。
假定files是一個"選擇文件"的表單元素(input[type="file"]),咱們將它裝入FormData對象。
var formData = new FormData();
for (var i = 0; i < files.length;i++) {
formData.append('files[]', files[i]);
}
而後,發送這個FormData對象。
xhr.send(formData);
7、跨域資源共享(CORS)
新版本的XMLHttpRequest對象,能夠向不一樣域名的服務器發出HTTP請求。這叫作"跨域資源共享"(Cross-origin resource sharing,簡稱CORS)。
使用"跨域資源共享"的前提,是瀏覽器必須支持這個功能,並且服務器端必須贊成這種"跨域"。若是可以知足上面的條件,則代碼的寫法與不跨域的請求徹底同樣。
xhr.open('GET', 'http://other.server/and/path/to/script');
目前,除了IE 8和IE 9,主流瀏覽器都支持CORS,IE 10也將支持這個功能。服務器端的設置,請參考《Server-Side Access Control》。
8、接收二進制數據(方法A:改寫MIMEType)
老版本的XMLHttpRequest對象,只能從服務器取回文本數據(不然它的名字就不用XML起首了),新版則能夠取回二進制數據。
這裏又分紅兩種作法。較老的作法是改寫數據的MIMEType,將服務器返回的二進制數據假裝成文本數據,而且告訴瀏覽器這是用戶自定義的字符集。
xhr.overrideMimeType("text/plain; charset=x-user-defined");
而後,用responseText屬性接收服務器返回的二進制數據。
var binStr = xhr.responseText;
因爲這時,瀏覽器把它當作文本數據,因此還必須再一個個字節地還原成二進制數據。
for (var i = 0, len = binStr.length; i < len; ++i) {
var c = binStr.charCodeAt(i);
var byte = c & 0xff;
}
最後一行的位運算"c & 0xff",表示在每一個字符的兩個字節之中,只保留後一個字節,將前一個字節扔掉。緣由是瀏覽器解讀字符的時候,會把字符自動解讀成Unicode的0xF700-0xF7ff區段。
8、接收二進制數據(方法B:responseType屬性)
從服務器取回二進制數據,較新的方法是使用新增的responseType屬性。若是服務器返回文本數據,這個屬性的值是"TEXT",這是默認值。較新的瀏覽器還支持其餘值,也就是說,能夠接收其餘格式的數據。
你能夠把responseType設爲blob,表示服務器傳回的是二進制對象。
var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png');
xhr.responseType = 'blob';
接收數據的時候,用瀏覽器自帶的Blob對象便可。
var blob = new Blob([xhr.response], {type: 'image/png'});
注意,是讀取xhr.response,而不是xhr.responseText。
你還能夠將responseType設爲arraybuffer,把二進制數據裝在一個數組裏。
var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png');
xhr.responseType = "arraybuffer";
接收數據的時候,須要遍歷這個數組。
var arrayBuffer = xhr.response;
if (arrayBuffer) {
var byteArray = new Uint8Array(arrayBuffer);
for (var i = 0; i < byteArray.byteLength; i++) {
// do something
}
}
更詳細的討論,請看Sending and Receiving Binary Data。
9、進度信息
新版本的XMLHttpRequest對象,傳送數據的時候,有一個progress事件,用來返回進度信息。
它分紅上傳和下載兩種狀況。下載的progress事件屬於XMLHttpRequest對象,上傳的progress事件屬於XMLHttpRequest.upload對象。
咱們先定義progress事件的回調函數。
xhr.onprogress = updateProgress;
xhr.upload.onprogress = updateProgress;
而後,在回調函數裏面,使用這個事件的一些屬性。
function updateProgress(event) {
if (event.lengthComputable) {
var percentComplete = event.loaded / event.total;
}
}
上面的代碼中,event.total是須要傳輸的總字節,event.loaded是已經傳輸的字節。若是event.lengthComputable不爲真,則event.total等於0。
與progress事件相關的,還有其餘五個事件,能夠分別指定回調函數:
* load事件:傳輸成功完成。
* abort事件:傳輸被用戶取消。
* error事件:傳輸中出現錯誤。
* loadstart事件:傳輸開始。
* loadEnd事件:傳輸結束,可是不知道成功仍是失敗。
2、fetch(non-streaming)
與XMLHttpRequest(XHR)相似,fetch()方法容許你發出AJAX請求。區別在於Fetch API使用Promise,所以是一種簡潔明瞭的API,比XMLHttpRequest更加簡單易用。
從Chrome 40開始,Fetch API能夠被利用在Service Worker全局做用範圍中,自Chrome 42開始,能夠被利用在頁面中。
若是你還不瞭解Promise,須要首先補充這方面知識。
讓咱們首先來比較一個XMLHttpRequest使用示例與fetch方法的使用示例。該示例向服務器端發出請求,獲得響應並使用JSON將其解析。
一個XMLHttpRequest須要設置兩個事件回調函數,一個用於獲取數據成功時調用,另外一個用於獲取數據失敗時調用,以及一個open()方法調用及一個send()方法調用。
function reqListener(){ var data=JSON.parse(this.responseText); console.log(data); } function reqError(err){ console.log("Fetch錯誤:"+err); } var oReq=new XMLHttpRequest(); oReq.οnlοad=reqListener; oReq.οnerrοr=reqError; oReq.open("get","/students.json",true); oReq.send();
一個fetch()方法的使用代碼示例以下所示:
fetch("/students.json") .then( function(response){ if(response.status!==200){ console.log("存在一個問題,狀態碼爲:"+response.status); return; } //檢查響應文本 response.json().then(function(data){ console.log(data); }); } ) .catch(function(err){ console.log("Fetch錯誤:"+err); });
在上面這個示例中,咱們在使用JSON解析響應前首先檢查響應狀態碼是否爲200。
一個fetch()請求的響應爲一個Stream對象,這表示當咱們調用json()方法,將返回一個Promise對象,由於流的讀取將爲一個異步過程。
在上一個示例中咱們檢查了Response對象的狀態碼,同時展現瞭如何使用JSON解析響應數據。咱們可能想要訪問響應頭等元數據,代碼以下所示:
fetch("/students.json") .then( function(response){ console.log(response.headers.get('Content-Type')); console.log(response.headers.get('Date')); console.log(response.status); console.log(response.statusText); console.log(response.type); console.log(response.url); } )
當咱們發出一個fetch請求時,響應類型將會爲如下幾種之一:「basic」、「cors」或「opaque」。這些類型標識資源來源,提示你應該怎樣對待響應流。
當請求的資源在相同域中時,響應類型爲「basic」,不嚴格限定你如何處理這些資源。
若是請求的資源在其餘域中,將返回一個CORS響應頭。響應類型爲「cors」。「cors」響應限定了你只能在響應頭中看見「Cache-Control」、「Content-Language」、「Content-Type」、「Expires」、「Last-Modified」以及「Progma」。
一個「opaque」響應針對的是訪問的資源位於不一樣域中,但沒有返回CORS響應頭的場合。若是響應類型爲「opaque」,咱們將不能查看數據,也不能查看響應狀態,也就是說咱們不能檢查請求成功與否。目前爲止不能在頁面腳本中請求其餘域中的資源。
你能夠爲fetch請求定義一個模式以確保請求有效。能夠定義的模式以下所示:
爲了定義模式,在fetch方法的第二個參數中添加選項對象並在該對象中定義模式:
fetch("http://www.html5online.com.cn/cors-enabled/students.json",{mode:"cors"}) .then( function(response){ console.log(response.headers.get('Content-Type')); console.log(response.headers.get('Date')); console.log(response.status); console.log(response.statusText); console.log(response.type); console.log(response.url); } ) .catch(function(err){ console.log("Fetch錯誤:"+err); });
Promise API的一個重大特性是能夠連接方法。對於fetch來講,這容許你共享fetch請求邏輯。
若是使用JSON API,你須要檢查狀態而且使用JSON對每一個響應進行解析。你能夠經過在不一樣的返回Promise對象的函數中定義狀態及使用JSON進行解析來簡化代碼,你將只須要關注於處理數據及錯誤:
function status(response){ if(response.status>=200 && response.status<300){ return Promise.resolve(response); } else{ return Promise.reject(new Error(response.statusText)); } } function json(response){ return response.json(); } fetch("/students.json") .then(status) .then(json) .then(function(data){ console.log("請求成功,JSON解析後的響應數據爲:",data); }) .catch(function(err){ console.log("Fetch錯誤:"+err); });
在上述代碼中,咱們定義了status函數,該函數檢查響應的狀態碼並返回Promise.resolve()方法或Promise.reject()方法的返回結果(分別爲具備確定結果的Promise及具備否認結果的Promise)。這是fetch()方法鏈中的第一個方法。若是返回確定結果,咱們調用json()函數,該函數返回來自於response.json()方法的Promise對象。在此以後咱們獲得了一個被解析過的JSON對象,若是解析失敗Promise將返回否認結果,致使catch段代碼被執行。
這樣書寫的好處在於你能夠共享fetch請求的邏輯,代碼容易閱讀、維護及測試。
在Web應用程序中常常須要使用POST方法提交頁面中的一些數據。
爲了執行POST提交,咱們能夠將method屬性值設置爲post,而且在body屬性值中設置須要提交的數據。
fetch(url,{ method:"post", headers:{ "Content-type":"application:/x-www-form-urlencoded:charset=UTF-8" }, body:"name=lulingniu&age=40" }) .then(status) .then(json) .then(function(data){ console.log("請求成功,JSON解析後的響應數據爲:",data); }) .catch(function(err){ console.log("Fetch錯誤:"+err); });
你可能想要使用Fetch發送帶有諸如cookie之類的憑證的請求。你能夠在選項對象中將credentials屬性值設置爲「include」:
fetch(url,{ credentials:"include" })
3、File API
file對象是對文件對象的一種表現
表明input上傳時的文件獨享對象
IE9中沒有這個對象,因此沒法操做文件
哪些 CSS 屬性會致使重排及重繪?
重繪是一個元素的外觀變化所引起的瀏覽器行爲;
重排是引發DOM樹從新計算的行爲;
一、迴流/重排
渲染樹的一部分必需要更新且節點的尺寸發生了變化,會觸發重排操做。每一個頁面至少在初始化的時候會有一次重排操做。
二、重繪
部分節點須要更新,但沒有改變其形狀,會觸發重繪操做。
會觸發重繪或迴流/重排的操做
一、添加、刪除元素(迴流+重繪)
二、隱藏元素,display:none(迴流+重繪),visibility:hidden(只重繪,不迴流)
三、移動元素,如改變top、left或移動元素到另外1個父元素中(重繪+迴流)
四、改變瀏覽器大小(迴流+重繪)
五、改變瀏覽器的字體大小(迴流+重繪)
六、改變元素的padding、border、margin(迴流+重繪)
七、改變瀏覽器的字體顏色(只重繪,不迴流)
八、改變元素的背景顏色(只重繪,不迴流)
說說計算 CSS 選擇器權重的算法。
4個等級的定義以下: 第一等:表明內聯樣式,如: style=」」,權值爲1000。 第二等:表明ID選擇器,如:#content,權值爲100。 第三等:表明類,僞類和屬性選擇器,如.content,權值爲10。 第四等:表明類型選擇器和僞元素選擇器,如div p,權值爲1。 例如上圖爲例,其中#NAV爲二等選擇器,.ACTIVE爲三等選擇器,UL、LI和A爲四等選擇器。則整個選擇器表達式的特殊性的值爲1*100+1*10+3*1=113
注意:通用選擇器(*),子選擇器(>)和相鄰同胞選擇器(+)並不在這四個等級中,因此他們的權值都爲0。
提示:像素不是像素不是像素 – ppk。
提示:它也被稱爲 HTML5 大綱算法。特別是在構建具備語義結構的網站時很是重要。
在html5中有一個很重要的概念,叫作html5大綱算法(HTML5 Outliner),它的用途爲用戶提供一份頁面的信息結構目錄。合理的使用HTML5元素標籤,能夠生成一個很是清晰的文檔大綱。
HTML5大綱算法
咱們能夠經過各類工具去查看當前頁面,這裏推薦使用一個測試工具:HTML5 Outliner,網址以下:https://gsnedders.html5.org/outliner/
1. 瞭解一個 section 和 div 的區別
①div元素在html5以前是最經常使用的最流行的標籤,但他自己是沒有任何語義的,它只不過是用來佈局頁面和css樣式以及js樣式。
②在html5中 section 標籤並非用來取代 div 的。他是具備語義的文檔標籤,在大綱規範中規定 session 至少要包含一個標題。也就是 section 標籤內至少包含一個h1~h6。
③若是是頁面佈局,且不是 header、footer之類的專屬區域都應該使用div。
2. body\nav\section 都是須要有標題才規範,header和div則是不須要標題的。
3. section 和 nav 元素大綱要求有標題h1~h6,暗示 section 必須有才規範,而 nav 若是沒有標題,也是合理的。給他添加了標題會讓大綱更好看,因此咱們能夠添加完了再隱藏,就不會破壞佈局了。(經過display:none;將其隱藏)
float: left | right;
)如何在 CSS Grid 和 flexbox 中渲染?提示:等高的列,垂直居中,複雜網格等。
示例:使用魔性數字,如 width: 67px;
或使用 em
代替 rem
單位,在通用代碼以前編寫 media queries(媒體查詢),濫用 ID 和類等。
有三種狀況,1爲觸摸鼠標均可以使用 2爲觸摸 3爲鼠標
if((document.hasOwnProperty("ontouchstart")) && (document.hasOwnProperty("onmousedown"))){ }else if(document.body.ontouchstart !== undefined){ }else{ } // document.body.ontouchstart !== undefined也是特性檢測的一種和那位兄弟的代碼差很少
mouse 事件是全部瀏覽器都支持的,一款普通的觸屏手機也能夠經過 USB OTG 外接鼠標。
因此你只能判斷瀏覽器是否支持觸屏,這裏的關鍵就是
【ontouchstart,ontouchmove,ontouchend,ontouchcancel】
支持觸屏的瀏覽器都會有這四個 touch 事件
window.onload = function() {
if(document.hasOwnProperty("ontouchstart")) {
alert("瀏覽器支持觸屏");
}
else {
alert("瀏覽器不支持觸屏");
}
};
在觸摸設備上,touch事件從手指剛碰到屏幕就會觸發,而click事件則要晚一下子才被觸發。觸發順序以下:
touchstart
mouseover
mousemove(一次)
mousedown
mouseup
click
touchend
因此,若是想提高web在觸摸設備下的用戶體驗,讓用戶以爲響應很是迅速,應該對頁面的觸摸事件進行事件處理程序的註冊,而不該再關注click事件。
touchstart
touchmove
events, depending on movement of the finger(s)touchend
mousemove
mousedown
mouseup
click
async
和 defer
屬性有什麼用?向html頁面中插入javascript代碼的主要方法就是經過script標籤。其中包括兩種形式,第一種直接在script標籤之間插入js代碼,第二種便是經過src屬性引入外部js文件。因爲解釋器在解析執行js代碼期間會阻塞頁面其他部分的渲染,對於存在大量js代碼的頁面來講會致使瀏覽器出現長時間的空白和延遲,爲了不這個問題,建議把所有的js引用放在</body>標籤以前。
script標籤存在兩個屬性,defer和async,所以script標籤的使用分爲三種狀況:
1.<script src="example.js"></script>
沒有defer或async屬性,瀏覽器會當即加載並執行相應的腳本。也就是說在渲染script標籤以後的文檔以前,不等待後續加載的文檔元素,讀到就開始加載和執行,此舉會阻塞後續文檔的加載;
2.<script async src="example.js"></script>
有了async屬性,表示後續文檔的加載和渲染與js腳本的加載和執行是並行進行的,即異步執行;
3.<script defer src="example.js"></script>
有了defer屬性,加載後續文檔的過程和js腳本的加載(此時僅加載不執行)是並行進行的(異步),js腳本的執行須要等到文檔全部元素解析完成以後,DOMContentLoaded事件觸發執行以前。
下圖能夠直觀的看出三者之間的區別:
其中藍色表明js腳本網絡加載時間,紅色表明js腳本執行時間,綠色表明html解析。
從圖中咱們能夠明確一下幾點:
1.defer和async在網絡加載過程是一致的,都是異步執行的;
2.二者的區別在於腳本加載完成以後什麼時候執行,能夠看出defer更符合大多數場景對應用腳本加載和執行的要求;
3.若是存在多個有defer屬性的腳本,那麼它們是按照加載順序執行腳本的;而對於async,它的加載和執行是牢牢挨着的,不管聲明順序如何,只要加載完成就馬上執行,它對於應用腳本用處不大,由於它徹底不考慮依賴。