刷前端面經筆記(八)
1.apply,call,bind有什麼區別?
三者都可以把一個函式應用到其他物件上, apply
, call
是直接執行函式呼叫, bind
是繫結,執行需要再次呼叫。
apply
和 call
的區別是 apply
接受陣列作為引數,而 call
是接受逗號分隔的無限多個引數列表。
程式碼如下:
function Person() { } Person.prototype.sayName() { alert(this.name); } var obj = {name: 'michaelqin'}; // 注意這是一個普通物件,它不是Person的例項 // 1) apply Person.prototype.sayName.apply(obj, [param1, param2, param3]); // 2) call Person.prototype.sayName.call(obj, param1, param2, param3); // 3) bind var liaoke = Person.prototype.sayName.bind(obj); liaoke ([param1, param2, param3]); // bind需要先繫結,再執行 liaoke (param1, param2, param3); // bind需要先繫結,再執行
2.介紹一下defineProperty,hasOwnProperty,isEnumerable
Object.defineProperty(obj,prop,descriptor)
用來給物件定義屬性,有 value
, writeable
, enumerable
, set/get
, configurable
,
hasOwnProperty
用於檢查某一屬性是不是存在於物件本身,
isEnumerable
用來檢測某一屬性是否可遍歷,也就是能不能用 for...in
迴圈來取到。
3.JS常用設計模式的實現思路(單例、工廠、代理、裝飾、觀察者模式等)
// 1) 單例: 任意物件都是單例,無須特別處理 var obj = {name: 'michaelqin', age: 30}; // 2) 工廠: 就是同樣形式引數返回不同的例項 function Person() { this.name = 'Person1'; } function Animal() { this.name = 'Animal1'; } function Factory() {} Factory.prototype.getInstance = function(className) { return eval('new ' + className + '()'); } var factory = new Factory(); var obj1 = factory.getInstance('Person'); var obj2 = factory.getInstance('Animal'); console.log(obj1.name); // Person1 console.log(obj2.name); // Animal1 // 3) 代理: 就是新建個類呼叫老類的介面,包一下 function Person() { } Person.prototype.sayName = function() { console.log('michaelqin'); } Person.prototype.sayAge = function() { console.log(30); } function PersonProxy() { this.person = new Person(); var that = this; this.callMethod = function(functionName) { console.log('before proxy:', functionName); that.person[functionName](); // 代理 console.log('after proxy:', functionName); } } var pp = new PersonProxy(); pp.callMethod('sayName'); // 代理呼叫Person的方法sayName() pp.callMethod('sayAge'); // 代理呼叫Person的方法sayAge() // 4) 觀察者: 就是事件模式,比如按鈕的onclick這樣的應用. function Publisher() { this.listeners = []; } Publisher.prototype = { 'addListener': function(listener) { this.listeners.push(listener); }, 'removeListener': function(listener) { delete this.listeners[listener]; }, 'notify': function(obj) { for(var i = 0; i < this.listeners.length; i++) { var listener = this.listeners[i]; if (typeof listener !== 'undefined') { listener.process(obj); } } } }; // 釋出者 function Subscriber() { } Subscriber.prototype = { 'process': function(obj) { console.log(obj); } }; // 訂閱者 var publisher = new Publisher(); publisher.addListener(new Subscriber()); publisher.addListener(new Subscriber()); publisher.notify({name: 'michaelqin', ageo: 30}); // 釋出一個物件到所有訂閱者 publisher.notify('2 subscribers will both perform process'); // 釋出一個字串到所有訂閱者
3.處理字串常用的十個函式
charAt()// 返回在指定位置的字元。 concat()// 連線字串。 fromCharCode()// 從字元編碼建立一個字串。 indexOf()// 檢索字串。 match()// 找到一個或多個正則表示式的匹配。 replace()// 替換與正則表示式匹配的子串。 search()// 檢索與正則表示式相匹配的值。 slice()// 提取字串的片斷,並在新的字串中返回被提取的部分。 split()// 把字串分割為字串陣列。 substr()// 從起始索引號提取字串中指定數目的字元。 substring()// 提取字串中兩個指定的索引號之間的字元。 toLocaleLowerCase()// 把字串轉換為小寫。 toLocaleUpperCase()// 把字串轉換為大寫。 toLowerCase()// 把字串轉換為小寫。 toUpperCase()// 把字串轉換為大寫。 toString()// 返回字串。 valueOf()// 返回某個字串物件的原始值。
4.如何判斷一個變數是物件還是陣列
function isObjArr(variable){ if (Object.prototype.toString.call(value) === "[object Array]") { console.log('value是陣列'); }else if(Object.prototype.toString.call(value)==='[object Object]'){//這個方法相容性好一點 console.log('value是物件'); }else{ console.log('value不是陣列也不是物件') } } // 注意:千萬不能使用typeof來判斷物件和陣列,因為這兩種型別都會返回"object"。
5.ES5的繼承和ES6的繼承有什麼區別?
ES5
的繼承是通過 prototype
或建構函式機制來實現。
ES5
的繼承實質上是先建立子類的例項物件,然後再將父類的方法新增到 this
上( Parent.apply(this)
)。
ES6
的繼承機制實質上是先建立父類的例項物件 this
(所以必須先呼叫父類的 super()
方法),然後再用子類的建構函式修改 this
。具體為 ES6
通過 class
關鍵字定義類,裡面有構造方法,類之間通過 extends
關鍵字實現繼承。子類必須在 constructor
方法中呼叫 super
方法,否則新建例項報錯。因為子類沒有自己的 this
物件,而是繼承了父類的 this
物件,然後對其呼叫。如果不呼叫 super
方法,子類得不到 this
物件。
注意:super關鍵字指代父類的例項,即父類的this物件。在子類建構函式中,呼叫super後,才可使用this關鍵字,否則報錯。
6.下面的ul,如何點選每一列的時候alert其index?(閉包)
<ul id="test"> <li>這是第一條</li> <li>這是第二條</li> <li>這是第三條</li> </ul> // 方法一: var lis=document.getElementById('test').getElementsByTagName('li'); for(var i=0;i<3;i++) { lis[i].index=i; lis[i].onclick=function(){ alert(this.index); }; } //方法二: var lis=document.getElementById('test').getElementsByTagName('li'); for(var i=0;i<3;i++) { lis[i].index=i; lis[i].onclick=(function(a){ return function() { alert(a); } })(i); }
7.對於MVVM的理解
MVVM
是 Model-View-ViewModel
的縮寫。
Model
代表資料模型,也可以在 Model
中定義資料修改和操作的業務邏輯。
View
代表 UI
元件,它負責將資料模型轉化成 UI
展現出來。
ViewModel
監聽模型資料的改變和控制檢視行為、處理使用者互動,
簡單理解就是一個同步 View
和 Model
的物件,連線 Model
和 View
。
在 MVVM
架構下, View
和 Model
之間並沒有直接的聯絡,
而是通過 ViewModel
進行互動, Model
和 ViewModel
之間的互動是雙向的,
因此 View
資料的變化會同步到 Model
中,而 Model
資料的變化也會立即反應到 View
上。
ViewModel
通過雙向資料繫結把 View
層和 Model
層連線了起來,
而 View
和 Model
之間的同步工作完全是自動的,無需人為干涉,
因此開發者只需關注業務邏輯,不需要手動操作 DOM
,
不需要關注資料狀態的同步問題,
複雜的資料狀態維護完全由 MVVM
來統一管理。
8.解釋Vue的生命週期
Vue
例項從建立到銷燬的過程,就是生命週期。從開始建立、初始化資料、編譯模板、掛載Dom->渲染、更新->渲染、銷燬等一系列過程,稱之為 Vue
的生命週期。
Vue
的生命週期包括:
beforeCreate
(建立前)在資料觀測和初始化事件還未開始,
created
(建立後)完成資料觀測,屬性和方法的運算,初始化事件, $el
屬性還沒有顯示出來;
beforeMount(
載入前)在掛載開始之前被呼叫,相關的 render
函式首次被呼叫,例項已完成以下的配置:編譯模板,把 data
裡面的資料和模板生成 html
,注意此時還沒有掛載 html
到頁面上;
mounted
(載入後)在 el
被新建立的 vm.$el
替換,並掛載到例項上去之後呼叫,例項已完成以下配置:用上面編譯好的 html
內容替換 el
屬性指向的 DOM
物件,完成模板中的 html
渲染到 html
頁面中,此過程中進行 ajax
互動。
beforeUpdate
(更新前)在資料更新之前呼叫,發生在虛擬 DOM
重新渲染和打補丁之前,可以在該鉤子中進一步地更改狀態,不會觸發附加的重渲染過程。
updated
(更新後)在由於資料更改導致的虛擬 DOM
重新渲染和打補丁之後呼叫。呼叫時,元件 DOM
已經更新,所以可以執行依賴於 DOM
的操作。然而在大多數情況下,應該避免在此期間更改狀態,因為這可能會導致更新無限迴圈,該鉤子在伺服器渲染期間不被呼叫。
beforeDestroy
(銷燬前)在例項銷燬之前呼叫,例項仍然完全可用。
destroyed
(銷燬後)在例項銷燬之後呼叫。呼叫後,所有的事件監聽器會被移除,所有的子例項也會被銷燬。該鉤子在伺服器端渲染期間不被呼叫。
9.為什麼使用Node.js,它有哪些優缺點?
優點:
事件驅動,通過閉包很容易實現客戶端的生命活期。
不用擔心多執行緒,鎖,平行計算的問題
V8
引擎速度非常快
對於遊戲來說,寫一遍遊戲邏輯程式碼,前端後端通用
缺點:
node.js
更新很快,可能會出現版本相容
node.js
還不算成熟,還沒有大製作
node.js
不像其他的伺服器,對於不同的連結,不支援程序和執行緒操作
10.什麼是錯誤優先的回撥函式?
錯誤優先( Error-first
)的回撥函式( Error-First Callback
)用於同時返回錯誤和資料。
第一個引數返回錯誤,並且驗證它是否出錯;其他引數返回資料。
fs.readFile(filePath, function(err, data) { if (err) { // 處理錯誤 return console.log(err); } console.log(data); });
11.使用npm有哪些好處?
通過 npm
,你可以安裝和管理專案的依賴,
並且能夠指明依賴項的具體版本號。
對於 Node
應用開發而言,
可以通過 package.json
檔案來管理專案資訊,
配置指令碼,以及指明依賴的具體版本。
12.在JavaScript原始檔的開頭包含 use strict 有什麼意義和好處?
use strict
是一種在 JavaScript
程式碼執行時自動實行更嚴格解析和錯誤處理的方法。(嚴格模式)
將值分配給一個未宣告的變數會自動建立該名稱的全域性變數。這是 JavaScript
中最常見的錯誤之一。在嚴格模式下,這樣做的話會丟擲錯誤。消除 this
強制。
當檢測到物件(例如, var object = {foo: "bar", foo: "baz"};
)中重複命名的屬性,或檢測到函式中(例如, function foo(val1, val2, val1){}
)重複命名的引數時,嚴格模式會丟擲錯誤,因此捕捉幾乎可以肯定是程式碼中的 bug
可以避免浪費大量的跟蹤時間。比 eval()
更安全。
13.vuejs與angularjs以及react的區別?
與 AngularJS
的區別
相同點:
都支援指令:內建指令和自定義指令。
都支援過濾器:內建過濾器和自定義過濾器。
都支援雙向資料繫結。
都不支援低端瀏覽器。
不同點:
1. AngularJS
的學習成本高,比如增加了 Dependency Injection
特性,而 Vue.js
本身提供的 API
都比較簡單、直觀。
2.在效能上, AngularJS
依賴對資料做髒檢查,所以 Watcher
越多越慢。
Vue.js
使用基於依賴追蹤的觀察並且使用非同步佇列更新。所有的資料都是獨立觸發的。
對於龐大的應用來說,這個優化差異還是比較明顯的。
與 React
的區別
相同點:
React
採用特殊的 JSX
語法, Vue.js
在元件開發中也推崇編寫 .vue
特殊檔案格式,對檔案內容都有一些約定,兩者都需要編譯後使用。
中心思想相同:一切都是元件,元件例項之間可以巢狀。
都提供合理的鉤子函式,可以讓開發者定製化地去處理需求。
都不內建列數 AJAX
, Route
等功能到核心包,而是以外掛的方式載入。
在元件開發中都支援 mixins
的特性。
不同點:
React
依賴 Virtual DOM
,而 Vue.js
使用的是 DOM
模板。 React
採用的 Virtual DOM
會對渲染出來的結果做髒檢查。
Vue.js
在模板中提供了指令,過濾器等,可以非常方便,快捷地操作 Virtual DOM
。
14.標籤keep-alive的作用是什麼?
<keep-alive></keep-alive>
包裹動態元件時,會快取不活動的元件例項,主要用於保留元件狀態或避免重新渲染。
15.WeakMap 和 Map 的區別?
WeakMap
結構與 Map
結構基本類似,唯一的區別是它只接受物件作為鍵名( null
除外),不接受其他型別的值作為鍵名,而且鍵名所指向的物件,不計入垃圾回收機制。
WeakMap
最大的好處是可以避免記憶體洩漏。一個僅被 WeakMap
作為 key
而引用的物件,會被垃圾回收器回收掉。
WeakMap
擁有和 Map
類似的 set(key, value)
、 get(key)
、 has(key)
、 delete(key)
和 clear()
方法, 沒有任何與迭代有關的屬性和方法。
16.http和https的基本概念?
http
: 超文字傳輸協議,是網際網路上應用最為廣泛的一種網路協議,是一個客戶端和伺服器端請求和應答的標準( TCP
),用於從 WWW
伺服器傳輸超文字到本地瀏覽器的傳輸協議,它可以使瀏覽器更加高效,使網路傳輸減少。
https
: 是以安全為目標的 HTTP
通道,簡單講是 HTTP
的安全版,即 HTTP
下加入 SSL
層, HTTPS
的安全基礎是 SSL
,因此加密的詳細內容就需要 SSL
。
https
協議的主要作用是:建立一個資訊保安通道,來確保陣列的傳輸,確保網站的真實性。
17.git fetch和git pull的區別?
git pull
:相當於是從遠端獲取最新版本並 merge
到本地
git fetch
:相當於是從遠端獲取最新版本到本地,不會自動 merge
18.介紹一下對瀏覽器核心的理解?
主要分成兩部分:渲染引擎( layout engineer
或 Rendering Engine
)和 JS
引擎。
渲染引擎:負責取得網頁的內容( HTML
、 XML
、影象等等)、
整理訊息(例如加入 CSS
等),以及計算網頁的顯示方式,然後會輸出至顯示器或印表機。
瀏覽器的核心的不同對於網頁的語法解釋會有不同,所以渲染的效果也不相同。
所有網頁瀏覽器、電子郵件客戶端以及其它需要編輯、顯示網路內容的應用程式都需要核心。
JS
引擎:解析和執行 javascript
來實現網頁的動態效果。
最開始渲染引擎和 JS
引擎並沒有區分的很明確,後來 JS
引擎越來越獨立,核心就傾向於只指渲染引擎。
19.什麼是微格式
微格式( Microformats
)是一種讓機器可讀的語義化 XHTML
詞彙的集合,是結構化資料的開放標準。
是為特殊應用而制定的特殊格式
優點:將智慧資料新增到網頁上,讓網站內容在搜尋引擎結果介面可以顯示額外的提示。
20.資料繫結基本的實現
// 實現一個方法,可以給 obj 所有的屬性新增動態繫結事件,當屬性值發生變化時會觸發事件 let obj = { key_1: 1, key_2: 2 } function func(key) { console.log(key + ' 的值發生改變:' + this[key]); } bindData(obj, func); obj.key_1 = 2; // 此時自動輸出 "key_1 的值發生改變:2" obj.key_2 = 1; // 此時自動輸出 "key_2 的值發生改變:1"
答案:
function bindData(obj, fn) { for (let key in obj) { Object.defineProperty(obj, key, { set(newVal) { if (this.value !== newVal) { this.value = newVal; fn.call(obj, key); } }, get() { return this.value; } }) } }
20.資料結構處理
// 有一個祖先樹狀 json 物件,當一個人有一個兒子的時候,其 child 為其兒子物件,如果有多個兒子,child 為兒子物件的陣列。 請實現一個函式,找出這個家族中所有有多個兒子的人的名字(name),輸出一個數組。 列子: // 樣例資料 let data = { name: 'jack', child: [ { name: 'jack1' }, { name: 'jack2', child: [{ name: 'jack2-1', child: { name: 'jack2-1-1' } }, { name: 'jack2-2' }] }, { name: 'jack3', child: { name: 'jack3-1' } } ] } // 答案: // 用遞迴 function findMultiChildPerson(data) { let nameList = []; function tmp(data) { if (data.hasOwnProperty('child')) { if (Array.isArray(data.child)) { nameList.push(data.name); data.child.forEach(child => tmp(child)); } else { tmp(data.child); } } } tmp(data); return nameList; } // 不用遞迴 function findMultiChildPerson(data) { let list = [data]; let nameList = []; while (list.length > 0) { const obj = list.shift(); if (obj.hasOwnProperty('child')) { if (Array.isArray(obj.child)) { nameList.push(obj.name); list = list.concat(obj.child); } else { list.push(obj.child); } } } return nameList; }