2019前端面試總結(jié)

HTML面試題

  1. 你是如何理解HTML語(yǔ)義化的?
  • 比較簡(jiǎn)單的回答:我理解的語(yǔ)義化就是 標(biāo)簽用在合適的位置,比如段落要用p標(biāo)簽,標(biāo)題要用h1-h6標(biāo)簽.
  • 更細(xì)點(diǎn)的回答:我理解的HTML語(yǔ)義化是正確的標(biāo)簽做正確的事情,能夠便于開(kāi)發(fā)者閱讀和寫出更優(yōu)雅的代碼的同時(shí),讓網(wǎng)絡(luò)爬蟲(chóng)更好的解析。
  1. 為什么要做到語(yǔ)義化?
  • 有利于SEO,有利于搜索引擎爬蟲(chóng)更好的解析我們的頁(yè)面,從而獲取更多的有效信息,提升網(wǎng)頁(yè)的權(quán)重。
  • 在沒(méi)有CSS的時(shí)候,能夠清晰看出網(wǎng)頁(yè)的結(jié)構(gòu),增強(qiáng)可讀性。
  • 便于團(tuán)隊(duì)合作開(kāi)發(fā)和維護(hù),提高開(kāi)發(fā)效率
  1. <!DOCTYPE> 文檔聲明,它不是HTML標(biāo)簽,是一個(gè)指示web瀏覽關(guān)于頁(yè)面使用哪個(gè)HTML版本編寫的指令。<!DOCTYPE> 聲明必須位于文檔的第一行,位于<html>標(biāo)簽之前。
    <!DOCTYPE html>

  2. <html lang='en'>lang屬性設(shè)定文檔語(yǔ)言。

    作用:SEO搜索引擎優(yōu)化;有助于閱讀障礙人士,通過(guò)讀屏器閱讀頁(yè)面

    還可以是 <html lang="zh-CN">

5.meta標(biāo)簽的幾種用法。

  • meta指定文檔編碼
//這行代碼的意思是,文檔用UTF-8編碼的,瀏覽器解析的時(shí)候用UTF-8編碼解析。
<meta charset="UTF-8">

  • 適配移動(dòng)頁(yè)面
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<meta name="viewport"  content="width=device-width,initial-scale=1.0">

  • 添加頁(yè)面描述
<meta name="description" content="騰訊網(wǎng)(www.qq.com)是中國(guó)最瀏覽量最大的門戶網(wǎng)站">

6.你用過(guò)哪些 HTML5標(biāo)簽。

內(nèi)容性的標(biāo)簽:

<header>          網(wǎng)頁(yè)的頭部
<nav>                網(wǎng)頁(yè)的導(dǎo)航
<section>          標(biāo)簽定義文檔中的節(jié)(比如章節(jié)、頁(yè)眉、頁(yè)腳或文檔中的其他部分。)
<article>            標(biāo)簽的內(nèi)容獨(dú)立于文檔的其余部分。比如外部的一篇文章,一個(gè)博客,論文等。
<aside>              網(wǎng)頁(yè)側(cè)邊欄
<footer>            網(wǎng)頁(yè)的頁(yè)腳         

功能性的標(biāo)簽

<canvas>  通過(guò)腳本繪制圖像
<Audio>    播放音頻
<Video>    播放視頻

  1. 什么是H5?
    H5是中國(guó)人制造的一個(gè)專有名詞
    實(shí)際上是指移動(dòng)端頁(yè)面,從某種意義上來(lái)說(shuō)它是 HTML5,微信網(wǎng)頁(yè),移動(dòng)PPT的母級(jí)。

CSS面試題

  1. 兩種盒模型。
    盒模型一共有兩種模式:
  • 標(biāo)準(zhǔn)盒模型
    標(biāo)準(zhǔn)盒模型下 盒子的總寬度/高度=width/height+padding+border+margin,標(biāo)準(zhǔn)盒模型下 width是指content的寬度
  • 怪異盒模型
    怪異盒模型下 盒子的總寬度/高度=width/height+margin,怪異盒模型下 width 指的是內(nèi)容content+padding+border的寬度------IE6,7,8 不設(shè)置 <!DOCTYPE html> 情況下,是怪異盒模型,如果加了就是標(biāo)準(zhǔn)盒模型
  • 具體是使用哪一種盒模型,用box-sizing來(lái)設(shè)置
  • 兼容性: box-sizing現(xiàn)代瀏覽器都支持,但I(xiàn)E家族只有IE8版本以上才支持
box-sizing:content-box;
box-sizing:border-box;

目前使用此屬性,需要加前綴,Mozilla需要加上-moz-,Webkit內(nèi)核需要加上-webkit-,Presto內(nèi)核-o-,IE8-ms-
-webkit-box-sizing:content-box;
-moz-box-sizing:birder-box;

標(biāo)準(zhǔn)盒模型的缺點(diǎn)是,盒子的寬度和高度計(jì)算復(fù)雜(兩子元素并排例子)
怪異盒模型的優(yōu)點(diǎn),當(dāng)確定了width/height之后,可以隨意修改padding和border的厚度值,而不用擔(dān)心父容器被撐爆。

  1. 如何實(shí)現(xiàn)水平和垂直居中?
  • 元素水平居中
//行內(nèi)元素
text-align:center

//塊級(jí)元素
margin-left: auto;
margin-right:auto;

  • 元素垂直居中
//方案1     position-----元素固定寬高的情況下
<div class="out">
  out
  <div class="in">in</div>
</div>

<style>
.out{
  width:300px;
  height:300px;
  background:#ccc;
  color:red;
  position:relative;
}
.in{
  width:100px;
  height:100px;
  background:pink;
  color:blue;
  position:absolute;
  left:50%;
  top:50%;
  margin-left:-50px;
  margin-top:-50px;
}
</style>

image
//方案2     元素寬高不固定的情況下,把上邊的 margin-left:(寬度/2);
  margin-top:(高度/2) 換成 transform:translate(-50%,-50%);
<div class="out">
  out
  <div class="in">測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)試測(cè)</div>
</div>

<style>
.out{
  width:300px;
  height:300px;
  background:#ccc;
  color:red;
  position:relative;
}
.in{
  background:pink;
  color:blue;
  position:absolute;
  left:50%;
  top:50%;
  transform:translate(-50%,-50%);
}
</style>

image
//方案3  flex
<div class="out">
  <div class="in"></div>
</div>

<style>
.out{
  width:300px;
  height:300px;
  background:#ccc;
  display:flex;  //flex布局
  justify-content:center;  //規(guī)定項(xiàng)目在主軸上居中對(duì)齊(水平居中)
  align-items:center;   //規(guī)定項(xiàng)目在交叉軸上居中對(duì)齊(垂直居中)
}
.in{
  width:100px;
  height:100px;
  background:pink;
}
</style>

image
//方案4 table-cell
給父元素設(shè)置 display:table; 父元素變成塊級(jí)表格元素(相當(dāng)于table);
給子元素設(shè)置 display:table-cell;使子元素變成表格單元格(相當(dāng)于td),然后設(shè)置 vertical-align:center; text-align:center;
<div class="out">
  <div class="in">哈哈哈哈哈哈哈哈哈哈</div>
</div>

<style>
.out{
  width:300px;
  height:300px;
  background:#ccc;
  display:table;   
}

.in{
  background:pink;
  display:table-cell;     
  vertical-align:middle;  
  text-align:center;      
}
</style>

image
  1. flex怎么用?常用的屬性有哪些?
    flex彈性布局,為盒模型提供最大的靈活性,任何一個(gè)容器,都可以指定為flex布局;
.box{
  display:flex;
}

//行內(nèi)元素也可以設(shè)置為flex
.box{
    display:inline-flex;
}

注意設(shè)為flex布局后,子元素的float,clear,vertical-align屬性將失效。

采用flex布局的元素,成為flex容器。它的所有子元素自動(dòng)成為容器成員,稱為flex項(xiàng)目。
常用的,設(shè)置到容器上的屬性有:


1.flex-direction:屬性決定主軸的方向(即項(xiàng)目的排列方向)。
.box{
  flex-direction:row | row-reverse | column | column-reverse
}

--------------------------------------------------------------------------------

2\. flex-wrap:默認(rèn)情況下,項(xiàng)目都排在一條線上。這個(gè)屬性定義,如果一條軸線拍不下,如何換行。
.box{
  flex-wrap:nowrap | wrap | wrap-reverse
}

--------------------------------------------------------------------------------

3\. justify-content:屬性定義了項(xiàng)目在主軸上的對(duì)齊方式。
.box{
   justify-content:flex-start | flex-end | center | space-between | space-around
}

--------------------------------------------------------------------------------

4.align-items:屬性定義項(xiàng)目在交叉軸上如何對(duì)齊
.box{
   align-items:flex-start | flex-end | center | baseline | stretch
}
baseline 項(xiàng)目的第一行文字的基線對(duì)齊
stretch(默認(rèn)值)  如果項(xiàng)目未設(shè)置高度或設(shè)為auto,將占滿整個(gè)容器的高度。

--------------------------------------------------------------------------------

5\. align-content:屬性定義了多根軸線的對(duì)齊方式。如果項(xiàng)目只有一根軸線,該屬性不起作用。
.box {
  align-content: flex-start | flex-end | center | space-between | space-around | stretch;
}

--------------------------------------------------------------------------------
6\. flex-flow  是flex-direction和 flex-wrap的簡(jiǎn)寫,默認(rèn)是 row nowrap

設(shè)置到項(xiàng)目上的屬性:

1.order 屬性定義項(xiàng)目的排列順序。數(shù)值越小,排列越靠前。默認(rèn)為0
.item {
  order: 1;
}

2.flex-grow屬性定義項(xiàng)目的放大比例,默認(rèn)為0.即如果有剩余空間,也不放大。
.item {
  flex-grow: <number>; /* default 0 */
}

3.flex-shrink 屬性定義了項(xiàng)目的縮小比例。默認(rèn)為1,如果空間不足,項(xiàng)目將縮小。
.item {
  flex-shrink: <number>; /* default 1 */
}
如果所有項(xiàng)目的flex-shrink屬性都為1,當(dāng)空間不足時(shí),都將等比例縮小。如果一個(gè)項(xiàng)目的flex-shrink屬性為0,其他項(xiàng)目都為1,則空間不足時(shí),前者不縮小。

負(fù)值對(duì)該屬性無(wú)效。

4\. flex-basis 屬性定義了在分配多余空間之前,項(xiàng)目占據(jù)的主軸空間。默認(rèn)為auto.即項(xiàng)目本來(lái)的大小。它可以設(shè)為跟width或height屬性一樣的值(比如350px),則項(xiàng)目將占據(jù)固定空間。
.item {
  flex-basis: <length> | auto; /* default auto */
}

5\. flex 屬性是flex-grow,flex-shrink,flex-basis,默認(rèn)值為0 1 auto
該屬性有兩個(gè)快捷值:auto (1 1 auto) 和 none (0 0 auto)。
建議優(yōu)先使用這個(gè)屬性,而不是單獨(dú)寫三個(gè)分離的屬性,因?yàn)闉g覽器會(huì)推算相關(guān)值。

6.align-self 屬性 允許單個(gè)項(xiàng)目有與其他項(xiàng)目不一樣的對(duì)齊方式,可覆蓋align-items屬性,默認(rèn)值為auto,表示繼承父元素的align-items屬性,如果沒(méi)有父元素,則等同于stretch。
.item {
  align-self: auto | flex-start | flex-end | center | baseline | stretch;
}

  1. BFC是什么?
    (Block Formatting Context)塊級(jí)格式化上下文。BFC就是頁(yè)面上的一個(gè)隔離的獨(dú)立容器,容器里面的子元素不會(huì)影響到外面的元素,反之也如此.并且在一個(gè)BFC中,塊盒與行盒(行盒由一行中所有的內(nèi)聯(lián)元素所組成)都會(huì)垂直的沿著其父元素的邊框排列。
    這個(gè)可以具體回答,比如給一個(gè)div設(shè)置 overflow:hidden 它里邊的浮動(dòng)元素就會(huì)被包裹起來(lái)。
    如何觸發(fā)BFC?
1. 根元素 即HTML元素
2. float的值,不為none
3. overflow的值,不為visible
4. display的值,為inline-block,table-cell ,table-caption
5. position 的值,為absolute 或fixed

  1. 選擇器的優(yōu)先級(jí)?
    CSS2的時(shí)候 (如果面試官更傾向于這個(gè)就這樣回答)
!important 優(yōu)先級(jí)最高  權(quán)值無(wú)窮大
style 寫在元素行的內(nèi)聯(lián)樣式其次   權(quán)值1000
id   權(quán)值100 
class/偽類/屬性     權(quán)值10 
標(biāo)簽選擇器       權(quán)值1
通配符        權(quán)值0 

更準(zhǔn)確的說(shuō)法
1.越具體,優(yōu)先級(jí)越高
2.寫在后面的,會(huì)覆蓋寫在前面的
3.important優(yōu)先級(jí)最高,但是要少用

6.清除浮動(dòng)的方法:

方法
1:給父元素設(shè)置高度
2:給父元素設(shè)置overflow:hidden
3:  最佳實(shí)踐  
.clearfix::after{
  content:'';
  display:block;
  clear:both;
}
.clearfix加到容器上,里邊的子元素浮動(dòng)就被清除了


原生JS面試題

1. ES6語(yǔ)法知道哪些?分別怎么用

A-------新增聲明命令 let 和 const, let表示變量 const表示常量

特點(diǎn):
1. 都是塊級(jí)作用域,以{}代碼塊作為作用域范圍,只能在代碼塊里面使用。
2. 不存在變量提升,只能先聲明再使用,否則會(huì)報(bào)錯(cuò)。在代碼塊內(nèi),在聲明變量之前,該變量都是不可用的,這在語(yǔ)法上成為'暫時(shí)性死區(qū)'
3. 在一個(gè)代碼塊內(nèi),不允許重復(fù)聲明
4. const聲明的是一個(gè)只讀常量,在聲明的時(shí)候就要賦值,否則會(huì)擺錯(cuò),(如果const聲明的是一個(gè)對(duì)象,對(duì)象所包含的值是可以修改的,抽象一點(diǎn)說(shuō),對(duì)象所指的地址是不能夠改變的,變量成員是可以修改的)

B--------模板字符串
用一對(duì)反引號(hào)(``)標(biāo)識(shí),它可以當(dāng)做普通字符串使用,也可以用來(lái)定義多行字符串。也可以在字符串中嵌入變量,JS表達(dá)式,或函數(shù)。需要寫在${}中

var str = `abc
def
gh`;
console.log(str);
let name = '明';
function a(){
  return 'ming'
}
console.log(`我的名字叫做${name},年齡${17+5}歲,我的性別是${'男'},游戲ID:${a()})

C------函數(shù)的擴(kuò)展

  • 函數(shù)的默認(rèn)參數(shù)
    ES6為參數(shù)提供了默認(rèn)值,在定義函數(shù)的時(shí)候,便初始化了這個(gè)參數(shù),以便在參數(shù)沒(méi)有被傳遞進(jìn)去的時(shí)候使用。
function A(a,b=1){
  console.log(a+b)  
  console.log(a);
  console.log(b);
}
A(1);   // 2, 1,1
A(2,3);   //5,2,3

  • 箭頭函數(shù)
    ES6中提供了一種簡(jiǎn)潔的函數(shù)的寫法,箭頭函數(shù)。
//只有一個(gè)參數(shù)時(shí)候,可以省略參數(shù)的括號(hào)。
//如果沒(méi)有參數(shù),或者參數(shù)在2個(gè)及兩個(gè)以上,必須帶括號(hào)
//當(dāng)代碼只有一行,并且有立即返回值的時(shí)候,可以省略花括號(hào){}
var f = a => a+1

var sayHi = ()=>{
  console.log('hi')
}

var sum = (num1,num2)=>{
    console.log(num1+mun2)
}

箭頭函數(shù)的特點(diǎn)
箭頭函數(shù)的this是在定義函數(shù)的時(shí)候綁定,而不是在執(zhí)行函數(shù)的時(shí)候綁定。
所謂在定義函數(shù)的時(shí)候綁定的意思是,箭頭函數(shù)的this是繼承自父執(zhí)行上下文。箭頭函數(shù)沒(méi)有自己的this.

D---------對(duì)象的擴(kuò)展

  • 屬性的簡(jiǎn)寫
    ES6允許在對(duì)象之中,直接寫變量,這時(shí)屬性名為變量名,屬性的值為變量的值。
var foo = 'bar';
var baz = {foo};   //相當(dāng)于 var bar={foo:foo}

  • 方法的簡(jiǎn)寫
    省略冒號(hào)和function關(guān)鍵字
var o = {
  sayHi:function(){
      console.log('hi')
}
}

相當(dāng)于
var o = {
  sayHi(){
  console.log('hi')
}
}

  • Object.keys()方法,獲取對(duì)象的所有屬性名和方法名。不包括原型的內(nèi)容,返回一個(gè)數(shù)組
var person = {name:"john",age:20,study(){alert('study')}};
console.log(Object.keys(person))    //  ["name", "age", "study"]

console.log(Object.keys(['aa','bb','cc']);      //["0", "1", "2"]
console.log(Object.keys('abcdef'));      //["0", "1", "2", "3", "4", "5"]

  • Object.assign()方法
    這個(gè)方法將多個(gè)原對(duì)象的屬性和方法,合并到了目標(biāo)對(duì)象上面。
    可以接收多個(gè)參數(shù),第一個(gè)參數(shù)是目標(biāo)對(duì)象,后邊都是源對(duì)象
var target ={}
var obj1 = {
  name:'petter',
  age:20,
  sex:'女'
}
var obj2 = {
  sex:'男',
  score:100
}

Object.assign(target,obj1,obj2);
console.log(target);  
//{age: 20,name: "petter", score: 100, sex: "男"}

E---------- for...of循環(huán)
是遍歷所有數(shù)據(jù)結(jié)構(gòu)的統(tǒng)一方法。for...of循環(huán)可以使用的范圍包括數(shù)組,Set,Map結(jié)構(gòu),某寫類數(shù)組對(duì)象(arguments, DOM NodeList對(duì)象)以及字符串。

var arr = ["水星","金星","地球","火星"];
for(var s of arr){
  console.log(s);   //"水星"   "金星"   "地球"   "火星"
}

F--------import 和 export
ES6標(biāo)準(zhǔn)中,JavaScript 原生支持模塊(module)了,這種將JS代碼分割成不同功能的小塊進(jìn)行模塊化,將不同功能的代碼分別寫在不同的文件中,各模塊只需要導(dǎo)出公共接口部分,然后通過(guò)模塊的導(dǎo)入方式可以在其他地方使用。

export 用于對(duì)外輸出本模塊(一個(gè)文件可以看做是一個(gè)模塊)的變量接口
import 用于在一個(gè)模塊中加載另外一個(gè)含有export接口的模塊
import和export命令只能在模塊的頂部,不能在代碼塊之中。

G-----Promise對(duì)象
Promise是異步編程的一種解決方案,將異步操作以同步操作的流程表達(dá)出來(lái),避免了層層嵌套的回調(diào)函數(shù)。

它有三種狀態(tài),分別是 pending-進(jìn)行中,resolved-已完成,rejected-已失敗

Promise構(gòu)造函數(shù)接收一個(gè)參數(shù),這個(gè)參數(shù)是函數(shù),并且這個(gè)函數(shù)傳入兩個(gè)參數(shù),分別是 resolve 和 reject,分別是異步操作執(zhí)行成功后的回調(diào)函數(shù),和異步操作執(zhí)行失敗后的回調(diào)函數(shù)。(按照標(biāo)準(zhǔn)來(lái)講,resolve是將Promise的狀態(tài)置為fullfiled,reject是將Promise的狀態(tài)置為rejected。)
所以我們用Promise的時(shí)候一般是包在一個(gè)函數(shù)中,在需要的時(shí)候去運(yùn)行這個(gè)函數(shù),如:

function runAsync(){
  var p = new Promise((resolve,reject)=>{
      setTimeout(function(){
        console.log('執(zhí)行完成');
        resolve('隨便什么數(shù)據(jù)')
},2000)
})  
return p;  
}

runAsync();

在我們包裝好的函數(shù)最后,會(huì)return出Promise對(duì)象,也就是說(shuō),執(zhí)行這個(gè)函數(shù)我們得到了一個(gè)Promise對(duì)象。
Promise對(duì)象上有then 和 catch方法。

runAsync().then(function(data){
  console.log(data)    //'隨便什么數(shù)據(jù)'
  //后面可以用傳過(guò)來(lái)的數(shù)據(jù)做些其他操作
  //......
})

在runAsync()的返回上直接調(diào)用then方法,then接收一個(gè)參數(shù),是函數(shù),并且會(huì)拿到我們?cè)趓unAsync中調(diào)用resolve時(shí)傳的的參數(shù)。

這時(shí)候你應(yīng)該有所領(lǐng)悟了,原來(lái)then里面的函數(shù)就跟我們平時(shí)的回調(diào)函數(shù)一個(gè)意思,能夠在runAsync這個(gè)異步任務(wù)執(zhí)行完成之后被執(zhí)行。這就是Promise的作用了,簡(jiǎn)單來(lái)講,就是能把原來(lái)的回調(diào)寫法分離出來(lái),在異步操作執(zhí)行完后,用鏈?zhǔn)秸{(diào)用的方式執(zhí)行回調(diào)函數(shù)。

而Promise的優(yōu)勢(shì)在于,可以在then方法中繼續(xù)寫Promise對(duì)象并返回,然后繼續(xù)調(diào)用then來(lái)進(jìn)行回調(diào)操作。

鏈?zhǔn)讲僮鞯挠梅?/h6>

從表面上看,Promise只是能夠簡(jiǎn)化層層回調(diào)的寫法,而實(shí)質(zhì)上,Promise的精髓是“狀態(tài)”,用維護(hù)狀態(tài)、傳遞狀態(tài)的方式來(lái)使得回調(diào)函數(shù)能夠及時(shí)調(diào)用,它比傳遞callback函數(shù)要簡(jiǎn)單、靈活的多。所以使用Promise的正確場(chǎng)景是這樣的:


function runAsync1(){
    var p = new Promise(function(resolve, reject){
        //做一些異步操作
        setTimeout(function(){
            console.log('異步執(zhí)行1完成');
            resolve('隨便什么數(shù)據(jù)1');
        }, 2000);
    });
    return p;            
}
function runAsync2(){
    var p = new Promise(function(resolve, reject){
        //做一些異步操作
        setTimeout(function(){
            console.log('異步執(zhí)行2完成');
            resolve('隨便什么數(shù)據(jù)2');
        }, 2000);
    });
    return p;            
}
function runAsync3(){
    var p = new Promise(function(resolve, reject){
        //做一些異步操作
        setTimeout(function(){
            console.log('異步執(zhí)行3完成');
            resolve('隨便什么數(shù)據(jù)3');
        }, 2000);
    });
    return p;            
}

runAsync1()
.then(function(data){
  console.log(data);
  return runAsync2()
})
.then(function(data){
  console.log(data);
  return runAsync3()
})
.then(function(data){
  console.log(data)
})

//2秒后打印
"異步執(zhí)行1完成"
"隨便什么數(shù)據(jù)1"

//又2秒后打印
"異步執(zhí)行2完成"
"隨便什么數(shù)據(jù)2"

//又2秒后打印
"異步執(zhí)行3完成"
"隨便什么數(shù)據(jù)3"

在then方法中,可以直接return 數(shù)據(jù),而不是Promise對(duì)象,在后邊的then中就可以接受到數(shù)據(jù)了。

runAsync1()
.then(function(data){
  console.log(data);
  return runAsync2()
})
.then(function(data){
  console.log(data);
  return "直接返回?cái)?shù)據(jù)"
})
.then(function(data){
  console.log(data)
})

//2秒后打印
"異步執(zhí)行1完成"
"隨便什么數(shù)據(jù)1"

//又2秒后打印
"異步執(zhí)行2完成"
"隨便什么數(shù)據(jù)2"
"直接返回?cái)?shù)據(jù)"

reject的用法

我們前面的例子都是只有“執(zhí)行成功”的回調(diào),還沒(méi)有“失敗”的情況
reject就是把Promise的狀態(tài)設(shè)置為 rejected,這樣我們?cè)趖hen中就能捕捉到,然后執(zhí)行失敗情況的回調(diào)。

function getNumber(){
  return new Promise((resolve,reject)=>{
    setTimeout(function(){
      var num = Math.ceil(Math.random()*10) //生成0-10之間的整數(shù)
      if(num<=5){
        resolve(num)
      }else{
        reject('數(shù)字太大了')
      }
    },2000)
  })
}

getNumber()
.then(
  function(data){
    console.log('resolved')
    console.log(data);
  },
  function(reason,data){
    console.log('rejected')
    console.log(reason)
  }
)

getNumber函數(shù)用來(lái)異步獲取一個(gè)數(shù)字,2秒后執(zhí)行完成,如果數(shù)字小于等于5,我們認(rèn)為是“成功”了,調(diào)用resolve修改Promise的狀態(tài)。否則我們認(rèn)為是“失敗”了,調(diào)用reject并傳遞一個(gè)參數(shù),作為失敗的原因。 運(yùn)行g(shù)etNumber并且在then中傳了兩個(gè)參數(shù),then方法可以接受兩個(gè)參數(shù),第一個(gè)對(duì)應(yīng)resolve的回調(diào),第二個(gè)對(duì)應(yīng)reject的回調(diào)。所以我們能夠分別拿到他們傳過(guò)來(lái)的數(shù)據(jù)

catch 的用法

我們知道 Promise除了有一個(gè)then方法,還有一個(gè)catch方法,它是干什么的呢?
其實(shí)它跟then方法的第二個(gè)參數(shù)的作用是一樣的。用來(lái)指定reject的回調(diào)。
它的用法:

getNumber()
.then(
  function(data){
    console.log('resolved')
    console.log(data);
  }
)
.catch(
  function(reason){
    console.log('rejected')
    console.log(reason)
  }
)

效果和寫在then的第二個(gè)參數(shù)里面一樣。不過(guò)它還有另外一個(gè)作用,在執(zhí)行resolve的回調(diào)(也就是then方法的第一個(gè)參數(shù))時(shí),如果拋出異常(代碼出錯(cuò)了),那么并不會(huì)報(bào)錯(cuò)卡死JS,而是會(huì)進(jìn)入到catch方法中


getNumber()
.then(function(data){
    console.log('resolved');
    console.log(data);
    console.log(somedata); //此處的somedata未定義
})
.catch(function(reason){
    console.log('rejected');
    console.log(reason);

在 resolve的會(huì)調(diào)用,我們console.log(somedata),而somedata是未定義的,如果不用Promise,代碼運(yùn)行到這里,就直接在控制臺(tái)報(bào)錯(cuò)了,不往下運(yùn)行了。但是在這里會(huì)得到這樣的結(jié)果:

image

也就是說(shuō)進(jìn)到catch方法里面去了,而且把錯(cuò)誤原因傳到了reason參數(shù)中。即便是有錯(cuò)誤的代碼也不會(huì)報(bào)錯(cuò)了,這與我們的try/catch語(yǔ)句有相同的功能。

all方法

Promise.all()提供了并行執(zhí)行異步操作的能力。并且在所有異步操作執(zhí)行完成以后,才執(zhí)行回調(diào)。

Promise
.all([runAsync1(), runAsync2(), runAsync3()])
.then(function(results){
    console.log(results);
});

Promise.all(),接收一個(gè)數(shù)組作為參數(shù),數(shù)組里的元素最終都返回一個(gè)Promise對(duì)象。這樣,三個(gè)異步的操作就并行執(zhí)行了,等到他們都執(zhí)行完以后,才會(huì)進(jìn)入到then里邊,那么三個(gè)異步操作執(zhí)行以后返回的數(shù)據(jù)哪里去了呢? all會(huì)把所有異步操作的結(jié)果放進(jìn)一個(gè)數(shù)組,并且把數(shù)組傳遞給then,就是上邊的results.
所以上邊代碼輸出的結(jié)果是:

image

有了all方法,你就可以并行執(zhí)行多個(gè)異步操作,并且在一個(gè)回調(diào)中處理所有的返回?cái)?shù)據(jù),有一個(gè)場(chǎng)景很適合用這個(gè)方法。
比如一些游戲類的素材比較多的應(yīng)用,打開(kāi)網(wǎng)頁(yè)時(shí)預(yù)先加載,需要用到各種資源,比如圖片,flash,以及各種靜態(tài)文件。所有都加載完以后,再進(jìn)行頁(yè)面的初始化。

race的用法

Promise.all方法,實(shí)際上是誰(shuí)跑的慢,以誰(shuí)為準(zhǔn)執(zhí)行回調(diào)。那么相對(duì)的就有另外一個(gè)方法,以誰(shuí)跑的塊,以誰(shuí)為準(zhǔn)執(zhí)行回調(diào)。
就是race方法,這個(gè)詞本來(lái)就是賽跑的意思。race的用法與all一樣。我們修改下上邊的計(jì)時(shí)器的時(shí)間:

function runAsync1(){
    var p = new Promise(function(resolve, reject){
        //做一些異步操作
        setTimeout(function(){
            console.log('異步執(zhí)行1完成');
            resolve('隨便什么數(shù)據(jù)1');
        }, 1000);
    });
    return p;            
}
function runAsync2(){
    var p = new Promise(function(resolve, reject){
        //做一些異步操作
        setTimeout(function(){
            console.log('異步執(zhí)行2完成');
            resolve('隨便什么數(shù)據(jù)2');
        }, 3000);
    });
    return p;            
}
function runAsync3(){
    var p = new Promise(function(resolve, reject){
        //做一些異步操作
        setTimeout(function(){
            console.log('異步執(zhí)行3完成');
            resolve('隨便什么數(shù)據(jù)3');
        }, 2000);
    });
    return p;            
}
Promise
.race(runAsync1(),runAsync2(),runAsync3())
.then(function(results){
  console.log(results);
})

//1秒后打印
"異步執(zhí)行1完成"
//再過(guò)1秒后打印
"異步執(zhí)行3完成"
//再過(guò)2秒后打印
"異步執(zhí)行2完成"

這個(gè)race有什么用呢?使用場(chǎng)景還是很多的,比如我們可以用race給某個(gè)異步請(qǐng)求設(shè)置超時(shí)時(shí)間,并且在超時(shí)后執(zhí)行相應(yīng)的操作,代碼如下:

//請(qǐng)求某個(gè)圖片資源
function requestImg(){
    var p = new Promise(function(resolve, reject){
        var img = new Image();
        img.onload = function(){
            resolve(img);
        }
        img.src = 'xxxxxx';
    });
    return p;
}

//延時(shí)函數(shù),用于給請(qǐng)求計(jì)時(shí)
function timeout(){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            reject('圖片請(qǐng)求超時(shí)');
        }, 5000);
    });
    return p;
}

Promise
.race([requestImg(), timeout()])
.then(function(results){
    console.log(results);
})
.catch(function(reason){
    console.log(reason);
});

requestImg函數(shù)會(huì)異步請(qǐng)求一張圖片,我把地址寫為"xxxxxx",所以肯定是無(wú)法成功請(qǐng)求到的。timeout函數(shù)是一個(gè)延時(shí)5秒的異步操作。我們把這兩個(gè)返回Promise對(duì)象的函數(shù)放進(jìn)race,于是他倆就會(huì)賽跑,如果5秒之內(nèi)圖片請(qǐng)求成功了,那么遍進(jìn)入then方法,執(zhí)行正常的流程。如果5秒鐘圖片還未成功返回,那么timeout就跑贏了,則進(jìn)入catch,報(bào)出“圖片請(qǐng)求超時(shí)”的信息。運(yùn)行結(jié)果如下:

image

H-------解構(gòu)賦值
ES6允許按照一定的模式,從數(shù)組和對(duì)象中提取值,對(duì)變量進(jìn)行賦值。這杯成為解構(gòu)賦值。

  • 數(shù)組的解構(gòu)賦值;
    數(shù)組中的值會(huì)被自動(dòng)解析到對(duì)應(yīng)接收該值的變量中,數(shù)組的解構(gòu)賦值要一一對(duì)應(yīng),如果有對(duì)應(yīng)不上的,就是undefined
let [a,b,c,d] = [1,2,3];
console.log(a);  1
console.log(b);  2
console.log(c);  3
console.log(d);  undefined

-對(duì)象的結(jié)構(gòu)賦值
對(duì)象的結(jié)構(gòu)賦值與數(shù)組有一個(gè)重要的不同,數(shù)組的元素是按照次序排列的,變量的取值由它的位置決定。而對(duì)象的屬性是沒(méi)有次序的,變量必須與屬性同名,才能取到正確的值。

var person = {name:'小明',age:20,sex:'男'};
var {name,age,sex} = person;
console.log(name);
console.log(age);
console.log(sex);

//如果想要變量名和屬性名不同,可以這樣寫

let {name:foo,age:baz} = {name:'小明',age:20,sex:'男'}
console.log(foo);  '小明'
console.log(baz);  20

I--------set數(shù)據(jù)結(jié)構(gòu)
Set的數(shù)據(jù)結(jié)構(gòu),類似數(shù)組,所有的數(shù)據(jù)都是唯一的,沒(méi)有重復(fù)的值,它本身是一個(gè)構(gòu)造函數(shù);

var arr = [1,2,2,3,4,4];
var s = new Set(arr);
console.log(s);   //{1,2,3,4}

屬性和方法:

size 數(shù)據(jù)的長(zhǎng)度
add() 添加某個(gè)值,返回 Set 結(jié)構(gòu)本身。
delete() 刪除某個(gè)值,返回一個(gè)布爾值,表示刪除是否成功。
has() 查找某條數(shù)據(jù),返回一個(gè)布爾值。
clear() 清除所有成員,沒(méi)有返回值。

console.log(s.size);  //4
console.log(s.add(5));  //{1,2,3,4,5}
console.log(s.delete(1));  //true
console.log(s.has(2));   //true
s.clear();


2.函數(shù)節(jié)流和函數(shù)防抖

前提條件:一個(gè)函數(shù)在短時(shí)間內(nèi)被多次執(zhí)行。
但是這種短時(shí)間內(nèi)多次重復(fù)執(zhí)行,通常情況下是沒(méi)有必要的。我們需要優(yōu)化這種高頻執(zhí)行的JS。

優(yōu)化的方法就是 函數(shù)節(jié)流 和 函數(shù)防抖
函數(shù)節(jié)流:
讓函數(shù)在特定的時(shí)間之內(nèi)只執(zhí)行一次。特定時(shí)間內(nèi)如果多次觸發(fā)函數(shù),只有一次生效。
應(yīng)用場(chǎng)景 :下拉加載。

函數(shù)防抖:
頻繁觸發(fā)某一事件,其中兩次觸發(fā)間隔時(shí)間大于等于特定時(shí)間,才執(zhí)行代碼一次。如果在特定時(shí)間內(nèi),又觸發(fā)了一次事件,那么重新開(kāi)始計(jì)算函數(shù)執(zhí)行時(shí)間。簡(jiǎn)單的說(shuō),一個(gè)動(dòng)作連續(xù)觸發(fā),只執(zhí)行最后一次。
應(yīng)用場(chǎng)景:搜索框搜索輸入,比如用戶輸入字母間隔超過(guò)2秒再發(fā)送請(qǐng)求。

函數(shù)節(jié)流的例子
<button id="btn">大招<button>

<script>
  var cd = false;  //cd用來(lái)記錄大招是否冷卻,默認(rèn)是沒(méi)有冷卻

  function attack(){
        console.log('發(fā)起攻擊')
  }
  var button = document.queryselector('#btn');
   button.onclick = function(){
      if(cd){
           //點(diǎn)擊大招按鈕的時(shí)候判斷一下,如果大招冷卻,給出提示。
           console.log('大招冷卻,無(wú)法攻擊!')
      }else{
            //如果大招沒(méi)有冷卻,發(fā)起攻擊
            attack();
            cd = true;     //發(fā)起攻擊后,技能狀態(tài)調(diào)整為冷卻
            var timer = setTimeout(function(){
                cd = false;  //3秒鐘后,技能冷卻結(jié)束
            },3000)
      }
  }
</script>

//函數(shù)防抖例子
//公交關(guān)門
function close(){
  console.log('關(guān)門');
}

var button = document.querySelector('#btn');

var timer = null ;  //定時(shí)器一開(kāi)始是空的
button.onclick = function(){
  //如果點(diǎn)擊了按鈕,發(fā)現(xiàn)上一個(gè)計(jì)時(shí)器還存在,那么就把它清除掉。
  if(timer){
    window.clearTimeout(timer);
  }
  //如果5秒種之內(nèi)沒(méi)有再點(diǎn),就設(shè)置一個(gè)定時(shí)器,并執(zhí)行關(guān)門函數(shù),并且把定時(shí)器清除掉。
    timer = setTimeout(function(){
    close();
    timer= null;
  },5000)                   
}

3.手寫AJAX

var xhr = new XMLHttpRequest();
xhr.open('GET','/xxx',true);

xhr.onreadystatechange = function(){
  if(xhr.readystate === 4){
    console.log('請(qǐng)求完成')
    if(xhr.status>=200&&xhr.status<300||xhr.status === 304){
          console.log('請(qǐng)求成功')
    }else{
          console.log('請(qǐng)求失敗')
    }
  }
}

xhr.send();

4.這段代碼里的this是什么?

主要是看函數(shù)怎么調(diào)用的!

fn()   // this指向 window/global
obj.fn()  //  this 指向 obj
fn.call(xx)  //this 指向 xx
fn.bind(xx)  //this指向 xx
new Fn()   //this指向 新的對(duì)象
fn  = ()=>{}   //this 指向外邊的this

判斷的通用方法
function fn(a,b){
    console.log(this)
}

fn(1,2);//這句等價(jià)于下邊
fn.call(undefined,1,2);
fn.apply(undefined,[1,2]);
注意:
在嚴(yán)格模式下 'use strict'  ,此時(shí) fn里的this,就是call和apply的第一個(gè)參數(shù),也就是 undefined
在非嚴(yán)格模式下,也就是不用 'use strict' ,call和apply里的第一個(gè)參數(shù)如果是undefined或者是null,那么this會(huì)默認(rèn)替換為 window

在看一個(gè)例子:
var  obj = {
    fn:function(a,b){
        console.log(this)
    },
    child:{
        fn2:function(){
            console.log(this)
        }
    }
}
obj.fn(1,2); 等價(jià)于  
obj.fn.call(obj,1,2);
obj.fn.apply(obj,[1,2]);  //所以this是obj

obj.child.fn2();等價(jià)于
obj.child.fn2.call(obj.child);
obj.child.fn2.apply(obj.child);   //所以this是 obj.child

5. 閉包和立即執(zhí)行函數(shù)是什么?

  • 閉包:
    就是能讀取其他函數(shù)內(nèi)部變量的函數(shù),在JS中,只有函數(shù)內(nèi)部的子函數(shù),能夠讀取函數(shù)內(nèi)部的局部變量。所以閉包可以理解為,定義在一個(gè)函數(shù)內(nèi)部的函數(shù)。

  • 閉包的缺點(diǎn):
    讓變量始終保持在內(nèi)容中,內(nèi)存消耗會(huì)很大。

  • 什么是詞法作用域?
    詞法作用域就是函數(shù)創(chuàng)建的時(shí)候所在的作用域。
    所以,函數(shù)在執(zhí)行的時(shí)候,使用的變量,會(huì)先從自己內(nèi)部去找,如果自己內(nèi)部沒(méi)有定義,就到自己的詞法作用域(scopes)去找。

function car(){
  var speed = 0;
  function fn(){
      speed++;
      console.log(speed);
  }
  return fn;
}

var speedUp = car();
speedUp();  //1
speedUp();  //2

// 如果在car函數(shù)里,再聲明一個(gè)函數(shù)fn,并返回fn,fn()內(nèi)部又使用了car聲明的變量,然后調(diào)用car函數(shù)并賦值給 一個(gè)全局變量speedUp
// 因?yàn)閟peedUp 屬于全局作用域,而全局作用域在頁(yè)面沒(méi)有關(guān)閉的時(shí)候,是不會(huì)銷毀的。所以也就導(dǎo)致了fn函數(shù)不會(huì)被銷毀
// 執(zhí)行speedUp就相當(dāng)于 執(zhí)行fn(),而fn()函數(shù)內(nèi)部有用到局部變量speed,按照作用域鏈來(lái)尋找,fn()函數(shù)內(nèi)沒(méi)有聲明
// 繼續(xù)往上一級(jí)找,fn()函數(shù)是聲明在car()內(nèi)的,而speed是在car內(nèi)聲明的
// 所以第一次執(zhí)行 speedUp() 的時(shí)候,結(jié)果是1;執(zhí)行之后,speed是沒(méi)有被銷毀的
// 再次執(zhí)行就是2

//再看個(gè)例子
var fnArr = [];
for(var i=0;i<10;i++){
  fnArr[i] = function(){
      return i;
  }
}

console.log(fnArr[1]);   //  這個(gè)時(shí)候數(shù)組里存的都是函數(shù) function(){ return i}
console.log(fnArr[2]())   //10
console.log(fnArr[5]())   //10
console.log(fnArr[7]())   //10

//為什么會(huì)輸出10呢?  分析一下
fnArr[2]本身是一個(gè)函數(shù),加括號(hào),表示執(zhí)行它指向的那個(gè)函數(shù)。
函數(shù)內(nèi)部 return i,用到了i這個(gè)變量,所以會(huì)從自己內(nèi)部去找這個(gè)變量,發(fā)現(xiàn)沒(méi)有聲明i,然后在去函數(shù)聲明的作用域去找,函數(shù)是在for里聲明的,但是for本身并不是函數(shù),所以function函數(shù)聲明的作用域是全局作用域。而全局作用域里,for循環(huán)完以后,i已經(jīng)變?yōu)?0了,所以fnArr[i]()會(huì)輸出10.

那么如果我們想輸出 0,1,2,3,4,5,....怎么辦呢?
用立即執(zhí)行函數(shù)
var fnArr = [];
for(var i=0;i<10;i++){
  fnArr[i] =( function(j){
      return j;
  })(i)
}

console.log(fnArr[0])   //0
console.log(fnArr[5])   //5
console.log(fnArr[7])   //7

再看個(gè)例子
for(var i=0;i<5;i++){
  setTimeout(function(){
    console.log(i)
  },2000)
}
//這里2秒后會(huì)輸出5個(gè)5

for(var i=0;i<5;i++){
  (function(j){
    setTimeout(function(){
    console.log(j)
  },2000)
  })(i)
}
//這里2秒后會(huì)輸出 0,1,2,3,4

再熟悉一下作用域鏈

var x = 10
function foo(){
  console.log(x);
}
foo();   //10  

function bar(){
  var x = 20;
  foo();
}
bar();  //10
執(zhí)行bar就是執(zhí)行foo,foo輸出x,先從自己內(nèi)部去找
發(fā)現(xiàn)沒(méi)有聲明x,然后到foo聲明的作用域去找
foo是聲明在全局作用域的,而全做作用域下,有聲明變量x,所以輸出10

//下邊之所以輸出30 是因?yàn)閒oo是在bar內(nèi)部聲明的。
 var x = 10;
 bar();  //30

 function bar(){
 var x = 30;
  function foo(){
        console.log(x); 
   }
    foo();
}

6.什么是JSONP,什么是CORS,什么是跨域?

什么是跨域?
--------是指不同源的網(wǎng)站之間的訪問(wèn)。

同源策略
---------提到跨域,就不得不說(shuō)一下同源策略,同源策略是NetScape提出的瀏覽器的一種安全策略,也就是指a網(wǎng)站,不能隨便讀取b網(wǎng)站的內(nèi)容。

所謂同源:
--------指的是,協(xié)議、域名、端口、相同

最常見(jiàn)的應(yīng)用是,當(dāng)我們調(diào)用ajax接口時(shí),如果不設(shè)置跨域,瀏覽器會(huì)報(bào)錯(cuò)。這證明使用XMLHttpRequest對(duì)象不能發(fā)送跨域請(qǐng)求。

有疑惑的小伙伴肯定會(huì)問(wèn),那我用a標(biāo)簽請(qǐng)求其他網(wǎng)站,是不是就是跨域了呢?
這里要明白跨域是指在當(dāng)前域下調(diào)用其他域下的東西,而鏈接則是直接跳轉(zhuǎn)到對(duì)方的域下了,跟你之前的域名毫無(wú)關(guān)系。

如果想從你的網(wǎng)站跨域去另一個(gè)網(wǎng)站拿到一些資源,有一個(gè)前提是另外一個(gè)網(wǎng)站的服務(wù)器支持跨域,這個(gè)需要在服務(wù)器端設(shè)置,不同服務(wù)器的設(shè)置方法不一樣,我們?cè)谶@里不多說(shuō),就看客戶端跨域如何解決?

解決跨域最常見(jiàn)的方式是 jsonp。

先弄清楚 json和jsonp的區(qū)別
json (JavaScript Object Notation)是一種輕量級(jí)的數(shù)據(jù)交換格式,用來(lái)在瀏覽器和服務(wù)器之間交換數(shù)據(jù)。
jsonp (JSON With Padding) 就是打包在函數(shù)中調(diào)動(dòng)的json,或者包裹的json
json 是一種數(shù)據(jù)格式;jsonp是一種數(shù)據(jù)調(diào)用方式

//json
{
  "name":"小明"
}

//jsonp
callback({
 "name":"小明"
})

jsonp是jquery給我們封裝的一個(gè)方法,使用方法如下:

$.ajax({
  ulr:'xxx',
  type:'GET',
  dataType:'jsonp',
  success:function(data){
    console.log(data)
  }
})

上邊的代碼是當(dāng)我們調(diào)用外部的一個(gè)接口時(shí),通過(guò)設(shè)置jquery里的ajax方法里的datatype屬性的值為jsonp,就可以成功調(diào)用這個(gè)接口了。

首先我們需要明白,在頁(yè)面上直接發(fā)起一個(gè)跨域的ajax請(qǐng)求是不可以的,但是,在頁(yè)面上引入不同域上的js腳本卻是可以的,就像你可以在自己的頁(yè)面上使用<img src=""> 標(biāo)簽來(lái)隨意顯示某個(gè)域上的圖片一樣。
看下怎么利用<script src="">來(lái)完成一個(gè)跨域請(qǐng)求。

 <div class="container">
        <ul class="news"></ul>
        <button class="show">show news</button>
 </div>

 $('.show').addEventListener('click', function () {
            var script = document.createElement('script');
            script.src = 'http://127.0.0.1:8080/getNews?callback=appendHtml';
            document.head.appendChild(script);
            // document.head.removeChild(script);  //為了保持代碼的整潔,直接刪除了新增的標(biāo)簽,但是標(biāo)簽的作用已經(jīng)起到了
        })

        function appendHtml(news) {
            var html = '';
            for (var i = 0; i < news.length; i++) {
                html += '<li>' + news[i] + '</li>';
            }
            console.log(html);
            $('.news').innerHTML = html;
        }

jsonp跨域的原理:

  1. 使用script標(biāo)簽發(fā)送請(qǐng)求,這個(gè)標(biāo)簽支持跨域訪問(wèn)
  2. 在script標(biāo)簽里,給服務(wù)器傳遞一個(gè)callback
  3. callback的值對(duì)應(yīng)到頁(yè)面上定義的一個(gè)全局函數(shù)(為什么是全局函數(shù)呢?因?yàn)榉?wù)器接收到callback函數(shù)后,會(huì)返回頁(yè)面中的script去尋找,如果不寫到全局作用域中,根本找不到)。
    4.服務(wù)器端返回的是一個(gè)函數(shù)的調(diào)用,調(diào)用的時(shí)候會(huì)把返回?cái)?shù)據(jù)作為參數(shù)包裹在這個(gè)函數(shù)里。

缺點(diǎn): jsonp只能解決get方式的跨域。如果傳輸?shù)臄?shù)據(jù)比較大,這種方式就不行了。

cors跨域

cors (cross origin resource sharing) 全稱 '跨域資源共享'
相對(duì)于jsonp, cors支持所有類型的HTTP請(qǐng)求,并且實(shí)施起來(lái)非常簡(jiǎn)單。

cors背后的基本思想是,使用自定義的HTTP頭部允許瀏覽器和服務(wù)器互相了解對(duì)方,從而決定請(qǐng)求和響應(yīng)成功與否。

cors原理:
當(dāng)使用XMLHttpRequest發(fā)送請(qǐng)求的時(shí)候,瀏覽器發(fā)現(xiàn)該請(qǐng)求不符合同源策略,會(huì)給該請(qǐng)求加一個(gè)請(qǐng)求頭 origin,后臺(tái)會(huì)進(jìn)行一系列的處理,如果確定接受請(qǐng)求,則在返回結(jié)果中,加入一個(gè)響應(yīng)頭 Access-Control-allow-origin;瀏覽器判斷該響應(yīng)頭中是否包含 origin的值,如果有,則瀏覽器會(huì)處理響應(yīng),我們就拿到數(shù)據(jù)。如果沒(méi)有,則瀏覽器會(huì)直接駁回,我們就拿不到數(shù)據(jù)。

IE10以上支持,現(xiàn)代瀏覽器也都支持cors

JSONP和CORS的區(qū)別?

  1. JSONP只支持GET請(qǐng)求,而CORS支持所有類型的HTTP請(qǐng)求。
  2. 使用CORS,開(kāi)發(fā)者可以使用普通的XMLHttpRequest對(duì)象發(fā)起請(qǐng)求和獲得數(shù)據(jù)。比起JSONP有更好的錯(cuò)誤處理。
    3.JSONP主要被老的瀏覽器支持,它們往往不支持CORS,而絕大多數(shù)的現(xiàn)代瀏覽器都已經(jīng)支持了CORS

7. async/await 怎么用? 怎么捕獲異常?

async是異步的縮寫。而await 可以認(rèn)為是 async wait的縮寫。

async作為一個(gè)關(guān)鍵字放在函數(shù)前面,用來(lái)聲明這個(gè)函數(shù)是一個(gè)異步函數(shù)。既然是異步函數(shù)就意味著該函數(shù)的執(zhí)行不會(huì)阻塞后邊的代碼;
async函數(shù)返回的是一個(gè)Promise對(duì)象。

await用于等待一個(gè)異步方法執(zhí)行完成,await等待的是一個(gè)表達(dá)式,而表達(dá)式的計(jì)算結(jié)果是一個(gè)Promise對(duì)象或者其他值。
(await相當(dāng)于運(yùn)算符,用于組成表達(dá)式,await表達(dá)式的運(yùn)算結(jié)果取決于它等到的東西)
如果等到的不是一個(gè)Promise對(duì)象,那么await表達(dá)式的運(yùn)算結(jié)果就是它等到的東西。
如果它等到的是一個(gè)Promise對(duì)象,await就忙起來(lái)了,它會(huì)阻塞后面的代碼,等待Promise對(duì)象resolve,然后得到resolve的值。作為await表達(dá)式的運(yùn)算結(jié)果。
await只能用到async函數(shù)中。

 function sayHi(name){
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      resolve('Hi'+name);
    },2000)
  })
}

async function test(){
  var result = await sayHi('小明');
  console.log(result);
}

test();

async和 await捕獲異常,需要用到 try/catch的方式:

因?yàn)閍wait后面跟著的是 Promise對(duì)象,當(dāng)有異常的情況下,會(huì)被Promise對(duì)象內(nèi)部的catch捕獲。而await就是一個(gè)then的語(yǔ)法糖,并不會(huì)捕獲異常,那么就需要借助try/catch來(lái)捕獲異常,并進(jìn)行相應(yīng)的邏輯處理。


 function sayHi(name){
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      //生成0-10的隨機(jī)整數(shù)
      var num  = Math.ceil(Math.random()*10);
      if(num>5){
         //如果隨機(jī)數(shù)大于5,就resolve出結(jié)果
         resolve('Hi'+name);
      }else{
        //否則就拋出錯(cuò)誤
        reject('出錯(cuò)了');
      }

    },2000)
  })
}

async function test(){
   try{
     var result = await sayHi('小明');
     console.log(result);
   }catch(error){
     console.log(error)
   }
}

test();

8.如何實(shí)現(xiàn)深拷貝?

關(guān)鍵字:
1、遞歸
2、判斷類型
3、檢查循環(huán)引用(環(huán))
4、不可能拷貝 _proto_                                                                                                                                                                                                            

拷貝分為淺拷貝和深拷貝
在這之前,我們先弄清楚兩個(gè)概念。

  • 基本數(shù)據(jù)類型
Number ,String,Boolean,Null,Undefined 都是基本數(shù)據(jù)類型
基本數(shù)據(jù)類型時(shí)按值訪問(wèn)的,因?yàn)榭梢灾苯硬僮鞅4嬖诒4嬖谧兞恐械膶?shí)際值。
var a = 10;
var b = a;    //b只是保存了a復(fù)制的一個(gè)副本。所以 a和b是互相獨(dú)立的。
console.log(a);   //10
console.log(b);  //10

//a值的改變不會(huì)影響b的值
a = 20;
console.log(a);  //20
console.log(b);  //10

  • 引用數(shù)據(jù)類型
引用數(shù)據(jù)類型也就是對(duì)象類型 Object type.比如 Object,Array,Function,Data等。
JavaScript的引用數(shù)據(jù)類型是保存在堆內(nèi)存中的對(duì)象。
與其他語(yǔ)言不同的是,你不可以直接訪問(wèn)堆內(nèi)存空間中的位置和操作堆內(nèi)存空間。只能操作對(duì)象在棧內(nèi)存中的引用地址。
所以,引用類型數(shù)據(jù)在棧內(nèi)存中保存的是對(duì)象在堆內(nèi)存中的引用地址,通過(guò)這個(gè)引用地址可以快速查找到保存在堆內(nèi)存中的對(duì)象。
var obj1 = new Object();
var obj2 = obj1;
obj2.name = '小明';
console.log(obj1.name);
說(shuō)明這兩個(gè)引用數(shù)據(jù)類型指向了同一個(gè)堆內(nèi)存對(duì)象。obj1賦值給onj2,實(shí)際上這個(gè)堆內(nèi)存對(duì)象在棧內(nèi)存的引用地址復(fù)制了一份給了obj2,但是實(shí)際上他們共同指向了同一個(gè)堆內(nèi)存對(duì)象。
實(shí)際上改變的是堆內(nèi)存對(duì)象。

下面我們來(lái)演示這個(gè)引用數(shù)據(jù)類型賦值過(guò)程:

image

好了,上邊兩個(gè)概念了解清楚以后,我們進(jìn)入正題。

對(duì)象的淺拷貝

對(duì)象淺拷貝比較簡(jiǎn)單,就是將一個(gè)變量賦值給另外一個(gè)變量

var obj1 = {
  name:"小明",
  age:20
}

var obj2 = obj1;
console.log(obj2.age)  //20
obj2經(jīng)過(guò)淺拷貝,擁有了obj1的屬性

封裝淺拷貝方法

var easyCopy = function(oldObj){
      //constructor 屬性應(yīng)用了該對(duì)象的構(gòu)造函數(shù)
      var newObj = oldObj.constructor === Array?[]:{};
      if(typeof oldObj != 'object')  return;
      for(var key in oldObj){
           if(oldObj.hasOwnProperty(key)){
               newObj[key] = oldObj[key]
           }
      }
      return newObj;
}

var obj1 = {
    name:'小明',
    age:20
}

var obj2 = easyCopy(obj1);
console.log(obj2)

淺拷貝存在的問(wèn)題:
我們知道,引用數(shù)據(jù)類型的賦值,其實(shí)是變量a把對(duì)象在堆內(nèi)存中的地址,復(fù)制了一份給變量b,這個(gè)時(shí)候,a和b指向的都是同一個(gè)對(duì)象。通過(guò)a或者b都可以改變堆內(nèi)存中的對(duì)象(比如添加刪除屬性),a和b是可以相互影響的。所以這種淺拷貝會(huì)帶來(lái)BUG。

對(duì)象的深拷貝

深拷貝的目的就是為了實(shí)現(xiàn) 復(fù)制出2個(gè)完全相同,卻又完全獨(dú)立,互不干擾的對(duì)象。

var deepCopy = function(obj){
  var cloneObj = Array.isArray(obj)? []:{};
  if(obj && typeof obj === 'object' ){
    for(var key in obj){
      if(obj.hasOwnProperty(key)){
        cloneObj[key] = typeof obj[key] === 'object'?deepCopy(obj[key]):obj[key]
      }
    }
  }
  return cloneObj;
}

var obj = {
  name:'小紅',
  age:18,
  friend:{
    name:'小明',
    sex:'男',
    study:["數(shù)學(xué)","英文","物理"]
  }
}
var obj2 = deepCopy(obj);
console.log(obj2)

obj.friend = {
  name:'小明明明',
  sex:"女"
}
console.log(obj);
console.log(obj2);

深拷貝的缺點(diǎn):雖然深拷貝能夠避免淺拷貝出現(xiàn)的問(wèn)題。但是卻會(huì)帶來(lái)性能上的問(wèn)題,如果一個(gè)對(duì)象非常復(fù)雜且數(shù)據(jù)龐大,所消耗的性能將會(huì)是很可觀的。

關(guān)于for in

for..in可以用來(lái)遍歷任何一個(gè)對(duì)象,它會(huì)將對(duì)象上的所有的屬性全部遍歷出來(lái),包裹原型鏈上的屬性。所以上邊代碼中需要hasOwnProperty來(lái)判斷這個(gè)屬性到底是不是對(duì)象本身的屬性。因?yàn)閿?shù)組也是對(duì)象,for..in也可以用來(lái)遍歷數(shù)組,但是for in損耗性能較多,所以盡量不要用它來(lái)遍歷數(shù)組。

關(guān)于遞歸

一個(gè)方法,重復(fù)調(diào)用自身的情況,叫做遞歸。
需要注意的是,一定要有一個(gè)條件來(lái)結(jié)束遞歸,否則將會(huì)陷入無(wú)限的循環(huán)。

var num = 0;
function recursion(){
    if(num < 50){
        num++;
        recursion()
    }
}
recursion()

9 如何用正則實(shí)現(xiàn)trim()?

trim()方法去掉字符串兩端的空格,無(wú)論有多少個(gè)空格都會(huì)去掉,字符串中間的空格不會(huì)被去掉。
function  trim(string){
    return string.replace(/^\s+|\s+$/g);
}
var str = '   ab  cd ef   ';
var res = replace(str);
console.log(res);   //'ab  cd ef'

10.不用class如何實(shí)現(xiàn)繼承,用class如何實(shí)現(xiàn)繼承?

//不用class
function Person(name,age){
  this.name = name;
  this.age = age;
}

Person.prototype.printName =function(){
  console.log(this.name);
}

// 繼承屬性
function Mail(name,age,sex){
  Person.call(this,name,age);
  this.sex = sex;
}

// 繼承方法
//Object.create 創(chuàng)建一個(gè)空對(duì)象,空對(duì)象的_proto_指向Person.prototype
Mail.prototype = Object.create(Person.prototype);
Mail.prototype.printSex = function(){
  console.log(this.sex);
}
//因?yàn)镸ail.prototype此時(shí)是指向Person.prototype的,所以Mail.prototype.contructor是指向 Person的。我們需要修改它的指向。
Mail.prototype.contructor = Mail

var john = new Mail('約翰',20,'男');
console.log(john.name);
john.printName();
john.printSex();

//用class
class Person {
  constructor(name,age){
    this.name = name;
    this.age = age;
  }

  sayAge(){
    console.log(`i am ${this.age}`)
  }
}

class Student extends Person{
  constructor(name,age,score){
    supre(name,age);
    this.socre = score;
  }

  sayScore(){
    console.log(`i get ${this.score}`);
  }
}

11.如何實(shí)現(xiàn)數(shù)組去重?

//雙循環(huán)去重
function fn(arr){
  if(Array.isArray(arr)){
    //聲明一個(gè)新的數(shù)組,然后把要遍歷的數(shù)組的第一個(gè)元素放入這個(gè)新數(shù)組
    var newArr = [arr[0]];
    //然后從第二個(gè)元素開(kāi)始遍歷老的數(shù)組,同時(shí)遍歷新數(shù)組
   //把老數(shù)組和新數(shù)組的已有元素做對(duì)比,如果不相等,就放入新數(shù)組。
    for(let i=1;i<arr.length;i++){
      let flag = true;
      for(let j=0;j<newArr.length;j++){
        if(arr[i] === newArr[j]){
          flag = false;
          break;
        }
      }
      if(flag){
        newArr.push(arr[i])
      }
    }
  }
  return newArr
}

var arr = [1,1,2,2,3,4,5,5];
var arr2 = fn(arr);
console.log(arr2)  [1, 2, 3, 4, 5]

//indexOf去重
//兩個(gè)關(guān)鍵點(diǎn) 1、聲明新數(shù)組 2、判斷老數(shù)組里的元素是否在新數(shù)組里,沒(méi)有就push進(jìn)新數(shù)組
function fn(arr){
  if(!Array.isArray(arr)){
       console.log('type error');
        return 
   }
   var newArr = [];
   for(let i=0;i<arr.length;i++){
       if(newArr.indexOf(arr[i]) == -1){
              newArr.push(arr[i])
        }
    }
   return newArr;
}
var arr = [1,1,2,2,3,4,5,5];
var arr2 = fn(arr);
console.log(arr2)   [1, 2, 3, 4, 5]

//set 去重  
//ES6?新增了一個(gè)數(shù)據(jù)類型 set,set的一個(gè)最大的特點(diǎn)就是數(shù)據(jù)不重復(fù)。
//Set()函數(shù)可以接收一個(gè)數(shù)組(或者類數(shù)組對(duì)象)作為參數(shù)來(lái)初始化。
function fn(arr){
  if(!Array.isArray(arr)){
    console.log('type error');
    return;
  }
  return [...new Set(arr)]
}

var arr = [1,1,2,2,3,4,5,5];
var arr2 = fn(arr);
console.log(arr2)   [1, 2, 3, 4, 5]


DOM面試題

1. DOM事件模型是什么?

  1. 事件冒泡-----事件開(kāi)始時(shí),由最具體的元素接收,然后逐級(jí)向上傳播到較為不具體的元素。
  2. 事件捕獲------不太具體的節(jié)點(diǎn)更早的接收事件,而最具體的元素最后接收事件,和事件冒泡相反。
  3. DOM事件流------DOM2級(jí)事件規(guī)定,事件流包括三個(gè)階段,事件捕獲階段,處于目標(biāo)階段,事件冒泡階段。首先發(fā)生的是事件捕獲,為截取事件提供機(jī)會(huì),然后是實(shí)際目標(biāo)接受事件,最后是事件冒泡

Opera、Firefox、Chrome、Safari都支持DOM事件流,IE不支持事件流,只支持事件冒泡

事件處理程序是什么?

響應(yīng)某個(gè)事件的函數(shù),就叫做事件處理程序

  • 事件處理程序分為4種
//HTML事件處理程序
<input type="button" name="btn" value="點(diǎn)擊" onclick="alert('clicked')"

//DOM0級(jí)事件處理程序
<button id="box">點(diǎn)擊</button>
var box = document.querySelector('#box');
box.onclick = function(){
  console.log('DOM0級(jí)事件處理程序')
}
box.onclick = null    //刪除綁定的事件

//DOM2級(jí)事件處理程序
<button id="box">點(diǎn)擊</button>
var box = document.querySelector('#box');
box.addEventListener('click',function(){
  console.log('DOM2級(jí)事件處理程序')
},true)
//刪除綁定的事件
box.removeEventListener('click',函數(shù)名,boolean)

//IE事件處理程序
function showMes(){
  console.log('我是IE事件處理程序')
}

var box = document.getElementById('box');
box.attatchEvent('onclick',showMes);
//刪除綁定的事件處理程序
box.detachEvent('onclick',showMes);

跨瀏覽器事件處理程序

function showMes(){
  console.log('我被點(diǎn)擊了')
}

var box = document.getElementById('box');
var EventUtil = {
    addHandler:function(element,type,handle){
      if(element.addEventListener){
        element.addEventListener(type,handle,false);
      }else if(element.attatchEvent){
        element.attatchEvent('on'+type,handle)
      }else{
        element['on'+type] = handle
      }
    },
    removeHandler:function(element,type,handle){
      if(element.removeEventListener){
        element.removeEventListener(type,handle,false);
      }else if(element.detachEvent){
        element.detachEvent('on'+type,handle);
      }else{
        element['on'+type] = null;
      }
    }
}
EventUtil.addHandler(box,'click',showMes);
EventUtil.removeHandler(box,'click',showMes);

事件對(duì)象

在觸發(fā)DOM上的事件時(shí),都會(huì)產(chǎn)生一個(gè)事件對(duì)象Event
Event 對(duì)象代表事件的狀態(tài),比如事件在其中發(fā)生的元素、鍵盤按鍵的狀態(tài)、鼠標(biāo)的位置、鼠標(biāo)按鈕的狀態(tài)。
兼容DOM的瀏覽器,會(huì)將一個(gè)對(duì)象,傳入事件處理程序中。
例如:

var box = document.querySelector('#box');
box.addEventListener('click',function(event){
  console.log(event.type)   //輸出事件的類型
})

event對(duì)象的屬性
event.target    獲取事件的類型
event.type      獲取事件的目標(biāo)(就是時(shí)間綁定到哪個(gè)元素上了)
...等等

event對(duì)象的屬性
event.stopPropagation :
不再派發(fā)事件。就是終于事件在傳播過(guò)程的捕獲,目標(biāo)處理,冒泡階段的傳播,事件不再被派到其他節(jié)點(diǎn)。

event.preventDefault :
通知瀏覽器不要執(zhí)行與事件相關(guān)的默認(rèn)動(dòng)作。比如a標(biāo)簽

IE事件對(duì)象 
window.event
常見(jiàn)的屬性
window.event.type   相當(dāng)于 event.type
window.event.srcElement 相當(dāng)與 event.target
window.event.cancleBubble 相當(dāng)于  event.stopPropagation()
window.event.returnValue   相當(dāng)于 event.preventDefault()

2.移動(dòng)端的觸摸事件了解嗎?

touch 觸摸事件,有4種之分
1. touch start   手指觸摸到屏幕會(huì)出發(fā)
2. touch move  當(dāng)手指在屏幕上移動(dòng)時(shí)會(huì)觸發(fā)
3. touch end    當(dāng)手指離開(kāi)屏幕時(shí)會(huì)觸發(fā)
4. touch cancel  可由系統(tǒng)進(jìn)行的觸發(fā),比如手指觸摸屏幕的時(shí)候,突然alert了一下,則可以觸發(fā)該事件。

tap事件
觸碰事件,一般用于代替click
tap:手指碰一下屏幕會(huì)觸發(fā)
longTap :手指長(zhǎng)按屏幕會(huì)觸發(fā)
singleTap:手指碰一下屏幕會(huì)觸發(fā)
doubleTap:手指雙擊屏幕會(huì)觸發(fā)

swipe事件
滑動(dòng)事件
swipe:手指在屏幕上滑動(dòng)時(shí)觸發(fā)
swipeLeft:手指在屏幕上向左滑動(dòng)時(shí)會(huì)觸發(fā)
swipeRight:手指在屏幕上向右滑動(dòng)時(shí)會(huì)觸發(fā)
swipeUp:手指在屏幕上向上滑動(dòng)時(shí)會(huì)觸發(fā)
swipeDown:手指在屏幕上向下滑動(dòng)時(shí)會(huì)觸發(fā)

模擬swipe事件:思路 記錄兩次touchmove的位置差,如果后一次在前一次的右邊,說(shuō)明向右滑了。

3.事件委托(也叫事件代理)是什么?

  • 事件委托就是利用事件冒泡,只指定一個(gè)事件處理程序,就可以管理某一類型的所有事件。

  • 舉個(gè)取快遞的例子:
    有三個(gè)同事預(yù)計(jì)會(huì)在周一收到快遞。為簽收快遞,有兩種辦法:一是三個(gè)人在公司門口等快遞;二是委托給前臺(tái)MM代為簽收。
    前臺(tái)MM收到快遞后,她會(huì)判斷收件人是誰(shuí),然后按照收件人的要求簽收,甚至代為付款。
    這種方案還有一個(gè)優(yōu)勢(shì),那就是即使公司里來(lái)了新員工(不管多少),前臺(tái)MM也會(huì)在收到寄給新員工的快遞后核實(shí)并代為簽收。
    這個(gè)例子包含2層意思
    1.現(xiàn)在委托前臺(tái)的同事,是可以代為簽收的(即程序中,現(xiàn)有的DOM節(jié)點(diǎn)是有事件的)
    2.新員工也是可以被前臺(tái)代為簽收的(即程序中,新添加的DOM節(jié)點(diǎn)也是有事件的)。

<div class="container">
  <div class="box">box1</div>
  <div class="box">box2</div>
</div>
 <button class="add">add</button>

var con = document.querySelector('.container');
var box = document.querySelectorAll('.box');
var addBtn = document.querySelector('.add');

//給每個(gè)box都綁定一個(gè)事件
// box.forEach(function(node){
//   node.onclick = function(){
//     console.log(this.innerText);
//   }
// })

//用父級(jí)div做事件代理
con.onclick = function(e){
  if(e.target.classList.contains('box')){
    console.log(e.target.innerText)
  }
}

//有了事件代理以后,哪怕新增的box,也會(huì)被綁定事件。
var i = 4;
addBtn.onclick =function(){
  var box = document.createElement('div');
  box.classList.add('box');
  box.innerText = 'box' + i++;
  con.appendChild(box);
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容