2018 – 2019 年前端 JavaScript 面試題

JavaScript 基礎問題

1.使如下代碼正常運行:

JavaScript 代碼:
    const a = [1, 2, 3, 4, 5];
     
    // Implement this
    a.multiply();
     
    console.log(a); // [1, 2, 3, 4, 5, 1, 4, 9, 16, 25]

2.如下代碼在 JavaScript 中返回 false 。 解釋一下爲何會這樣:

JavaScript 代碼:
    // false
    0.2 + 0.1 === 0.3

3.JavaScript 中有哪些不一樣的數據類型?

提示:只有兩種類型 – 主要數據類型和引用類型(對象)。 有 6 種主要類型。javascript

4.解決如下異步代碼問題。

檢索並計算屬於同一教室中每一個學生的平均分數,其中一些ID爲75。每一個學生能夠在一年內參加一門或多門課程。 如下 API 可用於檢索所需數據。php

JavaScript 代碼:
    // 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

JavaScript 代碼:
    [
    { "id": 1, "name": "John", "average": 70.5 },
    { "id": 3, "name": "Lois", "average": 67 },
    ]

使用普通的 callbacks ,promises ,observablesgenerator 或 async-wait 編寫所需的函數。嘗試使用至少 3 種不一樣的技術解決這個問題。html

5.使用 JavaScript Proxy 實現簡單的數據綁定

提示:ES Proxy 容許您攔截對任何對象屬性或方法的調用。首先,每當更改底層綁定對象時,都應更新 DOM 。前端

6.解釋 JavaScript 併發模型

您是否熟悉 Elixir,Clojure,Java 等其餘編程語言中使用的任何其餘併發模型?vue

提示:查找事件循環,任務隊列,調用棧,堆等。html5

7.new 關鍵字在 JavaScript 中有什麼做用?

提示:在 JavaScript 中,new 是用於實例化對象的運算符。 這裏的目的是瞭解知識廣度和記憶狀況。java

另外,請注意 [[Construct]] 和 [[Call]]web

8.JavaScript 中有哪些不一樣的函數調用模式? 詳細解釋。

提示:有四種模式,函數調用,方法調用,.call() 和 .apply()ajax

9.解釋任一即將發佈新的 ECMAScript 提案。

提示:好比 2018 的 BigInt,partial 函數,pipeline 操做符 等。

10.JavaScript 中的迭代器(iterators)和迭代(iterables)是什麼? 你知道什麼是內置迭代器嗎?

11.爲何 JavaScript classes(類)被認爲是壞的或反模式?

這是一個神話嗎?它是否遭受了誤傳?是否有一些有用的用例?

12.如何在 JSON 中序列化如下對象?

若是咱們將如下對象轉換爲 JSON 字符串,會發生什麼?

JavaScript 代碼:
    const a = {
    key1: Symbol(),
    key2: 10
    }
    // What will happen?
    console.log(JSON.stringify(a));

13.你熟悉 Typed Arrays 嗎? 若是熟悉,請解釋他們與 JavaScript 中的傳統數組相比的異同?

Arrays

ES6對數組添加了一些新的方法,另外還添加了TypedArray類型,這種類型支持對內存的操做,ArrayBuffer和C語言內存分配同樣,分配一塊內存塊。下面從如下幾個方面來看看ES6數組的變化:

  1. 2個靜態方法 Array.of(), Array.from();
  2. 數組原型上新添加的方法 find(), findIndex(), fill(), copyWithin();
  3. 新類型 ArrayBuffer;
  4. Typed Arrays, 以及和Array的相同性,差別性

一.Array.of() & Array.from()

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"

上面例子咱們能夠發現,是有這些方法都會改變數組自身


三.ArrayBuffer

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

四.TypedArray, Views視圖

光有內存塊,而不進行操做也是沒有用的,javascript經過視圖的方式對內存塊進行讀寫,存在兩種視圖:

  1. TypedArray: 特定類型的數據類型,特定類型的一種視圖,對特定類型操做性能更高;
  2. DataView: 各類數據類型均可以,還能夠指定大端序(BIG_ENDIAN),小端序(LITTLE_ENDIAN),功能更強大的一種視圖

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

14. 默認參數是如何工做?

若是咱們在調用 makeAPIRequest 函數時必須使用 timeout 的默認值,那麼正確的語法是什麼?

JavaScript 代碼:
    function makeAPIRequest(url, timeout = 2000, headers) {
    // Some code to fetch data
    }

15.解釋 TCO – 尾調用優化(Tail Call Optimization)。 有沒有支持尾調用優化的 JavaScript 引擎?

  • 尾調用指「函數尾調用」。
  • 尾調用是函數式編程的一個重要概念。
  • 尾調用就是某個函數最後一步是調用另外一個函數

提示:截至 2018 年,沒有。


JavaScript 前端應用設計問題

1.解釋單向數據流和雙向數據綁定。

Angular 1.x 基於雙向數據綁定,而 React,Vue,Elm 等基於單向數據流架構。

單向數據綁定

單向數據綁定,帶來單向數據流。。
指的是咱們先把模板寫好,而後把模板和數據(數據可能來自後臺)整合到一塊兒造成HTML代碼,而後把這段HTML代碼插入到文檔流裏面。適用於總體項目,並於追溯。

 

 


 

優勢:
1. 全部狀態的改變可記錄、可跟蹤,源頭易追溯;
2. 全部數據只有一份,組件數據只有惟一的入口和出口,使得程序更直觀更容易理解,有利於應用的可維護性;
3. 一旦數據變化,就去更新頁面(data-頁面),可是沒有(頁面-data);
4. 若是用戶在頁面上作了變更,那麼就手動收集起來(雙向是自動),合併到原有的數據中。
方神:雙向綁定 = 單向綁定 + UI事件監聽,可瞭解vuex
缺點:
1. HTML代碼渲染完成,沒法改變,有新數據,就須把舊HTML代碼去掉,整合新數據和模板從新渲染;
2. 代碼量上升,數據流轉過程變長,出現不少相似的樣板代碼;
3. 同時因爲對應用狀態獨立管理的嚴格要求(單一的全局store),在處理局部狀態較多的場景時(如用戶輸入交互較多的「富表單型」應用),會顯得囉嗦及繁瑣。

雙向數據綁定

雙向數據綁定,帶來雙向數據流。AngularJS2添加了單向數據綁定
數據模型(Module)和視圖(View)之間的雙向綁定。不管數據改變,或是用戶操做,都能帶來互相的變更,自動更新。適用於項目細節,如:UI控件中(一般是類表單操做)。

 


優勢:
1. 用戶在視圖上的修改會自動同步到數據模型中去,數據模型中值的變化也會馬上同步到視圖中去;
2. 無需進行和單向數據綁定的那些CRUD(Create,Retrieve,Update,Delete)操做;
3. 在表單交互較多的場景下,會簡化大量業務無關的代碼。
缺點:
1. 沒法追蹤局部狀態的變化;
2. 「暗箱操做」,增長了出錯時 debug 的難度;
3. 因爲組件數據變化來源入口變得可能不止一個,數據流轉方向易紊亂,若再缺少「管制」手段,血崩。
雙向數據綁定,Angular使用 髒檢查「digest」 - 「dirty checking」
(在angular中,他沒有辦法判斷你的數據是否作了更改, 因此它設置了一些條件,當你觸發了這些條件以後,它就執行一個檢測來遍歷全部的數據,對比你更改了地方,而後執行變化。這個檢查很不科學。並且效率不高,有不少多餘的地方,因此官方稱爲髒檢查)

2.單向數據流架構在哪些方面適合 MVC?

MVC 擁有大約 50 年的悠久歷史,並已演變爲 MVP,MVVM 和 MV *。二者之間的相互關係是什麼?若是 MVC 是架構模式,那麼單向數據流是什麼?這些競爭模式是否能解決一樣的問題?

3.客戶端 MVC 與服務器端或經典 MVC 有何不一樣?

提示:經典 MVC 是適用於桌面應用程序的 Smalltalk MVC。在 Web 應用中,至少有兩個不一樣的數據 MVC 週期。

4.使函數式編程與面向對象或命令式編程不一樣的關鍵因素是什麼?

提示:Currying(柯里化),point-free 函數,partial 函數應用,高階函數,純函數,獨立反作用,record 類型(聯合,代數數據類型)等。

5.在 JavaScript 和前端的上下文中,函數式編程與響應式編程有什麼關係?

提示:沒有正確答案。但粗略地說,函數式編程是關於小型編碼,編寫純函數和響應式編程是大型編碼,即模塊之間的數據流,鏈接以 FP 風格編寫的組件。 FRP – 功能響應式編程( Functional Reactive Programming)是另外一個不一樣但相關的概念。

6.不可變數據結構(immutable data structures)解決了哪些問題?

不可變結構是否有任何性能影響? JS 生態系統中哪些庫提供了不可變的數據結構?這些庫的優勢和缺點是什麼?

提示:線程安全(咱們真的須要在瀏覽器 JavaScript 中擔憂嗎?),無反作用的函數,更好的狀態管理等。

7.大型應用程序是否應使用靜態類型?

  1. 如何比較 TypeScript/Flow 與 Elm/ReasonML/PureScript 等 JS 轉換語言?這些方法的優缺點是什麼?
  2. 選擇特定類型系統的主要標準應該是什麼?
  3. 什麼是類型推斷(type inference)?
  4. 靜態類型語言和強類型語言有什麼區別?在這方面 JavaScript 的本質是什麼?
  5. 有你知道的弱類型但靜態類型的語言嗎?有你知道的動態類型但強類型的語言嗎?舉例一二。

提示:Structural 與 Nominal 類型系統,類型穩健性,工具/生態系統支持,正確性超過方便。

8.JavaScript 中有哪些傑出的模塊系統(module systems )?如何評價 ES 模塊系統。

列出在實現不一樣模塊系統之間互操做所涉及的一些複雜性問題(主要對 ES 模塊和 CommonJS 互操做感興趣)

9.HTTP/2 將如何影響 JavaScript 應用程序打包?

列出 HTTP/2 與其上一個版本的基本區別。

10.Fetch API 相對於傳統的 Ajax 有哪些改進?

  1. 使用 Fetch API 有那些缺點/難點嗎?
  2. 哪些是Ajax 能夠作的,而 fetch 不能作的?

11.如何評論 pull-based 和 push-based 的反應系統。

討論概念,含義,用途等。

  1. 在這個討論中加入惰性和及早求值。
  2. 而後在討論中添加單數和複數值維度。
  3. 最後,討論值解析的同步和異步性質。
  4. 爲JavaScript中可用的每一個組合提供示例。

提示:Observable 是惰性的,基於推送的複數值構造,同時具備 async/sync 調度程序。

12.討論與 Promise 相關的問題。

提示:及早求值(eager evaluation),尷尬的取消機制,用 then() 方法假裝 map() 和 flatMap() 等。


前端基礎和理論問題

1.HTML 中 Doctype 的用途是什麼?

具體談談,如下每種狀況下會發生什麼:

    1. Doctype 不存在。
    2. 使用了 HTML4 Doctype,但 HTML 頁面使用了 HTML5 的標籤,如 <audio> 或 <video> 。它會致使任何錯誤嗎?
    3. 使用了無效的 Doctype。

一、告訴瀏覽器使用什麼樣的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簡化了這種聲明,意在告訴瀏覽器使用統一的標準便可)

2. DOM 和 BOM 的區別是什麼?

提示: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 可以對頁面中的全部事件作出反應

3.JavaScript 中的事件處理如何運行?

以下圖所示,咱們有三個 div 元素。每一個 div 都有一個與之關聯的點擊處理程序。處理程序執行如下任務:

  • Outer div click 處理程序將 hello outer 打印到控制檯。
  • Inner div click 處理程序將 hello inner 打印到控制檯。
  • Innermost div click 處理程序將 hello innermost 打印到控制檯。

編寫一段代碼來分配這些任務,以便在單擊 innermost div 時始終打印如下序列?

hello inner → hello innermost → hello outer

事件冒泡和捕獲

提示:事件捕獲和事件冒泡

探究JavaScript中的五種事件處理程序

  咱們知道JavaScript與HTML之間的交互是經過事件實現的。事件最先是在IE3和Netscape Navigator 2中出現的,當時是做爲分擔服務器運算負載的一種手段。  通俗地理解,事件就是用戶或瀏覽器自身執行的某種操做。而事件處理程序即爲響應某個事件的函數。抽出主幹,即事件處理程序爲函數。  咱們又把事件處理程序稱爲事件偵聽器。  事件處理程序是以"on"開頭的,所以對於事件on的時間處理程序即爲onclick。時間處理程序在JavaScript中大體有五種,下面會根據這五種不一樣的時間處理程序分爲5部分來介紹。

  1. HTML事件處理程序
  2. DOM0級事件處理程序
  3. DOM2級事件處理程序
  4. IE事件處理程序
  5. 跨瀏覽器的事件處理程序

第一部分:HTML事件處理程序

  什麼使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語法字符""(雙引號),這時不會彈出窗口,而是報錯語法錯誤。可是我若是仍是但願使用雙引號呢? 這時就要用&quot;實體來代替HTML中的語法字符。以下例所示:

例3:

<button onclick="alert(&quot;success&quot;)">點我</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級事件處理程序用的也很是廣泛。之因此成爲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級事件處理程序並非完美的,它一樣有兩個缺點:

  1. 咱們不能給一個元素同時添加兩個事件。
  2. 咱們不能控制元素的事件流(捕獲or冒泡)。

  對於第二個問題後面會講到,第一個問題舉例以下:

複製代碼
    <button id="button">點我</button>

<script>
    var button=document.getElementById("button");
    button.onclick=function(){
        alert("clicked");
    }
    button.onclick=function(){
        alert("again");
    }
複製代碼

雖然我對同一個元素設置了兩個事件處理程序,可是最終的結果是:只有第二個事件有效(覆蓋了第一個事件)。固然,人類是聰明的動物,DOM2級事件很好的解決了這個問題!

第三部分:DOM2級事件處理程序

  DOM2級事件處理程序定義了兩個方法:

  • addEventListener()   ---添加事件偵聽器
  • removeEventListener()   ---刪除事件偵聽器

 在博文的開頭我就提到了事件處理程序即事件偵聽器。這兩個方法都接收三個參數:

  1. 要處理的事件名(注意:是時間名,因此沒有on!),如click、mouseover等。
  2. 做爲事件處理程序的函數,如function(){alert("clicked");}
  3.  表示事件流方式的布爾值。false爲冒泡階段調用事件處理程序;true爲捕獲階段調用事件處理程序。對於冒泡和捕獲這兩種時間流能夠看《JavaScript中的兩種事件流


下面經過兩個例子加深理解:

例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事件處理程序

  IE事件處理程序中有相似與DOM2級事件處理程序的兩個方法:

  1. attachEvent()
  2. detachEvent()

  它們都接收兩個參數:

  1. 事件處理程序名稱。如onclick、onmouseover,注意:這裏不是事件,而是事件處理程序的名稱,因此有on。
  2. 事件處理程序函數。如function(){alert("clicked");}

   之因此沒有和DOM2級事件處理程序中相似的第三個參數,是由於IE8及更早版本只支持冒泡事件流。

  注意:

  1.IE事件處理程序中attachEvent()的事件處理程序的做用域和DOM0與DOM2不一樣,她的做用域是在全局做用域中。所以,不一樣於DOM0和DOM2中this指向元素,IE中的this指向window。 

  2.一樣,咱們能夠使用attachEvent()來給同一個元素添加多個事件處理程序。可是與DOM2不一樣,事件觸發的順序不是添加的順序而是添加順序的相反順序。

  3.一樣地,經過attachEvent()添加的事件處理程序必須經過detachEvent()方法移除,一樣的,不能使用匿名函數。

  4.支持IE事件處理程序的瀏覽器不僅有IE瀏覽器,還有Opera瀏覽器。

第五部分:跨瀏覽器的事件處理程序

  實際上,這一部分視爲了跨瀏覽器使用,將前面的幾部分結合起來就能夠了。

  這一部分須要建立兩個方法:

  • addHandler()  --這個方法職責是視狀況來使用DOM0級、DOM2級、IE事件處理程序來添加事件。
  • removeHandler()--這個方法就是移除使用addHandler添加的事件。

  這兩個方法接收相同的三個參數:

  1. 要操做的元素--經過dom方法獲取
  2. 事件名稱--注意:沒有on,如click、mouseover
  3. 事件處理程序函數--即handler函數

  這兩個方法的構造狀況以下:

複製代碼
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」。

4.使用單頁應用將文件上傳到服務器的有哪些方法?

提示: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,須要首先補充這方面知識。

基本的Fetch請求

讓咱們首先來比較一個XMLHttpRequest使用示例與fetch方法的使用示例。該示例向服務器端發出請求,獲得響應並使用JSON將其解析。

XMLHttpRequest

一個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()方法的使用代碼示例以下所示:

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請求定義一個模式以確保請求有效。能夠定義的模式以下所示:

  • "same-origin":只在請求同域中資源時成功,其餘請求將被拒絕。
  • "cors":容許請求同域及返回CORS響應頭的域中的資源。
  • "cors-with-forced-preflight":在發出實際請求前執行preflight檢查。
  • "no-cors"針對的是向其餘不返回CORS響應頭的域中的資源發出的請求(響應類型爲「opaque」),但如前所述,目前在頁面腳本代碼中不起做用。

爲了定義模式,在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方法鏈

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請求的邏輯,代碼容易閱讀、維護及測試。

POST請求

在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請求發送憑證

你可能想要使用Fetch發送帶有諸如cookie之類的憑證的請求。你能夠在選項對象中將credentials屬性值設置爲「include」:

fetch(url,{
credentials:"include"
})

3、File API

file對象是對文件對象的一種表現

表明input上傳時的文件獨享對象

IE9中沒有這個對象,因此沒法操做文件

5.CSS 重排和重繪之間有什麼區別?

哪些 CSS 屬性會致使重排及重繪?

重繪是一個元素的外觀變化所引起的瀏覽器行爲;

重排是引發DOM樹從新計算的行爲;

一、迴流/重排
渲染樹的一部分必需要更新且節點的尺寸發生了變化,會觸發重排操做。每一個頁面至少在初始化的時候會有一次重排操做。
二、重繪
部分節點須要更新,但沒有改變其形狀,會觸發重繪操做。
會觸發重繪或迴流/重排的操做
一、添加、刪除元素(迴流+重繪)
二、隱藏元素,display:none(迴流+重繪),visibility:hidden(只重繪,不迴流)
三、移動元素,如改變top、left或移動元素到另外1個父元素中(重繪+迴流)
四、改變瀏覽器大小(迴流+重繪)
五、改變瀏覽器的字體大小(迴流+重繪)
六、改變元素的padding、border、margin(迴流+重繪)
七、改變瀏覽器的字體顏色(只重繪,不迴流)
八、改變元素的背景顏色(只重繪,不迴流)

6. 什麼是 CSS 選擇器權重以及它如何工做?

說說計算 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。

7.CSS 中的 pixel 與硬件/物理中的 pixel 有何不一樣?

提示:像素不是像素不是像素 – ppk。

css裏全部的px都是css pixel,其顯示大小是相對的而不是絕對的,是設備相關的。
而css pixel * devicePixelRatio = 實際屏幕像素。
(平時電腦屏幕的devicePixelRatio都是1因此感受不到,會讓你誤覺得css裏的px就是實際屏幕像素)
同時media query使用的px顯然也是css pixel。

8.什麼是 sectioning 算法?

提示:它也被稱爲 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;將其隱藏)

9.若是你用過 CSS Flex / CSS Grid(網格)佈局,請說明你爲何要使用它?它爲你解決了什麼問題?

  • 使用 CSS Grid,百分比%和 fr 單位有何不一樣?
  • fr是一個相對尺寸單位,表示剩餘空間作等分,此項分配到的百分比(若是隻有一個項使用此單位,那就佔剩餘空間的100%,因此多個項聯合使用更有意義)
  • 使用 CSS flexbox,有時 flex-items/children 會不考慮 flex 容器設置的寬度/高度?爲何會這樣?
  • 能夠使用 CSS Grid 建立 Masonry layout(瀑布流佈局)嗎?若是能夠,怎麼作?
  • 解釋 CSS Grid 和 CSS flexbox 術語?
  • CSS Grid佈局 (又名"網格"),是一個基於二維網格佈局的系統,主要目的是改變咱們基於網格設計的用戶接口方式
  • 浮動元素(float: left | right;)如何在 CSS Grid 和 flexbox 中渲染?

提示:等高的列,垂直居中,複雜網格等。

10.何時應該使用 CSS animations 而不是 CSS transitions ?你作出這個決定標準是什麼?

11.若是你正在 Review CSS 代碼,那麼你在代碼中常常遇到的問題是什麼?

示例:使用魔性數字,如 width: 67px; 或使用 em 代替 rem 單位,在通用代碼以前編寫 media queries(媒體查詢),濫用 ID 和類等。

12.如何在 JavaScript 中檢測觸摸事件?

有三種狀況,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("瀏覽器不支持觸屏");
    }
};
  1. 你是否不看好檢測設備對觸摸事件的支持?若是是,爲何?
  2. 主流作法通常是經過 UserAgent 判斷。
  3. 比較觸摸事件和點擊事件。
  4. 在觸摸設備上,touch事件從手指剛碰到屏幕就會觸發,而click事件則要晚一下子才被觸發。觸發順序以下:

    touchstart
    mouseover
    mousemove(一次)
    mousedown
    mouseup
    click
    touchend
    因此,若是想提高web在觸摸設備下的用戶體驗,讓用戶以爲響應很是迅速,應該對頁面的觸摸事件進行事件處理程序的註冊,而不該再關注click事件。

  5. 當設備同時支持觸摸和鼠標事件時,你認爲這些事件的正確事件順序是什麼或應該是什麼?
  • touchstart
  • Zero or more touchmove events, depending on movement of the finger(s)
  • touchend
  • mousemove
  • mousedown
  • mouseup
  • click

13.爲 script 標籤訂義的 async 和 defer 屬性有什麼用?

  • 如今咱們有 HTTP/2 和 ES 模塊,它們真的頗有用嗎?
  • 向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,它的加載和執行是牢牢挨着的,不管聲明順序如何,只要加載完成就馬上執行,它對於應用腳本用處不大,由於它徹底不考慮依賴。

相關文章
相關標籤/搜索