備戰金三四銀—前端必問的面試題(1)

前言

又快到了到金三四銀找工作的黃金時間,通過這些時間做面試題整理了一些經典必問的面試題,相信會對大家有幫助!

如何知道(任何的)一個網站使用了多少種HTML標籤?

1,獲取所有的DOM節點

document.querySelectorAll('*')

此時得到的是一個NodeList集合,我們需要將其轉化成數組,然後對其篩選

2,轉化爲數組

[...document.querySelectorAll('*')]

一個拓展運算符就搞定

3,獲取數組每個元素的標籤名

[...document.querySelectorAll('*')}.map(ele => ele.tagName)

使用一個map方法,將我們需要的結果映射到一個新數組

4,去重

new Set([...document.querySelectorAll('*').map(ele=>ele.tagName)).size

我們使用ES6中的set對象,把數組作爲構造函數的參數,就實現了去重,在使用Set對象的size方法就可以得到有多少種HTML元素了

這段代碼運行結果

async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}
async function async2() {
    console.log('async2');
}
console.log('script start');
setTimeout(function() {
    console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
    console.log('promise1');
    resolve();
}).then(function() {
    console.log('promise2');
});
console.log('script end');

第一步,輸出script start

雖然有兩個函數聲明,有async關鍵字,但是沒有調用,就不看,直接打印同步代碼console.log(‘script start’)

第二步,輸出async1 start

因爲執行async1這個函數的時候,async表達式定義的函數也是立即執行

第三步,輸出async2

因爲async2是async定義的函數,輸出async2並返回promise對象

第四步,輸出promise1

因爲執行到new promise,promise是立即執行,就先打印promise1

第五步,輸出script end

因爲上一步先打印了promise1,然後執行到resolve的時候,然後跳出promise繼續向下執行,輸出script end

第六步,輸出promise2

因爲前面的nue promise放進resolve回調,所以resolve被放到調用棧執行

第七步,輸出async1 end

因爲await定義的這個promise已經執行完,並且返回結果,所以繼續執行async1函數後的任務,就是console.log(‘async1 end’)

第八步,輸出setTimerout

setTimeout被放在最後被調用

怎麼讓裏面的div垂直水平居中

列舉6種

<div class="parent">
    <div class="child">
    </div>
</div>

<style>
    .parent{
        width: 550px;
        height: 550px;
        border: 1px  solid #FF7F24;
        /* 方法一:定位 */
        /* position: relative; */

        /* 方法二:margin: auto */
        /* position: relative; */

        /* 方法三:利用display:table-cell */
        /* display:table-cell 加上 vertical-align:middle和text-align:center 使高度不同的元素都水平垂直居中,其中display:inline-block使這兩個div在同一行顯示 */
        /* display: table-cell;
        vertical-align: middle;
        text-align: center; */

        /* 方法四:利用display:flex */
        /* display: flex;
        justify-content: center;
        align-items: center; */

        /* 方法五:計算父盒子與子盒子的空間距離(這跟方法一是一個道理) */

        /* 方法六:利用transfrom */
        /* position: relative */

        /* 方法七:利用calc計算 */
        /* position: relative; */

    }
    .child{
        width: 150px;
        height: 150px;
        border: 1px solid #FF3030;
        /* 方法一:定位 */
        /* position: absolute;
        top: 50%;
        left: 50%;
        margin-top: -75px;
        margin-left: -75px;    */

        /* 方法二:margin: auto */
        /* top: 0,left: 0,right: 0,bottom: 0就像四個方向有相同的力在拉這個盒子,然後margin:auto相當於平方剩餘空間居中 */
        /* position: absolute;
        margin: auto;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0; */

        /* 方法三:利用display:table-cell */
        /* display: inline-block */

        /* 方法五:計算父盒子與子盒子的空間距離(這跟方法一是一個道理) */
        /* (parent550-child150)÷2=200 */
        /* margin-top: 200px;
        margin-left: 200px; */

        /* 方法六:利用transfrom */
        /* position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%,-50%); */

        /* 方法七:利用calc計算 */
        /* top和left都是(父元素高-子元素高)÷2 */
        /* position: absolute;
        top:calc(200px);
        left: calc(200px); */
    }
</style>

如何比較React和Vue?

1、監聽數據變化的實現原理不同

  • Vue通過getter/setter以及一些函數,能精確知道數據變化

  • React默認是通過比較引用的方式(diff)進行的,React不精確監聽數據變化

2、數據流不同

  • Vue2.0可以通過props實現雙向綁定,用vuex單向數據流的狀態管理框架

  • React不支持雙向綁定,提倡單項數據流,Redux單向數據流的狀態管理框架

3、組件通信的區別

  • Vue三種組件通信方法:

    父組件通過props向子組件傳遞數據或回調

    子組件通過事件event向父組件發送數據或回調

    通過provide/inject實現父組件向子組件傳入數據,可跨層級

  • React三種組件通信方法:

    父組件通過props向子組件傳遞數據

    React不支持子組件像父組件發送數據,而使用的是回調函數

    通過 context實現父組件向子組件傳入數據, 可跨層級

4、模板渲染方式不同

  • 表面上來看:

    React通過JSX渲染模板

    Vue通過HTML進行渲染

  • 深層上來看:

    React是通過原生JS實現模板中常見語法,如:插件,條件,循環

    Vue是與組件JS分離的單獨模板,通過指令實現,如:v-if

5、模板中使用的數據

  • React裏模板中使用的數據可以直接import的組件在render中調用

  • Vue裏模板中使用的數據必須要在this上進行中轉,還要import一個組件,還要在components中聲明

6、渲染過程不同

  • Vue不需要渲染整個組件樹

  • React狀態改變時,全部子組件重新渲染

7、框架本質不同

  • Vue本質是MVVM框架,由MVC發展而來

  • React是前端組件化框架,由後端組件化發展而來

8、Vuex和Redux的區別

  • Vuex可以使用dispatch、commit提交更新

  • Redux只能用dispatch提交更新

9、組合不同功能方式不同

  • Vue組合不同功能方式是通過mixin,可以幫我定義的模板進行編譯、聲明的props接收到數據….

  • React組合不同功能方式是通過HoC(高階組件),本質是高階函數

談談你對MVVM的理解

MVVM:MVVM是Model-View-ViewModel的簡寫

M指的是模型,V指的是視圖,VM指的是視圖模型

模型指的是後端傳遞的數據

視圖指的是所看到的頁面,試圖模型mvvm模式的核心,它是連接view和model的橋樑。
它有兩個方向:一是將模型轉化成試圖,即將後端傳遞的數據轉化成所看到的頁面。實現的方式是:數據綁定。二是將視圖轉化爲模型,即將所看到的頁面轉化成後端數據。

視圖模型指的是界面和對應的model,因爲數據庫結構不能直接跟界面控件一一對應,所以,需要再定義一個數據對象專門對應view上的控件,而ViewModel的職責就是把model對象封裝成可以顯示和接受輸入的界面數據對象

談談你對淺拷貝和深拷貝的理解

比如:

A複製了B,B被修改後

A要是隨着B的變化而變化,那就是淺拷貝

要是B改變了,A不變,那就是深拷貝

淺拷貝:

拷貝一層,對象級別的則拷貝引用

深拷貝:

拷貝多層,每個層級的屬性都會拷貝

vue組件間的6種通信方法

父向子傳遞數據通過props

子向父傳遞是通過$emit、event

子實例訪問父實例通過$parent

父實例訪問子實例通過$children

$attrs用父組件傳遞數據給子組件或孫組件
(包含了父作用域中不作爲 prop 被識別 (且獲取) 的特性綁定 (class 和 style 除外))

listeners用父組件傳遞數據給子組件或孫組件
包含了父作用域中的 (不含 .native 修飾器的) v-on 事件監聽器

祖先組件通過provider提供變量給子孫組件

子孫組件通過inject注入變量給祖先組件

ref用來訪問組件實例

emit用於父子、隔代、兄弟組件通信

on用於父子、隔代、兄弟組件通信

vuex用來作爲兄弟之間和跨級之間的通信

call,apply和bind相同點和區別?

相同點:

都是用來改變函數的this對象的指向的

第一個參數都是this要指向的對象

都可以利用後續參數傳參

區別:

call、apply的區別:接受參數的方式不一樣

bind:不立即執行。而apply、call 立即執行

談談防抖和節流

如果事件處理函數調用的頻率無限制,會加重瀏覽器的負擔,導致用戶體驗非常糟糕。此時我們可以採用debounce(防抖)和throttle(節流)的方式來減少調用頻率,同時又不影響實際效果

防抖:當持續觸發事件時,一定時間段內沒有再觸發事件,事件處理函數纔會執行一次,如果設定的時間到來之前,又一次觸發了事件,就重新開始延時

節流:當持續觸發事件時,保證一定時間段內只調用一次事件處理函數,原理是通過判斷是否到達一定時間來觸發函數。

節流不管事件觸發有多頻繁,都會保證在規定時間內一定會執行一次真正的事件處理函數,而函數防抖只是在最後一次事件後才觸發一次函數。 比如在頁面的無限加載場景下,我們需要用戶在滾動頁面時,每隔一段時間發一次 Ajax 請求,而不是在用戶停下滾動頁面操作時纔去請求數據。這樣的場景,就適合用節流技術來實現

給數字增加“逗號”分隔?

例如輸入: ‘“123456789.888”’ 輸出:123,456,789.888

var rmb = 123456789.888; 
var retRmb = rmb.toFixed(3).replace(/\d{1,3}(?=(\d{3})+(\.\d*)?$)/g, '$&,'); 
console.log(retRmb);

描述一下vue響應原理?

Vue響應式底層實現方法是Object.defineProperty()方法,該方法中存在一個getter和setter的可選項,可以對屬性值的獲取和設置造成影響

Vue中編寫了一個wather來處理數據

在使用getter方法時,總會通知wather實例對view層渲染頁面

同樣的,在使用setter方法時,總會在變更值的同時,通知wather實例對view層進行更新

js中有幾種判斷數據類型的方法?

js的基本數據類型:

number、string、boolean、null、undefined、symbol(es6新增)

js的引用數據類型:

object、function

1、通過typeof判斷

通過typeof無法判斷出數組、對象、null的具體類型

  console.log(typeof 10); //number
  console.log(typeof NaN); //number

  console.log(typeof "10"); //string
  console.log(typeof false); //boolean
  console.log(typeof Symbol()); //symbol
  console.log(typeof function () {}); //function

  console.log(typeof []); //object
  console.log(typeof {}); //object
  console.log(typeof null); //object
  console.log(typeof undefined); //undefined

2、通過constructor判斷

通過constructor判斷數組和對象,但無法判斷null,因爲null不是對象 沒有construtor

  console.log("".constructor == String); //true
  console.log(false.constructor == Boolean); //true
  console.log(new Number(10).constructor == Number); //true 這裏要使用對象的方式
  console.log([].constructor == Array); //true
  console.log({}.constructor == Object); //true

3、通過instanceof判斷

instanceof是用於判斷A是否爲B的實例,因爲instanceof是基於原型鏈進行檢測的,所以可以通過instanceof檢測數組,但是要檢測對象就不是那麼準確了,因爲數組對象也是Object

  console.log([] instanceof Array); //true
  console.log([] instanceof Object); //true
  console.log({} instanceof Array); //false
  console.log({} instanceof Object); //true

4、通過Object.prototype.toString.call判斷

都能判斷出來,包括unll undefined

  let getType = Object.prototype.toString;
  console.log(getType.call(undefined)); //[object Undefined]
  console.log(getType.call(null)); //[object Null]
  console.log(getType.call([])); //[object Array]
  console.log(getType.call({})); //[object Object]

js中事件傳遞有哪幾個階段?

捕獲階段:先由文檔的根節點document往事件觸發對象,從外向內捕獲事件對象

目標階段:到達目標事件位置(事發地),觸發事件

冒泡階段:再從目標事件位置往文檔的根節點方向回溯,從內向外冒泡事件對象

談談對this的理解?

this表示當前對象,this的指向是根據調用的上下文來決定的,默認指向window對象,指向window對象時可以省略不寫

調用的上下文環境包括全局和局部

全局環境:
全局環境就是在裏面,這裏的this始終指向的是window對象

局部環境:
在全局作用域下直接調用函數,this指向window
對象函數調用,哪個對象調用就指向哪個對象
使用new實例化對象,在構造函數中的this指向實例化對象
使用call或apply改變this的指向

其它:
用於區分全局變量和局部變量,需要使用this
返回函數當前的對象

瀏覽器地址輸入URL到看到界面經歷了哪些?

  • 輸入網址
  • 緩存解析
  • 域名解析
  • tcp連接,三層握手
  • 頁面渲染

詳細介紹一下前端緩存

前端緩存主要分爲HTTP緩存和瀏覽器緩存,

其中HTTP緩存是在HTTP請求傳輸時用到的緩存,

主要在服務器代碼上設置,而瀏覽器緩存則主要由前端js上進行設置

你知道閉包嗎?平時工作中又有用到過嗎?適用於什麼場景?

閉包就是能夠讀取其他函數內部變量的函數,也就是個函數,只不過是處於其他函數內部而已
由於在javascript中,只有函數內部的子函數才能讀取局部變量,所以說,閉包可以簡單理解成“定義在一個函數內部的函數“

所以,在本質上,閉包是將函數內部和函數外部連接起來的橋樑

閉包可以讀取函數內部的變量,和讓這些變量的值始終保持在內存中

適用場景:

採用函數引用方式的setTimeout調用

將函數關聯到對象的實例方法

封裝相關的功能集

說說JS中原型鏈與繼承

每個構造函數都有一個原型對象,

原型對象都包含一個指向構造函數想指針(constructor),而實例對象都包含一個指向原型對象的內部指針(proto)。

如果讓原型對象等於另一個類型的實例,此時的原型對象將包含一個指向另一個原型的指針(proto),另一個原型也包含着一個指向另一個構造函數的指針(constructor)。

假如另一個原型又是另一個類型的實例……這就構成了實例與原型的鏈條

原型鏈是實現繼承的主要方法

你瞭解前端跨域嗎?有哪些方法可以處理跨域?

跨域是指一個域下的文檔或腳本試圖去請求另一個域下的資源

跨域的解決方案:

  • 通過jsonp跨域
  • document.domain + iframe跨域
  • location.hash + iframe
  • window.name + iframe跨域
  • postMessage跨域
  • 跨域資源共享(CORS)
  • nginx代理跨域
  • nodejs中間件代理跨域
  • WebSocket協議跨域

Proxy與Object.defineProperty的優缺點?

Proxy的優點:

可以直接監聽對象而非屬性,並返回一個新對象

可以直接監聽數值的變化

攔截方法較多

可以劫持整個對象,並返回一個新對象

Proxy作爲新標準將受到瀏覽器廠商重點持續的性能優化

Proxy的缺點:

Proxy是es6提供的新特性,兼容性不好

Object.defineProperty的優點:

兼容性好,支持IE9

IE9以下的版本不兼容

Object.defineProperty的缺點:

無法監控到數組下標的變化,導致直接通過數組的下標給數組設置值,不能實時響應

只能劫持對象的屬性,我們需要對每個對象的每個屬性進行遍歷

在vue2.0中,是通過遞歸+遍歷data對象來實現對數據的監控的,

如果屬性值也是對象,那麼需要深度遍歷,顯然如果能劫持一個完整的對象纔是最好的選擇,

所以在vue2.0中選Object.defineProperty,

當時雖然es6的新屬性出現了Proxy,但是兼容性不好,最主要的是這個屬性無法用polyfill來兼容,

但是在vue3.0的版本中會有效的提供兼容解決方案

在vue3.0中,Proxy不用像Object.defineProperty給屬性都設置set,get的方法,所以Proxy不會觸發循環調用

Vue中的key的作用?

key的作用是爲了在diff算法執行時更快的找到對應的節點,提高diff速度

vue組件高度複用增加key可以標識組件的唯一性,爲了更高效更新虛擬DOM

談談 Vue 響應式原理?

Vue響應式底層實現方法是Object.defineProperty()方法,該方法中存在一個getter和setter的可選項,可以對屬性值的獲取和設置造成影響

Vue中編寫了一個wather來處理數據

在使用getter方法時,總會通知wather實例對view層渲染頁面

同樣的,在使用setter方法時,總會在變更值得同時,通知wather實例對view層進行更新

Vue 組件 data 選項爲什麼一定要是函數?

如果data是一個函數的話,這樣沒複用一次組件,就會返回一份新的data,類似於給每個組件實例創建一個私有的數據空間,讓各個組件實例維護各自的數據。而單純的寫成對象形式,就使得所有組件實例共用一份data,就會造成一個變了全都會變得結果

所以說vue組件的data必須是函數。這都是因爲js的特性帶來的,跟vue本身設計無關。

js本身的面向對象編程也是基於原型鏈和構造函數,應該會注意原型鏈上添加一般都是一個函數方法而不會去添加一個對象了

談談 Vue 的渲染過程?

1、把模板編譯爲render函數

2、實例進行掛載, 根據根節點render函數的調用,遞歸的生成虛擬dom

3、對比虛擬dom,渲染到真實dom

4、組件內部data發生變化,組件和子組件引用data作爲props重新調用render函數,生成虛擬dom, 返回到步驟3

你對Webpack4瞭解嗎?可以說說它的打包原理嗎?

webpack是一個打包模塊化JavaScript的工具,在webpack裏一切文件皆模塊,通過Loader轉換文件,通過Plugin注入鉤子,最後輸出由多個模塊組合成得文件,webpack專注於構建模塊化項目

1、利用babel完成代碼轉換及解析,並生成單個文件的依賴模塊Map

2、從入口開始遞歸分析,並生成整個項目的依賴圖譜

3、將各個引用模塊打包爲一個立即執行函數

4、將最終的bundle文件寫入bundle.js中

用CSS畫一個三角形的原理?

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>
<style>
    #app{
        width: 0;
        height: 0;
        border: 93px solid;
        border-color: red green yellow transparent;
    }
</style>

cmd-markdown-logo
這裏主要用到了CSS3的一個transparent屬性

transparent是透明的意思,在CSS3以後,支持所有的coloe屬性

畫的三角形其實border的寬度加上顏色來控制的,其實三角形的形成不過是,其它的邊框顏色設置成了透明瞭而已

CSS3有哪些新特性?

  • CSS3的選擇器:

    E:last-child匹配父元素的最後一個子元素E

    E:nth-child(n)匹配父元素的第n個子元素E

    E:nth-last-child(n)匹配父元素的倒數第n個子元素E

  • @Font-face特性

  • 圓角(border-radius)

  • 多列布局

  • 陰影(Shadow)

  • CSS3的漸變效果

  • CSS彈性盒子模型

  • transition對象變換時的過渡效果

  • transforms 2D轉換效果

  • Animation動畫效果

position有哪些值?分別代表什麼作用?

static:默認值 沒有定位,元素出現在正常的流中

absolute:絕對定位 相對於static定位以外的第一個父元素進行定位

relative:相對定位 生成相對定位的元素,相對於其正常位置進行定位

fixed:固定定位 生成絕對定位的元素,相對於瀏覽器窗口進行定位

你對緩存有了解嗎?

緩存是建立一個函數的過程,這個函數能夠記住之前計算的結果或值。使用緩存函數是爲了避免在最後一次使用相同參數的計算中已經執行的函數的計算。這節省了時間,但也有不利的一面,即我們將消耗更多的內存來保存以前的結果

js有哪些繼承方式?

原型鏈繼承:父類的實例作爲子類的原型

借用構造函數繼承:複製父類的實例屬性給子類

組合繼承:調用父類構造函數,繼承父類的屬性,通過將父類實例作爲子類原型,實現函數複用

原型式繼承:不自定義類型的情況下,臨時創建一個構造函數,藉助已有的對象作爲臨時構造函數的原型,然後在此基礎實例化對象,並返回

寄生式繼承:其實就是在原型式繼承得到對象的基礎上,在內部再以某種方式來增強對象,然後返回

寄生組合繼承:通過寄生的方式來修復組合式繼承的不足,完美的實現繼承

最後

如果本文對你有幫助得話,給本文點個贊❤️❤️❤️

歡迎大家加入,一起學習前端,共同進步!
cmd-markdown-logo
cmd-markdown-logo

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章