JavaScript小記(持續更新)

學習js遇到的疑問和js基礎都記錄在這裏,持續更新中。javascript

JavaScript小記(持續更新)

一、正則表達式

/b+/g 至少出現一次b(1~n次)php

/b*/g 能夠不出現b,也能夠出現一次或屢次(0~n次)css

/b{n,m}/g 最少出現n次b,最多出現m次b(n~m次)html

/colou?r/g 能夠匹配color或colour,?表示前面的字符最多隻出現一次(0次或1次)前端

二、代碼回收規則

1.全局變量不會被回收java

2.局部變量會被回收,也就是函數一旦運行完之後,函數內部的東西都會被銷燬node

3.只要被另一個做用域所引用就不會被回收jquery

三、數據類型

基本數據類型:undefined、null、string、number、boolean、symbolios

複雜數據類型:objectes6

引用類型:array、function、object

四、JS中數據類型的判斷( typeof,instanceof,constructor,Object.prototype.toString.call() )

  • typeof

    對一個值使用typeof操做符可能返回:

    undefined、string、number、boolean、symbol、object(對象或null)、function

    console.log(typeof 2);               // number
    console.log(typeof true);            // boolean
    console.log(typeof 'str');           // string
    console.log(typeof []);              // object  []數組的數據類型在 typeof 中被解釋爲object
    console.log(typeof function(){});    // function
    console.log(typeof {});              // object
    console.log(typeof undefined);       // undefined
    console.log(typeof null);            // object    null 的數據類型被 typeof 解釋爲 object
  • instanceof

    只有引用數據類型(Array,Function,Object)被精準判斷,其餘(數值Number,布爾值Boolean,字符串String)字面值不能被instanceof精準判斷。

    console.log(2 instanceof Number);                    // false
    console.log(true instanceof Boolean);                // false 
    console.log('str' instanceof String);                // false  
    console.log([] instanceof Array);                    // true
    console.log(function(){} instanceof Function);       // true
    console.log({} instanceof Object);                   // true    
    // console.log(undefined instanceof Undefined);
    // console.log(null instanceof Null);
  • constructor

    console.log((2).constructor === Number);                  // true
    console.log((true).constructor === Boolean);              // true
    console.log(('str').constructor === String);             // true
    console.log(([]).constructor === Array);                  // true
    console.log((function() {}).constructor === Function);  // true
    console.log(({}).constructor === Object);               // true

    用costructor來判斷類型看起來是完美的,然而,若是我建立一個對象,更改它的原型,這種方式也變得不可靠了。

    function Fn(){};
    
    Fn.prototype = new Array();
    
    var f = new Fn();
    
    console.log(f.constructor === Fn);    // false
    console.log(f.constructor === Array); // true
  • object.prototype.toString.call()

    console.log(Object.prototype.toString.call(2));                // [object Number]
    console.log(Object.prototype.toString.call(true));            // [object Boolean]
    console.log(Object.prototype.toString.call('str'));            // [object String]
    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(undefined));        // [object Undefined]
    console.log(Object.prototype.toString.call(null));            // [object Null]

​ 使用 Object 對象的原型方法 toString ,使用 call 進行狸貓換太子,借用Object的 toString 方法結果精準的顯示咱們須要的數據類型。就算咱們改變對象的原型,依然會顯示正確的數據類型。

五、JavaScript實現繼承共六種方式

詳細請看這篇文章:https://www.cnblogs.com/humin...

  • 原型鏈繼承:利用原型讓一個引用類型繼承另一個引用類型的屬性和方法。
  • 構造函數繼承:在子類型構造函數的內部調用超類構造函數,經過使用call()和apply()方法能夠在新建立的對象上執行構造函數。
  • 組合繼承:將原型鏈和借用構造函數的技術組合在一塊,從而發揮二者之長的一種繼承模式。
  • 原型式繼承:藉助原型能夠基於已有的對象建立新對象,同時還沒必要須所以建立自定義的類型。
  • 寄生式繼承:建立一個僅用於封裝繼承過程的函數,該函數在內部以某種方式來加強對象,最後再像真正是它作了全部工做同樣返回對象。
  • 寄生組合式繼承:經過借用函數來繼承屬性,經過原型鏈的混成形式來繼承方法。

六、改變做用域鏈的方法

with、try...catch、eval

七、Ajax

Ajax技術核心就是XMLHttpRequest對象。

(1)設置請求參數(請求方式,請求頁面的相對路徑,是否異步)

(2)設置回調函數,一個處理服務器響應的函數,使用 onreadystatechange ,相似函數指針

(3)獲取異步對象的readyState 屬性:該屬性存有服務器響應的狀態信息。每當 readyState 改變時,onreadystatechange 函數就會被執行。

(4)判斷響應報文的狀態,若爲200說明服務器正常運行並返回響應數據。

(5)讀取響應數據,能夠經過 responseText 屬性來取回由服務器返回的數據。

var xhr = new XMLHttpRequest();                // 建立Ajax對象
xhr.open('get', 'aabb.php', true);            // xhr發送請求
xhr.send(null);
xhr.onreadystatechange = function() {         // xhr獲取響應
    if(xhr.readyState == 4) {
        if(xhr.status == 200) {
            console.log(xhr.responseText);
        }
    }
}
 /* ajax返回的狀態:
     0:(未初始化)請求尚未創建(open執行前) 
     1:(載入)請求創建了還沒發送(執行了open) 
     2:(載入完成)請求正式發送,send()方法執行完成,已經接收到所有響應內容(執行了send) 
     3:(交互)請求已受理,有部分數據能夠用,但尚未處理完成,正在解析響應內容
     4:(完成)響應內容解析完成,能夠在客戶端調用了 
*/

八、Ajax,jQuery ajax,axios和fetch的區別

Ajax:

​ ajax,最先出現的發送後端請求技術,隸屬於原始js中,核心使用XMLHttpRequest對象,多個請求之間若是有前後關係的話,就會出現回調地獄。

Jquery Ajax:

是jQuery框架中的發送後端請求技術,因爲jQuery是基於原始的基礎上作的封裝,因此,jquery Ajax天然也是對原始ajax的封裝

$.ajax({
   type: 'POST',
   url: url,
   data: data,
   dataType: dataType,
   success: function () {},
   error: function () {}
});

​ 優缺點:

  • 自己是針對MVC的編程,不符合如今前端MVVM的浪潮
  • 基於原生的XHR開發,XHR自己的架構不清晰,已經有了fetch的替代方案
  • JQuery整個項目太大,單純使用ajax卻要引入整個JQuery很是的不合理(採起個性化打包的方案又不能享受CDN服務)

Fetch:

​ fetch號稱是AJAX的替代品,是在ES6出現的,使用了ES6中的promise對象。Fetch是基於promise設計的。Fetch的代碼結構比起ajax簡單多了,參數有點像jQuery ajax。

​ 可是,必定記住fetch不是ajax的進一步封裝,而是原生js。Fetch函數就是原生js,沒有使用XMLHttpRequest對象。

try {
  let response = await fetch(url);
  let data = response.json();
  console.log(data);
} catch(e) {
  console.log("Oops, error", e);
}

axios:

​ axios不是原生JS的,須要進行安裝,它不但能夠在客戶端使用,並且能夠在nodejs端使用。Axios也能夠在請求和響應階段進行攔截。一樣也是基於promise對象的。

https://blog.csdn.net/Roselan...

axios({
    method: 'post',
    url: '/user/12345',
    data: {
        firstName: 'Fred',
        lastName: 'Flintstone'
    }
})
.then(function (response) {
    console.log(response);
})
.catch(function (error) {
    console.log(error);
});

優缺點:

  • 從 node.js 建立 http 請求
  • 支持 Promise API
  • 客戶端支持防止CSRF
  • 提供了一些併發請求的接口(重要,方便了不少的操做)

九、replace(/s/g,"")

"/ /"這個是固定寫法,"s"是轉移符號用以匹配任何空白字符,包括空格、製表符、換頁符等等,"g"表示全局匹配將替換全部匹配的子串,若是不加"g"當匹配到第一個後就結束了。

這個例子的意思就是將原字符串中的全部空白字符替換成"",好比"abc d efg "字樣的字符串使用這個函數後將變成"abcdefg"

通常用來把字符串中全部的空格去掉

十、Map和ForEach的區別

  • forEach(): 數組中的每一個元素執行一次回調函數
  • map(): 返回一個由原數組中的每一個元素調用一個指定方法後的返回值組成的新數組

示例

下方提供了一個數組,若是咱們想將其中的每個元素翻倍,咱們可使用mapforEach來達到目的。

let arr = [1, 2, 3, 4, 5];

ForEach

注意,forEach是不會返回有意義的值的。
咱們在回調函數中直接修改arr的值。

arr.forEach((num, index) => {
    return arr[index] = num * 2;
});

Map

let doubled = arr.map(num => {
    return num * 2;
});

十一、indexOf與search的區別

  • indexOf() 方法可返回某個指定的字符串值在字符串中首次出現的位置,若是沒有找到返回-1

    • 該方法將從頭至尾地檢索字符串stringObject,看它是否含有子串searchvalue。開始檢索的位置在字符串的fromindex處。若是沒有fromindex參數則從字符串的開頭檢索。若是找到一個searchvalue,則返回searchvalue的第一次出現的位置。stringObjec中的字符串位置是從0開始的。
    • indexOf()方法對大小寫敏感。
  • search方法用於檢索字符串中指定的子字符串,檢索與正則表達式相匹配的子字符串。若是沒有找到,返回-1。

    • search() 方法不執行全局匹配,它將忽略標誌 g。它同時忽略 regexp 的 lastIndex 屬性,而且老是從字符串的開始進行檢索,這意味着它老是返回 stringObject 的第一個匹配的位置。
    • search() 方法對大小寫敏感。

indexOf與search的區別

search()的參數必須是正則表達式,而indexOf()的參數只是普通的字符串。indexOf()是比search()更加底層的方法。

若是隻是對一個具體字符串來檢索,那麼使用indexOf()的系統資源消耗更小,效率更高;若是查找具備某些特徵的字符串(例如查找以a開頭,後面是數字的字符串),那麼indexOf()就無能爲力,必需要使用正則表達式和search()方法了。

大可能是時候用indexOf()不是爲了真的想知道子字符串的位置,而是想知道長字符串中有沒有包含這個子字符串。r若是返回索引爲-1,那麼說明沒有,反之則有。

十二、String對象中slice()、substring()、substr()的用法與區別

  • slice() 方法可提取字符串的某個部分,並以新的字符串返回被提取的部分。

    • 語法:stringObject.slice(start,end)
    • 返回一個新的字符串。包括字符串 stringObject 從 start 開始(包括 start)到 end 結束(不包括 end)爲止的全部字符。
    • 若是是負數,則該參數規定的是從字符串的尾部開始算起的位置。也就是說,-1 指字符串的最後一個字符,-2 指倒數第二個字符,以此類推。
  • substring() 方法用於提取字符串中介於兩個指定下標之間的字符。

    • 語法:stringObject.substring(start,stop)
    • 一個新的字符串,該字符串值包含 stringObject 的一個子字符串,其內容是從 start 處到 stop-1 處的全部字符,其長度爲 stop減 start。
    • 若是 start 比 stop 大,那麼該方法在提取子串以前會先交換這兩個參數。
    • 與 slice() 和 substr() 方法不一樣的是,substring() 不接受負的參數。
  • substr() 方法可在字符串中抽取從 start 下標開始的指定數目的字符。

    • 語法:stringObject.substr(start,length)
    • 一個新的字符串,包含從 stringObject 的 start(包括 start 所指的字符) 處開始的 length 個字符。若是沒有指定 length,那麼返回的字符串包含從 start 到 stringObject 的結尾的字符。
    • 若是是負數,那麼該參數聲明從字符串的尾部開始算起的位置。也就是說,-1 指字符串中最後一個字符,-2 指倒數第二個字符,以此類推。
    • ECMAscript 沒有對該方法進行標準化,所以反對使用它。

String 對象的方法 slice()、substring() 和 substr() (不建議使用)均可返回字符串的指定部分。slice() 比 substring() 要靈活一些,由於它容許使用負數做爲參數。slice() 與 substr() 有所不一樣,由於它用兩個字符的位置來指定子串,而 substr() 則用字符位置和長度來指定子串。

還要注意的是,String.slice() 與 Array.slice() 類似。

1三、Array對象中slice() 、splice()的區別

slice() 方法可從已有的數組中返回選定的元素。

  • 語法:arrayObject.slice(start,end)
  • 返回一個新的數組,包含從 start 到 end (不包括該元素)的 arrayObject 中的元素。在只有一個參數的狀況下,返回從該參數指定位置開始到當前數組末尾的全部項。
  • 該方法不改變原字符串,而是返回新的字符串。若是想刪除數組中的一段元素,應該使用方法 Array.splice()。
  • 若是slice()方法的參數中有一個負數,則用數組長度加上該數來肯定相應的位置。
  • 若是結束爲止小於起始位置,則返回空數組。

splice() 方法主要用途是向數組的中部插入項。

  • 語法:arrayObject.splice(index,howmany,item1,.....,itemX)
  • index 必需。整數,規定添加/刪除項目的位置,使用負數可從數組結尾處規定位置。
    howmany 必需。要刪除的項目數量。若是設置爲 0,則不會刪除項目。
    item1, ..., itemX 可選。向數組添加的新項目。
  • splice()方法始終都會返回一個數組,該數組中包含從原始數組中刪除的項(若是沒有刪除任何項,則返回一個空數組)

1四、Object.assign()

語法:Obejct.assign(target,...sources)

用途:未來自一個或多個源對象中的值複製到一個目標對象,它將返回目標對象。其中對象的繼承屬性和不可枚舉屬性是不能拷貝的,因此不能使用它來實現深拷貝。

第一級屬性深拷貝,從第二級屬性開始就是淺拷貝。

若是多個源對象具備同名屬性,則排位靠後的源對象會覆蓋排位靠前的。但null或undefined被視爲空對象同樣對待,不會覆蓋。

var receiver = {};
Object.assign(receiver, 
    {
        type: "js", 
        name:"file.js"
    },
    {
        type: "css"
    }
)
console.log(receiver); // {type: "css", name: "file.js"}
//實現 deepAssign({a: {b: 1, c: 2}}, {a: {c: 3}});
//=> {a: {b: 1, c: 3}}

// 只有兩個對象
function deepAssign(obj1, obj2){
    for(var item in obj2){
        obj1[item] = typeof obj2[item] === 'object' ? deepClone(obj1[item], obj2[item]) : obj2[item];
    }
    return obj1;
}
// 通用
function deepAssign() {
  var args = Array.from(arguments);
  return args.reduce(deepClone, args[0]);

  function deepClone(target, obj){
    if(!target) target = Array.isArray(obj) ? [] : {};
      for(key in obj){
        target[key] = typeof obj[key] ==="object" ? deepClone(target[key], obj[key]) : obj[key]
      }
    return target;
  }
}

1五、ES6之Array.from()方法

一、類數組對象:所謂類數組對象,最基本的要求就是具備length屬性的對象

二、Array.from()方法就是將一個類數組對象或者可遍歷對象轉換成一個真正的數組。

​ Array.from有三個參數,Array.from(arrayLike[, mapFn[, thisArg]]),

​ arrayLike:想要轉換成數組的僞數組對象或可迭代對象;

​ mapFn:若是指定了該參數,新數組中的每一個元素會執行該回調函數;

​ thisArg:可選參數,執行回調函數 mapFn 時 this 對象。

​ 該方法的返回值是一個新的數組實例(真正的數組)。

例1:Array.from ({length:n}, Fn) 將類數組轉換爲數組

Array.from({length:3}, () => 'jack') //["jack", "jack", "jack"]
 
Array.from({length:3}, item => (item = {'name':'shao','age':18})) //[{'name':'shao','age':18}, {'name':'shao','age':18}, {'name':'shao','age':18}]
 
Array.from({length:2}, (v, i) => item = {index:i});
//生成一個index從0到4的數組對象[{index: 0},{index: 1}]
let array = {
    0: 'name', 
    1: 'age',
    2: 'sex',
    3: ['user1','user2','user3'],
    'length': 4
}
let arr = Array.from(array)
console.log(arr) // ['name','age','sex',['user1','user2','user3']]

若是將上面代碼中length屬性去掉arr將會是一個長度爲0的空數組,若是將代碼修改一下,就是具備length屬性,可是對象的屬性名再也不是數字類型的,而是其餘字符串型的,代碼以下:

let array = {
    'name': 'name', 
    'age': 'age',
    'sex': 'sex',
    'user': ['user1','user2','user3'],
    'length': 4
}
let arr = Array.from(array )
console.log(arr)  // [ undefined, undefined, undefined, undefined ]

會發現結果是長度爲4,元素均爲undefined的數組,因而可知,要將一個類數組對象轉換爲一個真正的數組,必須具有如下條件:
(1)該類數組對象必須具備length屬性,用於指定數組的長度。若是沒有length屬性,那麼轉換後的數組是一個空數組。
(2)該類數組對象的屬性名必須爲數值型或字符串型的數字
該類數組對象的屬性名能夠加引號,也能夠不加引號

例2:Array.from (obj, mapFn)

obj指的是數組對象、相似數組對象或者是set對象,map指的是對數組中的元素進行處理的方法。

let arr = [1,1,2,5,5,6,7,8,9]
let set = new Set(arr)
console.log(Array.from(set))  // [1, 2, 5, 6, 7, 8, 9]
//將數組中布爾值爲false的成員指爲0
Array.from([1, ,2,3,3], x => x || 0) //[1,0,2,3,3]
 
//將一個相似數組的對象轉爲一個數組,並在原來的基礎上乘以2倍
let arrayLike = { '0': '2', '1': '4', '2': '5', length: 3 }
Array.from(arrayLike, x => x*2) //[4,8,10]
 
//將一個set對象轉爲數組,並在原來的基礎上乘以2倍
Array.from(new Set([1,2,3,4]), x => x*2) //[2,4,6,8]

例3:Array.from(string) 將字符串轉換爲數組

Array.from('abc') //['a','b','c']

例子4——將Map解構轉爲數組,最方便的作法就是使用擴展運算符(...)

const myMap = new Map().set(true, 7)
console.log(myMap); //Map(1) {true => 7}
console.log([...myMap]); //[true ,7]

1六、數組去重的方法

一、利用ES6 Set去重(ES6中最經常使用)

function unique (arr) {
  return Array.from(new Set(arr))
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
 //[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {}, {}]

不考慮兼容性,這種去重的方法代碼最少。這種方法沒法去掉「{}」空對象。

二、利用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]){         
                arr.splice(j,1);
                j--;
            }
        }
    }
    return arr;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
    console.log(unique(arr))
    //[1, "true", 15, false, undefined, NaN, NaN, "NaN", "a", {…}, {…}]     
    //NaN和{}沒有去重,兩個null直接消失了

三、for...of + includes()

雙重for循環的升級版,外層用 for...of 語句替換 for 循環,把內層循環改成 includes()

先建立一個空數組,當 includes() 返回 false 的時候,就將該元素 push 到空數組中

相似的,還能夠用 indexOf() 來替代 includes()

function unique(arr) {
    var result = []
    for (var i of arr) {
        !result.includes(i) && result.push(i)
    }
    return result
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
   // [1, "true", true, 15, false, undefined, null, NaN, NaN, "NaN", 0, "a", {…}, {…}]      //NaN、{}沒有去重

四、利用indexOf去重

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!');
        return;
    }
    var array = [];
    for (var i = 0; i < arr.length; i++) {
        if (array.indexOf(arr[i]) === -1) {
            array.push(arr[i]);
        }
    }
    return array;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
   // [1, "true", true, 15, false, undefined, null, NaN, NaN, "NaN", 0, "a", {…}, {…}]      //NaN、{}沒有去重

五、利用sort()

function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return;
    }
    arr = arr.sort()
    var arrry= [arr[0]];
    for (var i = 1; i < arr.length; i++) {
        if (arr[i] !== arr[i-1]) {
            arrry.push(arr[i]);
        }
    }
    return arrry;
}
     var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
        console.log(unique(arr))
// [0, 1, 15, "NaN", NaN, NaN, {…}, {…}, "a", false, null, true, "true", undefined]      // NaN、{}沒有去重

6 、利用hasOwnProperty

function unique(arr) {
    var obj = {};
    return arr.filter(function(item, index, arr){
        return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true)
    })
}
    var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
    console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}]   //全部的都去重了

七、利用filter

function unique(arr) {
  return arr.filter(function(item, index, arr) {
    //當前元素,在原始數組中的第一個索引==當前索引值,不然返回當前元素
    return arr.indexOf(item, 0) === index;
  });
}
    var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
        console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, "NaN", 0, "a", {…}, {…}]

八、利用Map數據結構去重

function arrayNonRepeatfy(arr) {
  let map = new Map();
  let array = new Array();  // 數組用於返回結果
  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);   // 若是沒有該key值
      array.push(arr[i]);
    }
  } 
  return array;
}
 var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
 console.log(arrayNonRepeatfy(arr))
//[1, "a", "true", true, 15, false, 1, {…}, null, NaN, NaN, "NaN", 0, "a", {…}, undefined]
// {}沒去

建立一個空Map數據結構,遍歷須要去重的數組,把數組的每個元素做爲key存到Map中。因爲Map中不會出現相同的key值,因此最終獲得的就是去重後的結果。

九、利用reduce+includes

function unique(arr){
    return arr.reduce((prev,cur) => prev.includes(cur) ? prev : [...prev,cur],[]);
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr));
// [1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}, {…}]

十、for...of + Object

首先建立一個空對象,而後用 for 循環遍歷

利用對象的屬性不會重複這一特性,校驗數組元素是否重複

function unique(arr){
    let result = []
    let obj = {}
    for (let i of arr) {
        if (!obj[i]) {
            result.push(i)
            obj[i] = 1
        }
    }
    return result
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr));
// [1, "true", 15, false, undefined, null, NaN, 0, "a", {…}]

1七、對象數組去重

const objArr = [{
    name: '名稱1'
},{
    name: '名稱2'
},{
    name: '名稱3'
},{
    name: '名稱1'
},{
    name: '名稱2'
}]

const obj = {}
const newObjArr = []
for(let i = 0; i < objArr.length; i++){
    if(!obj[objArr[i].name]){
        newObjArr.push(objArr[i]);
        obj[objArr[i].name] = true
    }
}

console.log(newObjArr);
/*結果:
     [{
        name: '名稱1'
      },{
        name: '名稱2'
      },{
        name: '名稱3'
      }]
*/
const objArr = [{
    name: '名稱1'
},{
    name: '名稱2'
},{
    name: '名稱3'
},{
    name: '名稱1'
},{
    name: '名稱2'
}]
const obj = {}
const newObjArr =  objArr.reduce((prev, curr)=>{
    obj[curr.name] ? true : obj[curr.name] = true && prev.push(curr);
    return prev
}, [])
console.log(newObjArr)

1八、fetch發送post請求時,老是發送兩次,第一次狀態碼204,第二次才成功

由於你用的fetch post修改了請求頭,致使fetch第一次發送一個options請求,詢問服務器是否支持修改的請求頭,如過服務器支持,那麼將會再次發送真正的請求。

1九、跨域(jsonp,ajax)

什麼是跨域

  • 瀏覽器有同源策略,不容許ajax訪問其餘域接口。
  • 跨域條件:協議、域名、端口有一個不一樣就算跨域。

JSONP:ajax請求受同源策略影響,不容許進行跨域請求,而script標籤src屬性中的連接卻能夠訪問跨域的js腳本,利用這個特性,服務端再也不返回JSON格式的數據,而是返回一段調用某個函數的js代碼,在src中進行了調用,這樣實現了跨域。

JSONP的缺點:

JSON只支持get,由於script標籤只能使用get請求;

JSONP須要後端配合返回指定格式的數據。

ajax與jsonp的區別

  • ajax和jsonp這兩種技術再調用方式上「看起來很像」,目的也同樣,都是請求一個url,而後把服務器返回的數據進行處理,所以jquery和ext等框架都把jsonp做爲ajax的一種形式進行了封裝。
  • 但ajax和jsonp在本質上是不一樣的東西。ajax的核心是經過XmlHttpRequest獲取非本頁內容,而jsonp的核心則是動態添加。

如何實現跨域

JSONP:經過動態建立script,再請求一個帶參網址實現跨域通訊。

document.domain + iframe跨域:兩個頁面都經過js強制設置document.domain爲基礎主域,就實現了同域。

location.hash + iframe跨域:a欲與b跨域相互通訊,經過中間頁c來實現。 三個頁面,不一樣域之間利用iframe的location.hash傳值,相同域之間直接js訪問來通訊。

window.name + iframe跨域:經過iframe的src屬性由外域轉向本地域,跨域數據即由iframe的window.name從外域傳遞到本地域。

postMessage跨域:能夠跨域操做的window屬性之一。

CORS:服務端設置Access-Control-Allow-Origin便可,前端無須設置,若要帶cookie請求,先後端都須要設置。

代理跨域:起一個代理服務器,實現數據的轉發

20、優化SPA應用的首屏加載速度慢的問題

  • 將公用的JS庫經過script標籤外部引入,減少 app.bundle 的大小,讓瀏覽器並行下載資源文件,提升下載速度;
  • 在配置路由時,頁面和組件使用懶加載的方式引入,進一步縮小 app.bundle 的體積,在調用某個組件時再加載對應的js文件;
  • 加一個首屏loading圖,提高用戶體驗;

2一、jQuery獲取的dom對象和原生的dom對象的區別

js原生獲取的dom是一個對象,jQuery對象就是一個數組對象,其實就是選擇出來的元素的數組集合,因此說他們二者是不一樣的對象類型不等價。

原生DOM對象轉jQuery對象:

var box = document.getElementById('box');
var $box = $(box);

jQuery對象轉原生DOM對象:

var $box = $('#box');
var box = $box[0];

2二、深拷貝和淺拷貝

區別

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

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

爲何要使用深拷貝?

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

深拷貝數組

1.直接遍歷

function copyArr(arr) {
    let res = []
    for (let i = 0; i < arr.length; i++) {
     res.push(arr[i])
    }
    return res
}
var arr = [1,2,3,4,5]
var arr2 = copyArr(arr)

2.slice()

var arr = [1,2,3,4,5]
var arr2 = arr.slice()

3.concat()

var arr = [1,2,3,4,5]
var arr2 = arr.concat()

深拷貝對象

1.迭代遞歸法

這是最常規的方法,思想很簡單:就是對對象進行迭代操做,對它的每一個值進行遞歸深拷貝。

function deepClone(obj){
    var newObj= obj instanceof Array ? []:{};
    for(var item in obj){
        var temple= typeof obj[item] == 'object' ? deepClone(obj[item]):obj[item];
        newObj[item] = temple;
    }
    return newObj;
}

2.ES6的Object.assign(除了根屬性是深拷貝,其他都是淺拷貝)

3.ES6擴展運算符:...

「一招鮮,吃遍天」 ——序列化反序列化法

JSON.parse(JSON.stringify(XXXX))

var array = [
    { number: 1 },
    { number: 2 },
    { number: 3 }
];
var copyArray = JSON.parse(JSON.stringify(array))
copyArray[0].number = 100;
console.log(array); //  [{number: 1}, { number: 2 }, { number: 3 }]
console.log(copyArray); // [{number: 100}, { number: 2 }, { number: 3 }]

它也只能深拷貝對象和數組,對於其餘種類的對象,會失真。這種方法比較適合日常開發中使用,由於一般不須要考慮對象和數組以外的類型。

2三、清空數組的方法

方式1:splice函數

arrayObject.splice(index,howmany,element1,.....,elementX)

index:必選,規定從何處添加/刪除元素。

howmany:必選,規定應該刪除多少元素。未規定此參數,則刪除從 index 開始到原數組結尾的全部元素。

element1:可選,規定要添加到數組的新元素。

<script type ="text/javascript">  
  var arr = [1,2,3,4];  
   arr.splice(0,arr.length);  
</script>

方式2:給數組的length賦值爲0

<script type ="text/javascript">  
  var arr = [1,2,3,4];  
   arr.length = 0;
</script>

賦予數組的長度小於自己的長度,數組中後面的元素將被截斷。

賦予數組的長度大於自己的長度,將擴展數組長度,多的元素爲undefined。

方式3:直接賦予新數組 []

<script type ="text/javascript">  
  var arr = [1,2,3,4];  
   arr = [];
</script>

這種方式爲將arr從新複製爲空數組,以前的數組若是沒有被引用,將等待垃圾回收。

2四、Pomise.all的使用

所有完成以後統一執行

let p1 = new Promise((resolve, reject) => {
  resolve('成功了')
})

let p2 = new Promise((resolve, reject) => {
  resolve('success')
})

let p3 = Promise.reject('失敗')

Promise.all([p1, p2]).then((result) => {
  console.log(result)               //['成功了', 'success']
}).catch((error) => {
  console.log(error)
})

Promise.all([p1,p2,p3]).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)      // 失敗了,打出 '失敗'
})
let wake = (time) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`${time / 1000}秒後醒來`)
    }, time)
  })
}

let p1 = wake(3000)
let p2 = wake(2000)

Promise.all([p1, p2]).then((result) => {
  console.log(result)       // [ '3秒後醒來', '2秒後醒來' ]
}).catch((error) => {
  console.log(error)
})

須要特別注意的是,Promise.all得到的成功結果的數組裏面的數據順序和Promise.all接收到的數組順序是一致的,即p1的結果在前,即使p1的結果獲取的比p2要晚。這帶來了一個絕大的好處:在前端開發請求數據的過程當中,偶爾會遇到發送多個請求並根據請求順序獲取和使用數據的場景,使用Promise.all毫無疑問能夠解決這個問題。

2五、簡單實現 Promise.all

function promiseAll(promises) {
    return new Promise((resolve, reject) => {
      let resultCount = 0;
      let results = new Array(promises.length);

      for (let i = 0; i < promises.length; i++) {
        promises[i].then(value => {
          resultCount++;
          results[i] = value;
          if (resultCount === promises.length) {
            return resolve(results)
          }
        }, error => {
          reject(error)
        })
      }
    })
  }

  let p1 = new Promise(resolve => resolve('p1'))
  let p2 = new Promise(resolve => resolve('p2'))
  let p3 = Promise.reject('p3 error')

  promiseAll([p1, p2]).then(results => {
    console.log(results)    // ['p1', 'p2']
  }).catch(error => {
    console.log(error)
  })

  promiseAll([p1, p2, p3]).then(results => {
    console.log(results)
  }).catch(error => {
    console.log(error)      // 'p3 error'
  })

2六、Promise.race的使用

只要有一個完成就執行,返回爲最早完成的

let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
  },1000)
})

let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('failed')
  }, 500)
})

Promise.race([p1, p2]).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)  // 打開的是 'failed'
})

2七、做用域鏈

var a = 100
function F1() {
    var b = 200
    function F2() {
        var c = 300
        console.log(a)    // a是自由變量,向父級做用域尋找a,未果,再向上一級父級做用域尋找a
        console.log(a)    // b是自由變量,向父級做用域尋找b
        console.log(a)
    }
    F2()
}
F1()               // 自由變量不斷地往父級做用域去找,造成一個鏈式結構

2八、 一個完整的JavaScript實現應該由下列三個不一樣的部分組成

1.核心(ECMAScript) ——提供核心語言功能
2.文檔對象模型(Dom) ——提供訪問和操做網頁內容的方法和接口
3.瀏覽器對象模型(Bom) ——提供與瀏覽器交互的方法和接口

2九、延遲腳本

defer=「defer」
defer屬性:代表腳本在執行時不會影響頁面的構造,也就是說,腳本會被延遲到整個頁面都解析完畢再運行

30、NaN

var result = 「a」 < 3; // false,由於「a」被轉化成了NaN
根據規則,任何操做數與NaN進行關係比較,結果都是false

3一、在函數體內能夠經過arguments對象來訪問這個參數數組,從而獲取傳遞給函數的每個參數

function doadd() {
    if (arguments.length == 1) {
        alert(arguments[0] + 10);
    } else if (arguments.length == 2) {
        alert(arguments[0] + arguments[1]);
    }
}
doAdd(10) // 20
doAdd(30, 20) // 50

3二、js數組拍平

一、遞歸

function flatArr(arr) {
    var newArr = [];
    arr.forEach((val) => {
        if (Array.isArray(val)) {
            newArr = newArr.concat(flatArr(val))
        } else {
            newArr.push(val)
        }
    })
    return newArr;
}

二、reduce

function flatArr(arr) {
   return arr.reduce((pre, cur) => pre.concat(Array.isArray(cur) ? flatArr(cur) : cur), [])
}

3.利用數組join()或toString()方法

// join()
function flatArr(arr) {
      arr.join().split(',')
    return JSON.parse(`[${arr.join()}]`)
}
// toString()
function flatArr(arr) {
    arr.toString().split(',')
    return JSON.parse(`[${arr.toString()}]`)
}

4.es6數組的flat()方法 (瀏覽器版本太低不支持)

flat方法默認打平一層嵌套,也能夠接受一個參數表示打平的層數,傳 Infinity 能夠打平任意層。

function flatArr(arr) {
      return arr.flat(Infinity);
}

3三、JavaScript 利用 async await 實現 sleep 效果

const sleep = (timeountMS) => new Promise((resolve) => {
  setTimeout(resolve, timeountMS);
});

(async () => {
  console.log('11111111, ' + new Date());
  await sleep(2000);
  console.log('22222222, ' + new Date());
  await sleep(2000);
  console.log('33333333, ' + new Date());
})();

3四、手寫設置cookie

1.設置cookie一天後過時

function setCookie(name,expireday){
    var dayobject = new Date();  // Date()函數獲取當前的日期和時間
    // getTime()函數獲取的事1970年1月1號至今的毫秒數
    // 注意要多加8小時,咱們位於東八區比標準時間相差8小時
    var daynum = dayobject.getTime() + expireday*(24+8)*60*60*1000;
    // 計算過時時間毫秒數
    dayobject.setTime(daynum);
    // 設置超時的時間
    alert('name=' + name + ';' + 'expires=' + dayobject.toGMTString());
    document.cookie = 'name=' + name + ';' + 'expires=' + dayobject.toGMTString();
}
setCookie('coco',1)

2.設置cookie立刻過時

function delCookie(name){
    var expires = new Date();
    expires.setTime(expires.getTime() - 1);
    document.cookie = 'name='+name+';'+'expires=' + expires.toGMTString();
}
// 設置cookie的過時時間是比當前時間提早一秒,也就是立馬過時了。
相關文章
相關標籤/搜索