(超詳細版)2019北京面試題————JavaScript

一、JS判斷數據類型的方法

① typeof:基本數據類型沒有問題,引用數據類型有問題。
當變量是:number, string, boolean, function, undefined, object類型時,可使用typeof進行判斷。
當變量是arr, json, null, date, reg, error 類型時所有被錯誤的檢測爲object類型。
② instanceof:基本數據類型會有問題,而引用數據類型沒有問題。

//基本數據類型
console.log("1" instanceof String); //false
console.log(1 instanceof Number);  //false
console.log(true instanceof Boolean);  //false
//引用數據類型
console.log([] instanceof Array);  //true
console.log(function(){} instanceof Function);  //true
console.log({} instanceof Object);  //true複製代碼
③ constructor:除了undefined和null,其它變量都能使用constructor判斷類型。

console.log(("1").constructor === String);  //true
console.log((1).constructor === Number);  //true
console.log((true).constructor === Boolean);  //true
console.log(([]).constructor === Array);  //true
console.log((function(){}).constructor === Function);  //true
console.log(({}).constructor === Object);  //true
console.log((null).constructor === Null);   //報錯
console.log((undefined).constructor === Undefined);  //報錯複製代碼

聲明瞭一個構造函數,而且把他的原型指向了Array的原型。

function Fn(){};
Fn.prototype=new Array(); 
var f=new Fn();
console.log(f.constructor===Fn);  //false
console.log(f.constructor===Array);  //true複製代碼

緣由:
一、array屬於引用型數據,在傳遞過程當中,僅僅是引用地址的傳遞。
二、每一個頁面的Array原生對象所引用的地址是不同的,在子頁面聲明的array,所對應的構造函數,是子頁面的Array對象;父頁面來進行判斷,使用的Array並不等於子頁面的Array;切記,否則很難跟蹤問題!
④ Object.prototype.toString.call();

console.log(Object.prototype.toString.call(1));  //[object Number]
console.log(Object.prototype.toString.call("1"));  //[object String]
console.log(Object.prototype.toString.call(true));  //[object Boolean]
console.log(Object.prototype.toString.call([]));  //[object Array]
console.log(Object.prototype.toString.call(function(){})); //[object Function]
console.log(Object.prototype.toString.call({}));  //[object Object]
console.log(Object.prototype.toString.call(null));  //[object Null]
console.log(Object.prototype.toString.call(undefined));  //[object Undefined]複製代碼

⑤ jquery中的$.type()

二、定義函數的方式:

/第一種
function myFunction(){
    
}
//定義函數的字符串,函數名本質是變量名,指向某個function的引用。
console.log(myFunction);
//function
console.log(typeof myFunction)

//第二種
var myFunction = new Function(
    "num1"
    "num2"
    "var sum = num1 + num2;return sum"
)
console.log(myFunction(10,20))複製代碼

三、數組排序:

var arr = [1,36,52,23,48,96,5];
//第一種:
function arrSort(a,b){
    return a - b;
}
console.log(arr.sort(arrSort));

//第二種:冒泡排序
//思想:讓數組當中相鄰的兩個數進行比較,數組當中比較小的數值向下沉,數值比較大的向上浮!
//      外層for循環控制循環次數,內層for循環控制相鄰的兩個元素進行比較。
function bubbleSort(arr){
    for(var i = 0;i < arr.length-1;i++){
        for(var j = 0;j < arr.length-1-i;j++){
            if(arr[j] > arr[j+1]){
                swap(arr,j,j+1)
            }
        }
    }
    return arr;
}
function swap(arr,i,j){
    var temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}
console.log(bubbleSort(arr));

//第三種:選擇排序
//思想:以起始元素爲基準,再從剩餘的未排序的元素中挨個與起始元素進行比較,找到剩餘未排序元素中
//     最小的一個與之交換位置。重複此步驟。
function selectSort(arr){
    for(var i = 0;i < arr.length-1;i++){
        for(var j = i+1;j < arr.length;j++){
            if(arr[i] > arr[j]){
                awap(arr,i,j)
            }
        }
    }
    return arr;
}
function swap(arr,i,j){
    var temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}
console.log(selectSort(arr))

//第四種:選擇排序2
function quickSort(arr){
    for(var i = 0;i < arr.length-1;i++){
        var num = arr[i];
        var index = i;
        for(var j = i+1;j < arr.length;j++){
            if(num > arr[j]){
                num = arr[j];
                index = j;
            }
         }
         if(index != i){
             swap(arr,i,index);
         }
      }
      return arr;
  }
  function swap(arr,i,j){
      var temp = arr[i];
      arr[i] = arr[j];
      arr[j] = temp;
  }
  var arr = [23,56,4,89,556,114,1];
  console.log(quickSort(arr));                      

//第五種:快速排序
//一、從數組中取出一個數做爲基準。在原數組中進行移動,將大於基準的數放到基準的右邊,小於基準的數放到
//    基準左邊,在基準左右造成兩個子數組。在左右子數組中反覆執行步驟一、2。直到全部子數組只剩下一個數。
function quickSort(arr,i,j){
    if(i < j){
        let left = i;
        let right = j;
        let pivot = arr[left];
        while(i < j){
            while(arr[j] >= pivot && i < j){
                j--;
            }
            if(i < j){
                arr[i++] = arr[j];
            }
            while(arr[i] <= pivot && i < j){
                i++;
            }
            if(i < j){
                arr[j--] = arr[i]
            }
        }
        arr[i] = pivot;
        quickSort(arr,left,i-1);
        quickSort(arr,i+1,right);
        return arr;
    }
}
let arr = [23,56,4,89,556,114,1];
console.log(quickSort(arr,0,arr.length-1));

//快速排序(for循環)
function quickSort(arr){
    //若是數組的長度小於等於1,則直接返回這個數組
    if(arr.length <= 1){
        return arr;
    }
    //選擇基準數(四捨五入)
    var pivotIndex = Math.floor(arr.length/2);
    //將基準數與原數組分離
    var pivot = arr.splice(pivotIndex,1)[0];
    var left = [];
    var right = [];
    for(var i = 0;i < arr.length;i++){
        if(arr[i] <= pivot){
            left.push(arr[i]);
        }else{
            right.push(arr[i]);
        }
    }
    return quickSort(left).concat(pivot,quickSort(right));
}
let arr = [23,56,4,89,556,114,1];
console.log(quickSort(arr));複製代碼

四、深拷貝和淺拷貝?

淺拷貝:淺拷貝是會將對象的每一個屬性進行依次複製,可是當對象的屬性值是引用類型時,實質複製的是其引用,當引用指向的值改變時也會跟着變化。
深拷貝:深拷貝複製變量值,對於非基本類型的變量,則遞歸至基本類型變量後,再複製。 深拷貝後的對象與原來的對象是徹底隔離的,互不影響,對一個對象的修改並不會影響另外一個對象。

深拷貝和淺拷貝是針對複雜數據類型來講的,淺拷貝只拷貝一層,而深拷貝是層層拷貝;
javascript

淺拷貝(只能拷貝一層):Object.assign和for in進行{ }和[ ]的拷貝。

//拷貝1層(測試)------------------------------------------------------------
//Object.assign()拷貝方法
    //拷貝{}
        a = {name:"張三"};
        b = Object.assign({},a);
        b.name = "李四";    //拷貝完以後進行改變屬性,測試拷貝的屬性值改變原數據是否改變。
        console.log(a,b);
        //輸出:{name:"張三"} 
               {name:"李四"} (拷貝成功)
    //拷貝[]
        a = ["1","2","3"];
        b = Object.assign([],a);
        b[1]="hello";
        console.log(a,b)
        //輸出:["1","2","3"] 
               ["1","hello","3"] (拷貝成功)

//for in拷貝方法
    var copy = function(a){
        var res = a.constructor();
        for(var key in a){
            if(a.hasOwnProperty(key)){
                res[key] = a[key]
            }
        }
        return res;
    }   
    a = {name:"123",people:{name:"456"}};
    b = copy(a);
    b.people.name="hello"; 
    a = ["a","b","c"];b = copy(a);
    b[1] = 0;//拷貝完以後進行改變屬性,測試拷貝的屬性值改變原數據是否改變。
    console.log(a,b)  
    //輸出:["a","b","c"] 
           ["a","0","c"] (拷貝成功)
//拷貝2層(測試)-------------------------------------------------------------
    //Object.assign()拷貝方法
        a = {name:"abc",people:{name:"張三"}};
        b = Object.assign({},a);
        b.people.name="李四"; 
        console.log(a,b)    
        //輸出:{name:"abc",people:{name:"李四"}} 
               {name:"abc",people:{name:"李四"}} (拷貝失敗)    //for in拷貝方法
         var copy = function(a){
             var res = a.constructor();
             console.log(res);
             for(var key in a){
                 if(a.hasOwnProperty(key)){
                    res[key] = a[key]
                 }
             }
         return res;
        }       
        a = ["a","b",{name:"張三"}];b = copy(a);b[2].name="李四";
        console.log(a,b)   
        //輸出:{name:"abc",people:{name:"李四"}} 
               {name:"abc",people:{name:"李四"}} (拷貝失敗)
複製代碼

constructor( ) 是一種用於建立和初始化class建立的對象的特殊方法。
hasOwnProperty( ) 方法會返回一個布爾值,指示對象自身屬性中是否具備指定的屬性(也就是,是否有指定的鍵);
深拷貝最簡單的實現是:JSON.parse ( JSON.stringify ( obj ) ),同時有必定的缺陷:
① 對象的屬性值是函數時,沒法拷貝
② 原型鏈上的屬性沒法拷貝
③ 不能正確的處理Data類型的數據
④ 不能處理RegExp
⑤ 會忽略symbol、undefined

//JSON.parse(JSON.stringify(obj))方法拷貝2層
    var deepCopy = function(a){
        return JSON.parse(JSON.stringify(a));
    }
    var a = {name:"aaa",people:{name:"abc"}};
    var b = deepCopy(a);
    b.people.name = "def";
    console.log(a,b)   
    //輸出:{name:"aaa",people:{name:"abc"}} 
           {name:"aaa",people:{name:"def"}} (拷貝成功)//JSON.parse(JSON.stringify(obj))方法拷貝3層
    var deepCopy = function(a){
        return JSON.parse(JSON.stringify(a))
    }
    var a = [1,2,{name:"aaa"}];
    var b = deepCopy(a);
    b[2].name = "bbb";
    console.log(a,b);   
    //輸出:["1","2",{name:"aaa"}]
           ["1","2",{name:"bbb"}] (拷貝成功)
//JSON.parse(JSON.stringify(obj))拷貝函數的時候
    var deepCopy = function(a){
        return JSON.parse(JSON.stringify(a));
    }
    var a = {name:"aaa",fun:function(){console.log(1)},age:undefined};
    var b = deep(a);
    b.name = "bbb"
    console.log(a,b);   
    //輸出:{name:"aaa",fun:function(){console.log(1)},age:undefined};
           {name:"bbb"} (拷貝失敗,只拷貝到了name屬性)  
//JSON.parse(JSON.stringify(obj))拷貝原型鏈上的屬性
    function Person(name){
        this.name=name;
    }
    var a = new Person("Bob");
    var b = deep(a);
    console.log(a.constructor == Person);   //true
    console.log(b.constructor == Object);   //true
    //先不說拷貝出的值,光是數據類型已經不一樣了 (拷貝失敗)
    console.log(a,b)    
    //輸出:
    // Person{name:"Bob"} {name:"Bob"}複製代碼

注意:
上述方法會忽略值爲function以及undefined的字段,並且對data類型的支持也不太友好。
上述方法只能克隆原始對象自身的值,不能克隆他繼承的值。

深拷貝(完美拷貝):css

① 若是是基本數據類型,直接返回;
② 若是是RegExp或者Data類型,返回對應類型;
③ 若是是複雜數據類型,遞歸;
④ 考慮循環引用的問題。

function deepClone(obj,hash = new WeakMap()){   //遞歸拷貝
     if(obj instanceof RegExp) return new RegExp(obj);
     if(obj instanceof Date) return new Date(obj);
     if(obj === null || typeof obj !== 'object'){
         //若是不是複雜數據類型,直接返回
        return obj;
     }
     if(hash.has(obj)){
        return hash.get(obj);
     }
     //若是obj是數組,那麼 obj.constructor 是 [Function: Array]
     //若是obj是對象,那麼 obj.constructor 是 [Function: Object]
    let t = new obj.constructor();
    hash.set(obj,t);
    for(let key in obj){
        //遞歸
        if(obj.hasOwnProperty(key)){    //是不是自身的屬性
            t[key] = deepClone(obj[key],hash);
        } 
     }
     return t;
}   
var show = {
    name:"Bob",
    fun:function(){console.log(1)},
    age:null,
    pic:undefined,
}
var show2 = deepClone(show);
show2.name="Mary"
console.log(show,show2)   //拷貝成功,完美拷貝 複製代碼

淺拷貝和深拷貝的區別:
淺拷貝只能複製對象或數組的第一層屬性,而深拷貝是拷貝多層,每一級的數據都會被拷貝出來。

五、跨域(怎麼解決跨域問題)(跨域是什麼)(爲何會有跨域):

       形成跨域的緣由就是瀏覽器的同源策略:只要知足 協議主機端口一致,則兩個頁面具備相同的源。同源策略限制了從同一個源加載的文檔或腳本如何來自另外一個源的資源進行交互,這是一個用於隔離潛在惡意文件的重要安全機制。(直白:我在一個域名地址下的網頁,若是請求一個受到同源策略限制的地址接口的時候,就會報錯,這是爲了在個人網頁下請求別的地址的文件多是惡意代碼,形成沒必要要的問題。)
解決方法:
      ① jsonp,容許script加載第三方資源。jsonp是一種非正式的傳輸協議,該協議的一個要點就是容許用戶傳遞一個callback函數給服務端,而後服務端返回數據的時候會將json數據包裹在這個callback函數中返回。jsonp的本質是利用script標籤的src熟悉進行跨域請求,但只能用於get請求。
      ② 反向代理 (nginx 服務內部配置 Access-Control-Allow-Origin *);
      ③ cors 先後端協做設置請求頭部,Access-Control-Allow-Origin 等頭部信息
      ④ iframe 嵌套通信 (能夠與下面的postmessage一塊兒使用)
      ⑤ window.postmessage ( ),該方法的使用須與iframe嵌套使用。

六、爲何會有同源策略?

JavaScript訪問資源,處於安全方面考慮,要限制JS的訪問能力,不容許跨域訪問資源。若是沒有同源限制存在瀏覽器中的cookie等其餘數據能夠任意讀取,不一樣域下DOM任意操做,ajax任意請求的話若是瀏覽了惡意網站那麼就會泄漏這些隱私數據。
(再次強調)同源策略:同協議、同域名、同端口。

七、原型、原型鏈、構造函數、實例、繼承?

原型(__proto__):每一個對象都有__proto__屬性,__proto__指向建立他的構造函數的原型對象(prototype)。
原型鏈:凡是對象都有一個原型,經過__proto__能夠訪問原型,訪問的原型又是對象,這樣依次下去,就會構成一個對象的序列,該結構成爲原型鏈。
(簡單明瞭說法:一個原型對象是另外一個原型對象的實例,相關的原型對象層層遞進,就構成了實例與原型的鏈條,就是原型鏈。當訪問一個對象的某個屬性時,會先在這個對象自己屬性上查找,若是沒有找到,則會去它的__proto__隱式原型上查找,即它的構造函數的prototype,若是尚未找到就會再在構造函數的prototype的__proto__中查找,這樣一層一層向上查找就會造成一個鏈式結構,咱們稱爲原型鏈,固然若是最終找不到返回null。)
構造函數:
① 構造函數的首字母必須大寫,用來區分於普通函數;
② 內部使用的this對象,來指向即將要生成的實例對象;
③ 使用new來生成實例對象。

繼承:強烈推薦各位去這個網站 github.com/YvetteLau/B…html

八、arguments的解釋?

       arguments是一個相似於數組的對象,對應於傳遞給函數的參數,他有length屬性,能夠arguments[ i ]來訪問對象中的元素,可是它不能用數組的一些方法。例如push、pop、slice等。arguments雖然不是一個數組,可是它能夠轉成一個真正的數組。

function argText(a,b,c){
    var actual = arguments.length;   //實際傳參個數
    var hope = argText.length   //指望傳參個數
    console.log(actual,hope);
    //轉換數組:
    var args = [].slice.call(arguments);   //第一種
    var args = Array.prototype.slice.call(arguments);   //第二種
    let args = Array.from(arguments);   //第三種
    let args = [...arguments];   //第四種
    console.log(args)
}
argText(1,2)
//輸出: 2 3   複製代碼

        每個函數都會有一個Arguments對象實例arguments, 它引用着函數的實參,能夠用數組下標的方式" [ ] "引用arguments的元素。arguments.length爲函數實參個數,arguments.callee引用函數自身。
       arguments對象是全部函數中可用的局部變量,你可使用arguments對象在函數中引用函數的參數,此參數包含傳遞給函數的每一個參數條目。
arguments.callee:Arguments的callee屬性能夠調用函數自己,當函數正在執行時纔可調用,能夠實現方法的遞歸調用。

function argText(){
    var e = arguments.callee.toString();
    console.log(e);
}
argText(); 複製代碼

arguments.caller:指向調用當前函數的函數

function argText(){
    if(argText.caller){
        var caller = argText.caller.toString();
        console.log(caller);
    }else{
        console.log("no caller");
    }  
}
function handler(){
    argText();
}
function copyHandler(){
    handler();
}
argText()
//輸出: no caller
handler()
//輸出: function handler(){argText();}
copyHandler();     
//輸出: function handler(){argText();}   複製代碼

arguments方法重載(能夠了解): blog.xieliqun.com/2016/08/14/…

九、做用域鏈、閉包、做用域;

⑴ 做用域鏈
       定義:一個函數在訪問變量的時候,優先使用本身的局部變量,若是沒有這個變量的申明,則向上級訪問,一直訪問到全局。全局都沒有的話,語法錯誤:is not defined。
⑵閉包closure
       定義:當一個函數的返回值是另一個函數,而返回的那個函數若是調用了其父函數的內部變量,且返回的那個函數在外部被執行,就產生了閉包.閉包是一個環境,具體指的就是外部函數--高階函數
        閉包的特性:
            ①函數嵌套函數;
            ②內部函數能夠直接訪問外部函數的內部變量或參數;
            ③變量或參數不會被垃圾回收機制回收。
        閉包的優勢:
            ①變量長期駐紮在內存中;
            ②避免全局變量的污染;
            ③私有成員的存在。
        閉包的缺點: 常駐內存,增大內存的使用量,使用不當會形成內存泄漏。
⑶做用域:
       全局做用域:window。
       局部做用域:函數內部定義的。

//使用閉包找到dome元素的下標
var oLi = document.getElementsByTagName("li");
// 使用閉包後點擊對應的li標籤,會返回對應的下標
for(var i = 0;i < oLi.length;i++){ 
    //閉包方式一
    (function(j){
        oLi[j].onclick = function(){
            console.log(j)
        }
    })(i);
    //閉包方式二
    oLi[i].onclick = function(index){
        return function(){
            console.log(index);
        }
    }(i);
    //不使用閉包的狀況下,輸出值所有爲(length+1)
    oLi[i].onclick = function(){
        console.log(i);
    }
}
複製代碼

十、ES3~5:

ES3~5數組常見的方法:
一、concat( ):數組合並。
二、join( ):數組轉字符串。
三、pop( ):刪除最後一個元素。
四、push( ):數組向後添加。
五、unshift( ):數組向前添加。
六、reverse( ):數組翻轉。
七、shift( ):刪除第一個元素。
八、slice( ):數組元素的截取,返回一個新數組,新數組是截取的元素,能夠爲負值。
九、sort( ):對數組元素進行排序;
十、splice( ):刪除元素,並向數組添加新元素;
十一、toString( ):數組轉字符串;
十二、toLocaleString( ):將數組轉換爲本地數組。
1三、forEach( ):數組進行遍歷;
1四、map( ):沒有return時,對數組的遍歷。有return時,返回一個新數組,該新數組的元素是通過過濾(邏輯處理)過的函數。
1五、filter( ):篩選。
1六、every( ):當數組中每個元素在callback上被返回true時就返回true。(注:every其實相似filter,只不過它的功能是判斷是否是數組中的全部元素都符合條件,而且返回的是布爾值)。
1七、some( ):當數組中有一個元素在callback上被返回true時就返回true。(注:every其實相似filter,只不過它的功能是判斷是否是數組中的全部元素都符合條件,而且返回的是布爾值)。
1八、reduce( ):回調函數中有4個參數。prev(以前計算過的值),next(以前計算過的下一個的值),index,arr。把數組列表計算成一個單一值 。

//一、every()
var arr = [1,56,80,5];
var main = arr.every(n => n > 0);
console.log(main)   //輸出:true

//二、some()
var arr = [1,-56,80,-5];
var main = arr.some(n => n > 0);
console.log(main)    //輸出:true

//三、reducer()
var arr = [10,20,30,40]
let result = arr.reduce(function(prev,next,index,arr){
    return prev + next;
})
console.log(result);  //輸出:100複製代碼

ES3~5字符串常見的方法:

一、chartAt( ):返回在指定位置的字符;
二、concat( ):字符串鏈接;
三、indexOf( ):檢索字符串,找不到返回-1;
四、slice( ):提取字符串片斷,並在新的字符串中返回被提取的部分;
五、split( ):字符串轉數組;
六、substr( ):從起始索引號提取字符串中指定數目的字符;
七、substring( ):提取字符串中兩個指定的索引號之間的字符;
八、toLowerCase( ):字符串轉小寫;
九、toUpperCase( ):字符串轉大寫;
十、valueOf( ):返回某個字符串對象的原始值; 
十一、trim( ):刪除字符串兩邊的空格;

十一、ES6

ES6數組的經常使用方法:
一、Array.from( ):將對象或字符串轉成數組,注意得有length。
二、Array.of( ): 將一組值轉換爲數組。
三、copyWithin(target,start(可選),end(可選)):數組內數據的複製替換
target:從該位置開始替換數據;
start:從該位置開始讀取數據,默認爲0;
end:到該位置中止數據的讀取,默認爲數組的長度
四、find( ):用於找出第一個符合條件的數組成員。
五、findIndex( ):返回第一個符合條件的數組成員的位置,若是全部成員都不符合條件,則返回-1。
六、fill(value,start,end):使用給定值,填充一個數組。
value:填充的值;
start:開始填充的位置;
end:填充結束的位置。
七、keys( ):對鍵名的遍歷。
八、values( ):對鍵值的遍歷。
九、entries( ):對鍵值對的遍歷。
十、includes( ):數組原型的方法,查找一個數值是否在數組中,只能判斷一些簡單類型的數據,對於複雜類型的數據沒法判斷。該方法接受兩個參數,分別是查詢的數據和初始的查詢索引值。
十一、flat( ):用於數組扁平,數組去除未定義。
十二、flatMap( ):對原數組的每一個成員執行一個函數。
1三、Map( ):是一組鍵值對的結構,具備極快的查找速度。
1四、Set( ):Set和Map相似,也是一組key的集合,但不存儲value。因爲key不能重複,因此,在Set中,沒有重複的key。

//一、Array.from()  --   Array.of()
    var  arrayLink = {
        "0":"a",
        "1":"b",
        "2":"c",
        length:3
    }
    var arr = Array.from(arrayLink)
    console.log(arr)   // 輸出: [a,b,c]
    console.log(Array.from("abcdefg"))  //輸出:["a", "b", "c", "d", "e", "f", "g"]
    console.log(Array.of(1,2,3,4,5))  //輸出: [1, 2, 3, 4, 5]

//二、copyWithin()
    var arr = [1,2,3,4,5];
    var main = arr.copyWithin(0,3);
    console.log(main);   //輸出:[4,5,3,4,5]

//三、find()
    var arr = [1,-5,2,9,-6];
    var main = arr.find(n =>  n < 0);
    console.log(main);   //輸出:-5

//四、fill()
    var arr = ["a","b","c","d"];
    console.log(arr.fill(7,1,2));//輸出:["a",7,"c","d"]  

//五、keys()  values()  entries()
    var arr = ["a","b","c","d"];
    for(let index of arr.keys()){
	    console.log(index);
    }
    for(let elem of arr.values()){
	    console.log(elem);
    }
    for(let [index,elem] of arr.entries()){
	    console.log(index,elem);
    }  

//六、includes()
    let arr = [12,34,223,45,67]
    console.log(arr.includes(45))   //輸出:true
    [1, 2, NaN].includes(NaN)     // true
    [1, 2, NaN].indexOf(NaN)      // -1

//七、Map
    var m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]);
    m.get('Michael'); // 95
    //初始化Map須要一個二維數組,或者直接初始化一個空Map。Map具備如下方法:
    var m = new Map(); // 空Map
    m.set('Adam', 67); // 添加新的key-value
    m.set('Bob', 59);
    m.has('Adam'); // 是否存在key 'Adam': true
    m.get('Adam'); // 67
    m.delete('Adam'); // 刪除key 'Adam'
    m.get('Adam'); // undefined
    //因爲一個key只能對應一個value,因此,屢次對一個key放入value,後面的值會把前面的值沖掉:
    var m = new Map();
    m.set('Adam', 67);
    m.set('Adam', 88);
    m.get('Adam'); // 88

//八、Set
    //要建立一個Set,須要提供一個Array做爲輸入,或者直接建立一個空Set:
    var s1 = new Set(); // 空Set
    var s2 = new Set([1, 2, 3]); // 含1, 2, 3
    //重複元素在Set中自動被過濾:
    var s = new Set([1, 2, 3, 3, '3']);
    s; // Set {1, 2, 3, "3"}  注意:數字3和字符串'3'是不一樣的元素
    //經過add(key)方法能夠添加元素到Set中,能夠重複添加,但不會有效果:
    s.add(4);
    s; // Set {1, 2, 3, 4}
    s.add(4);
    s; // 仍然是 Set {1, 2, 3, 4}
    //經過delete(key)方法能夠刪除元素:
    var s = new Set([1, 2, 3]);
    s; // Set {1, 2, 3}
    s.delete(3);
    s; // Set {1, 2}複製代碼

ES6字符串的經常使用方法:
一、for···of:遍歷
二、模板字符串:` `

ES6新增數據類型:Symbol前端

ES10新增數據類型BigInt:BigInt數據類型的目的是比Number數據類型支持的範圍更大的整數值vue

十二、ES6解構賦值

①數組的解構賦值:

let [a,b,c,d] = [1,2,3,4];
console.log(a,b,c,d);

let [x,y,...z] = [1,2,3,4,5,6,7,8];
console.log(x,y,z);

let arr = [1,2,3,4,5];
console.log(...arr);   //輸出:1 2 3 4 5 

let [, , third] = ["foo","bar","baz"];
console.log(third);   //輸出: baz

let [x,y,...z] = ["a"];
console.log(x,y,z);   //輸出:"a" undefined []

//報錯:
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};

let [foo = true] = [];
console.log(foo) //輸出: true

let [x = 1] = [undefined];
let [y = 1] = [null];
console.log(x)   //輸出: 1
console.log(y)   //輸出: 1
複製代碼

②對象的解構賦值:
對象的解構賦值的內部機制,是先找到同名屬性,而後再賦給對應的變量。真正被賦值的是後者,而不是前者。

let {foo,bar} = {bar:"acb",foo:"dce"};
console.log(foo,bar);    //輸出:dce abc

let {baz} = {foo:"aaa",bar:"bbb"};
console.log(baz);   //輸出:undefined

let {foo:baz} = {foo:"aaa",bar:"bbb"};
console.log(baz);   //輸出:aaa
console.log(foo);   //輸出:報錯

let x;
{x} = {x:1}   //輸出:報錯

let x;
({x} = {x:1});
console.log(x)    //輸出:1複製代碼

③字符串的解構賦值:

const [a,b,c,d,e] = "hello";
console.log(a,b,c,d,e)   //輸出:h e l l o

let {length : len} = "hello";
console.log(len);    //輸出: 5複製代碼

④函數參數的解構賦值:

function add([x,y]){
    return x + y;
}
console.log(add([1,2]));   //輸出:3複製代碼

       (重點)解構賦值的用處:java

用處1:交換兩個變量的值react

let a = 10,b = 20;
console.log([a,b] = [b,a]);複製代碼

用處2:從函數返回多個值jquery

function fn(){
    return [1,2,3,4,5];
}
var [a,b,c,d,e] = fn();
console.log(a,b,c,d,e);複製代碼

用處3:函數參數的定義ios

function fn3([x,y,z]){
    return x+y+z;
}
console.log(fn3([4,5,6]))複製代碼

用處4:函數參數的默認值nginx

function fn4([x=1,y=2,z=3]){
    return x+y+z;
}    
console.log(fn4([4,5,6]))複製代碼

用處5:提取JSON數據

var dataJson = {
    "id":1,
    "status":"ok",
    "data":[1,2,3,4]
}    
var {id,status,data:arr} = dataJson;
console.log(id,status,arr);
//輸出: 1 "ok" [1,2,3,4]複製代碼

用處6:遍歷Set、Map數據結構

var map = new Map();
map.set("first","hello");
map.set("second","world");
console.log(map);
for(var [key,value] of map){
    console.log(key+"is:"+value)
}複製代碼

用處7:輸入模塊的指定方法

var {sourceSort,sourceNumber} = require("soure-map")複製代碼

1三、數組去重

var arr = [1,2,45,44,45,2,89,1,1,2,1,2];複製代碼

第一種:new Set()

var box = Array.from(new Set(arr));
var box = [...new Set(arr)];
console.log(box);複製代碼

第二種:indexOf()

var box = [];
for(var i = 0;i < arr.length;i++){
	if(box.indexOf(arr[i]) == -1){
		box.push(arr[i])
	}
}
console.log(box);複製代碼

第三種:splice()

for(var i = 0;i < arr.length;i++){
	for(var j = i+1; j < arr.length;j++){
		if(arr[i] == arr[j]){
			arr.splice(j,1);
		}
	}
}
console.log(arr);複製代碼

第四種:sort()+splice()

arr.sort();
for(var i = 0; i < arr.length;i++){
	if(arr[i] == arr[i+1]){
		arr.splice(i,1);
		i--;
	}
}	
console.log(arr);複製代碼

第五種:遞歸函數

Array.prototype.unique = function(){
	var arr = this;
	len = arr.length;
	arr.sort(function(a,b){
		return a - b; 
	})
	function loop(index){
		if(index >= 1){
			if(arr[index] === arr[index-1]){
				arr.splice(index,1);
			}
			loop(index-1);
		}
	}
	loop(len-1);
	return arr;
}
var b = arr.unique();
console.log(b);複製代碼

簡單寫法如上,還有不少寫法,在此不一一列舉。

1四、ES7新增(不定時添加)

① 求冪運算符(**)

var x = 5 ** 3;   // 5的三次方
console.log(x);   //輸出: 125複製代碼

② async、await異步解決方案:async函數返回的是一個 Promise 對象,可使用 then 方法添加回調函數,async 函數內部 return 語句返回的值,會成爲 then 方法回調函數的參數。當函數執行的時候,一旦遇到await就會先返回,等到異步操做完成,再接着執行函數體內後面的語句。await是等待的意思,它等待的是promise 對象的執行完畢,並返回Promise 對象

1五、ES8新增(不定時添加)

① Object.entries( ):該方法會將某個對象的可枚舉屬性與值按照二維數組的方式返回。(若是目標對象是數組,則會將數組的下標做爲鍵值返回)

var obj = {
	name:"Bob",
	age:25
}
var arr = Object.entries(obj)
console.log(arr);   //輸出:[['name', 'Bob'], ['age', 25]]

var arr = ["Bob","Tom"];
var Create = Object.entries(arr);
console.log(Create);   //輸出:[['0', 'Bob'], ['1', 'Tom']]複製代碼

② Object.values():它的工做原理和Object.entries()方法很像,可是它只返回鍵值對中的值,結果是一維數組。

var obj = {
    	name:"Bob",
    	age:25
}
var arr = Object.values(obj);
console.log(arr);   //輸出:["Bob", 25]

var obj = {
    	2:"a",
	1:"b",
	3:"c"
}
var arr = Object.values(obj);
console.log(arr);   //輸出:["b", "a", "c"]

var obj = [1,3];
var arr = Object.values(obj);
console.log(arr);   //輸出:[1,3]複製代碼

③字符串填充 padStart()、padEnd():字符串填充方法,該方法可使得字符串達到固定長度。它有兩個參數,字符串目標長度和填充內容。

var str = "create"
var newStr1 = str.padStart(10,"x");
var newStr3 = str.padStart(6,"x");
var newStr2 = str.padEnd(10,"x");
var newStr4 = str.padEnd(6,"x");
console.log(newStr1,newStr2,newStr3,newStr4)
//輸出:xxxxcreate createxxxx create create複製代碼

④ Object.assign():方法用於對象的合併。

var obj1 = {name:"Bob"};
var obj2 = {age:25};
var newObj = Object.assign(obj1,obj2);
console.log(newObj);   //輸出: {name: "Bob", age: 25}複製代碼

1六、總結異步編程的6種方式:

① 回調函數;
② 事件監聽;
③ 發佈訂閱模式;
④ Promise;
⑤ Generator(ES6);
⑥ async。

//Generator函數:
function* add(){
	yield "1";
	yield "2";
	yield "3";
	reutrn;
}
var h = add();
console.log(h.next());   //輸出: {value:"1",done:false}
console.log(h.next());  //輸出: {value:"2",done:false}
console.log(h.next());  //輸出: {value:"3",done:false}
console.log(h.next());  //輸出: 報錯
//若是去掉return 則,輸出:{value: undefined, done: true}複製代碼

(強烈推薦)原文連接:mp.weixin.qq.com/s/TY6LbYQDy…

1七、設計模式(下面只列舉了經常使用10大)

推薦學會前4種,瞭解後6種

① 工廠模式:去作一樣的事情,實現一樣的效果,解決多個類似的問題這時候須要使用工廠模式

function CreatePerson(){
    var obj = new Object();
    obj.name = name;
    obj.age = age;
    obj.sayName = function(){
        return this.name;
    }
    return obj;
}
var p1 = new CreatePerson("Bob",28);
var p2 = new CreatePerson("Tom",25);複製代碼

② 單體模式:一個用來劃分命名空間並將一批屬性和方法組織在一塊兒的對象,若是它能夠被實例化,那麼它只能被實例化一次。

var Singleton = function(name){    //單體模式
    this.name = name;
    this.instance = null;
}
Singleton.prototype.getName = function(){
    return this.name;
}
function getInstance(name){   //獲取實例對象
    if(!this.instance){
        this.instance = new Singleton(name);
    }
    return this.instance;
}
//測試單體模式
var a = getInstance("Bob");
var b = getInstance("Tom");
console.log(a);   //輸出:Singleton {name: "Bob", instance: null}
console.log(b);   //輸出:Singleton {name: "Bob", instance: null}
console.log(a === b);   //輸出:true複製代碼

③ 模塊模式:以對象字面量的方式來建立單體模式, 爲單體模式添加私有變量和私有方法可以減小全局變量的使用。使用場景:若是咱們必須建立一個對象並以某些數據進行初始化,同時還要公開一些可以訪問這些私有數據的方法,那麼咱們這個時候就可使用模塊模式了。

var singleMode = (function(){
    var privateNum = 112;   //建立私有變量
    function privateFunc(){
        //實現本身業務邏輯的代碼
    }
    //返回一個對象包含公有方法和屬性
    return {
        publicMethod1:publicMethod1,
        publicMethod2:publicMethos1,
    }
})()複製代碼

④ 代理模式:代理是一個對象,它能夠用來控制對本體對象的訪問,它與本體對象實現了一樣的接口,代理對象會把全部的調用方法傳遞給本體對象的;代理模式最基本的形式是對訪問進行控制,而本體對象則負責執行所分派的那個對象的函數或者類,簡單的來說本地對象注重的去執行頁面上的代碼,代理則控制本地對象什麼時候被實例化,什麼時候被使用;

//好比如今京東ceo想送給奶茶妹一個禮物,可是呢假如該ceo很差意思送,或者因爲工做忙沒有時間送,
//那麼這個時候他就想委託他的經紀人去作這件事
var TeaAndMilkGirl = function(name) {  // 先申明一個奶茶妹對象
    this.name = name;
};
var Ceo = function(girl) {   // 這是京東ceo先生
    this.girl = girl;
    this.sendMarriageRing = function(ring) {   // 送結婚禮物 給奶茶妹
        console.log("Hi " + this.girl.name + ", ceo送你一個禮物:" + ring);
    }
};
var ProxyObj = function(girl){   // 京東ceo的經紀人是代理,來代替送
    this.girl = girl;
    this.sendGift = function(gift) {   // 經紀人代理送禮物給奶茶妹
        (new Ceo(this.girl)).sendMarriageRing(gift);   // 代理模式負責本體對象實例化
    }
};
// 初始化
var proxy = new ProxyObj(new TeaAndMilkGirl("奶茶妹"));
proxy.sendGift("結婚戒"); // Hi 奶茶妹, ceo送你一個禮物:結婚戒複製代碼

⑤ 職責鏈模式: 消除請求的發送者與接收者之間的耦合。職責鏈是由多個不一樣的對象組成的,發送者是發送請求的對象,而接收者則是鏈中那些接收這種請求而且對其進行處理或傳遞的對象。請求自己有時候也能夠是一個對象,它封裝了和操做有關的全部數據。
⑥ 命令模式:命令模式中的命令指的是一個執行某些特定事情的指令。命令模式使用的場景有:有時候須要向某些對象發送請求,可是並不知道請求的接收者是誰,也不知道請求的操做是什麼,此時但願用一種鬆耦合的方式來設計程序代碼;使得請求發送者和請求接受者消除彼此代碼中的耦合關係。
⑦ 模板方法模式:模板方法模式由二部分組成,第一部分是抽象父類,第二部分是具體實現的子類,通常的狀況下是抽象父類封裝了子類的算法框架,包括實現一些公共方法及封裝子類中全部方法的執行順序,子類能夠繼承這個父類,而且能夠在子類中重寫父類的方法,從而實現本身的業務邏輯。
⑧ 策略模式:定義一系列的算法,把它們一個個封裝起來,而且使它們能夠相互替換。
優勢:1. 策略模式利用組合,委託等技術和思想,有效的避免不少if條件語句。
          2. 策略模式提供了開放-封閉原則,使代碼更容易理解和擴展。
          3. 策略模式中的代碼能夠複用。
⑨ 發佈-訂閱者模式:發佈---訂閱模式又叫觀察者模式,它定義了對象間的一種一對多的關係,讓多個觀察者對象同時監聽某一個主題對象,當一個對象發生改變時,全部依賴於它的對象都將獲得通知。
⑩ 中介者模式:中介者模式的做用是解除對象與對象之間的耦合關係,增長一箇中介對象後,全部的相關對象都經過中介者對象來通訊,而不是相互引用,因此當一個對象發送改變時,只須要通知中介者對象便可。中介者使各個對象之間耦合鬆散,並且能夠獨立地改變它們之間的交互。

參考網站:blog.csdn.net/song_mou_xi…

1八、Ajax的原生寫法:

var xhr;    //建立ajax對象
if(window.XMLHttpRequest){   //兼容IE
    xhr = new XMLHttpRequest();
}else{
    xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
xhr.open("get",url,true);   //創建鏈接
xhr.send();   //發送
xhr.onreadystatechange = function(){  //獲取數據
    if(xhr.readyState == 4 && xhr.status == 200){
        var data = JSON.parse(xhr.responseText);
    }
}複製代碼

1九、圖片的懶加載、預加載:

原理:
        預加載原理:就是在網頁所有加載以前,提早加載圖片,當用戶須要查看時可直接從本地緩存中渲染,以提供給用戶更好的體驗,減小等待的時間。
       圖片懶加載原理(緩載):經過 監聽onscroll事件判斷資源位置,延遲加載圖片或符合某些條件時才加載某些圖片。首先爲全部懶加載的靜態資源添加自定義屬性字段,好比若是是圖片,能夠指定data-src爲真實的圖片地址,src指向loading的圖片。 而後當資源進入視口的時候,將src屬性值替換成data-src的值。 可使用元素的getBoundingRect().top判斷是否在視口內,也可使用元素距離文檔頂部的距離offsetTop和scrollTop是否小於視口高度來判斷 

//懶加載簡單代碼實現
//《JS代碼》
window.onload = function(){
		//獲取當前瀏覽器視口高度
		var viewHeight = document.documentElement.clientHeight;
		console.log(viewHeight);
		//鼠標滾動回調
		function lazyload(){
			var img = document.getElementsByClassName("img");
			for(let item of img){
				//獲取每張圖片距離頂部的距離
				var imgHeight = item.getBoundingClientRect();
				console.log(imgHeight)
				//判斷當圖片出如今視口160px的時候把地址放入src中,顯示出圖片
				if(imgHeight.top < (viewHeight - 10)){
					item.src = item.getAttribute("data-original");
				}
			}
		}
		lazyload(); //頁面加載時把當前視口中的圖片加載進來
		document.addEventListener("scroll",lazyload);
	}
//《HTML代碼》
<img class="img" lazyload="true" data-origin="圖片http地址">
<img class="img" lazyload="true" data-origin="圖片http地址"><img class="img" lazyload="true" data-origin="圖片http地址"><img class="img" lazyload="true" data-origin="圖片http地址"><img class="img" lazyload="true" data-origin="圖片http地址">複製代碼

//預加載簡單代碼實現
//style
#box{width:0;height:30px;background:red;transition:1s;}
//html
<div id="box"></div>
<ul>
    <li><img src="圖片http地址"></li>
    <li><img src="圖片http地址"></li>
    <li><img src="圖片http地址"></li>    <li><img src="圖片http地址"></li>
    <li><img src="圖片http地址"></li>    <li><img src="圖片http地址"></li>    <li><img src="圖片http地址"></li>    <li><img src="圖片http地址"></li>
</ul>
//JS代碼
window.onload = function(){
	var oBox = document.getElementById("box");
	var arr = [
		"https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3916481728,2850933383&fm=26&gp=0.jpg",
		"https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=291222564,3369340489&fm=26&gp=0.jpg",
		"https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3974834430,2578081919&fm=26&gp=0.jpg",
		"http://img3.imgtn.bdimg.com/it/u=2332284096,307149879&fm=26&gp=0.jpg",
		"http://img0.imgtn.bdimg.com/it/u=2667939876,1138687338&fm=26&gp=0.jpg",
		"http://img0.imgtn.bdimg.com/it/u=2981065320,3294183154&fm=26&gp=0.jpg",
		"http://img4.imgtn.bdimg.com/it/u=2647953258,2919321872&fm=26&gp=0.jpg",
		"http://img1.imgtn.bdimg.com/it/u=221171515,1209752772&fm=26&gp=0.jpg"
	]
	var num = 0;
	for(var i = 0; i < arr.length;i++ ){
		var oImg = new Image();  //新建一個圖片對象
		console.log(oImg);
		oImg.onload = function(){
			num++;  //圖片預加載完成,執行onload裏面的代碼
			oBox.style.width = num/arr.length * 100 + "%"; //經過圖片的加載順序,來控制進度條的寬度
		}
		oImg.src = arr[i];  //給圖片對象添加路徑,注意這條代碼必須加在onload後面
	}
}
複製代碼

20、防抖、節流

防抖:所謂防抖,就是指觸發事件後在 n 秒內函數只能執行一次,若是在 n 秒內又觸發了事件,則會從新計算函數執行時間。
節流:所謂節流,就是指連續觸發事件可是在 n 秒中只執行一次函數。
兩種方式能夠實現,分別是時間戳版和定時器版。

//節流
// 思路:在規定時間內只觸發一次
function throttle (fn, delay) {
  // 利用閉包保存時間
  let prev = Date.now()
  return function () {
    let context = this
    let arg = arguments
    let now = Date.now()
    if (now - prev >= delay) {
      fn.apply(context, arg)
      prev = Date.now()
    }
  }
}

function fn () {
  console.log('節流')
}
addEventListener('scroll', throttle(fn, 1000))
複製代碼

//防抖
// 思路:在規定時間內未觸發第二次,則執行
function debounce (fn, delay) {
  // 利用閉包保存定時器
  let timer = null
  return function () {
    let context = this
    let arg = arguments
    // 在規定時間內再次觸發會先清除定時器後再重設定時器
    clearTimeout(timer)
    timer = setTimeout(function () {
      fn.apply(context, arg)
    }, delay)
  }
}

function fn () {
  console.log('防抖')
}
addEventListener('scroll', debounce(fn, 1000))
複製代碼

2一、頁面加載進度條的實現

①定時器加載(原理):設置固定的時間後將遮罩層和加載圖片隱藏,顯示頁面內容。
②經過加載狀態事件實現進度條(原理):document.onreadystatechange(頁面加載狀態改變時的事件),document.readyState(返回當前文檔的狀態);
③經過CSS3來實現進度條;
④實時獲取加載數據的進度條(原理):經過加載圖像來實現效果。
⑤根據加載進度來改變進度條的長度(width值);
⑥根據文件的加載順序來實現加載進度條。

2二、this關鍵字(指向)

this是JavaScript語言的一個關鍵字,它是函數運行時,在函數體內部自動生成一個對象,只能在函數體內部使用。函數的不一樣使用場合,this有不一樣的值。總的來講this就是函數運行時所在的環境對象。

狀況一:純粹的函數調用:這是函數的最一般的用法,屬於全局調用,所以this就表明全局對象

var x = 1;
function test(){
    console.log(this.x);
}
test();   // 1  狀況二:做爲對象方法複製代碼

狀況二:做爲對象方法的調用:函數還能夠做爲某個對象的方法調用,這時this就指這個上級對象。

function test(){
    console.log(this.x);
}
var obj = {};
obj.x = 1;
obj.m = test;
obj.m();複製代碼

狀況三:做爲構造函數調用:所謂構造函數,就是經過這個函數,能夠生成一個新對象。這時this就指這個新對象。

function test(){
    this.x = 1;
}
var obj = new test();
console.log(obj.x);   // 1複製代碼

狀況四:apply的調用:apply( )是函數的一個方法,做用是改變函數的調用對象。它的第一個參數就表示改變後的調用這個函數的對象。所以這時this指的就是這第一個參數。
apply( )的參數爲空時,默認調用全局對象。這時運行結果爲0,證實this指的是全局對象。

var x = 0;
function test(){
    console.log(this.x);
}
var obj = {};
obj.x = 1;
obj.m = test;
obj.m.apply();    // 0   this指的是全局對象
obj.m.apply(obj);   // 1    this指的是obj複製代碼

狀況五:隱式綁定:函數的調用是在某個對象上觸發的,即調用位置上存在上下文對象,典型隱士調用:xxx.fn()

function info(){
    console.log(this.age);
}
var person = {
    age: 20,
    info
}
var age = 28;
person.info(); //20;執行的是隱式綁定複製代碼

狀況六:箭頭函數:箭頭函數沒有本身的this,繼承外層上下文綁定的this;

let obj = {
    age: 20,
    info: function() {
        return () => {
            console.log(this.age); //this繼承的是外層上下文綁定的this
        }
    }
}
let person = {age: 28};
let info = obj.info();
info(); //20
let info2 = obj.info.call(person);
info2(); //28複製代碼

2三、對象和麪向對象

對象:屬性和方法的集合叫作對象(萬物皆對象)。
面向對象:首先就是找對象,若是該對象不具有所須要的方法或屬性,那就給它添加。面向對象是一種編程思惟的改變。經過原型的方式來實現面向對象編程。
建立對象的方式(4種):new Object、字面量、構造函數、原型。

2四、函數式編程

含義:函數式編程是一種強調以函數爲主的軟件開發風格。經過組合純函數,避免共享狀態、可變做用和反作用來構建軟件的過程。
目的:使用函數來抽象做用在數據之上的控制流和操做,從而在系統中消除反作用並減小對狀態的改變。
特色:函數式編程是聲明性的而不是命令式的,應用狀態流經純函數中。相比於面向對象編程,其中的應用狀態常常是共享的,而且和方法一塊兒定義在一些對象中。

2五、怎麼判斷兩個對象是否相等?

① 首先比較兩個對象的長度,若是長度不相等使flag爲false,爲不相等;
② 若是長度相等那就遍歷對象1(對象2也能夠),利用hasOwnProperty()方法查看對象1中是否包含對象2中的屬性或者方法,若是不包含則使flag爲false,爲不想等。
③ 接下來判斷兩對象的內存地址是否相同,不一樣則爲false

function compreObj(obj1, obj2) {
    var flag = true;
    function compre(obj1, obj2) {
        if (Object.keys(obj1).length != Object.keys(obj2).length) {
            flag = false;
        } else {
            for (let x in obj1) {
                if (obj2.hasOwnProperty(x)) {
                    if (obj1[x] !== obj2[x]) {
                        compre(obj1[x], obj2[x]);
                    }
                } else {
                    flag = false;
                }
            }
        }
        if (flag === false) {
            return false;
        } else {
            return true;
        }
    }
    return compre(obj1, obj2)
}
console.log(compreObj(對象1, 對象2));複製代碼

2六、事件模型:事件委託、代理?如何讓事件先冒泡後捕獲?

事件委託:又叫事件代理,利用事件冒泡,只指定一個事件處理程序,就能夠管理某一類型的全部事件。
原理:事件冒泡機制,從最深的節點開始,而後逐步向上傳播事件。
做用:①支持爲同一個DOM元素註冊多個同類型事件;②可將事件分爲事件捕獲和事件冒泡。
代碼:addEventListener(event,function,useCapture布爾值)默認爲false冒泡,true爲捕獲
attachEvent() //IE8及IE更早版本 detachEvent() //移除事件監聽

//不使用事件捕獲
window.onload = function(){
    	let oBox = document.getElementById("box");
    	oBox.onclick = function(){
    		alert(1);   //不觸發
    }
    	oBox.onclick = function(){
    		alert(2);   //觸發
    	}
}
//使用事件捕獲
window.onload = function(){
    oBox.addEventListener("click",function(){
    	alert(1);   //觸發
    })
	oBox.addEventListener("click",function(){
		alert(2);   //觸發
	})
}複製代碼

事件捕獲:當一個事件觸發後,從Window對象觸發,不斷通過下級節點,直到目標節點。在事件到達目標節點以前的過程就是捕獲階段。全部通過的節點,都會觸發對應的事件。

當爲事件捕獲(useCapture:true)時,先執行body的事件,再執行div的事件

事件冒泡:當事件到達目標節點後,會沿着捕獲階段的路線原路返回。一樣,全部通過的節點,都會觸發對應的事件。

當爲事件冒泡(useCapture:false)時,先執行div的事件,再執行body的事件

先冒泡後捕獲:根據w3c標準,應先捕獲再冒泡。若要實現先冒泡後捕獲,給一個元素綁定兩個addEventListener,其中一個第三個參數設置爲false(即冒泡),另外一個第三個參數設置爲true(即捕獲),調整它們的代碼順序,將設置爲false的監聽事件放在設置爲true的監聽事件前面便可。

2七、window的onload事件和domcontentloaded

window.onload:當一個資源及其依賴資源已完成加載時,將觸發onload事件。
document.onDOMContentLoaded:當初始的HTML文檔被徹底加載和解析完成以後,DOMContentLoaded事件被觸發,而無需等待樣式表、圖像和子框架的完成加載。
區別:
     ①onload事件是DOM事件,onDOMContentLoaded是HTML5事件。
     ②onload事件會被樣式表、圖像和子框架阻塞,而onDOMContentLoaded不會。
     ③當加載的腳本內容並不包含當即執行DOM操做時,使用onDOMContentLoaded事件是個更好的選擇,會比onload事件執行時間更早。

2八、for···in和for···of的區別:(for···in取key,for··of取value)

①從遍歷數組角度來講,for···in遍歷出來的是key(即下標),for···of遍歷出來的是value(即數組的值);

var arr = [99,88,66,77];
for(let i in arr){
    console.log(i);   //0,1,2,3
}
for(let i of arr){
    consoel.log(i);   //99,88,66,77
}複製代碼

②從遍歷字符串的角度來講,同數組同樣。
③從遍歷對象的角度來講,for···in會遍歷出來的爲對象的key,但for···of會直接報錯。

var obj = {name:"Bob",age:25};
for(var i in obj){
	console.log(i)  // name age
}
for(var i of obj){
	console.log(i)   //報錯
}複製代碼

2九、函數柯里化(卡瑞化、加里化)?

概念:把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,而且返回接受餘下的參數並且返回結果的新函數的技術。容易理解的概念:Currying概念其實很簡單,只傳遞給函數一部分參數來調用它,讓它返回一個函數去處理剩下的參數(主要是利用閉包實現的)。
特色: 
   ①接收單一參數,將更多的參數經過回調函數來搞定;
   ②返回一個新函數,用於處理全部的想要傳入的參數;
   ③須要利用call/apply與arguments對象收集參數;
   ④返回的這個函數正是用來處理收集起來的參數。
做用:能進行部分傳值,而傳統函數調用則須要預先肯定全部實參。若是你在代碼某一處只獲取了部分實參,而後在另外一處肯定另外一部分實參,這個時候柯里化和偏應用就能派上用場。
用途:我認爲函數柯里化是對閉包的一種應用形式,延遲計算、參數複用、動態生成函數(都是閉包的用途)。

function add(x,y){  //普通函數
    console.log(x+y);
}
function curryingAdd(x){  //柯里化函數(閉包)
    return function(y){
        console.log(x+y);
    }
}
add(1,2)  //3
curryingAdd(1)(2)   //3  複製代碼

30、JS預解析?

JS代碼在執行前,瀏覽器會對js代碼進行掃描,默認的把全部帶var和function聲明的變量進行提早的聲明或者定義,遵循先解析後使用的原則。

3一、call、applyd的區別,原生JS實現bind。(call,apply,bind三者用法和區別:角度可爲參數、綁定規則,運行效率、運行狀況)。

定義:
apply():調用一個對象的一個方法,用另外一個對象替換當前對象,例如:B.apply(A,arguments);即A對象應用B對象的方法。
call():調用一個對象的一個方法,用另外一個對象替換當前對象,例如:B.call(A,args1,args2,···);即A對象調用用B對象的方法。

做用:①改變this指向;②借用別的對象的方法;③單純的調用函數;④實現繼承;

function add(a,b){
	return a+b;
}
function sub(a,b){
	return a-b;
}
var a1 = add.apply(sub,[4,2]); //sub調用add的方法
var a2 = sub.apply(add,[4,2]); //add調用sub的方法
var a3 = add.call(sub,4,2); //sub調用add的方法
console.log(a1);  //6
console.log(a2);  //2
console.log(a3);  //6複製代碼

//改變this指向
var obj = {
	name:"Bob"
}
var name = "Tom";
function test(){
	console.log(this.name);
	console.log(this);
}
test();  // Tom   Window
test.call(obj);  //Bob {name:"Bob"}

//借用別的對象的方法
var Person1 = function(){
	this.name = "Bob";
}
var Person2 = function(){
	this.getName = function(){
		console.log(this.name);
	}
	Person1.call(this);//this指向Person2,結果至關於給Person2加了name屬性
}
var person = new Person2();
person.getName();   //Bob

//單純的函數調用:
function add(){
    alert(1);
}
add.call();複製代碼

apply、call和bind的區別:
類似之處:
    ①都是用來改變函數的this對象的指向的;
    ②第一個參數都是this要指向的對象;
    ③均可以利用後續參數傳參;
區別:
①apply、call、bind的第一個參數都是this要指向的對象,但apply只有兩個參數,第二個參數爲一個數組,須要傳輸的參數值須所有放到數組中。而call、bind同樣,參數用逗號分開。
②apply、call返回的的是一個值,而bind返回的是一個函數,須要執行這個函數纔會獲得值。

3二、當即執行函數和使用場景

當即執行函數:( function( ){ })( ) 返回值能夠爲基本數據類型,也能返會任何類型的值。
寫法緣由:由於在 javascript 裏,括號內部不能包含語句,當解析器對代碼進行解釋的時候,先碰到了(),而後碰到function關鍵字就會自動將()裏面的代碼識別爲函數表達式而不是函數聲明。
做用:當即執行函數會造成一個單獨的做用域,咱們能夠封裝一些臨時變量或者局部變量,避免污染全局變量。
使用場景:
①代碼在頁面加載完成以後,不得不執行一些設置工做,好比時間處理器,建立對象等等。
② 全部的這些工做只須要執行一次,好比只須要顯示一個時間。
③須要一些臨時的變量,可是初始化過程結束以後,就不再會被用到,咱們能夠用當即執行函數——去將咱們全部的代碼包裹在它的局部做用域中,不會讓任何變量泄露成全局變量。

3三、iframe的優缺點有哪些?

優勢:
①iframe可以原封不動的把嵌入的網頁展示出來;
②若是有多個網頁引用iframe,那麼你只須要修改iframe的內容,就能夠實現調用的每個頁面內容的更改,方便快捷。
③網頁若是爲了統一風格,頭部和版本都是同樣的,就能夠寫成一個頁面,用iframe來嵌套,能夠增長代碼的可重用。
④若是遇到加載緩慢的第三方內容如圖標和廣告,這些問題能夠由iframe來解決。
缺點:
①會產生不少頁面不易管理;
②iframe框架結構有時會讓人感到迷惑,若是框架個數多的話,可能會出現上下、左右滾動條,會分散訪問者的注意力,用戶體驗度差。
③代碼複雜,沒法被一些搜索引擎索引到,這一點很關鍵,如今的搜索引擎爬蟲還不能很好的處理iframe中的內容,因此使用iframe會不利於搜索引擎優化。
④不少的移動設備(PDA 手機)沒法徹底顯示框架,設備兼容性差。
⑤iframe框架頁面會增長服務器的http請求,對於大型網站是不可取的。
本題延申:
frame框架:
優勢:
①重載頁面時不須要重載整個頁面,只須要重載頁面中的一個框架頁(減小了數據的傳輸,加快了網頁下載速度);
②技術易於掌握,使用方便,使用者衆多,可主要應用於不需搜索引擎來搜索的頁面;
③方便製做導航欄 ;
缺點:
①搜索引擎程序不能解讀這種頁面;
②不能打印全框架;
③瀏覽器的後退按鈕無效;
④手機等終端設備沒法顯示所有框架內容;
iframe和frame區別:
①frame不能脫離frameSet單獨使用,iframe能夠;
②frame不能放在body中,不然不能正常顯示,frame不能和body同時使用,iframe能夠;
③嵌套在frameSet中的iframe必需放在body中,不嵌套在frameSet中的iframe能夠隨意使用;
④frame的高度只能經過frameSet控制;iframe能夠本身控制,不能經過frameSet控制;
⑤iframe 能夠放到表格裏面。frame 則不行。 

3四、查找數組重複項

       查找該元素首次出現的位置和最後出現的位置下標是否相同,同時判斷新數組中是否不存在該元素,若是都知足則添加進新數組中去。

var arr = [1,2,45,44,45,2,89,1,1,2,1,2,44];
Array.prototype.unique = function(){
	var arr = this;
	var box = [];
	for(var str of arr){
		if(arr.indexOf(str) != arr.lastIndexOf(str) && box.indexOf(str) == -1){
			box.push(str);
		}
	}
	return box;
}
console.log(arr.unique());複製代碼

3五、數組扁平化

var arr = [1,2,[3,4,[5,6,[7,8,[9,10]]]]];複製代碼

第一種:

function flatten(arr){
	var box = [];
	arr.map(v => {
		if(Array.isArray(v)){
			box = box.concat(flatten(v))
		}else{
			box.push(v);
		}
	})
	return box;
}
console.log(flatten(arr));複製代碼

第二種(不推薦):

function flatten(arr){
	return arr.toString().split(",").map(v => {
		return Number(v);
	})
}
console.log(flatten(arr));複製代碼

第三種:

function flatten(arr){
	console.log(arr.join(","))
	return arr.join(",").split(",").map(v => {
		return parseInt(v);
	})
}
console.log(flatten(arr));複製代碼

第四種:

var arr = [1,2,[3,4,[5,6,[7,8,[9,10]]]]];
function flatten(arr){
	return arr.reduce((result,item) => {
		console.log(result,item)
		return result.concat(Array.isArray(item) ? flatten(item) : item);
	},[]);
}
console.log(flatten(arr)); 複製代碼

第五種:

console.log([].concat(...arr));
function flatten(arr){
	while(arr.some(item => Array.isArray(item))){
		arr = [].concat(...arr);
	}
	return arr;
}
console.log(flatten(arr));    複製代碼

3六、BOM屬性對象方法

BOM,即 JavaScript能夠進行操做的瀏覽器的各個功能部件的接口。
⑴window對象:
①window的方法:confirm(確認框),open(url)打開新的窗口,close()關閉窗口;
②window的屬性:closed,opener。
咱們有的時候須要代開咱們的子窗體,域名下面還有一個新的域名,也就是子域名,子網頁 var myWindow = window.open("xxx.html"); 咱們打開一個子網頁, 會返回給咱們一個值的,這個值表明另外一個頁面的window屬性能夠經過myWindow.closed來查看另外一個頁面的是否關閉。

⑵Navigator對象(導航器對象):
appCodeName:返回瀏覽器的代碼名;
appName:返回瀏覽器名字;
appVersion:返回瀏覽器的平臺和版本信息;
cookieEnabled:返回指明瀏覽器中是否啓用cookie的布爾值;
platform:返回運行瀏覽器的操做系統平臺;
userAgent:返回客戶機發送服務器的user-agent頭部的值;

⑶screen(顯示器對象):
avaiHeight:返回顯示屏幕的可用高度;
availWidth:返回顯示屏幕的可用寬度;
height:返回屏幕的像素高度;
width:返回屏幕的像素寬度;
colorDepth:返回屏幕顏色的位數;

⑷history(歷史對象):
back():返回前一個URL;
forward():返回下一個URL;
go():返回某個具體頁面;

⑸localtion(位置對象):
hash:返回或設置從井號(#)開始的URL;
host:設置或返回主機名和當前URL的端口號;
href:設置或者返回完整的URL;
hostname:設置或返回當前URL主機名;
search:設置或者返回從問號(?)開始的URL;
port:設置或返回當前URL的端口號;

3七、服務端渲染

定義:將組件或頁面經過服務器生成html字符串,在發送到瀏覽器,最後將靜態標記"混合"爲客戶端上徹底交互的應用程序。
解釋:服務端渲染的模式下,當用戶第一次請求頁面時,由服務器把須要的組件或頁面渲染成 HTML 字符串,而後把它返回給客戶端。客戶端拿到手的,是能夠直接渲染而後呈現給用戶的 HTML 內容,不須要爲了生成 DOM 內容本身再去跑一遍 JS 代碼。使用服務端渲染的網站,能夠說是「所見即所得」,頁面上呈現的內容,咱們在 html 源文件裏也能找到。有了服務端渲染,當請求用戶頁面時,返回的body裏已經有了首屏的html結構,以後結合css顯示出來。

優勢:
①首屏渲染快(關鍵性問題):相比於加載單頁應用,我只須要加載當前頁面的內容,而不須要像 React 或者 Vue 同樣加載所有的 js 文件;
②SEO(搜索引擎)優化:不一樣爬蟲工做原理相似,只會爬取源碼,不會執行網站的任何腳本(Google除外,聽說Googlebot能夠運行javaScript)。使用了React或者其它MVVM框架以後,頁面大多數DOM元素都是在客戶端根據js動態生成,可供爬蟲抓取分析的內容大大減小。另外,瀏覽器爬蟲不會等待咱們的數據完成以後再去抓取咱們的頁面數據。服務端渲染返回給客戶端的是已經獲取了異步數據並執行JavaScript腳本的最終HTML,網絡爬中就能夠抓取到完整頁面的信息。
③能夠生成緩存片斷、節能;

缺點:用戶體驗較差,不容易維護、一般前端改了部分html或者css,後端也須要改;
使用場景:vue全家桶或者react全家桶,都是推薦經過服務端渲染來實現路由的。

3八、垃圾回收機制

什麼是垃圾:通常來講沒有被引用的對象就是垃圾,就是要被清除, 有個例外若是幾個對象引用造成一個環,互相引用,但根訪問不到它們,這幾個對象也是垃圾,也要被清除。
方法:
①JS具備垃圾自動回收的機制:週期性執行,找出那些不在繼續使用的變量,而後釋放其內存。
②標記清除(常見):當變量進入環境時,將這個變量標記爲「進入環境」。當變量離開環境時,則將其標記爲「離開環境」。標記「離開環境」的就回收內存。垃圾回收器完成內存清除工做,銷燬那些帶標記的值並回收他們所佔用的內存空間。
③引用計數:
     原理:跟蹤記錄每一個值被引用的次數。
     工做流程:當聲明瞭一個變量並將一個引用類型值賦給該變量時,則這個值的引用次數就是1。若是同一個值又被賦給另外一個變量,則該值的引用次數加1。相反,若是包含對這個值引用的變量又取得了另一個值,則這個值的引用次數減1。當這個值的引用次數變成0時,則說明沒有辦法再訪問這個值了,於是就能夠將其佔用的內存空間回收回來。這樣,當垃圾回收器下次再運行時,它就會釋放那些引用次數爲0的值所佔用的內存。

3九、eventloop(事件循環):進程和線程,任務隊列;

瀏覽器內核是多線程,JavaScript是單線程;
JS單線程詳解:由於 js 是面向客戶端的一門語言,主要是用戶交互,操做dom,渲染數據。試想一下,若是是多線程,咱們在一個線程刪除了一個dom節點,另一個線程添加了一個dom節點,以那個線程爲主呢,就會出現混亂的狀況。固然你能夠說咱們在操做一個dom以後加上鎖,只容許一個線程操做,這樣其實增長了程序的複雜度,並非一個好辦法。

單線程產生的問題:必需要等待前一個程序執行完畢才執行下一個,因此將程序分爲了兩類:同步任務和異步任務。異步任務又能夠分爲宏任務和微任務。

棧:先進後出的數據結構,存儲基本數據類型的變量。
堆:主要負責引用數據類型的存儲。

任務隊列:爲何會有任務隊列呢,仍是由於 javascript 單線程的緣由,單線程,就意味着一個任務一個任務的執行,執行完當前任務,執行下一個任務,這樣也會遇到一個問題,就好比說,要向服務端通訊,加載大量數據,若是是同步執行,js 主線程就得等着這個通訊完成,而後才能渲染數據,爲了高效率的利用cpu, 就有了同步任務和異步任務之分。

同步任務: 指的是在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務。
異步任務: 指的是不進入主線程,某個異步任務能夠執行了,該任務纔會進入主線程執行。


宏任務macrotask: 能夠理解是每次執行棧執行的代碼就是一個宏任務(包括每次從事件隊列中獲取一個事件回調並放到執行棧中執行)。常見的宏任務:script, setTimeout, setInterval, setImmediate, I/O, UI rendering。
微任務microtask(異步): 能夠理解是在當前task執行結束後當即執行的任務。常見的微任務:process.nextTick(Nodejs),Promise.then(), MutationObserver。

40、如何快速讓字符串變成已千爲精度的數字

var str = "10000000000";複製代碼

第一種:把數字轉換成字符串後,打散爲數組,再從末尾開始,逐個把數組中的元素插入到新數組(result)的開頭。 每插入一個元素,counter就計一次數(加1),當counter爲3的倍數時,就插入一個逗號,可是要注意開頭(i爲0時)不須要逗號。最後經過調用新數組的join方法得出結果。

String.prototype.toThousands = function(){
	var num = this;
    var result = [ ], counter = 0;
    num = (num || 0).toString().split('');
    for (var i = num.length - 1; i >= 0; i--) {
        counter++;
        result.unshift(num[i]);
        if (!(counter % 3) && i != 0) { result.unshift(','); }
    }
    return result.join('');
}
console.log(str.toThousands());複製代碼

第二種:經過正則表達式循環匹配末尾的三個數字,每匹配一次,就把逗號和匹配到的內容插入到結果字符串的開頭,  而後把匹配目標(num)賦值爲還沒匹配的內(RegExp.leftContext)。         若是數字的位數是3的倍數時,最後一次匹配到的內容確定是三個數字,可是最前面的三個數字前不須要加逗號;若是數字的位數不是3的倍數,那num變量最後確定會剩下1到2個數字,循環事後,要把剩餘的數字插入到結果字符串的開頭。

function toThousands(num) {
    var num = (num || 0).toString(), re = /\d{3}$/, result = '';
    while ( re.test(num) ) {
        result = RegExp.lastMatch + result;
        if (num !== RegExp.lastMatch) {
            result = ',' + result;
            num = RegExp.leftContext;
        } else {
            num = '';
            break;
        }
    }
    if (num) { result = num + result; }
    return result;
}
console.log(toThousands(str));  複製代碼

第三種:第二種的改良版

function toThousands(num) {
    var num = (num || 0).toString(), result = '';
    while (num.length > 3) {
        result = ',' + num.slice(-3) + result;
        num = num.slice(0, num.length - 3);
    }
    if (num) { result = num + result; }
    return result;
}
console.log(toThousands(str));  複製代碼

第四種:懶人版

function toThousands(num) {
    return (num || 0).toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,');
}
console.log(toThousands(str));複製代碼

4一、Promise的解釋:

          Promise是異步b編程解決方案之一。最大的好處是提供了一個then,來爲異步提供回調函數。其先進之處是能夠在then方法中繼續寫Promise對象並f返回,而後繼續用then來進行回調操做,而且可以在外層捕獲異步函數的異常信息。
⑴Promise用法:

const fn = new Promise(function(resolve,reject){
    axios.get(url).then(res => {
        resolve(res);
    }).catch(err => {
        reject(err);
    })
}).then((res) => {
    console.log(res);
}).catch((err) => {
    console.log(err);
})複製代碼

⑵Promise原理:
      在Promise的內部,有一個狀態管理器的存在,有三種狀態:pending、fulfilled、rejected。
      ①promised對象初始化爲pending;
      ②當調用resolve(成功),會由pending => fulfilled。
      ③dda當調用reject(失敗),會由pending => rejected。
        看上面的的代碼中的resolve(num)實際上是將promise的狀態由pending改成fulfilled,而後向then的成功回掉函數傳值,reject反之。可是須要記住的是注意promsie狀態 只能由 pending => fulfilled/rejected, 一旦修改就不能再變。promise.then方法每次調用,都返回一個新的promise對象 因此能夠鏈式寫法(不管resolve仍是reject都是這樣)

⑶Promise的三個狀態:
    pending:異步任務正在進行中;
    resolved(也能夠叫fulfilled),異步任務執行成功;
    rejected,異步任務執行失敗。

⑷Promise對象初始化:
     ① new Promise(fn);
     ②Promise.resolve(fn);

⑸Promise特色:
    ①對象的狀態不受外界影響;
    ②一旦狀態改變,就不會再變,任什麼時候候均可以獲得這個結果;

⑹Promise方法:
       ①Promise.all( [promise1,promise2,promise3] ).then( );
做爲參數的幾個promise對象一旦有一個的狀態爲rejected,則all的返回值就是rejected。
當這幾個做爲參數的函數的返回狀態爲fulfilled時,至於輸出的時間就要看誰跑的慢了。

var   p1 = Promise.resolve(1),
      p2 = Promise.reject(2),
      p3 = Promise.resolve(3);
Promise.all([p1, p2, p3]).then((res)=>{
    //then方法不會被執行
    console.log(results);
}).catch((err)=>{
    //catch方法將會被執行,輸出結果爲:2
    console.log(err);
});複製代碼

        ②promise.race( ):從字面意思上理解就是競速,那麼理解起來上就簡單多了,也就是說在數組中的元素實例那個率先改變狀態,就向下傳遞誰的狀態和異步結果。可是,其他的仍是會繼續進行的。

let p1 = new Promise((resolve)=>{
  setTimeout(()=>{
    console.log('1s') //1s後輸出
    resolve(1)
  },1000)
})
let p10 = new Promise((resolve)=>{
  setTimeout(()=>{
    console.log('10s') //10s後輸出
    resolve(10) //不傳遞
  },10000)
})
let p5 = new Promise((resolve)=>{
  setTimeout(()=>{
    console.log('5s') //5s後輸出
    resolve(5) //不傳遞
  },5000)
})
Promise.race([p1, p10, p5]).then((res)=>{
    console.log(res); // 最後輸出
})
//結果:
1s
1
5s
10s複製代碼

4二、模塊化

模塊定義:將一個複雜的程序依據必定的規則封裝成幾個塊(文件),並進行組合在一塊兒,塊的內部數據是私有的,只是向外部暴露一些接口(方法)與外部其它模塊通訊。

模塊的組成:數據(內部屬性)、操做數據的行爲(內部的函數);

模塊化:編碼時是按照模塊一個一個編碼的, 整個項目就是一個模塊化的項目。可以幫助開發者拆分和組織代碼,解決JS全局做用域的污染問題。

第一種:CommonJS(同步加載)主要用在Node開發上,每一個文件j就是一個模塊,每一個文件都有本身的一個做用域。經過module.exports暴露public成員,經過let xm = require(模塊路徑)來引用。
第二種:AMD:AMD規範的被依賴模塊是異步加載的,而定義的模塊是被看成回調函數來執行的,依賴於require.js模塊管理工具庫。固然,AMD規範不是採用匿名函數自調用的方式來封裝,咱們依然能夠利用閉包的原理來實現模塊的私有成員和公有成員。

// 定義AMD規範的模塊
define([function() {
  return 模塊
})
//私有成員
define(['module1', 'module2'], function(m1, m2) {
  let x = 1;
  function add() {
    x += 1;
    return x;
  }
  return { add };
})複製代碼

第三種:CMD:CMD 是 SeaJS 在推廣過程當中對模塊定義的規範化產出。AMD 推崇依賴前置,CMD 推崇依賴就近。CMD集成了CommonJS和AMD的的特色,支持同步和異步加載模塊。CMD加載完某個依賴模塊後並不執行,只是下載而已,在全部依賴模塊加載完成後進入主邏輯,遇到require語句的時候才執行對應的模塊,這樣模塊的執行順序和書寫順序是徹底一致的。所以,在CMD中require函數同步加載模塊時沒有HTTP請求過程。

define(function(require, exports, module) {
  //  同步加載模塊
  var a = require('./a');
  a.doSomething();
  // 異步加載一個模塊,在加載完成時,執行回調
  require.async(['./b'], function(b) {
    b.doSomething();
  });
  // 對外暴露成員
  exports.doSomething = function() {};
});
// 使用模塊
seajs.use('path');複製代碼

第四種:module:ES6的模塊化已經不是規範了,而是JS語言的特性。隨着ES6的推出,AMD和CMD也隨之成爲了歷史。ES6模塊與模塊化規範相比,有兩大特色:

     模塊化規範輸出的是一個值的拷貝,ES6 模塊輸出的是值的引用。模塊化規範是運行時加載,ES6 模塊是編譯時輸出接口。
      模塊化規範輸出的是一個對象,該對象只有在腳本運行完纔會生成。而 ES6 模塊不是對象,ES6 module 是一個多對象輸出,多對象加載的模型。從原理上來講,模塊化規範是匿名函數自調用的封裝,而ES6 module則是用匿名函數自調用去調用輸出的成員。

//react種運用的就是ES6的模塊化
var module1 = value1;
var module2 = value2;
export {module1,module2}  //導出模塊
import {module1,module2} from "模塊路徑/模塊名" //引入模塊複製代碼

4三、new的原理

new其實是在堆內存中開闢一個空間。
①建立一個空對象,構造函數中的this指向這個空對象;
②這個新對象被執行[ [ 原型 ] ]鏈接;
③執行構造函數方法,屬性和方法被添加到this引用的對象中;
④若是構造函數中沒有返回其它對象,那麼返回this,即建立的這個的新對象,不然,返回構造函數中返回的對象。

function _new(){
    let target = {};   //建立的新對象
    let [constructor,...args] = [...arguments];
       //執行[[原型]]鏈接,target是constructor的實例
    target.__proto__ = constructor.prototype;
        //執行構造函數,將屬性或方法添加到建立的空對象上
    let result = constructor.prototype;
    if(result && (typeof (result) == "object" || typeof (result) == "function")){
           //若是構造函數執行的結構返回的是一個對象,那麼返回這個對象
        return result;
    }
       //若是構造函數返回的不是一個對象,返回建立的對象
    return target;
}複製代碼

本身理解的new:
         new其實是在堆內存中開闢一個新的空間。首先建立一個空對象obj,而後呢,把這個空對象的原型(__proto__)和構造函數的原型對象(constructor.prototype)鏈接(說白了就是等於);而後執行函數中的代碼,就是爲這個新對象添加屬性和方法。最後進行判斷其返回值,若是構造函數返回的是一個對象,那就返回這個對象,若是不是,那就返回咱們建立的對象。

4四、數組和類數組

類數組:
①擁有length屬性,其它屬性(索引)爲非負整數;
②不具備數組所具備的方法;
③類數組是一個普通對象,而真實的數組是Array類型。
常見的類數組:arguments,document.querySelectorAll獲得的列表,jQuery對象($("div"));

4五、call、apply、bind封裝

call函數

// 思路:將要改變this指向的方法掛到目標this上執行並返回
Function.prototype.mycall = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('not funciton')
  }
  context = context || window
  context.fn = this
  let arg = [...arguments].slice(1)
  let result = context.fn(...arg)
  delete context.fn
  return result
}複製代碼

apply函數

// 思路:將要改變this指向的方法掛到目標this上執行並返回
Function.prototype.myapply = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('not funciton')
  }
  context = context || window
  context.fn = this
  let result
  if (arguments[1]) {
    result = context.fn(...arguments[1])
  } else {
    result = context.fn()
  }
  delete context.fn
  return result
}複製代碼

bind函數

// 思路:相似call,但返回的是函數
Function.prototype.mybind = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  let _this = this
  let arg = [...arguments].slice(1)
  return function F() {
    // 處理函數使用new的狀況
    if (this instanceof F) {
      return new _this(...arg, ...arguments)
    } else {
      return _this.apply(context, arg.concat(...arguments))
    }
  }
}複製代碼

4六、如何讓(a == 1 && a == 2 && a == 3)的值爲true?

          " == "操做符在左右數據不一致的時候,會先進行隱式轉換,該值意味着不是基本數據類型,由於若是a是null或者undefined、bool類型都不可能返回true;能夠推測a是複雜數據類型。

方法一:數組的 toString 接口默認調用數組的 join 方法,從新 join 方法

let a = [1,2,3];
a.join = a.shift;
console.log(a == 1 && a == 2 && a == 3) //true複製代碼

方法二:利用數據劫持(Proxy/Object.definedProperty)

let i = 1;
let a = new Proxy({},{
	i:1,
	get:function(){
		return () => this.i++
	}
});
console.log(a == 1 && a == 2 && a == 3);複製代碼
相關文章
相關標籤/搜索