this指向:
a.如果是一般函數,this指向全局對象window;
b.在嚴格模式下"use strict",爲undefined.
c.對象的方法裏調用,this指向調用該方法的對象.
d.構造函數裏的this,指向創建出來的實例.
改變this指向:(call,apply,bind)
call 、bind 、 apply 這三個函數的第一個參數都是 this 的指向對象,第二個參數差別就來了:
call 的參數是直接放進去的,第二第三第 n 個參數全都用逗號分隔,直接放到後面 obj.myFun.call(db,‘成都’, … ,‘string’ )。
apply 的所有參數都必須放在一個數組裏面傳進去 obj.myFun.apply(db,[‘成都’, …, ‘string’ ])。
bind 除了返回是函數以外,它 的參數和 call 一樣。區別在於bind方法返回值是函數以及bind接收的參數列表的使用。
當然,三者的參數不限定是 string 類型,允許是各種類型,包括函數 、 object 等等!
Cookie
Cookie的特性:會話數據保存在瀏覽器客戶端
單個 cookies 保存的數據不能超過 4 K,很多瀏覽器限制一個站點保存最多 20 個 cookies
Cookie的底層實現原理:
1)服務器創建cookie對象,把會話數據存儲到cookie對象中。
new Cookie("name","value");
2)服務器發送cookie信息到瀏覽器
response.addCookie(cookie);
3)瀏覽器從響應頭中得到服務器發送的Cookie然後保存到瀏覽器本地。
Session
Session的特性:會話數據保存在服務器端
Session底層實現原理:服務器創建一個session之後,會在響應頭裏面將sessionId返回給瀏覽器。瀏覽器從響應頭中獲取sessionId,然後保存在瀏覽器本地。與瀏覽器窗口打開有關,關閉就沒了(換窗口也會消失)
sessionStorage與存儲數據的頂級窗口或瀏覽器選項卡具有相同的生命週期。當選項卡永久關閉時,將刪除通過sessionStorage存儲的所有數據。
Locastrage:通過localStorage存儲的數據是永久性的:它不會過期並保留在用戶的計算機上,直到Web應用程序刪除它或用戶要求瀏覽器刪除它。
localstorage和sessionstorage都是H5提供的新的存儲類型,以前只有cookies來完成存儲的工作。
這兩種新方式存儲限制比使用cookie要大得多(至少5MB),而且速度更快。
數據永遠不會傳輸到服務器,只有在客戶端特別要求時才能使用。
事件冒泡:從目標元素開始,往頂層元素傳播直到document爲止,有的瀏覽器可能到window爲止,途中如果有節點綁定了相應的事件處理函數,這些函數都會被一次觸發。
- Js中使用stopPropagation來阻止事件的冒泡,IE中使用cancleBuble=true
- jQuery使用使用preventDefault()方法
事件捕獲:當某個元素觸發某個事件(如onclick),頂層對象document就會發出一個事件流,隨着DOM樹的節點向目標元素節點流去,直到到達事件真正發生的目標元素。在這個過程中,事件相應的監聽函數是不會被觸發的。
js事件委託:事件委託又可以叫事件代理,事件委託就是利用事件冒泡,只指定一個事件處理程序,就可以管理某一類型的所有事件。
減少dom操作可以提高網頁性能,當一個頁面的父級元素和很多子級元素都需要操作同一件事件的時候,我們不可能每個元素都去給它綁定一個事件
e.target:(隱式函數)
例:var e = e || window.event;
var target = e.target || e.srcElement;
什麼是節流和防抖?
節流(throttle):節流可以控制事件觸發的頻率,節流就跟小水管一樣,如果不加節流的話,水就會嘩啦啦啦啦啦啦的流出來,但是一旦加了節流閥,你就可以自己控制水的流速了,加了節流後水可以從嘩啦啦啦變成滴答滴答滴答,放到我們的函數事件裏面說就是可以讓事件觸發變慢,比如說事件觸發可以讓它在每一秒內只觸發一次;
防抖(debounce):防抖就是可以限制事件在一定時間內不能多次觸發,比如說你瘋狂按點擊按鈕,一頓操作猛如虎,不加防抖的話它也會跟着你瘋起來,瘋狂執行觸發的方法。但是一旦加了防抖,無論你點擊多少次,他都只會在你最後一次點擊的時候才執行;
防抖:
理解:在車站上車,人員上滿了車才發走重點是人員上滿觸發一次。
場景:實時搜索,拖拽。
實現:
//每一次都要清空定時器,重新設置上計時器值,使得計時器每一次都重新開始,直到最後滿足條件並且等待delay時間後,纔開始執行handler函數。
function debunce(handler,delay){
//handler是要傳入的進行防抖的函數,delay是等待時間。
var timer = null;
return function(){
var _self = this,args = arguments;
clearTimeout(timer); //每次都要清除這個定時器
timer = setTimeout(function(){ //重新開啓定時器
handler.apply(_self,args);
},delay);
}
}
節流:
理解:大於等於10分鐘發一次車,重點是一定間隔時間就會觸發一次。
(即預定一個函數只有在大於等於執行週期時纔會執行,週期內不執行)。
場景:窗口調整(調整大小),頁面滾動(滾動),搶購時瘋狂點擊(鼠標按下)
實現:
//處理程序是要傳入的進行節流的函數,wait是上述的間隔時間。
//使用時間戳進行時間的計算。
function throttle(handler,wait){ //handler是要進行節流的函數,wait是等待時間
var lastTime = 0;
return function(){
var nowTime = new Date().getTime(); //獲取當前時間
if(nowTime - lastTime> wait){
handler.apply(this,arguments);
lastTime = nowTime; //更新最後時間
}
}
}
AJAX:
1、是一種在無需重新加載整個網頁的情況下,能夠更新部分網頁的技術。
2、通過在後臺與服務器進行少量數據交換,AJAX 可以使網頁實現異步更新。這意味着可以在不重新加載整個網頁的情況下,對網頁的某部分進行更新。
3、傳統的網頁(不使用 AJAX)如果需要更新內容,必需重載整個網頁面。
Fetch
fetch號稱是AJAX的替代品,是在ES6出現的,使用了ES6中的promise對象。Fetch是基於promise設計的。Fetch的代碼結構比起ajax簡單多了,參數有點像jQuery ajax。但是,一定記住fetch不是ajax的進一步封裝,而是原生js,沒有使用XMLHttpRequest對象。
Axios
-axios主要是jQuery的ajax與fetch的融合,十分強大
特點:
- 支持瀏覽器和node.js
- 支持promise(重點)
- 能攔截請求和響應
- 能轉換請求和響應數據
- 能取消請求
- 自動轉換JSON數據
- 瀏覽器端支持防止CSRF(跨站請求僞造)
Js閉包
閉包指的是:能讀取其他函數內部變量的函數。(函數嵌套函數,內訪外)
清晰的講:閉包就是一個函數,這個函數能夠訪問其他函數的作用域中的變量。
優點:
- 避免全局變量的污染
- 希望一個變量長期存儲在內存中(緩存變量)
閉包帶來的問題:
1 、閉包使得函數被保存在內存中,內存消耗很大,所以不能濫用
2 、函數本身是對象,內部變量相當於它的私有屬性,所以這是不安全的
從輸入一個url到瀏覽器頁面展示都經歷了哪些過程
1、首先,在瀏覽器地址欄中輸入url
2、瀏覽器先查看瀏覽器緩存-系統緩存-路由器緩存,如果緩存中有,會直接在屏幕中顯示頁面內容。若沒有,則跳到第三步操作。
3、在發送http請求前,需要域名解析(DNS解析),解析獲取相應的IP地址。
4、瀏覽器向服務器發起tcp連接,與瀏覽器建立tcp三次握手。
5、握手成功後,瀏覽器向服務器發送http請求,請求數據包。
6、服務器處理收到的請求,將數據返回至瀏覽器
7、瀏覽器收到HTTP響應
8、讀取頁面內容,瀏覽器渲染,解析html源碼
9、生成Dom樹、解析css樣式、js交互
10、客戶端和服務器交互
11、ajax查詢
其中,步驟2的具體過程是:
瀏覽器緩存:瀏覽器會記錄DNS一段時間,因此,只是第一個地方解析DNS請求;
操作系統緩存:如果在瀏覽器緩存中不包含這個記錄,則會使系統調用操作系統,獲取操作系統的記錄(保存最近的DNS查詢緩存);
路由器緩存:如果上述兩個步驟均不能成功獲取DNS記錄,繼續搜索路由器緩存;
ISP緩存:若上述均失敗,繼續向ISP搜索。
.基本的數據類型:String, Number, boolean, Null, Undefined,Symbol(ES6新增)
特點: 存儲的是該對象的實際數據,(存放在棧中)
.對象數據類型(也稱爲引用數據類型):Array,Object,Function
特點: 存儲的是該對象在棧中引用,真實的數據存放在堆內存裏,(存放在堆內存中的對象,每個空間大小不一樣,要根據情況進行特定的配置)
注:在JS中除了基本數據類型以外的都是對象,數據是對象,函數是對象,正則表達式是對象
深拷貝和淺拷貝的區別
1.淺拷貝: 將原對象或原數組的引用直接賦給新對象,新數組,新對象/數組只是原對象的一個引用
2.深拷貝: 創建一個新的對象和數組,將原對象的各項屬性的“值”(數組的所有元素)拷貝過來,是“值”而不是“引用”
例:
假設B複製了A,修改A的時候,看B是否發生變化:
如果B跟着也變了,說明是淺拷貝,拿人手短!(修改堆內存中的同一個值)
如果B沒有改變,說明是深拷貝,自食其力!(修改堆內存中的不同的值)
new操作符在創建實例的時候經歷了哪幾個階段
new創建了一個對象,共經歷了4個階段:
1、 創建一個空對象
2、 設置原型鏈
3、讓實例化對象中的this指向對象,並執行函數體
4、 判斷實例化對象的返回值類型
跨域
https://blog.csdn.net/Sestid/article/details/103913229
HTTP
工作原理:HTTP協議工作於客戶端-服務端架構上。瀏覽器作爲HTTP客戶端通過URL向HTTP服務端即WEB服務器發送所有請求。
客戶端請求消息:
客戶端發送一個HTTP請求到服務器的請求消息包括以下格式:請求行(request line)、請求頭部(header)、空行和請求數據四個部分組成,下圖給出了請求報文的一般格式。
服務器響應消息:
HTTP響應也由四個部分組成,分別是:狀態行、消息報頭、空行和響應正文。
HTTP 請求方法:
HTTP1.0 定義了三種請求方法: GET, POST 和 HEAD方法。
HTTP1.1 新增了六種請求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法。
HTTPS
是一種通過計算機網絡進行安全通信的傳輸協議。HTTPS經由HTTP進行通信,但利用SSL/TLS來加密數據包。
HTTPS 提供了加密 (Encryption)、認證 (Verification)、鑑定 (Identification) 三種功能。如下的解釋中,假設是張三和李四在通訊。
- 私密性(Confidentiality/Privacy):
也就是提供信息加密,保證數據傳輸的安全;保證信息只有張三和李四知道,而不會被竊聽。 - 可信性(Authentication):
身份驗證,主要是服務器端的,確認網站的真實性,有些銀行也會對客戶端進行認證;用來證明李四就是李四。 - 完整性(Message Integrity):
保證信息傳輸過程中的完整性,防止被修改;李四接收到的消息就是張三發送的。
HTTPS就是在應用層和傳輸層中間加了一道驗證的門檻以保證數據安全
get和post
http協議最常見的兩種方法GET和POST:
請求緩存:GET 會被緩存,而post不會
收藏書籤:GET可以,而POST不能
保留瀏覽器歷史記錄:GET可以,而POST不能
用處:get常用於取回數據,post用於提交數據
安全性:post比get安全
請求參數:querystring 是url的一部分get、post都可以帶上。 get的querystring(僅支持urlencode編碼),post的參數是放在body(支持多種編碼)
請求參數長度限制:get請求長度最多1024kb,post對請求數據沒有限制
三次握手
三次握手(Three-way Handshake)其實就是指建立一個TCP連接時,需要客戶端和服務器總共發送3個包。進行三次握手的主要作用就是爲了確認雙方的接收能力和發送能力是否正常、指定自己的初始化序列號爲後面的可靠性傳送做準備。實質上其實就是連接服務器指定端口,建立TCP連接,並同步連接雙方的序列號和確認號,交換TCP窗口大小信息。
四次揮手
建立一個連接需要三次握手,而終止一個連接要經過四次揮手(也有將四次揮手叫做四次握手的)。這由TCP的半關閉(half-close)造成的。所謂的半關閉,其實就是TCP提供了連接的一端在結束它的發送後還能接收來自另一端數據的能力。
TCP 的連接的拆除需要發送四個包,因此稱爲四次揮手(Four-way handshake),客戶端或服務器均可主動發起揮手動作。
詳細的看這裏https://blog.csdn.net/Sestid/article/details/103909449
es6新特性:
Class 的基本語法
JavaScript 語言中,生成實例對象的傳統方法是通過構造函數。例子:
function Point(x, y) {
this.x = x;
this.y = y;
}
ES6 提供了更接近傳統語言的寫法,引入了 Class(類)這個概念,作爲對象的模板。通過class
關鍵字,可以定義類。
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
上面代碼定義了一個“類”,可以看到裏面有一個constructor
方法,這就是構造方法,而this
關鍵字則代表實例對象。
constructor 方法
constructor
方法是類的默認方法,通過new
命令生成對象實例時,自動調用該方法。一個類必須有constructor
方法,如果沒有顯式定義,一個空的constructor
方法會被默認添加。
class Point {
}
// 等同於
class Point {
constructor() {}
}
上面代碼中,定義了一個空的類Point
,JavaScript 引擎會自動爲它添加一個空的constructor
方法。
///////////////this指向
1、在構造方法中綁定this
,這樣就不會找不到print
方法了。
class Logger {
constructor() {
this.printName = this.printName.bind(this);
}
// ...
}
2、另一種解決方法是使用箭頭函數。
class Obj {
constructor() {
this.getThis = () => this;
}
}
const myObj = new Obj();
myObj.getThis() === myObj // true
箭頭函數內部的this
總是指向定義時所在的對象。
3、還有一種解決方法是使用Proxy
,獲取方法的時候,自動綁定this
。
class繼承
需要注意的地方是,在子類的構造函數中,只有調用super
之後,纔可以使用this
關鍵字,否則會報錯。這是因爲子類實例的構建,基於父類實例,只有super
方法才能調用父類實例。
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class ColorPoint extends Point {
constructor(x, y, color) {
this.color = color; // ReferenceError
super(x, y);
this.color = color; // 正確
}
}
上面代碼中,子類的constructor
方法沒有調用super
之前,就使用this
關鍵字,結果報錯,而放在super
方法之後就是正確的。
super 關鍵字
super
這個關鍵字,既可以當作函數使用,也可以當作對象使用。在這兩種情況下,它的用法完全不同。
第一種情況,super
作爲函數調用時,代表父類的構造函數。ES6 要求,子類的構造函數必須執行一次super
函數。
第二種情況,super
作爲對象時,在普通方法中,指向父類的原型對象;在靜態方法中,指向父類。
import()
// 報錯
if (x === 2) {
import MyModual from './myModual';
}
import
和export
命令只能在模塊的頂層,不能在代碼塊之中(比如,在if
代碼塊之中,或在函數之中)。
const path = './' + fileName;
const myModual = require(path);
上面的語句就是動態加載,require
到底加載哪一個模塊,只有運行時才知道。import
命令做不到這一點。
ES2020提案 引入import()
函數,支持動態加載模塊。
import()
函數可以用在任何地方,不僅僅是模塊,非模塊的腳本也可以使用。它是運行時執行,也就是說,什麼時候運行到這一句,就會加載指定的模塊。另外,import()
函數與所加載的模塊沒有靜態連接關係,這點也是與import
語句不相同。import()
類似於 Node 的require
方法,區別主要是前者是異步加載,後者是同步加載。
“同步模式" 就是上一段的模式,後一個任務等待前一個任務結束,然後再執行,程序的執行順序與任務的排列順序是一致的、同步的;"異步模式"則完全不同,每一個任務有一個或多個回調函數(callback),前一個任務結束後,不是執行後一個任務,而是執行回調函數,後一個任務則是不等前一個任務結束就執行,所以程序的執行順序與任務的排列順序是不一致的、異步的。
“異步模式" 非常重要。在瀏覽器端,耗時很長的操作都應該異步執行,避免瀏覽器失去響應,最好的例子就是Ajax操作。在服務器端,"異步模式"甚至是唯一的模式,因爲執行環境是單線程的,如果允許同步執行所有http請求,服務器性能會急劇下降,很快就會失去響應。
一、回調函數(callback)
回調是一個函數被作爲一個參數傳遞到另一個函數裏,在那個函數執行完後再執行。( 也即:B函數被作爲參數傳遞到A函數裏,在A函數執行完後再執行B )
二、事件監聽
監聽函數有:on,bind,listen,addEventListener,observe
三、發佈/訂閱
四、promise對象(promise 模式)
五、優雅的async/await
Promise詳解
Promise有三種狀態:pengding(),resolved(已完成),rejected(已失敗);Promise從Pending狀態開始,如果成功就轉到成功態,並執行resolve回調函數;如果失敗就轉到失敗狀態並執行reject回調函數。
Promise 新建後就會立即執行。
優點:可以將異步操作以同步操作的流程表達出來,避免了層層嵌套的回調函數。
缺點:無法取消 Promise;當處於pending狀態時,無法得知目前進展到哪一個階段。
兩個特點:1、對象的狀態不受外界影響 2、一旦狀態改變,就不會再變,任何時候都可以得到這個結果
採用鏈式的then
,可以指定一組按照次序調用的回調函數。
getJSON("/post/1.json").then(function(post) {
return getJSON(post.commentURL);
}).then(function (comments) {
console.log("resolved: ", comments);
}, function (err){
console.log("rejected: ", err);
});
let,const,var有什麼區別
let 和 const 定義的變量不會出現變量提升,而 var 定義的變量會提升;let 和 const 是JS中的塊級作用域;let 和 const 不允許重複聲明(會拋出錯誤);let 和 const 定義的變量在定義語句之前,如果使用會拋出錯誤(形成了暫時性死區),而 var 不會;const 聲明一個只讀的常量,一旦聲明,常量的值就不能改變(如果聲明是一個對象,那麼不能改變的是對象的引用地址)。
Webpack如何實現打包
WebPack是一個模塊打包工具,你可以使用WebPack管理你的模塊依賴,並編繹輸出模塊們所需的靜態文件。它能夠很好地管理、打包Web開發中所用到的HTML、Javascript、CSS以及各種靜態文件(圖片、字體等),讓開發過程更加高效。對於不同類型的資源,webpack有對應的模塊加載器。webpack模塊打包器會分析模塊間的依賴關係,最後生成了優化且合併後的靜態資源。
MVC模式【Model(模型)+View(視圖)+controller(控制器)】
View通過Controller來和Model聯繫,Controller是View和Model的協調者,View和Model不直接聯繫,基本聯繫都是單向的。用戶User通過控制器Controller來操作模板Model從而達到視圖View的變化
React框架【MVC】
優點:jsx語法創建虛擬DOM,極速的渲染性能;組件化開發,組件獨立,方便重複使用;單向數據流;組件生命週期;跨瀏覽器兼容性好
缺點:不適合單獨做一個完整的框架
應用場景:個性化需求、中型應用
Vue與React的區別
相同點:react和vue都是用虛擬DOM Virtual DOM;中心思想相同:一切都是組件,組件實例之間可以嵌套;都有着合理的鉤子函數;都不內置ajax、route等核心包,以插件的形式加載;都有配套的路由和負責處理全局狀態管理的庫;
不同點:React使用JSX渲染頁面,Vue使用簡單的模板;Vue雙向數據流,React單向數據流;Vue.js在模板中提供了指令,過濾器等,可以非常方便,快捷地操作DOM;Vue比react運行更快
React生命週期函數
1、組件的掛載(Mounting)階段(初始): constructor 、componentWillMount、 render 、componentDidMount
2、組件的更新(update)階段:componentWillReceiveProps,shouldComponentUpdate,componentWillUpdate,render,componentDidUpdate
3、組件銷燬階段:componentWillUnmount
數組去重
方法一:
這是個沒有什麼技術含量的方法(使用了ES6 Set數據結構)!
let arr = [1,1,1,1,2,3,4,5,5,6];
arr = [...new Set(arr)]; // [1,2,3,4,5,6]
方法二:
for循環嵌套for循環
function unique(arr){
for(let i = 0; i<arr.length; i++) {
for(let j = i+1; j < arr.length; j++) {
if(arr[i] === arr[j]){
arr.splice(j,1); // 去除重複的這個
j--;
}
}
}
return arr;
}
方法三
使用indexOf方法,或數組的includes方法
function unique(arr) {
let arr1 = [];
for(let i=0; i<arr.length; i++) {
if(arr1.indexOf(arr[i]) === -1) { // !arr1.includes(arr[i])
arr1.push(arr[i]); // 如果arr1中不存在該元素則push進arr1
}
}
return arr1;
}
方法四
利用sort排序後,相鄰元素進行對比
function unique(arr) {
arr.sort( (a,b) => a-b);
for(let i=0; i< arr.length-1; i++){
if(arr[i] === arr[i+1]) {
arr.splice(i+1,1);
}
}
return arr;
}
方法五
reduce()
let arr = [1,1,2,3,4,5,5,6]
let arr2 = arr.reduce(function(ar,cur) {
if(!ar.includes(cur)) {
ar.push(cur)
}
return ar
},[])
方法六
filter()
// 這種方法會有一個問題:[1,'1']會被當做相同元素,最終輸入[1]
let arr = [1,1,2,3,4,5,5,6]
let arr2 = arr.filter(function(item,index) {
// indexOf() 方法可返回某個指定的 字符串值 在字符串中首次出現的位置
return arr.indexOf(item) === index
})
數組排序
第一種:arrayObject.sort(sortby)
function sortNum(a, b) {
return a - b;
}
let arr = [524, 684, 5, 69, 15];
let res = arr.sort(sortNum);
console.log(res);
第二種:冒泡排序
function bubbleSort(arr) {
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < arr.length; j++) {
if (arr[i] < arr[j]) {
let temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
}
}
return arr;
}
let arr = [524, 684, 5, 69, 15];
console.log(bubbleSort(arr));
第三種:快速排序
function quickSort(arr) {
if (arr.length <= 1) {
return arr;
}
let pivotIndex = Math.floor(arr.length / 2),
pivot = arr.splice(pivotIndex, 1)[0],
lef = [],
rig = [];
for (var i = 0; i < arr.length; i++) {
if (arr[i] < pivot) {
lef.push(arr[i]);
}else {
rig.push(arr[i]);
}
}
return quickSort(lef).concat(pivot, quickSort(rig));
}
let arr = [524, 684, 5, 69, 15];
console.log(quickSort(arr));
第四種:插入排序
function insertSort(arr, a) {
for (let i = 1; i < arr.length; i++) {
if (arr[i] >= a) {
for (let j = arr.length; j > i; j--) {
arr[j] = arr[j - 1];
}
arr[i] = a;
break;
}
}
return arr;
}
let arr = [5, 15, 69, 524, 684];
console.log(insertSort(arr, 92));
1、使用apply結合concat,缺點是隻能將二維轉一維,多維數組就不行了
let arr = [1,[2,3],[4,5]];
console.log([].concat.apply([],arr));
2、將數組轉爲字符串再轉爲數組,缺點是數組中每項成字符串了
let arr = [1,[2,[[3,4],5],6]];
let arr2 = arr.join(',').split(',');
console.log(arr2);//["1", "2", "3", "4", "5", "6"]
//或
let c=[1,3,4,5,[6,[0,1,5],9],[2,5,[1,5]],[5]];
console.log(c.toString().split(','))
3、遞歸調用
let arr = [1, 2, 3, 4, 5, [6, 7, 8, [9, 10, 11, 12, [13, 14, 15, 16]]]]
let newArr = [] // 存放轉化後的一維數組
function arrConversion (arr) {
for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i])) {
arrConversion(arr[i])
} else {
newArr.push(arr[i])
}
}
}
arrConversion(arr)
console.log(newArr) // 輸出:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
4、es6中的flat()方法
console.log([1 ,[2, 3]].flat()); // [1, 2, 3]
// 指定轉換的嵌套層數
console.log([1, [2, [3, [4, 5]]]].flat(2)); // [1, 2, 3, [4, 5]]
// 不管嵌套多少層
console.log([1, [2, [3, [4, 5]]]].flat(Infinity)); // [1, 2, 3, 4, 5]
// 自動跳過空位
console.log([1, [2, , 3]].flat());<p> // [1, 2, 3]
5、正則
let ary = [1, [2, [3, [4, 5]]], 6];
let str = JSON.stringify(ary);
let result = str.replace(/(\[|\])/g, '').split(',');
console.log( result )