成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

刷《一年半經(jīng)驗(yàn),百度、有贊、阿里面試總結(jié)》·手記

NusterCache / 1113人閱讀

摘要:在掘金上看到了一位大佬發(fā)了一篇很詳細(xì)的面試記錄文章一年半經(jīng)驗(yàn),百度有贊阿里面試總結(jié),為了查漏補(bǔ)缺,抽空就詳細(xì)做了下。

在掘金上看到了一位大佬發(fā)了一篇很詳細(xì)的面試記錄文章-《一年半經(jīng)驗(yàn),百度、有贊、阿里面試總結(jié)》,為了查漏補(bǔ)缺,抽空就詳細(xì)做了下。(估計(jì)只有我這么無聊了哈哈哈

有給出的或者有些不完善的答案,也盡力給出/完善了(可能有錯(cuò),大家自行辨別)。有些很困難的題目(例如實(shí)現(xiàn)Promise),附帶相關(guān)鏈接(懶癌患者福利)。

總的來說,將這些題目分成了“Javascript”、“CSS”、“瀏覽器/協(xié)議”、“算法”和“Web工程化”5個(gè)部分進(jìn)行回答和代碼實(shí)現(xiàn)。

最后,歡迎來我的博客和我扯犢子:godbmw.com。直接戳本篇原文的地址:刷《一年半經(jīng)驗(yàn),百度、有贊、阿里面試總結(jié)》·手記

1. Javascript相關(guān) 1.1 回文字符串
題目:實(shí)現(xiàn)一個(gè)函數(shù),判斷是不是回文字符串

原文的思路是將字符串轉(zhuǎn)化成數(shù)組=>反轉(zhuǎn)數(shù)組=>拼接成字符串。這種做法充分利用了js的BIF,但性能有所損耗

function run(input) {
  if (typeof input !== "string") return false;
  return input.split("").reverse().join("") === input;
}

其實(shí)正常思路也很簡(jiǎn)單就能實(shí)現(xiàn),性能更高,但是沒有利用js的特性

// 回文字符串
const palindrome = (str) => {
  // 類型判斷
  if(typeof str !== "string") {
    return false;
  }

  let len = str.length;
  for(let i = 0; i < len / 2; ++i){
    if(str[i] !== str[len - i - 1]){
      return false;
    }
  }
  return true;
}
1.2 實(shí)現(xiàn)Storage
題目:實(shí)現(xiàn)Storage,使得該對(duì)象為單例,并對(duì)localStorage進(jìn)行封裝設(shè)置值setItem(key,value)和getItem(key)

題目重點(diǎn)是單例模式,需要注意的是借助localStorage,不是讓自己手動(dòng)實(shí)現(xiàn)!

感謝@whiteyork_和@chenzesam的提醒:箭頭函數(shù)沒有prototype

function Storage(){}

Storage.getInstance = (function(){
  var instance = null
  return function(){
    if(!instance){
      instance = new Storage()
    }
    return instance
  }
})()

Storage.prototype.setItem = function(key, value) {
  return localStorage.setItem(key, value)
}

Storage.prototype.getItem = function(key){
  return localStorage.getItem(key)
}

// 測(cè)試代碼:Chrome環(huán)境
let a = Storage.getInstance()
let b = Storage.getInstance()
console.log(a === b)

a.setItem("key", 1)
console.log(b.getItem("key"))
1.3 JS事件流
題目:說說事件流吧

事件流分為冒泡和捕獲。

事件冒泡:子元素的觸發(fā)事件會(huì)一直向父節(jié)點(diǎn)傳遞,一直到根結(jié)點(diǎn)停止。此過程中,可以在每個(gè)節(jié)點(diǎn)捕捉到相關(guān)事件??梢酝ㄟ^stopPropagation方法終止冒泡。

事件捕獲:和“事件冒泡”相反,從根節(jié)點(diǎn)開始執(zhí)行,一直向子節(jié)點(diǎn)傳遞,直到目標(biāo)節(jié)點(diǎn)。印象中只有少數(shù)瀏覽器的老舊版本才是這種事件流,可以忽略。這里說的確實(shí)有問題,更正下:addEventLister給出了第三個(gè)參數(shù)同時(shí)支持冒泡與捕獲。

感謝@junior-yang的提醒

1.4 實(shí)現(xiàn)函數(shù)繼承
題目:現(xiàn)在有一個(gè)函數(shù)A和函數(shù)B,請(qǐng)你實(shí)現(xiàn)B繼承A。并且說明他們優(yōu)缺點(diǎn)。

方法一:綁定構(gòu)造函數(shù)

優(yōu)點(diǎn):可以實(shí)現(xiàn)多繼承

缺點(diǎn):不能繼承父類原型方法/屬性

function Animal(){
  this.species = "動(dòng)物";
}

function Cat(){
  Animal.apply(this, arguments); // 父對(duì)象的構(gòu)造函數(shù)綁定到子節(jié)點(diǎn)上
}

var cat = new Cat()
console.log(cat.species) // 輸出:動(dòng)物

方法二:原型鏈繼承

優(yōu)點(diǎn):能夠繼承父類原型和實(shí)例方法/屬性,并且可以捕獲父類的原型鏈改動(dòng)

缺點(diǎn):無法實(shí)現(xiàn)多繼承,會(huì)浪費(fèi)一些內(nèi)存(Cat.prototype.constructor = Cat)。除此之外,需要注意應(yīng)該將Cat.prototype.constructor重新指向本身。

js中交換原型鏈,均需要修復(fù)prototype.constructor指向問題。

function Animal(){
  this.species = "動(dòng)物";
}
Animal.prototype.func = function(){
  console.log("heel")
}

function Cat(){}
Cat.prototype = new Animal()
Cat.prototype.constructor = Cat

var cat = new Cat()
console.log(cat.func, cat.species)

方法3:結(jié)合上面2種方法

function Animal(){
  this.species = "動(dòng)物";
}
Animal.prototype.func = function(){
  console.log("heel")
}

function Cat(){
  Animal.apply(this, arguments)
}
Cat.prototype = new Animal()
Cat.prototype.constructor = Cat;

var cat = new Cat()
console.log(cat.func, cat.species)
1.5 ES5對(duì)象 vs ES6對(duì)象
題目:es6 class 的new實(shí)例和es5的new實(shí)例有什么區(qū)別?

ES6中(和ES5相比),classnew實(shí)例有以下特點(diǎn):

class的構(gòu)造參數(shù)必須是new來調(diào)用,不可以將其作為普通函數(shù)執(zhí)行

es6class不存在變量提升

最重要的是:es6內(nèi)部方法不可以枚舉。es5的prototype上的方法可以枚舉。

為此我做了以下測(cè)試代碼進(jìn)行驗(yàn)證:

console.log(ES5Class()) // es5:可以直接作為函數(shù)運(yùn)行
// console.log(new ES6Class()) // 會(huì)報(bào)錯(cuò):不存在變量提升

function ES5Class(){
  console.log("hello")
}

ES5Class.prototype.func = function(){ console.log("Hello world") }

class ES6Class{
  constructor(){}
  func(){
    console.log("Hello world")
  }
}

let es5 = new ES5Class()
let es6 = new ES6Class()

console.log("ES5 :")
for(let _ in es5){
  console.log(_)
}

// es6:不可枚舉
console.log("ES6 :")
for(let _ in es6){
  console.log(_)
}

這篇《JavaScript創(chuàng)建對(duì)象—從es5到es6》對(duì)這個(gè)問題的深入解釋很好,推薦觀看!

1.6 實(shí)現(xiàn)MVVM
題目:請(qǐng)簡(jiǎn)單實(shí)現(xiàn)雙向數(shù)據(jù)綁定mvvm

vuejs是利用Object.defineProperty來實(shí)現(xiàn)的MVVM,采用的是訂閱發(fā)布模式。每個(gè)data中都有set和get屬性,這種點(diǎn)對(duì)點(diǎn)的效率,比Angular實(shí)現(xiàn)MVVM的方式的效率更高。


  
  
1.7 實(shí)現(xiàn)Promise

這是一位大佬實(shí)現(xiàn)的Promise版本:過了Promie/A+標(biāo)準(zhǔn)的測(cè)試!??!網(wǎng)上能搜到的基本都是從這篇文章變形而來或者直接照搬!??!原文地址,直接戳:剖析Promise內(nèi)部結(jié)構(gòu),一步一步實(shí)現(xiàn)一個(gè)完整的、能通過所有Test case的Promise類

下面附上一種近乎完美的實(shí)現(xiàn):可能無法和其他Promise庫(kù)的實(shí)現(xiàn)無縫對(duì)接。但是,上面的原文實(shí)現(xiàn)了全部的,歡迎Mark!

function MyPromise(executor){
  var that = this
  this.status = "pending" // 當(dāng)前狀態(tài)
  this.data = undefined
  this.onResolvedCallback = [] // Promise resolve時(shí)的回調(diào)函數(shù)集,因?yàn)樵赑romise結(jié)束之前有可能有多個(gè)回調(diào)添加到它上面
  this.onRejectedCallback = [] // Promise reject時(shí)的回調(diào)函數(shù)集,因?yàn)樵赑romise結(jié)束之前有可能有多個(gè)回調(diào)添加到它上面

  // 更改狀態(tài) => 綁定數(shù)據(jù) => 執(zhí)行回調(diào)函數(shù)集
  function resolve(value){
    if(that.status === "pending"){
      that.status = "resolved"
      that.data = value
      for(var i = 0; i < that.onResolvedCallback.length; ++i){
        that.onResolvedCallback[i](value)
      }
    }
  }

  function reject(reason){
    if(that.status === "pending"){
      that.status = "rejected"
      that.data = reason
      for(var i = 0; i < that.onResolvedCallback.length; ++i){
        that.onRejectedCallback[i](reason)
      }
    }
  }

  try{ 
    executor(resolve, reject) // resolve, reject兩個(gè)函數(shù)可以在外部傳入的函數(shù)(executor)中調(diào)用
  } catch(e) { // 考慮到執(zhí)行過程可能有錯(cuò)
    reject(e)
  }
}

// 標(biāo)準(zhǔn)是沒有catch方法的,實(shí)現(xiàn)了then,就實(shí)現(xiàn)了catch
// then/catch 均要返回一個(gè)新的Promise實(shí)例

MyPromise.prototype.then = function(onResolved, onRejected){
  var that = this
  var promise2

  // 值穿透
  onResolved = typeof onResolved === "function" ? onResolved : function(v){ return v }
  onRejected = typeof onRejected === "function" ? onRejected : function(r){ return r }

  if(that.status === "resolved"){
    return promise2 = new MyPromise(function(resolve, reject){
      try{
        var x = onResolved(that.data)
        if(x instanceof MyPromise){ // 如果onResolved的返回值是一個(gè)Promise對(duì)象,直接取它的結(jié)果做為promise2的結(jié)果
          x.then(resolve, reject)
        }
        resolve(x) // 否則,以它的返回值做為promise2的結(jié)果 
      } catch(e) {
        reject(e) // 如果出錯(cuò),以捕獲到的錯(cuò)誤做為promise2的結(jié)果
      }
    })
  }

  if(that.status === "rejected"){
    return promise2 = new MyPromise(function(resolve, reject){
      try{
        var x = onRejected(that.data)
        if(x instanceof MyPromise){
          x.then(resolve, reject)
        }
      } catch(e) {
        reject(e)
      }
    })
  }

  if(that.status === "pending"){
    return promise2 = new MyPromise(function(resolve, reject){
      self.onResolvedCallback.push(function(reason){
        try{
          var x = onResolved(that.data)
          if(x instanceof MyPromise){
            x.then(resolve, reject)
          }
        } catch(e) {
          reject(e)
        }
      })

      self.onRejectedCallback.push(function(value){
        try{
          var x = onRejected(that.data)
          if(x instanceof MyPromise){
            x.then(resolve, reject)
          }
        } catch(e) {
          reject(e)
        }
      })
    })
  }
}

MyPromise.prototype.catch = function(onRejected){
  return this.then(null, onRejected)
}

// 以下是簡(jiǎn)單的測(cè)試樣例:
new MyPromise(resolve => resolve(8)).then(value => {
  console.log(value)
})
1.8 Event Loop
題目:說一下JS的EventLoop

其實(shí)阮一峰老師這篇《JavaScript 運(yùn)行機(jī)制詳解:再談Event Loop》已經(jīng)講的很清晰了(手動(dòng)贊)!

這里簡(jiǎn)單總結(jié)下:

JS是單線程的,其上面的所有任務(wù)都是在兩個(gè)地方執(zhí)行:執(zhí)行棧和任務(wù)隊(duì)列。前者是存放同步任務(wù);后者是異步任務(wù)有結(jié)果后,就在其中放入一個(gè)事件。

當(dāng)執(zhí)行棧的任務(wù)都執(zhí)行完了(??眨?,js會(huì)讀取任務(wù)隊(duì)列,并將可以執(zhí)行的任務(wù)從任務(wù)隊(duì)列丟到執(zhí)行棧中執(zhí)行。

這個(gè)過程是循環(huán)進(jìn)行,所以稱作Loop。

2. CSS相關(guān) 2.1 水平垂直居中
題目: 兩種以上方式實(shí)現(xiàn)已知或者未知寬度的垂直水平居中

第一種方法就是利用CSS3translate進(jìn)行偏移定位,注意:兩個(gè)參數(shù)的百分比都是針對(duì)元素本身計(jì)算的。

.wrap {
  position: relative;
  width: 100vw;
  height: 100vh;
}

.box {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

第二種方法是利用CSS3flex布局,父元素diplay屬性設(shè)置為flex,并且定義元素在兩條軸線的布局方式均為center

.wrap {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100vw;
  height: 100vh;
}

.wrap .box {
  width: 100px;
  height: 100px;
}

第三種方法是利用margin負(fù)值來進(jìn)行元素偏移,優(yōu)點(diǎn)是瀏覽器兼容好,缺點(diǎn)是不夠靈活(要自行計(jì)算margin的值):

.wrap {
  position: relative;
  width: 100vw;
  height: 100vh;
}

.box {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 100px;
  height: 100px;
  margin: -50px 0 0 -50px;
}
2.2 “點(diǎn)擊”改變樣式
題目:實(shí)現(xiàn)效果,點(diǎn)擊容器內(nèi)的圖標(biāo),圖標(biāo)邊框變成border 1px solid red,點(diǎn)擊空白處重置。

利用event.target可以判斷是否是指定元素本身(判斷“空白處”),除此之外,注意禁止冒泡(題目指明了“容器內(nèi)”)。




  
  
  
  Document
  


  
123456
3. 瀏覽器/協(xié)議相關(guān) 3.1 緩存機(jī)制
題目:說一下瀏覽器的緩存機(jī)制。

瀏覽器緩存分為強(qiáng)緩存和協(xié)商緩存。緩存的作用是提高客戶端速度、節(jié)省網(wǎng)絡(luò)流量、降低服務(wù)器壓力。

強(qiáng)緩存:瀏覽器請(qǐng)求資源,如果header中的Cache-ControlExpires沒有過期,直接從緩存(本地)讀取資源,不需要再向服務(wù)器請(qǐng)求資源。

協(xié)商緩存:瀏覽器請(qǐng)求的資源如果是過期的,那么會(huì)向服務(wù)器發(fā)送請(qǐng)求,header中帶有Etag字段。服務(wù)器再進(jìn)行判斷,如果ETag匹配,則返回給客戶端300系列狀態(tài)碼,客戶端繼續(xù)使用本地緩存;否則,客戶端會(huì)重新獲取數(shù)據(jù)資源。

關(guān)于過程中詳細(xì)的字段,可以參考這篇《http協(xié)商緩存VS強(qiáng)緩存》

3.2 從URL到頁面生成
題目:輸入U(xiǎn)RL到看到頁面發(fā)生的全過程,越詳細(xì)越好

DNS解析

建立TCP連接(3次握手)

發(fā)送HTTP請(qǐng)求,從服務(wù)器下載相關(guān)內(nèi)容

瀏覽器構(gòu)建DOM樹和CSS樹,然后生成渲染樹。這個(gè)一個(gè)漸進(jìn)式過程,引擎會(huì)力求最快將內(nèi)容呈現(xiàn)給用戶。

在第四步的過程中,

閱讀需要支付1元查看
<