js 高頻面試題(最新)

1、深淺拷貝

(1) 定義

淺拷貝: 將原對(duì)象或原數(shù)組的引用直接賦給新對(duì)象读整,新數(shù)組蚀苛,新對(duì)象/數(shù)組只是原對(duì)象的一個(gè)引用

深拷貝: 創(chuàng)建一個(gè)新的對(duì)象和數(shù)組统阿,將原對(duì)象的各項(xiàng)屬性的“值”(數(shù)組的所有元素)拷貝過(guò)來(lái),是“值”而不是“引用”

(2) 淺拷貝

下面這段代碼就是淺拷貝姥卢,有時(shí)候我們只是想備份數(shù)組卷要,但是只是簡(jiǎn)單讓它賦給一個(gè)變量,改變其中一個(gè)独榴,另外一個(gè)就緊跟著改變僧叉,但很多時(shí)候這不是我們想要的

var obj = {
    name:'wsscat',
    age:0
}
var obj2 = obj;
obj2['c'] = 5;
console.log(obj);//Object {name: "wsscat", age: 0, c: 5}
console.log(obj2);////Object {name: "wsscat", age: 0, c: 5}

var arr1 = [1,2,3,4];
var arr2 = arr1;
arr2.push(5);
console.log(arr1); // [1,2,3,4,5] 
console.log(arr2); // [1,2,3,4,5] 

(3) 深拷貝(只拷貝第一層)

拷貝數(shù)組

// 使用slice實(shí)現(xiàn)
var arr = ['wsscat', 'autumns', 'winds'];
var arrCopy = arr.slice(0);
arrCopy[0] = 'tacssw'
console.log(arr)//['wsscat', 'autumns', 'winds']
console.log(arrCopy)//['tacssw', 'autumns', 'winds']

// 使用concat實(shí)現(xiàn)
var arr = ['wsscat', 'autumns', 'winds'];
var arrCopy = arr.concat();
arrCopy[0] = 'tacssw'
console.log(arr)//['wsscat', 'autumns', 'winds']
console.log(arrCopy)//['tacssw', 'autumns', 'winds']

// 使用擴(kuò)展運(yùn)算符...
var arr1 = [1,2,3,4];
var arr2 = [...arr1];

拷貝對(duì)象

// 遍歷屬性并賦給新對(duì)象
var obj = {
    name:'wsscat',
    age:0
}

var obj2 = new Object();
obj2.name = obj.name;
obj2.age = obj.age

obj.name = 'autumns';
console.log(obj);//Object {name: "autumns", age: 0}
console.log(obj2);//Object {name: "wsscat", age: 0}


// es6的Object.assign方法
// Object.assign:用于對(duì)象的合并,將源對(duì)象(source)的所有可枚舉屬性棺榔,復(fù)制到目標(biāo)對(duì)象(target)瓶堕,并返回合并后的target
var obj = {
  name: '彭湖灣',
  job: '學(xué)生'
}
var copyObj = Object.assign({}, obj);


// 擴(kuò)展運(yùn)算符
let obj1 = {a:2, b:3};
let obj2 = {...obj1};

(4) 深拷貝(拷貝所有層級(jí))

方法一: 封裝一個(gè)方法來(lái)處理對(duì)象的深拷貝,代碼如下:

var obj = {
    name: 'wsscat',
    age: 0
}
var deepCopy = function(source) {
    var result = {};
    for(var key in source) {
        if(typeof source[key] === 'object') {
            result[key] = deepCopy(source[key])
        } else {
            result[key] = source[key]
        }
    }
    return result;
}
var obj3 = deepCopy(obj)
obj.name = 'autumns';
console.log(obj);//Object {name: "autumns", age: 0}
console.log(obj3);//Object {name: "wsscat", age: 0}

方法二: 使用JSON

var obj = {
  a:2,
  b:3,
  o: {
    x: 100
  }
}

var objStr = JSON.stringify(obj);
var obj2 = JSON.parse(objStr);
obj2.o.x = 1000;
consolo.log(obj2.o.x); // 1000
consolo.log(obj.o.x); // 100

2.typeof運(yùn)算符和instanceof運(yùn)算符以及isPrototypeOf()方法的區(qū)別

typeof是一個(gè)運(yùn)算符症歇,用于檢測(cè)數(shù)據(jù)的類型郎笆,比如基本數(shù)據(jù)類型null、undefined忘晤、string宛蚓、number、boolean设塔,以及引用數(shù)據(jù)類型object苍息、function,但是對(duì)于正則表達(dá)式、日期竞思、數(shù)組這些引用數(shù)據(jù)類型表谊,它會(huì)全部識(shí)別為object; instanceof同樣也是一個(gè)運(yùn)算符盖喷,它就能很好識(shí)別數(shù)據(jù)具體是哪一種引用類型爆办。它與isPrototypeOf的區(qū)別就是它是用來(lái)檢測(cè)構(gòu)造函數(shù)的原型是否存在于指定對(duì)象的原型鏈當(dāng)中;而isPrototypeOf是用來(lái)檢測(cè)調(diào)用此方法的對(duì)象是否存在于指定對(duì)象的原型鏈中课梳,所以本質(zhì)上就是檢測(cè)目標(biāo)不同距辆。

3.什么是事件代理/事件委托?

事件代理/事件委托是利用事件冒泡的特性暮刃,將本應(yīng)該綁定在多個(gè)元素上的事件綁定在他們的祖先元素上跨算,尤其在動(dòng)態(tài)添加子元素的時(shí)候,可以非常方便的提高程序性能椭懊,減小內(nèi)存空間诸蚕。

4.什么是事件冒泡?什么是事件捕獲氧猬?

冒泡型事件:事件按照從最特定的事件目標(biāo)到最不特定的事件目標(biāo)(document對(duì)象)的順序觸發(fā)背犯。

捕獲型事件:事件從最不精確的對(duì)象(document 對(duì)象)開(kāi)始觸發(fā),然后到最精確(也可以在窗口級(jí)別捕獲事件盅抚,不過(guò)必須由開(kāi)發(fā)人員特別指定)漠魏。

5.請(qǐng)指出document.onload和document.ready兩個(gè)事件的區(qū)別

頁(yè)面加載完成有兩種事件,一是ready妄均,表示文檔結(jié)構(gòu)已經(jīng)加載完成(不包含圖片等非文字媒體文件)柱锹,二是onload,指示頁(yè)面包含圖片等文件在內(nèi)的所有元素都加載完成丰包。

6.如何從瀏覽器的URL中獲取查詢字符串參數(shù)禁熏?

getUrlParam : function(name){
        //baidu.com/product/list?keyword=XXX&page=1
        var reg     = new RegExp('(^|&)' + name + '=([^&]*)(&|$)');
        var result  = window.location.search.substr(1).match(reg);
        return result ? decodeURIComponent(result[2]) : null;
    }

7.什么是"use strict";?使用它的好處和壞處分別是什么?

在代碼中出現(xiàn)表達(dá)式-"use strict"; 意味著代碼按照嚴(yán)格模式解析烫沙,這種模式使得Javascript在更嚴(yán)格的條件下運(yùn)行匹层。

好處:

  • 消除Javascript語(yǔ)法的一些不合理、不嚴(yán)謹(jǐn)之處锌蓄,減少一些怪異行為;
  • 消除代碼運(yùn)行的一些不安全之處升筏,保證代碼運(yùn)行的安全;
  • 提高編譯器效率瘸爽,增加運(yùn)行速度您访;
  • 為未來(lái)新版本的Javascript做好鋪墊。

壞處:

  • 同樣的代碼剪决,在"嚴(yán)格模式"中灵汪,可能會(huì)有不一樣的運(yùn)行結(jié)果檀训;
  • 一些在"正常模式"下可以運(yùn)行的語(yǔ)句,在"嚴(yán)格模式"下將不能運(yùn)行享言。

8.請(qǐng)解釋JSONP的工作原理峻凫,以及它為什么不是真正的AJAX。

JSONP (JSON with Padding)是一個(gè)簡(jiǎn)單高效的跨域方式览露,HTML中的script標(biāo)簽可以加載并執(zhí)行其他域的javascript荧琼,于是我們可以通過(guò)script標(biāo)記來(lái)動(dòng)態(tài)加載其他域的資源。例如我要從域A的頁(yè)面pageA加載域B的數(shù)據(jù)差牛,那么在域B的頁(yè)面pageB中我以JavaScript的形式聲明pageA需要的數(shù)據(jù)命锄,然后在 pageA中用script標(biāo)簽把pageB加載進(jìn)來(lái),那么pageB中的腳本就會(huì)得以執(zhí)行偏化。JSONP在此基礎(chǔ)上加入了回調(diào)函數(shù)脐恩,pageB加載完之后會(huì)執(zhí)行pageA中定義的函數(shù),所需要的數(shù)據(jù)會(huì)以參數(shù)的形式傳遞給該函數(shù)侦讨。JSONP易于實(shí)現(xiàn)驶冒,但是也會(huì)存在一些安全隱患,如果第三方的腳本隨意地執(zhí)行搭伤,那么它就可以篡改頁(yè)面內(nèi)容只怎,截獲敏感數(shù)據(jù)袜瞬。但是在受信任的雙方傳遞數(shù)據(jù)怜俐,JSONP是非常合適的選擇。

AJAX是不跨域的邓尤,而JSONP是一個(gè)是跨域的拍鲤,還有就是二者接收參數(shù)形式不一樣!

9.防抖和節(jié)流

1??.如果用戶持續(xù)點(diǎn)擊一個(gè)按鈕汞扎,如何只提交一次請(qǐng)求季稳,且不影響后續(xù)使用?(其實(shí)就是如何節(jié)流這個(gè)真的問(wèn)的好多3浩恰>笆蟆!痹扇!)

何為節(jié)流 觸發(fā)函數(shù)事件后铛漓,短時(shí)間間隔內(nèi)無(wú)法連續(xù)調(diào)用,只有上一次函數(shù)執(zhí)行后鲫构,過(guò)了規(guī)定的時(shí)間間隔浓恶,才能進(jìn)行下一次的函數(shù)調(diào)用,一般用于http請(qǐng)求结笨。

解決原理 對(duì)處理函數(shù)進(jìn)行延時(shí)操作包晰,若設(shè)定的延時(shí)到來(lái)之前湿镀,再次觸發(fā)事件,則清除上一次的延時(shí)操作定時(shí)器伐憾,重新定時(shí)勉痴。

    function conso(){
          console.log('is run');
      }
      var btnUse=true;
     $("#btn").click(function(){
         if(btnUse){
             conso();
             btnUse=false;
         }
         setTimeout(function(){
             btnUse=true;
         },1500) //點(diǎn)擊后相隔多長(zhǎng)時(shí)間可執(zhí)行
     })
復(fù)制代碼

2??.如何防抖?(一般都和節(jié)流一起問(wèn)树肃,一定要搞懂J赐取!)

何為防抖 多次觸發(fā)事件后扫外,事件處理函數(shù)只執(zhí)行一次莉钙,并且是在觸發(fā)操作結(jié)束時(shí)執(zhí)行,一般用于scroll事件筛谚。

解決原理 對(duì)處理函數(shù)進(jìn)行延時(shí)操作磁玉,若設(shè)定的延時(shí)到來(lái)之前再次觸發(fā)事件,則清除上一次的延時(shí)操作定時(shí)器驾讲,重新定時(shí)蚊伞。

let timer;
window.onscroll  = function () {
    if(timer){
        clearTimeout(timer)
    }
    timer = setTimeout(function () {
        //滾動(dòng)條位置
        let scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
        console.log('滾動(dòng)條位置:' + scrollTop);
        timer = undefined;
    },200)
}
復(fù)制代碼

或者是這樣:

function debounce(fn, wait) {
    var timeout = null;
    return function() {  
        if(timeout !== null)   clearTimeout(timeout);        
        timeout = setTimeout(fn, wait);    
    }
}
// 處理函數(shù)
function handle() {    
    console.log(Math.random()); 
}
// 滾動(dòng)事件
window.addEventListener('scroll', debounce(handle, 1000));

10.數(shù)組去重的方法

1??:利用ES6 Set去重(ES6中最常用)

function unique (arr) {
  return Array.from(new Set(arr))
}

2??:利用for嵌套for,然后splice去重(ES5中最常用)

function unique(arr){            
        for(var i=0; i<arr.length; i++){
            for(var j=i+1; j<arr.length; j++){
                if(arr[i]==arr[j]){         //第一個(gè)等同于第二個(gè)吮铭,splice方法刪除第二個(gè)
                    arr.splice(j,1);
                    j--;
                }
            }
        }
        return arr;
}

3??:利用對(duì)象的屬性不能相同的特點(diǎn)進(jìn)行去重(這種數(shù)組去重的方法有問(wèn)題时迫,不建議用,但是有人考)

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return
    }
    var arrry= [];
     var  obj = {};
    for (var i = 0; i < arr.length; i++) {
        if (!obj[arr[i]]) {
            arrry.push(arr[i])
            obj[arr[i]] = 1
        } else {
            obj[arr[i]]++
        }
    }
    return arrry;
}

4??:利用filter

function unique(arr) {
  return arr.filter(function(item, index, arr) {
    //當(dāng)前元素谓晌,在原始數(shù)組中的第一個(gè)索引==當(dāng)前索引值掠拳,否則返回當(dāng)前元素
    return arr.indexOf(item, 0) === index;
  });
}

5??:Map數(shù)據(jù)結(jié)構(gòu)去重

function arrayNonRepeatfy(arr) {
  let map = new Map();
  let array = new Array();  // 數(shù)組用于返回結(jié)果
  for (let i = 0; i < arr.length; i++) {
    if(map .has(arr[i])) {  // 如果有該key值
      map .set(arr[i], true); 
    } else { 
      map .set(arr[i], false);   // 如果沒(méi)有該key值
      array .push(arr[i]);
    }
  } 
  return array ;
}

11.數(shù)組的排序

1??:冒泡排序:思路:重復(fù)遍歷數(shù)組中的元素,依次比較兩個(gè)相鄰的元素纸肉,如果前一個(gè)元素大于后一個(gè)元素溺欧,就依靠第三個(gè)變量將它們換過(guò)來(lái),直到所有元素遍歷完柏肪。

function bubbleSort(arr){

          for(let i = 0; i < arr.length - 1; i ++){

                for(let j = 0; j < arr.length - 1 - i; j ++){

                      if(arr[j] > arr[j+1]){   

                            let tem = arr[j];

                            arr[j] = arr[j+1];

                            arr[j+1] = tem;

                      }

                }

          }

2??:選擇排序:思路:每一次從數(shù)組中選出最小的一個(gè)元素姐刁,存放在數(shù)組的起始位置,然后烦味,再?gòu)氖S辔磁判虻臄?shù)組中繼續(xù)尋找最小元素聂使,然后放到已排序序列的末尾。直到全部數(shù)據(jù)元素排完谬俄。

function selectSort(arr){

          let min = 0; // 用來(lái)保存數(shù)組中最小的數(shù)的索引值

          for(let i = 0; i < arr.length - 1; i ++){

                min = i;

                for(let j = i + 1; j < arr.length; j ++){

                      if(arr[j] < arr[min]){

                            min = j;

                      }

                }

              if(min != i){

                      swap(arr,i,min);

                }

          }

          console.log(arr);

    };

    function swap(arr,index1,index2){ 

    let tem = arr[index1];

          arr[index1] = arr[index2];

          arr[index2] = tem;

    }

    selectSort([7,5,1,2,6,4,8,3,2]);    // output: [1, 2, 2, 3, 4, 5, 6, 7, 8]

3??:快速排序: 對(duì)冒泡排序的一種改進(jìn)柏靶。通過(guò)一趟排序?qū)⒁判虻臄?shù)據(jù)分割成獨(dú)立的兩部分,其中一部分的所有數(shù)據(jù)都比另外一部分的所有數(shù)據(jù)都比另一部分的所有數(shù)據(jù)小凤瘦,然后再按此方法對(duì)這兩部分?jǐn)?shù)據(jù)分別進(jìn)行快速排序宿礁,整個(gè)排序過(guò)程可以遞歸進(jìn)行,以此達(dá)到整個(gè)數(shù)據(jù)變成有序序列蔬芥。

思路:
(1)找基準(zhǔn)(一般以中間項(xiàng)為基準(zhǔn))
(2)遍歷數(shù)組梆靖,小于基準(zhǔn)的放在 left控汉,大于基準(zhǔn)的放在 right
(3)遞歸

function quickSort(arr){

    if(arr.length<=1){return arr;}    //如果數(shù)組<=1,則直接返回

    let pivotIndex = Math.floor(arr.length/2);   

    let pivot = arr.splice(pivotIndex,1)[0]; //找基準(zhǔn),并把基準(zhǔn)從原數(shù)組刪除

    let left=[], right=[];    //定義左右數(shù)組

    for(let i=0; i<arr.length; i++){        //比基準(zhǔn)小的放在left返吻,比基準(zhǔn)大的放在right

        if(arr[i] <= pivot){

            left.push(arr[i]);   

        } else { 

        right.push(arr[i]);

        }

    }

    return quickSort(left).concat([pivot],quickSort(right));    //遞歸

}

12.繼承

第一種姑子,prototype的方式:

//父類 
function person(){ 
  this.hair = 'black'; 
  this.eye = 'black'; 
  this.skin = 'yellow'; 
  this.view = function(){ 
    return this.hair + ',' + this.eye + ',' + this.skin; 
  } 
} 
 
//子類 
function man(){ 
  this.feature = ['beard','strong']; 
} 
 
man.prototype = new person(); 
var one = new man(); 

這種方式最為簡(jiǎn)單,只需要讓子類的prototype屬性值賦值為被繼承的一個(gè)實(shí)例就行了测僵,之后就可以直接使用被繼承類的方法了街佑。

prototype 屬性是啥意思呢? prototype 即為原型捍靠,每一個(gè)對(duì)象 ( 由 function 定義出來(lái) ) 都有一個(gè)默認(rèn)的原型屬性沐旨,該屬性是個(gè)對(duì)象類型。

并且該默認(rèn)屬性用來(lái)實(shí)現(xiàn)鏈的向上攀查榨婆。意思就是說(shuō)磁携,如果某個(gè)對(duì)象的屬性不存在,那么將通過(guò)prototype屬性所屬對(duì)象來(lái)查找這個(gè)屬性良风。如果 prototype 查找不到呢谊迄?

js會(huì)自動(dòng)地找prototype的prototype屬性所屬對(duì)象來(lái)查找,這樣就通過(guò)prototype一直往上索引攀查烟央,直到查找到了該屬性或者prototype最后為空 (“undefined”);

例如上例中的one.view()方法统诺,js會(huì)先在one實(shí)例中查找是否有view()方法,因?yàn)闆](méi)有疑俭,所以查找man.prototype屬性粮呢,而prototype的值為person的一個(gè)實(shí)例,

該實(shí)例有view()方法怠硼,于是調(diào)用成功鬼贱。

第二種移怯,apply的方式:

//父類 
function person(){ 
  this.hair = 'black'; 
  this.eye = 'black'; 
  this.skin = 'yellow'; 
  this.view = function(){ 
    return this.hair + ',' + this.eye + ',' + this.skin; 
  } 
} 
 
//子類 
function man(){ 
  // person.apply(this,new Array()); 
  person.apply(this,[]); 
  this.feature = ['beard','strong']; 
} 

第三種,call+prototype的方式:

/父類 
function person(){ 
  this.hair = 'black'; 
  this.eye = 'black'; 
  this.skin = 'yellow'; 
  this.view = function(){ 
    return this.hair + ',' + this.eye + ',' + this.skin; 
  } 
} 
 
//子類 
function man(){ 
  // person.apply(this,new Array()); 
  person.call(this,[]); 
  this.feature = ['beard','strong']; 
} 
 
man.prototype = new person(); 
var one = new man(); 

13. for of , for in 和 forEach,map 的區(qū)別。

for...of循環(huán):具有 iterator 接口裂垦,就可以用for...of循環(huán)遍歷它的成員(屬性值)魔招。for...of循環(huán)可以使用的范圍包括數(shù)組、Set 和 Map 結(jié)構(gòu)嵌溢、某些類似數(shù)組的對(duì)象眯牧、Generator 對(duì)象,以及字符串赖草。for...of循環(huán)調(diào)用遍歷器接口学少,數(shù)組的遍歷器接口只返回具有數(shù)字索引的屬性。對(duì)于普通的對(duì)象秧骑,for...of結(jié)構(gòu)不能直接使用版确,會(huì)報(bào)錯(cuò)扣囊,必須部署了 Iterator 接口后才能使用∪蘖疲可以中斷循環(huán)侵歇。

for...in循環(huán):遍歷對(duì)象自身的和繼承的可枚舉的

屬性

, 不能直接獲取屬性值∠拍ⅲ可以中斷循環(huán)惕虑。

forEach: 只能遍歷數(shù)組,不能中斷磨镶,沒(méi)有返回值(或認(rèn)為返回值是undefined)溃蔫。

map: 只能遍歷數(shù)組,不能中斷琳猫,返回值是修改后的數(shù)組酒唉。

14.說(shuō)下ES6中的class

ES6 class 內(nèi)部所有定義的方法都是不可枚舉的;

ES6 class 必須使用 new 調(diào)用;

ES6 class 不存在變量提升;

ES6 class 默認(rèn)即是嚴(yán)格模式;

ES6 class 子類必須在父類的構(gòu)造函數(shù)中調(diào)用super(),這樣才有this對(duì)象;ES5中類繼承的關(guān)系是相反的沸移,先有子類的this痪伦,然后用父類的方法應(yīng)用在this上。

15.在JS中什么是變量提升雹锣?什么是暫時(shí)性死區(qū)网沾?

變量提升就是變量在聲明之前就可以使用,值為undefined蕊爵。

在代碼塊內(nèi)辉哥,使用 let/const 命令聲明變量之前,該變量都是不可用的(會(huì)拋出錯(cuò)誤)攒射。這在語(yǔ)法上醋旦,稱為“暫時(shí)性死區(qū)”。暫時(shí)性死區(qū)也意味著 typeof 不再是一個(gè)百分百安全的操作会放。

暫時(shí)性死區(qū)的本質(zhì)就是饲齐,只要一進(jìn)入當(dāng)前作用域,所要使用的變量就已經(jīng)存在了咧最,但是不可獲取捂人,只有等到聲明變量的那一行代碼出現(xiàn),才可以獲取和使用該變量矢沿。

16.什么是閉包滥搭?閉包的作用是什么?閉包有哪些使用場(chǎng)景捣鲸?

閉包是指有權(quán)訪問(wèn)另一個(gè)函數(shù)作用域中的變量的函數(shù)瑟匆,創(chuàng)建閉包最常用的方式就是在一個(gè)函數(shù)內(nèi)部創(chuàng)建另一個(gè)函數(shù)。

閉包的作用有:

  1. 封裝私有變量
  2. 模仿塊級(jí)作用域(ES5中沒(méi)有塊級(jí)作用域)
  3. 實(shí)現(xiàn)JS的模塊

17.call栽惶、apply有什么區(qū)別愁溜?call,aplly和bind的內(nèi)部是如何實(shí)現(xiàn)的无午?

call 和 apply 的功能相同,區(qū)別在于傳參的方式不一樣:祝谚,apply的實(shí)現(xiàn)和call很類似宪迟,但是需要注意他們的參數(shù)是不一樣的,apply的第二個(gè)參數(shù)是數(shù)組或類數(shù)組.

bind 和 call/apply 有一個(gè)很重要的區(qū)別交惯,一個(gè)函數(shù)被 call/apply 的時(shí)候次泽,會(huì)直接調(diào)用,但是 bind 會(huì)創(chuàng)建一個(gè)新函數(shù)席爽。當(dāng)這個(gè)新函數(shù)被調(diào)用時(shí)意荤,bind() 的第一個(gè)參數(shù)將作為它運(yùn)行時(shí)的 this,之后的一序列參數(shù)將會(huì)在傳遞的實(shí)參前傳入作為它的參數(shù)只锻。

18:new的原理是什么玖像?通過(guò)new的方式創(chuàng)建對(duì)象和通過(guò)字面量創(chuàng)建有什么區(qū)別?

  1. 創(chuàng)建一個(gè)新對(duì)象齐饮。
  2. 這個(gè)新對(duì)象會(huì)被執(zhí)行[[原型]]連接捐寥。
  3. 將構(gòu)造函數(shù)的作用域賦值給新對(duì)象,即this指向這個(gè)新對(duì)象.
  4. 如果函數(shù)沒(méi)有返回其他對(duì)象祖驱,那么new表達(dá)式中的函數(shù)調(diào)用會(huì)自動(dòng)返回這個(gè)新對(duì)象握恳。

19。ES6新的特性有哪些捺僻?

新增了塊級(jí)作用域(let,const)

提供了定義類的語(yǔ)法糖(class)

新增了一種基本數(shù)據(jù)類型(Symbol)

新增了變量的解構(gòu)賦值

函數(shù)參數(shù)允許設(shè)置默認(rèn)值乡洼,引入了rest參數(shù),新增了箭頭函數(shù)

數(shù)組新增了一些API匕坯,如 isArray / from / of 方法;數(shù)組實(shí)例新增了 entries()束昵,keys() 和 values() 等方法

對(duì)象和數(shù)組新增了擴(kuò)展運(yùn)算符

ES6 新增了模塊化(import/export)

ES6 新增了 Set 和 Map 數(shù)據(jù)結(jié)構(gòu)

ES6 原生提供 Proxy 構(gòu)造函數(shù),用來(lái)生成 Proxy 實(shí)例

ES6 新增了生成器(Generator)和遍歷器(Iterator)

20葛峻。setTimeout倒計(jì)時(shí)為什么會(huì)出現(xiàn)誤差锹雏?

setTimeout() 只是將事件插入了“任務(wù)隊(duì)列”,必須等當(dāng)前代碼(執(zhí)行棧)執(zhí)行完泞歉,主線程才會(huì)去執(zhí)行它指定的回調(diào)函數(shù)逼侦。要是當(dāng)前代碼消耗時(shí)間很長(zhǎng),也有可能要等很久腰耙,所以并沒(méi)辦法保證回調(diào)函數(shù)一定會(huì)在 setTimeout() 指定的時(shí)間執(zhí)行。所以铲球, setTimeout() 的第二個(gè)參數(shù)表示的是最少時(shí)間挺庞,并非是確切時(shí)間。

HTML5標(biāo)準(zhǔn)規(guī)定了 setTimeout() 的第二個(gè)參數(shù)的最小值不得小于4毫秒稼病,如果低于這個(gè)值选侨,則默認(rèn)是4毫秒掖鱼。在此之前。老版本的瀏覽器都將最短時(shí)間設(shè)為10毫秒援制。另外戏挡,對(duì)于那些DOM的變動(dòng)(尤其是涉及頁(yè)面重新渲染的部分),通常是間隔16毫秒執(zhí)行晨仑。這時(shí)使用 requestAnimationFrame() 的效果要好于 setTimeout();

21.為什么 0.1 + 0.2 != 0.3 ?

0.1 + 0.2 != 0.3 是因?yàn)樵谶M(jìn)制轉(zhuǎn)換和進(jìn)階運(yùn)算的過(guò)程中出現(xiàn)精度損失褐墅。

下面是詳細(xì)解釋:

JavaScript使用 Number 類型表示數(shù)字(整數(shù)和浮點(diǎn)數(shù)),使用64位表示一個(gè)數(shù)字洪己。

[圖片上傳失敗...(image-bc1b91-1574390988740)]

圖片說(shuō)明:

  • 第0位:符號(hào)位妥凳,0表示正數(shù),1表示負(fù)數(shù)(s)
  • 第1位到第11位:儲(chǔ)存指數(shù)部分(e)
  • 第12位到第63位:儲(chǔ)存小數(shù)部分(即有效數(shù)字)f

計(jì)算機(jī)無(wú)法直接對(duì)十進(jìn)制的數(shù)字進(jìn)行運(yùn)算, 需要先對(duì)照 IEEE 754 規(guī)范轉(zhuǎn)換成二進(jìn)制答捕,然后對(duì)階運(yùn)算逝钥。

1.進(jìn)制轉(zhuǎn)換

0.1和0.2轉(zhuǎn)換成二進(jìn)制后會(huì)無(wú)限循環(huán)

0.1 -> 0.0001100110011001...(無(wú)限循環(huán))
0.2 -> 0.0011001100110011...(無(wú)限循環(huán))
復(fù)制代碼復(fù)制代碼

但是由于IEEE 754尾數(shù)位數(shù)限制,需要將后面多余的位截掉拱镐,這樣在進(jìn)制之間的轉(zhuǎn)換中精度已經(jīng)損失艘款。

2.對(duì)階運(yùn)算

由于指數(shù)位數(shù)不相同,運(yùn)算時(shí)需要對(duì)階運(yùn)算 這部分也可能產(chǎn)生精度損失沃琅。

按照上面兩步運(yùn)算(包括兩步的精度損失)磷箕,最后的結(jié)果是

0.0100110011001100110011001100110011001100110011001100

結(jié)果轉(zhuǎn)換成十進(jìn)制之后就是 0.30000000000000004。

22.Promise和setTimeout的區(qū)別 ?

Promise 是微任務(wù)阵难,setTimeout 是宏任務(wù)岳枷,同一個(gè)事件循環(huán)中,promise.then總是先于 setTimeout 執(zhí)行呜叫。

23.如何實(shí)現(xiàn) Promise.all ?

要實(shí)現(xiàn) Promise.all,首先我們需要知道 Promise.all 的功能:

  1. 如果傳入的參數(shù)是一個(gè)空的可迭代對(duì)象空繁,那么此promise對(duì)象回調(diào)完成(resolve),只有此情況,是同步執(zhí)行的朱庆,其它都是異步返回的盛泡。
  2. 如果傳入的參數(shù)不包含任何 promise,則返回一個(gè)異步完成. promises 中所有的promise都“完成”時(shí)或參數(shù)中不包含 promise 時(shí)回調(diào)完成娱颊。
  3. 如果參數(shù)中有一個(gè)promise失敗傲诵,那么Promise.all返回的promise對(duì)象失敗
  4. 在任何情況下,Promise.all 返回的 promise 的完成狀態(tài)的結(jié)果都是一個(gè)數(shù)組
Promise.all = function (promises) {
    return new Promise((resolve, reject) => {
        let index = 0;
        let result = [];
        if (promises.length === 0) {
            resolve(result);
        } else {
            function processValue(i, data) {
                result[i] = data;
                if (++index === promises.length) {
                    resolve(result);
                }
            }
            for (let i = 0; i < promises.length; i++) {
                //promises[i] 可能是普通值
                Promise.resolve(promises[i]).then((data) => {
                    processValue(i, data);
                }, (err) => {
                    reject(err);
                    return;
                });
            }
        }
    });
}

24.如何實(shí)現(xiàn) Promise.finally ?

不管成功還是失敗箱硕,都會(huì)走到finally中,并且finally之后拴竹,還可以繼續(xù)then。并且會(huì)將值原封不動(dòng)的傳遞給后面的then.

Promise.prototype.finally = function (callback) {
    return this.then((value) => {
        return Promise.resolve(callback()).then(() => {
            return value;
        });
    }, (err) => {
        return Promise.resolve(callback()).then(() => {
            throw err;
        });
    });
}

25.什么是函數(shù)柯里化剧罩?實(shí)現(xiàn) sum(1)(2)(3) 返回結(jié)果是1,2,3之和

函數(shù)柯里化是把接受多個(gè)參數(shù)的函數(shù)變換成接受一個(gè)單一參數(shù)(最初函數(shù)的第一個(gè)參數(shù))的函數(shù)栓拜,并且返回接受余下的參數(shù)而且返回結(jié)果的新函數(shù)的技術(shù)。

function sum(a) {
    return function(b) {
        return function(c) {
            return a+b+c;
        }
    }
}
console.log(sum(1)(2)(3)); // 6

26.談?wù)剬?duì) async/await 的理解,async/await 的實(shí)現(xiàn)原理是什么?

async/await 就是 Generator 的語(yǔ)法糖幕与,使得異步操作變得更加方便挑势。來(lái)張圖對(duì)比一下:

[圖片上傳失敗...(image-fc5dc6-1574390988740)]

async 函數(shù)就是將 Generator 函數(shù)的星號(hào)(*)替換成 async,將 yield 替換成await啦鸣。

我們說(shuō) async 是 Generator 的語(yǔ)法糖潮饱,那么這個(gè)糖究竟甜在哪呢?

1)async函數(shù)內(nèi)置執(zhí)行器诫给,函數(shù)調(diào)用之后香拉,會(huì)自動(dòng)執(zhí)行,輸出最后結(jié)果蝙搔。而Generator需要調(diào)用next或者配合co模塊使用缕溉。

2)更好的語(yǔ)義,async和await吃型,比起星號(hào)和yield证鸥,語(yǔ)義更清楚了。async表示函數(shù)里有異步操作勤晚,await表示緊跟在后面的表達(dá)式需要等待結(jié)果枉层。

3)更廣的適用性。co模塊約定赐写,yield命令后面只能是 Thunk 函數(shù)或 Promise 對(duì)象鸟蜡,而async 函數(shù)的 await 命令后面,可以是 Promise 對(duì)象和原始類型的值挺邀。

4)返回值是Promise揉忘,async函數(shù)的返回值是 Promise 對(duì)象,Generator的返回值是 Iterator端铛,Promise 對(duì)象使用起來(lái)更加方便泣矛。

async 函數(shù)的實(shí)現(xiàn)原理,就是將 Generator 函數(shù)和自動(dòng)執(zhí)行器禾蚕,包裝在一個(gè)函數(shù)里您朽。

function my_co(it) {
    return new Promise((resolve, reject) => {
        function next(data) {
            try {
                var { value, done } = it.next(data);
            }catch(e){
                return reject(e);
            }
            if (!done) { 
                //done為true,表示迭代完成
                //value 不一定是 Promise,可能是一個(gè)普通值换淆。使用 Promise.resolve 進(jìn)行包裝哗总。
                Promise.resolve(value).then(val => {
                    next(val);
                }, reject);
            } else {
                resolve(value);
            }
        }
        next(); //執(zhí)行一次next
    });
}
function* test() {
    yield new Promise((resolve, reject) => {
        setTimeout(resolve, 100);
    });
    yield new Promise((resolve, reject) => {
        // throw Error(1);
        resolve(10)
    });
    yield 10;
    return 1000;
}

my_co(test()).then(data => {
    console.log(data); //輸出1000
}).catch((err) => {
    console.log('err: ', err);
});

27.requestAnimationFrame 和 setTimeout/setInterval 有什么區(qū)別?使用 requestAnimationFrame 有哪些好處倍试?

在 requestAnimationFrame 之前讯屈,我們主要使用 setTimeout/setInterval 來(lái)編寫JS動(dòng)畫。

編寫動(dòng)畫的關(guān)鍵是循環(huán)間隔的設(shè)置易猫,一方面耻煤,循環(huán)間隔足夠短具壮,動(dòng)畫效果才能顯得平滑流暢准颓;另一方面哈蝇,循環(huán)間隔還要足夠長(zhǎng),才能確保瀏覽器有能力渲染產(chǎn)生的變化攘已。

大部分的電腦顯示器的刷新頻率是60HZ炮赦,也就是每秒鐘重繪60次。大多數(shù)瀏覽器都會(huì)對(duì)重繪操作加以限制样勃,不超過(guò)顯示器的重繪頻率吠勘,因?yàn)榧词钩^(guò)那個(gè)頻率用戶體驗(yàn)也不會(huì)提升。因此峡眶,最平滑動(dòng)畫的最佳循環(huán)間隔是 1000ms / 60 剧防,約為16.7ms。

setTimeout/setInterval 有一個(gè)顯著的缺陷在于時(shí)間是不精確的辫樱,setTimeout/setInterval 只能保證延時(shí)或間隔不小于設(shè)定的時(shí)間峭拘。因?yàn)樗鼈儗?shí)際上只是把任務(wù)添加到了任務(wù)隊(duì)列中,但是如果前面的任務(wù)還沒(méi)有執(zhí)行完成狮暑,它們必須要等待鸡挠。

requestAnimationFrame 才有的是系統(tǒng)時(shí)間間隔,保持最佳繪制效率搬男,不會(huì)因?yàn)殚g隔時(shí)間過(guò)短拣展,造成過(guò)度繪制,增加開(kāi)銷缔逛;也不會(huì)因?yàn)殚g隔時(shí)間太長(zhǎng)备埃,使用動(dòng)畫卡頓不流暢,讓各種網(wǎng)頁(yè)動(dòng)畫效果能夠有一個(gè)統(tǒng)一的刷新機(jī)制褐奴,從而節(jié)省系統(tǒng)資源按脚,提高系統(tǒng)性能,改善視覺(jué)效果歉糜。

綜上所述乘寒,requestAnimationFrame 和 setTimeout/setInterval 在編寫動(dòng)畫時(shí)相比,優(yōu)點(diǎn)如下:

1.requestAnimationFrame 不需要設(shè)置時(shí)間匪补,采用系統(tǒng)時(shí)間間隔伞辛,能達(dá)到最佳的動(dòng)畫效果。

2.requestAnimationFrame 會(huì)把每一幀中的所有DOM操作集中起來(lái)夯缺,在一次重繪或回流中就完成蚤氏。

3.當(dāng) requestAnimationFrame() 運(yùn)行在后臺(tái)標(biāo)簽頁(yè)或者隱藏的 <iframe> 里時(shí),requestAnimationFrame() 會(huì)被暫停調(diào)用以提升性能和電池壽命(大多數(shù)瀏覽器中)踊兜。

28.簡(jiǎn)述下對(duì) webWorker 的理解竿滨?

HTML5則提出了 Web Worker 標(biāo)準(zhǔn),表示js允許多線程,但是子線程完全受主線程控制并且不能操作dom于游,只有主線程可以操作dom毁葱,所以js本質(zhì)上依然是單線程語(yǔ)言。

web worker就是在js單線程執(zhí)行的基礎(chǔ)上開(kāi)啟一個(gè)子線程贰剥,進(jìn)行程序處理倾剿,而不影響主線程的執(zhí)行,當(dāng)子線程執(zhí)行完之后再回到主線程上蚌成,在這個(gè)過(guò)程中不影響主線程的執(zhí)行前痘。子線程與主線程之間提供了數(shù)據(jù)交互的接口postMessage和onmessage,來(lái)進(jìn)行數(shù)據(jù)發(fā)送和接收担忧。

var worker = new Worker('./worker.js'); //創(chuàng)建一個(gè)子線程
worker.postMessage('Hello');
worker.onmessage = function (e) {
    console.log(e.data); //Hi
    worker.terminate(); //結(jié)束線程
};復(fù)制代碼
//worker.js
onmessage = function (e) {
    console.log(e.data); //Hello
    postMessage("Hi"); //向主進(jìn)程發(fā)送消息
};復(fù)制代碼

僅是最簡(jiǎn)示例代碼芹缔,項(xiàng)目中通常是將一些耗時(shí)較長(zhǎng)的代碼,放在子線程中運(yùn)行瓶盛。

29.跨域的方法有哪些最欠?原理是什么?

知其然知其所以然蓬网,在說(shuō)跨域方法之前窒所,我們先了解下什么叫跨域,瀏覽器有同源策略帆锋,只有當(dāng)“協(xié)議”吵取、“域名”、“端口號(hào)”都相同時(shí)锯厢,才能稱之為是同源皮官,其中有一個(gè)不同,即是跨域实辑。

那么同源策略的作用是什么呢捺氢?同源策略限制了從同一個(gè)源加載的文檔或腳本如何與來(lái)自另一個(gè)源的資源進(jìn)行交互。這是一個(gè)用于隔離潛在惡意文件的重要安全機(jī)制剪撬。

那么我們又為什么需要跨域呢摄乒?一是前端和服務(wù)器分開(kāi)部署,接口請(qǐng)求需要跨域残黑,二是我們可能會(huì)加載其它網(wǎng)站的頁(yè)面作為iframe內(nèi)嵌馍佑。

跨域的方法有哪些?

常用的跨域方法

  1. jsonp

盡管瀏覽器有同源策略梨水,但是 <script> 標(biāo)簽的 src 屬性不會(huì)被同源策略所約束拭荤,可以獲取任意服務(wù)器上的腳本并執(zhí)行。jsonp 通過(guò)插入script標(biāo)簽的方式來(lái)實(shí)現(xiàn)跨域疫诽,參數(shù)只能通過(guò)url傳入舅世,僅能支持get請(qǐng)求旦委。

實(shí)現(xiàn)原理:

Step1: 創(chuàng)建 callback 方法

Step2: 插入 script 標(biāo)簽

Step3: 后臺(tái)接受到請(qǐng)求,解析前端傳過(guò)去的 callback 方法雏亚,返回該方法的調(diào)用缨硝,并且數(shù)據(jù)作為參數(shù)傳入該方法

Step4: 前端執(zhí)行服務(wù)端返回的方法調(diào)用

下面代碼僅為說(shuō)明 jsonp 原理,項(xiàng)目中請(qǐng)使用成熟的庫(kù)评凝。分別看一下前端和服務(wù)端的簡(jiǎn)單實(shí)現(xiàn):

//前端代碼
function jsonp({url, params, cb}) {
    return new Promise((resolve, reject) => {
        //創(chuàng)建script標(biāo)簽
        let script = document.createElement('script');
        //將回調(diào)函數(shù)掛在 window 上
        window[cb] = function(data) {
            resolve(data);
            //代碼執(zhí)行后追葡,刪除插入的script標(biāo)簽
            document.body.removeChild(script);
        }
        //回調(diào)函數(shù)加在請(qǐng)求地址上
        params = {...params, cb} //wb=b&cb=show
        let arrs = [];
        for(let key in params) {
            arrs.push(`${key}=${params[key]}`);
        }
        script.src = `${url}?${arrs.join('&')}`;
        document.body.appendChild(script);
    });
}
//使用
function sayHi(data) {
    console.log(data);
}
jsonp({
    url: 'http://localhost:3000/say',
    params: {
        //code
    },
    cb: 'sayHi'
}).then(data => {
    console.log(data);
});復(fù)制代碼
//express啟動(dòng)一個(gè)后臺(tái)服務(wù)
let express = require('express');
let app = express();

app.get('/say', (req, res) => {
    let {cb} = req.query; //獲取傳來(lái)的callback函數(shù)名腺律,cb是key
    res.send(`${cb}('Hello!')`);
});
app.listen(3000);復(fù)制代碼

從今天起奕短,jsonp的原理就要了然于心啦~

  1. cors

jsonp 只能支持 get 請(qǐng)求,cors 可以支持多種請(qǐng)求匀钧。cors 并不需要前端做什么工作翎碑。

簡(jiǎn)單跨域請(qǐng)求:

只要服務(wù)器設(shè)置的Access-Control-Allow-Origin Header和請(qǐng)求來(lái)源匹配,瀏覽器就允許跨域

  1. 請(qǐng)求的方法是get之斯,head或者post日杈。
  2. Content-Type是application/x-www-form-urlencoded, multipart/form-data 或 text/plain中的一個(gè)值,或者不設(shè)置也可以佑刷,一般默認(rèn)就是application/x-www-form-urlencoded莉擒。
  3. 請(qǐng)求中沒(méi)有自定義的HTTP頭部,如x-token瘫絮。(應(yīng)該是這幾種頭部 Accept涨冀,Accept-Language,Content-Language麦萤,Last-Event-ID鹿鳖,Content-Type)
//簡(jiǎn)單跨域請(qǐng)求
app.use((req, res, next) => {
    res.setHeader('Access-Control-Allow-Origin', 'XXXX');
});復(fù)制代碼

帶預(yù)檢(Preflighted)的跨域請(qǐng)求

不滿于簡(jiǎn)單跨域請(qǐng)求的,即是帶預(yù)檢的跨域請(qǐng)求壮莹。服務(wù)端需要設(shè)置 Access-Control-Allow-Origin (允許跨域資源請(qǐng)求的域) 翅帜、 Access-Control-Allow-Methods (允許的請(qǐng)求方法) 和 Access-Control-Allow-Headers (允許的請(qǐng)求頭)

app.use((req, res, next) => {
    res.setHeader('Access-Control-Allow-Origin', 'XXX');
    res.setHeader('Access-Control-Allow-Headers', 'XXX'); //允許返回的頭
    res.setHeader('Access-Control-Allow-Methods', 'XXX');//允許使用put方法請(qǐng)求接口
    res.setHeader('Access-Control-Max-Age', 6); //預(yù)檢的存活時(shí)間
    if(req.method === "OPTIONS") {
        res.end(); //如果method是OPTIONS,不做處理
    }
});復(fù)制代碼

更多CORS的知識(shí)可以訪問(wèn): HTTP訪問(wèn)控制(CORS)

  1. nginx 反向代理

使用nginx反向代理實(shí)現(xiàn)跨域命满,只需要修改nginx的配置即可解決跨域問(wèn)題涝滴。

A網(wǎng)站向B網(wǎng)站請(qǐng)求某個(gè)接口時(shí),向B網(wǎng)站發(fā)送一個(gè)請(qǐng)求胶台,nginx根據(jù)配置文件接收這個(gè)請(qǐng)求歼疮,代替A網(wǎng)站向B網(wǎng)站來(lái)請(qǐng)求。 nginx拿到這個(gè)資源后再返回給A網(wǎng)站概作,以此來(lái)解決了跨域問(wèn)題腋妙。

例如nginx的端口號(hào)為 8090,需要請(qǐng)求的服務(wù)器端口號(hào)為 3000讯榕。(localhost:8090 請(qǐng)求 localhost:3000/say)

nginx配置如下:

server {
    listen       8090;

    server_name  localhost;

    location / {
        root   /Users/liuyan35/Test/Study/CORS/1-jsonp;
        index  index.html index.htm;
    }
    location /say {
        rewrite  ^/say/(.*)$ /$1 break;
        proxy_pass   http://localhost:3000;
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Credentials' 'true';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
    }
  ## others
}復(fù)制代碼
  1. websocket

Websocket 是 HTML5 的一個(gè)持久化的協(xié)議骤素,它實(shí)現(xiàn)了瀏覽器與服務(wù)器的全雙工通信匙睹,同時(shí)也是跨域的一種解決方案。

Websocket 不受同源策略影響济竹,只要服務(wù)器端支持痕檬,無(wú)需任何配置就支持跨域。

前端頁(yè)面在 8080 的端口送浊。

let socket = new WebSocket('ws://localhost:3000'); //協(xié)議是ws
socket.onopen = function() {
    socket.send('Hi,你好');
}
socket.onmessage = function(e) {
    console.log(e.data)
}復(fù)制代碼

服務(wù)端 3000端口梦谜。可以看出websocket無(wú)需做跨域配置袭景。

let WebSocket = require('ws');
let wss = new WebSocket.Server({port: 3000});
wss.on('connection', function(ws) {
    ws.on('message', function(data) {
        console.log(data); //接受到頁(yè)面發(fā)來(lái)的消息'Hi,你好'
        ws.send('Hi'); //向頁(yè)面發(fā)送消息
    });
});復(fù)制代碼
  1. postMessage

postMessage 通過(guò)用作前端頁(yè)面之前的跨域唁桩,如父頁(yè)面與iframe頁(yè)面的跨域。window.postMessage方法耸棒,允許跨窗口通信荒澡,不論這兩個(gè)窗口是否同源。

話說(shuō)工作中兩個(gè)頁(yè)面之前需要通信的情況并不多与殃,我本人工作中单山,僅使用過(guò)兩次,一次是H5頁(yè)面中發(fā)送postMessage信息幅疼,ReactNative的webview中接收此此消息米奸,并作出相應(yīng)處理。另一次是可輪播的頁(yè)面爽篷,某個(gè)輪播頁(yè)使用的是iframe頁(yè)面悴晰,為了解決滑動(dòng)的事件沖突,iframe頁(yè)面中去監(jiān)聽(tīng)手勢(shì)狼忱,發(fā)送消息告訴父頁(yè)面是否左滑和右滑膨疏。

子頁(yè)面向父頁(yè)面發(fā)消息

父頁(yè)面

window.addEventListener('message', (e) => {
    this.props.movePage(e.data);
}, false);復(fù)制代碼

子頁(yè)面(iframe):

if(/*左滑*/) {
    window.parent && window.parent.postMessage(-1, '*')
}else if(/*右滑*/){
    window.parent && window.parent.postMessage(1, '*')
}復(fù)制代碼

父頁(yè)面向子頁(yè)面發(fā)消息

父頁(yè)面:

let iframe = document.querySelector('#iframe');
iframe.onload = function() {
    iframe.contentWindow.postMessage('hello', 'http://localhost:3002');
}復(fù)制代碼

子頁(yè)面:

window.addEventListener('message', function(e) {
    console.log(e.data);
    e.source.postMessage('Hi', e.origin); //回消息
});復(fù)制代碼
  1. node 中間件

node 中間件的跨域原理和nginx代理跨域,同源策略是瀏覽器的限制钻弄,服務(wù)端沒(méi)有同源策略佃却。

node中間件實(shí)現(xiàn)跨域的原理如下:

1.接受客戶端請(qǐng)求

2.將請(qǐng)求 轉(zhuǎn)發(fā)給服務(wù)器。

3.拿到服務(wù)器 響應(yīng) 數(shù)據(jù)窘俺。

4.將 響應(yīng) 轉(zhuǎn)發(fā)給客戶端饲帅。

不常用跨域方法

以下三種跨域方式很少用,如有興趣瘤泪,可自行查閱相關(guān)資料灶泵。

  1. window.name + iframe
  2. location.hash + iframe
  3. document.domain (主域需相同)

30 js異步加載的方式有哪些?

  1. <script> 的 defer 屬性对途,HTML4 中新增
  2. <script> 的 async 屬性赦邻,HTML5 中新增

<script>標(biāo)簽打開(kāi)defer屬性,腳本就會(huì)異步加載实檀。渲染引擎遇到這一行命令惶洲,就會(huì)開(kāi)始下載外部腳本按声,但不會(huì)等它下載和執(zhí)行,而是直接執(zhí)行后面的命令恬吕。

defer 和 async 的區(qū)別在于: defer要等到整個(gè)頁(yè)面在內(nèi)存中正常渲染結(jié)束签则,才會(huì)執(zhí)行;

async一旦下載完铐料,渲染引擎就會(huì)中斷渲染渐裂,執(zhí)行這個(gè)腳本以后,再繼續(xù)渲染钠惩。defer是“渲染完再執(zhí)行”柒凉,async是“下載完就執(zhí)行”。

如果有多個(gè) defer 腳本妻柒,會(huì)按照它們?cè)陧?yè)面出現(xiàn)的順序加載扛拨。

多個(gè)async腳本是不能保證加載順序的。

31.實(shí)現(xiàn)雙向綁定 Proxy 與 Object.defineProperty 相比優(yōu)劣如何?

  1. Object.definedProperty 的作用是劫持一個(gè)對(duì)象的屬性举塔,劫持屬性的getter和setter方法,在對(duì)象的屬性發(fā)生變化時(shí)進(jìn)行特定的操作求泰。而 Proxy 劫持的是整個(gè)對(duì)象央渣。
  2. Proxy 會(huì)返回一個(gè)代理對(duì)象,我們只需要操作新對(duì)象即可渴频,而 Object.defineProperty 只能遍歷對(duì)象屬性直接修改芽丹。
  3. Object.definedProperty 不支持?jǐn)?shù)組,更準(zhǔn)確的說(shuō)是不支持?jǐn)?shù)組的各種API卜朗,因?yàn)槿绻麅H僅考慮arry[i] = value 這種情況拔第,是可以劫持的,但是這種劫持意義不大场钉。而 Proxy 可以支持?jǐn)?shù)組的各種API蚊俺。
  4. 盡管 Object.defineProperty 有諸多缺陷,但是其兼容性要好于 Proxy.

PS: Vue2.x 使用 Object.defineProperty 實(shí)現(xiàn)數(shù)據(jù)雙向綁定逛万,V3.0 則使用了 Proxy.

//攔截器
let obj = {};
let temp = 'Yvette';
Object.defineProperty(obj, 'name', {
    get() {
        console.log("讀取成功");
        return temp
    },
    set(value) {
        console.log("設(shè)置成功");
        temp = value;
    }
});

obj.name = 'Chris';
console.log(obj.name);復(fù)制代碼

PS: Object.defineProperty 定義出來(lái)的屬性泳猬,默認(rèn)是不可枚舉,不可更改棕兼,不可配置【無(wú)法delete】

我們可以看到 Proxy 會(huì)劫持整個(gè)對(duì)象曲掰,讀取對(duì)象中的屬性或者是修改屬性值汽久,那么就會(huì)被劫持。但是有點(diǎn)需要注意忙上,復(fù)雜數(shù)據(jù)類型,監(jiān)控的是引用地址闲坎,而不是值疫粥,如果引用地址沒(méi)有改變洋腮,那么不會(huì)觸發(fā)set。

let obj = {name: 'Yvette', hobbits: ['travel', 'reading'], info: {
    age: 20,
    job: 'engineer'
}};
let p = new Proxy(obj, {
    get(target, key) { //第三個(gè)參數(shù)是 proxy手形, 一般不使用
        console.log('讀取成功');
        return Reflect.get(target, key);
    },
    set(target, key, value) {
        if(key === 'length') return true; //如果是數(shù)組長(zhǎng)度的變化啥供,返回。
        console.log('設(shè)置成功');
        return Reflect.set([target, key, value]);
    }
});
p.name = 20; //設(shè)置成功
p.age = 20; //設(shè)置成功; 不需要事先定義此屬性
p.hobbits.push('photography'); //讀取成功;注意不會(huì)觸發(fā)設(shè)置成功
p.info.age = 18; //讀取成功;不會(huì)觸發(fā)設(shè)置成功復(fù)制代碼

最后库糠,我們?cè)倏聪聦?duì)于數(shù)組的劫持伙狐,Object.definedProperty 和 Proxy 的差別

Object.definedProperty 可以將數(shù)組的索引作為屬性進(jìn)行劫持,但是僅支持直接對(duì) arry[i] 進(jìn)行操作瞬欧,不支持?jǐn)?shù)組的API贷屎,非常雞肋。

let arry = []
Object.defineProperty(arry, '0', {
    get() {
        console.log("讀取成功");
        return temp
    },
    set(value) {
        console.log("設(shè)置成功");
        temp = value;
    }
});

arry[0] = 10; //觸發(fā)設(shè)置成功
arry.push(10); //不能被劫持復(fù)制代碼

Proxy 可以監(jiān)聽(tīng)到數(shù)組的變化艘虎,支持各種API唉侄。注意數(shù)組的變化觸發(fā)get和set可能不止一次,如有需要野建,自行根據(jù)key值決定是否要進(jìn)行處理属划。

let hobbits = ['travel', 'reading'];
let p = new Proxy(hobbits, {
    get(target, key) {
        // if(key === 'length') return true; //如果是數(shù)組長(zhǎng)度的變化,返回候生。
        console.log('讀取成功');
        return Reflect.get(target, key);
    },
    set(target, key, value) {
        // if(key === 'length') return true; //如果是數(shù)組長(zhǎng)度的變化同眯,返回。
        console.log('設(shè)置成功');
        return Reflect.set([target, key, value]);
    }
});
p.splice(0,1) //觸發(fā)get和set唯鸭,可以被劫持
p.push('photography');//觸發(fā)get和set
p.slice(1); //觸發(fā)get须蜗;因?yàn)?slice 是不會(huì)修改原數(shù)組的

32.Object.is() 與比較操作符 ===、== 有什么區(qū)別目溉?

以下情況明肮,Object.is認(rèn)為是相等

兩個(gè)值都是 undefined
兩個(gè)值都是 null
兩個(gè)值都是 true 或者都是 false
兩個(gè)值是由相同個(gè)數(shù)的字符按照相同的順序組成的字符串
兩個(gè)值指向同一個(gè)對(duì)象
兩個(gè)值都是數(shù)字并且
都是正零 +0
都是負(fù)零 -0
都是 NaN
都是除零和 NaN 外的其它同一個(gè)數(shù)字復(fù)制代碼

Object.is() 類似于 ===,但是有一些細(xì)微差別缭付,如下:

  1. NaN 和 NaN 相等
  2. -0 和 +0 不相等
console.log(Object.is(NaN, NaN));//true
console.log(NaN === NaN);//false
console.log(Object.is(-0, +0)); //false
console.log(-0 === +0); //true復(fù)制代碼

Object.is 和 ==差得遠(yuǎn)了柿估, == 在類型不同時(shí),需要進(jìn)行類型轉(zhuǎn)換

33.toStringString的區(qū)別

  • toString
  1. toString()可以將數(shù)據(jù)都轉(zhuǎn)為字符串蛉腌,但是nullundefined不可以轉(zhuǎn)換官份。

    console.log(null.toString())
    //報(bào)錯(cuò) TypeError: Cannot read property 'toString' of null
    
    console.log(undefined.toString())
    //報(bào)錯(cuò) TypeError: Cannot read property 'toString' of undefined
    復(fù)制代碼
    
  2. toString()括號(hào)中可以寫數(shù)字,代表進(jìn)制

    二進(jìn)制:.toString(2);

    八進(jìn)制:.toString(8);

    十進(jìn)制:.toString(10);

    十六進(jìn)制:.toString(16);

  • String
  1. String()可以將nullundefined轉(zhuǎn)換為字符串烙丛,但是沒(méi)法轉(zhuǎn)進(jìn)制字符串

    console.log(String(null));
    // null
    console.log(String(undefined));
    // undefined
    

34.TCP的三次握手和四次揮手

三次握手

  • 第一次握手:客戶端發(fā)送一個(gè)SYN碼給服務(wù)器舅巷,要求建立數(shù)據(jù)連接;
  • 第二次握手: 服務(wù)器SYN和自己處理一個(gè)SYN(標(biāo)志)河咽;叫SYN+ACK(確認(rèn)包)钠右;發(fā)送給客戶端,可以建立連接
  • 第三次握手: 客戶端再次發(fā)送ACK向服務(wù)器忘蟹,服務(wù)器驗(yàn)證ACK沒(méi)有問(wèn)題飒房,則建立起連接搁凸;

四次揮手

  • 第一次揮手: 客戶端發(fā)送FIN(結(jié)束)報(bào)文,通知服務(wù)器數(shù)據(jù)已經(jīng)傳輸完畢狠毯;
  • 第二次揮手: 服務(wù)器接收到之后护糖,通知客戶端我收到了SYN,發(fā)送ACK(確認(rèn))給客戶端,數(shù)據(jù)還沒(méi)有傳輸完成
  • 第三次揮手: 服務(wù)器已經(jīng)傳輸完畢嚼松,再次發(fā)送FIN通知客戶端嫡良,數(shù)據(jù)已經(jīng)傳輸完畢
  • 第四次揮手: 客戶端再次發(fā)送ACK,進(jìn)入TIME_WAIT狀態(tài);服務(wù)器和客戶端關(guān)閉連接献酗;

35.為什么建立連接是三次握手寝受,而斷開(kāi)連接是四次揮手呢?

建立連接的時(shí)候, 服務(wù)器在LISTEN狀態(tài)下罕偎,收到建立連接請(qǐng)求的SYN報(bào)文后很澄,把ACK和SYN放在一個(gè)報(bào)文里發(fā)送給客戶端。 而關(guān)閉連接時(shí)颜及,服務(wù)器收到對(duì)方的FIN報(bào)文時(shí)甩苛,僅僅表示對(duì)方不再發(fā)送數(shù)據(jù)了但是還能接收數(shù)據(jù),而自己也未必全部數(shù)據(jù)都發(fā)送給對(duì)方了器予,所以己方可以立即關(guān)閉浪藻,也可以發(fā)送一些數(shù)據(jù)給對(duì)方后,再發(fā)送FIN報(bào)文給對(duì)方來(lái)表示同意現(xiàn)在關(guān)閉連接乾翔,因此,己方ACK和FIN一般都會(huì)分開(kāi)發(fā)送施戴,從而導(dǎo)致多了一次反浓。

36.map和forEach的區(qū)別

相同點(diǎn)

  • 都是循環(huán)遍歷數(shù)組中的每一項(xiàng) forEach和map方法里每次執(zhí)行匿名函數(shù)都支持3個(gè)參數(shù),參數(shù)分別是item(當(dāng)前每一項(xiàng))赞哗、index(索引值)雷则、arr(原數(shù)組),需要用哪個(gè)的時(shí)候就寫哪個(gè) 匿名函數(shù)中的this都是指向window 只能遍歷數(shù)組

不同點(diǎn)

  • map方法返回一個(gè)新的數(shù)組肪笋,數(shù)組中的元素為原始數(shù)組調(diào)用函數(shù)處理后的值月劈。(原數(shù)組進(jìn)行處理之后對(duì)應(yīng)的一個(gè)新的數(shù)組。) map()方法不會(huì)改變?cè)紨?shù)組 map()方法不會(huì)對(duì)空數(shù)組進(jìn)行檢測(cè) forEach()方法用于調(diào)用數(shù)組的每個(gè)元素藤乙,將元素傳給回調(diào)函數(shù).(沒(méi)有return猜揪,返回值是undefined)

注意:forEach對(duì)于空數(shù)組是不會(huì)調(diào)用回調(diào)函數(shù)的。

37.setTimeout 和 setInterval的機(jī)制

因?yàn)閖s是單線程的坛梁。瀏覽器遇到etTimeout 和 setInterval會(huì)先執(zhí)行完當(dāng)前的代碼塊而姐,在此之前會(huì)把定時(shí)器推入瀏覽器的待執(zhí)行時(shí)間隊(duì)列里面,等到瀏覽器執(zhí)行完當(dāng)前代碼之后會(huì)看下事件隊(duì)列里有沒(méi)有任務(wù)划咐,有的話才執(zhí)行定時(shí)器里的代碼

38.JS中常見(jiàn)的異步任務(wù)

定時(shí)器拴念、ajax钧萍、事件綁定、回調(diào)函數(shù)政鼠、async await风瘦、promise

39.瀏覽器渲染的主要流程是什么?

將html代碼按照深度優(yōu)先遍歷來(lái)生成DOM樹(shù)。 css文件下載完后也會(huì)進(jìn)行渲染公般,生成相應(yīng)的CSSOM万搔。 當(dāng)所有的css文件下載完且所有的CSSOM構(gòu)建結(jié)束后,就會(huì)和DOM一起生成Render Tree俐载。 接下來(lái)蟹略,瀏覽器就會(huì)進(jìn)入Layout環(huán)節(jié),將所有的節(jié)點(diǎn)位置計(jì)算出來(lái)遏佣。 最后挖炬,通過(guò)Painting環(huán)節(jié)將所有的節(jié)點(diǎn)內(nèi)容呈現(xiàn)到屏幕上。

40.ajax中g(shù)et和post請(qǐng)求的區(qū)別

get 一般用于獲取數(shù)據(jù)

get請(qǐng)求如果需要傳遞參數(shù)状婶,那么會(huì)默認(rèn)將參數(shù)拼接到url的后面意敛;然后發(fā)送給服務(wù)器;

get請(qǐng)求傳遞參數(shù)大小是有限制的膛虫;是瀏覽器的地址欄有大小限制草姻;

get安全性較低

get 一般會(huì)走緩存,為了防止走緩存稍刀,給url后面每次拼的參數(shù)不同撩独;放在?后面,一般用個(gè)時(shí)間戳

post 一般用于發(fā)送數(shù)據(jù)

post傳遞參數(shù)账月,需要把參數(shù)放進(jìn)請(qǐng)求體中综膀,發(fā)送給服務(wù)器;

post請(qǐng)求參數(shù)放進(jìn)了請(qǐng)求體中局齿,對(duì)大小沒(méi)有要求剧劝;

post安全性比較高;

post請(qǐng)求不會(huì)走緩存抓歼;

42.DOM diff原理

  • 如果元素類型發(fā)生變化讥此,直接替換
  • 如果是文本,則比較文本里面的內(nèi)容谣妻,是否有差異萄喳,如果是元素就需要比較當(dāng)前元素的屬性是否相等,會(huì)先比較key, 在比較類型 為什么 react中循環(huán) 建議不要使用索引 ,如果純?yōu)榱苏故?那可以使用索引

43.動(dòng)手實(shí)現(xiàn)一個(gè)bind(原理通過(guò)apply拌禾,call)

一句話概括:1.bind()返回一個(gè)新函數(shù)取胎,并不會(huì)立即執(zhí)行。
        2.bind的第一個(gè)參數(shù)將作為他運(yùn)行時(shí)的this,之后的一系列參數(shù)將會(huì)在傳遞的實(shí)參前傳入作為他的參數(shù)
        3.bind返回函數(shù)作為構(gòu)造函數(shù)闻蛀,就是可以new的匪傍,bind時(shí)指定的this值就會(huì)消失,但傳入的參數(shù)依然生效
Function.prototype.bind = function (obj, arg) {
   var arg = Array.prototype.slice.call(arguments, 1);
   var context = this;
   var bound = function (newArg) {
   arg = arg.concat(Array.prototype.slice.call(newArg);
   return context.apply(obj, arg)
}
  var F =  function () {}  // 在new一個(gè)bind會(huì)生成新函數(shù)觉痛,必須的條件就是要繼承原函數(shù)的原型役衡,因此用到寄生繼承來(lái)完成我們的過(guò)程
  F.prototype = context.prototype;
  bound.prototype =  new F();
  return bound;
}   
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市薪棒,隨后出現(xiàn)的幾起案子手蝎,更是在濱河造成了極大的恐慌,老刑警劉巖俐芯,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棵介,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡吧史,警方通過(guò)查閱死者的電腦和手機(jī)邮辽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)贸营,“玉大人吨述,你說(shuō)我怎么就攤上這事〕” “怎么了揣云?”我有些...
    開(kāi)封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)冰啃。 經(jīng)常有香客問(wèn)我邓夕,道長(zhǎng),這世上最難降的妖魔是什么阎毅? 我笑而不...
    開(kāi)封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任翎迁,我火速辦了婚禮,結(jié)果婚禮上净薛,老公的妹妹穿的比我還像新娘。我一直安慰自己蒲拉,他們只是感情好肃拜,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著雌团,像睡著了一般燃领。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上锦援,一...
    開(kāi)封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天猛蔽,我揣著相機(jī)與錄音,去河邊找鬼。 笑死曼库,一個(gè)胖子當(dāng)著我的面吹牛区岗,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播毁枯,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼慈缔,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了种玛?” 一聲冷哼從身側(cè)響起藐鹤,我...
    開(kāi)封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎赂韵,沒(méi)想到半個(gè)月后娱节,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡祭示,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年肄满,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绍移。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡悄窃,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蹂窖,到底是詐尸還是另有隱情轧抗,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布瞬测,位于F島的核電站横媚,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏月趟。R本人自食惡果不足惜灯蝴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望孝宗。 院中可真熱鬧穷躁,春花似錦、人聲如沸因妇。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)婚被。三九已至狡忙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間址芯,已是汗流浹背灾茁。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工窜觉, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人北专。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓禀挫,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親逗余。 傳聞我的和親對(duì)象是個(gè)殘疾皇子特咆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,089評(píng)論 1 32
  • 本文中講述到的面試題: 說(shuō)說(shuō)對(duì)閉包的認(rèn)識(shí), 它解決了什么問(wèn)題?跨域問(wèn)題有哪些處理方式?for...in 和 for...
    寫代碼的胖猴子閱讀 1,100評(píng)論 0 5
  • 面試題一:https://github.com/jimuyouyou/node-interview-questio...
    R_X閱讀 1,611評(píng)論 0 5
  • PNG 有PNG8和truecolor PNG PNG8類似GIF顏色上限為256,文件小录粱,支持alpha透明度腻格,...
    hudaren閱讀 1,502評(píng)論 0 0
  • 【轉(zhuǎn)載】CSDN - 張林blog http://blog.csdn.net/XIAOZHUXMEN/articl...
    竿牘閱讀 3,481評(píng)論 1 14