刷前端面經筆記(八)

大翰仔仔發表於2019-02-12

1.apply,call,bind有什麼區別?

三者都可以把一個函式應用到其他物件上,apply,call是直接執行函式呼叫,bind是繫結,執行需要再次呼叫。
applycall的區別是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,configurablehasOwnProperty用於檢查某一屬性是不是存在於物件本身, 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);
        }
    };&emsp;// 訂閱者

    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的理解

MVVMModel-View-ViewModel的縮寫。 Model代表資料模型,也可以在Model中定義資料修改和操作的業務邏輯。 View 代表UI 元件,它負責將資料模型轉化成UI 展現出來。 ViewModel監聽模型資料的改變和控制檢視行為、處理使用者互動, 簡單理解就是一個同步ViewModel的物件,連線ModelView。 在MVVM架構下,ViewModel之間並沒有直接的聯絡, 而是通過ViewModel進行互動,ModelViewModel之間的互動是雙向的, 因此View 資料的變化會同步到Model中,而Model 資料的變化也會立即反應到View 上。 ViewModel通過雙向資料繫結把 View 層和 Model 層連線了起來, 而ViewModel之間的同步工作完全是自動的,無需人為干涉, 因此開發者只需關注業務邏輯,不需要手動操作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特殊檔案格式,對檔案內容都有一些約定,兩者都需要編譯後使用。 中心思想相同:一切都是元件,元件例項之間可以巢狀。 都提供合理的鉤子函式,可以讓開發者定製化地去處理需求。 都不內建列數AJAXRoute等功能到核心包,而是以外掛的方式載入。 在元件開發中都支援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,因此加密的詳細內容就需要SSLhttps協議的主要作用是:建立一個資訊保安通道,來確保陣列的傳輸,確保網站的真實性。

17.git fetch和git pull的區別?

git pull:相當於是從遠端獲取最新版本並merge到本地 git fetch:相當於是從遠端獲取最新版本到本地,不會自動merge

18.介紹一下對瀏覽器核心的理解?

主要分成兩部分:渲染引擎(layout engineerRendering Engine)和JS引擎。 渲染引擎:負責取得網頁的內容(HTMLXML、影象等等)、 整理訊息(例如加入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;
}
複製程式碼

歡迎關注

相關文章