2019前端基礎面試秘籍

一、html和css部分
1、如何理解CSS的盒子模型?
標準盒子模型:寬度=內容的寬度(content)+ border + padding
低版本IE盒子模型:寬度=內容寬度(content+border+padding)
2、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 會在垂直方向上一個接一個的放置。
    • 垂直方向上的距
      3、如何清除浮動?
      不清楚浮動會發生高度塌陷:浮動元素父元素高度自適應(父元素不寫高度時,子元素寫了浮動后,父元素會發生高度塌陷)
  • 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
    }
    4、用純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;
    }
    5、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>
    6、css實現三欄布局
    左右固定,中間自適應。
    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>
    絕對定位方式
    <!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>
    浮動方式
    <!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>
    7、讓一個div垂直居中
    寬度和高度已知的
    <!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>
    寬度和高度未知
    <!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
    1、閉包
    閉包概念
    能夠讀取其他函數內部變量的函數。或簡單理解為定義在一個函數內部的函數,內部函數持有外部函數內變量的引用。
    閉包用途
    1、讀取函數內部的變量
    2、讓這些變量的值始終保持在內存中。不會再f1調用后被自動清除。
    3、方便調用上下文的局部變量。利于代碼封裝。
    原因:f1是f2的父函數,f2被賦給了一個全局變量,f2始終存在內存中,f2的存在依賴f1,因此f1也始終存在內存中,不會在調用結束后,被垃圾回收機制回收。
    閉包缺點
    1、由于閉包會使得函數中的變量都被保存在內存中,內存消耗很大,所以不能濫用閉包,否則會造成網頁的性能問題,在IE中可能導致內存泄露。解決方法是,在退出函數之前,將不使用的局部變量全部刪除。
    2、閉包會在父函數外部,改變父函數內部變量的值。所以,如果你把父函數當作對象(object)使用,把閉包當作它的公用方法(Public Method),把內部變量當作它的私有屬性(private value),這時一定要小心,不要隨便改變父函數內部變量的值。
    閉包應用場景
    閉包應用場景之setTimeout
    //setTimeout傳遞的第一個函數不能帶參數
    setTimeout((param) => {
    alert(param)
    }, 1000);
    //通過閉包可以實現傳參效果
    function func(param) {
    return function() {
    alert(param)
    }
    }
    var f1 = func('汪某');
    setTimeout(f1, 1000)//汪某
    2、js中函數執行
    在 ES5.1 里面函數是這樣執行的(不討論use strict和一些特殊情況,JS好復雜的),按如下順序執行:
  1. 確定“this”的值 (確切的來說,this在JS里面不是一個變量名而是一個關鍵字)
  2. 創建一個新的作用域
  3. 處理形參/實參(沒有定義過才聲明,無論如何都重新賦值,沒有對應實參則賦值為"undefined"):
    對于每一個傳入的實參,按照從左往右的順序依次執行:如果對應的形參在本作用域中還沒有定義,則在本作用域中聲明形參,并賦值。如果已經定義過了,則重新給其賦值。(沒有對應實參則賦值為"undefined")(沒有定義:就是“沒有聲明”的意思)
  4. 處理函數定義(沒有定義過才聲明,無論如何都重新賦值):
    對該函數中所有的定義的函數,按照代碼寫的順序依次執行:如果這個變量名在本作用域中還沒有定義,則在本作用域中聲明這個函數名,并且賦值為對應的函數,如果定義了這個變量,在可寫的情況下重新給這個變量賦值為這個函數,否則拋出異常。
  5. 處理 "arguments"(沒有定義過才聲明和賦值):
    如果在本作用域中沒有定義 arguments,則在本作用域中聲明arguments并給其賦值。
  6. 處理變量聲明(沒有定義過才聲明,不賦值):
    對于所有變量聲明,按照代碼寫的順序依次執行:如果在本作用域中沒有定義這個變量,則在本作用域中聲明這個變量,賦值為undefined
  7. 然后執行函數代碼。(當然是去變量定義里面的 var 執行)
    3、new一個對象的過程中發生了什么嘛
  8. 創建空對象;
    var obj = {};
  9. 設置新對象的constructor屬性為構造函數的名稱,設置新對象的proto屬性指向構造函數的prototype對象;
    obj.proto = ClassA.prototype;
  10. 使用新對象調用函數,函數中的this被指向新實例對象:
    ClassA.call(obj);//{}.構造函數();
  11. 將初始化完畢的新對象地址,保存到等號左邊的變量中
    4、宏任務跟微任務
    macro-task(宏任務):包括整體代碼script,setTimeout,setInterval
    micro-task(微任務):Promise,process.nextTick
    這一次,徹底弄懂 JavaScript 執行機制
    5、防抖和節流
    綜合應用場景
    防抖(debounce):就是指觸發事件后在 n 秒內函數只能執行一次,如果在 n 秒內又觸發了事件,則會重新計算函數執行時間。
    search搜索聯想,用戶在不斷輸入值時,用防抖來節約請求資源。
    window觸發resize的時候,不斷的調整瀏覽器窗口大小會不斷的觸發這個事件,用防抖來讓其只觸發一次
    節流(throttle):就是指連續觸發事件但是在 n 秒中只執行一次函數。節流會稀釋函數的執行頻率。
    鼠標不斷點擊觸發,mousedown(單位時間內只觸發一次)
    監聽滾動事件,比如是否滑到底部自動加載更多,用throttle來判斷 所謂防抖,就是指觸發事件后在 n 秒內函數只能執行一次,如果在 n 秒內又觸發了事件,則會重新計算函數執行時間。
    防抖函數分為非立即執行版和立即執行版。
    非立即執行版的意思是觸發事件后函數不會立即執行,而是在 n 秒后執行,如果在 n 秒內又觸發了事件,則會重新計算函數執行時間。
    立即執行版的意思是觸發事件后函數會立即執行,然后 n 秒內不觸發事件才能繼續執行函數的效果。
    /**
  • @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 秒中只執行一次函數。 節流會稀釋函數的執行頻率。
    對于節流,一般有兩種方式可以實現,分別是時間戳版和定時器版。
    時間戳版的函數觸發是在時間段內開始的時候
    定時器版的函數觸發是在時間段內結束的時候。
    /
    *
  • @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)
    }
    }
    }
    }
    6、數組的常用方法
    改變原數組的方法
    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開始搜索。
    返回值: 返回布爾
    7、立即執行函數
    聲明一個匿名函數,馬上調用這個匿名函數。目的是保護內部變量不受污染。
    (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));
    8、js原型和原型鏈
    每個對象都會在其內部初始化一個屬性,就是prototype(原型),當我們訪問一個對象的屬性時,如果這個對象內部不存在這個屬性,那么他就會去prototype里找這個屬性,這個prototype又會有自己的prototype,于是就這樣一直找下去,也就是我們平時所說的原型鏈的概念。
    關系:instance.constructor.prototype = instance.proto
    特點: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原型和原型鏈
    9、js中call,apply,bind
    參考:JavaScript中call,apply,bind方法的總結。
    10、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);
    }
    }
    }
    11、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命令的語法糖。
    待補充。。。
    12、深拷貝、淺拷貝
    淺拷貝和深拷貝都只針對于引用數據類型,淺拷貝只復制指向某個對象的指針,而不復制對象本身,新舊對象還是共享同一塊內存;但深拷貝會另外創造一個一模一樣的對象,新對象跟原對象不共享內存,修改新對象不會改到原對象;
    區別:淺拷貝只復制對象的第一層屬性、深拷貝可以對對象的屬性進行遞歸復制;
    淺拷貝的實現方式
    自定義函數
    function simpleCopy (initalObj) {
    var obj = {};
    for ( var i in initalObj) {
    obj[i] = initalObj[i];
    }
    return obj;
    }
    ES6 的 Object.assign()
    let newObj = Object.assign({}, obj);
    ES6 的對象擴展
    let newObj = {...obj};
    深拷貝的實現方式
    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;
    }
    13、跨域
    跨域需要針對瀏覽器的同源策略來理解,同源策略指的是請求必須是同一個端口,同一個協議,同一個域名,不同源的客戶端腳本在沒有明確授權的情況下,不能讀寫對方資源。
    受瀏覽器同源策略的影響,不是同源的腳本不能操作其他源下面的對象。想要操作另一個源下的對象是就需要跨域。
    jsonp
    iframe
    跨域資源共享(CORS)
    nginx 代理跨域
    14、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
    }
    15、如何阻止冒泡?
    冒泡型事件:事件按照從最特定的事件目標到最不特定的事件目標(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;
    }
    16、如何阻止默認事件?
    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;
    }
    17、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來說,這里就是暫緩性死區。
    18、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(); // 我是汪某!
    19、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(); // 清空所有成員 無返回
    基本使用
    傳入數組作為參數,指定鍵值對的數組。
    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
    方法
    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]
    三、手擼代碼
    1、實現一個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);
    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的模擬實現
    3、實現一個Function.bind
    Function.prototype.bind2 = function (context) {
    if (typeof this !== "function") {
    throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
    }
    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);
    var fNOP = function () {};
    var fbound = function () {
    self.apply(this instanceof self ? this : context, args.concat(Array.prototype.slice.call(arguments)));
    }
    fNOP.prototype = this.prototype;
    fbound.prototype = new fNOP();
    return fbound;
    }
    參考:JavaScript深入之bind的模擬實現
    4、實現一個繼承
    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', '汪某');
    5、手寫一個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)
    })
    6、手寫防抖(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();
    }
    }
    }
    7、手寫一個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;
    }
    四、VUE
    1、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" />

    <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>
    2、請詳細說下你對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:可以寫一個確認停止事件的確認框。
    3、動態路由定義和獲取
    在 router 目錄下的 index.js 文件中,對 path 屬性加上 /:id。
    使用 router 對象的 params.id 獲取
    4、vue-router 有哪幾種導航鉤子?
    三種
    全局導航鉤子(跳轉前進行判斷攔截)
    router.beforeEach(to, from, next),
    router.beforeResolve(to, from, next),
    router.afterEach(to, from ,next)
    組件內鉤子
    beforeRouteEnter
    beforeRouteUpdate
    beforeRouteLeave
    單獨路由獨享組件
    beforeEnter
    5、組件之間的傳值通信?
    父組件向子組件傳值:
    子組件在props中創建一個屬性,用來接收父組件傳過來的值;
    在父組件中注冊子組件;
    在子組件標簽中添加子組件props中創建的屬性;
    把需要傳給子組件的值賦給該屬性
    子組件向父組件傳值:
    子組件中需要以某種方式(如點擊事件)的方法來觸發一個自定義的事件;
    將需要傳的值作為$emit的第二個參數,該值將作為實參傳給響應事件的方法;
    在父組件中注冊子組件并在子組件標簽上綁定自定義事件的監聽。
    6、vuex
    是一個能方便vue實例及其組件傳輸數據的插件 方便傳輸數據,作為公共存儲數據的一個庫
    state: 狀態中心
    mutations: 更改狀態,同步的
    actions: 異步更改狀態
    getters: 獲取狀態
    modules: 將state分成多個modules,便于管理
    應用場景:單頁應用中,組件之間的狀態。音樂播放、登錄狀態、加入購物車。
    網上找的一個通俗易懂的了解vuex的例子
    公司有個倉庫
    1.State(公司的倉庫)
    2.Getter(只能取出物品,包裝一下,不能改變物品任何屬性)
    3.Muitation(倉庫管理員,只有他可以直接存儲到倉庫)
    4.Action(公司的物料采購員,負責從外面買東西和接貨, 要往倉庫存東西,告訴倉庫管理員要存什么)
    非常要注意的地方:只要刷新或者退出瀏覽器,倉庫清空。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,702評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,615評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,606評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,044評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,826評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,227評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,307評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,447評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,992評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,807評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,001評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,550評論 5 361
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,243評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,667評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,930評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,709評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,996評論 2 374