前端面試祕籍

前端面試祕籍

本文引用自掘進用戶[A Loity]

2019年前端面試祕籍javascript

1、html和css部分

一、如何理解CSS的盒子模型?

標準盒子模型:寬度=內容的寬度(content)+ border + padding + margin

低版本IE盒子模型:寬度=內容寬度(content+border+padding)+ margin

二、BFC?

* 什麼是 BFC

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

* 造成 BFC 的條件

   * 浮動元素,float 除 none 之外的值
   * 定位元素,position(absolute,fixed)
   * display 爲如下其中之一的值 inline-block,table-cell,table-caption
   * overflow 除了 visible 之外的值(hidden,auto,scroll)

* BFC 的特性

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

三、如何清除浮動?

不清楚浮動會發生高度塌陷:浮動元素父元素高度自適應(父元素不寫高度時,子元素寫了浮動後,父元素會發生高度塌陷)
* clear清除浮動(添加空div法)在浮動元素下方添加空div,並給該元素寫css樣式:   {clear:both;height:0;overflow:hidden;}
* 給浮動元素父級設置高度
* 父級同時浮動(須要給父級同級元素添加浮動)
* 父級設置成inline-block,其margin: 0 auto居中方式失效
* 給父級添加overflow:hidden 清除浮動方法
* 萬能清除法 after僞類 清浮動(如今主流方法,推薦使用)

.float_div:after{
    content:".";
    clear:both;
    display:block;
    height:0;
    overflow:hidden;
    visibility:hidden;
}
.float_div{
    zoom:1
}

四、用純CSS建立一個三角形的原理是什麼?

span {
    width: 0;
    height: 0;
    border-top: 40px solid transparent;
    border-left: 40px solid transparent;
    border-right: 40px solid transparent;
    border-bottom: 40px solid #ff0000;
}

五、css3實現0.5px的細線?

/* css */
.line {
    position: relative;
}
.line:after {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 1px;
    background-color: #000000;
    -webkit-transform: scaleY(.5);
    transform: scaleY(.5);
}

/* html */
<div class="line"></div>

六、css實現三欄佈局

  • 左右固定,中間自適應。

  • 1.flex佈局
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .box {
            display: flex;
            justify-content: center;
            height: 200px;
        }
        .left {
            width: 200px;
            background-color: red;
            height: 100%;
        }
        .content {
            background-color: yellow;
            flex: 1;
        }
        .right {
            width: 200px;
            background-color: green;
        }
    </style>
</head>
<body>
    <div class="box">
        <div class="left"></div>
        <div class="content"></div>
        <div class="right"></div>
    </div>
</body>
</html>
  • 2.絕對定位方式
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .box {
            position: relative;
            height: 200px;
        }
        .left {
            width: 200px;
            background-color: red;
            left: 0;
            height: 100%;
            position: absolute;
        }
        .content {
            background-color: yellow;
            left: 200px;
            right: 200px;
            height: 100%;
            position: absolute;
        }
        .right {
            width: 200px;
            background-color: green;
            right: 0;
            height: 100%;
            position: absolute;
        }
    </style>
</head>
<body>
    <div class="box">
        <div class="left"></div>
        <div class="content"></div>
        <div class="right"></div>
    </div>
</body>
</html>
  • 3.浮動方式
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .box {
            height: 200px;
        }
        .left {
            width: 200px;
            background-color: red;
            float: left;
            height: 100%;
        }
        .content {
            background-color: yellow;
            height: 100%;
        }
        .right {
            width: 200px;
            background-color: green;
            float: right;
            height: 100%;
        }
    </style>
</head>
<body>
    <div class="box">
        <div class="left"></div>
        <div class="right"></div>
        <div class="content"></div>
    </div>
</body>
</html>

七、讓一個div垂直居中

  • 1.寬度和高度已知的
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .box {
            width: 400px;
            height: 200px;
            position: relative;
            background: red;
        }
        .content {
            width: 200px;
            height: 100px;
            position: absolute;
            top: 50%;
            left: 50%;
            margin-left: -100px;
            margin-top: -50px;
            background: green;
        }
    </style>
</head>
<body>
    <div class="box">
        <div class="content"></div>
    </div>
</body>
</html>

2.寬度和高度未知

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .box {
            width: 400px;
            height: 200px;
            position: relative;
            background: red;
        }
        .content {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: green;
        }
    </style>
</head>
<body>
    <div class="box">
        <div class="content"></div>
    </div>
</body>
</html>

flex佈局

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .box {
            width: 400px;
            height: 200px;
            background: red;
            display: flex;
            justify-content: center;
            align-items: center;
        }
        .content {
            width: 200px;
            height: 100px;
            background: green;
        }
    </style>
</head>
<body>
    <div class="box">
        <div class="content"></div>
    </div>
</body>
</html>

JS

一、閉包

  • 閉包概念
可以讀取其餘函數內部變量的函數。

或簡單理解爲定義在一個函數內部的函數,內部函數持有外部函數內變量的引用。
  • 閉包用途
一、因爲閉包會使得函數中的變量都被保存在內存中,內存消耗很大,因此不能濫用閉包,不然會形成網頁的性能問題,在IE中可能致使內存泄露。解決方法是,在退出函數以前,將不使用的局部變量所有刪除。

二、閉包會在父函數外部,改變父函數內部變量的值。因此,若是你把父函數看成對象(object)使用,把閉包看成它的公用方法(Public Method),把內部變量看成它的私有屬性(private value),這時必定要當心,不要隨便改變父函數內部變量的值。
  • 閉包應用場景 閉包應用場景之setTimeout

  • 閉包應用場景之回調

二、js中函數執行

  • 在 ES5.1 裏面函數是這樣執行的(不討論use strict和一些特殊狀況,JS好複雜的),按以下順序執行:
1. 肯定「this」的值 (確切的來講,this在JS裏面不是一個變量名而是一個關鍵字)

2. 建立一個新的做用域

3. 處理形參/實參(沒有定義過才聲明,不管如何都從新賦值,沒有對應實參則賦值爲"undefined"):
    對於每個傳入的實參,按照從左往右的順序依次執行:若是對應的形參在本做用域中尚未定義,則在本做用域中聲明形參,並賦值。若是已經定義過了,則從新給其賦值。(沒有對應實參則賦值爲"undefined")(沒有定義:就是「沒有聲明」的意思)

4. 處理函數定義(沒有定義過才聲明,不管如何都從新賦值):
    對該函數中全部的定義的函數,按照代碼寫的順序依次執行:若是這個變量名在本做用域中尚未定義,則在本做用域中聲明這個函數名,而且賦值爲對應的函數,若是定義了這個變量,在可寫的狀況下從新給這個變量賦值爲這個函數,不然拋出異常。

5. 處理 "arguments"(沒有定義過才聲明和賦值):
    若是在本做用域中沒有定義 arguments,則在本做用域中聲明arguments並給其賦值。

6. 處理變量聲明(沒有定義過才聲明,不賦值):
    對於全部變量聲明,按照代碼寫的順序依次執行:若是在本做用域中沒有定義這個變量,則在本做用域中聲明這個變量,賦值爲undefined

7. 而後執行函數代碼。(固然是去變量定義裏面的 var 執行)

三、new一個對象的過程當中發生了什麼嘛

1. 建立空對象;
    var obj = {};

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

3. 使用新對象調用函數,函數中的this被指向新實例對象:
    ClassA.call(obj);//{}.構造函數();

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

四、宏任務跟微任務

  • macro-task(宏任務):包括總體代碼script,setTimeout,setInterval
  • micro-task(微任務):Promise,process.nextTick

這一次完全弄懂javascript的機制css

五、防抖和節流

綜合應用場景

  • 防抖(debounce):就是指觸發事件後在 n 秒內函數只能執行一次,若是在 n 秒內又觸發了事件,則會從新計算函數執行時間。html

    • search搜索聯想,用戶在不斷輸入值時,用防抖來節約請求資源。
    • window觸發resize的時候,不斷的調整瀏覽器窗口大小會不斷的觸發這個事件,用防抖來讓其只觸發一次
  • 節流(throttle):就是指連續觸發事件可是在 n 秒中只執行一次函數。節流會稀釋函數的執行頻率。前端

    • 鼠標不斷點擊觸發,mousedown(單位時間內只觸發一次)
    • 監聽滾動事件,好比是否滑到底部自動加載更多,用throttle來判斷
      所謂防抖,就是指觸發事件後在 n 秒內函數只能執行一次,若是在 n 秒內又觸發了事件,則會從新計算函數執行時間。

防抖函數分爲非當即執行版和當即執行版。

  • 非當即執行版的意思是觸發事件後函數不會當即執行,而是在 n 秒後執行,若是在 n 秒內又觸發了事件,則會從新計算函數執行時間。vue

  • 當即執行版的意思是觸發事件後函數會當即執行,而後 n 秒內不觸發事件才能繼續執行函數的效果。java

/**
 * @desc 函數防抖
 * @param func 函數
 * @param wait 延遲執行毫秒數
 * @param immediate true 表當即執行,false 表非當即執行
 */
function debounce(func,wait,immediate) {
    let timeout;

    return function () {
        let context = this;
        let args = arguments;

        if (timeout) clearTimeout(timeout);
        if (immediate) {
            var callNow = !timeout;
            timeout = setTimeout(() => {
                timeout = null;
            }, wait)
            if (callNow) func.apply(context, args)
        }
        else {
            timeout = setTimeout(function(){
                func.apply(context, args)
            }, wait);
        }
    }
}

所謂節流,就是指連續觸發事件可是在 n 秒中只執行一次函數。 節流會稀釋函數的執行頻率。css3

對於節流,通常有兩種方式能夠實現,分別是時間戳版和定時器版。nginx

  • 時間戳版的函數觸發是在時間段內開始的時候
  • 定時器版的函數觸發是在時間段內結束的時候。
/**
 * @desc 函數節流
 * @param func 函數
 * @param wait 延遲執行毫秒數
 * @param type 1 表時間戳版,2 表定時器版
 */
function throttle(func, wait ,type) {
    if(type===1){
        let previous = 0;
    }else if(type===2){
        let timeout;
    }
    return function() {
        let context = this;
        let args = arguments;
        if(type===1){
            let now = Date.now();

            if (now - previous > wait) {
                func.apply(context, args);
                previous = now;
            }
        }else if(type===2){
            if (!timeout) {
                timeout = setTimeout(() => {
                    timeout = null;
                    func.apply(context, args)
                }, wait)
            }
        }
    }
}

六、數組的經常使用方法

改變原數組的方法

  • splice() 添加/刪除數組元素
語法:arrayObject.splice(index,howmany,item1,.....,itemX)
參數:
   1.index:必需。整數,規定添加/刪除項目的位置,使用負數可從數組結尾處規定位置。
   2.howmany:可選。要刪除的項目數量。若是設置爲 0,則不會刪除項目。
   3.item1, ..., itemX: 可選。向數組添加的新項目。

返回值: 若是有元素被刪除,返回包含被刪除項目的新數組。
  • sort() 數組排序
語法:arrayObject.sort(sortby)
參數:
   1.sortby 可選。規定排序順序。必須是函數。。

返回值: 返回包排序後的新數組。
  • pop() 刪除一個數組中的最後的一個元素
語法:arrayObject.pop()
參數:無

返回值: 返回被刪除的元素。
  • shift() 刪除數組的第一個元素
語法:arrayObject.shift()
參數:無

返回值: 返回被刪除的元素。
  • push() 向數組的末尾添加元素
語法:arrayObject.push(newelement1,newelement2,....,newelementX)
參數:
   1.newelement1    必需。要添加到數組的第一個元素。
   2.newelement2    可選。要添加到數組的第二個元素。
   3.newelementX    可選。可添加若干個元素。

返回值: 返回被刪除的元素。
  • unshift() 向數組的開頭添加一個或更多元素
語法:arrayObject.unshift(newelement1,newelement2,....,newelementX)
參數:
   1.newelement1    必需。要添加到數組的第一個元素。
   2.newelement2    可選。要添加到數組的第二個元素。
   3.newelementX    可選。可添加若干個元素。

返回值: arrayObject 的新長度。。
  • reverse() 顛倒數組中元素的順序
語法:arrayObject.reverse()
參數:無

返回值: 顛倒後的新數組。
  • copyWithin() 指定位置的成員複製到其餘位置
語法: array.copyWithin(target, start = 0, end = this.length)
參數:
   1.target(必需):從該位置開始替換數據。若是爲負值,表示倒數。
   2.start(可選):從該位置開始讀取數據,默認爲 0。若是爲負值,表示倒數。
   3.end(可選):到該位置前中止讀取數據,默認等於數組長度。若是爲負值,表示倒數。

返回值: 返回當前數組。
  • fill() 填充數組
語法: array.fill(value, start, end)
參數:
   1.value  必需。填充的值。
   2.start  可選。開始填充位置。
   3.end    可選。中止填充位置 (默認爲 array.length)

返回值: 返回當前數組。

不改變原數組的方法

  • slice() 淺拷貝數組的元素
語法: array.slice(begin, end);
參數:
   1.begin(可選): 索引數值,接受負值,從該索引處開始提取原數組中的元素,默認值爲0。
   2.end(可選):索引數值(不包括),接受負值,在該索引處前結束提取原數組元素,默認值爲數組末尾(包括最後一個元素)。

返回值: 返回一個從開始到結束(不包括結束)選擇的數組的一部分淺拷貝到一個新數組對象,且原數組不會被修改。
  • join() 數組轉字符串
語法:array.join(str)
參數:
   1.str(可選): 指定要使用的分隔符,默認使用逗號做爲分隔符。

返回值: 返回生成的字符串。
  • concat() 合併兩個或多個數組
語法: var newArr =oldArray.concat(arrayX,arrayX,......,arrayX)
參數:
   1.arrayX(必須):該參數能夠是具體的值,也能夠是數組對象。能夠是任意多個。

返回值: 返回返回合併後的新數組。
  • indexOf() 查找數組是否存在某個元素
語法:array.indexOf(searchElement,fromIndex)
參數:
   1.searchElement(必須):被查找的元素
   2.fromIndex(可選):開始查找的位置(不能大於等於數組的長度,返回-1),接受負值,默認值爲0。

返回值: 返回下標
  • lastIndexOf() 查找指定元素在數組中的最後一個位置
語法:arr.lastIndexOf(searchElement,fromIndex)
參數:
   1.searchElement(必須): 被查找的元素
   2.fromIndex(可選): 逆向查找開始位置,默認值數組的長度-1,即查找整個數組。

返回值: 方法返回指定元素,在數組中的最後一個的索引,若是不存在則返回 -1。(從數組後面往前查找)
  • includes() 查找數組是否包含某個元素
語法: array.includes(searchElement,fromIndex=0)
參數:
   1.searchElement(必須):被查找的元素
   2.fromIndex(可選):默認值爲0,參數表示搜索的起始位置,接受負值。正值超過數組長度,數組不會被搜索,返回false。負值絕對值超過長數組度,重置從0開始搜索。

返回值: 返回布爾

七、當即執行函數

  • 聲明一個匿名函數,立刻調用這個匿名函數。目的是保護內部變量不受污染。
(function(n1, n2) {
    console.log("這是匿名函數的自執行的第一種寫法,結果爲:" + (n1 + n2))
})(10, 100);
(function start(n1, n2) {
    console.log("這是函數聲明方式的自執行的第一種寫法,結果爲:" + (n1 + n2))
})(10, 100);
(function(n1, n2) {
    console.log("這是匿名函數的自執行的第二種寫法,結果爲:" + (n1 + n2))
}(10, 100));
(function start(n1, n2) {
    console.log("這是函數聲明方式的自執行的第二種寫法,結果爲:" + (n1 + n2))
}(10, 100));

八、js原型和原型鏈

  • 每一個對象都會在其內部初始化一個屬性,就是prototype(原型),當咱們訪問一個對象的屬性時,若是這個對象內部不存在這個屬性,那麼他就會去prototype裏找這個屬性,這個prototype又會有本身的prototype,因而就這樣一直找下去,也就是咱們平時所說的原型鏈的概念。es6

  • 關係:instance.constructor.prototype = instance.protoweb

  • 特色:JavaScript對象是經過引用來傳遞的,咱們建立的每一個新對象實體中並無一份屬於本身的原型副本,當咱們修改原型時,與之相關的對象也會繼承這一改變。當咱們須要一個屬性時,JavaScript引擎會先看當前對象中是否有這個屬性,若是沒有的話,就會查找它的prototype對象是否有這個屬性,如此遞推下去,一致檢索到Object內建對象。

function Func(){}
Func.prototype.name = "汪某";
Func.prototype.getInfo = function() {
   return this.name;
}
var person = new Func();
console.log(person.getInfo());//"汪某"
console.log(Func.prototype);//Func { name = "汪某", getInfo = function() }

參考:js原型與原型鏈

九、js中call,apply,bind

參考:JavaScript中call,apply,bind方法的總結。

十、Promise

一句話歸納Promise:Promise對象用於異步操做,它表示一個還沒有完成且預計在將來完成的異步操做。

promise是用來解決兩個問題的:

  • 回調地獄,代碼難以維護,經常第一個的函數的輸出是第二個函數的輸入這種現象
  • promise能夠支持多個併發的請求,獲取併發請求中的數據

這個promise能夠解決異步的問題,自己不能說promise是異步的

/*Promise 的簡單實現*/

class MyPromise {
    constructor(fn) {
        this.resolvedCallbacks = [];
        this.rejectedCallbacks = [];
        this.state = "PADDING";
        this.value = "";
        fn(this.resolve.bind(this), this.reject.bind(this));
    }
    resolve(value) {
        if (this.state === "PADDING") {
            this.state = "RESOLVED";
            this.value = value;
            this.resolvedCallbacks.forEach(cb => cb());
        }
    }
    reject(value) {
        if (this.state === "PADDING") {
            this.state = "REJECTED";
            this.value = value;
            this.rejectedCallbacks.forEach(cb => cb());
        }
    }
    then(resolve = function() {}, reject = function() {}) {
        if (this.state === "PADDING") {
            this.resolvedCallbacks.push(resolve);
            this.rejectedCallbacks.push(reject);
        }
        if (this.state === "RESOLVED") {
            resolve(this.value);
        }
        if (this.state === "REJECTED") {
            reject(this.value);
        }
    }
}

十一、async/await

  • 如何使用 Async 函數
async function timeout(ms) {
  await new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

async function asyncPrint(value, ms) {
  await timeout(ms);
  console.log(value);
}

asyncPrint('hello world', 50);

上面代碼指定50毫秒之後,輸出hello world。 進一步說,async函數徹底能夠看做多個異步操做,包裝成的一個 Promise 對象,而await命令就是內部then命令的語法糖。

十二、深拷貝、淺拷貝

淺拷貝和深拷貝都只針對於引用數據類型,淺拷貝只複製指向某個對象的指針,而不復制對象自己,新舊對象仍是共享同一塊內存;但深拷貝會另外創造一個如出一轍的對象,新對象跟原對象不共享內存,修改新對象不會改到原對象;
區別:淺拷貝只複製對象的第一層屬性、深拷貝能夠對對象的屬性進行遞歸複製;

圖片的說明(深淺拷貝)

深拷貝的實現方式

  • JSON.stringify 和 JSON.parse

用 JSON.stringify 把對象轉換成字符串,再用 JSON.parse 把字符串轉換成新的對象。

let newObj = JSON.parse(JSON.stringify(obj));
  • lodash

用 lodash 函數庫提供的 _.cloneDeep 方法實現深拷貝。

var _ = require('lodash');
var newObj = _.cloneDeep(obj);
  • 本身封裝
function deepClone(obj) {
    let objClone = Array.isArray(obj) ? [] : {};
    if (obj && typeof obj === "object") {
        // for...in 會把繼承的屬性一塊兒遍歷
        for (let key in obj) {
            // 判斷是否是自有屬性,而不是繼承屬性
            if (obj.hasOwnProperty(key)) {
                //判斷ojb子元素是否爲對象或數組,若是是,遞歸複製
                if (obj[key] && typeof obj[key] === "object") {
                    objClone[key] = this.deepClone(obj[key]);
                } else {
                    //若是不是,簡單複製
                    objClone[key] = obj[key];
                }
            }
        }
    }
    return objClone;
}

1三、跨域

跨域須要針對瀏覽器的同源策略來理解,同源策略指的是請求必須是同一個端口,同一個協議,同一個域名,不一樣源的客戶端腳本在沒有明確受權的狀況下,不能讀寫對方資源。
受瀏覽器同源策略的影響,不是同源的腳本不能操做其餘源下面的對象。想要操做另外一個源下的對象是就須要跨域。

  • jsonp
  • iframe
  • 跨域資源共享(CORS)
  • nginx 代理跨域

1四、for in 和 for of

  • for in
1.通常用於遍歷對象的可枚舉屬性。以及對象從構造函數原型中繼承的屬性。對於每一個不一樣的屬性,語句都會被執行。
2.不建議使用for in 遍歷數組,由於輸出的順序是不固定的。
3.若是迭代的對象的變量值是null或者undefined, for in不執行循環體,建議在使用for in循環以前,先檢查該對象的值是否是null或者undefined
  • for of
1.for…of 語句在可迭代對象(包括 Array,Map,Set,String,TypedArray,arguments 對象等等)上建立一個迭代循環,調用自定義迭代鉤子,併爲每一個不一樣屬性的值執行語句
  • 遍歷對象
var s = {
    a: 1,
    b: 2,
    c: 3
}
var s1 = Object.create(s);
for (var prop in s1) {
    console.log(prop); //a b c
    console.log(s1[prop]); //1 2 3
}
for (let prop of s1) {
    console.log(prop); //報錯以下 Uncaught TypeError: s1 is not iterable
}
for (let prop of Object.keys(s1)) {
    console.log(prop); // a b c
    console.log(s1[prop]); //1 2 3
}

1五、如何阻止冒泡?

冒泡型事件:事件按照從最特定的事件目標到最不特定的事件目標(document對象)的順序觸發。

w3c的方法是e.stopPropagation(),IE則是使用e.cancelBubble = true。

//阻止冒泡行爲
function stopBubble(e) {
//若是提供了事件對象,則這是一個非IE瀏覽器
if ( e && e.stopPropagation )
    //所以它支持W3C的stopPropagation()方法
    e.stopPropagation();
else
    //不然,咱們須要使用IE的方式來取消事件冒泡
    window.event.cancelBubble = true;
}

1六、如何阻止默認事件?

w3c的方法是e.preventDefault(),IE則是使用e.returnValue = false

//阻止瀏覽器的默認行爲
function stopDefault( e ) {
    //阻止默認瀏覽器動做(W3C)
    if ( e && e.preventDefault )
        e.preventDefault();
    //IE中阻止函數器默認動做的方式
    else
        window.event.returnValue = false;
    return false;
}

1七、var,let,const

//變量提高
console.log(a);  // undefined
console.log(b);  // 報錯
console.log(c);  // 報錯
var a = 1;
let b = 2;
const c = 3;

// 全局聲明
console.log(window.a) //  1

// 重複聲明
let b  = 200;//報錯

其實這裏很容易理解,var是能夠變量提高的。而let和const是必須聲明後才能調用的。 對於let和const來講,這裏就是暫緩性死區。

1八、Class

es6新增的Class其實也是語法糖,js底層其實沒有class的概念的,其實也是原型繼承的封裝。

class People {
    constructor(props) {
        this.props = props;
        this.name = '汪某';
    }
    callMyName() {
        console.log(this.name);
    }
}
class Name extends People { // extends 其實就是繼承了哪一個類
    constructor(props) {
        //  super至關於 把類的原型拿過來
        //  People.call(this, props)
        super(props)
    }
    callMyApple() {
        console.log('我是汪某!')
    }
}

let a = new Name('啊啊啊')
a.callMyName(); //汪某
a.callMyApple(); // 我是汪某!

1九、Set

Set數據結構相似數組,但全部成員的值惟一。

let a = new Set();
[1,2,2,1,3,4,5,4,5].forEach(x=>a.add(x));
for(let k of a){
    console.log(k)
};
// 1 2 3 4 5

基本使用

let a = new Set([1,2,3,3,4]);
[...a]; // [1,2,3,4]
a.size; // 4

// 數組去重
[...new Set([1,2,3,4,4,4])];// [1,2,3,4]

方法

  • add(value):添加某個值,返回 Set 結構自己。
  • delete(value):刪除某個值,返回一個布爾值,表示刪除是否成功。
  • has(value):返回一個布爾值,表示該值是否爲Set的成員。
  • clear():清除全部成員,沒有返回值。
let a = new Set();
a.add(1).add(2); // a => Set(2) {1, 2}
a.has(2);        // true
a.has(3);        // false
a.delete(2);     // true  a => Set(1) {1}
a.clear();       // a => Set(0) {}

20、Map

Map結構提供了「值—值」的對應,是一種更完善的 Hash 結構實現。

let a = new Map();
let b = {name: 'leo' };
a.set(b,'my name'); // 添加值
a.get(b);           // 獲取值
a.size;      // 獲取總數
a.has(b);    // 查詢是否存在
a.delete(b); // 刪除一個值
a.clear();   // 清空全部成員 無返回

Map基本使用

  • 傳入數組做爲參數,指定鍵值對的數組。
let a = new Map([
    ['name','wzx'],
    ['age',23]
])
  • 若是對同一個鍵屢次賦值,後面的值將覆蓋前面的值。
let a = new Map();
a.set(1,'aaa').set(1,'bbb');
a.get(1); // 'bbb'
  • 若是讀取一個未知的鍵,則返回undefined。
new Map().get('asdsad'); // undefined
  • 一樣的值的兩個實例,在 Map 結構中被視爲兩個鍵。
let a = new Map();
let a1 = ['aaa'];
let a2 = ['aaa'];
a.set(a1,111).set(a2,222);
a.get(a1); // 111
a.get(a2); // 222

Map方法

  • keys():返回鍵名的遍歷器。
  • values():返回鍵值的遍歷器。
  • entries():返回全部成員的遍歷器。
  • forEach():遍歷 Map 的全部成員。
let a = new Map([
    ['name', 'leo'],
    ['age', 18]
])
for (let i of a.keys()) {
    console.log(i)
};
//name
//age

for (let i of a.values()) {
    console.log(i)
};
//leo
//18

for (let i of a.entries()) {
    console.log(i)
};
//["name", "leo"]

a.forEach((v, k, m) => {
    console.log(`key:${k},value:${v},map:${m}`)
})
//["age", 18]

3、手擼代碼

一、實現一個new操做符

function New(func) {
    var res = {};
    if (func.prototype !== null) {
        res.__proto__ = func.prototype;
    }
    var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));
    if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
        return;
        ret;
    }
    return;
    res;
}
var obj = New(A, 1, 2);
// equals to
var obj = new A(1, 2);

實現一個call或 apply

  • call
Function.prototype.call2 = function (context) {
    var context = context || window;
    context.fn = this;

    var args = [];
    for(var i = 1, len = arguments.length; i < len; i++) {
        args.push('arguments[' + i + ']');
    }

    var result = eval('context.fn(' + args +')');

    delete context.fn
    return result;
}
  • apply
Function.prototype.apply2 = function (context, arr) {
    var context = Object(context) || window;
    context.fn = this;

    var result;
    if (!arr) {
        result = context.fn();
    }
    else {
        var args = [];
        for (var i = 0, len = arr.length; i < len; i++) {
            args.push('arr[' + i + ']');
        }
        result = eval('context.fn(' + args + ')')
    }

    delete context.fn
    return result;
}

參考:JavaScript深刻之call和apply的模擬實現

四、實現一個繼承

function Parent(name) {
    this.name = name;
}

Parent.prototype.sayName = function() {
    console.log('parent name:', this.name);
}

function Child(name, parentName) {
    Parent.call(this, parentName);
    this.name = name;
}

function create(proto) {
    function F() {}
    F.prototype = proto;
    return new F();
}
Child.prototype = create(Parent.prototype);
Child.prototype.sayName = function() {
    console.log('child name:', this.name);
}

Child.prototype.constructor = Child;
var parent = new Parent('汪某');
parent.sayName();// parent name: 汪某
var child = new Child('son', '汪某');

五、手寫一個Promise(中高級必考)

面試夠用版

function myPromise(constructor) {
    let self = this;
    self.status = "pending"
        //定義狀態改變前的初始狀態
    self.value = undefined;
    //定義狀態爲resolved的時候的狀態
    self.reason = undefined;
    //定義狀態爲rejected的時候的狀態
    function resolve(value) {
        //兩個==="pending",保證了狀態的改變是不可逆的
        if (self.status === "pending") {
            self.value = value;
            self.status = "resolved";
        }
    }
    function reject(reason) {
        //兩個==="pending",保證了狀態的改變是不可逆的
        if (self.status === "pending") {
            self.reason = reason;
            self.status = "rejected";
        }
    }
    //捕獲構造異常
    try {
        constructor(resolve, reject);
    } catch (e) {
        reject(e);
    }
}

//同時,須要在 myPromise的原型上定義鏈式調用的 then方法:
myPromise.prototype.then = function(onFullfilled, onRejected) {
    let self = this;
    switch (self.status) {
        case "resolved":
            onFullfilled(self.value);
            break;
        case "rejected":
            onRejected(self.reason);
            break;
        default:
    }
}

//測試一下:
var p = new myPromise(function(resolve, reject) {
    resolve(1)
});
p.then(function(x) {
    console.log(x)
})

高級版請參考:史上最最最詳細的手寫Promise教程

六、手寫防抖(Debouncing)和節流(Throttling)

  • 防抖
// 防抖函數
function debounce(fn, wait = 50, immediate) {
    let timer;
    return function() {
        if (immediate) {
            fn.apply(this, arguments)
        }
        if (timer) clearTimeout(timer)
        timer = setTimeout(() => {
            fn.apply(this, arguments)
        }, wait)
    }
}
  • 節流
// 節流函數
function throttle(fn, wait) {
    let prev = new Date();
    return function() {
        const args = arguments;
        const now = new Date();
        if (now - prev > wait) {
            fn.apply(this, args);
            prev = new Date();
        }
    }
}

七、手寫一個JS深拷貝

面試版

function deepCopy(obj) {
    //判斷是不是簡單數據類型,
    if (typeof obj == "object") {
        //複雜數據類型
        var result = obj.constructor == Array ? [] : {};
        for (let i in obj) {
            result[i] = typeof obj[i] == "object" ? deepCopy(obj[i]) : obj[i];
        }
    } else {
        //簡單數據類型 直接 == 賦值
        var result = obj;
    }
    return result;
}

4、VUE

一、Vue的雙向數據綁定原理是什麼?

vue.js 是採用數據劫持結合發佈者-訂閱者模式的方式,經過Object.defineProperty()來劫持各個屬性的setter,getter,在數據變更時發佈消息給訂閱者,觸發相應的監聽回調。

<!-- 
    vue實現數據雙向綁定的原理就是用Object.defineproperty()從新定義(set方法)對象設置屬性值和(get方法)獲取屬性值的操縱來實現的。
    Object.property()方法的解釋:Object.property(參數1,參數2,參數3)  返回值爲該對象obj
    其中參數1爲該對象(obj),參數2爲要定義或修改的對象的屬性名,參數3爲屬性描述符,屬性描述符是一個對象,主要有兩種形式:數據描述符和存取描述符。這兩種對象只能選擇一種使用,不能混合使用。而get和set屬於存取描述符對象的屬性。
    這個方法會直接在一個對象上定義一個新屬性或者修改對象上的現有屬性,並返回該對象。 -->

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
</head>
<body>
    <div id="myapp">
        <input v-model="message" /><br>
        <span v-bind="message"></span>
    </div>
    <script type="text/javascript">
        var model = {
            message: ""
        };
        var models = myapp.querySelectorAll("[v-model=message]");
        for (var i = 0; i < models.length; i++) {
            models[i].onkeyup = function() {
                model[this.getAttribute("v-model")] = this.value;
            }
        }
        // 觀察者模式 / 鉤子函數
        // defineProperty 來定義一個對象的某個屬性
        Object.defineProperty(model, "message", {
            set: function(newValue) {
                var binds = myapp.querySelectorAll("[v-bind=message]");
                for (var i = 0; i < binds.length; i++) {
                    binds[i].innerHTML = newValue;
                };
                var models = myapp.querySelectorAll("[v-model=message]");
                for (var i = 0; i < models.length; i++) {
                    models[i].value = newValue;
                };
                this.value = newValue;
            },
            get: function() {
                return this.value;
            }
        })
    </script>
</body>
</html>

二、請詳細說下你對vue生命週期的理解?

總共分爲8個階段建立前/後,載入前/後,更新前/後,銷燬前/後

  • beforeCreate 建立前執行(vue實例的掛載元素$el和數據對象data都爲undefined,還未初始化)
  • created 完成建立 (完成了data數據初始化,el還未初始化)
  • beforeMount 載入前(vue實例的$el和data都初始化了,但仍是掛載以前爲虛擬的dom節點,data.message還未替換。)
  • mounted 載入後html已經渲染(vue實例掛載完成,data.message成功渲染。)
  • beforeUpdate 更新前狀態(view層的數據變化前,不是data中的數據改變前)
  • updated 更新狀態後
  • beforeDestroy 銷燬前
  • destroyed 銷燬後 (在執行destroy方法後,對data的改變不會再觸發周期函數,說明此時vue實例已經解除了事件監聽以及和dom的綁定,可是dom結構依然存在)

說一下每個階段能夠作的事情

  • beforeCreate:能夠在這裏加一個loading事件,在加載實例時觸發。
  • created:初始化完成時的事件寫這裏,若是這裏結束了loading事件,異步請求也在這裏調用。
  • mounted:掛在元素,獲取到DOM節點
  • updated:對數據進行處理的函數寫這裏。
  • beforeDestroy:能夠寫一個確認中止事件的確認框。

附上一張中文解析圖

vue生命週期圖

三、動態路由定義和獲取

在 router 目錄下的 index.js 文件中,對 path 屬性加上 /:id。

使用 router 對象的 params.id 獲取

四、vue-router 有哪幾種導航鉤子

三種

  • 全局導航鉤子(跳轉前進行判斷攔截)

    • router.beforeEach(to, from, next),
    • router.beforeResolve(to, from, next),
    • router.afterEach(to, from ,next)
  • 組件內鉤子

    • beforeRouteEnter
    • beforeRouteUpdate
    • beforeRouteLeave
  • 單獨路由獨享組件

    • beforeEnter

五、組件之間的傳值通訊?

父組件向子組件傳值:

  • 子組件在props中建立一個屬性,用來接收父組件傳過來的值;
  • 在父組件中註冊子組件;
  • 在子組件標籤中添加子組件props中建立的屬性;
  • 把須要傳給子組件的值賦給該屬性

子組件向父組件傳值:

  • 子組件中須要以某種方式(如點擊事件)的方法來觸發一個自定義的事件;
  • 將須要傳的值做爲$emit的第二個參數,該值將做爲實參傳給響應事件的方法;
  • 在父組件中註冊子組件並在子組件標籤上綁定自定義事件的監聽。

六、vuex

是一個能方便vue實例及其組件傳輸數據的插件 方便傳輸數據,做爲公共存儲數據的一個庫

state: 狀態中心

mutations: 更改狀態,同步的

actions: 異步更改狀態

getters: 獲取狀態

modules: 將state分紅多個modules,便於管理

應用場景:單頁應用中,組件之間的狀態。音樂播放、登陸狀態、加入購物車。

網上找的一個通俗易懂的瞭解vuex的例子

公司有個倉庫
1.State(公司的倉庫)

2.Getter(只能取出物品,包裝一下,不能改變物品任何屬性)

3.Muitation(倉庫管理員,只有他能夠直接存儲到倉庫)

4.Action(公司的物料採購員,負責從外面買東西和接貨, 要往倉庫存東西,告訴倉庫管理員要存什麼)
很是要注意的地方:只要刷新或者退出瀏覽器,倉庫清空。
相關文章
相關標籤/搜索