前端知識點

html

Doctype做用?標準模式與兼容模式各有什麼區別?

  1. 聲明位於HTML文檔的第一行,告知瀏覽器的解析器用什麼文檔標準解析這個文檔。DOCTYPE不存在或格式不正確會致使文檔以兼容模式呈現。
  2. 標準模式的排版和JS運做模式都是以該瀏覽器支持的最高標準運行。在兼容模式中,頁面以寬鬆的向後兼容的方式顯示,模擬老式瀏覽器的行爲以防止站點沒法工做。簡單的說,就是儘量的能顯示東西給用戶看。javascript

HTML5 爲何只須要寫 <!DOCTYPE HTML>

HTML5 不基於 SGML,所以不須要對DTD進行引用,可是須要doctype來規範瀏覽器的行爲(讓瀏覽器按照它們應該的方式來運行);css

而HTML4.01基於SGML,因此須要對DTD進行引用,才能告知瀏覽器文檔所使用的文檔類型。html

什麼是盒子模型?CSS-標準盒模型和怪異盒模型的區別?哪一個css能夠改變盒子模型?


css盒子模型 又稱爲框模型(Box Model),包含了元素內容(content)、內邊距(padding)、邊框(border)、外邊距(margin)幾個要素。前端

標準盒模型

怪異盒模型

區別:當不對doctype進行定義時,會觸發怪異模式。

  • 在標準模式下,一個塊的總寬度= width + margin(左右) + padding(左右) + border(左右)
  • 在怪異模式下,一個塊的總寬度= width + margin(左右)(即width已經包含了padding和border值)

CSS

什麼是外邊距合併?

外邊距合併指的是,當兩個垂直外邊距相遇時,它們將造成一個外邊距。
合併後的外邊距的高度等於兩個發生合併的外邊距的高度中的較大者。
w3school介紹網址: http://www.w3school.com.cn/css/css_margin_collapsing.asp

注意:水平方向上的外邊距不會合並,而是會相加
複製代碼

如何居中div

  • 水平居中:給div設置一個寬度,而後添加margin:0 auto;屬性
div{
 	width:200px;
 	margin:0 auto;
  }
複製代碼
  • 讓絕對定位的div居中
div {
 	position: absolute;
 	width: 300px;
 	height: 300px;
 	margin: auto;
 	top: 0;
 	left: 0;
 	bottom: 0;
 	right: 0;
 	background-color: pink;	/* 方便看效果 */
 }
複製代碼
  • 水平垂直居中一

肯定容器的寬高 寬500 高 300 的層 設置層的外邊距java

div {
 	position: relative;		/* 相對定位或絕對定位都可 */
 	width:500px;
 	height:300px;
 	top: 50%;
 	left: 50%;
 	margin: -150px 0 0 -250px;     	/* 外邊距爲自身寬高的一半 */
 	background-color: pink;	 	/* 方便看效果 */

  }
複製代碼
  • 水平垂直居中二

未知容器的寬高,利用 transform 屬性node

div {
 	position: absolute;		/* 相對定位或絕對定位都可 */
 	width:500px;
 	height:300px;
 	top: 50%;
 	left: 50%;
 	transform: translate(-50%, -50%);
 	background-color: pink;	 	/* 方便看效果 */

 }

複製代碼
  • 水平垂直居中三

利用 flex 佈局 實際使用時應考慮兼容性webpack

.container {
 	display: flex;
 	align-items: center; 		/* 垂直居中 */
 	justify-content: center;	/* 水平居中 */

 }
 .container div {
 	width: 100px;
 	height: 100px;
 	background-color: pink;		/* 方便看效果 */
 }  
複製代碼

左側固定,右側自適應

法一:

複製代碼

多列等高佈局

來源:codepen.io/yangbo5207/…css3

  1. 視覺等高實現之絕對定位

html代碼nginx

<div class="outer">
  <div class="left">
    <div class="equh"></div>
    <div class="left-con">
      <p>left</p>
      <p>left</p>
      <p>left</p>
    </div>
  </div>
  <div class="right">
    <p>right</p>
    <p>right</p>
    <p>right</p>
    <p>right</p>
    <p>right</p>
  </div>
</div>
複製代碼

css代碼git

.outer {
  width: 960px;
  margin: 0 auto;
  border: 1px solid #000;
  overflow: hidden;
  background-color: green;
  color: #fff;
}
.left {
  width: 200px;
  position: relative;
  float: left;
}
.equh {
  width: 100%;
  height: 999em;
  position: absolute;
  left: 0;
  top: 0;
  border-right: 1px solid #000;
  background-color: orange;
}

.left-con {
  padding: 1em;
  position: relative;
  z-index: 1;
}
.right {
  padding: 1em;
  overflow: hidden;
}

複製代碼
  1. 視覺等高實現之padding-bottom與margin-bottom

核心代碼

padding-bottom: 9999px;
margin-bottom: -9999px;
複製代碼

html代碼

<div class="box">
  <div class="sub">
    <p>a</p>
  </div>
  <div class="sub">
    <p>b</p>
    <p>b</p>
  </div>
  <div class="sub">
    <p>c</p>
    <p>c</p>
    <p>c</p>
  </div>
</div>
複製代碼

css代碼

.box {
  width: 600px;
  overflow: hidden;
  margin: 10px auto;
  border: 1px solid #888;
}
.sub {
  float: left;
  width: 30%;
  margin-right: 3%;
  border: 1px solid orange;
  padding-bottom: 9999px;
  margin-bottom: -9999px;
}
複製代碼
  1. 真實等高實現之table-cell

該方案利用了全部單元格高度都相等的特性,不過因爲ie6/7不支持該屬性,所以略有瑕疵。不過總的來講仍是很是不錯的方案。

html 代碼

<div class="box">
  <div class="row">
    <div class="cell">你必定也有過這種感受的。當你心事重重,渴望找一我的聊一聊的時候,那個能夠聊的人來了,但是大家卻並無聊什麼。固然,聊是聊了,但是他聊他的,你也試着開始聊你的,只是到後來,你放棄了……那麼,最後的辦法就是靜下來,啃齧本身的寂寞。或者反過來講,讓寂寞來吞噬你。------羅蘭《寂寞的感受》</div>
    <div class="cell">做爲一個被基阿異捅過兩個大血窟窿的人。告訴後來的基友們一句:一命二運三風水,四積陰功五讀書。</div>
    <div class="cell">奔波了一天,收到了無數的生日快樂,享受了電影見面會現場各類形式的祝福和禮物,以及場面宏大的生日快樂歌,感謝<西風烈>,感謝支持個人朋友們!如今機場舉長壽麪祝大家都永遠幸福快樂!</div>
  </div>
</div>
複製代碼

css代碼

.box {
  width: 600px;
  margin: 40px auto;
  font-size: 12px;
}
.row {
  display: table-row;
  overflow: hidden;
}
.cell {
  display: table-cell;
  width: 30%;
  padding: 1.6%;
  background-color: #f5f5f5;
  // 在IE6/7下使用上一方法,添加一些hack便可,這樣就能作到所有兼容了
  *float: left;
  *padding-bottom: 9999px;
  *margin-bottom: -9999px;
}
複製代碼
  1. 真實等高實現之彈性盒模型

若不考慮兼容性,此方法最簡單

html代碼

<div class="box">
  <div class="cell">你必定也有過這種感受的。當你心事重重,渴望找一我的聊一聊的時候,那個能夠聊的人來了,但是大家卻並無聊什麼。固然,聊是聊了,但是他聊他的,你也試着開始聊你的,只是到後來,你放棄了……那麼,最後的辦法就是靜下來,啃齧本身的寂寞。或者反過來講,讓寂寞來吞噬你。------羅蘭《寂寞的感受》</div>
  <div class="cell">做爲一個被基阿異捅過兩個大血窟窿的人。告訴後來的基友們一句:一命二運三風水,四積陰功五讀書。</div>
  <div class="cell">奔波了一天,收到了無數的生日快樂,享受了電影見面會現場各類形式的祝福和禮物,以及場面宏大的生日快樂歌,感謝<西風烈>,感謝支持個人朋友們!如今機場舉長壽麪祝大家都永遠幸福快樂!</div>
</div>
複製代碼

css代碼

.box {
  width: 600px;
  margin: 20px auto;
  display: flex;
}
.cell {
  width: 30%;
  border: 1px solid red;
}
複製代碼

總結: 若是須要兼容到ie6/ie7,則使用方法三便可,其中結合了方法二的思路。若是僅僅只是移動端的h5頁面的實現,那麼絕不猶豫的使用彈性盒模型來實現。簡單高效。

position取值

  • static 默認值 沒有定位
  • inherit 規定應該從父元素繼承position屬性的值
  • relative 生成相對定位的元素,相對於其正常位置進行定位
  • absolute 絕對定位 相對於static之外的第一個父元素進行定位
  • fixed 絕對定位 相對於瀏覽器窗口進行定位

sticky佈局

法一:

dom結構

<div class="wrapper">
        <header>header</header>
        <section style="height:300px">內容</section>
    </div>
    <footer>designer by echo hu</footer>
複製代碼

css

/*相同dom不一樣css*/
第一種樣式
footer {
    height: 7em;
    background: #3c3c3c;
}
.wrapper {
    width: 100%;
    min-height: calc(100vh - 7em);
}

第二種樣式
html,body{
    height:100%;
}
footer{
    height:7em;
}
.wrapper{
    height:calc(100% - 7em);
}
複製代碼

法二:

dom結構

<div class="wrapper">
    <div class="main">
       <div class="content"></div>
    </div>
    <div class="close"></div>
</div>
複製代碼

css樣式

.wrapper{
    width:100%;
    height:100%;
}
.main{
    min-height:100%;
}
.close{
    width:100%;
    height:32px;
    margin-top:-32px;
}
複製代碼

flex佈局

what is flex?

flex是Flexible Box的縮寫,意爲彈性佈局。

任何一個容器均可指定爲flex佈局

.box{
    display:flex;
}
複製代碼

行內元素也可使用flex佈局

.box{
    display:inline-flex;
}
複製代碼

注意:設爲flex佈局後,子元素的clear,float,vertical-align屬性將失效

容器的屬性

+ flex-direction
+ flex-wrap
+ flex-flow
+ justify-content
+ align-items
+ align-content
複製代碼

flex-direction屬性決定主軸的方向

.box{
    flex-direction: row | row-reverse | column | column-reverse
}
複製代碼

它有四個值

+ row(默認值):主軸爲水平方向,起點在左端
+ row-reverse:主軸爲水平方向,起點在右端。
+ column:主軸爲垂直方向,起點在上沿。
+ column-reverse:主軸爲垂直方向,起點在下沿。
複製代碼

flex-wrap屬性定義,若是一條軸線排不下,如何換行。默認狀況下,項目都排在一條線上

.box{
    flex-wrap: nowrap(不換行) | wrap(換行,第一行在上方) | wrap-reverse(換行,第一行在下方)
}
複製代碼

flex-flow屬性是flex-directionflex-wrap的簡寫,默認row nowrap

.box {
  flex-flow: <flex-direction> || <flex-wrap>;
}
複製代碼

justify-content屬性定義了項目在主軸上的對齊方式。

它有五個值,具體對齊方式與軸的方向有關。下面假設主軸爲從左往右。

+ flex-start(默認值):左對齊
+ flex-end:右對齊
+ center:居中
+ space-between:兩端對齊,項目之間的間隔都相等
+ space-around:每一個項目兩側的間隔相等。因此,項目之間的間隔比項目與邊框的間隔大一倍。
複製代碼

align-items屬性定義項目在交叉軸上如何對齊 它可能取5個值。具體的對齊方式與交叉軸的方向有關,下面假設交叉軸從上到下。

+ flex-start:交叉軸的起點對齊。
+ flex-end:交叉軸的終點對齊。
+ center:交叉軸的中點對齊。
+ baseline: 項目的第一行文字的基線對齊。
+ stretch(默認值):若是項目未設置高度或設爲auto,將佔滿整個容器的高度。
複製代碼

align-content屬性定義了多根軸線的對齊方式。若是項目只有一根軸線,該屬性不起做用。

該屬性可能取6個值。

+ flex-start:與交叉軸的起點對齊。
+ flex-end:與交叉軸的終點對齊。
+ center:與交叉軸的中點對齊。
+ space-between:與交叉軸兩端對齊,軸線之間的間隔平均分佈。
+ space-around:每根軸線兩側的間隔都相等。因此,軸線之間的間隔比軸線與邊框的間隔大一倍。
+ stretch(默認值):軸線佔滿整個交叉軸。
複製代碼

項目的屬性

如下6個屬性設置在項目上。

+ order 定義項目的排列順序。數值越小,排列越靠前,默認爲0
+ flex-grow 屬性定義項目的放大比例,默認爲0,即若是存在剩餘空間,也不放大。
+ flex-shrink 屬性定義了項目的縮小比例,默認爲1,即若是空間不足,該項目將縮小
+ flex-basis 屬性定義了在分配多餘空間以前,項目佔據的主軸空間
+ flex 是flex-grow, flex-shrink 和 flex-basis的簡寫,默認值爲0 1 auto
+ align-self 屬性容許單個項目有與其餘項目不同的對齊方式,可覆蓋align-items屬性。默認值爲auto,表示繼承父元素的align-items屬性,若是沒有父元素,則等同於stretch。
複製代碼

display取值,元素默認是什麼,內聯元素塊級元素可變元素有哪些?img是什麼?

img是內聯元素

display CSS屬性指定用於元素的呈現框的類型。在 HTML 中,默認的 display 屬性取決於 HTML 規範所描述的行爲或瀏覽器/用戶的默認樣式表。

默認值是inline

display取值以下:

none	此元素不會被顯示。
block	此元素將顯示爲塊級元素,此元素先後會帶有換行符。
inline	默認。此元素會被顯示爲內聯元素,元素先後沒有換行符。
inline-block	行內塊元素。(CSS2.1 新增的值)
list-item	此元素會做爲列表顯示。
run-in	此元素會根據上下文做爲塊級元素或內聯元素顯示。
compact	CSS 中有值 compact,不過因爲缺少普遍支持,已經從 CSS2.1 中刪除。
marker	CSS 中有值 marker,不過因爲缺少普遍支持,已經從 CSS2.1 中刪除。
table	此元素會做爲塊級表格來顯示(相似 <table>),表格先後帶有換行符。
inline-table	此元素會做爲內聯表格來顯示(相似 <table>),表格先後沒有換行符。
table-row-group	此元素會做爲一個或多個行的分組來顯示(相似 <tbody>)。
table-header-group	此元素會做爲一個或多個行的分組來顯示(相似 <thead>)。
table-footer-group	此元素會做爲一個或多個行的分組來顯示(相似 <tfoot>)。
table-row	此元素會做爲一個表格行顯示(相似 <tr>)。
table-column-group	此元素會做爲一個或多個列的分組來顯示(相似 <colgroup>)。
table-column	此元素會做爲一個單元格列顯示(相似 <col>)
table-cell	此元素會做爲一個表格單元格顯示(相似 <td> 和 <th>)
table-caption	此元素會做爲一個表格標題顯示(相似 <caption>)
inherit	規定應該從父元素繼承 display 屬性的值。
複製代碼

float:left狀況下是怎樣的,此時若是超出了寬度範圍

該元素從網頁的正常流動中移除,儘管仍然保持部分的流動性

css3的transform

CSS transform 屬性容許你修改CSS視覺格式模型的座標空間。使用它,元素能夠被轉換(translate)、旋轉(rotate)、縮放(scale)、傾斜(skew)。

javaScript

什麼是閉包?閉包的優缺點


答:閉包是將外部做用域中的局部變量封閉起來的函數對象。被封閉起來的變量與封閉它的函數對象有相同的生命週期。

優勢:一個是前面提到的能夠讀取函數內部的變量,另外一個就是讓這些變量的值始終保持在內存中,不會在f1調用後被自動清除。

缺點

  • 因爲閉包會使得函數中的變量都被保存在內存中,內存消耗很大,因此不能濫用閉包,不然會形成網頁的性能問題,在IE中可能致使內存泄露。解決方法是,在退出函數以前,將不使用的局部變量所有刪除。
  • 閉包會在父函數外部,改變父函數內部變量的值。因此,若是你把父函數看成對象(object)使用,把閉包看成它的公用方法(Public Method),把內部變量看成它的私有屬性(private value),這時必定要當心,不要隨便改變父函數內部變量的值。

輸入一個URL後會發生什麼?

整體來講分爲如下幾個過程:

  • DNS解析

  • TCP鏈接

  • 發送HTTP請求

  • 服務器處理請求並返回HTTP報文

  • 瀏覽器解析渲染頁面

  • 鏈接結束

tcp 協議,http協議

hit-alibaba.github.io/interview/b…

tcp協議便是傳輸控制協議,

http協議:

HTTP構建於TCP/IP協議之上,默認端口號是80 HTTP是無鏈接無狀態的

如何實現一個LazyMan?

題目

實現一個LazyMan,能夠按照如下方式調用:
LazyMan(「Hank」)輸出: Hi! This is Hank!

LazyMan(「Hank」).sleep(10).eat(「dinner」)輸出 Hi! This is Hank! //等待10秒..Wake up after 10 Eat dinner~

LazyMan(「Hank」).eat(「dinner」).eat(「supper」)輸出 Hi This is Hank! Eat dinner~ Eat supper~

LazyMan(「Hank」).sleepFirst(5).eat(「supper」)輸出 //等待5秒 Wake up after 5 Hi This is Hank! Eat supper 以此類推。
複製代碼

實現

  1. 隊列
function _LazyMan(name) {
        this.tasks = []
        var self = this
        var fn = (function(n) {
          var name = n
          return function() {
            console.log('Hi! this is ' + name + '!')
            self.next()
          }
        })(name)
        this.tasks.push(fn)
        setTimeout(function() {
          self.next()
        }, 0)
        // 在下一個事件循環啓動任務
      }
      /* 事件調度函數 */

      _LazyMan.prototype.next = function() {
        var fn = this.tasks.shift()
        fn && fn()
      }
      _LazyMan.prototype.eat = function(name) {
        var self = this
        var fn = (function(name) {
          return function() {
            console.log('Eat ' + name + ' ~')
            self.next()
          }
        })(name)
        this.tasks.push(fn)
        return this // 實現鏈式調用
      }
      _LazyMan.prototype.sleep = function(time) {
        var self = this
        var fn = (function(time) {
          return function() {
            setTimeout(function() {
              console.log('Wake up after ' + time + ' s!')
              self.next()
            }, time * 1000)
          }
        })(time)
        this.tasks.push(fn)
        return this
      }
      _LazyMan.prototype.sleepFirst = function(time) {
        var self = this
        var fn = (function(time) {
          return function() {
            setTimeout(function() {
              console.log('Wake up after ' + time + ' s!')
            }, time * 1000)
          }
        })(time)
        this.tasks.unshift(fn)
        return this
      } /* 封裝 */
      function LazyMan(name) {
        return new _LazyMan(name)
      }
複製代碼
  1. promise
//lazyman裏邊含有鏈式調用,那麼每個子任務 return this;這個程序支持任務優先順序,那麼就須要兩個貫穿全場的Promise對象:第一,普通順序promise;第二,插入順序promise,同時插入順序是阻塞普通順序的,代碼以下:
      function _LazyMan(name) {
        this.orderPromise = this.newPromise() // 定義順序promise對象
        this.insertPromise = this.newPromise() // 定義插入promise對象
        this.order(function(resolve) {
          console.log(name)
          resolve()
        })
      }
      _LazyMan.prototype = {
        /*實例化promise對象工廠*/

        newPromise: function() {
          return new Promise(function(resolve, reject) {
            resolve()
          })
        },
        order: function(fn) {
          var self = this
          this.orderPromise = this.orderPromise.then(function() {
            return new Promise(function(resolve, reject) {
              //若是有insertPromise,阻塞
              orderPromise.self.fir
                ? self.insertPromise.then(function() {
                    fn(resolve)
                  })
                : fn(resolve)
            })
          })
        },
        insert: function(fn) {
          var self = this
          this.fir = true
          this.insertPromise = this.insertPromise.then(function() {
            return new Promise(function(resolve, reject) {
              fn(resolve)
              self.fir = false
            })
          })
        },
        sleepFirst: function(time) {
          this.insert(function(resolve) {
            setTimeout(function() {
              console.log('wait ' + time + ' s,other logic')
              resolve()
            }, time * 1000)
          })
          return this
        },
        eat: function(something) {
          this.order(function(resolve) {
            console.log(something + ' ~~')
            resolve()
          })
          return this
        },
        sleep: function(time) {
          this.order(function(resolve) {
            setTimeout(function() {
              console.log('sleep ' + time + ' s')
            }, time * 1000)
          })
          return this
        }
      } 
      //接口封裝。
      function LazyMan(name) {
        return new _LazyMan(name)
      } 
      //調用測試
      LazyMan('RoryWu')
        .firstTime(1)
        .sleep(2)
        .firstTime(3)
        .eat('dinner')
        .eat('breakfast') // 彈出: // wait 1 s, other logic // wait 3 s, other logic // RoryWu // sleep 2 s // dinner~~ // breakfast~~
複製代碼

用JS代碼求出頁面上一個元素的最終的background-color,不考慮IE瀏覽器,不考慮元素float狀況。

代碼實例

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    .button {
    height: 2em;
    border: 0;
    border-radius: .2em;
    background-color: #34538b;
    color: #fff;
    font-size: 12px;
    font-weight: bold;
}
  </style>
</head>
<body>
  <input type="button" id="button" class="button" value="點擊我,顯示背景色" />
  <script>
    document.getElementById("button").onclick = function() {
    var oStyle =window.getComputedStyle(this, null); // null不是必須
    // 若是考慮IE var oStyle = this.currentStyle? this.currentStyle : window.getComputedStyle(this, null);
    alert(oStyle.getPropertyValue("background-color")); //這裏也能夠用鍵值獲取,建議用getPropertyValue("background-color")
   };
  </script>
</body>
</html>

複製代碼

setTimeout(fn,0)

題目

for (var i = 0; i < 3; i++) {
    setTimeout(function() {
        console.log(i);
    }, 0);
    console.log(i);
}
複製代碼

題解

結果是:0 1 2 3 3 3

不少公司面試都愛出這道題,此題考察的知識點仍是蠻多的。

爲了防止初學者栽在此問題上,此文稍微分析一下。

都考察了那些知識點呢? 異步、做用域、閉包,你沒聽錯,是閉包。

咱們來簡化此題:

setTimeout(function() {
        console.log(1);
}, 0);
console.log(2);
先打印2,後打印1。

由於是setTimeout是異步的。

正確的理解setTimeout的方式(註冊事件):

有兩個參數,第一個參數是函數,第二參數是時間值。

調用setTimeout時,把函數參數,放到事件隊列中。等主程序運行完,再調用。

沒啥很差理解的。就像咱們給按鈕綁定事件同樣:

btn.onclick = function() {
        alert(1);
};
這麼寫完,會彈出1嗎。不會!!只是綁定事件而已!

必須等咱們去觸發事件,好比去點擊這個按鈕,纔會彈出1。

setTimeout也是這樣的!只是綁定事件,等主程序運行完畢後,再去調用。

setTimeout的時間值是怎麼回事呢?

好比:

setTimeout(fn, 2000)
咱們能夠理解爲2000以後,再放入事件隊列中,若是此時隊列爲空,那麼就直接調用fn。若是前面還有其餘的事件,那就等待。 所以setTimeout是一個約會歷來都不許時的童鞋。

繼續看:

setTimeout(function() {
        console.log(i);
}, 0);
var i = 1;
程序會不會報錯?

不會!並且還會準確得打印1。

爲何?

由於真正去執行console.log(i)這句代碼時,var i = 1已經執行完畢了!

因此咱們進行dom操做。能夠先綁定事件,而後再去寫其餘邏輯。

window.onload = function() {
        fn();
}
var fn = function() {
        alert('hello')
};
這麼寫,徹底是能夠的。由於異步! es5中是沒有塊級做用域的

for (var i = 0; i < 3; i++) {}
console.log(i);
也就說i能夠在for循環體外訪問到。因此是沒有塊級做用域。

但此問題在es6裏終結了,由於es6,發明了let。

這回咱們再來看看原題。

原題使用了for循環。循環的本質是幹嗎的?

是爲了方便咱們程序員,少寫重複代碼。

讓咱們倒退50年,原題等價於:

var i = 0;
setTimeout(function() {
    console.log(i);
}, 0);
console.log(i);
i++;
setTimeout(function() {
    console.log(i);
}, 0);
console.log(i);
i++;
setTimeout(function() {
    console.log(i);
}, 0);
console.log(i);
i++;
由於setTimeout是註冊事件。根據前面的討論,能夠都放在後面。 原題又等價於以下的寫法:

var i = 0;
console.log(i);
i++;
console.log(i);
i++;
console.log(i);
i++;
setTimeout(function() {
    console.log(i);
}, 0);
setTimeout(function() {
    console.log(i);
}, 0);
setTimeout(function() {
    console.log(i);
}, 0);
這回你明白了爲啥結果是0 1 2 3 3 3了吧。

那個,說它是閉包,又是怎麼回事?

爲了很好的說明白這個事情,咱們把它放到一個函數中:

var fn = function() {
        for (var i = 0; i < 3; i++) {
                setTimeout(function() {
                        console.log(i);
                }, 0);
                console.log(i);
        }
};
fn();
上面的函數跟咱們常見另外一個例子(div綁定事件)有什麼區別:

var fn = function() {
        var divs = document.querySelectorAll('div');
        for (var i = 0; i < 3; i++) {
                divs[i].onclick = function() {
                        alert(i);
                };
        }
};
fn();
點擊每一個div都會彈出3。道理是同樣的。由於alert(i)中的i是fn做用越中的,於是這是閉包。

《javascript忍者祕籍》書裏把一個函數能調用全局變量,也稱閉包。

由於做者認爲全局環境也能夠想象成一個大的頂級函數。 怎麼保證能彈出0,1, 2呢。

解決之道:以毒攻毒! 再建立個閉包!!

var fn = function() {
        var divs = document.querySelectorAll('div');
        for (var i = 0; i < 3; i++) {
                divs[i].onclick = (function(i) {
                        return function() {
                                alert(i);
                        };
                })(i);
        }
};
fn();
或者以下的寫法:

var fn = function() {
        var divs = document.querySelectorAll('div');
        for (var i = 0; i < 3; i++) {
                (function(i) {
                        divs[i].onclick = function() {
                                alert(i);
                        };
                })(i);
        }
};
fn();
所以原題若是也想setTimeout也彈出0,1,2的話,改爲以下:

for (var i = 0; i < 3; i++) {
    setTimeout((function(i) {
        return function() {
            console.log(i);
        };
    })(i), 0);
    console.log(i);
}

複製代碼

原型與繼承

題目

請用js實現一個類P,包含成員變量a,成員變量b,成員函數sum,sum輸出a與b的和,a,b默認值都爲0。實現一個類M,M繼承自P,在P的基礎上增長成員變量c,成員函數sum變成輸出a,b,c的和。

題目分析

Js全部的函數都有一個prototype屬性,這個屬性引用了一個對象,即原型對象,也簡稱原型。這個函數包括構造函數和普通函數,咱們講的更可能是構造函數的原型,可是也不可否定普通函數也有原型,實現繼承的方法不少,這裏使用原型鏈和構造繼承,即組合繼承的方式。

function P(a, b) {
    this.a = a || 0;
    this.b = b || 0;
    this.sum = function() {
        return this.a + this.b;
    }
}
 
function M(a, b, c) {
    P.call(this, a, b) //繼承P類的成員對象
    this.c = c; //在本身的構造函數中定義的
    this.sum = function() {
        return this.a + this.b + this.c;
    }
}
M.prototype = new P();
var m = new M(2, 2, 2);
M.sum(); //輸出6

複製代碼

js繼承的實現方式

既然要實現繼承,那麼首先咱們得有一個父類,代碼以下:

//定義一個動物類
function Animal(name, eye, skin) {
 
    //屬性
    this.name = name || 'Animal';
    this.eye = eye;
    this.skin = skin;
 
    //實例方法
    this.sleep = function() {
        console.log(this.name + '正在睡覺');
    }
}
 
//原型方法
Animal.prototype.eat = function(food) {
    console.log(this.name + '正在吃:' + food);
};

複製代碼

下面給你們列出幾種繼承方式的實現

原型鏈繼承

實現父類代碼在(js繼承的實現方式中)

核心: 將父類的實例做爲子類的原型

function Cat() {}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';
 
//Test Code
var cat = new Cat();
console.log(cat.name);                     //cat
console.log(cat.eat('fish'));              //cat正在吃:fish
console.log(cat.sleep());                  //cat正在睡覺!
console.log(cat instanceof Animal);        //true
console.log(cat instanceof Cat);           //true
特色:

1. 很是純粹的繼承關係,實例是子類的實例,也是父類的實例

2. 父類新增原型方法/原型屬性,子類都能訪問到

3. 簡單,易於實現

缺點:

1. 要想爲子類新增屬性和方法,必需要在new Animal()這樣的語句以後執行,不能放到構造器中

2. 沒法實現多繼承

3. 來自原型對象的引用屬性是全部實例共享的

4. 建立子類實例時,沒法向父類構造函數傳參(即沒法像這樣var cat=new Cat(hair,eye,skin)傳參給父類)

推薦指數:★★(三、4兩大體命缺陷)
複製代碼

構造繼承

核心:使用父類的構造函數來加強子類實例,等因而複製父類的實例屬性給子類(沒用到原型)

function Cat(name) {
    Animal.call(this);
    this.name = name || 'Tom';
}
 
//Test Code
var cat = new Cat();
console.log(cat.name);                 //Tom
//console.log(cat.eat('fish'));        //報錯
console.log(cat.sleep());              //Tom正在睡覺!
console.log(cat instanceof Animal);    //false
console.log(cat instanceof Cat);       //true
特色:

1. 解決了1中,子類實例共享父類引用屬性的問題

2. 建立子類實例時,能夠向父類傳遞參數(可經過Animal.call(this,name,eye,skin)或者Animal.apply(this,[name,eye,skin])實現)

3. 能夠實現多繼承(call多個父類對象)

缺點:

1. 實例並非父類的實例,只是子類的實例

2. 只能繼承父類的實例屬性和方法,不能繼承原型屬性/方法

3. 沒法實現函數複用,每一個子類都有父類實例函數的副本,影響性能

推薦指數:★★(缺點3)
複製代碼

實例繼承

核心:爲父類實例添加新特性,做爲子類實例返回

function Cat(name) {
    var instance = new Animal();
    instance.name = name || 'Tom';
    return instance;
}
 
//Test Code
var cat = new Cat();
console.log(cat.name); //Tom
console.log(cat.sleep()); //Tom正在睡覺!
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //false
特色:

不限制調用方式,無論是new 子類()仍是子類(),返回的對象具備相同的效果

缺點:

實例是父類的實例,不是子類的實例

不支持多繼承

推薦指數:★★
複製代碼

拷貝繼承

function Cat(name) {
    var animal = new Animal();
    for (var p in animal) {
        Cat.prototype[p] = animal[p];
    }
    Cat.prototype.name = name || 'Tom';
}
 
//Test Code
var cat = new Cat();
console.log(cat.name); //Tom
console.log(cat.sleep()); //Tom正在睡覺!
console.log(cat instanceof Animal); //false
console.log(cat instanceof Cat); //true
特色:

支持多繼承

缺點:

1. 效率較低,內存佔用高(由於要拷貝父類的屬性)

2. 沒法獲取父類不可枚舉的方法(不可枚舉方法,不能使用for in 訪問到)

推薦指數:★(缺點1)
複製代碼

組合繼承

核心:經過調用父類構造,繼承父類的屬性並保留傳參的優勢,而後經過將父類實例做爲子類原型,實現函數複用

function Cat(name) {
    Animal.call(this);
    this.name = name || 'Tom';
}
Cat.prototype = new Animal();
 
//Test Code
var cat = new Cat();
console.log(cat.name);                 //Tom
console.log(cat.sleep());              //Tom正在睡覺!
console.log(cat instanceof Animal);    //true
console.log(cat instanceof Cat);       //true
特色:

1. 彌補了方式2的缺陷,能夠繼承實例屬性/方法,也能夠繼承原型屬性/方法

2. 既是子類的實例,也是父類的實例

3. 不存在引用屬性共享問題

4. 可傳參

5. 函數可複用

缺點:

調用了兩次父類構造函數,生成了兩份實例(子類實例將子類原型上的那份屏蔽了)

推薦指數:★★★★(僅僅多消耗了一點內存)
複製代碼

寄生組合繼承

核心:經過寄生方式,砍掉父類的實例屬性,這樣,在調用兩次父類的構造的時候,就不會初始化兩次實例方法/屬性,避免的組合繼承的缺點

function Cat(name) {
    Animal.call(this);
    this.name = name || 'Tom';
} (function() {
    // 建立一個沒有實例方法的類
    var Super = function() {};
    Super.prototype = Animal.prototype;
    //將實例做爲子類的原型
    Cat.prototype = new Super();
})();
 
//Test Code
var cat = new Cat();
console.log(cat.name); //Tom
console.log(cat.sleep()); //Tom正在睡覺!
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true
特色:

堪稱完美

缺點:

實現較爲複雜

推薦指數:★★★★(實現複雜,扣掉一顆星)

4.9 附錄代碼
function Animal (name) {
  // 屬性
  this.name = name || 'Animal';
  // 實例方法
  this.sleep = function(){
    console.log(this.name + '正在睡覺!');
  }
  //實例引用屬性
  this.features = [];
}
function Cat(name){
}
Cat.prototype = new Animal();
 
var tom = new Cat('Tom');
var kissy = new Cat('Kissy');
 
console.log(tom.name); // "Animal"
console.log(kissy.name); // "Animal"
console.log(tom.features); // []
console.log(kissy.features); // []
 
tom.name = 'Tom-New Name';
tom.features.push('eat');
 
//針對父類實例值類型成員的更改,不影響
console.log(tom.name); // "Tom-New Name"
console.log(kissy.name); // "Animal"
//針對父類實例引用類型成員的更改,會經過影響其餘子類實例
console.log(tom.features); // ['eat']
console.log(kissy.features); // ['eat']
緣由分析:

關鍵點:屬性查找過程

執行tom.features.push,首先找tom對象的實例屬性(找不到),
那麼去原型對象中找,也就是Animal的實例。發現有,那麼就直接在這個對象的
features屬性中插入值。
在console.log(kissy.features); 的時候。同上,kissy實例上沒有,那麼去原型上找。
恰好原型上有,就直接返回,可是注意,這個原型對象中features屬性值已經變化了。
複製代碼

new關鍵字

假設已經定義了父類Base對象

咱們執行以下代碼

var obj = new Base(); 
這樣代碼的結果是什麼,咱們在Javascript引擎中看到的對象模型是:
![](https://img-blog.csdn.net/20180725134719974?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zNzU4MDIzNQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

new操做符具體幹了什麼呢?其實很簡單,就幹了三件事情。

var obj = {}; 
obj.__proto__ = Base.prototype; 
Base.call(obj); 

第一行,咱們建立了一個空對象obj

第二行,咱們將這個空對象的__proto__成員指向了Base函數對象prototype成員對象

第三行,咱們將Base函數對象的this指針替換成obj,而後再調用Base函數

注意:new的過程會執行構造函數Base() 再對空對象進行構造
複製代碼

如何實現圖片懶加載

圖片懶加載的原理很簡單,就是咱們先設置圖片的data-set屬性(固然也能夠是其餘任意的,只要不會發送http請求就好了,做用就是爲了存取值)值爲其圖片路徑,因爲不是src,因此不會發送http請求。 而後咱們計算出頁面scrollTop的高度和瀏覽器的高度之和, 若是圖片舉例頁面頂端的座標Y(相對於整個頁面,而不是瀏覽器窗口)小於前二者之和,就說明圖片就要顯示出來了(合適的時機,固然也能夠是其餘狀況),這時候咱們再將 data-set 屬性替換爲 src 屬性便可。

同源,跨域

推薦閱讀 瀏覽器同源政策及其規避方法
複製代碼

js有幾種類型值,畫內存圖

棧:原始數據類型(Undefined,Null,Boolean,Number、String)

堆:引用數據類型(對象、數組和函數)

兩種類型的區別是:

存儲位置不一樣;

原始數據類型直接存儲在棧(stack)中的簡單數據段,佔據空間小、大小固定,屬於被頻繁使用數據,因此放入棧中存儲;

引用數據類型存儲在堆(heap)中的對象,佔據空間大、大小不固定,若是存儲在棧中,將會影響程序運行的性能;引用數據類型在棧中存儲了指針,該指針指向堆中該實體的起始地址。當解釋器尋找引用值時,會首先檢索其在棧中的地址,取得地址後從堆中得到實體。

![](https://user-gold-cdn.xitu.io/2019/3/20/169991ff8c378649?w=505&h=431&f=png&s=6925)
複製代碼

["1", "2", "3"].map(parseInt)

[1, NaN, NaN] 由於 parseInt 須要兩個參數 (val, radix), 其中 radix 表示解析時用的基數。 map 傳了 3 個 (element, index, array),對應的 radix 不合法致使解析失敗。
複製代碼

聲明提高

var let const

那些操做會形成內存泄漏?

內存泄漏指任何對象在您再也不擁有或須要它以後仍然存在。

垃圾回收器按期掃描對象,並計算引用了每一個對象的其餘對象的數量。若是一個對象的引用數量爲 0(沒有其餘對象引用過該對象),或對該對象的唯一引用是循環的,那麼該對象的內存便可回收。

setTimeout 的第一個參數使用字符串而非函數的話,會引起內存泄漏。

閉包、控制檯日誌、循環(在兩個對象彼此引用且彼此保留時,就會產生一個循環)
複製代碼

關於arguments

1. 定義

因爲JavaScript容許函數有不定數目的參數,因此咱們須要一種機制,能夠在函數體內部讀取全部參數。這就是arguments對象的由來。

arguments對象包含了函數運行時的全部參數,arguments[0]就是第一個參數,arguments[1]就是第二個參數,以此類推。這個對象只有在函數體內部,纔可使用。

var f = function(one) {
    console.log(arguments[0]); //1
    console.log(arguments[1]); //2
    console.log(arguments[2]); //3
}
f(1, 2, 3);
arguments對象除了能夠讀取參數,還能夠爲參數賦值(嚴格模式不容許這種用法)

var f = function(a, b) {
    arguments[0] = 3; //對a從新賦值
    arguments[1] = 2; //對b從新賦值
    return a + b;
}
console.log(f(1, 1)); //5
能夠經過arguments對象的length屬性,判斷函數調用時到底帶幾個參數。

var f = function() {
    return arguments.length;
}
console.log(f(1, 2, 3));               //3
console.log(f(1, 2));                  //2
console.log(f(1));                     //1
console.log(f());                      //0
2. 與數組的關係

須要注意的是,雖然arguments很像數組,但它是一個對象。數組專有的方法(好比slice和forEach),不能在arguments對象上直接使用。

可是,能夠經過apply方法,把arguments做爲參數傳進去,這樣就可讓arguments使用數組方法了。

// 用於apply方法
 myfunction.apply(obj, arguments). 
// 使用與另外一個數組合並 
Array.prototype.concat.apply([1,2,3], arguments) 
要讓arguments對象使用數組方法,真正的解決方法是將arguments轉爲真正的數組。下面是兩種經常使用的轉換方法:slice方法和逐一填入新數組。

var args = Array.prototype.slice.call(arguments); 
 
// or
 
var args = []; 
for (var i = 0; i < arguments.length; i++) { 
    args.push(arguments[i]); 
}
3. callee屬性

arguments對象帶有一個callee屬性,返回它所對應的原函數。

var f = function(one) {
    console.log(arguments.callee === f);
}
f(1);
能夠經過arguments.callee,達到調用函數自身的目的。這個屬性在嚴格模式裏面是禁用的,所以不建議使用。

4. 題目sum(2)(3)

// 寫一個 function 讓下面兩行代碼輸出的結果都爲 5 
console.log(sum(2, 3)); 
console.log(sum(2)(3)); 
說實話,第一眼看到的時候內心是有點虛的(由於第一次看到它)。sum(2)(3),這種形式的代碼確實少見。可是第一反應就是鏈式調用。

鏈式調用咱們熟悉啊,特別是 jQuery 裏面,咱們經常能看到連着寫的代碼。實現原理就是在方法結束時 return 合適的元素對象。

$('#id').parent().siblings('selector').css({
    color: 'red'
});
這道題考什麼呢?認真分析了一下,應該有鏈式調用,toString,柯里化,數組操做等相關內容。大概這些能夠知足需求吧?

如何寫代碼,腦海中大致上有構思了,可是當時手上僅有筆和紙,思路連不上來啊。還好面前放着一臺臺式機(嘿嘿嘿,機器上寫完再抄回紙上)

個人實現大概是這樣的。

var sum = (function() {
    var list = [];
 
    var add = function() {
        // 拼接數組
        var args = Array.prototype.slice.call(arguments);
        list = list.concat(args);
        return add;
    }
    // 覆蓋 toString 方法
    add.toString = function() {
        // 計算總和
        var sum = list.reduce(function(pre, next) {
            return pre + next;
        });
        // 清除記錄
        list.length = 0;
        return sum;
    }
 
    return add;
})();
 
sum(2, 3);
// 5
sum(2)(3);
// 5
這個方法比較複雜,下面介紹個簡便的。

var add = function add() {
    var cache;
    if (arguments.length === 1) {
        cache = arguments[0];
        return function(number) {
            return cache + number;
        }
    } else {
        return arguments[0] + arguments[1];
    }
}
console.log(add(2, 3));
console.log(add(2)(3));

複製代碼

寫一個js判斷全等的方法

//利用JSON.stringify,將兩個對象轉化爲字符串。
字符串相等的話,說明兩個對象全等。
let a = {a:0,b:1,c:2};
let b = {a:0,b:1,c:2};
let c = {a:1,b:1,c:2};
let x = JSON.stringify(a) == JSON.stringify(b);
let y = JSON.stringify(a) == JSON.stringify(c);
console.log(x);
console.log(y);
複製代碼

遍歷對象的方法

var obj={a:'A',b:'B',c:'C'};

1. for ... in 循環
for(key in obj){
    console.log(key);// a,b,c
}

2. Object.keys()
Object.keys(obj);//["a", "b", "c"]
複製代碼

防抖和節流

一、防抖

觸發高頻事件後n秒內函數只會執行一次,若是n秒內高頻事件再次被觸發,則從新計算時間

  • 思路

每次觸發事件時都取消以前的延時調用方法

function debounce(fn) {
      let timeout = null; // 建立一個標記用來存放定時器的返回值
      return function () {
        clearTimeout(timeout); // 每當用戶輸入的時候把前一個 setTimeout clear 掉
        timeout = setTimeout(() => { // 而後又建立一個新的 setTimeout, 這樣就能保證輸入字符後的 interval 間隔內若是還有字符輸入的話,就不會執行 fn 函數
          fn.apply(this, arguments);
        }, 500);
      };
    }
    function sayHi() {
      console.log('防抖成功');
    }

    var inp = document.getElementById('inp');
    inp.addEventListener('input', debounce(sayHi)); // 防抖
複製代碼

二、節流

高頻事件觸發,但在n秒內只會執行一次,因此節流會稀釋函數的執行頻率

  • 思路

每次觸發事件時都判斷當前是否有等待執行的延時函數

function throttle(fn) {
      let canRun = true; // 經過閉包保存一個標記
      return function () {
        if (!canRun) return; // 在函數開頭判斷標記是否爲true,不爲truereturn
        canRun = false; // 當即設置爲false
        setTimeout(() => { // 將外部傳入的函數的執行放在setTimeout中
          fn.apply(this, arguments);
          // 最後在setTimeout執行完畢後再把標記設置爲true(關鍵)表示能夠執行下一次循環了。當定時器沒有執行的時候標記永遠是false,在開頭被return掉
          canRun = true;
        }, 500);
      };
    }
    function sayHi(e) {
      console.log(e.target.innerWidth, e.target.innerHeight);
    }
    window.addEventListener('resize', throttle(sayHi));
複製代碼
  • 問題

爲何要 fn.apply(this, arguments);而不是這樣 fn()

答:加上 apply 確保 在 sayHi 函數裏的 this 指向的是 input對象(否則就指向 window 了,不是咱們想要的)。 這裏的箭頭函數依舊是指向 input 對象。

https怎麼保證安全的?

什麼狀況下會碰到跨域問題?有哪些解決方法?

  • 跨域問題是這是瀏覽器爲了安全實施的同源策略致使的,同源策略限制了來自不一樣源的document、腳本,同源的意思就是兩個URL的域名、協議、端口要徹底相同。
  • script標籤jsonp跨域、nginx反向代理、node.js中間件代理跨域、後端在頭部信息設置安全域名、後端在服務器上設置cors。

如何判斷一個變量是對象仍是數組?

function isObjArr(value){
     if (Object.prototype.toString.call(value) === "[object Array]") {
            console.log('value是數組');
       }else if(Object.prototype.toString.call(value)==='[object Object]'){//這個方法兼容性好一點
            console.log('value是對象');
      }else{
          console.log('value不是數組也不是對象')
      }
}
//ps:千萬不能使用typeof來判斷對象和數組,由於這兩種類型都會返回"object"複製代碼

事件循環機制

html事件循環:
一個瀏覽器環境,只能有一個事件循環,而一個事件循環能夠多個任務隊列(task queue),每一個任務都有一個任務源(task source)。

相同任務源的任務,只能放到一個任務隊列中。

不一樣任務源的任務,能夠放到不一樣任務隊列中。

EcmaScript規範中指出:
任務隊列(Job queue)是一個先進先出的隊列,每個任務隊列是有名字的,至於有多少個任務隊列,取決於實現。每個實現至少應該包含以上兩個任務隊列。

結論:EcmaScript的Job queue與HTML的Task queue有殊途同歸之妙。它們均可以有好幾個,多個任務隊列之間的順序都是不保證的。

例子:
setImmediate(function(){ 
   console.log(1); 
},0); 

setTimeout(function(){ 
   console.log(2); 
},0);

new Promise(function(resolve){ 
    console.log(3); 
    resolve(); 
    console.log(4); 
}).then(function(){
    console.log(5); 
}); 

console.log(6); 

process.nextTick(function(){ 
    console.log(7); 
}); 

console.log(8);

結果:3 4 6 8 7 5 2 1

事件註冊順序以下:
setImmediate - setTimeout - promise.then - process.nextTick

優先級關係:
process.nextTick > promise.then > setTimeout > setImmediate

V8實現中,兩個隊列各包含不一樣的任務:
macrotasks(宏任務): script(總體代碼),setTimeout, setInterval, setImmediate, I/O, UI rendering
microtasks(微任務): process.nextTick, Promises, Object.observe, MutationObserver

執行過程以下:
js引擎首先從macrotask queue中取出第一個任務,執行完畢後,將microtask queue中的全部任務取出,按順序所有執行;而後再從macrotask queue中取下一個,執行完畢後,再次將microtask queue中的所有取出; 循環往復,直到兩個queue中的任務都取完。

setTimeout會默認延遲4毫秒(ms)。

問題:
process.nextTick也會放入microtask quque,爲何優先級比promise.then高呢?

答:process.nextTick 永遠大於promise.then,緣由其實很簡單。
在Node中,_tickCallback在每一次執行完TaskQueue中的一個任務後被調用,而這個_tickCallback中實質上幹了兩件事:
1. nextTickQueue中全部任務執行掉(長度最大1e4,Node版本v6.9.1)
2. 第一步執行完後執行_runMicrotasks函數,執行microtask中的部分(promise.then註冊的回調)
因此很明顯process.nextTick > promise.then

複製代碼

深拷貝和淺拷貝

區別

1.淺拷貝: 將原對象或原數組的引用直接賦給新對象,新數組,新對象/數組只是原對象的一個引用

2.深拷貝: 建立一個新的對象和數組,將原對象的各項屬性的「值」(數組的全部元素)拷貝過來,是「值」而不是「引用」

爲何要使用深拷貝?

咱們但願在改變新的數組(對象)的時候,不改變原數組(對象)

深拷貝的要求程度

咱們在使用深拷貝的時候,必定要弄清楚咱們對深拷貝的要求程度:是僅「深」拷貝第一層級的對象屬性或數組元素,仍是遞歸拷貝全部層級的對象屬性和數組元素?

怎麼檢驗深拷貝成功

改變任意一個新對象/數組中的屬性/元素, 都不改變原對象/數組

只作第一層深拷貝

深拷貝數組(只拷貝第一級數組元素)

  1. 直接遍歷
var arr = [1,2,3,4];


function copy(arg){
  
  var newArr = [];
  
  for(var i = 0; i < arr.length; i++) {
    newArr.push(arr[i]);
  }
  
  return newArr;
}

var newArry = copy(arr);
console.log(newArry);
newArry[0] = 10;
console.log(newArry); // [10,2,3,4]
console.log(arr)  // [1,2,3,4]
複製代碼
  1. slice()
var arr = [1,2,3,4]
var copyArr = arr.slice();
copyArr[0] = 10;
console.log(copyArr); // [10,2,3,4]
console.log(arr); // [1,2,3,4]

// slice() 方法返回一個從已有的數組中截取一部分元素片斷組成的新數組(不改變原來的數組!)
用法:array.slice(start,end) start表示是起始元素的下標, end表示的是終止元素的下標
當slice()不帶任何參數的時候,默認返回一個長度和原數組相同的新數組

複製代碼
  1. concat()
var arr = [1,2,3,4]
var copyArr = arr.concat();
copyArr[0] = 10;
console.log(copyArr); // [10,2,3,4]
console.log(arr); // [1,2,3,4]

//concat() 方法用於鏈接兩個或多個數組。( 該方法不會改變現有的數組,而僅僅會返回被鏈接數組的一個副本。)
用法:array.concat(array1,array2,......,arrayN)
由於咱們上面調用concat的時候沒有帶上參數,因此var copyArray = array.concat();實際上至關於var copyArray = array.concat([]);
也即把返回數組和一個空數組合並後返回
複製代碼

深拷貝對象

1.直接遍歷

var obj = {
    name: "張三",
    job: "學生"
  }
  
  function copy (obj) {
    let newobj = {}
    for(let item in obj) {
      newobj[item] = obj[item];
    }
    return newobj;
  }
  
  var copyobj = copy(obj)
  copyobj.name = "李四"
  console.log(copyobj) // {name: '李四', job:: '學生'}
  console.log(obj) // {name: '張三', job:: '學生'}

複製代碼
  1. ES6的Object.assign
var obj = {
 name: '張三',
 job: '學生'
}

var copyobj = Object.assign({},obj)
copyobj.name = '李四'
console.log(copyobj) // {name: '李四', job:: '學生'}
console.log(obj)    // {name: '張三', job:: '學生'}

Object.assign:用於對象的合併,將源對象(source)的全部可枚舉屬性,複製到目標對象(target),並返回合併後的target
用法: Object.assign(target, source1, source2);  因此 copyObj = Object.assign({}, obj);  這段代碼將會把obj中的一級屬性都拷貝到 {}中,而後將其返回賦給copyObj
複製代碼
  1. ES6擴展運算符:
var obj = {
  name: '張三',
  job: '學生'
}

var copyobj = {...obj}
copyobj.name = '李四'
console.log(copyobj)
console.log(obj)

擴展運算符(...)用於取出參數對象的全部可遍歷屬性,拷貝到當前對象之中
複製代碼

ajax請求

// 1.XMLHttpRequest對象用於在後臺與服務器交換數據   
var xhr = new XMLHttpRequest();
// 2.
xhr.open('GET', url, true);
//3.發送請求
xhr.send();
//4.接收返回
//客戶端和服務器端有交互的時候會調用onreadystatechange
xhr.onreadystatechange = function() {
    // readyState == 4說明請求已完成
    if (xhr.readyState == 4 && xhr.status == 200 || xhr.status == 304) { 
        // 從服務器得到數據 
        fn.call(this, xhr.responseText);  
    }
};
複製代碼

前端安全性問題

一、xss跨站腳本攻擊(原理、如何進行的、防護手段是什麼,要說清楚)

二、CSRF跨站請求僞造(如何僞造法?怎麼防護?等等都要說清楚)

三、sql腳本注入(注入方式,防護方式)

四、上傳漏洞 (防護方式)

你是如何處理前端性能問題的?

減小 HTTP 請求數量

在瀏覽器與服務器進行通訊時,主要是經過 HTTP 進行通訊。瀏覽器與服務器須要通過三次握手,每次握手須要花費大量時間。並且不一樣瀏覽器對資源文件併發請求數量有限(不一樣瀏覽器容許併發數),一旦 HTTP 請求數量達到必定數量,資源請求就存在等待狀態,這是很致命的,所以減小 HTTP 的請求數量能夠很大程度上對網站性能進行優化。

  • CSS Sprites:國內俗稱 CSS 精靈,這是將多張圖片合併成一張圖片達到減小 HTTP 請求的一種解決方案,能夠經過 CSS background 屬性來訪問圖片內容。這種方案同時還能夠減小圖片總字節數。
  • 合併 CSS 和 JS 文件:如今前端有不少工程化打包工具,如:grunt、gulp、webpack等。爲了減小 HTTP 請求數量,能夠經過這些工具再發布前將多個 CSS 或者 多個 JS 合併成一個文件。
  • 採用 lazyLoad:俗稱懶加載,能夠控制網頁上的內容在一開始無需加載,不須要發請求,等到用戶操做真正須要的時候當即加載出內容。這樣就控制了網頁資源一次性請求數量。

控制資源文件加載優先級

瀏覽器在加載 HTML 內容時,是將 HTML 內容從上至下依次解析,解析到 link 或者 script 標籤就會加載 href 或者 src 對應連接內容,爲了第一時間展現頁面給用戶,就須要將 CSS 提早加載,不要受 JS 加載影響。 通常狀況下都是 CSS 在頭部,JS 在底部。

利用瀏覽器緩存

瀏覽器緩存是將網絡資源存儲在本地,等待下次請求該資源時,若是資源已經存在就不須要到服務器從新請求該資源,直接在本地讀取該資源。

減小重排(Reflow)

基本原理:重排是 DOM 的變化影響到了元素的幾何屬性(寬和高),瀏覽器會從新計算元素的幾何屬性,會使渲染樹中受到影響的部分失效,瀏覽器會驗證 DOM 樹上的全部其它結點的 visibility 屬性,這也是 Reflow 低效的緣由。若是 Reflow 的過於頻繁,CPU 使用率就會急劇上升。 減小 Reflow,若是須要在 DOM 操做時添加樣式,儘可能使用 增長 class 屬性,而不是經過 style 操做樣式。

減小 DOM 操做

圖標使用 IconFont 替換

算法

寫一個冒泡算法

思路:
    a)比較兩個相鄰的元素,若是後一個比前一個大,則交換位置

    b) 第一輪的時候最後一個元素應該是最大的一個

    c) 按照第一步的方法進行兩個相鄰的元素的比較,因爲最後一個元素已是最大的了,因此最後一個元素不用比較。
代碼:
    
    function sort(element){
        for(var i = 0;i<element.length-1;i++) {
            for(var j = 0;j<element.length-i-1;j++){
                if(element[j]>element[j+1]){
                    //把大的數字放到後面
                    var swap = element[j];
                    element[j] = element[j+1];
                    element[j+1] = swap;
                }
            }
        }
    }
    var element = [3,5,1,2,7,8,4,5,3,4];
    sort(element);
複製代碼

寫一個快速排序

"快速排序"的思想很簡單,整個排序過程只須要三步:

(1)在數據集之中,選擇一個元素做爲"基準"(pivot)。

(2)全部小於"基準"的元素,都移到"基準"的左邊;全部大於"基準"的元素,都移到"基準"的右邊。

(3)對"基準"左邊和右邊的兩個子集,不斷重複第一步和第二步,直到全部子集只剩下一個元素爲止。
複製代碼
function quickSort(arr) {
  if(arr.length < 2) {
    return arr;
  } else {
    const pivot = arr[0]; // 基準值
    const pivotArr = []; // 同樣大的放中間
    const lowArr= []; // 小的放左邊
    const hightArr = []; // 大的放右邊
    arr.forEach(current => {
      if(current === pivot) pivotArr.push(current);
      else if(current > pivot) hightArr.push(current);
      else lowArr.push(current);
    })
    return quickSort(lowArr).concat(pivotArr).concat(quickSort(hightArr));
  }
}
複製代碼

移動端適配

插件:amfe-flexible + postcss-px2rem

amfe-flexible:自動根據不一樣設備改變data-dpr的值,這樣就能夠根據不一樣的data-dpr設置字體大小不變,僅放大相應倍數。

postcss-px2rem:打包的時候把項目裏面的px統一轉換成rem,轉換的基準值根據配置設置的(.postcssrc.js) /由於我是以750px(iphone6)寬度爲基準,因此remUnit爲37.5/

通過試驗結果:

postcss-px2rem:只負責把項目裏面的px按照基準值轉換成rem,並不負責根節點動態font-size的計算。 例如,代碼裏面有個高度固定:180px, 基準值是:37.5, 那最後界面上的rem=180/37.5=4.8rem 無論換不一樣客戶端手機,不一樣分辨率,界面上都是固定4.8rem【rem的值是固定的,根據根節點的font-size不一樣,在界面顯示的px也不一樣】,界面上顯示的px = 16(沒有設置font-size的話默認是16px)* 4.8rem = 76.8px

相關文章
相關標籤/搜索