2019前端面試題

MVC,MVVC,MVVM模式的理解

1.MVC(Model-View-Controller)

Model(模型):數據層,負責存儲數據。

View(視圖):展現層,用戶所看到的頁面

Controller(控制器):協調層,負責協調Model和View,根據用戶在View上的動作在Model上作出對應的更改,同時將更改的信息返回到View上。

三者之間的關係

Controller可以直接訪問Model,也可以直接控制View,但是Model和View不能相互通信,相當於COntroller就是介於這兩者之間的協調者。

2,MVVM(Model-View-ViewModel)

Model(模型):數據層,負責存儲數據。

View(控制器):就是ViewController層,他的任務就是從ViewModel層獲取數據,然後顯示。

ViewModel(視圖模型):就是View和Model層的粘合劑,封裝業務邏輯處理,封裝網絡處理,封裝數據緩存。就是把原來ViewController層的業務邏輯和頁面邏輯等剝離出來放到ViewModel層。

3,MVVC(Model-View-View-Controller)

Model(模型):數據層,負責存儲數據。

View(視圖):展現層,創建需求創建cell

View(視圖):定義數組,用來接收控制中的數據。處理回調(比如:刷新回調、點擊cell回調、加載更多回調、動態視圖高度回調等等)

Controller(控制器):加載網絡數據懶加載

4. Vue 有哪些指令?

v-html、v-show、v-if、v-for等等

5. v-if 和 v-show 有什麼區別?

v-show 僅僅控制元素的顯示方式,將 display 屬性在 block 和 none 來回切換;而v-if會控制這個 DOM 節點的存在與否。當我們需要經常切換某個元素的顯示/隱藏時,使用v-show會更加節省性能上的開銷;當只需要一次顯示或隱藏時,使用v-if更加合理。

6. 簡述Vue的響應式原理

當一個Vue實例創建時,vue會遍歷data選項的屬性,用 Object.defineProperty 將它們轉為 getter/setter並且在內部追蹤相關依賴,在屬性被訪問和修改時通知變化。

每個組件實例都有相應的 watcher 程序實例,它會在組件渲染的過程中把屬性記錄為依賴,之後當依賴項的 setter 被調用時,會通知 watcher 重新計算,從而致使它關聯的組件得以更新。

7. 簡述Vue的生命週期

它可以總共分為8個階段:

beforeCreate(創建前),

created(創建後),

beforeMount(載入前),

mounted(載入後),

beforeUpdate(更新前),

updated(更新後),

beforeDestroy(銷燬前),

destroyed(銷燬後)

8.vue中Computed 和 Watch的使用和區別

1、計算屬性computed適用的情形

我們可能會有這樣的需求,一個數據屬性在它所依賴的屬性發生變化時,也要發生變化,這種情況下,我們最好使用計算屬性。

2、監聽器watch適當的情形

watch函數適用於,當數據發生變化時,執行異步操作或較大開銷操作的情況。

8.vue 與 react優缺點對比

vue

API設計上簡單,語法簡單,學習成本低

構建方面不包含路由和ajax功能,使用vuex, vue-router

指令(dom)和組件(視圖,數據,邏輯)處理清晰

性能好,容易優化

基於依賴追蹤的觀察系統,並且異步隊列更新

獨立觸發

v-model 實時渲染

適用於:模板和渲染函數的彈性選擇

簡單的語法及項目搭建

更快的渲染速度和更小的體積

react

利用jsx創建虛擬dom

是一種在內存中描述dom數狀態的數據結構

函數式的方法描述視圖

使用虛擬dom作為模板

程序片段

不好控制dom

生命週期

服務端渲染:react的虛擬dom的生成可以在任何支持js的環境生成的,所以可以在node環境生成,直接轉為string,然後插入到html文件中輸出瀏覽器便可

適用於:大型應用和更好的可測試性;同時適用於web端和原生app;更大的生態圈

優點

React偉大之處就在於,提出了Virtual Dom這種新穎的思路,並且這種思路衍生出了React Native,有可能會統一Web/Native開發。在性能方面,由於運用了Virtual Dom技術,Reactjs只在調用setState的時候會更新dom,而且還是先更新Virtual Dom,然後和實際Dom比較,最後再更新實際Dom。這個過程比起Angularjs的bind方式來說,一是更新dom的次數少,二是更新dom的內容少,速度肯定快

ReactJS更關注UI的組件化,和數據的單向更新,提出了FLUX架構的新概念,現在React可以直接用Js ES6語法了,然後通過webpack編譯成瀏覽器兼容的ES5,開發效率上有些優勢.

React Native生成的App不是運行在WebView上,而是系統原生的UI,React通過jsx生成系統原生的UI,iOS和Android的React UI組件還是比較相似的,大量代碼可以複用

維護UI的狀態,Angular 裡面使用的是 $scope,在 React 裡面使用的是 this.setState。 而 React 的好處在於,它更簡單直觀。所有的狀態改變都只有唯一一個入口 this.setState(),

同構的JavaScript

單頁面JS應用程序的最大缺陷在於對搜索引擎的索引有很大限制。React對此有了解決方案。

React可以在服務器上預渲染應用再發送到客戶端。它可以從預渲染的靜態內容中恢復一樣的記錄到動態應用程序中。

因為搜索引擎的爬蟲程序依賴的是服務端響應而不是JavaScript的執行,預渲染你的應用有助於搜索引擎優化。

缺點

React是目標是UI組件,通常可以和其它框架組合使用,目前並不適合單獨做一個完整的框架。React 即使配上 redux 的組合,也不能稱之一個完整的框架,比如你想用Promise化的AJAX?對不起沒有,自己找現成的庫去。而且第三方組件遠遠不如Angular多。目前在大的穩定的項目上採用React的,我也就只知道有Yahoo的Email。React本身只是一個V而已,所以如果是大型項目想要一套完整的框架的話,也許還需要引入Redux和route相關的東西。

vue,react 共性:

虛擬dom實現快速渲染

輕量級響應式組件

服務端渲染易於集成路由工具,打包工具及狀態管理工具

9.知道什麼是vuex嗎?

在SPA單頁面組件的開發中 Vue的vuex和React的Redux 都統稱為同一狀態管理,個人的理解是全局狀態管理更合適;簡單的理解就是你在state中定義了一個數據之後,你可以在所在項目中的任何一個組件裡進行獲取、進行修改,並且你的修改可以得到全局的響應變更。

核心概念1: State

state就是Vuex中的公共的狀態, 將state看作是所有組件的data, 用於保存所有組件的公共數據.

核心概念2: Getters

getters屬性理解為所有組件的computed屬性, 也就是計算屬性. vuex的官方文檔也是說到可以將getter理解為store的計算屬性, getters的返回值會根據它的依賴被緩存起來,且只有當它的依賴值發生了改變才會被重新計算。

核心概念3: Mutations

mutaions理解為store中的methods, mutations對象中保存著更改數據的回調函數,該函數名官方規定叫type, 第一個參數是state, 第二參數是payload, 也就是自定義的參數.

核心概念4: Actions

actions 類似於 mutations,不同在於:actions提交的是mutations而不是直接變更狀態。actions中可以包含異步操作, mutations中絕對不允許出現異步,actions中的回調函數的第一個參數是context, 是一個與store實例具有相同屬性和方法的對象

10.Vue-自定義指令

v-model,v-on,v-bind這一類的指令很經常使用,但有時候也不能滿足我們的需求,比如我們想要通過v-focus(這個指令是vue不提供的)實現表單的焦點獲取,就會遇到無指令提供的窘況。

這個時候我們可以自定義指令,有個地方要注意下,如果使用了未定義的指令,vue會提出警告,程序依然會正常運行。

1、比如定義一個全局可用的focus實現input選中,全局的東西如JS中的全局變量,一般不會用。

2019前端面試題

2、定義局部指令,局部指令只在組件內部有效,超出組件範圍則無效,類似局部變量的作用。

2019前端面試題

vue給自定義指令提供了幾個函數鉤子,用於實現自定義指令的功能:

bind: 指令第一次綁定到元素時調用,

inserted: 被綁定元素插入父節點時調用(父節點存在即可調用,不必存在於 document 中)。

update: 被綁定元素所在的模板更新時調用,而不論綁定值是否變化。通過比較更新前後的綁定值,可以忽略不必要的模板更新(詳細的鉤子函數參數見下)。

componentUpdated: 被綁定元素所在模板完成一次更新週期時調用。

unbind: 指令與元素解綁時調用。

11,vue組件之間互相傳值:父傳子,子傳父

一、父組件向子組件傳值步驟

1.創建子組件,在src/components/文件夾下新建一個Child.vue

2.Child.vue的中創建props,然後創建一個名為message的屬性

2019前端面試題

3.在App.vue中註冊Child組件,並在template中加入child標籤,標籤中添加message屬性並賦值

2019前端面試題

二、子組件向父組件傳值

1.在子組件中創建一個按鈕,給按鈕綁定一個點擊事件

2019前端面試題

2.在響應該點擊事件的函數中使用$emit來觸發一個自定義事件,並傳遞一個參數

2019前端面試題

3.在父組件中的子標籤中監聽該自定義事件並添加一個響應該事件的處理方法

2019前端面試題

4.保存修改的文件,在瀏覽器中點擊按鈕

12,vue2.0中的$router 和 $route的區別

1.router是VueRouter的一個對象,通過Vue.use(VueRouter)和VueRouter構造函數得到一個router的實例對象,這個對象中是一個全局的對象

,他包含了所有的路由包含了許多關鍵的對象和屬性。

舉例:history對象

$router.push({path:'home'});本質是向history棧中添加一個路由,在我們看來是 切換路由,但本質是在添加一個history記錄

方法:

$router.replace({path:'home'});//替換路由,沒有歷史記錄

2.route是一個跳轉的路由對象,每一個路由都會有一個route對象,是一個局部的對象,可以獲取對應的name,path,params,query等

我們可以從vue devtools中看到每個路由對象的不同

13,如何優化SPA應用的首屏加載速度慢的問題?

  • 將公用的JS庫通過script標籤外部引入,減小app.bundel的大小,讓瀏覽器並行下載資源文件,提高下載速度;
  • 在配置 路由時,頁面和組件使用懶加載
    的方式引入,進一步縮小 app.bundel 的體積,在調用某個組件時再加載對應的js文件;
  • 加一個首屏 loading 圖,提升用戶體驗

14,網頁從輸入網址到渲染完成經歷了哪些過程?

  1. 輸入網址;
  2. 發送到DNS服務器,並獲取域名對應的web服務器對應的ip地址;
  3. 與web服務器建立TCP連接;
  4. 瀏覽器向web服務器發送http請求;
  5. web服務器響應請求,並返回指定url的數據(或錯誤信息,或重定向的新的url地址);
  6. 瀏覽器下載web服務器返回的數據及解析html源文件;
  7. 生成DOM樹,解析css和js,渲染頁面,直至顯示完成;

15.ajax請求的過程

1、創建XMLHttpRequest對象

2,創建一個新的HTTP請求,並指定該HTTP請求的方法、URL及驗證信息.

3,設置響應HTTP請求狀態變化的函數.

4,發送HTTP請求.

5,獲取異步調用返回的數據.

6,使用JavaScript和DOM實現局部刷新.

16,js的數組去重

一,es6 set去重

2019前端面試題

二,js去重

2019前端面試題

17,js數組排序

2019前端面試題

18,js的原型和原型鏈

1、javascript原型

JS中每個函數都存在有一個原型對象屬性prototype。並且所有函數的默認原型都是Object的實例。

2、javascript原型鏈

每個繼承父函數的子函數的對象都包含一個內部屬性_proto_。該屬性包含一個指針,指向父函數的prototype。若父函數的原型對象的_proto_屬性為再上一層函數。在此過程中就形成了原型鏈。

3、特點

原型鏈實現了繼承。

2019前端面試題

19,js實現繼承的方法

2019前端面試題

2019前端面試題

2019前端面試題

20,js的基本數據類型

  • 基本類型:String、Number、Boolean、Sy有mbol、Undefined、Null
  • 引用類型:Object

21,js有哪些內置對象

Math對象:

Math.abs(x);//用來返回數的絕對值

Date對象:var date = new Date();// 通過new的方式創建一個日期對象;

Array對象:var arr=new Array();

字符串對象:var str=new String();

22,js操作數組的方法

join():join(separator): 將數組的元素組起一個字符串,以separator為分隔符,省略的話則用默認用逗號為分隔符,該方法只接收一個參數:即分隔符。

push()和pop():push(): 可以接收任意數量的參數,把它們逐個添加到數組末尾,並返回修改後數組的長度。 pop():數組末尾移除最後一項,減少數組的 length 值,然後返回移除的項。

shift() 和 unshift():shift():刪除原數組第一項,並返回刪除元素的值;如果數組為空則返回undefined 。 unshift:將參數添加到原數組開頭,並返回數組的長度

sort():按升序排列數組項——即最小的值位於最前面,最大的值排在最後面

reverse():反轉數組項的順序。

concat():將參數添加到原數組中。這個方法會先創建當前數組一個副本,然後將接收到的參數添加到這個副本的末尾,最後返回新構建的數組。在沒有給 concat()方法傳遞參數的情況下,它只是複製當前數組並返回副本。

slice():返回從原數組中指定開始下標到結束下標之間的項組成的新數組。slice()方法可以接受一或兩個參數,即要返回項的起始和結束位置。在只有一個參數的情況下, slice()方法返回從該參數指定位置開始到當前數組末尾的所有項。如果有兩個參數,該方法返回起始和結束位置之間的項——但不包括結束位置的項。

splice():splice():很強大的數組方法,它有很多種用法,可以實現刪除、插入和替換。

刪除:可以刪除任意數量的項,只需指定 2 個參數:要刪除的第一項的位置和要刪除的項數。例如, splice(0,2)會刪除數組中的前兩項。

插入:可以向指定位置插入任意數量的項,只需提供 3 個參數:起始位置、 0(要刪除的項數)和要插入的項。例如,splice(2,0,4,6)會從當前數組的位置 2 開始插入4和6。

替換:可以向指定位置插入任意數量的項,且同時刪除任意數量的項,只需指定 3 個參數:起始位置、要刪除的項數和要插入的任意數量的項。插入的項數不必與刪除的項數相等。例如,splice (2,1,4,6)會刪除當前數組位置 2 的項,然後再從位置 2 開始插入4和6。

splice()方法始終都會返回一個數組,該數組中包含從原始數組中刪除的項,如果沒有刪除任何項,則返回一個空數組。

indexOf()和 lastIndexOf() (ES5新增):indexOf():接收兩個參數:要查找的項和(可選的)表示查找起點位置的索引。其中, 從數組的開頭(位置 0)開始向後查找。

lastIndexOf:接收兩個參數:要查找的項和(可選的)表示查找起點位置的索引。其中, 從數組的末尾開始向前查找。

這兩個方法都返回要查找的項在數組中的位置,或者在沒找到的情況下返回1。在比較第一個參數與數組中的每一項時,會使用全等操作符。

forEach() (ES5新增)forEach():對數組進行遍歷循環,對數組中的每一項運行給定函數。這個方法沒有返回值。參數都是function類型,默認有傳參,參數分別為:遍歷的數組內容;第對應的數組索引,數組本身。

map() (ES5新增)map():指“映射”,對數組中的每一項運行給定函數,返回每次函數調用的結果組成的數組。

filter() (ES5新增)filter():“過濾”功能,數組中的每一項運行給定函數,返回滿足過濾條件組成的數組。

every() (ES5新增)every():判斷數組中每一項都是否滿足條件,只有所有項都滿足條件,才會返回true。

some() (ES5新增)some():判斷數組中是否存在滿足條件的項,只要有一項滿足條件,就會返回true。

reduce()和 reduceRight() (ES5新增)這兩個方法都會實現迭代數組的所有項,然後構建一個最終返回的值。reduce()方法從數組的第一項開始,逐個遍歷到最後。而 reduceRight()則從數組的最後一項開始,向前遍歷到第一項。

這兩個方法都接收兩個參數:一個在每一項上調用的函數和(可選的)作為歸併基礎的初始值。

傳給 reduce()和 reduceRight()的函數接收 4 個參數:前一個值、當前值、項的索引和數組對象。這個函數返回的任何值都會作為第一個參數自動傳給下一項。第一次迭代發生在數組的第二項上,因此第一個參數是數組的第一項,第二個參數就是數組的第二項。

23,什麼是閉包,閉包的優缺點

閉包各種專業文獻的閉包定義都非常抽象,我的理解是: 閉包就是能夠讀取其他函數內部變量的函數。

由於在javascript中,只有函數內部的子函數才能讀取局部變量,所以說,閉包可以簡單理解成“定義在一個函數內部的函數“。

所以,在本質上,閉包是將函數內部和函數外部連接起來的橋樑。

優點:

1. 邏輯連續,當閉包作為另一個函數調用的參數時,避免你脫離當前邏輯而單獨編寫額外邏輯。

2. 方便調用上下文的局部變量。

3. 加強封裝性,第2點的延伸,可以達到對變量的保護作用。

缺點:

閉包有一個非常嚴重的問題,那就是內存浪費問題,這個內存浪費不僅僅因為它常駐內存,更重要的是,對閉包的使用不當會造成無效內存的產生

24,阻止時間冒泡和默認事件

1,e.stopPropagation()

2,e.preventDefault(),

3,return false

25,javascript中apply、call和bind的區別

在JS中,這三者都是用來改變函數的this對象的指向的,他們有什麼樣的區別呢。

在說區別之前還是先總結一下三者的相似之處:

1、都是用來改變函數的this對象的指向的。

2、第一個參數都是this要指向的對象。

3、都可以利用後續參數傳參

call和apply都是對函數的直接調用,而bind方法返回的仍然是一個函數,因此後面還需要()來進行調用才可以。

call後面的參數與say方法中是一一對應的,而apply的第二個參數是一個數組,數組中的元素是和say方法中一一對應的,這就是兩者最大的區別。

26,如何解決跨域的問題

JSONP:利用script標籤可跨域的特點,在跨域腳本中可以直接回調當前腳本的函數。

反向代理

27,vue this.$set 給data對象新增屬性,並觸發視圖更新

2019前端面試題

28,vue路由實現的原理

2019前端面試題

2019前端面試題

29,寫一個閉包

2019前端面試題

30,創建對象的方法

一、通過”字面量“方式創建。

var person = {} //創建空對象

2019前端面試題

2019前端面試題

31,HTTP中Get、Post、Put與Delete的區別

1、GET請求會向數據庫發索取數據的請求,從而來獲取信息,該請求就像數據庫的select操作一樣,只是用來查詢一下數據,不會修改、增加數據,不會影響資源的內容,即該請求不會產生副作用。無論進行多少次操作,結果都是一樣的。

2、與GET不同的是,PUT請求是向服務器端發送數據的,從而改變信息,該請求就像數據庫的update操作一樣,用來修改數據的內容,但是不會增加數據的種類等,也就是說無論進行多少次PUT操作,其結果並沒有不同。

3、POST請求同PUT請求類似,都是向服務器端發送數據的,但是該請求會改變數據的種類等資源,就像數據庫的insert操作一樣,會創建新的內容。幾乎目前所有的提交操作都是用POST請求的。

4、DELETE請求顧名思義,就是用來刪除某一個資源的,該請求就像數據庫的delete操作。

就像前面所講的一樣,既然PUT和POST操作都是向服務器端發送數據的,那麼兩者有什麼區別呢。。。POST主要作用在一個集合資源之上的(url),而PUT主要作用在一個具體資源之上的(url/xxx),通俗一下講就是,如URL可以在客戶端確定,那麼可使用PUT,否則用POST。

綜上所述,我們可理解為以下:

1、POST /url 創建

2、DELETE /url/xxx 刪除

3、PUT /url/xxx 更新

4、GET /url/xxx 查看

32,箭頭函數和普通函數的區別

1,箭頭函數是匿名函數,不能作為構造函數,不能使用new

2,箭頭函數不綁定arguments,取而代之用rest參數...解決

3,箭頭函數不綁定this,會捕獲其所在的上下文的this值,作為自己的this值

4,箭頭函數通過 call() 或 apply() 方法調用一個函數時,只傳入了一個參數,對 this 並沒有影響。

5,箭頭函數沒有原型屬性

6,箭頭函數不能當做Generator函數,不能使用yield關鍵字

33,es6新特性

let+const塊級作用域,有了塊級作用域,還可以像強類型語言一樣定義常量。由於之前沒有塊級作用域以及 var 關鍵字所帶來的變量提升,經常給我們的開發帶來一些莫名其妙的問題.

Arrows 箭頭函數

Class, extends, super 類的支持

模板字面量用倒引號( `` )

在ES6中,可以使用解構從數組和對象提取值並賦值給獨特的變量

2019前端面試題

2019前端面試題

2019前端面試題

Promise的理解與使用

promise對象有兩個特點:(1)對象的狀態不受外界外界影響.promise對象代表一個異步操作,有三種狀態;pending(進行中),fulfilled(已成功)和rejected(已失敗).只有異步操作的結果,可以決定當前是哪一種狀態;(2)一旦狀態改變,就不會再改變,任何時候都可以得到這個結果.promise的狀態改變,只有兩種可能:從pending變為fulfilled和從pending變成rejected.只要這兩種狀態情況發生,狀態就凝固了,不會再變了,會一直保持這個結果,這時就稱為resolved(已定型).

基本用法

new Promise( /* executor */ function(resolve, reject) { ... } )

promise接受一個函數作為參數,該函數的兩個參數分別是resolve和reject.它們是兩個函數,由JavaScript引擎提供,不用自己部署.

then方法

Promise實例具有then方法,也就是說,then方法是定義在原型對象Promise.prototype上的,它的作用是是為Promise實例添加狀態改變時的回調函數.then方法的第一個參數是resolved狀態的回調函數,第二個參數(可選)是rejected狀態的回調函數.並且then方法返回的是一個新的Promise實例

http

34,常用http的http方法有哪些?

GET:用於請求訪問已經被uri識別的資源,可以通過url傳參給服務器.

POST:用於傳輸信息給服務器,主要功能GET類似,但一般推薦使用POST方式.

PUT:傳輸文件,報文主體中包含文件內容,保存到對應的uri位置.

HEAD:獲得報文首部,與GET方法類似,只是不返回報文主體,一般用於驗證uri是否有效.

DELETE:刪除文件,與PUT方法相反,刪除對應URI位置的文件.

OPTIONS:查詢對應uri支持的http方法

35,GET和POST的區別

區別一:

GET重點從服務器上獲取資源,POST重點向服務器發送數據.

區別二:

get傳輸數據是通過URL請求,置於URL後,並用”?”連接,多個請求數據間用”&”連接.post傳輸數據通過Http的post機制,將字段與對應值封存在請求實體中發送給服務器,這個過程對用戶是不可見的;

區別三:

Get傳輸的數據量小,因為受URL長度限制,但效率較高;

Post可以傳輸大量數據,所以上傳文件時只能用Post方式;

區別四:

get是不安全的,因為URL是可見的,可能會洩露私密信息,如密碼等;

post較get安全性較高;

區別五:

get方式只能支持ASCII字符,向服務器傳的中文字符可能會亂碼。

post支持標準字符集,可以正確傳遞中文字符。

36,HTTP請求報文與響應報文格式

請求報文包含三部分:

a、請求行:包含請求方法、URI、HTTP版本信息

b、請求首部字段

c、請求內容實體

響應報文包含三部分:

a、狀態行:包含HTTP版本、狀態碼、狀態碼的原因短語

b、響應首部字段

c、響應內容實體

37,理解 async/await

ES7 提出的async 函數,終於讓 JavaScript 對於異步操作有了終極解決方案。No more callback hell。

async 函數是 Generator 函數的語法糖。使用 關鍵字 async 來表示,在函數內部使用 await 來表示異步。

想較於 Generator,Async 函數的改進在於下面四點:

  • 內置執行器。Generator 函數的執行必須依靠執行器,而 Aysnc 函數自帶執行器,調用方式跟普通函數的調用一樣
  • 更好的語義。async 和 await 相較於 * 和 yield 更加語義化
  • 更廣的適用性。co 模塊約定,yield 命令後面只能是 Thunk 函數或 Promise對象。而 async 函數的 await 命令後面則可以是 Promise 或者 原始類型的值(Number,string,boolean,但這時等同於同步操作)
  • 返回值是 Promise。async 函數返回值是 Promise 對象,比 Generator 函數返回的 Iterator 對象方便,可以直接使用 then() 方法進行調用

Async 與其他異步操作的對比

先定義一個 Fetch 方法用於獲取 github user 的信息:

function fetchUser() { return new Promise((resolve, reject) => { fetch('https://api.github.com/users/superman66') .then((data) => { resolve(data.json()); }, (error) => { reject(error); }) }); }

Promise 方式

/** * Promise 方式 */ function getUserByPromise() { fetchUser() .then((data) => { console.log(data); }, (error) => { console.log(error); }) } getUserByPromise();

Promise 的方式雖然解決了 callback hell,但是這種方式充滿了 Promise的 then() 方法,如果處理流程複雜的話,整段代碼將充滿 then。語義化不明顯,代碼流程不能很好的表示執行流程。

Generator 方式

/** * Generator 方式 */ function* fetchUserByGenerator() { const user = yield fetchUser(); return user; } const g = fetchUserByGenerator(); const result = g.next().value; result.then((v) => { console.log(v); }, (error) => { console.log(error); })

Generator 的方式解決了 Promise 的一些問題,流程更加直觀、語義化。但是 Generator 的問題在於,函數的執行需要依靠執行器,每次都需要通過 g.next() 的方式去執行。

async 方式

/** * async 方式 */ async function getUserByAsync(){ let user = await fetchUser(); return user; } getUserByAsync() .then(v => console.log(v));

async 函數完美的解決了上面兩種方式的問題。流程清晰,直觀、語義明顯。操作異步流程就如同操作同步流程。同時 async 函數自帶執行器,執行的時候無需手動加載。

語法

async 函數返回一個 Promise 對象

async 函數內部 return 返回的值。會成為 then 方法回調函數的參數。

async function f() { return 'hello world' }; f().then( (v) => console.log(v)) // hello world

如果 async 函數內部拋出異常,則會導致返回的 Promise 對象狀態變為 reject 狀態。拋出的錯誤而會被 catch 方法回調函數接收到。

async function e(){ throw new Error('error'); } e().then(v => console.log(v)) .catch( e => console.log(e));

async 函數返回的 Promise 對象,必須等到內部所有的 await 命令的 Promise 對象執行完,才會發生狀態改變

也就是說,只有當 async 函數內部的異步操作都執行完,才會執行 then 方法的回調。

const delay = timeout => new Promise(resolve=> setTimeout(resolve, timeout)); 
async function f(){ await delay(1000);
await delay(2000);
await delay(3000);return 'done'; }
f().then(v => console.log(v)); // 等待6s後才輸出 'done'

正常情況下,await 命令後面跟著的是 Promise ,如果不是的話,也會被轉換成一個 立即 resolve 的 Promise

如下面這個例子:

async function f() { return await 1 }; 
f().then( (v) => console.log(v)) // 1

如果返回的是 reject 的狀態,則會被 catch 方法捕獲。

Async 函數的錯誤處理

async 函數的語法不難,難在錯誤處理上。

先來看下面的例子:

let a; 
async function f() { await Promise.reject('error');
a = await 1; // 這段 await 並沒有執行 }
f().then(v => console.log(a));

如上面所示,當 async 函數中只要一個 await 出現 reject 狀態,則後面的 await 都不會被執行。

解決辦法:可以添加 try/catch。

// 正確的寫法
let a;
async function correct() { try { await Promise.reject('error') } catch (error) { console.log(error); }
a = await 1; return a; }
correct().then(v => console.log(a)); // 1

如果有多個 await 則可以將其都放在 try/catch 中

38.怎樣添加、移除、移動、複製、創建和查找節點?

1)創建新節點

createDocumentFragment() //創建一個DOM片段
createElement() //創建一個具體的元素

createTextNode() //創建一個文本節點

2)添加、移除、替換、插入

appendChild() //添加
removeChild() //移除
replaceChild() //替換
insertBefore() //插入

3)查找

getElementsByTagName() //通過標籤名稱
getElementsByName() //通過元素的Name屬性的值
getElementById() //通過元素Id,唯一性

39.談談垃圾回收機制方式及內存管理

回收機制方式

1、定義和用法:垃圾回收機制(GC:Garbage Collection),執行環境負責管理代碼執行過程中使用的內存。

2、原理:垃圾收集器會定期(週期性)找出那些不在繼續使用的變量,然後釋放其內存。但是這個過程不是實時的,因為其開銷比較大,所以垃圾回收器會按照固定的時間間隔週期性的執行。

3、實例如下:

function fn1() { 

var obj = {name: 'hanzichi', age: 10};
}
function fn2() {
var obj = {name:'hanzichi', age: 10};
return obj;
}
var a = fn1();
var b = fn2();

fn1中定義的obj為局部變量,而當調用結束後,出了fn1的環境,那麼該塊內存會被js引擎中的垃圾回收器自動釋放;在fn2被調用的過程中,返回的對象被全局變量b所指向,所以該塊內存並不會被釋放。

4、垃圾回收策略:標記清除(較為常用)和引用計數。

標記清除:

定義和用法:當變量進入環境時,將變量標記"進入環境",當變量離開環境時,標記為:"離開環境"。某一個時刻,垃圾回收器會過濾掉環境中的變量,以及被環境變量引用的變量,剩下的就是被視為準備回收的變量。

到目前為止,IE、Firefox、Opera、Chrome、Safari的js實現使用的都是標記清除的垃圾回收策略或類似的策略,只不過垃圾收集的時間間隔互不相同。

引用計數:

定義和用法:引用計數是跟蹤記錄每個值被引用的次數。

基本原理:就是變量的引用次數,被引用一次則加1,當這個引用計數為0時,被視為準備回收的對象。

內存管理

1、什麼時候觸發垃圾回收?

垃圾回收器週期性運行,如果分配的內存非常多,那麼回收工作也會很艱鉅,確定垃圾回收時間間隔就變成了一個值得思考的問題。

IE6的垃圾回收是根據內存分配量運行的,當環境中的變量,對象,字符串達到一定數量時觸發垃圾回收。垃圾回收器一直處於工作狀態,嚴重影響瀏覽器性能。

IE7中,垃圾回收器會根據內存分配量與程序佔用內存的比例進行動態調整,開始回收工作。

2、合理的GC方案:(1)、遍歷所有可訪問的對象; (2)、回收已不可訪問的對象。

3、GC缺陷:(1)、停止響應其他操作;

4、GC優化策略:(1)、分代回收(Generation GC);(2)、增量GC

開發過程中遇到的內存洩露情況,如何解決的?

1、定義和用法:

內存洩露是指一塊被分配的內存既不能使用,又不能回收,直到瀏覽器進程結束。C#和Java等語言採用了自動垃圾回收方法管理內存,幾乎不會發生內存洩露。我們知道,瀏覽器中也是採用自動垃圾回收方法管理內存,但由於瀏覽器垃圾回收方法有bug,會產生內存洩露。

2、內存洩露的幾種情況:

(1)、當頁面中元素被移除或替換時,若元素綁定的事件仍沒被移除,在IE中不會作出恰當處理,此時要先手工移除事件,不然會存在內存洩露。

實例如下:




解決方法如下:




(2)、由於是函數內定義函數,並且內部函數--事件回調的引用外暴了,形成了閉包。閉包可以維持函數內局部變量,使其得不到釋放。

實例如下:

function bindEvent(){
var obj=document.createElement("XXX");
obj.onclick=function(){
//Even if it's a empty function
}
}

解決方法如下:

function bindEvent(){
var obj=document.createElement("XXX");
obj.onclick=function(){
//Even if it's a empty function
}
obj=null;
}

1、Array相關的屬性和方法

這裡只是做了相關的列舉,具體的使用方法,請參考網址。

40.Array 對象屬性

constructor 返回對創建此對象的數組函數的引用。

length 設置或返回數組中元素的數目。

prototype 使您有能力向對象添加屬性和方法。

Array 對象方法

concat() 連接兩個或更多的數組,並返回結果。

join() 把數組的所有元素放入一個字符串。元素通過指定的分隔符進行分隔。

pop() 刪除並返回數組的最後一個元素。

shift() 刪除並返回數組的第一個元素

push() 向數組的末尾添加一個或更多元素,並返回新的長度。

unshift() 向數組的開頭添加一個或更多元素,並返回新的長度。

reverse() 顛倒數組中元素的順序。

slice() 從某個已有的數組返回選定的元素

sort() 對數組的元素進行排序

splice() 刪除元素,並向數組添加新元素。

toSource() 返回該對象的源代碼。

toString() 把數組轉換為字符串,並返回結果。

toLocaleString() 把數組轉換為本地數組,並返回結果。

valueOf() 返回數組對象的原始值



分享到:


相關文章: