面試完50個人後我寫下這篇總結

謝小飛發表於2019-12-12

  2019年的春節來的似乎格外的早,過完年相信很多童鞋都開始蠢蠢欲動了;筆者總結了多篇教程,結合平時自己的面試經歷,整理了這份文件,希望幫助大家來突擊一下前端知識的盲區。文章很長很長很長。。。。(建議先收藏,技術大佬請Ctrl+F4,面向基礎

整理不易,希望大家關注公眾號【前端壹讀】,更多前端原創好文等著你。

一、CSS

盒模型

  CSS盒模型本質上是一個盒子,封裝周圍的HTML元素,它包括:邊距margin,邊框border,填充padding,和實際內容content。盒模型允許我們在其它元素和周圍元素邊框之間的空間放置元素。

Box-Model.jpg

box-sizing: content-box(W3C盒模型,又名標準盒模型):元素的寬高大小表現為內容的大小。 box-sizing: border-box(IE盒模型,又名怪異盒模型):元素的寬高表現為內容 + 內邊距 + 邊框的大小。背景會延伸到邊框的外沿。

CSS3的新特性

  • word-wrap 文字換行
  • text-overflow 超過指定容器的邊界時如何顯示
  • text-decoration 文字渲染
  • text-shadow文字陰影
  • gradient漸變效果
  • transition過渡效果 transition-duration:過渡的持續時間
  • transform拉伸,壓縮,旋轉,偏移等變換
  • animation動畫

transition和animation的區別:

  Animation和transition大部分屬性是相同的,他們都是隨時間改變元素的屬性值,他們的主要區別是transition需要觸發一個事件才能改變屬性,而animation不需要觸發任何事件的情況下才會隨時間改變屬性值,並且transition為2幀,從from .... to,而animation可以一幀一幀的。

CSS選擇器及其優先順序

  • !important
  • 內聯樣式style=""
  • ID選擇器#id
  • 類選擇器/屬性選擇器/偽類選擇器.class.active[href=""]
  • 元素選擇器/關係選擇器/偽元素選擇器html+div>span::after
  • 萬用字元選擇器*

BFC

BFC(Block Formatting Context)格式化上下文,是Web頁面中盒模型佈局的CSS渲染模式,指一個獨立的渲染區域或者說是一個隔離的獨立容器。 

BFC應用

  • 防止margin重疊
  • 清除內部浮動
  • 自適應兩(多)欄佈局
  • 防止字型環繞

觸發BFC條件

  • 根元素
  • float的值不為none
  • overflow的值不為visible
  • display的值為inline-block、table-cell、table-caption
  • position的值為absolute、fixed

BFC的特性

  • 內部的Box會在垂直方向上一個接一個的放置。
  • 垂直方向上的距離由margin決定
  • bfc的區域不會與float的元素區域重疊。
  • 計算bfc的高度時,浮動元素也參與計算
  • bfc就是頁面上的一個獨立容器,容器裡面的子元素不會影響外面元素。

div水平居中

  1. 行內元素
.parent {
    text-align: center;
}
複製程式碼
  1. 塊級元素
.son {
    margin: 0 auto;
}
複製程式碼
  1. flex佈局
.parent {
    display: flex;
    justify-content: center;
}
複製程式碼
  1. 絕對定位定寬
.son {
    position: absolute;
    width: 寬度;
    left: 50%;
    margin-left: -0.5*寬度
}
複製程式碼
  1. 絕對定位不定寬
.son {
    position: absolute;
    left: 50%;
    transform: translate(-50%, 0);
}
複製程式碼
  1. left/right: 0
.son {
    position: absolute;
    width: 寬度;
    left: 0;
    right: 0;
    margin: 0 auto;
}
複製程式碼

div垂直居中

  1. 行內元素
.parent {
    height: 高度;
}
.son {
    line-height: 高度;
}
複製程式碼
  1. table
.parent {
  display: table;
}
.son {
  display: table-cell;
  vertical-align: middle;
}
複製程式碼
  1. flex
.parent {
    display: flex;
    align-items: center;
}
複製程式碼
  1. 絕對定位定高
.son {
    position: absolute;
    top: 50%;
    height: 高度;
    margin-top: -0.5高度;
}
複製程式碼
  1. 絕對定位不定高
.son {
    position: absolute;
    top: 50%;
    transform: translate( 0, -50%);
}
複製程式碼
  1. top/bottom: 0;
.son {
    position: absolute;
    height: 高度;
    top: 0;
    bottom: 0;
    margin: auto 0;
}
複製程式碼

絕對定位和相對定位

  • absolute 絕對定位 相對於最近的已定位的祖先元素, 有已定位(指position不是static的元素)祖先元素, 以最近的祖先元素為參考標準。如果無已定位祖先元素, 以body元素為偏移參照基準, 完全脫離了標準文件流。

  • fixed 固定定位的元素會相對於視窗來定位,這意味著即便頁面滾動,它還是會停留在相同的位置。一個固定定位元素不會保留它原本在頁面應有的空隙。

共同點:改變行內元素的呈現方式,都脫離了文件流;不同點:absolute的”根元素“是可以設定的,fixed的“根元素”固定為瀏覽器視窗

flex佈局

採用 Flex 佈局的元素,稱為 Flex 容器(flex container),簡稱"容器"。它的所有子元素自動成為容器成員,稱為 Flex 專案(flex item),簡稱“專案”。

flex.jpeg

  1. 父元素屬性
屬性名 屬性值 備註
display flex 定義了一個flex容器,它的直接子元素會接受這個flex環境
flex-direction row,row-reverse,column,column-reverse 決定主軸的方向
flex-wrap nowrap,wrap,wrap-reverse 如果一條軸線排不下,如何換行
flex-flow [flex-direction] , [flex-wrap] flex-direction屬性和flex-wrap屬性的簡寫形式,預設值為row nowrap
justify-content flex-start,flex-end,center,space-between,space-around 設定或檢索彈性盒子元素在主軸(橫軸)方向上的對齊方式
align-items flex-start,flex-end,center,baseline,stretch 設定或檢索彈性盒子元素在側軸(縱軸)方向上的對齊方式
  1. 子元素屬性
屬性名 屬性值 備註
order [int] 預設情況下flex order會按照書寫順訓呈現,可以通過order屬性改變,數值小的在前面,還可以是負數。
flex-grow [number] 設定或檢索彈性盒的擴充套件比率,根據彈性盒子元素所設定的擴充套件因子作為比率來分配剩餘空間
flex-shrink [number] 設定或檢索彈性盒的收縮比率,根據彈性盒子元素所設定的收縮因子作為比率來收縮空間
flex-basis [length], auto 設定或檢索彈性盒伸縮基準值
align-self auto,flex-start,flex-end,center,baseline,stretch 設定或檢索彈性盒子元素在側軸(縱軸)方向上的對齊方式,可以覆蓋父容器align-items的設定

讓元素消失

visibility:hidden、display:none、z-index=-1、opacity:0

  1. opacity:0,該元素隱藏起來了,但不會改變頁面佈局,並且,如果該元素已經繫結了一些事件,如click事件也能觸發
  2. visibility:hidden,該元素隱藏起來了,但不會改變頁面佈局,但是不會觸發該元素已經繫結的事件
  3. display:none, 把元素隱藏起來,並且會改變頁面佈局,可以理解成在頁面中把該元素刪掉
  4. z-index=-1置於其他元素下面

清除浮動

  1. 在浮動元素後面新增 clear:both 的空 div 元素,
<div class="container">
    <div class="left"></div>
    <div class="right"></div>
    <div style="clear:both"></div>
</div>
複製程式碼
  1. 給父元素新增 overflow:hidden 或者 auto 樣式,觸發BFC。
<div class="container">
    <div class="left"></div>
    <div class="right"></div>
</div>
複製程式碼
.container{
    width: 300px;
    background-color: #aaa;
    overflow:hidden;
    zoom:1;   /*IE6*/
}
複製程式碼
  1. 使用偽元素,也是在元素末尾新增一個點並帶有 clear: both 屬性的元素實現的。
<div class="container clearfix">
    <div class="left"></div>
    <div class="right"></div>
</div>
複製程式碼
.clearfix{
    zoom: 1; /*IE6*/
}
.clearfix:after{
    content: ".";
    height: 0;
    clear: both;
    display: block;
    visibility: hidden;
}
複製程式碼

推薦使用第三種方法,不會在頁面新增div,文件結構更加清晰。

calc函式

calc函式是css3新增的功能,可以使用calc()計算border、margin、pading、font-size和width等屬性設定動態值。

#div1 {
    position: absolute;
    left: 50px;
    width: calc( 100% / (100px * 2) );
    //相容寫法
    width: -moz-calc( 100% / (100px * 2) );
    width: -webkit-calc( 100% / (100px * 2) );
    border: 1px solid black;
}
複製程式碼

注意點:

  • 需要注意的是,運算子前後都需要保留一個空格,例如:width: calc(100% - 10px);
  • calc()函式支援 "+", "-", "*", "/" 運算;
  • 對於不支援 calc() 的瀏覽器,整個屬性值表示式將被忽略。不過我們可以對那些不支援 calc()的瀏覽器,使用一個固定值作為回退。

移動端rem

rem官方定義『The font size of the root element』,即根元素的字型大小。rem是一個相對的CSS單位,1rem等於html元素上font-size的大小。所以,我們只要設定html上font-size的大小,就可以改變1rem所代表的大小。

(function () {
    var html = document.documentElement;
    function onWindowResize() {
        html.style.fontSize = html.getBoundingClientRect().width / 20 + 'px';
    }
    window.addEventListener('resize', onWindowResize);
    onWindowResize();
})();
複製程式碼

移動端1px

一般來說,在PC端瀏覽器中,裝置畫素比(dpr)等於1,1個css畫素就代表1個物理畫素;但是在retina螢幕中,dpr普遍是2或3,1個css畫素不再等於1個物理畫素,因此比實際設計稿看起來粗不少。

  1. 偽元素+scale
<style>
    .box{
        width: 100%;
        height: 1px;
        margin: 20px 0;
        position: relative;
    }
    .box::after{
        content: '';
        position: absolute;
        bottom: 0;
        width: 100%;
        height: 1px;
        transform: scaleY(0.5);
        transform-origin: 0 0; 
        background: red;
    }
</style>

<div class="box"></div>

複製程式碼
  1. border-image
div{
    border-width: 1px 0px;
    -webkit-border-image: url(border.png) 2 0 stretch;
    border-image: url(border.png) 2 0 stretch;
}
複製程式碼

兩邊寬度固定中間自適應的三欄佈局

聖盃佈局和雙飛翼佈局是前端工程師需要日常掌握的重要佈局方式。兩者的功能相同,都是為了實現一個兩側寬度固定,中間寬度自適應的三欄佈局。

聖盃佈局

<style>
body{
    min-width: 550px;
}
#container{
    padding-left: 200px;
    padding-right: 150px;
}
#container .column{
    float: left;
}
#center{
    width: 100%;
}
#left{
    width: 200px;
    margin-left: -100%;
    position: relative;
    right: 200px;
}
#right{
    width: 150px;
    margin-right: -150px;
}
</style>
<div id="container">
    <div id="center" class="column">center</div>
    <div id="left" class="column">left</div>
    <div id="right" class="column">right</div>
</div>
複製程式碼

Layout.gif

雙飛翼佈局

<style>
body {
    min-width: 500px;
}
#container {
    width: 100%;
}
.column {
    float: left;
}
#center {
    margin-left: 200px;
    margin-right: 150px;
}
#left {
    width: 200px;
    margin-left: -100%;
}
#right {
    width: 150px;
    margin-left: -150px;
}
</style>
<div id="container" class="column">
    <div id="center">center</div>
</div>
<div id="left" class="column">left</div>
<div id="right" class="column">right</div>

複製程式碼

偽類和偽元素

css引入偽類和偽元素概念是為了格式化文件樹以外的資訊。也就是說,偽類和偽元素都是用來修飾不在文件樹中的部分。

before-after.jpg

偽類

偽類存在的意義是為了通過選擇器找到那些不存在DOM樹中的資訊以及不能被常規CSS選擇器獲取到的資訊。

  1. 獲取不存在與DOM樹中的資訊。比如a標籤的:link、visited等,這些資訊不存在與DOM樹結構中,只能通過CSS選擇器來獲取;
  2. 獲取不能被常規CSS選擇器獲取的資訊。比如:要獲取第一個子元素,我們無法用常規的CSS選擇器獲取,但可以通過 :first-child 來獲取到。

weilei.png

偽元素

偽元素用於建立一些不在文件樹中的元素,併為其新增樣式。比如說,我們可以通過:before來在一個元素前增加一些文字,併為這些文字新增樣式。雖然使用者可以看到這些文字,但是這些文字實際上不在文件樹中。常見的偽元素有:::before::after::first-line::first-letter::selection::placeholder

因此,偽類與偽元素的區別在於:有沒有建立一個文件樹之外的元素。

::after和:after的區別

在實際的開發工作中,我們會看到有人把偽元素寫成:after,這實際是 CSS2 與 CSS3新舊標準的規定不同而導致的。

CSS2 中的偽元素使用1個冒號,在 CSS3 中,為了區分偽類和偽元素,規定偽元素使用2個冒號。所以,對於 CSS2 標準的老偽元素,比如:first-line:first-letter:before:after,寫一個冒號瀏覽器也能識別,但對於 CSS3 標準的新偽元素,比如::selection,就必須寫2個冒號了。

CSS畫圓半圓扇形三角梯形

div{
    margin: 50px;
    width: 100px;
    height: 100px;
    background: red;
}
/* 半圓 */
.half-circle{
    height: 50px;
    border-radius: 50px 50px 0 0;
}
/* 扇形 */
.sector{
    border-radius: 100px 0 0;
}
/* 三角 */
.triangle{
    width: 0px;
    height: 0px;
    background: none;
    border: 50px solid red;
    border-color: red transparent transparent transparent;
}
/* 梯形 */
.ladder{
    width: 50px;
    height: 0px;
    background: none;
    border: 50px solid red;
    border-color: red transparent transparent transparent;
}
複製程式碼

二、JS

JS資料型別

JS基本有5種簡單資料型別:String,Number,Boolean,Null,Undefined。引用資料型別:Object,Array,Function。

判斷資料型別的方法

在寫業務邏輯的時候,經常要用到JS資料型別的判斷,面試常見的案例深淺拷貝也要用到資料型別的判斷。

typeof

console.log(typeof 2);               // number
console.log(typeof true);            // boolean
console.log(typeof 'str');           // string
console.log(typeof undefined);       // undefined
console.log(typeof []);              // object 
console.log(typeof {});              // object
console.log(typeof function(){});    // function
console.log(typeof null);            // object
複製程式碼

優點:能夠快速區分基本資料型別 缺點:不能將Object、Array和Null區分,都返回object

instanceof

console.log(2 instanceof Number);                    // false
console.log(true instanceof Boolean);                // false 
console.log('str' instanceof String);                // false  
console.log([] instanceof Array);                    // true
console.log(function(){} instanceof Function);       // true
console.log({} instanceof Object);                   // true
複製程式碼

優點:能夠區分Array、Object和Function,適合用於判斷自定義的類例項物件 缺點:Number,Boolean,String基本資料型別不能判斷

Object.prototype.toString.call()

var toString = Object.prototype.toString;
 
console.log(toString.call(2));                      //[object Number]
console.log(toString.call(true));                   //[object Boolean]
console.log(toString.call('str'));                  //[object String]
console.log(toString.call([]));                     //[object Array]
console.log(toString.call(function(){}));           //[object Function]
console.log(toString.call({}));                     //[object Object]
console.log(toString.call(undefined));              //[object Undefined]
console.log(toString.call(null));                   //[object Null]
複製程式碼

優點:精準判斷資料型別 缺點:寫法繁瑣不容易記,推薦進行封裝後使用

var,let,const的區別

letES6 新新增申明變數的命令,它類似於 var,但是有以下不同:

  • var 宣告的變數,其作用域為該語句所在的函式內,且存在變數提升現象
  • let 宣告的變數,其作用域為該語句所在的程式碼塊內,不存在變數提升
  • const宣告的變數不允許修改

null和undefined區別

Undefined型別只有一個值,即undefined。當宣告的變數還未被初始化時,變數的預設值為undefined。用法:

  • 變數被宣告瞭,但沒有賦值時,就等於undefined。
  • 呼叫函式時,應該提供的引數沒有提供,該引數等於undefined。
  • 物件沒有賦值的屬性,該屬性的值為undefined。
  • 函式沒有返回值時,預設返回undefined。

Null型別也只有一個值,即null。null用來表示尚未存在的物件,常用來表示函式企圖返回一個不存在的物件。用法

  • 作為函式的引數,表示該函式的引數不是物件。
  • 作為物件原型鏈的終點。

定義函式的方法

  1. 函式宣告
//ES5
function getSum(){}
function (){}//匿名函式
//ES6
()=>{}
複製程式碼
  1. 函式表示式
//ES5
var getSum=function(){}
//ES6
let getSum=()=>{}
複製程式碼
  1. 建構函式
const getSum = new Function('a', 'b' , 'return a + b')
複製程式碼

JS作用域的理解

JS中的作用域分為兩種:全域性作用域和函式作用域。函式作用域中定義的變數,只能在函式中呼叫,外界無法訪問。沒有塊級作用域導致了if或for這樣的邏輯語句中定義的變數可以被外界訪問,因此ES6中新增了let和const命令來進行塊級作用域的宣告。

更多作用域的瞭解可以看JS作用域

閉包的理解

簡單來說閉包就是在函式裡面宣告函式,本質上說就是在函式內部和函式外部搭建起一座橋樑,使得子函式可以訪問父函式中所有的區域性變數,但是反之不可以,這只是閉包的作用之一,另一個作用,則是保護變數不受外界汙染,使其一直存在記憶體中,在工作中我們還是少使用閉包的好,因為閉包太消耗記憶體,不到萬不得已的時候儘量不使用。

更多閉包的內容可以看JS閉包

陣列去重

let arr = [1,'1',2,'2',1,2,'x','y','f','x','y','f'];
function unique1(arr){
	let result = [arr[0]];
	for (let i = 1; i < arr.length; i++) {
		let item = arr[i];
		if(result.indexOf(item) == -1){
			result.push(item);
		}
	}
	return result;
}
console.log(unique1(arr));
複製程式碼

更多JS去重的方法JS陣列去重

call,apply和bind區別

三個函式的作用都是將函式繫結到上下文中,用來改變函式中this的指向;三者的不同點在於語法的不同。

fun.call(thisArg[, arg1[, arg2[, ...]]])
fun.apply(thisArg, [argsArray])
複製程式碼

所以applycall的區別是call方法接受的是若干個引數列表,而apply接收的是一個包含多個引數的陣列。

而bind()方法建立一個新的函式, 當被呼叫時,將其this關鍵字設定為提供的值,在呼叫新函式時,在任何提供之前提供一個給定的引數序列。

var bindFn = fun.bind(thisArg[, arg1[, arg2[, ...]]])
bindFn()
複製程式碼

Demos:

var name = 'window';
var sayName = function (param) {
    console.log('my name is:' + this.name + ',my param is ' + param)
};
//my name is:window,my param is window param
sayName('window param')

var callObj = {
    name: 'call'
};
//my name is:call,my param is call param
sayName.call(callObj, 'call param');


var applyObj = {
    name: 'apply'
};
//my name is:apply,my param is apply param
sayName.apply(applyObj, ['apply param']);

var bindObj = {
    name: 'bind'
}
var bindFn = sayName.bind(bindObj, 'bind param')
//my name is:bind,my param is bind param
bindFn();
複製程式碼

==和===區別

  • ==, 兩邊值型別不同的時候,要先進行型別轉換,再比較
  • ===,不做型別轉換,型別不同的一定不等。

==型別轉換過程:

  1. 如果型別不同,進行型別轉換
  2. 判斷比較的是否是 null 或者是 undefined, 如果是, 返回 true .
  3. 判斷兩者型別是否為 string 和 number, 如果是, 將字串轉換成 number
  4. 判斷其中一方是否為 boolean, 如果是, 將 boolean 轉為 number 再進行判斷
  5. 判斷其中一方是否為 object 且另一方為 string、number 或者 symbol , 如果是, 將 object 轉為原始型別再進行判斷

經典面試題:[] == ![] 為什麼是true

轉化步驟:

  1. !運算子優先順序最高,![]會被轉為為false,因此表示式變成了:[] == false
  2. 根據上面第(4)條規則,如果有一方是boolean,就把boolean轉為number,因此表示式變成了:[] == 0
  3. 根據上面第(5)條規則,把陣列轉為原始型別,呼叫陣列的toString()方法,[]轉為空字串,因此表示式變成了:'' == 0
  4. 根據上面第(3)條規則,兩邊資料型別為string和number,把空字串轉為0,因此表示式變成了:0 == 0
  5. 兩邊資料型別相同,0==0為true

深拷貝和淺拷貝

淺拷貝

function simpleClone(obj) {
    var result = {};
    for (var i in obj) {
        result[i] = obj[i];
    }
    return result;
}
複製程式碼

深拷貝,遍歷物件中的每一個屬性

function deepClone(obj) {
    let result;
    if (typeof obj == 'object') {
        result = isArray(obj) ? [] : {}
        for (let i in obj) {
            //isObject(obj[i]) ? deepClone(obj[i]) : obj[i]
            //多謝"朝歌在掘金"指出,多維陣列會有問題
            result[i] = isObject(obj[i])||isArray(obj[i])?deepClone(obj[i]):obj[i]
        }
    } else {
        result = obj
    }
    return result
}
function isObject(obj) {
    return Object.prototype.toString.call(obj) == "[object Object]"
}
function isArray(obj) {
    return Object.prototype.toString.call(obj) == "[object Array]"
}
複製程式碼

防抖和節流

debounce_throttle.png

防抖

function debounce(fn, delay) {
  let timer = null;
  return function () {
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, arguments);
    }, delay);
  }
}
複製程式碼

節流

function throttle(fn, cycle) {
  let start = Date.now();
  let now;
  let timer;
  return function () {
    now = Date.now();
    clearTimeout(timer);
    if (now - start >= cycle) {
      fn.apply(this, arguments);
      start = now;
    } else {
      timer = setTimeout(() => {
        fn.apply(this, arguments);
      }, cycle);
    }
  }
}

複製程式碼

cookie,sessionStorage和localStorage

  • cookie用來儲存登入資訊,大小限制為4KB左右
  • localStorage是Html5新增的,用於本地資料儲存,儲存的資料沒有過期時間,一般瀏覽器大小限制在5MB
  • sessionStorage介面方法和localStorage類似,但儲存的資料的只會在當前會話中儲存下來,頁面關閉後會被清空。
名稱 生命期 大小限制 與伺服器通訊
cookie 一般由伺服器生成,可設定失效時間。如果在瀏覽器端生成Cookie,預設是關閉瀏覽器後失效 4KB 每次都會攜帶在HTTP頭中,如果使用cookie儲存過多資料會帶來效能問題
localStorage 除非被清除,否則永久儲存 5MB 僅在瀏覽器中儲存,不與伺服器通訊
sessionStorage 僅在當前會話下有效,關閉頁面或瀏覽器後被清除 5MB 僅在瀏覽器中儲存,不與伺服器通訊

0.1+0.2!=0.3怎麼處理

把需要計算的數字升級(乘以10的n次冪)成計算機能夠精確識別的整數,等計算完成後再進行降級(除以10的n次冪),即:

(0.1*10 + 0.2*10)/10 == 0.3 //true
複製程式碼

更多關於浮點數精度處理請看JS中浮點數精度問題

JS實現繼承

首先建立一個父類

// 定義一個動物類
function Animal(name, color) {
    // 屬性
    this.name = name || 'Animal';
    this.color = color || ['black'];
    // 例項方法
    this.sleep = function () {
        console.log(this.name + '正在睡覺!');
    }
}
// 原型方法
Animal.prototype.eat = function (food) {
    console.log(this.name + '正在吃:' + food);
};
複製程式碼

原型鏈繼承

new了一個空物件,這個空物件指向Animal並且Cat.prototype指向了這個空物件,這種就是基於原型鏈的繼承。

function Cat(name) {
    this.name = name || 'tom'
}
Cat.prototype = new Animal()

var cat = new Cat()
cat.color.push('red')
cat.sleep() //tom正在睡覺!
cat.eat('fish') //tom正在吃:fish
console.log(cat.color) //["black", "red"]
console.log(cat instanceof Animal) //true
console.log(cat instanceof Cat) //true
var new_cat = new Cat()
console.log(new_cat.color) //["black", "red"]
複製程式碼
  • 特點:基於原型鏈,既是父類的例項,也是子類的例項。
  • 缺點:1.無法實現多繼承;2.所有新例項都會共享父類例項的屬性。

構造繼承

function Dog(name) {
    Animal.call(this)
    this.name = name || 'mica'
}
var dog = new Dog()
dog.color.push('blue')
dog.sleep() // mica正在睡覺!
dog.eat('bone') //Uncaught TypeError: dog.eat is not a function
console.log(dog.color) //["black", "blue"]
console.log(dog instanceof Animal) //false
console.log(dog instanceof Dog) //true
var new_dog = new Dog()
console.log(new_dog.color) //["black"]
複製程式碼
  • 特點:可以實現多繼承(call多個),解決了所有例項共享父類例項屬性的問題。
  • 缺點:1.只能繼承父類例項的屬性和方法;2.不能繼承原型上的屬性和方法。

組合繼承

function Mouse(name){
    Animal.call(this)
    this.name = name || 'jerry'
}
Mouse.prototype = new Animal()
Mouse.prototype.constructor = Mouse

var mouse = new Mouse()
mouse.color.push('yellow)
mouse.sleep() //jerry正在睡覺!
mouse.eat('carrot') //jerry正在吃:carrot
console.log(mouse instanceof Animal)//true
console.log(mouse instanceof Mouse)//true
var new_mouse = new Mouse()
console.log(new_mouse.color) //["black"]
複製程式碼
  • 特點:可以繼承例項屬性/方法,也可以繼承原型屬性/方法
  • 缺點:呼叫了兩次父類建構函式,生成了兩份例項

三、vue

mvc和mvvm理解

MVC

MVC即Model View Controller,簡單來說就是通過controller的控制去操作model層的資料,並且返回給view層展示。

mvc.png

  • View 接受使用者互動請求
  • View 將請求轉交給Controller處理
  • Controller 操作Model進行資料更新儲存
  • 資料更新儲存之後,Model會通知View更新
  • View 更新變化資料使使用者得到反饋

MVVM

MVVM即Model-View-ViewModel,將其中的 View 的狀態和行為抽象化,讓我們可以將UI和業務邏輯分開。MVVM的優點是低耦合、可重用性、獨立開發。

mvvm.jpg

  • View 接收使用者互動請求
  • View 將請求轉交給ViewModel
  • ViewModel 操作Model資料更新
  • Model 更新完資料,通知ViewModel資料發生變化
  • ViewModel 更新View資料

MVVM模式和MVC有些類似,但有以下不同

  • ViewModel 替換了 Controller,在UI層之下
  • ViewModel 向 View 暴露它所需要的資料和指令物件
  • ViewModel 接收來自 Model 的資料

概括起來,MVVM是由MVC發展而來,通過在Model之上而在View之下增加一個非視覺的元件將來自Model的資料對映到View中。

響應原理

vue採用資料劫持結合釋出者-訂閱者模式的方式,通過Object.defineProperty劫持data屬性的setter,getter,在資料變動時釋出訊息給訂閱者,觸發相應的監聽回撥。

生命週期函式

  • beforeCreate(建立前) vue例項的掛載元素$el和資料物件 data都是undefined, 還未初始化
  • created(建立後) 完成了 data資料初始化, el還未初始化
  • beforeMount(載入前) vue例項的$el和data都初始化了, 相關的render函式首次被呼叫
  • mounted(載入後) 此過程中進行ajax互動
  • beforeUpdate(更新前)
  • updated(更新後)
  • beforeDestroy(銷燬前)
  • destroyed(銷燬後)

元件data為什麼返回函式

元件中的data寫成一個函式,資料以函式返回值形式定義,這樣每複用一次元件,就會返回一份新的data。如果單純的寫成物件形式,就使得所有元件例項共用了一份data,造成了資料汙染。

vue給物件新增屬性頁面沒有響應

由於Vue會在初始化例項時對屬性執行getter/setter轉化,所以屬性必須在data物件上存在才能讓Vue將它轉換為響應式的。Vue提供了$set方法用來觸發檢視更新。

export default {
    data(){
        return {
            obj: {
                name: 'fei'
            }
        }
    },
    mounted(){
        this.$set(this.obj, 'sex', 'man')
    }

}
複製程式碼

v-if和v-show區別

v-if 是真正的條件渲染,因為它會確保在切換過程中條件塊內的事件監聽器和子元件適當地被銷燬和重建;也是惰性的:如果在初始渲染時條件為假,則什麼也不做——直到條件第一次變為真時,才會開始渲染條件塊。

v-show 就簡單得多——不管初始條件是什麼,元素總是會被渲染,並且只是簡單地基於 CSS 的 “display” 屬性進行切換。

所以,v-if 適用於在執行時很少改變條件,不需要頻繁切換條件的場景;v-show 則適用於需要非常頻繁切換條件的場景。

v-model雙向繫結原理

v-model本質上是語法糖,v-model 在內部為不同的輸入元素使用不同的屬性並丟擲不同的事件。

  • text 和 textarea 元素使用 value 屬性和 input 事件
  • checkbox 和 radio 使用 checked 屬性和 change 事件
  • select 欄位將 value 作為 prop 並將 change 作為事件

所以我們可以v-model進行如下改寫:

<input v-model="sth" />
//  等同於
<input :value="sth" @input="sth = $event.target.value" />
複製程式碼

這個語法糖必須是固定的,也就是說屬性必須為value,方法名必須為:input。

知道了v-model的原理,我們可以在自定義元件上實現v-model。

//Parent
<template>
    {{num}}
    <Child v-model="num">
</template>
export default {
    data(){
        return {
            num: 0
        }
    }
}

//Child
<template>
    <div @click="add">Add</div>
</template>
export default {
    props: ['value'],
    methods:{
        add(){
            this.$emit('input', this.value + 1)
        }
    }
}
複製程式碼

key的作用

  1. 讓vue精準的追蹤到每一個元素,高效的更新虛擬DOM
  2. 觸發過渡
<transition>
  <span :key="text">{{ text }}</span>
</transition>
複製程式碼

當text改變時,這個元素的key屬性就發生了改變,在渲染更新時,Vue會認為這裡新產生了一個元素,而老的元素由於key不存在了,所以會被刪除,從而觸發了過渡。

scoped屬性作用

在Vue檔案中的style標籤上有一個特殊的屬性,scoped。當一個style標籤擁有scoped屬性時候,它的css樣式只能用於當前的Vue元件,可以使元件的樣式不相互汙染。如果一個專案的所有style標籤都加上了scoped屬性,相當於實現了樣式的模組化。

scoped屬性的實現原理是給每一個dom元素新增了一個獨一無二的動態屬性,給css選擇器額外新增一個對應的屬性選擇器,來選擇元件中的dom。

<template>
    <div class="box">dom</div>
</template>
<style lang="scss" scoped>
.box{
    background:red;
}
</style>
複製程式碼

vue將程式碼轉譯成如下:

.box[data-v-11c6864c]{
    background:red;
}
<template>
    <div class="box" data-v-11c6864c>dom</div>
</template>
複製程式碼

scoped樣式穿透

scoped雖然避免了元件間樣式汙染,但是很多時候我們需要修改元件中的某個樣式,但是又不想去除scoped屬性。

  1. 使用/deep/
//Parent
<template>
<div class="wrap">
    <Child />
</div>
</template>

<style lang="scss" scoped>
.wrap /deep/ .box{
    background: red;
}
</style>

//Child
<template>
    <div class="box"></div>
</template>
複製程式碼
  1. 使用兩個style標籤
//Parent
<template>
<div class="wrap">
    <Child />
</div>
</template>

<style lang="scss" scoped>
//其他樣式
</style>
<style lang="scss">
.wrap .box{
    background: red;
}
</style>

//Child
<template>
    <div class="box"></div>
</template>
複製程式碼

ref的作用

  1. 獲取dom元素this.$refs.box
  2. 獲取子元件中的datathis.$refs.box.msg
  3. 呼叫子元件中的方法this.$refs.box.open()

computed和watch區別

1.當頁面中有某些資料依賴其他資料進行變動的時候,可以使用計算屬性computed。

computed.png

<template>{{fullName}}</template>
export default {
    data(){
        return {
            firstName: 'xie',
            lastName: 'yu fei',
        }
    },
    computed:{
        fullName: function(){
            return this.firstName + ' ' + this.lastName
        }
    }
}
複製程式碼

2.watch用於觀察和監聽頁面上的vue例項,如果要在資料變化的同時進行非同步操作或者是比較大的開銷,那麼watch為最佳選擇。

watch.png

<template>{{fullName}}</template>
export default {
    data(){
        return {
            firstName: 'xie',
            lastName: 'xiao fei',
            fullName: 'xie xiao fei'
        }
    },
    watch:{
        firstName(val) {
            this.fullName = val + ' ' + this.lastName
        },
        lastName(val) {
            this.fullName = this.firstName + ' ' + val
        }
    }
}
複製程式碼

vue路由有幾種模式

  1. hash模式

即位址列URL中的#符號,它的特點在於:hash 雖然出現URL中,但不會被包含在HTTP請求中,對後端完全沒有影響,不需要後臺進行配置,因此改變hash不會重新載入頁面。

  1. history模式

利用了HTML5 History Interface 中新增的pushState() 和replaceState() 方法(需要特定瀏覽器支援)。history模式改變了路由地址,因為需要後臺配置地址。

元件之間的傳值通訊

  1. 父元件給子元件傳值通過props
  2. 子元件給父元件傳值通過$emit觸發回撥
  3. 兄弟元件通訊,通過例項一個vue例項eventBus作為媒介,要相互通訊的兄弟元件之中,都引入eventBus
//main.js
import Vue from 'vue'
export const eventBus = new Vue()

//brother1.vue
import eventBus from '@/main.js'
export default{
	methods: {
	    toBus () {
	        eventBus.$emit('greet', 'hi brother')
	    }
	}
}

//brother2
import eventBus from '@/main.js'
export default{
    mounted(){
        eventBus.$on('greet', (msg)=>{
            this.msg = msg
        })
    }
}
複製程式碼

axios攔截器怎麼配

// 新增請求攔截器
axios.interceptors.request.use(function (config) {
    // 在傳送請求之前做些什麼
    return config;
}, function (error) {
    // 對請求錯誤做些什麼
    return Promise.reject(error);
});
// 新增響應攔截器
axios.interceptors.response.use(function (response) {
    // 對響應資料做點什麼
    return response;
  }, function (error) {
    // 對響應錯誤做點什麼
    return Promise.reject(error);
  });
複製程式碼

四、瀏覽器

瀏覽器渲染機制

  1. 構建DOM樹(parse):渲染引擎解析HTML文件,首先將標籤轉換成DOM樹中的DOM node
  2. 構建渲染樹(construct):解析對應的CSS樣式檔案資訊
  3. 佈局渲染樹(reflow/layout):從根節點遞迴呼叫,計算每一個元素的大小、位置等,給出每個節點所應該在螢幕上出現的精確座標;
  4. 繪製渲染樹(paint/repaint):遍歷渲染樹,使用UI後端層來繪製每個節點。

重繪和重排的區別

重繪(repaint或redraw):當盒子的位置、大小以及其他屬性,例如顏色、字型大小等都確定下來之後,瀏覽器便把這些原色都按照各自的特性繪製一遍,將內容呈現在頁面上。重繪是指一個元素外觀的改變所觸發的瀏覽器行為,瀏覽器會根據元素的新屬性重新繪製,使元素呈現新的外觀。

重繪發生在元素的可見的外觀被改變,但並沒有影響到佈局的時候。比如,僅修改DOM元素的字型顏色(只有Repaint,因為不需要調整佈局)

重排(重構/迴流/reflow):當渲染樹中的一部分(或全部)因為元素的規模尺寸,佈局,隱藏等改變而需要重新構建, 這就稱為迴流(reflow)。每個頁面至少需要一次迴流,就是在頁面第一次載入的時候。

觸發重排的條件:任何頁面佈局和幾何屬性的改變都會觸發重排:

  • 頁面渲染初始化(無法避免)
  • 新增或刪除可見的DOM元素
  • 元素位置的改變,或者使用動畫
  • 元素尺寸的改變——大小,外邊距,邊框
  • 瀏覽器視窗尺寸的變化
  • 填充內容的改變,比如文字的改變或圖片大小改變而引起的計算值寬度和高度的改變

重排必定會引發重繪,但重繪不一定會引發重排。

幾種請求方法

GET、POST、HEAD、PUT、DELETE、CONNECT、OPTIONS、TRACE

get和post區別

請求方式 GET POST
引數位置 引數拼接到url的後面 引數在請求體中
引數大小 受限於瀏覽器url大小,一般不超過32K 1G
伺服器資料接收 接收1次 根據資料大小,可分多次接收
適用場景 從伺服器端獲取資料 向伺服器提交資料
安全性 引數攜帶在url中,安全性低 相對於GET請求,安全性更高

如何解決跨域

  1. CORS
  2. jsonp
  3. 伺服器代理

更多CORS請看徹底讀懂前端跨域CORS

JSONP原理

由於瀏覽器的同源策略限制,不允許跨域請求;但是頁面中的 script、img、iframe標籤是例外,不受同源策略限制。

Jsonp 就是利用script標籤跨域特性進行請求。

JSONP 的原理就是,先在全域性註冊一個回撥函式,定義回撥資料的處理;與服務端約定好一個同名回撥函式名,服務端接收到請求後,將返回一段 Javascript,在這段 Javascript 程式碼中呼叫了約定好的回撥函式,並且將資料作為引數進行傳遞。當網頁接收到這段 Javascript 程式碼後,就會執行這個回撥函式。

JSONP缺點:它只支援GET請求,而不支援POST請求等其他型別的HTTP請求。

快取的理解

快取分為強快取和協商快取。強快取不過伺服器,協商快取需要過伺服器,協商快取返回的狀態碼是304。兩類快取機制可以同時存在,強快取的優先順序高於協商快取。當執行強快取時,如若快取命中,則直接使用快取資料庫中的資料,不再進行快取協商。

  1. 強快取
  • Expires
  • cache-control
  1. 協商快取
  • Last-Modified 和 If-Modified-Since
  • Etag 和 If-None-Match

更多快取內容請看前端也要懂Http快取機制

XSS和CSRF區別

  1. 跨站指令碼攻擊(Cross Site Scripting),為了不和層疊樣式表 CSS 混淆,故將跨站指令碼攻擊縮寫為 XSS。惡意攻擊者往 Web 頁面裡插入惡意 Script 程式碼,當使用者瀏覽該頁之時,嵌入其中 Web 裡面的 Script 程式碼會被執行,從而達到惡意攻擊使用者的目的。

  2. 跨站請求偽造(Cross-site request forgery),是偽造請求,冒充使用者在站內的正常操作。我們知道,絕大多數網站是通過 cookie 等方式辨識使用者身份,再予以授權的。所以要偽造使用者的正常操作,最好的方法是通過 XSS 或連結欺騙等途徑,讓使用者在本機(即擁有身份 cookie 的瀏覽器端)發起使用者所不知道的請求。

區別:

  • 原理不同,CSRF是利用網站A本身的漏洞,去請求網站A的api;XSS是向目標網站注入JS程式碼,然後執行JS裡的程式碼。
  • CSRF需要使用者先登入目標網站獲取cookie,而XSS不需要登入
  • CSRF的目標是使用者,XSS的目標是伺服器
  • XSS是利用合法使用者獲取其資訊,而CSRF是偽造成合法使用者發起請求

HTTP與HTTPS的區別

  • HTTP的URL由http://起始且預設使用埠80,而HTTPS的URL由https://起始且預設使用埠443
  • HTTP是超文字傳輸協議,資訊是明文傳輸,HTTPS則是具有安全性的 SSL 加密傳輸協議
  • HTTP的連線很簡單,是無狀態的,HTTPS 協議是由 SSL+HTTP 協議構建的可進行加密傳輸、身份認證的網路協議,比 http 協議安全

HTTP狀態碼

1xx表示客戶端應該繼續傳送請求

2xx表示成功的請求

  • 200表示OK,正常返回資訊
  • 201表示請求成功且伺服器建立了新的資源
  • 202表示伺服器已經接受了請求,但還未處理

3xx表示重定向

  • 301表示永久重定向,請求的網頁已經永久移動到新位置
  • 302表示臨時重定向
  • 304表示自從上一次請求以來,頁面的內容沒有改變過

4xx表示客戶端錯誤

  • 401表示伺服器無法理解請求的格式
  • 402表示請求未授權
  • 403表示禁止訪問
  • 404表示請求的資源不存在,一般是路徑寫錯了

5xx表示伺服器錯誤

  • 500表示最常見的伺服器錯誤
  • 503表示伺服器暫時無法處理請求

五、效能優化

  • 使用CDN
  • gzip壓縮
  • 文字壓縮
  • 合併請求
  • 雪碧圖
  • 圖片懶載入
  • 快取資源
  • 減少DOM操作

更多前端資料請關注公眾號【前端壹讀】。整理不易,且看且珍惜。

如果覺得寫得還不錯,請關注我的掘金主頁。更多文章請訪問謝小飛的部落格

PS:看到評論區好多同學批評我說整理得太簡單了,react,webpack,Promise都沒有,怎麼能算面試題呢?首先感謝大家的評論,我會更加努力整理深入的知識點,其次,前端的東西多而雜,我整理這篇文章花了大概一個月時間,有些基礎的知識點我可能工作了一段時間都沒有用到過,所以偏向基礎一點,請技術大佬忽略本文。

更多內容待續。。。

參考

flex

CSS清除浮動

聖盃佈局和雙飛翼佈局的理解與思考

一圖秒懂函式防抖和函式節流

web前端面試總結

公司要求會使用框架vue,面試題會被問及哪些

30道Vue面試題

總結了17年初到18年初百場前端面試的面試經驗

面試分享:兩年工作經驗成功面試阿里P6總結

相關文章