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, propertyIsEnumerable
Object.defineProperty(obj,prop,descriptor)
用來給物件定義屬性,有value
,writeable
,enumerable
,set/get
,configurable
,
hasOwnProperty
用於檢查某一屬性是不是存在於物件本身,
propertyIsEnumerable
用來檢測某一屬性是否可遍歷,也就是能不能用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;
}
複製程式碼