補:get請求傳參長度的誤區
實際上HTTP 協議從未規定 GET/POST 的請求長度限制是多少。對get請求參數的限制是來源與瀏覽器或web服務器,瀏覽器或web服務器限制了url的長度。
不同的瀏覽器和WEB服務器,限制的最大長度不一樣要支持IE,則最大長度為2083byte,若只支持Chrome,則最大長度 8182byte
補:get和post請求在緩存方面的區別
- get請求類似于查找的過程,用戶獲取數據,可以不用每次都與數據庫連接,所以可以使用緩存。
- post不同,post做的一般是修改和刪除的工作,所以必須與數據庫交互,所以不能使用緩存。因此get請求適合于請求緩存。
1.三次握手and四次揮手
-
三次握手
詳細描述:
客戶端發送連接請求報文,服務器接受連接后回復ACK報文,并為這次連接分配資源。客戶端接收到ACK報文后也向服務器發生ACK報文,并分配資源,這樣TCP連接就建立了。
簡單的理解:- 客戶端看到服務器,打聲招呼(
發送syn
); - 服務器收到客戶端的招呼,也向客戶端打招呼,表示他看到了(
發送syn+ack
) - 客戶端看到服務器的回應,相當建立溝通(
發送ack
),表示很開心
詳細過程:
- 1)第一次握手:A的TCP客戶進程也是首先創建傳輸控制塊TCB,然后向B發出連接請求報文段(首部的同步位SYN=1,初始序號seq=x)
- 2)第二次握手:B收到連接請求報文段后,如同意建立連接,則向A發送確認,在確認報文段中(SYN=1,ACK=1)
- 3)第三次握手:TCP客戶進程收到B的確認后,要向B給出確認報文段(ACK=1)
- 客戶端看到服務器,打聲招呼(
-
四次揮手
TCP斷開鏈接的過程和建立鏈接的過程比較類似,只不過中間的兩部并不總是會合成一步走,所以它分成了4個動作。
簡單理解:- 客戶端揮手(fin)
- 服務器傷感地微笑(ack)
- 服務器揮手(fin)
- 客戶端傷感地微笑(ack)。
詳細描述: - 1)客戶端發出連接釋放報文,并且停止發送數據。釋放數據報文首部,FIN=1,客戶端進入FIN-WAIT-1(終止等待1)狀態
- 2)服務器收到連接釋放報文,發出確認報文,ACK=1,服務端就進入了CLOSE-WAIT(關閉等待)狀態
- 3)客戶端收到服務器的確認請求后,此時,客戶端就進入FIN-WAIT-2(終止等待2)狀態。
服務器將最后的數據發送完畢后,就向客戶端發送連接釋放報文,FIN=1。服務器就進入了LAST-ACK(最后確認)狀態,等待客戶端的確認。 - 4) 客戶端收到服務器的連接釋放報文后,必須發出確認,ACK=1。此時,客戶端就進入了TIME-WAIT(時間等待)狀態。
注意此時TCP連接還沒有釋放,必須經過2個MSL(最長報文段壽命)的時間后(即兩分鐘),當客戶端撤銷相應的TCB后,才進入CLOSED狀態。
服務器只要收到了客戶端發出的確認,立即進入CLOSED狀態。同樣,撤銷TCB后,就結束了這次的TCP連接。可以看到,服務器結束TCP連接的時間要比客戶端早一些。
- 總的說就是:
客戶端要斷開,告訴服務器,服務器同意斷開連接。
服務器發送完最后數據,服務器要斷開,告訴客戶端,客戶端同意斷開連接。
客戶端同意斷開連接,服務器立馬close。但是客戶端還要等待兩分鐘。
- 為什么連接的時候是三次握手,關閉的時候卻是四次握手?
答:中間的兩個動作沒有合并,是因為tcp存在「半關閉」狀態,也就是單向關閉。
因為當Server端收到Client端的syn連接請求報文后,可以直接發送syn+ack報文。其中ack報文是用來應答的,syn報文是用來同步的。但是關閉連接時,當Server端收到fin報文時,很可能并不會立即關閉,所以只能先回復一個ack報文,告訴Client端,"你發的fin報文我收到了"。只有等到我Server端所有的數據報文都發送完了,我才能發送fin報文,因此不能一起發送。故需要四步握手。
- 為什么TIME_WAIT狀態需要經過2MSL(最大報文段生存時間)才能返回到CLOSE狀態?
答:雖然按道理,四個報文都發送完畢,我們可以直接進入CLOSE狀態了,但是我們必須假象網絡是不可靠的,有可以最后一個ACK丟失。所以TIME_WAIT狀態就是用來重發可能丟失的ACK報文。
2.url到頁面渲染完成的經過
大致分為三步:
-
1. 域名解析
- 瀏覽器會將輸入的域名解析成相應的ip地址
- 查看瀏覽器內部緩存
- 查看本機的host文件,會查看本機的host文件下,是否存了對應的ip地址
- 本地路由器的DNS解析
- 查看網絡服務DNS
- 查詢到ip地址后,開始建立TCP三次握手,與服務器建立連接
- 通過協議(http)向目標主機發送請求
-
2. 服務器接收請求并返回數據
- 服務器接收到了瀏覽器發送的請求后,根據某個協議,通過web-server把瀏覽器發送的數據進行打包(包含請求頭,ip地址,請求路徑和查詢參數等)
- web-server把數據打包后,發送給網站代碼(比如django、flask、node.js等后端服務)
- 后端服務軟件會根據路徑和查詢參數進行相應處理,返回給瀏覽器對應的數據包(包括http協議組成的代碼。里面包含頁面的布局、文字。數據也可能是圖片、腳本程序,反應頭,反應數據,請求頭等)
-
3. 瀏覽器接收數據并渲染頁面
- 瀏覽器接收到返回的數據包,根據瀏覽器的渲染機制對相應的數據進行渲染。
- 渲染后的數據,進行相應的頁面呈現和腳步的交互。
3.js引擎的執行機制
(1) JS是單線程語言
(2) JS的 Event Loop是JS的執行機制。
js為什么是單線程的呢?
因為,如果是多線程,若幾個線程同時操作dom的話,瀏覽器該怎么執行呢。js為什么需要異步呢?
js中不存在異步,是從上而下順序執行的,但是這樣很容易阻塞,若某一句代碼解析執行時間很長,那用戶就需要等待很長時間,所以需要異步執行。js怎么實現異步呢?
就是通過本節的核心事件循環(Event Loop)了,那事件循環具體是什么呢?
比如:
cosole.log(1);
setTimeOut(function(){
cosole.log(2);
},0);
cosole.log(3);
執行的輸出順序是: 1 3 2
js是順序從上到下執行,但是setTimeOut是最后才執行的,就證明了異步的存在。js也就將任務分為:同步任務和異步任務。
-
那事件循環具體怎么循環?
- 1.js判斷是同步事件還是異步事件,同步就進入執行棧,異步事件被掛起
- 異步事件返回結果后,就進入消息隊列
- 同步任務進入執行棧后一直執行,直到執行棧為空時,才會去消息隊列中查看是否有可執行的異步任務,如果有就推入執行棧中
循環執行上述三步,直到執行棧為空,就是事件循環了
所以上面例子的執行順序分析是怎樣的呢?
console.log(1) 是同步任務,放入主線程(執行棧)里
setTimeout() 是 異步任務,被掛起, 0秒之后被推入消息隊列里
console.log(3 是同步任務,放到主線程(執行棧)里
當 1、 3輸出后,主線程去消息隊列(事件隊列)里查看是否有可執行的函數,執行setTimeout里的函數,輸出2
以上就是event loop 的簡單分析了。但是只是很淺的一部分,因為還有下面這樣情況:
setTimeout(function(){
console.log('定時器')
});
new Promise(function(resolve){
console.log('開始for循環');
for(var i = 0; i < 10000; i++){
i == 99 && resolve();
}
}).then(function(){
console.log('執行then')
});
console.log('執行結束');
對于這樣多個異步的事件,按照之前的分析應該輸出:開始for循環 --> 執行結束 --> 定時器 --> 執行then
但是實際的輸出卻是: 開始for循環 --> 執行結束 --> 執行then --> 定時器
會發現 是 先執行promise 再執行的setTimeOut ,那難道是異步任務的執行順序,不是前后順序,而是另有規定? 事實上,按照異步和同步的劃分方式,并不準確。
而準確的劃分方式是:
- macro-task(宏任務):包括整體代碼script,setTimeout,setInterval
- micro-task(微任務):Promise,process.nextTick
按照這種分類方式:JS的執行機制是
執行一個宏任務,過程中如果遇到微任務,就將其放到微任務的【事件隊列】里
當前宏任務執行完成后,會查看微任務的【事件隊列】,并將里面全部的微任務依次執行完
重復以上2步驟,再結合前面的事件循環,就是更為準確的JS執行機制了。
所以上面例子的執行順序分析是怎樣的呢?
先執行script宏任務
遇到 setTimeOut 是宏任務,將其放入宏任務隊列
遇到 new Promise直接執行,打印 "開始for循環"
遇到 then 是微任務,將其放入微任務隊列
打印 "執行結束"
本輪宏任務(script)執行完畢,檢查微任務隊列,遇到then,執行輸出 " 執行then",就只有這一個微任務,所以執行結束
本輪 event loop 執行結束
進入下一輪
執行宏任務 setTimeOut,打印 "定時器"
再查看微任務隊列,沒有微任務
執行完畢
4.hash與history的區別
-
hash模式
- hash就是指url尾巴后的#號以及后面的字符,hash值變化不會導致瀏覽器向服務器發出請求,而且hash改變會觸發hashchange事件,瀏覽器的進后退也能對其進行控制,所以人們在html5的history出現前,基本都是使用hash來實現前端路由的。
- hash出現url中,但不會被包含在HTTP請求中,對后端完全沒有影響,因此改變hash不會重新加載頁面。
- hash 本來是拿來做頁面定位的,如果拿來做路由的話,原來的錨點功能就不能用了。其次,hash的傳參是基于url的,如果要傳遞復雜的數據,會有體積的限制
-
history模式
- history模式不僅可以在url里放參數,還可以將數據存放在一個特定的對象中。
history——— 利用了HTML5 History Interface 中新增的 pushState()和replaceState()方法。
(需要特定瀏覽器的支持)history不能運用與IE8一下
- history模式不僅可以在url里放參數,還可以將數據存放在一個特定的對象中。
-
pushState()和 replaceState()的區別:
- pushState 是創建新的歷史紀錄
- replaceState是修改當前歷史紀錄
window.history.pushState(state,title,url)
state:需要保存的數據,這個數據在觸發popstate事件時,可以在event.state里獲取
title:標題,基本沒用,一般傳null
url:設定新的歷史紀錄的url。新的url與當前url的origin必須是一樣的,否則會拋出錯誤。url可以時絕對路徑,也可以是相對路徑。
如 當前url是 https://www.baidu.com/a/,執行history.pushState(null, null, './qq/'),則變成 https://www.baidu.com/a/qq/,
執行history.pushState(null, null, '/qq/'),則變成 https://www.baidu.com/qq/
window.history.replaceState(state,title,url)
與pushState 基本相同,但她是修改當前歷史紀錄,而 pushState 是創建新的歷史紀錄
window.addEventListener("pospstate",function(){
監聽瀏覽器前進后退事件,pushState與replaceState方法不會觸發
})
window.history.back() 后退
window.history.forward() 前進
window.history.go(1) 前進一部,-2回退兩不,window.history.lengthk可以查看當前歷史堆棧中頁面的數量
這兩個方法應用于瀏覽器的歷史紀錄站,在當前已有的back、forward、go 的基礎之上,他們提供了對歷史紀錄進行修改的功能,只是當他們執行修改使,雖然改變了當前的url,但你的瀏覽器不會立即像后端發送請求。
- 404錯誤
1、hash模式下,僅hash符號之前的內容會被包含在請求中,如 http://www.abc.com 因此對于后端來說,即使沒有做到對路由的全覆蓋,也不會返回404錯誤;
2、history模式下,前端的url必須和實際后端發起請求的url一致,如http://www.abc.com/book/id 。如果后端缺少對/book/id 的路由處理,將返回404錯誤。
5.vue鉤子函數
(1)與生命周期有關的生命周期函數: beforeCreate
、created
、beforeMounted
、mounted
、beforeUpdate
、updated
、beforeDestory
、destoryed
。
(2)computed
、watch
、filter
等
(3)自定義指令directive
的鉤子函數
-
bind
: 只調用一次,指令第一次綁定到元素時調用,用這個鉤子函數可以定義一個在綁定時執行一次的初始化動作 -
inserted
:被綁定元素插入父節點時調用(父節點存在即可調用,不必存在于 document) -
update
: 被綁定元素所在的模板更新時調用,而不論綁定值是否 -
componentUpdated
: 被綁定元素所在模板完成一次更新周期時調 -
unbind
: 只調用一次,指令與元素解綁時
常用參考鏈接:http://www.lxweimin.com/p/8314ccd03fa9
6.vue常用指令
-
v-for
v-for="字段名 in(of) 數組json" 循環數組或json -
v-model
數據的雙向綁定 -
v-if
顯示與隱藏 ,是創建和刪除元素 -
v-else-if
必須和v-if連用 -
v-else
必須和v-if連用 不能單獨使用 否則報錯 模板編譯錯誤-
v-show
顯示內容,只是切換display值 -
v-hidden
隱藏內容
-
-
v-bind
動態綁定 -
v-bind:class
3種綁定方法- 1.對象型
{red:isred}
- 2.三元型
isred?"red":"blue"
- 3.數組型
[{red:"isred"},{blue:"isblue"}]
- 1.對象型
-
v-on
監聽dom事件,可以縮寫為@,例如綁定一個點擊函數 ,函數必須寫在methods里面 -
v-text
解析文本 -
v-html
解析html標簽 -
v-once
進入頁面時 ,只渲染一次,不在進行渲染
常用參考鏈接:https://blog.csdn.net/dz13271116886/article/details/99708315
7.vue常用修飾符
-
事件修飾符(5個)
-
.stop
:阻止事件冒泡 -
.prevent
:阻止默認事件 -
.once
:只執行一次 -
.capture
:捕獲事件,與冒泡相反 -
.self
:只觸發自身事件
-
-
鍵盤修飾符(9個)
-
.enter
:回車鍵 -
.tab
:制表鍵 -
.delete
:含delete和backspace鍵 -
.esc
:返回鍵 -
.space
: 空格鍵 -
.up
:向上鍵 -
.down
:向下鍵 -
.left
:向左鍵 -
.right
:向右鍵
-
-
v-modle修飾符(3個)
-
.number
:將輸出字符串轉為Number類型 -
.lazy
:在改變后才觸發(也就是說只有光標離開input輸入框的時候值才會改變) -
.trim
:自動過濾用戶輸入的首尾空格
-
詳細參考鏈接:https://blog.csdn.net/qq_42238554/article/details/86592295
8.vue常用組件
-
vue-cli
: 項目構建工具 -
vue-router
:路由 -
vuex
:狀態管理 -
axios
:http請求 - 組件庫的組件 eg:Element-ui 、iview
詳細參考鏈接:sohu.com/a/328202078_120047065
9.vue 過濾器
在vue中提供了Vue.filter('filterName',fn)
來定義一個過濾器。
過濾器可以在HTML代碼中使用,對動態拿到的數據進行過濾。
filter不會修改原始數據,它的作用是過濾數據。
通過|
管道符來過濾前面數據
- 過濾器參數:
- 第一個參數 fliterName:是過濾器的名字
- 第二個參數 fn :是過濾器功能函數(兩個參數)
- 過濾功能函數參數:
- 第一個參數是傳入的要過濾數據,即原數據。
- 第二個參數開始就是html調用過濾器的時候傳入的參數。
用法參考鏈接:https://blog.csdn.net/badmoonc/article/details/81485803
10.MVVM的理解
MVVM是Model-View-ViewModel的簡寫。它本質上就是MVC 的改進版。MVVM 就是將其中的View 的狀態和行為抽象化,讓我們將視圖 UI 和業務邏輯分開。
響應式原理
觀察者-訂閱者(數據劫持):
- vueObserver 數據監聽器,把一個普通的 JavaScript 對象傳給 Vue 實例的 data 選項,Vue 將遍歷此對象所有的屬性,并使用Object.defineProperty()方法把這些屬性全部轉成setter、getter方法。當data中的某個屬性被訪問時,則會調用getter方法,當data中的屬性被改變時,則會調用setter方法。
- Compile指令解析器,它的作用對每個元素節點的指令進行解析,替換模板數據,并綁定對應的更新函數,初始化相應的訂閱。
- Watcher 訂閱者,作為連接 Observer 和 Compile 的橋梁,能夠訂閱并收到每個屬性變動的通知,執行指令綁定的相應回調函數。
- Dep 消息訂閱器,內部維護了一個數組,用來收集訂閱者(Watcher),數據變動觸發notify 函數,再調用訂閱者的 update 方法
實現方法:
- 1.實現compile,進行模板的編譯,包括編譯元素(指令)、編譯文本等,達到初始化視圖的目的,并且還需要綁定好更新函數;
- 2.實現Observe,監聽所有的數據,并對變化數據發布通知;
- 3.實現watcher,作為一個中樞,接收到observe發來的通知,并執行compile中相應的更新方法。
-
4.結合上述方法,向外暴露mvvm方法。
執行過程
過程描述:
(1) 當創建一個vue對象時,先進入初始化階段:(兩部分工作)
一方面:vue 會遍歷data的所有屬性,通過object.defineproprety()方法,將所有屬性變成setter和getter。 另一方面:vue的指令編譯器Complie會解析每個元素節點,初始化視圖,然后由watcher(訂閱者)更新視圖,此時watcher會將自身添加到消息訂閱器(Dep)中,初始化完畢。
(2) 當數據變化時:會觸發 observer數據監聽器中的setter方法,setter 會調用Dep中的方法,此時Dep會去遍歷所有的訂閱者,然后去調用訂閱者的update方法,通知訂閱者進行視圖更新。
參考:https://segmentfault.com/a/1190000018399478
11.vue生命周期
Vue實例有一個完整的生命周期,也就是說從開始創建、初始化數據、編譯模板、掛在DOM、渲染-更新-渲染、卸載等一系列過程
-
beforeCreate
:創建之前,在實例初始化之后,數據觀測和事件配置之前被調用。 -
created
: 創建完成,實例已完成以下配置:數據觀測、屬性和方法的運算,watch/event事件回調,完成了data 數據的初始化,el沒有。此時dom還沒渲染,可以在此處進行ajax請求。 -
beforeMount
: 掛載之前,相關的render函數首次被調用(虛擬DOM),實例已完成以下的配置: 編譯模板,把data里面的數據和模板生成html,完成了el和data 初始化,注意此時還沒有掛在html到頁面上。 -
mounted
:掛載完成,此時dom已渲染完成,可以訪問dom元素,只在掛載到vue 對象上執行一次,而后每次更新執行的都是update -
beforeUpdate
:在數據更新之前被調用 -
updated
:數據更新之后,該鉤子在服務器端渲染期間不被調用 -
beforeDestroy
:銷毀之前,此時vue實例依然可以使用 -
destroyed
:銷毀,所有的事件監聽器會被移出,所有的子實例也會被銷毀,該鉤子在服務器端渲染期間不被調用
詳細參考連接:http://www.lxweimin.com/p/672e967e201c
12.vue動態路由
在vue項目中,使用vue-router如果進行不傳遞參數的路由模式,則稱為靜態路由;
如果能夠傳遞參數,對應的路由數量是不確定的,此時的路由稱為動態路由。
比如在寫商品詳情頁面的時候,頁面結構都一樣,只是商品id的不同,所以這個時候就可以用動態路由動態。
冒號后面就是動態的參數
路由配置:
const router = new VueRouter({
routers:[
{
path:'/home:id'
name:'home'
component:home
}
]
});
使用:
<template>
<div>
<router-link to="/home/10">衣服</router-link>
<router-link to="/home/11">麻辣火鍋</router-link>
<router-link to="/home/12">肉夾饃</router-link>
</div>
</template>
實現參數傳遞的方法:
- 使用query傳參,name屬性為要跳轉的組件所對應的name,query為要攜帶的參數
<router-link :to="{name:'main','query':{data:'allData'}}"></router-link>
- 使用params傳參,name屬性為要跳轉的組件所對應的name,params為要攜帶的參數
<router-link :to="{name:'main','params':{data:'allData'}}"></router-link>
- 使用params傳參時,url中不會出現參數,頁面刷新后參數會消失
- 使用query傳參時,url中會出現參數,頁面刷新后參數不會消失
3.設置頁面默認的路由參數(query/params):
this.$router.push( {name: 'main', 'query': {data: 'allData'} } )
this.$router.push( {path: '/main', 'query': {data: 'allData'} } )
- 在組件中接受參數 :
this.$route.query.data || this.$route.params.data
13.post、get、put、delete
post、get、put、delete就像對應著數據庫的CRUD(增、刪、改、查)
post /url 創建
delete /url/xxx 刪除
put /url/xxx 更新或創建
get /url/xxx 查詢
(1) get請求,請求會向數據庫發索取數據的請求,從而來獲取信息,該請求就像數據庫的select操作一樣,只是用來查詢一下數據,不會修改、增加數據,不會影響資源的內容,即該請求不會產生副作用。無論進行多少次操作,結果都是一樣的,具有冪等性。
(2) put 請求,求是向服務器端發送數據的(與get不同)從而改變信息,該請求就像數據庫的update操作一樣,用來修改數據的內容,但是不會增加數據的種類等,也就是說無論進行多少次put操作,其結果并沒有不同,具有冪等性。
(3) post 請求,與put請求類似。都是向服務器端發送數據求會改變數據的種類等資源,就像數據庫的insert操作一樣,會創建新的內容。幾乎目前所有的提交操作都是用POST請求的。不具有冪等性。
(4) delete 請求,用來刪除某一資源,該請求就像數據庫的delete操作。
put 與post 的共同點及區別?
- put和post 都是向服務器發送數據
- post 主要是在一個集合資源之上(url),put 主要作用在一個具體的資源之上(url/xxx)
- put 通常指定了資源的存放位置,而post沒有。post的數據存放位置由服務器自己決定,如果url可以在客戶端確定,那么可使用put,否則用post
- put 有等冪性,而post沒有。
冪等性:冪等意味著對同一個URL的多次請求會返回一樣的結果
14.跨域方法
- jsonp
- CORS
- webSocket
- postMessage
詳情鏈接:http://www.imooc.com/article/40074
15.vue插件(圖表,excel)
16.callback、promise、async-await
17.map、reduce、filter、forEach
- map、filter、reduce會返回新數組,返回值是新數組或結果
- forEach會改變原來數組,forEach沒有返回值
- map:用來迭代對數組進行統一的操作(運算),返回一個新數組
- reduce: 用來迭代一個數組,并且把它累積到一個值中
- filter:用來迭代一個數組,并且按給出的條件過濾出符合的元素
18.for、forEach、 for-in 、 for-of
for循環
遍歷數組forEach循環
遍歷數組,對象(不包括原型上的屬性)
循環不能中途退出,不能使用break,returnfor-in
這個循環是特別針對遍歷對象屬性的。
會遍歷對象的所有屬性,包括原型上的屬性和自定義屬性
對象的屬性是沒有順序的,所以for-in遍歷屬性輸出也是沒有順序的
若對象是null或undefined有可能會報錯for-of
這個循環是最棒的,不僅支持數組,還支持遍歷類數組對象和其他可迭代對象。
可以使用 break、continue、return
for-of循環也支持字符串遍歷,將字符串視為一系列的Unicode字符來進行遍歷
for-of也支持Map和Set遍歷。
for-of不遍歷普通對象。
小總結:
- for-in循環的每次迭代操作會同時搜索實例或者原型屬性,for-in循環的每次迭代會產生很多開銷。除非明確要迭代一個屬性數量未知的對象,否則應該避免使用。 for-in 并不適合用來遍歷數組中的元素,其更適合遍歷對象中的屬性。
- forEach循環不會遍歷原型鏈上的屬性,不能break和return。
- for-of 循環這是最直接、最簡潔的遍歷數組的方法。這個方法避開了for-in循環的所有缺陷
- forEach 的速度不如 for
-
for in循環出的是key,for of循環出的是value
for-in 、for-of遍歷普通對象
let arr = {
name:'aaa',
age:23,
sex:'女'
}
普通對象要加可枚舉的屬性Object.keys(),不然報錯
for(let i of Object.keys(arr)){
console.log(i);
} //name age sex
for(let i in arr){
console.log(i);
} //name age sex
for-of 遍歷 Map ,初始是一個二維數組,對應的鍵值匹配
let test = new Map([['name','aaa'],['age',12],['sex','女']]);
for (var [key, value] of test) {
console.log(key +" is "+ value);
}
//name is aaa
//age is 12
//sex is 女
for-of 遍歷 Set ,遍歷同時會進行數組的去重
var test = new Set([1,1,2,3,4,5]);
for (var i of test) {
console.log(i);
}
//1,2,3,4,5