前端工程師自檢清單73答

開篇

本文參考文章《一名【合格】前端工程師的自檢清單》, 並對其中的部分題目進行了解答,如有遺漏或錯誤之處望你們指出糾正,共同進步。(點擊題目展開答案!) javascript

此文章 Markdown 源文件地址:github.com/zxpsuper/bl…css

1、JavaScript基礎

前端工程師吃飯的傢伙,深度、廣度同樣都不能差。html

變量和類型

1. JavaScript 規定了幾種語言類型?

JavaScript中的每個值都有它本身的類型,JavaScript規定了七種語言類型,他們是:前端

Undefined Null Boolean String Number Symbol Objectvue

2. JavaScript 對象的底層數據結構是什麼?

對象數據被存儲於堆中 (如對象、數組、函數等,它們是經過拷貝和new出來的)。html5

引用類型的數據的地址指針是存儲於棧中的,當咱們想要訪問引用類型的值的時候,須要先從棧中得到對象的地址指針,而後,在經過地址指針找到堆中的所須要的數據。java

3. Symbol 類型在實際開發中的應用、可手動實現一個簡單的 Symbol?

ES6 引入了一種新的原始數據類型 Symbol,表示獨一無二的值。node

symbol類型的 key 不能被 Object.keysfor..of 循環枚舉。所以可看成私有變量使用。react

let mySymbol = Symbol('key');
// 第一種寫法
let a = {};
a[mySymbol] = 'Hello!';
// 第二種寫法
let a = {
  [mySymbol]: 'Hello!'
};
複製代碼
4. JavaScript 中的變量在內存中的具體存儲形式

JavaScript 中的變量分爲基本類型和引用類型:webpack

基本類型: 保存在棧內存中的簡單數據段,它們的值都有固定的大小,保存在棧空間,經過按值訪問

引用類型: 保存在堆內存中的對象,值大小不固定,棧內存中存放的該對象的訪問地址指向堆內存中的對象,JavaScript 不容許直接訪問堆內存中的位置,所以操做對象時,實際操做對象的引用

5. 基本類型對應的內置對象,以及他們之間的裝箱拆箱操做

String(), Number(), Boolean()

裝箱:就是把基本類型轉變爲對應的對象。裝箱分爲隱式和顯示

// 隱式裝箱: 每當讀取一個基本類型的值時,後臺會建立一個該基本類型所對應的對象。
  // 在這個基本類型上調用方法,實際上是在這個基本類型對象上調用方法。
  // 這個基本類型的對象是臨時的,它只存在於方法調用那一行代碼執行的瞬間,執行方法後馬上被銷燬。
  let num=123;
  num.toFixed(2); // '123.00'//上方代碼在後臺的真正步驟爲
  var c = new Number(123);
  c.toFixed(2);
  c = null;
  // 顯式裝箱: 經過內置對象 Boolean、Object、String 等能夠對基本類型進行顯示裝箱。
  var obj = new String('123');
複製代碼

拆箱: 拆箱與裝箱相反,把對象轉變爲基本類型的值。

Number([1]); //1
  // 轉換演變:
  [1].valueOf(); // [1];
  [1].toString(); // '1';Number('1'); //1 
複製代碼
6. 理解值類型和引用類型

JavaScript中的變量分爲基本類型和引用類型:

基本類型: 保存在棧內存中的簡單數據段,它們的值都有固定的大小,保存在棧空間,經過按值訪問

引用類型: 保存在堆內存中的對象,值大小不固定,棧內存中存放的該對象的訪問地址指向堆內存中的對象,JavaScript 不容許直接訪問堆內存中的位置,所以操做對象時,實際操做對象的引用

7. null 和 undefined 的區別
  1. Number 轉換的值不一樣,Number(null) 輸出爲 0, Number(undefined) 輸出爲 NaN

  2. null 表示一個值被定義了,可是這個值是空值

  3. undefined 表示缺乏值,即此處應該有值,可是尚未定義

8. 至少能夠說出三種判斷 JavaScript 數據類型的方式,以及他們的優缺點,如何準確的判斷數組類型
  1. typeof —— 返回給定變量的數據類型,可能返回以下字符串:
'undefined'——Undefined
  'boolean'——Boolean
  'string'——String
  'number'——Number
  'symbol'——Symbol
  'object'——Object / Null (Null 爲空對象的引用)
  'function'——Function
  // 對於一些如 error() date() array()沒法判斷,都是顯示object類型
複製代碼
  1. instanceof 檢測 constructor.prototype 是否存在於參數 object 的原型鏈上,是則返回 true,不是則返回 false
alert([1,2,3] instanceof Array) // true
  alert(new Date() instanceof Date) // true
  alert(function(){this.name="22";} instanceof Function) //true
  alert(function(){this.name="22";} instanceof function) //false // instanceof 只能用來判斷兩個對象是否屬於實例關係,而不能判斷一個對象實例具體屬於哪一種類型。 複製代碼
  1. constructor —— 返回對象對應的構造函數。
alert({}.constructor === Object);  =>  true
  alert([].constructor === Array);  =>  true
  alert('abcde'.constructor === String);  =>  true
  alert((1).constructor === Number);  =>  true
  alert(true.constructor === Boolean);  =>  true
  alert(false.constructor === Boolean);  =>  true
  alert(function s(){}.constructor === Function);  =>  true
  alert(new Date().constructor === Date);  =>  true
  alert(new Array().constructor === Array);  =>  true
  alert(new Error().constructor === Error);  =>  true
  alert(document.constructor === HTMLDocument);  =>  true
  alert(window.constructor === Window);  =>  true
  alert(Symbol().constructor);    =>    undefined 
  // null 和 undefined 是無效的對象,沒有 constructor,所以沒法經過這種方式來判斷。
複製代碼
  1. Object.prototype.toString() 默認返回當前對象的 [[Class]] 。這是一個內部屬性,其格式爲 [object Xxx] ,是一個字符串,其中 Xxx 就是對象的類型。
Object.prototype.toString.call(new Date);//[object Date]
  Object.prototype.toString.call(new String);//[object String]
  Object.prototype.toString.call(Math);//[object Math]
  Object.prototype.toString.call(undefined);//[object Undefined]
  Object.prototype.toString.call(null);//[object Null]
  Object.prototype.toString.call('') ;   // [object String]
  Object.prototype.toString.call(123) ;    // [object Number]
  Object.prototype.toString.call(true) ; // [object Boolean]
  Object.prototype.toString.call(Symbol()); //[object Symbol]
  Object.prototype.toString.call(new Function()) ; // [object Function]
  Object.prototype.toString.call(new Date()) ; // [object Date]
  Object.prototype.toString.call([]) ; // [object Array]
  Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
  Object.prototype.toString.call(new Error()) ; // [object Error]
  Object.prototype.toString.call(document) ; // [object HTMLDocument]
  Object.prototype.toString.call(window) ; //[object global] window 是全局對象 global 的引用
  // 比較全面
複製代碼
9. 可能發生隱式類型轉換的場景以及轉換原則,應如何避免或巧妙應用

隱式轉換通常說的是 Boolean 的轉換

if 語句中,null""undefinded, 0, false 都會被轉化爲 false

通常應用於對接口數據判空時使用

10. 出現小數精度丟失的緣由,JavaScript 能夠存儲的最大數字、最大安全數字,JavaScript處理大數字的方法、避免精度丟失的方法
  • 精度丟失緣由,說是 JavaScript 使用了 IEEE 754 規範,二進制儲存十進制的小數時不能完整的表示小數

  • 可以表示的最大數字 Number.MAX_VALUE 等於 1.7976931348623157e+308 ,最大安全數字 Number.MAX_SAFE_INTEGER 等於 9007199254740991

  • 避免精度丟失

    • 計算小數時,先乘 1001000,變成整數再運算
    • 若是值超出了安全整數,有一個最新提案,BigInt 大整數,它能夠表示任意大小的整數,注意只能表示整數,而不受安全整數的限制

原型和原型鏈

1. 理解原型設計模式以及 JavaScript 中的原型規則
A. 全部的引用類型(數組、對象、函數),都具備對象特性,便可自由擴展屬性;
B. 全部的引用類型(數組、對象、函數),都有一個`__proto__`屬性(隱式原型),屬性值是一個普通的對象;
C. 全部的函數,都具備一個 `prototype`(顯式原型),屬性值也是一個普通對象;
D. 全部的引用類型(數組、對象、函數),其隱式原型指向其構造函數的顯式原型;`(obj._proto_ === Object.prototype)`;
E. 當試圖獲得一個對象的某個屬性時,若是這個對象自己沒有這個屬性,那麼會去它的 `__proto__` (即它的構造函數的 `prototype`)中去尋找;
複製代碼
2. instanceof 的底層實現原理,手動實現一個 instanceof

簡單說就是判斷實例對象的__proto__是否是強等於對象的prototype屬性,若是不是繼續往原型鏈上找,直到 __proto__null 爲止。

function instanceOf(obj, object) {//obj 表示實例對象,object 表示對象
  var O = object.prototype;
  obj = obj.__proto__;
  while (true) { 
      if (obj === null) 
          return false; 
      if (O === obj) // 這裏重點:當 O 嚴格等於 obj 時,返回 true 
          return true; 
      obj = obj.__proto__; 
  } 
}
複製代碼
3. 理解 JavaScript 的執行上下文棧,能夠應用堆棧信息快速定位問題

執行上下文 就是當前 JavaScript 代碼被解析和執行時所在環境的抽象概念, JavaScript 中運行任何的代碼都是在執行上下文中運行。

執行上下文總共有三種類型:全局執行上下文, 函數執行上下文, Eval 函數執行上下文

執行棧,在其餘編程語言中也被叫作調用棧,具備 LIFO(後進先出)結構,用於存儲在代碼執行期間建立的全部執行上下文。

4. 實現繼承的幾種方式以及他們的優缺點

詳情請點擊:《繼承的幾種實現方式》

5. 能夠描述 new 一個對象的詳細過程,手動實現一個 new 操做符
  • new一個對象的詳細過程:
function Test() {}
const test = new Test();
複製代碼
  1. 建立一個對象 const obj = {}

  2. 設置新對象的 constructor 屬性爲構造函數的名稱,設置新對象的__proto__屬性指向構造函數的 prototype 對象

obj.constructor = Test;
obj.__proto__ = Test.prototype;
複製代碼
  1. 使用新對象調用函數,函數中的 this 被指向新實例對象 Test.call(obj)

  2. 將初始化完畢的新對象地址,保存到等號左邊的變量中

  • 實現一個new操做符
function myNew(Obj,...args){
    var obj = Object.create(Obj.prototype);//使用指定的原型對象及其屬性去建立一個新的對象
    Obj.apply(obj,args); // 綁定 this 到obj, 設置 obj 的屬性
    return obj; // 返回實例
}
複製代碼
6. 理解 es6 class 構造以及繼承的底層實現原理
  • ES6 類的底層仍是經過構造函數去建立的。
// es6 Parent類實現
class Parent {
  constructor(name,age){
      this.name = name;
      this.age = age;
  }
  speakSomething(){
      console.log("I can speek chinese");
  }
}
// 轉化爲
var Parent = function () {
  function Parent(name, age) {
      _classCallCheck(this, Parent); // 判斷實例 Parent instanceof Parent(函數)是否爲true

      this.name = name;
      this.age = age;
  }
  // 此方法經過使用 Object.defineProperty 爲 function Parent 的 prototype 添加屬性值
  _createClass(Parent, [{
      key: "speakSomething",
      value: function speakSomething() {
          console.log("I can speek chinese");
      }
  }]);

  return Parent;
}();
複製代碼
  • ES6 的繼承實現
//定義子類,繼承父類
class Child extends Parent {
  static width = 18
  constructor(name,age){
      super(name,age);
  }
  coding(){
      console.log("I can code JS");
  }
}
// 轉化爲
var Child = function (_Parent) {
  _inherits(Child, _Parent);

  function Child(name, age) {
      _classCallCheck(this, Child);

      return _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).call(this, name, age));
  }

  _createClass(Child, [{
      key: "coding",
      value: function coding() {
          console.log("I can code JS");
      }
  }]);

  return Child;
}(Parent);
複製代碼

這裏其實就是多了一個 _inherits(Child, _Parent); 方法,實現瞭如下功能,具體可看文章《ES6類以及繼承的實現原理》

//實現的結果是:
  subClass.prototype.__proto__ = superClass.prototype
  subClass.__proto__ = superClass // 實現靜態屬性的繼承
複製代碼

做用域和閉包

1. 理解詞法做用域和動態做用域

詞法做用域也稱靜態做用域,javascript 採用靜態做用域

靜態做用域 —— 函數的做用域基於函數建立的位置。

動態做用域 —— 函數的做用域基於函數的使用位置。

var value = 1;

function foo() {
  console.log(value);
}

function bar() {
  var value = 2;
  foo();
}

bar(); // 輸出 1 。JavaScript 採用的是詞法做用域,也稱爲靜態做用域。相同的,動態做用域此代碼應該輸出 2
複製代碼
2. 理解 JavaScript 的做用域和做用域鏈

做用域(scope)就是變量訪問規則的有效範圍。

JavaScript 中全局變量的做用域是全局的,在代碼的任何地方都是有定義的。然而函數的參數和局部變量只在函數體內有定義。另外局部變量的優先級要高於同名的全局變量,也就是說當局部變量與全局變量重名時,局部變量會覆蓋全局變量。

3. this的原理以及幾種不一樣使用場景的取值

this的幾種不一樣使用場景的取值 + JavaScript 的 this 原理

4. 閉包的實現原理和做用,能夠列舉幾個開發中閉包的實際應用

原理:閉包就是可以讀取其餘函數內部變量的函數。因爲在Javascript語言中,只有函數內部的子函數才能讀取局部變量,所以能夠把閉包簡單理解成"定義在一個函數內部的函數"。

因此,在本質上,閉包就是將函數內部和函數外部鏈接起來的一座橋樑。

做用:閉包能夠用在許多地方。它的最大用處有兩個,一個是前面提到的能夠讀取函數內部的變量,另外一個就是讓這些變量的值始終保持在內存中。

應用:1. 匿名自執行函數 2. 結果緩存 3. 封裝局部變量

參考連接:《學習Javascript閉包(Closure)》

5. 理解堆棧溢出和內存泄漏的原理,如何防止

堆棧溢出 的產生是因爲過多的函數調用,致使調用堆棧沒法容納這些調用的返回地址,通常在遞歸中產生。堆棧溢出極可能由無限遞歸(Infinite recursion)產生,但也可能僅僅是過多的堆棧層級.

參考連接:《內存泄漏與避免》

6. 如何處理循環的異步操做
  1. 將異步操做變同步,使用 async/await.
  2. 去掉循環,將循環變成遞歸

執行機制

1. 爲什麼 try 裏面放 return,finally 還會執行,理解其內部機制

try 語句中,在執行 return 語句時,要返回的結果已經準備好了,就在此時,程序轉到 finally 執行了。

在轉去以前,try 中先把要返回的結果存放到局部變量中去,執行完 finally 以後,在從中取出返回結果。

所以,即便finally 中對返回的結果進行了改變,可是不會影響返回結果。

它應該使用棧保存返回值。

2. JavaScript 如何實現異步編程,能夠詳細描述 EventLoop 機制

JavaScript 如何實現異步編程:

  1. callback (回調函數) 回調函數表明着,當某個任務處理完,而後須要作的事。好比讀取文件,鏈接數據庫,等文件準備好,或數據庫鏈接成功執行編寫的回調函數,又好比像一些動畫處理,當動畫走完,而後執行回調。

  2. 發佈訂閱模式 顧名思義,即是先訂閱了事件,有人一發布事件你就知道了,接着執行後面的操做。

  3. Promise Promise,簡單說就是一個容器,裏面保存着某個將來纔會結束的事件的結果,相比回調函數,Promise 提供統一的 API,各類異步操做均可以用一樣的方法進行處理。

  4. Generator (生成器)函數 Generator 函數是 ES6 提供的一種異步編程解決方案,其行爲相似於狀態機。

  5. async/await async/await 本質上仍是基於 Generator 函數,能夠說是 Generator 函數的語法糖,async 就至關於以前寫的run函數(執行Generator函數的函數),而 await 就至關於 yield ,只不過 await 表達式後面只能跟着 Promise 對象,若是不是 Promise 對象的話,會經過 Promise.resolve 方法使之變成 Promise 對象。async 修飾 function,其返回一個 Promise 對象。

《瀏覽器 Event Loop 機制》

3. 宏任務和微任務分別有哪些

宏任務: setTimeout,setInterval,setImmediate (Node獨有),requestAnimationFrame (瀏覽器獨有),I/O,UI rendering (瀏覽器獨有)

微任務: process.nextTick (Node獨有),Promise,Object.observe,MutationObserver

4. 能夠快速分析一個複雜的異步嵌套邏輯,並掌握分析方法
// 執行順序,先微隊列,後宏隊列。
console.log(1);
setTimeout(() => {
  console.log(2);
  setTimeout(() => {
    console.log(8);
  })
  Promise.resolve().then(() => {
    console.log(3)
  });
});
new Promise((resolve, reject) => {
  console.log(4)
  setTimeout(() => {
    console.log(10);
  })
  resolve()
}).then(() => {
  console.log(5);
  Promise.resolve().then(() => {
    console.log(11)
  });
  setTimeout(() => {
    console.log(13);
  })
})
setTimeout(() => {
  Promise.resolve().then(() => {
    console.log(9)
  });
  console.log(6);
  setTimeout(() => {
    console.log(12);
  })
})
console.log(7);
複製代碼

從頭到尾執行一次代碼,根據上面分類規則分至不一樣隊列, new promise( function ) 也是當即執行。setTimeout 的回調函數屬於宏隊列(macrotask)resolve 的回調函數屬於微隊列

// 棧區(stack)
console.log(1);
console.log(4);
console.log(7);
複製代碼
// 宏隊列
() => {
  console.log(2);
  setTimeout(() => {
    console.log(8);
  })
  Promise.resolve().then(() => {
    console.log(3)
  });
}
() => {
  console.log(10);
}
() => {
  Promise.resolve().then(() => {
    console.log(9)
  });
  console.log(6);
  setTimeout(() => {
    console.log(12);
  })
}
複製代碼
// 微隊列
() => {
  console.log(5);
  Promise.resolve().then(() => {
    console.log(11)
  });
  setTimeout(() => {
    console.log(13);
  })
}
複製代碼

優先執行微隊列,微隊列執行過程當中產生的微隊列和宏隊列置於隊列末尾排序執行,而宏隊列產生的微隊列和宏隊列於新的隊列中等待。。

執行微隊列:(分類)

// 棧區(stack)
console.log(1);
console.log(4);
console.log(7);
//////////
console.log(5);
複製代碼
// 微隊列
() => {
  console.log(11)
});
複製代碼
// 宏隊列
() => {
  console.log(2);
  setTimeout(() => {
    console.log(8);
  })
  Promise.resolve().then(() => {
    console.log(3)
  });
}
() => {
  console.log(10);
}
() => {
  Promise.resolve().then(() => {
    console.log(9)
  });
  console.log(6);
  setTimeout(() => {
    console.log(12);
  })
}
() => {
    console.log(13);
}
複製代碼

此時新增了一個微隊列console.log(11),由於是微隊列產生的,繼續執行:

// 棧區(stack)
console.log(1);
console.log(4);
console.log(7);
//////////
console.log(5);
/////////
console.log(11)
複製代碼
// 微隊列-空
複製代碼
// 宏隊列
() => {
  console.log(2);
  setTimeout(() => {
    console.log(8);
  })
  Promise.resolve().then(() => {
    console.log(3)
  });
}
() => {
  console.log(10);
}
() => {
  Promise.resolve().then(() => {
    console.log(9)
  });
  console.log(6);
  setTimeout(() => {
    console.log(12);
  })
}
() => {
    console.log(13);
}
複製代碼

執行完微隊列後執行宏隊列:

// 棧區(stack)
console.log(1);
console.log(4);
console.log(7);
//////////
console.log(5);
/////////
console.log(11);
/////////
console.log(2);
console.log(10);
console.log(6);
console.log(13);
複製代碼
// 微隊列
() => {
  console.log(3)
}
() => {
  console.log(9)
}
複製代碼
// 宏隊列
() => {
  console.log(8);
}
() => {
  console.log(12);
}
複製代碼

接下來執行微隊列後宏隊列,即:

// 棧區(stack)
console.log(1);
console.log(4);
console.log(7);
//////////
console.log(5);
/////////
console.log(11);
/////////
console.log(2);
console.log(10);
console.log(6);
console.log(13);
////////
console.log(3)
console.log(9)
////////
console.log(8);
console.log(12);
複製代碼
5. 使用 Promise 實現串行
// 一個 promise 的 function
function delay(time) {
 return new Promise((resolve, reject) => {
   console.log(`wait ${time}s`)
   setTimeout(() => {
     console.log('execute');
     resolve()
   }, time * 1000)
 })
}
const arr = [3, 4, 5];
複製代碼
  1. reduce
arr.reduce((s, v) => {
 return s.then(() => delay(v))
}, Promise.resolve())
複製代碼
  1. async + 循環 + await
(
 async function () {
   for (const v of arr) {
     await delay(v)
   }
 }
)()
複製代碼
  1. 普通循環
let p = Promise.resolve()
for (const i of arr) {
 p = p.then(() => delay(i))
}
複製代碼
  1. 遞歸
function dispatch(i, p = Promise.resolve()) {
 if (!arr[i]) return Promise.resolve()
 return p.then(() => dispatch(i + 1, delay(arr[i])))
}
dispatch(0)
複製代碼
6. Node 與瀏覽器 EventLoop 的差別

《JavaScript 運行機制詳解:再談Event Loop》

《帶你完全弄懂Event Loop》

7. 如何解決頁面加載海量數據而頁面不卡頓
  1. 分治思想,在必定的時間內屢次加載數據,直至渲染完成,使用 window.requestAnimationFramedocument.createDocumentFragment() 實現, 可參考文章【如何解決頁面加載海量數據而不凍結前端UI】
  2. 局部顯示,畢竟用戶能看到的就一屏內容,監聽用戶的滾動行爲,改變顯示元素,可以使 DOM 結構最簡單化。可參考文章【大數據如何在前端流暢展現】,不過他的 Demo有點問題.

語法和API

1. 理解 ECMAScript 和 JavaScript 的關係

ECMAScriptJavaScript 的規範,JavaScriptECMAScript 的實現。

2. 熟練運用 es五、es6 提供的語法規範

【JavaScript 標準參考教程(alpha)】

【ECMAScript 6 入門】

3. setInterval 須要注意的點,使用 settimeout 實現 setInterval
  • setInterval 須要注意的點:

在使用 setInterval 方法時,每一次啓動都須要對 setInterval 方法返回的值作一個判斷,判斷是不是空值,若不是空值,則要中止定時器並將值設爲空,再從新啓動,若是不進行判斷並賦值,有可能會形成計時器循環調用,在同等的時間內同時執行調用的代碼,並會隨着代碼的運行時間增長而增長,致使功能沒法實現,甚至佔用過多資源而卡死奔潰。所以在每一次使用setInterval方法時,都須要進行一次判斷。

let timer = setInterval(func, 1000)
// 在其餘地方再次用到setInterval(func, 1000)
if (timer !== null) {
    clearInterval(timer)
    timer = null
}
timer = setInterval(func, 1000)
複製代碼
  • 使用 settimeout 實現 setInterval
setIntervalFunc = () =>{
  console.log(1) //使用遞歸
  setTimeout(setIntervalFunc, 1000);
};
setInterval()
複製代碼
4. JavaScript 提供的正則表達式 API、可使用正則表達式(郵箱校驗、URL解析、去重等)解決常見問題

郵箱校驗:

function isEmail(emailStr) {
    return /^[a-zA-Z0-9]+([._-]*[a-zA-Z0-9]*)*@[a-zA-Z0-9]+.[a-zA-Z0-9{2,5}$]/.test(emailStr);
}
複製代碼

URL解析:

function isUrl(urlStr) {
    return /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\*\+,;=.%]+$/.test(value)
}
複製代碼

數組去重:

// set結構
let arr = [1, 1, 2, 2, 3, 3]
arr2 = [...new Set(arr)]
console.log(arr2) // [1,2,3]

// Object.keys(), 利用屬性 key 的惟一性
let arrObj = [1, 1, 2, 2, 3, 3]
arrObj2 = {}
for (i in arrObj) {
    arrObj2[arrObj[i]] = true
}
let arrObj3 = Object.keys(arrObj2)
console.log(arrObj3)

// 利用 indexOf() 查詢數組內是否已經包含該元素
var arrIndexOf = ['a','c','b','d','a','b']
var arrIndexOf2 = [];
for(var i = 0;i<arrIndexOf.length;i++){
    if(arrIndexOf2.indexOf(arrIndexOf[i])<0){
        arrIndexOf2.push(arrIndexOf[i]);
    }
}
console.log(arrIndexOf2)// ['a', 'c', 'b', 'd']
複製代碼

2、HTML和CSS

HTML

1. 從規範的角度理解 HTML,從分類和語義的角度使用標籤

語義化標籤<header> <footer> <nav> <section> <article> <aside> 等

  • 讓頁面呈現清晰的結構
  • 屏幕閱讀器(若是訪客有視障)會徹底根據你的標記來「讀」你的網頁
  • 搜索引擎的爬蟲依賴標籤肯定上下文和權重問題
  • 便於團隊開發和維護

標籤分類

  • 文檔標籤(10 個):<html>、<head>、<body>、<title>、<meta>、<base> 、<style>、<link>、<script>、<noscript>

  • 表格標籤(10 個):<table>、<thead>、<tbody>、<tfoot>、<tr>、<td>、<th> 、<col>、<colgroup>、<caption>

  • 表單標籤(10 個):<from>、<input>、<textarea>、<button>、<select> 、<optgroup>、<option>、<label>、<fieldset>、<legend>

  • 列表標籤(6個):<ul>、<ol>、<li>、<dl>、<dt>、<dd>

  • 多媒體標籤(5個):<img>、<map>、<area>、<object>、<param>

  • 文章標籤:<h1> - <h6> 、<p>、<br>、<span>、<bdo>、<pre>、<acronym>、<abbr>、<blockquote>、<q>、<ins>、<del>、<address>

  • 字體樣式標籤:<tt>、<i>、<b>、<big>、<small>、<em>、<strong>、<dfn>、<code>、<samp>、<kbd>、<var>、<cite>、<sup>、<sub>

    在不一樣的場景使用不一樣的標籤,更能顯示清晰的結構。

2. 元信息類標籤 (head、title、meta) 的使用目的和配置方法

<head> 標籤用於定義文檔的頭部,它是全部頭部元素的容器。<head> 中的元素能夠引用腳本、指示瀏覽器在哪裏找到樣式表、提供元信息等等。能夠包含的標籤有: <base>, <link>, <meta>, <script>, <style>, 以及 <title>。

<title> 定義文檔的標題,它是 head 部分中惟一必需的元素。

<meta>元素可提供有關頁面的元信息(meta-information),好比針對搜索引擎和更新頻度的描述和關鍵詞。使用方法參考【meta標籤詳解】

3. HTML5 離線緩存原理
  • 待補充
4. 可使用 Canvas API、SVG 等繪製高性能的動畫

CSS

1. CSS 盒模型,在不一樣瀏覽器的差別
  • 標準 w3c 盒子模型的範圍包括 margin、border、padding、content,而且 content 部分不包含其餘部分
  • ie 盒子模型的範圍也包括 margin、border、padding、content,和標準 w3c 盒子模型不一樣的是:ie 盒子模型的 content 部分包含了 borderpading
2. CSS 全部選擇器及其優先級、使用場景,哪些能夠繼承,如何運用at規則

不一樣級別優先級!important > 行內樣式 > ID選擇器 > 類選擇器 > 元素 > 通配符 > 繼承 > 瀏覽器默認屬性

相同級別優先級內聯(行內)樣式 > 內部樣式表 > 外部樣式表 > 導入樣式(@import)。

可繼承屬性

字體系列屬性, font-family, font-weight, font-size, font-style...
文本系列屬性, text-indent, text-align, line-heigh,&emsp;word-spacing, letter-spacing, text-transform, color
元素可見性:visibility, 光標屬性:cursor
複製代碼

AT rule:

1、什麼是 at-rules

eg:@charset "utf-8";

at-ruleCSS 樣式聲明,以 @ 開頭,緊跟着是標識符(charset),最後以分號(;)結尾。

2、幾個 at-rules

一、@charset —定義被樣式表使用的字符集

二、@import ——告訴 CSS 引擎包含外部的 CSS 樣式表

三、@namespace——告訴 CSS 引擎全部的內容都必須考慮使用 XML 命名空間前綴

四、嵌套at-rules

(1)@media——條件組規則。若是設備符合標準定義的條件查詢則使用該媒體

(2)@font-face——描述了一個將從外部下載的字體

(3)@keyframes——描述了中間步驟在 CSS 動畫的序列

(4)@page——描述了文件的佈局變化,當要打印文檔時。

(5)@supports——條件組規則,若是瀏覽器知足給出的規則,則把它應用到內容中

(6)@document——條件組規則,若是被用到文檔的 CSS 樣式表知足了給定的標準,那麼將被應用到全部的內容中。

3. CSS 僞類和僞元素有哪些,它們的區別和實際應用

僞類:用於向某些選擇器添加特殊的效果. :active, :focus, :link, :visited, :hover, :first-child

僞元素:用於將特殊的效果添加到某些選擇器. :before, :after, :first-line, :first-letter

僞類和僞元素的根本區別在於:它們是否創造了新的元素(抽象)。從咱們模仿其意義的角度來看,若是須要添加新元素加以標識的,就是僞元素,反之,若是隻須要在既有元素上添加類別的,就是僞類。

4. HTML 文檔流的排版規則,CSS 幾種定位的規則、定位參照物、對文檔流的影響,如何選擇最好的定位方式,雪碧圖實現原理

HTML 文檔流的排版規則: 把元素按從上而下,從左到右的順序默認排列。不在一行的元素從上而下,在一行的從左到右排列。

CSS 幾種定位的規則:

  • static 定位(普通流定位)
  • float 定位(浮動定位), 有兩個取值:left (左浮動)和 right (右浮動)。

浮動元素會在沒有浮動元素的上方,效果上看是遮擋住了沒有浮動的元素,有float樣式規則的元素是脫離文檔流的,它的父元素的高度並不能有它撐開。

  • relative 定位(相對定位), 相對本元素的左上角進行定位,top,left,bottom,right 均可以有值。雖然通過定位後,位置可能會移動,可是本元素並無脫離文檔流,還佔有原來的頁面空間。
  • absolute 定位(絕對定位), 相對於祖代中有 relative (相對定位)而且離本元素層級關係上是最近的元素的左上角進行定位,若是在祖代元素中沒有有 relative定位的,就默認相對於body進行定位。絕對定位是脫離文檔流的
  • fixed 定位(固定定位),這種定位方式是相對於整個文檔的,只需設置它相對於各個方向的偏移值,就能夠將該元素固定在頁面固定的位置,一般用來顯示一些提示信息,脫離文檔流;

雪碧圖實現原理CSS Sprite,是一種 CSS 圖像合併技術,該方法是將小圖標和背景圖像合併到一張圖片上,而後利用 css 的背景定位來顯示須要顯示的圖片部分。

5. 水平垂直居中的方案、能夠實現6種以上並對比它們的優缺點

參考文章: 【CSS實現水平垂直居中的1010種方式】

6. BFC 實現原理,能夠解決的問題,如何建立BFC

BFC(Block formatting context) 直譯爲"塊級格式化上下文"。它是一個獨立的渲染區域,只有塊級元素參與, 它規定了內部的塊級元素如何佈局,而且與這個區域外部絕不相干。

BCF 能夠解決的問題:浮動定位,消除外邊距摺疊,清除浮動,自適應多欄佈局

BFC的建立:根元素或包含根元素的元素,浮動元素(float 不爲none),絕對定位元素( positionabsolute 或者 fixed),displayinline-block,table-cell,table-caption,overflow 值不爲 visible,彈性元素( flex 佈局),網格元素( grid 佈局)

7. CSS模塊化方案、如何配置按需加載、如何防止 CSS 阻塞渲染

CSS模塊化方案: 文件細化,命名約定,CSS Modules , css in js

如何防止 CSS 阻塞渲染:

CSS 是阻塞渲染的資源。須要將它儘早、儘快地下載到客戶端,以便縮短首次渲染的時間。

有一些 CSS 樣式只在特定條件下(例如顯示網頁或將網頁投影到大型顯示器上時)使用,咱們能夠經過 CSS「媒體類型」和「媒體查詢」來解決這類用例:

<link href="print.css" rel="stylesheet" media="print">
<link href="other.css" rel="stylesheet" media="(min-width: 40em)">

首屏相關的關鍵 `CSS` 使用阻塞渲染的方式加載,全部的非關鍵 `CSS` 在首屏渲染完成後加載。
複製代碼
8. 手寫圖片瀑布流效果

參考文章:【瀑布流佈局的實現】

9. 使用CSS繪製幾何圖形(圓形、三角形、扇形、菱形等)
// 圓形
.circle{
	width:100px;
	height:100px;
	border-radius:50%;
	background:blue;
}
// 三角形
.triangle {
    width: 0;
    height: 0;
    border: 50px solid blue;
    /* 經過改變邊框顏色,能夠改變三角形的方向 */
    border-color: blue transparent transparent transparent;
}
// 扇形,扇形是由一個圓形和一個矩形進行組合獲得的,用矩形遮住圓形的一部分就造成了扇形。
.sector {
    width: 142px;
    height: 142px;
    background: #fff;
    border-radius: 50%;
    background-image: linear-gradient(to right, transparent 50%, #655 0);
}

.sector::before {
    content: '';
    display: block;
    margin-left: 50%;
    height: 100%;
	width: 100%;
    background-color: inherit;
    transform-origin: left;
	/*調整角度,改變扇形大小*/
    transform: rotate(230deg);
}
// 菱形
.rhombus {
    width: 200px;
    height: 200px;
    transform: rotateZ(45deg) skew(30deg, 30deg);
    background: blue;
}
複製代碼
10. 使用純 CSS 實現曲線運動(貝塞爾曲線)

CSS3 新增了 transition-timing-function 屬性,它的取值就能夠設置爲一個三次貝塞爾曲線方程。

參考文章: 【貝塞爾曲線的css實現——淘寶加入購物車基礎動畫】

11. 實現經常使用佈局(三欄、聖盃、雙飛翼、吸頂),說出多種方式並理解其優缺點

聖盃佈局, 兩邊頂寬,中間自適應的三欄佈局。

  • 期待評論補充

3、計算機基礎

關於編譯原理,不須要理解很是深刻,可是最基本的原理和概念必定要懂,這對於學習一門編程語言很是重要

編譯原理

1. 理解代碼究竟是什麼,計算機如何將代碼轉換爲能夠運行的目標程序

代碼就是程序員用開發工具所支持的語言寫出來的源文件,是一組由字符、符號或信號碼元以離散形式表示信息的明確的規則體系。

計算機源代碼最終目的是將人類可讀文本翻譯成爲計算機可執行的二進制指令,這種過程叫編譯,它由經過編譯器完成。

2. 正則表達式的匹配原理和性能優化
  • 待補充
3. 如何將JavaScript代碼解析成抽象語法樹(AST)
  • 待補充
4. base64 的編碼原理
  • 待補充
5. 幾種進制的相互轉換計算方法,在 JavaScript 中如何表示和轉換

parseInt(str, radix) 將一個 radix 進制的 str 轉化爲十進制,parseInt('23',8) // 19,將八進制的‘23’轉化爲10進制的‘19’

number.toString(radix) 將一個數字轉化爲 radix 進制的數字字符串

0x11.toString(8) // 21
0x11.toString(10) // 17
0x11.toString(2) // 10001
複製代碼

網絡協議

1. 理解什麼是協議,瞭解 TCP/IP 網絡協議族的構成,每層協議在應用程序中發揮的做用

協議,網絡協議的簡稱,網絡協議是通訊計算機雙方必須共同聽從的一組約定。如怎麼樣創建鏈接、怎麼樣互相識別等。只有遵照這個約定,計算機之間才能相互通訊交流。它的三要素是:語法、語義、時序。

TCP/IP 網絡協議族的構成: TCP/IP 協議是 Internet 最基本的協議。由傳輸層的 TCP 協議和網絡層的 IP 協議組成。

TCP 負責發現傳輸的問題,一有問題就發出信號,要求從新傳輸,直到全部數據安全正確地傳輸到目的地。而 IP 是給因特網的每一臺聯網設備規定一個地址。

應用層

應用層決定了向用戶提供應該服務時通訊的活動。

TCP/IP 協議族內預存了各種通用的應用服務。好比,FTP(File Transfer Protocol,文件傳輸協議)和 DNS(Domain Name System,域名系統)服務就是其中的兩類。HTTP 協議也處於該層。

傳輸層

傳輸層對上層應用層,提供處於網絡鏈接中兩臺計算機之間的數據傳輸。

在傳輸層有兩個性質不一樣的協議:TCP(Transmission Control Protocol,傳輸控制協議)和 UDP(User Data Protocol,用戶數據報協議)。

網絡層(又名網絡互連層)

網絡層用來處理在網絡上流動的數據包。數據包是網絡傳輸的最小數據單位。該層規定了經過怎樣的路徑(所謂的傳輸路線)到達對方計算機,並把數據包傳送給對方。

與對方計算機之間經過多臺計算機或網絡設備進行傳輸時,網絡層所起的所用就是在衆多的選項內選擇一條傳輸路線。

鏈路層(又名數據鏈路層,網絡接口層)

用來處理鏈接網絡的硬件部分。包括控制操做系統、硬件的設備驅動、NIC(Network Interface Card,網絡適配器,即網卡),及光纖等物理可見部分(還包括鏈接器等一切傳輸媒介)。硬件上的範疇均在鏈路層的做用範圍以內。

2. 三次握手和四次揮手詳細原理,爲何要使用這種機制

三次握手和四次揮手詳細原理:

三次握手:避免鏈接請求的數據包丟失,數據傳輸過程由於網絡併發量很大在某結點被阻塞

四次揮手: TCP鏈接是全雙工通道,須要雙向關閉。

參考文章: 【TCP/IP協議族】

3. 有哪些協議是可靠,TCP有哪些手段保證可靠交付

TCP的協議:FTP(文件傳輸協議)、Telnet(遠程登陸協議)、SMTP(簡單郵件傳輸協議)、POP3(和 SMTP 相對,用於接收郵件)、HTTP 協議等。

TCP 提供可靠的、面向鏈接的數據傳輸服務。使用 TCP 通訊以前,須要進行「三次握手」創建鏈接,通訊結束後還要使用「四次揮手」斷開鏈接。

4. DNS的做用、DNS解析的詳細過程,DNS優化原理

DNS 的做用:DNS是互聯網的一項服務。它做爲將域名和IP地址相互映射的一個分佈式數據庫,可以令人更方便地訪問互聯網。

DNS解析過程

一、在瀏覽器中輸入 www.qq.com 域名,操做系統會先檢查本身本地的 hosts 文件是否有這個網址映射關係,若是有,就先調用這個 IP 地址映射,完成域名解析。

二、若是 hosts 裏沒有這個域名的映射,則查找本地 DNS解析器緩存,是否有這個網址映射關係,若是有,直接返回,完成域名解析。

三、若是 hosts本地DNS解析器緩存 都沒有相應的網址映射關係,首先會找 TCP/ip 參數中設置的 首選DNS服務器,在此咱們叫它 本地DNS服務器,此服務器收到查詢時,若是要查詢的域名,包含在本地配置區域資源中,則返回解析結果給客戶機,完成域名解析,此解析具備權威性。

四、若是要查詢的域名,不禁 本地DNS服務器區域解析,但該服務器已緩存了此網址映射關係,則調用這個 IP 地址映射,完成域名解析,此解析不具備權威性。

五、若是 本地DNS服務器 本地區域文件與緩存解析都失效,則根據 本地DNS服務器 的設置(是否設置轉發器)進行查詢,若是未用轉發模式,本地 DNS 就把請求發至 13 臺根 DNS,根 DNS 服務器收到請求後會判斷這個域名 (.com) 是誰來受權管理,並會返回一個負責該頂級域名服務器的一個IP本地DNS服務器 收到 IP 信息後,將會聯繫負責 .com 域的這臺服務器。這臺負責 .com 域的服務器收到請求後,若是本身沒法解析,它就會找一個管理 .com 域的下一級 DNS 服務器地址(http://qq.com)本地DNS服務器。當 本地DNS服務器 收到這個地址後,就會找 http://qq.com 域服務器,重複上面的動做,進行查詢,直至找到 www.qq .com 主機。

六、若是用的是轉發模式,此 DNS 服務器就會把請求轉發至上一級 DNS 服務器,由上一級服務器進行解析,上一級服務器若是不能解析,或找根 DNS 或把轉請求轉至上上級,以此循環。無論是 本地DNS服務器 用是是轉發,仍是根提示,最後都是把結果返回給 本地DNS服務器,由此 DNS 服務器再返回給客戶機。

DNS 優化:減小DNS的請求次數;進行DNS預獲取 。

減小DNS的請求次數————在項目中減小不一樣域名的http請求,儘可能少的域名減小DNS的請求數

DNS預獲取————減小用戶的等待時間,提高用戶體驗 。

默認狀況下瀏覽器會對頁面中和當前域名(正在瀏覽網頁的域名)不在同一個域的域名進行預獲取,而且緩存結果,這就是隱式的 DNS Prefetch。若是想對頁面中沒有出現的域進行預獲取,那麼就要使用顯示的 DNS Prefetch 了。

<meta http-equiv="x-dns-prefetch-control" content="on">
<link rel="dns-prefetch" href="//www.itechzero.com">
<link rel="dns-prefetch" href="//api.share.baidu.com">
<link rel="dns-prefetch" href="//bdimg.share.baidu.com">
複製代碼
5. CDN的做用和原理
  • CDN的做用

CDN 的全稱是 Content Delivery Network,即內容分發網絡。其基本思路是儘量避開互聯網上有可能影響數據傳輸速度和穩定性的瓶頸和環節,使內容傳輸的更快、更穩定。

  • CDN 的原理 一、多域名加載資源

通常狀況下,瀏覽器都會對單個域名下的併發請求數(文件加載)進行限制,一般最多有 4 個,那麼第 5 個加載項將會被阻塞,直到前面的某一個文件加載完畢。由於 CDN 文件是存放在不一樣區域(不一樣 IP)的,因此對瀏覽器來講是能夠同時加載頁面所需的全部文件(遠不止 4 個),從而提升頁面加載速度。

二、文件可能已經被加載過並保存有緩存

一些通用的 js 庫或者是 css 樣式庫,如 jQuery ,在網絡中的使用是很是廣泛的。當一個用戶在瀏覽你的某一個網頁的時候,頗有可能他已經經過你網站使用的 CDN 訪問過了其餘的某一個網站,恰巧這個網站一樣也使用了 jQuery,那麼此時用戶瀏覽器已經緩存有該 jQuery 文件(同 IP 的同名文件若是有緩存,瀏覽器會直接使用緩存文件,不會再進行加載),因此就不會再加載一次了,從而間接的提升了網站的訪問速度。

三、分佈式的數據中心

假如你的站點佈置在北京,當一個香港或者更遠的用戶訪問你的站點的時候,他的數據請求勢必會很慢很慢。而 CDN 則會讓用戶從離他最近的節點去加載所需的文件,因此加載速度提高就是理所固然的了。

6. HTTP 請求報文和響應報文的具體組成,能理解常見請求頭的含義,有幾種請求方式,區別是什麼

參考文章:【HTTP 請求詳解】

HTTP協議的六種請求方法

  1. GET: 發送請求來得到服務器上的資源,請求體中不會包含請求數據,請求數據放在協議頭中

  2. POST: 和 get 同樣很常見,向服務器提交資源讓服務器處理,好比提交表單、上傳文件等,可能致使創建新的資源或者對原有資源的修改。提交的資源放在請求體中。不支持快取。非冪等

  3. HEAD: 本質和 get 同樣,可是響應中沒有呈現數據,而是 http 的頭信息,主要用來檢查資源或超連接的有效性或是否能夠可達、檢查網頁是否被串改或更新,獲取頭信息等,特別適用在有限的速度和帶寬下。

  4. PUT: 和 post 相似,html 表單不支持,發送資源與服務器,並存儲在服務器指定位置,要求客戶端事先知道該位置;好比 post 是在一個集合上(/province),而 put 是具體某一個資源上(/province/123)。因此 put 是安全的,不管請求多少次,都是在 123 上更改,而 post 可能請求幾回建立了幾回資源。冪等

  5. DELETE: 請求服務器刪除某資源。和 put 都具備破壞性,可能被防火牆攔截

  6. CONNECT: HTTP/1.1 協議中預留給可以將鏈接改成管道方式的代理服務器。就是把服務器做爲跳板,去訪問其餘網頁而後把數據返回回來,鏈接成功後,就能夠正常的 getpost 了。

7: OPTIONS: 獲取 http 服務器支持的 http 請求方法,容許客戶端查看服務器的性能,好比 ajax 跨域時的預檢等。 8: TRACE: 回顯服務器收到的請求,主要用於測試或診斷。通常禁用,防止被惡意攻擊或盜取信息。

7. HTTP 全部狀態碼的具體含義,看到異常狀態碼能快速定位問題
  • 1XX:信息狀態碼

    • 100 Continue 繼續,通常在發送post請求時,已發送了http header以後服務端將返回此信息,表示確認,以後發送具體參數信息
  • 2XX:成功狀態碼

    • 200 OK 正常返回信息
    • 201 Created 請求成功而且服務器建立了新的資源
    • 202 Accepted 服務器已接受請求,但還沒有處理
  • 3XX:重定向

    • 301 Moved Permanently 請求的網頁已永久移動到新位置。
    • 302 Found 臨時性重定向。
    • 303 See Other 臨時性重定向,且老是使用 GET 請求新的 URI。
    • 304 Not Modified 自從上次請求後,請求的網頁未修改過。
  • 4XX:客戶端錯誤

    • 400 Bad Request 服務器沒法理解請求的格式,客戶端不該當嘗試再次使用相同的內容發起請求。
    • 401 Unauthorized 請求未受權。
    • 403 Forbidden 禁止訪問。
    • 404 Not Found 找不到如何與 URI 相匹配的資源。
  • 5XX: 服務器錯誤

    • 500 Internal Server Error 最多見的服務器端錯誤。
    • 503 Service Unavailable 服務器端暫時沒法處理請求(多是過載或維護)。
8. HTTP1.一、HTTP2.0帶來的改變

緩存處理,在 HTTP1.0 中主要使用 header 裏的 If-Modified-Since,Expires 來作爲緩存判斷的標準,HTTP1.1 則引入了更多的緩存控制策略例如 Entity tag,If-Unmodified-Since, If-Match, If-None-Match 等更多可供選擇的緩存頭來控制緩存策略。

帶寬優化及網絡鏈接的使用HTTP1.0 中,存在一些浪費帶寬的現象,例如客戶端只是須要某個對象的一部分,而服務器卻將整個對象送過來了,而且不支持斷點續傳功能,HTTP1.1 則在請求頭引入了 range 頭域,它容許只請求資源的某個部分,即返回碼是 206(Partial Content),這樣就方便了開發者自由的選擇以便於充分利用帶寬和鏈接。

錯誤通知的管理,在 HTTP1.1 中新增了24 個錯誤狀態響應碼,如 409(Conflict)表示請求的資源與資源的當前狀態發生衝突;410(Gone)表示服務器上的某個資源被永久性的刪除。

Host頭處理,在 HTTP1.0 中認爲每臺服務器都綁定一個惟一的 IP 地址,所以,請求消息中的 URL 並無傳遞主機名 (hostname)。但隨着虛擬主機技術的發展,在一臺物理服務器上能夠存在多個虛擬主機(Multi-homed Web Servers),而且它們共享一個 IP 地址。HTTP1.1 的請求消息和響應消息都應支持 Host 頭域,且請求消息中若是沒有 Host 頭域會報告一個錯誤(400 Bad Request)

長鏈接HTTP 1.1 支持長鏈接(PersistentConnection)和請求的流水線(Pipelining)處理,在一個 TCP 鏈接上能夠傳送多個 HTTP 請求和響應,減小了創建和關閉鏈接的消耗和延遲,在 HTTP1.1 中默認開啓 Connection: keep-alive,必定程度上彌補了 HTTP1.0 每次請求都要建立鏈接的缺點。

HTTP2.0和HTTP1.X相比的新特性

  • 新的二進制格式(Binary Format),HTTP1.x 的解析是基於文本。基於文本協議的格式解析存在自然缺陷,文本的表現形式有多樣性,要作到健壯性考慮的場景必然不少,二進制則不一樣,只認 01 的組合。基於這種考慮 HTTP2.0 的協議解析決定採用二進制格式,實現方便且健壯。

  • 多路複用(MultiPlexing),即鏈接共享,即每個 request 都是是用做鏈接共享機制的。一個 request 對應一個 id,這樣一個鏈接上能夠有多個 request,每一個鏈接的 request 能夠隨機的混雜在一塊兒,接收方能夠根據 requestidrequest 再歸屬到各自不一樣的服務端請求裏面。

  • header 壓縮,如上文中所言,對前面提到過 HTTP1.xheader 帶有大量信息,並且每次都要重複發送,HTTP2.0 使用 encoder 來減小須要傳輸的 header 大小,通信雙方各自 cache 一份 header fields 表,既避免了重複 header 的傳輸,又減少了須要傳輸的大小。

  • 服務端推送(server push),HTTP2.0具備 server push 功能。

9. HTTPS 的加密原理,如何開啓 HTTPS,如何劫持 HTTPS 請求

參考文章:【一個故事講完 https】

設計模式

1. 熟練使用前端經常使用的設計模式編寫代碼,如單例模式、裝飾器模式、代理模式等

參考文章:【設計模式】

2. 發佈訂閱模式和觀察者模式的異同以及實際應用

觀察者模式和發佈訂閱模式最大的區別就是發佈訂閱模式有個事件調度中心。

// 觀察者模式
class Subject{
  constructor(){
    this.subs = [];
  }
  addSub(sub){
    this.subs.push(sub);
  }
  notify(){
    this.subs.forEach(sub=> {
      sub.update();
    });
  }
}
class Observer{
  update(){
    console.log('update');
  }
}
let subject = new Subject();
let ob = new Observer();
//目標添加觀察者了
subject.addSub(ob);
//目標發佈消息調用觀察者的更新方法了
subject.notify();   //update
複製代碼
// 發佈訂閱者模式
class PubSub {
    constructor() {
        this.subscribers = {}
    }
    subscribe(type, fn) {
        if (!Object.prototype.hasOwnProperty.call(this.subscribers, type)) {
          this.subscribers[type] = [];
        }
        
        this.subscribers[type].push(fn);
    }
    unsubscribe(type, fn) {
        let listeners = this.subscribers[type];
        if (!listeners || !listeners.length) return;
        this.subscribers[type] = listeners.filter(v => v !== fn);
    }
    publish(type, ...args) {
        let listeners = this.subscribers[type];
        if (!listeners || !listeners.length) return;
        listeners.forEach(fn => fn(...args));        
    }
}

let ob = new PubSub();
ob.subscribe('add', (val) => console.log(val));
ob.publish('add', 1);
複製代碼

4、數據結構和算法

據我瞭解的大部分前端對這部分知識有些欠缺,甚至抵觸,可是,若是突破更高的天花板,這部分知識是必不可少的,並且我親身經歷——很是有用!

JavaScript編碼能力

1. 多種方式實現數組去重、扁平化、對比優缺點

參考文章:【JS 數組去重方法整理】

【5種方式實現數組扁平化】

2. 多種方式實現深拷貝、對比優缺點

參考文章:【遞歸實現深拷貝】

3. 手寫函數柯里化工具函數、並理解其應用場景和優點
  • 待補充
4.手寫防抖和節流工具函數、並理解其內部原理和應用場景

參考文章:【函數的防抖與節流】

5.實現一個 sleep 函數
function sleep(time) {
  return new Promise((resolve,reject) => setTimeout(resolve, time))
}
sleep(3000).then(() => {console.log('沉睡3000ms')})
複製代碼

手動實現前端輪子

1. 手動實現call、apply、bind

call

  1. 判斷當前 this 是否爲函數,防止 Function.prototype.myCall() 直接調用
  2. context 爲可選參數,若是不傳的話默認上下文爲 window
  3. context 建立一個 Symbol(保證不會重名)屬性,將當前函數賦值給這個屬性
  4. 處理參數,傳入第一個參數後的其他參數
  5. 調用函數後即刪除該 Symbol 屬性
Function.prototype.myCall = function(context = window, ...args) {
    if (this === Function.prototype) {
        return undefined; // 用於防止 Function.prototype.myCall() 直接調用
    }
    context = context || window;
    const fn = Symbol();
    context[fn] = this;
    const result = context[fn](...args);
    delete context[fn];
    return result;
};
複製代碼

apply

apply 實現相似 call,參數爲數組

Function.prototype.myApply = function(context = window, args) {
    if (this === Function.prototype) {
        return undefined; // 用於防止 Function.prototype.myCall() 直接調用
    }
    const fn = Symbol();
    context[fn] = this;
    let result;
    if (Array.isArray(args)) {
        result = context[fn](...args);
    } else {
        result = context[fn]();
    }
    delete context[fn];
    return result;
};
複製代碼

bind

由於 bind() 返回一個方法需手動執行,所以利用閉包實現。

Function.prototype.myBind = function(context, ...args1) {
    if (this === Function.prototype) {
        throw new TypeError('Error');
    }
    const _this = this;
    return function F(...args2) {
        // 判斷是否用於構造函數
        if (this instanceof F) {
            return new _this(...args1, ...args2);
        }
        return _this.apply(context, args1.concat(args2));
    };
};
複製代碼
2.手動實現符合 Promise/A+ 規範的 Promise

參考文章:【手動實現 promise】

3. 手寫一個 EventEmitter 實現事件發佈、訂閱
function EventEmitter() {
      this._events = Object.create(null);
    }

    // 向事件隊列添加事件
    // prepend爲true表示向事件隊列頭部添加事件
    EventEmitter.prototype.addListener = function (type, listener, prepend) {
      if (!this._events) {
        this._events = Object.create(null);
      }
      if (this._events[type]) {
        if (prepend) {
          this._events[type].unshift(listener);
        } else {
          this._events[type].push(listener);
        }
      } else {
        this._events[type] = [listener];
      }
    };

    // 移除某個事件
    EventEmitter.prototype.removeListener = function (type, listener) {
      if (Array.isArray(this._events[type])) {
        if (!listener) {
          delete this._events[type]
        } else {
          this._events[type] = this._events[type].filter(e => e !== listener && e.origin !== listener)
        }
      }
    };

    // 向事件隊列添加事件,只執行一次
    EventEmitter.prototype.once = function (type, listener) {
      const only = (...args) => {
        listener.apply(this, args);
        this.removeListener(type, listener);
      }
      only.origin = listener;
      this.addListener(type, only);
    };

    // 執行某類事件
    EventEmitter.prototype.emit = function (type, ...args) {
      if (Array.isArray(this._events[type])) {
        this._events[type].forEach(fn => {
          fn.apply(this, args);
        });
      }
    };
    // 測試一下
    var emitter = new EventEmitter();

    var onceListener = function (args) {
      console.log('我只能被執行一次', args, this);
    }

    var listener = function (args) {
      console.log('我是一個listener', args, this);
    }

    emitter.once('click', onceListener);
    emitter.addListener('click', listener);

    emitter.emit('click', '參數');
    emitter.emit('click');

    emitter.removeListener('click', listener);
    emitter.emit('click');
複製代碼
4.能夠說出兩種實現雙向綁定的方案、能夠手動實現

參考文章:【Vue 雙向數據綁定原理】

5.手寫JSON.stringify、JSON.parse
let Myjson = {
      parse: function(jsonStr) {
          return eval('(' + jsonStr + ')');
      },
      stringify: function(jsonObj) {
          var result = '',
              curVal;
          if (jsonObj === null) {
              return String(jsonObj);
          }
          switch (typeof jsonObj) {
              case 'number':
              case 'boolean':
                  return String(jsonObj);
              case 'string':
                  return '"' + jsonObj + '"';
              case 'undefined':
              case 'function':
                  return undefined;
          }

          switch (Object.prototype.toString.call(jsonObj)) {
              case '[object Array]':
                  result += '[';
                  for (var i = 0, len = jsonObj.length; i < len; i++) {
                      curVal = JSON.stringify(jsonObj[i]);
                      result += (curVal === undefined ? null : curVal) + ",";
                  }
                  if (result !== '[') {
                      result = result.slice(0, -1);
                  }
                  result += ']';
                  return result;
              case '[object Date]':
                  return '"' + (jsonObj.toJSON ? jsonObj.toJSON() : jsonObj.toString()) + '"';
              case '[object RegExp]':
                  return "{}";
              case '[object Object]':
                  result += '{';
                  for (i in jsonObj) {
                      if (jsonObj.hasOwnProperty(i)) {
                          curVal = JSON.stringify(jsonObj[i]);
                          if (curVal !== undefined) {
                              result += '"' + i + '":' + curVal + ',';
                          }
                      }
                  }
                  if (result !== '{') {
                      result = result.slice(0, -1);
                  }
                  result += '}';
                  return result;

              case '[object String]':
                  return '"' + jsonObj.toString() + '"';
              case '[object Number]':
              case '[object Boolean]':
                  return jsonObj.toString();
          }
      }
  };
複製代碼
6. 手寫懶加載效果

參考文章:【圖片懶加載】

瀏覽器原理

1. 可詳細描述瀏覽器從輸入URL到頁面展示的詳細過程

參考文章:【輸入URL至頁面渲染】

2. 瀏覽器的垃圾回收機制,如何避免內存泄漏

參考文章:【瀏覽器內存回收機制】

參考文章:【內存泄漏與避免】

資源推薦

語言基礎

計算機基礎

數據結構和算法

運行環境

框架和類庫

前端工程

項目和業務

學習提高

博客推薦

技術以外

此文章 Markdown 源文件地址:github.com/zxpsuper/bl…

相關文章
相關標籤/搜索