Html5新增標籤和屬性javascript
增長標籤: 一、結構標籤 (1)section:獨立內容區塊,能夠用h1~h6組成大綱,表示文檔結構,也能夠有章節、頁眉、頁腳或頁眉的其餘部分; (2)article:特殊獨立區塊,表示這篇頁眉中的核心內容; (3)aside:標籤內容以外與標籤內容相關的輔助信息; (4)header:某個區塊的頭部信息/標題; (5)hgroup:頭部信息/標題的補充內容; (6)footer:底部信息; (7)nav:導航條部分信息 (8)figure:獨立的單元,例如某個有圖片與內容的新聞塊。 二、表單標籤 (1)email:必須輸入郵件; (2)url:必須輸入url地址; (3)number:必須輸入數值; (4)range:必須輸入必定範圍內的數值; (5)Date Pickers:日期選擇器; a.date:選取日、月、年 b.month:選取月、年 c.week:選取周和年 d.time:選取時間(小時和分鐘) e.datetime:選取時間、日、月、年(UTC時間) f.datetime-local:選取時間、日、月、年(本地時間) (6)search:搜索常規的文本域; (7)color:顏色 三、媒體標籤 (1)video:視頻 (2)audio:音頻 (3)embed:嵌入內容(包括各類媒體),Midi、Wav、AU、MP三、Flash、AIFF等。 四、其餘功能標籤 (1)mark:標註(像熒光筆作筆記) (2)progress:進度條;<progress max="最大進度條的值" value="當前進度條的值"> (3)time:數據標籤,給搜索引擎使用;發佈日期<time datetime="2014-12-25T09:00">9:00</time>更新日期<time datetime="2015- 01-23T04:00" pubdate>4:00</time> (4)ruby和rt:對某一個字進行註釋;<ruby><rt>註釋內容</rt><rp>瀏覽器不支持時如何顯示</rp></ruby> (5)wbr:軟換行,頁面寬度到須要換行時換行; (6)canvas:使用JS代碼作內容進行圖像繪製; (7)command:按鈕; (8)deteils :展開菜單; (9)dateilst:文本域下拉提示; (10)keygen:加密; 新增的屬性: 對於js進行添加的屬性。 <script defer src=".....js" onload="alert('a')"></script> <script async src=".....js" onload="alert('b')"></script> 若是沒有以上兩個屬性的話,執行順序爲先加載(下載)第一個src,而後在執行其onload,而後在向下依次同步執行defer屬性在h5以前就已經有了,輸入延遲加載(推遲執行),它會先加載(下載)src中文件內容,而後等頁面所有加載完成後,再加載onload中js.async屬性屬於異步加載,它會在加載src後,當即執行onload,同時還會繼續加載頁面以上執行順序,alert顯示會先顯示b而後再顯示a 網頁中標籤中加入小圖標的樣式代碼 <link rel="icon" href="url..." type="圖片名稱" sizes="16*16"> 有序列表ol:新增start(列表起始值),reversed(是否倒置)menu菜單type屬性(3個菜單類型)內嵌css樣式:在標籤內部來定義一個樣式區塊(scoped),只對樣式標籤內部纔有效內嵌框架:iframe元素,新增了seamless無邊距無邊框,srcdoc定義了內嵌框架的內容 <iframe>新增屬性: <!--seamless定義框架無邊框 無邊距--> <!--srcdoc的顯示級別比sandbox高--> <!--sandbox用來規定一個內嵌框架的安全級別--> <!--sandbox="allow-forms:容許提交表單"--> <!--sandbox="allow-origin:容許是相同的源"--> <!--sandbox="allow-scripts:容許執行腳本"--> <!--sandbox="allow-top-navigation:容許使外面的頁面進行跳轉"--> manifest屬性: 定義頁面須要用到的離線應用文件,通常放在<html>標籤裏 charset屬性: meta屬性之一,定義頁面的字符集 sizes屬性: <link>新增屬性,當link的rel="icon"時,用以設置圖標大小 base屬性: <base href="http://localhost/" target="_blank">表示當在新窗口打開一個頁面時,會將href中的內容做爲前綴添加到地址前 defer屬性: script標籤屬性,表示腳本加載完畢後,只有當頁面也加載完畢才執行(推遲執行) async屬性: script標籤屬性,腳本加載完畢後立刻執行(運行過程當中瀏覽器會解析下面的內容),即便頁面尚未加載完畢(異步執行) media屬性: <a>元素屬性:表示對何種設備進行優化 hreflang屬性: <a>的屬性,表示超連接指向的網址使用的語言 ref屬性: <a>的屬性,定義超連接是不是外部連接 reversed屬性: <ol>的屬性,定義序號是否倒敘 start屬性: <ol>的屬性,定義序號的起始值 scoped屬性: 內嵌CSS樣式的屬性,定義該樣式只侷限於擁有該內嵌樣式的元素,適用於單頁開發 HTML5全局屬性:對任意標籤均可以使用的,如下6個 data-yourvalue 、hidden、Spenllecheck、tabindex、contenteditable、desginMode; 全局屬性: 1.可直接在標籤裏插入的:data-自定義屬性名字; hidden(直接放上去就是隱藏); spellcheck="true"(語法糾錯); tabindex="1"(Tab跳轉順序); contenteditable="true"(可編輯狀態,單擊內容,可修改); 2.在JavaScript裏插入的window.document.designMode = 'on'(JavaScript的全局屬性,整個頁面的文本均可以編輯了);
垂直水平居中php
僅居中元素定寬高適用 absolute + 負margin absolute + margin auto absolute + calc 居中元素不定寬高 absolute + transform lineheight writing-mode table css-table flex grid
數組去重css
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", {}, {}]
不考慮兼容性,這種去重的方法代碼最少。這種方法還沒法去掉「{}」空對象,後面的高階方法會添加去掉重複「{}」的方法。html
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]){ //第一個等同於第二個,splice方法刪除第二個 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直接消失了
雙層循環,外層循環元素,內層循環時比較值。值相同時,則刪去這個值。
想快速學習更多經常使用的ES6語法,能夠看我以前的文章《學習ES6筆記──工做中經常使用到的ES6語法》。前端
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、{}沒有去重
新建一個空的結果數組,for 循環原數組,判斷結果數組是否存在當前元素,若是有相同的值則跳過,不相同則push進數組。vue
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、{}沒有去重
利用sort()排序方法,而後根據排序後的結果進行遍歷及相鄰元素比對。java
function unique(arr) { if (!Array.isArray(arr)) { console.log('type error!') return } var arrry= []; var obj = {}; for (var i = 0; i < arr.length; i++) { if (!obj[arr[i]]) { arrry.push(arr[i]) obj[arr[i]] = 1 } else { obj[arr[i]]++ } } return arrry; } 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", {…}] //兩個true直接去掉了,NaN和{}去重
function unique(arr) { if (!Array.isArray(arr)) { console.log('type error!') return } var array =[]; for(var i = 0; i < arr.length; i++) { if( !array.includes( arr[i]) ) {//includes 檢測數組是否有某個值 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", 0, "a", {…}, {…}] //{}沒有去重
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", {…}] //全部的都去重了
利用hasOwnProperty 判斷是否存在對象屬性node
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", {…}, {…}]
function unique(arr) { var array= arr; var len = array.length; array.sort(function(a,b){ //排序後更加方便去重 return a - b; }) function loop(index){ if(index >= 1){ if(array[index] === array[index-1]){ array.splice(index,1); } loop(index - 1); //遞歸loop,而後數組去重 } } loop(len-1); 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, "a", "true", true, 15, false, 1, {…}, null, NaN, NaN, "NaN", 0, "a", {…}, undefined]
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(unique(arr)) //[1, "a", "true", true, 15, false, 1, {…}, null, NaN, NaN, "NaN", 0, "a", {…}, undefined]
建立一個空Map數據結構,遍歷須要去重的數組,把數組的每個元素做爲key存到Map中。因爲Map中不會出現相同的key值,因此最終獲得的就是去重後的結果。jquery
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", {…}, {…}]
[...new Set(arr)] //代碼就是這麼少----(其實,嚴格來講並不算是一種,相對於第一種方法來講只是簡化了代碼)
js經常使用的模式
一.單例模式webpack
單例模式也稱做爲單子模式,更多的也叫作單體模式。爲軟件設計中較爲簡單可是最爲經常使用的一種設計模式。 在JavaScript裏,實現單例的方式有不少種,其中最簡單的一個方式是使用對象字面量的方法,其字面量裏能夠包含大量的屬性和方法。
要擴展該對象,能夠添加本身的私有成員和方法,而後使用閉包在其內部封裝這些變量和函數聲明。樣例代碼以下:
2、工廠模式
工廠模式是由一個方法來決定到底要建立哪一個類的實例,而這些實例常常都擁有相同的接口。這種模式主要用在所實例化的類型在編譯期並不能肯定, 而是在執行期決定的狀況。
實例:
這段代碼來自es5的new和構造器的相關說明, new自己只是一個對象的複製和改寫過程, 而具體會生成什麼是由調用ObjectFactory時傳進去的參數所決定的。
3、 適配模式
適配模式主要是爲了解決一些接口不兼容產生的解決方法。適配器能夠在不修改這些不兼容接口的狀況下給使用者提供統一的包裝過的適配接口。表面上又感受和以前的門面模式比較像,均是對其餘對象或者接口進行包裝再呈現,而適配器模式偏向的是解決兼容性問題,門面模式則偏向方便性爲原則。
好比一個簡單的學生查詢學科成績的方法:
這是一個關於適配器來處理參數方面兼容的形式。 適配器模式意義上很簡單 - 適配,解決兼容問題。
例子二:jquery裏邊的$選擇器須要改爲$id才能和項目搭配,將$轉換成$id就很輕鬆了。以下:
4、外觀模式
外觀模式,是一種相對簡單而又無處不在的模式。外觀模式提供一個高層接口,這個接口使得客戶端或子系統更加方便調用。 用一段再簡單不過的代碼來表示:
實現一個簡單的訂閱發佈者
觀察者模式,定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,全部依賴於它的對象都將獲得通知。
事實上,只要你曾經在DOM節點上綁定過事件函數,那麼你就曾經使用過觀察者模式了!
document.body.addEventListener('click', function () {
alert(2);
});
可是這只是對觀察者模式最簡單的使用,在不少場景下咱們常常會實現一些自定義事件來知足咱們的需求。
舉個例子:
你去一家公司應聘,談了一頓下來,hr跟你說:"好了,你回去等通知吧!"。
這個時候,1.你會問公司的電話,而後天天打過去問一遍結果
2.把本身的手機號留給hr,而後等他給你打電話
相信不少時候呢,你們都是選擇了後者。
萬一你天天給hr打電話弄煩他了,或許他原本打算招你的,如今也再也不打算再鳥你啦!
那麼這個時候,hr就至關於一個發佈者,而你就是一個訂閱者啦!
好吧,大部分叫你回去等消息的就等於沒救啦......
我還遇到過一個若是你沒被錄取,就連通知都不通知你的公司!
那麼一個簡單的觀察者模式應該怎麼實現呢?
要指定一個發佈者; 給發佈者添加一個緩存列表,用於存放回調函數以便通知訂閱者;(這家公司不少人來應聘) 最後發佈消息的時候,發佈者會遍歷這個緩存列表,依次觸發裏面存放的訂閱者回調函數;(你up or 你over)
var event = {}; //發佈者(hr)
event.clietList = []; //發佈者的緩存列表(應聘者列表)
event.listen = function(fn) { //增長訂閱者函數
this.clietList.push(fn);
};
event.trigger = function() { //發佈消息函數
for (var i = 0; i < this.clietList.length; i++) { var fn = this.clietList[i]; fn.apply(this, arguments); }
};
event.listen(function(time) { //某人訂閱了這個消息
console.log('正式上班時間:' + time);
});
event.trigger('2016/10',yes); //發佈消息
//輸出 正式上班時間:2016/10
到這裏,咱們已經實現了一個最簡單的觀察者模式了!
可是上面的函數其實存在一個問題,那就是發佈者沒辦法選擇本身要發佈的消息類型!
好比這家公司同時在招php,web前端,若是使用上面的函數就沒辦法區分職位了!只能一次性把所有訂閱者都發送一遍消息。
對上面的代碼進行改寫:
var event = {}; //發佈者(hr)
event.clietList = []; //發佈者的緩存列表(應聘者列表)
event.listen = function(key, fn) { //增長訂閱者函數
if (!this.clietList[key]) { this.clietList[key] = []; } this.clietList[key].push(fn);
};
event.trigger = function() { //發佈消息函數
var key = Array.prototype.shift.call(arguments), fns = this.clietList[key]; for (var i = 0; i < fns.length; i++) { var fn = fns[i]; fn.apply(this, arguments); }
};
event.listen('web前端', fn1 = function(time) { //小強訂閱了這個消息。
console.log('姓名:小強'); console.log('正式上班時間:' + time);
});
event.listen('web前端', fn2 = function(time) { //大大強訂閱了這個消息
console.log('姓名:大大強'); console.log('正式上班時間:' + time);
});
//發佈者發佈消息
event.trigger('web前端','小強', '2016/10'); //姓名:小強 正式上班時間:2016/10
event.trigger('php','大大強', '2016/15'); //姓名:大大強 正式上班時間:2016/15
經過添加了一個key,咱們實現了對職位的判斷。
有了訂閱事件,咱們怎麼能少了取消訂閱事件呢?
event.remove = function(key, fn) {
var fns = this.clietList[key]; if (!fns) { return false; } if (!fn) { //若是沒有傳入fn回調函數,直接取消key對應消息的全部訂閱 this.clietList[key] = []; } else { for (var i = 0; i < fns.length; i++) { //遍歷回調函數列表 var _fn = fns[i]; if (_fn === fn) { fns.splice(i, 1); //刪除訂閱者的回調函數 } } }
};
//這時候必須指定回調函數,不然沒法在remove函數中進行對比刪除。
event.listen('web前端', fn1 = function(time) { //小強訂閱了這個消息。
console.log('姓名:小強'); console.log('正式上班時間:' + time);
});
event.listen('web前端', fn2 = function(time) { //大大強訂閱了這個消息
console.log('姓名:大大強'); console.log('正式上班時間:' + time);
});
event.remove('web前端',fn1);
//發佈者發佈消息
event.trigger('web前端','2016/10');
//輸出 姓名:大大強 正式上班時間:2016/10
對上面代碼進行改進,建立一個全局對象來實現觀察者模式,
使用閉包實現私有變量,僅暴露必須的API給使用者:
var event = (function() {
var clietList = []; //發佈者的緩存列表(應聘者列表) var listen = function(key, fn) { //增長訂閱者函數 if (!this.clietList[key]) { this.clietList[key] = []; } this.clietList[key].push(fn); }; var trigger = function() { //發佈消息函數 var key = Array.prototype.shift.call(arguments), fns = this.clietList[key]; for (var i = 0; i < fns.length; i++) { var fn = fns[i]; fn.apply(this, arguments); } }; var remove = function(key, fn) { var fns = this.clietList[key]; if (!fns) { return false; } if (!fn) { //若是沒有傳入fn回調函數,直接取消key對應消息的全部訂閱 this.clietList[key] = []; } else { for (var i = 0; i < fns.length; i++) { //遍歷回調函數列表 var _fn = fns[i]; if (_fn === fn) { fns.splice(i, 1); //刪除訂閱者的回調函數 } } } }; return{ listen:listen, trigger:trigger, remove:remove }
})();
觀察者模式進階:
使用命名空間防止事件名衝突 實現先發布後訂閱功能
說說weakMap
### **Objects** 咱們應該首先討論如何使用對象。 好吧,我相信90%以上的人已經知道這部份內容了,由於你點擊這篇文章是爲了瞭解新的集合對象,但對於JavaScript的初學者來講,咱們仍是簡單說說它們吧。 1. const algorithm = { site: "leetcode" }; 2. console.log(algorithm.site); // leetcode 4. for (const key in algorithm) { 5. console.log(key, algorithm[key]); 6. } 8. // site leetcode 9. delete algorithm.site; 10. console.log(algorithm.site); // undefined 因此我作了一個 algorithm 對象,它的key和value是一個字符串類型的值,而我經過用 . 關鍵字來調用該值。 另外,for-in 循環也很適合在對象中循環。你能夠用 [] 關鍵字訪問其鍵對應的值。可是不能使用 for-of 循環,由於對象是不可迭代的。 對象的屬性能夠用 delete 關鍵字來刪除。這樣就能夠完全擺脫對象的屬性,你們要注意不要和這種方法混淆。 1. const algorithm = { site: "leetcode" }; 2. // Property is not removed!! 3. algorithm.site = undefined; 4. // Property is removed!! 5. delete algorithm.site; algorithm.site = undefined 只是將新值分配給 site。 好的,咱們已經快速討論了有關對象的一些事項: * 如何添加屬性 * 如何遍歷對象 * 如何刪除屬性 ### **Map** Map 是JavaScript中新的集合對象,其功能相似於對象。可是,與常規對象相比,存在一些主要差別。 首先,讓咱們看一個建立Map對象的簡單示例。 **/ 如何添加屬性 /** 1. const map = new Map(); 2. // Map(0) {} Map 不須要建立任何內容,可是添加數據的方式略有不一樣。 1. map.set('name', 'john'); 2. // Map(1) {"name" => "john"} Map 有一種特殊的方法可在其中添加稱爲 set 的屬性。它有兩個參數:鍵是第一個參數,值是第二個參數。 1. map.set('phone', 'iPhone'); 2. // Map(2) {"name" => "john", "phone" => "iPhone"} 3. map.set('phone', 'iPhone'); 4. // Map(2) {"name" => "john", "phone" => "iPhone"} 可是,它不容許你在其中添加現有數據。若是 Map 對象中已經存在與新數據的鍵對應的值,則不會添加新數據。 1. map.set('phone', 'Galaxy'); 2. // Map(2) {"name" => "john", "phone" => "Galaxy"} 可是你能夠用其餘值覆蓋現有數據。 **/ 如何遍歷對象 /** Map 是一個可迭代的對象,這意味着能夠使用 for-of 語句將其映射。 1. for (const item of map) { 2. console.dir(item); 3. } 4. // Array(2) ["name", "john"] 5. // Array(2) ["phone", "Galaxy"] 要記住的一件事是 Map 以數組形式提供數據,你應該解構數組或訪問每一個索引以獲取鍵或值。 要僅獲取鍵或值,還有一些方法可供你使用。 1. map.keys(); 2. // MapIterator {"name", "phone"} 3. map.values(); 4. // MapIterator {"john", "Galaxy"} 5. map.entries(); 6. // MapIterator {"name" => "john", "phone" => "Galaxy"} 你甚至能夠使用展開操做符(...)來獲取Map的所有數據,由於展開操做符還能夠在幕後與可迭代對象一塊兒工做。 1. const simpleSpreadedMap = [...map]; 2. // [Array(2), Array(2)] **/ 如何刪除屬性 /** 從 Map 對象中刪除數據也很容易,你所須要作的就是調用 delete。 1. map.delete('phone'); 2. // true 3. map.delete('fake'); 4. // false delete 返回布爾值,該布爾值指示 delete 函數是否成功刪除了數據。若是是,則返回 true,不然返回 false。 ### WeakMap WeakMap起源於Map,所以它們彼此很是類似。可是,WeakMap具備很大的不一樣。 WeakMap的名字是怎麼來的呢?嗯,是由於它與它的引用連接所指向的數據對象的鏈接或關係沒有Map的鏈接或關係那麼強,因此它是弱的。 那麼,這究竟是什麼意思呢? **/ 差別1:key必須是對象 /** 1. const John = { name: 'John' }; 2. const weakMap = new WeakMap(); 3. weakMap.set(John, 'student'); 4. // WeakMap {{...} => "student"} 5. weakMap.set('john', 'student'); 6. // Uncaught TypeError: Invalid value used as weak map key 你能夠將任何值做爲鍵傳入Map對象,但WeakMap不一樣,它只接受一個對象做爲鍵,不然,它將返回一個錯誤。 **/ 差別2:並不是Map中的全部方法都支持 /** 能夠使用WeakMap的方法以下。 * delete * get * has * set 這個話題最大的不一樣是WeakMap不支持迭代對象的方法。可是爲何呢?下面將對此進行描述。 **/ 區別3:當GC清理引用時,數據會被刪除 /** 與Map相比,這是最大的不一樣。 1. let John = { major: "math" }; 3. const map = new Map(); 4. const weakMap = new WeakMap(); 6. map.set(John, 'John'); 7. weakMap.set(John, 'John'); 9. John = null; 10. /* John 被垃圾收集 */ 當John對象被垃圾回收時,Map對象將保持引用連接,而WeakMap對象將丟失連接。因此當你使用WeakMap時,你應該考慮這個功能。 ### Set Set也很是相似於Map,可是Set對於單個值更有用。 **/ 如何添加屬性 /** 1. const set = new Set(); 3. set.add(1); 4. set.add('john'); 5. set.add(BigInt(10)); 6. // Set(4) {1, "john", 10n} 與Map同樣,Set也阻止咱們添加相同的值。 1. set.add(5); 2. // Set(1) {5} 4. set.add(5); 5. // Set(1) {5} **/ 如何遍歷對象 /** 因爲Set是一個可迭代的對象,所以能夠使用 for-of 或 forEach 語句。 1. for (const val of set) { 2. console.dir(val); 3. } 4. // 1 5. // 'John' 6. // 10n 7. // 5 9. set.forEach(val => console.dir(val)); 10. // 1 11. // 'John' 12. // 10n 13. // 5 **/ 如何刪除屬性 /** 這一部分和 Map 的刪除徹底同樣。若是數據被成功刪除,它返回 true,不然返回 false。 1. set.delete(5); 2. // true 4. set.delete(function(){}); 5. // false; 若是你不想將相同的值添加到數組表單中,則Set可能會很是有用。 1. /* With Set */ 2. const set = new Set(); 3. set.add(1); 4. set.add(2); 5. set.add(2); 6. set.add(3); 7. set.add(3); 8. // Set {1, 2, 3} 10. // Converting to Array 11. const arr = [ ...set ]; 12. // [1, 2, 3] 14. Object.prototype.toString.call(arr); 15. // [object Array] 17. /* Without Set */ 18. const hasSameVal = val => ar.some(v === val); 19. const ar = []; 21. if (!hasSameVal(1)) ar.push(1); 22. if (!hasSameVal(2)) ar.push(2); 23. if (!hasSameVal(3)) ar.push(3); ### WeakSet 與WeakMap同樣,WeakSet也將丟失對內部數據的訪問連接(若是內部數據已被垃圾收集)。 1. let John = { major: "math" }; 3. const set = new Set(); 4. const weakSet = new WeakSet(); 6. set.add(John); 7. // Set {{...}} 8. weakSet.add(John); 9. // WeakSet {{...}} 11. John = null; 12. /* John 被垃圾收集 */ 一旦對象 John 被垃圾回收,WeakSet就沒法訪問其引用 John 的數據。並且WeakSet不支持 for-of 或 forEach,由於它不可迭代。 ### 比較總結 相同點:添加相同的值不支持。 Map vs. WeakMap:WeakMap僅接受對象做爲鍵,而Map不接受。 **Map and Set:** * 可迭代的對象,支持 for..of,forEach 或 ... 運算符 * 脫離GC關係 **WeakMap and WeakSet:** * 不是一個可迭代的對象,不能循環。 * 若是引用數據被垃圾收集,則沒法訪問數據。 * 支持較少的方法。
ES6的經常使用API
var m1 = new Map(); var m2 = new Map([['a', 123], ['b', 456], [3, 'abc']]);
Symbol類型的屬性取值必須是obj[xm],不能直接obj.xm
Symbol能夠用來保護對象的某個屬性,由於對象的Symbol屬性不會被遍歷出來
let s1 = Symbol('name'); let s2 = Symbol('name'); console.log( s1 === s2 ); // false
let s1 = Symbol('name'); let s2 = Symbol('name'); console.log( s1 === s2 ); // false
const obj = { foo: 'bar', baz: 42 }; Object.entries(obj); // [ ["foo", "bar"], ["baz", 42] ]
var obj1 = {a: 1, b: 2, c: {d: 4, e: 5}}; var obj2 = Object.assign({}, obj1); console.log(obj1.c === obj2.c); // ture
var obj1 = {a: 1, b: 2, c: {d: 4, e: 5}}; var obj2 = JSON.parse(JSON.stringify(obj1)); console.log(obj1.c === obj2.c); // false
class Cat { mm = 789; // 原型上的屬性 constructor(n,c){ // 構造器 this.name = n; this.color = c; this.trait = function () { console.log('賣萌~'); } }; skill(){ // 原型上的方法 console.log('抓老鼠'); }; } class Dog extends Cat { // 繼承 constructor(n,c,f){ super(n,c); // 構造函數繼承 this.food = f; // super.skill();//super當一個對象來使用時,只能訪問方法(函數) // console.log(super.abc);//不能訪問屬性 // console.log(this.abc);//123 // this.skill();//'抓老鼠' // console.log(super);報錯 }; } var dog1 = new Dog('大黃','黑色','shi'); dog1.trait(); dog1.skill(); console.log( dog1.name ); console.log( dog1.color ); console.log( dog1.food ); console.log( dog1.mm ); console.log( dog1.constructor ); // Dog
Vue路由模式
hash與history 對於Vue 這類漸進式前端開發框架,爲了構建SPA(單頁面應用),須要引入前端路由系統,這也就是Vue-router存在的意義。前端路由的核心,就在於——— 改變視圖的同時不會向後端發出請求。
1、爲了達到這個目的,瀏覽器提供瞭如下兩種支持:
一、hash ——即地址欄URL中的#符號(此hsah 不是密碼學裏的散列運算)。 好比這個URL:http://www.abc.com/#/hello, hash 的值爲#/hello。它的特色在於:hash 雖然出現URL中,但不會被包含在HTTP請求中,對後端徹底沒有影響,所以改變hash不會從新加載頁面。
二、history ——利用了HTML5 History Interface 中新增的pushState() 和replaceState() 方法。(須要特定瀏覽器支持) 這兩個方法應用於瀏覽器的歷史記錄站,在當前已有的back、forward、go 的基礎之上,它們提供了對歷史記錄進行修改的功能。只是當它們執行修改是,雖然改變了當前的URL,但你瀏覽器不會當即向後端發送請求。 history模式,會出現404 的狀況,須要後臺配置。
2、404 錯誤
一、hash模式下,僅hash符號以前的內容會被包含在請求中,如 http://www.abc.com, 所以對於後端來講,即便沒有作到對路由的全覆蓋,也不會返回404錯誤;
二、history模式下,前端的url必須和實際向後端發起請求的url 一致,如http://www.abc.com/book/id 。若是後端缺乏對/book/id 的路由處理,將返回404錯誤。
hash和history的實現方式
主要說一下新增的兩個API history.pushState() 和 history.replaceState()
pushState() 和 history.replaceState()同樣採用三個參數:狀態對象,標題(當前被忽略)和(可選)URL。讓咱們更詳細地研究這三個參數中的每個
history.replaceState()操做徹底同樣history.pushState(),只是replaceState()修改當前的歷史條目,而不是建立一個新的。請注意,這不會阻止在全局瀏覽器歷史記錄中建立新條目。
replaceState() 當您想要更新當前歷史記錄條目的狀態對象或URL以響應某些用戶操做時,此功能特別有用。
不一樣之處在於,pushState()會增長一條新的歷史記錄,而replaceState()則會替換當前的歷史記錄。
舉一個例子
在百度頁面打開控制檯輸入
window.history.pushState(null, null, "https://www.baidu.com/?name=history");
按下回車會發現地址欄變成這樣
上面的例子中 改變url頁面並無刷新,一樣根據API所述,瀏覽器會產生瀏覽記錄
注意pushState()的url不支持跨域
經過用戶的歷史記錄中向後和向前移動使用作了back(),forward()和go() 方法。
咱們常常在 url 中看到 #,這個 # 有兩種狀況,一個是咱們所謂的錨點,好比典型的回到頂部按鈕原理、Github 上各個標題之間的跳轉等,路由裏的 # 不叫錨點,咱們稱之爲 hash,大型框架的路由系統大多都是哈希實現的。
一樣咱們須要一個根據監聽哈希變化觸發的事件 ——hashchange 事件
咱們用 window.location 處理哈希的改變時不會從新渲染頁面,而是看成新頁面加到歷史記錄中,這樣咱們跳轉頁面就能夠在 hashchange 事件中註冊 ajax 從而改變頁面內容。
hashchange 在低版本 IE 須要經過輪詢監聽 url 變化來實現,咱們能夠模擬以下
(function(window) { // 若是瀏覽器不支持原生實現的事件,則開始模擬,不然退出。 if ( "onhashchange" in window.document.body ) { return; } var location = window.location, oldURL = location.href, oldHash = location.hash; // 每隔100ms檢查hash是否發生變化 setInterval(function() { var newURL = location.href, newHash = location.hash; // hash發生變化且全局註冊有onhashchange方法(這個名字是爲了和模擬的事件名保持統一) if ( newHash != oldHash && typeof window.onhashchange === "function" ) { // 執行方法 window.onhashchange({ type: "hashchange", oldURL: oldURL, newURL: newURL }); oldURL = newURL; oldHash = newHash; } }, 100); })(window)
我的感受仍是hash方案好一點,由於照顧到低級瀏覽器,就是多了#號感受不太美觀,二者兼容也是能夠的,只能根據瀏覽器的版本給出對應的方案 不過也支持IE8+ 仍是不錯的
兄弟組件的傳值
注意:註冊的 Bus 要在組件銷燬時卸載,不然會屢次掛載,形成觸發一次但多個響應的狀況。
beforeDestroy () {
this.$Bus.$off('sendMessage', this.message);
}
Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的全部組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。
圖片引用自網絡:
vuex
Vuex 的具體使用。
不是方法的方法:
子組件 A 經過事件 $emit 傳值傳給父組件。 父組件經過屬性 props 傳值給子組件 B。
深層次嵌套組件傳值
provide 選項容許咱們指定咱們想要提供給後代組件的數據/方法。
provide: function () {
return {
getMap: this.getMap
}
}
而後在任何後代組件裏,咱們均可以使用 inject 選項來接收指定的咱們想要添加在這個實例上的屬性:
inject: ['getMap']
provide 和 inject 主要爲高階插件/組件庫提供用例。並不推薦直接用於應用程序代碼中。
// 父級組件提供 'foo'
var Provider = {
provide: {
foo: 'bar'
},
// ...
}
// 子組件注入 'foo'
var Child = {
inject: ['foo'],
created () {
console.log(this.foo) // => "bar"
}
// ...
}
然而,依賴注入仍是有負面影響的。它將你應用程序中的組件與它們當前的組織方式耦合起來,使重構變得更加困難。同時所提供的屬性是非響應式的。這是出於設計的考慮,由於使用它們來建立一箇中心化規模化的數據跟使用 $root 作這件事都是不夠好的。若是你想要共享的這個屬性是你的應用特有的,而不是通用化的,或者若是你想在祖先組件中更新所提供的數據,那麼這意味着你可能須要換用一個像 Vuex 這樣真正的狀態管理方案了。
這個兩個屬性是 2.4 新增的特性。
$attrs:
官網介紹的很累贅,暫且理解爲非 props 屬性集合。更多介紹。
當一個組件中沒有聲明任何 prop 時,this.$attrs 能夠獲取到全部父做用域的屬性綁定 (class 和 style 除外),而且能夠經過 v-bind="$attrs" 傳給其內部組件 —— 在建立高級別的組件時很是有用。
inheritAttrs:
控制元素屬性是否顯示在 dom 上,默認值爲 true。
默認狀況下父做用域的不被認做 props 的特性綁定 (attribute bindings) 將會「回退」且做爲普通的 HTML 特性應用在子組件的根元素上。當撰寫包裹一個目標元素或另外一個組件的組件時,這可能不會老是符合預期行爲。經過設置 inheritAttrs 到 false,這些默認行爲將會被去掉。而經過 (一樣是 2.4 新增的) 實例屬性 $attrs 可讓這些特性生效,且能夠經過 v-bind 顯性的綁定到非根元素上。
祖先組件:
<template>
<div>
<List-item :title="title" :message="message"></List-item>
</div>
</template>
<script>
import ListItem from "./List-item";
export default {
data() {
return { title: "我是title", message: "傳給後代" }
},
components: {
ListItem
}
}
</script>
父組件:
<template>
<div>
<h1>{{title}}</h1> <h2>{{$attrs.message}}</h2> <!-- 經過 v-bind="$attrs" 傳入後代組件--> <ListItem2 v-bind='$attrs'></ListItem2>
</div>
</template>
<script>
import ListItem2 from './List-item2'
export default {
props: {
title: String
},
components: {
ListItem2
},
// 默認爲 true,若是傳入的屬性子組件沒有 prop 接受,就會以字符串的形式做爲標籤的屬性存在 <div message="傳給後代"></div>
// 設爲 false,在 dom 中就看不到這些屬性 <div>...</div>
inheritAttrs: false
}
</script>
後代組件:
<template>
<div>
{{$attrs.message}}
</div>
</template>
<script>
export default {
mounted() {
console.log(this.$attrs) // {message: "傳給後代"}
}
}
</script>
渲染出來的結果爲:
$attrs/inheritAttrs
插槽 slot 與子組件傳值
在實際項目中確實有遇到插槽後備內容 動態顯示的狀況,因此這裏要補充一下插槽 後備內容 是如何與子組件進行通訊的。
插槽後備內容是指:寫在父組件中,包含在子組件標籤裏的,與子組件中的 slot 對應。
<template>
<child-component>
我是插槽的後備內容
</child-component>
</template>
好比這裏有一個含有 slot 的 current-user 組件,它的模版結構是這樣的:
<!-- 子組件 current-user.vue -->
<template>
<div>
<div>current-user組件</div> <slot>插槽裏默認顯示:{{user.firstName}}</slot>
</div>
</template>
<script>
export default {
data() {
return { user: { firstName: "zhao", lastName: "xinglei" } }
}
}
</script>
它的父組件是這樣的:
<!-- 父組件 Users.vue -->
<template>
<div>
<div>我是Users組件</div> <current-user> 我是插槽裏的後備內容: {{user.lastName}}(我想顯示爲子組件中 user.lastName ) </current-user>
</div>
</template>
<script>
import CurrentUser from './Current-User.vue'
export default {
components: {
CurrentUser
}
}
</script>
咱們看到,在父組件 Users 中,爲子組件 current-user 提供的後備內容中,想要顯示子組件定義的 user.firstName 是不能作到的。
官網中提供一個指令 v-slot,它與 props 結合使用從而達到插槽後備內容與子組件通訊的目的。
咱們首先須要在子組件的 slot 中傳遞一個 props(這個props 叫作插槽props),這裏咱們起名叫 user:
<!-- 子組件 current-user.vue -->
<template>
<div>
<div>current-user組件</div> <slot :user="user"> 插槽裏默認顯示:{{user.firstName}} </slot>
</div>
</template>
在父組件中,包含插槽後備內容的子組件標籤上咱們綁定一個 v-slot 指令,像這樣:
<template>
<div>
<div>我是Users組件</div> <!-- slotProps裏的內容就是子組件傳遞過來的 props --> <!-- "user": { "firstName": "zhao", "lastName": "xinglei" } --> <current-user v-slot="slotProps"> {{slotProps}} </current-user>
</div>
</template>
最後渲染出來的結果爲:
做用域插槽
官網給這種插槽起名叫作做用域插槽,更多瞭解。
總結
3 ref 子組件引用,在操做表單元素時會應用的到。
說說vue的響應式
Vue 的響應式原理是核心是經過 ES5 的保護對象的 Object.defindeProperty 中的訪問器屬性中的 get 和 set 方法,data 中聲明的屬性都被添加了訪問器屬性,當讀取 data 中的數據時自動調用 get 方法,當修改 data 中的數據時,自動調用 set 方法,檢測到數據的變化,會通知觀察者 Wacher,觀察者 Wacher自動觸發從新render 當前組件(子組件不會從新渲染),生成新的虛擬 DOM 樹,Vue 框架會遍歷並對比新虛擬 DOM 樹和舊虛擬 DOM 樹中每一個節點的差異,並記錄下來,最後,加載操做,將全部記錄的不一樣點,局部修改到真實 DOM 樹上。
Mixin
混入 (mixin) 提供了一種很是靈活的方式,來分發 Vue 組件中的可複用功能。一個混入對象能夠包含任意組件選項。當組件使用混入對象時,全部混入對象的選項將被「混合」進入該組件自己的選項。
// 定義一個混入對象 var myMixin = { created: function () { this.hello() }, methods: { hello: function () { console.log('hello from mixin!') } } } // 定義一個使用混入對象的組件 var Component = Vue.extend({ mixins: [myMixin] }) var component = new Component() // => "hello from mixin!"
vue的訂閱發佈者
##### 一、vue響應原理: vue.js採用數據劫持結合發佈-訂閱者模式,經過Object.defineProperty()來劫持data中各個屬性的setter、getter,在數據變更時,發佈消息給訂閱者,觸發響應的監聽回調。 (setter和getter是對象的存儲器屬性,是一個函數,用來獲取和設置值) ##### 二、發佈-訂閱者模式的做用: ##### 一、vue響應原理: vue.js採用數據劫持結合發佈-訂閱者模式,經過Object.defineProperty()來劫持data中各個屬性的setter、getter,在數據變更時,發佈消息給訂閱者,觸發響應的監聽回調。 (setter和getter是對象的存儲器屬性,是一個函數,用來獲取和設置值) ##### 二、發佈-訂閱者模式的做用: 處理一對多的場景,應用於不一樣狀況下的不一樣函數調用 優勢:低耦合性,易於代碼維護; 缺點:若訂閱的消息未發生,需消耗必定的時間和內存。 [![複製代碼](https://upload-images.jianshu.io/upload_images/23849911-0de625688003404e.gif?imageMogr2/auto-orient/strip)](javascript:void(0); "複製代碼") <pre><!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Vue發佈-訂閱模式</title> </head> <body> <div id="app"> 訂閱試圖-1:<span class="box-1">第一個值</span> 訂閱試圖-2:<span class="box-2">第二個值</span> </div> <script> //訂閱器模型 var Dep = { list: {}, listen: function (key, fn) { (this.list[key] || (this.list[key] = [])).push(fn); }, trigger: function () { var key = Array.prototype.shift.call(arguments); fns = this.list[key]; if (!fns || fns.length == 0) return; for (var i = 0, fn; fn = fns[i++];) { fn.apply(this, arguments);//發佈消息附帶的參數 } } }; //劫持的方法 Object.defineProperty方法,給對象的屬性賦值 var dataHijack = function ({ data, tag, datakey, selector }) { debugger var value = ''; el = document.querySelector(selector); Object.defineProperty(data, datakey, { //拿到數據 get: function () { console.log('我獲取到值了'); return value; }, //設置數據 set: function (newVal) { console.log('我設置值了'); value = newVal; Dep.trigger(tag, newVal); //發佈消息,更新變化 } }) //綁定觀察者 Dep.listen(tag, function (text) { el.innerHTML = text; }) }; var dataObj = {}; //數據 //數據劫持 dataHijack({ data: dataObj, tag: 'view-1', datakey: 'one', selector: '.box-1' }); dataHijack({ data: dataObj, tag: 'view-2', datakey: 'two', selector: '.box-2' }); </script> </body> </html></pre> [![複製代碼](https://upload-images.jianshu.io/upload_images/23849911-c0f72b5bf7fc48f0.gif?imageMogr2/auto-orient/strip)](javascript:void(0); "複製代碼") [![複製代碼](https://upload-images.jianshu.io/upload_images/23849911-09d49fa4ab9cd9bd.gif?imageMogr2/auto-orient/strip)](javascript:void(0); "複製代碼") <pre> // jquery中的發佈-訂閱者 //建立一個事件池 $.Callback() let $pond= $.Callback(); $('.submit').click(function(){ //發佈 點擊的時候通知事件池中的方法執行,同時傳遞實參 $pond.fire(100,200); }); let fn1=function(){console.log(1)} let fn2=function(){console.log(2)} let fn3=function(n,m){console.log(3,n+m)} //把須要作的事情添加到事件池中 //事件池至關於一個登記冊,把全部訂閱者收集到上面 $pond.add(fn1); $pond.add(fn2); $pond.add(fn3); </pre>
常見的狀態碼
各種別常見狀態碼: 2xx (3種) 200 OK:表示從客戶端發送給服務器的請求被正常處理並返回; 204 No Content:表示客戶端發送給客戶端的請求獲得了成功處理,但在返回的響應報文中不含實體的主體部分(沒有資源能夠返回); 206 Patial Content:表示客戶端進行了範圍請求,而且服務器成功執行了這部分的GET請求,響應報文中包含由Content-Range指定範圍的實體內容。 3xx (5種) 301 Moved Permanently:永久性重定向,表示請求的資源被分配了新的URL,以後應使用更改的URL; 302 Found:臨時性重定向,表示請求的資源被分配了新的URL,但願本次訪問使用新的URL; 301與302的區別:前者是永久移動,後者是臨時移動(以後可能還會更改URL) 303 See Other:表示請求的資源被分配了新的URL,應使用GET方法定向獲取請求的資源; 302與303的區別:後者明確表示客戶端應當採用GET方式獲取資源 304 Not Modified:表示客戶端發送附帶條件(是指採用GET方法的請求報文中包含if-Match、If-Modified-Since、If-None-Match、If-Range、If-Unmodified-Since中任一首部)的請求時,服務器端容許訪問資源,可是請求爲知足條件的狀況下返回改狀態碼; 307 Temporary Redirect:臨時重定向,與303有着相同的含義,307會遵守瀏覽器標準不會從POST變成GET;(不一樣瀏覽器可能會出現不一樣的狀況); 4xx (4種) 400 Bad Request:表示請求報文中存在語法錯誤; 401 Unauthorized:未經許可,須要經過HTTP認證; 403 Forbidden:服務器拒絕該次訪問(訪問權限出現問題) 404 Not Found:表示服務器上沒法找到請求的資源,除此以外,也能夠在服務器拒絕請求但不想給拒絕緣由時使用; 5xx (2種) 500 Inter Server Error:表示服務器在執行請求時發生了錯誤,也有多是web應用存在的bug或某些臨時的錯誤時; 503 Server Unavailable:表示服務器暫時處於超負載或正在進行停機維護,沒法處理請求;
Webpack性能優化
`懶加載:`也叫延遲加載,即在須要的時候進行加載,隨用隨載。 使用懶加載的緣由: `vue`是單頁面應用,使用`webpcak`打包後的文件很大,會使進入首頁時,加載的資源過多,頁面會出現白屏的狀況,不利於用戶體驗。運用懶加載後,就能夠按需加載頁面,提升用戶體驗。
import Vue from 'vue' import Router from 'vue-router' Vue.use(Router) export default new Router({ mode: 'history', routes: [ { path: '/', component: resolve => require(['@/components/DefaultIndex'],resolve), children: [ { path: '', component: resolve => require(['@/components/Index'],resolve) }, { path: '*', redirect: '/Index' } ] }) 複製代碼
import Vue from 'vue' import Router from 'vue-router' import DefaultIndex from '@/components/DefaultIndex' import Index from '@/components/Index' Vue.use(Router) export default new Router({ mode: 'history', routes: [ { path: '/', component: 'DefaultIndex ', children: [ { path: '', component: 'Index' }, { path: '*', redirect: '/Index' } ] }) 複製代碼
通常在`vue`項目中用`webpack`打包時,會根據`webpack.base.conf.js`中`url-loader`中設置`limit`大小來對圖片處理,對小於`limit`的圖片轉化爲`base64`格式,其他的不作操做。因此對有些較大的圖片資源,在請求資源的時候,加載會很慢,能夠用`image-webpack-loader`來壓縮圖片。
npm install image-webpack-loader --save-dev 複製代碼
在`webpack.base.conf.js`文件中引入配置(此項目我用的是腳手架搭建的,因此是`webpack.base.conf.js`)
1: 引入: require("image-webpack-loader") 2:配置: module: { rules: [ ...(config.dev.useEslint ? [createLintingRule()] : []), { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, loader: 'url-loader', options: { loader: 'image-webpack-loader', options: { bypassOnDebug: true, } } }, 或者也可配置爲: { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, use:[ { loader: 'url-loader', options: { limit: 10000, name: utils.assetsPath('img/[name].[hash:7].[ext]') } }, { loader: 'image-webpack-loader', options: { bypassOnDebug: true, } } ] } 複製代碼
因爲`webpack`打包後的`js`過大,以致於在加載資源時間過長。因此將文件打包成多個`js`文件,在須要的時候按需加載。
entry:{ main:'xxx.js' } plugins:{ new commonsChunkPlugin({ name:'commons', minChunks:function(module){ // 下邊return參考的vue-cli配置 // any required modules inside node_modules are extracted to vendor return ( module.resource && /\.js$/.test(module.resource) && module.resource.indexOf( path.join(__dirname, '../node_modules') ) === 0 ) } }) , // 如下才是關鍵 new commonsChunkPlugin({ name:'charts', chunks:['commons'] minChunks:function(module){ return ( module.resource && /\.js$/.test(module.resource) && module.resource.indexOf( path.join(__dirname, '../node_modules') ) === 0 && ['jquery.js', 'highcharts.js','echarts'].indexOf( module.resource.substr(module.resource.lastIndexOf('/')+1).toLowerCase() ) != -1 ) } }) } 複製代碼
1:打包時,將一些沒必要要的插件能夠去掉,以防止打包一些無用的資源,致使打包後的文件過大,影響性能。 2:在引入第三方插件的時候,若是該插件的組件過大,能夠按需引入,如`element-ui`。 3:使用`webpack.optimize.UglifyJsPlugin`插件壓縮混淆[js](http://lib.csdn.net/base/javascript "JavaScript知識庫")代碼,使用方法以下:
plugins: [//webpack.config.jsnew webpack.optimize.UglifyJsPlugin({ warnings: false, compress: { join_vars: true, warnings: false, }, toplevel: false, ie8: false, ] 複製代碼
web前端項目,靜態資源放在`cdn`上比較多,`gzip`的壓縮是很是必要的,它直接改變了`js`文件的大小,減小兩到三倍。 參考[加速nginx: 開啓gzip和緩存](https://link.jianshu.com/?t=https%3A%2F%2Fwww.darrenfang.com%2F2015%2F01%2Fsetting-up-http-cache-and-gzip-with-nginx%2F),`nginx`的`gzip`配置很是簡單,在你對應的域名底下,添加下面的配置,重啓服務便可。`gzip_comp_level`的值大於`2`的時候並不明顯,建議設置在`1或者2`之間。
# 開啓gzip gzip on; # 啓用gzip壓縮的最小文件,小於設置值的文件將不會壓縮 gzip_min_length 1k; # gzip 壓縮級別,1-10,數字越大壓縮的越好,也越佔用CPU時間,後面會有詳細說明 gzip_comp_level 2; # 進行壓縮的文件類型。javascript有多種形式。其中的值能夠在 mime.types 文件中找到。 gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png; # 是否在http header中添加Vary: Accept-Encoding,建議開啓 gzip_vary on; # 禁用IE 6 gzip gzip_disable "MSIE [1-6]\."; 複製代碼
這種方法我 沒有使用過,有用過的親,能夠留言,溝通一下下。
爲了提升服務器獲取數據的速度,`nginx`緩存着靜態資源是很是必要的。若是是測試服務器對`html`應該不設置緩存,而`js`等靜態資源環境由於文件尾部會加上一個`hash`值,這能夠有效實現緩存的控制。
location ~* ^.+\.(ico|gif|jpg|jpeg|png)$ { access_log off; expires 30d; } location ~* ^.+\.(css|js|txt|xml|swf|wav)$ { access_log off; expires 24h; } location ~* ^.+\.(html|htm)$ { expires 1h; }
Angular和vue的區別
angularjs和vue的區別:
一、vueJS簡單易學,而angularJS的上手較高;
二、vue專一於View層,主打的是快速便捷,而angularJS功能則比較全面,固然體量也較大,相對沒有vue那麼便捷;
三、angularJS的指令都是ng-xxx,而vueJS的指令都是v-xxx;
四、angularJS的全部指令和方法都是綁定在$scope上的,而vueJS是new出來一個實例,全部的方法和指令都在這個實例上,一個頁面上能夠有多個vue實例,可是angularJS的對象只能有一個;
五、angularJS是由google開發和維護的,vueJS是由我的維護的;
六、vueJS通常用於移動端的開發,而angularJS通常應用於大型的項目
Post和get請求區別
GET 被強制服務器支持 瀏覽器對URL的長度有限制,因此GET請求不能代替POST請求發送大量數據 GET請求發送數據更小 GET請求是安全的 GET請求是冪等的 POST請求不能被緩存 POST請求相對GET請求是「安全」的
Css動畫跟js動畫的區別
CSS動畫 優勢: (1)瀏覽器能夠對動畫進行優化。 一、 瀏覽器使用與 requestAnimationFrame 相似的機制,requestAnimationFrame比起setTimeout,setInterval設置動畫的優點主要是:1)requestAnimationFrame 會把每一幀中的全部DOM操做集中起來,在一次重繪或迴流中就完成,而且重繪或迴流的時間間隔牢牢跟隨瀏覽器的刷新頻率,通常來講,這個頻率爲每秒60幀。2)在隱藏或不可見的元素中requestAnimationFrame不會進行重繪或迴流,這固然就意味着更少的的cpu,gpu和內存使用量。 二、強制使用硬件加速 (經過 GPU 來提升動畫性能) (2)代碼相對簡單,性能調優方向固定 (3)對於幀速表現很差的低版本瀏覽器,CSS3能夠作到天然降級,而JS則須要撰寫額外代碼 缺點: 一、 運行過程控制較弱,沒法附加事件綁定回調函數。CSS動畫只能暫停,不能在動畫中尋找一個特定的時間點,不能在半路反轉動畫,不能變換時間尺度,不能在特定的位置添加回調函數或是綁定回放事件,無進度報告 二、 代碼冗長。想用 CSS 實現稍微複雜一點動畫,最後CSS代碼都會變得很是笨重。 JS動畫 優勢: (1)JavaScript動畫控制能力很強, 能夠在動畫播放過程當中對動畫進行控制:開始、暫停、回放、終止、取消都是能夠作到的。 (2)動畫效果比css3動畫豐富,有些動畫效果,好比曲線運動,衝擊閃爍,視差滾動效果,只有JavaScript動畫才能完成 (3)CSS3有兼容性問題,而JS大多時候沒有兼容性問題 缺點: (1)JavaScript在瀏覽器的主線程中運行,而主線程中還有其它須要運行的JavaScript腳本、樣式計算、佈局、繪製任務等,對其干擾致使線程可能出現阻塞,從而形成丟幀的狀況。 (2)代碼的複雜度高於CSS動畫 總結:若是動畫只是簡單的狀態切換,不須要中間過程控制,在這種狀況下,css動畫是優選方案。它可讓你將動畫邏輯放在樣式文件裏面,而不會讓你的頁面充斥 Javascript 庫。然而若是你在設計很複雜的富客戶端界面或者在開發一個有着複雜UI狀態的 APP。那麼你應該使用js動畫,這樣你的動畫能夠保持高效,而且你的工做流也更可控。因此,在實現一些小的交互動效的時候,就多考慮考慮CSS動畫。對於一些複雜控制的動畫,使用javascript比較可靠。 css 動畫和 js 動畫的差別 1. 代碼複雜度,js 動畫代碼相對複雜一些 2. 動畫運行時,對動畫的控制程度上,js 可以讓動畫,暫停,取消,終止,css動畫不能添加事件 3. 動畫性能看,js 動畫多了一個js 解析的過程,性能不如 css 動畫好
Hash跟history的區別
vue-router 中hash模式和history模式。 在vue的路由配置中有mode選項,最直觀的區別就是在url中hash 帶了一個很醜的 # ,而history是沒有#的。vue默認使用hash。 mode:"hash"; mode:"history"; hash —— 即地址欄 URL 中的 # 符號(此 hash 不是密碼學裏的散列運算)。 好比這個 URL:http://www.aaa.com/#/hello,hash 的值爲 #/hello。它的特色在於:hash 雖然出如今 URL 中,但不會被包括在 HTTP 請求中,對後端徹底沒有影響,所以改變 hash 不會從新加載頁面。 history —— 利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。(須要特定瀏覽器支持) 這兩個方法應用於瀏覽器的歷史記錄棧,在當前已有的 back、forward、go 的基礎之上,它們提供了對歷史記錄進行修改的功能。只是當它們執行修改時,雖然改變了當前的 URL,但瀏覽器不會當即向後端發送請求。 所以能夠說,hash 模式和 history 模式都屬於瀏覽器自身的特性,Vue-Router 只是利用了這兩個特性(經過調用瀏覽器提供的接口)來實現前端路由。 history模式的問題 經過history api,咱們丟掉了醜陋的#,可是它也有個問題:不怕前進,不怕後退,就怕刷新,f5,(若是後端沒有準備的話),由於刷新是實實在在地去請求服務器的。 在hash模式下,前端路由修改的是#中的信息,而瀏覽器請求時不會將 # 後面的數據發送到後臺,因此沒有問題。可是在history下,你能夠自由的修改path,當刷新時,若是服務器中沒有相應的響應或者資源,則會刷新出來404頁面。
跨域問題
1) 在www.a.com/a.html中:
document.domain = 'a.com';var ifr = document.createElement('iframe');ifr.src = 'http://www.script.a.com/b.html';ifr.display = none;document.body.appendChild(ifr);ifr.onload = function(){ var doc = ifr.contentDocument || ifr.contentWindow.document; //在這裏操做doc,也就是b.html ifr.onload = null;};
2) 在www.script.a.com/b.html中:
document.domain = 'a.com';
這個沒什麼好說的,由於script標籤不受同源策略的限制。
function loadScript(url, func) { var head = document.head || document.getElementByTagName('head')[0]; var script = document.createElement('script'); script.src = url; script.onload = script.onreadystatechange = function(){ if(!this.readyState || this.readyState=='loaded' || this.readyState=='complete'){ func(); script.onload = script.onreadystatechange = null; } }; head.insertBefore(script, 0);}window.baidu = { sug: function(data){ console.log(data); }}loadScript('http://suggestion.baidu.com/su?wd=w',function(){console.log('loaded')});//咱們請求的內容在哪裏?//咱們能夠在chorme調試面板的source中看到script引入的內容
原理是利用location.hash來進行傳值。
假設域名a.com下的文件cs1.html要和cnblogs.com域名下的cs2.html傳遞信息。
1) cs1.html首先建立自動建立一個隱藏的iframe,iframe的src指向cnblogs.com域名下的cs2.html頁面
2) cs2.html響應請求後再將經過修改cs1.html的hash值來傳遞數據
3) 同時在cs1.html上加一個定時器,隔一段時間來判斷location.hash的值有沒有變化,一旦有變化則獲取獲取hash值
注:因爲兩個頁面不在同一個域下IE、Chrome不容許修改parent.location.hash的值,因此要藉助於a.com域名下的一個代理iframe
代碼以下:
先是a.com下的文件cs1.html文件:
function startRequest(){ var ifr = document.createElement('iframe'); ifr.style.display = 'none'; ifr.src = 'http://www.cnblogs.com/lab/cscript/cs2.html#paramdo'; document.body.appendChild(ifr);} function checkHash() { try { var data = location.hash ? location.hash.substring(1) : ''; if (console.log) { console.log('Now the data is '+data); } } catch(e) {};}setInterval(checkHash, 2000);
cnblogs.com域名下的cs2.html:
//模擬一個簡單的參數處理操做switch(location.hash){ case '#paramdo': callBack(); break; case '#paramset': //do something…… break;} function callBack(){ try { parent.location.hash = 'somedata'; } catch (e) { // ie、chrome的安全機制沒法修改parent.location.hash, // 因此要利用一箇中間的cnblogs域下的代理iframe var ifrproxy = document.createElement('iframe'); ifrproxy.style.display = 'none'; ifrproxy.src = 'http://a.com/test/cscript/cs3.html#somedata'; // 注意該文件在"a.com"域下 document.body.appendChild(ifrproxy); }}
a.com下的域名cs3.html
//由於parent.parent和自身屬於同一個域,因此能夠改變其location.hash的值parent.parent.location.hash = self.location.hash.substring(1);
window.name 的美妙之處:name 值在不一樣的頁面(甚至不一樣域名)加載後依舊存在,而且能夠支持很是長的 name 值(2MB)。
1) 建立a.com/cs1.html
2) 建立a.com/proxy.html,並加入以下代碼
<head> <script> function proxy(url, func){ var isFirst = true, ifr = document.createElement('iframe'), loadFunc = function(){ if(isFirst){ ifr.contentWindow.location = 'http://a.com/cs1.html'; isFirst = false; }else{ func(ifr.contentWindow.name); ifr.contentWindow.close(); document.body.removeChild(ifr); ifr.src = ''; ifr = null; } }; ifr.src = url; ifr.style.display = 'none'; if(ifr.attachEvent) ifr.attachEvent('onload', loadFunc); else ifr.onload = loadFunc; document.body.appendChild(iframe); }</script></head><body> <script> proxy('http://www.baidu.com/', function(data){ console.log(data); }); </script></body>
3 在b.com/cs1.html中包含:
<script> window.name = '要傳送的內容';</script>
1) a.com/index.html中的代碼:
<iframe id="ifr" src="b.com/index.html"></iframe><script type="text/javascript">window.onload = function() { var ifr = document.getElementById('ifr'); var targetOrigin = 'http://b.com'; // 若寫成'http://b.com/c/proxy.html'效果同樣 // 若寫成'http://c.com'就不會執行postMessage了 ifr.contentWindow.postMessage('I was there!', targetOrigin);};</script>
2) b.com/index.html中的代碼:
<script type="text/javascript"> window.addEventListener('message', function(event){ // 經過origin屬性判斷消息來源地址 if (event.origin == 'http://a.com') { alert(event.data); // 彈出"I was there!" alert(event.source); // 對a.com、index.html中window對象的引用 // 但因爲同源策略,這裏event.source不能夠訪問window對象 } }, false);</script>
CORS背後的思想,就是使用自定義的HTTP頭部讓瀏覽器與服務器進行溝通,從而決定請求或響應是應該成功,仍是應該失敗。
IE中對CORS的實現是xdr
var xdr = new XDomainRequest();xdr.onload = function(){ console.log(xdr.responseText);}xdr.open('get', 'http://www.baidu.com');......xdr.send(null);
其它瀏覽器中的實現就在xhr中
var xhr = new XMLHttpRequest();xhr.onreadystatechange = function () { if(xhr.readyState == 4){ if(xhr.status >= 200 && xhr.status < 304 || xhr.status == 304){ console.log(xhr.responseText); } }}xhr.open('get', 'http://www.baidu.com');......xhr.send(null);
實現跨瀏覽器的CORS
function createCORS(method, url){ var xhr = new XMLHttpRequest(); if('withCredentials' in xhr){ xhr.open(method, url, true); }else if(typeof XDomainRequest != 'undefined'){ var xhr = new XDomainRequest(); xhr.open(method, url); }else{ xhr = null; } return xhr;}var request = createCORS('get', 'http://www.baidu.com');if(request){ request.onload = function(){ ...... }; request.send();}
JSONP包含兩部分:回調函數和數據。
回調函數是當響應到來時要放在當前頁面被調用的函數。
數據就是傳入回調函數中的json數據,也就是回調函數的參數了。
function handleResponse(response){ console.log('The responsed data is: '+response.data);}var script = document.createElement('script');script.src = 'http://www.baidu.com/json/?callback=handleResponse';document.body.insertBefore(script, document.body.firstChild);/*handleResonse({"data": "zhe"})*///原理以下://當咱們經過script標籤請求時//後臺就會根據相應的參數(json,handleResponse)//來生成相應的json數據(handleResponse({"data": "zhe"}))//最後這個返回的json數據(代碼)就會被放在當前js文件中被執行//至此跨域通訊完成
jsonp雖然很簡單,可是有以下缺點:
1)安全問題(請求代碼中可能存在安全隱患)
2)要肯定jsonp請求是否失敗並不容易
web sockets是一種瀏覽器的API,它的目標是在一個單獨的持久鏈接上提供全雙工、雙向通訊。(同源策略對web sockets不適用)
web sockets原理:在JS建立了web socket以後,會有一個HTTP請求發送到瀏覽器以發起鏈接。取得服務器響應後,創建的鏈接會使用HTTP升級從HTTP協議交換爲web sockt協議。
只有在支持web socket協議的服務器上才能正常工做。
var socket = new WebSockt('ws://www.baidu.com');//http->ws; https->wsssocket.send('hello WebSockt');socket.onmessage = function(event){ var data = event.data;}
vue 的params和query的區別
初學vue的時候,不知道如何在方法中跳轉界面並傳參,百度事後,瞭解到兩種方式,params 與 query。而後,錯誤就這麼來了:
router文件下index.js裏面,是這麼定義路由的:
<pre>{
path:"/detail", name:"detail", component:home }</pre>
我想用params來傳參,是這麼寫的,嗯~
this.$router.push({
path:"/detail",
params:{
name:'nameValue',
code:10011
}
});
結果可想而知,接收參數的時候:
<pre>this.$route.params.code //undefined</pre>
這是由於,params只能用name來引入路由,下面是正確的寫法:
<pre>this.$router.push({
name:"detail",
params:{
name:'nameValue', code:10011
}
});</pre>
這回就對了,能夠直接拿到傳遞過來的參數nameValue了。
說完了個人犯傻,下面整理一下這二者的差異:
一、用法上的
剛纔已經說了,query要用path來引入,params要用name來引入,接收參數都是相似的,分別是this.$route.query.name和this.$route.params.name。
注意接收參數的時候,已是$route而不是$router了哦!!
二、展現上的
query更加相似於咱們ajax中get傳參,params則相似於post,說的再簡單一點,前者在瀏覽器地址欄中顯示參數,後者則不顯示
原型,原型鏈,閉包
全部函數都有一個特別的屬性:
prototype
: 顯式原型屬性全部實例對象都有一個特別的屬性:
__proto__
: 隱式原型屬性顯式原型與隱式原型的關係
原型鏈
變量提高與函數提高
理解
分類:
生命週期
包含哪些屬性:
全局 :
函數
原型鏈繼承 : 獲得方法
function Parent(){} Parent.prototype.test = function(){}; function Child(){} Child.prototype = new Parent(); // 子類型的原型指向父類型實例 Child.prototype.constructor = Child var child = new Child(); //有test()
借用構造函數 : 獲得屬性
function Parent(xxx){this.xxx = xxx} Parent.prototype.test = function(){}; function Child(xxx,yyy){ Parent.call(this, xxx);//借用構造函數 this.Parent(xxx) } var child = new Child('a', 'b'); //child.xxx爲'a', 但child沒有test()
組合
function Parent(xxx){this.xxx = xxx} Parent.prototype.test = function(){}; function Child(xxx,yyy){ Parent.call(this, xxx);//借用構造函數 this.Parent(xxx) } Child.prototype = new Parent(); //獲得test() var child = new Child(); //child.xxx爲'a', 也有test()
new一個對象背後作了些什麼?
執行上下文建立和初始化的過程
全局:
函數:
理解:
分類:
做用
區別做用域與執行上下文
理解:
做用:
寫一個閉包程序
function fn1() { var a = 2; function fn2() { a++; console.log(a); } return fn2; } var f = fn1(); f(); f();
閉包應用:
缺點:
解決:
內存溢出
內存泄露
常見的內存泄露:
箭頭函數和普通函數的區別
箭頭函數:
let fun = () => {
console.log('lalalala');
}
普通函數:
function fun() {
console.log('lalla');
}
箭頭函數至關於匿名函數,而且簡化了函數定義。箭頭函數有兩種格式,一種只包含一個表達式,連{ ... }和return都省略掉了。還有一種能夠包含多條語句,這時候就不能省略{ ... }和return。
let FunConstructor = () => {
console.log('lll');
}
let fc = new FunConstructor();
function A(a){
console.log(arguments);
}
A(1,2,3,4,5,8); // [1, 2, 3, 4, 5, 8, callee: ƒ, Symbol(Symbol.iterator): ƒ]
let B = (b)=>{
console.log(arguments);
}
B(2,92,32,32); // Uncaught ReferenceError: arguments is not defined
let C = (...c) => {
console.log(c);
}
C(3,82,32,11323); // [3, 82, 32, 11323]
var obj = {
a: 10,
b: () => {
console.log(this.a); // undefined console.log(this); // Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
},
c: function() {
console.log(this.a); // 10 console.log(this); // {a: 10, b: ƒ, c: ƒ}
}
}
obj.b();
obj.c();
var obj = {
a: 10,
b: function(){
console.log(this.a); //10
},
c: function() { return ()=>{
console.log(this.a); //10
}
}
}
obj.b();
obj.c()();
let obj2 = { a: 10, b: function(n) { let f \= (n) => n + this.a; return f(n); }, c: function(n) { let f \= (n) => n + this.a; let m \= { a: 20 }; return f.call(m,n); } }; console.log(obj2.b(1)); // 11 console.log(obj2.c(1)); // 11
var a = ()=>{ return 1; } function b(){ return 2; } console.log(a.prototype); // undefined console.log(b.prototype); // {constructor: ƒ}
在JavaScript中我們之前主要用關鍵var來定義變量,ES6以後,新增了定義變量的兩個關鍵字,分別是let和const。
對於變量來講,在ES5中var定義的變量會提高到做用域中全部的函數與語句前面,而ES6中let定義的變量則不會,let聲明的變量會在其相應的代碼塊中創建一個暫時性死區,直至變量被聲明。
let和const都可以聲明塊級做用域,用法和var是相似的,let的特色是不會變量提高,而是被鎖在當前塊中。
一個很是簡單的例子:
function test() { if(true) { console.log(a)//TDZ,俗稱臨時死區,用來描述變量不提高的現象 let a = 1 } } test() // a is not defined function test() { if(true) { let a = 1 } console.log(a) } test() // a is not defined
惟一正確的使用方法:先聲明,再訪問。
function test() { if(true) { let a = 1 console.log(a) } } test() // 1
const
聲明常量,一旦聲明,不可更改,並且常量必須初始化賦值。
const雖然是常量,不容許修改默認賦值,但若是定義的是對象Object,那麼能夠修改對象內部的屬性值。
const type = { a: 1 } type.a = 2 //沒有直接修改type的值,而是修改type.a的屬性值,這是容許的。 console.log(type) // {a: 2}
const和let的異同點
相同點:const和let都是在當前塊內有效,執行到塊外會被銷燬,也不存在變量提高(TDZ),不能重複聲明。
不一樣點:const不能再賦值,let聲明的變量能夠重複賦值。
const實際上保證的,並非變量的值不得改動,而是變量指向的那個內存地址所保存的數據不得改動。對於簡單類型的數據(數值、字符串、布爾值),值就保存在變量指向的那個內存地址,所以等同於常量。但對於複合類型的數據(主要是對象和數組),變量指向的內存地址,保存的只是一個指向實際數據的指針,const只能保證這個指針是固定的(即老是指向另外一個固定的地址),至於它指向的數據結構是否是可變的,就徹底不能控制了。所以,將一個對象聲明爲常量必須很是當心。
塊級做用域的使用場景
除了上面提到的經常使用聲明方式,咱們還能夠在循環中使用,最出名的一道面試題:循環中定時器閉包的考題
在for循環中使用var聲明的循環變量,會跳出循環體污染當前的函數。
for(var i = 0; i < 5; i++) { setTimeout(() => { console.log(i) //5, 5, 5, 5, 5 }, 0) } console.log(i) //5 i跳出循環體污染外部函數 //將var改爲let以後 for(let i = 0; i < 5; i++) { setTimeout(() => { console.log(i) // 0,1,2,3,4 }, 0) } console.log(i)//i is not defined i沒法污染外部函數
在實際開發中,咱們選擇使用var、let仍是const,取決於咱們的變量是否是須要更新,一般咱們但願變量保證不被惡意修改,而使用大量的const。使用const聲明,聲明一個對象的時候,也推薦使用const,當你須要修改聲明的變量值時,使用let,var能用的場景均可以使用let替代。
symbol
ES6 之前,咱們知道5種基本數據類型分別是Undefined,Null,Boolean,Number以及String,而後加上一種引用類型Object構成了JavaScript中全部的數據類型,可是ES6出來以後,新增了一種數據類型,名叫symbol,像它的名字表露的同樣,意味着獨一無二,意思是每一個 Symbol類型都是獨一無二的,不與其它 Symbol 重複。
能夠經過調用 Symbol() 方法將建立一個新的 Symbol 類型的值,這個值獨一無二,不與任何值相等。
var mySymbol=Symbol(); console.log(typeof mySymbol) //"symbol"
ES6字符串新增的方法
UTF-16碼位:ES6強制使用UTF-16字符串編碼。關於UTF-16的解釋請自行百度瞭解。
codePointAt():該方法支持UTF-16,接受編碼單元的位置而非字符串位置做爲參數,返回與字符串中給定位置對應的碼位,即一個整數值。
String.fromCodePoiont():
做用與codePointAt相反,檢索字符串中某個字符的碼位,也能夠根據指定的碼位生成一個字符。
normalize():提供Unicode的標準形式,接受一個可選的字符串參數,指明應用某種Unicode標準形式。
在ES6中,新增了3個新方法。每一個方法都接收2個參數,須要檢測的子字符串,以及開始匹配的索引位置。
模板字符串
字符串是JavaScript中基本類型之一,應該算是除了對象以外是使用最爲頻繁的類型吧,字符串中包含了例如substr,replace,indexOf,slice等等諸多方法,ES6引入了模板字符串的特性,用反引號來表示,能夠表示多行字符串以及作到文本插值(利用模板佔位符)。
// 之前的多行字符串咱們這麼寫: console.log("hello world 1\n\ hello cala"); // "hello world // hello cala" //有了模板字符串以後 console.log(`hello world string text line 2`); // "hello world // hello cala"
能夠用${}來表示模板佔位符,能夠將你已經定義好的變量傳進括弧中,例如:
var name="cala"; var age=22; console.log(`hello,I'am ${name},my age is ${age}`) //hello,I'am cala,my age is 22
includes(str, index):若是在字符串中檢測到指定文本,返回true,不然false。
let t = 'abcdefg' if(t.includes('cde')) { console.log(2) } //true
startsWith(str, index):若是在字符串起始部分檢測到指定文本,返回true,不然返回false。
let t = 'abcdefg' if(t.startsWith('ab')) { console.log(2) } //true
endsWith(str, index):若是在字符串的結束部分檢測到指定文本,返回true,不然返回false。
let t = 'abcdefg' if(t.endsWith('fg')) { console.log(2) } //true
若是你只是須要匹配字符串中是否包含某子字符串,那麼推薦使用新增的方法,若是須要找到匹配字符串的位置,使用indexOf()。
函數的默認參數
在ES5中,咱們給函數傳參數,而後在函數體內設置默認值,以下面這種方式。
function a(num, callback) { num = num || 6 callback = callback || function (data) {console.log('ES5: ', data)} callback(num * num) } a() //ES5: 36,不傳參輸出默認值 //你還能夠這樣使用callback a(10, function(data) { console.log(data * 10) // 1000, 傳參輸出新數值 })
在ES6中,咱們使用新的默認值寫法
function a(num = 6, callback = function (data) {console.log('ES6: ', data)}) { callback(num * num) } a() //ES6: 36, 不傳參輸出默認值 a(10, function(data) { console.log(data * 10) // 1000,傳參輸出新數值 })
(箭頭函數比較重要,如今簡單提一下,遲一點有空專門寫一篇箭頭函數的文章。)
const arr = [5, 10] const s = arr.reduce((sum, item) => sum + item) console.log(s) // 15
箭頭函數中this的使用跟普通函數也不同,在JavaScript的普通函數中,都會有一個本身的this值,主要分爲:
普通函數:
一、函數做爲全局函數被調用時,this指向全局對象
二、函數做爲對象中的方法被調用時,this指向該對象
三、函數做爲構造函數的時候,this指向構造函數new出來的新對象
四、還能夠經過call,apply,bind改變this的指向
箭頭函數:
一、箭頭函數沒有this,函數內部的this來自於父級最近的非箭頭函數,而且不能改變this的指向。
二、箭頭函數沒有super
三、箭頭函數沒有arguments
四、箭頭函數沒有new.target綁定。
五、不能使用new
六、沒有原型
七、不支持重複的命名參數。
箭頭函數的簡單理解
一、箭頭函數的左邊表示輸入的參數,右邊表示輸出的結果。
const s = a => a console.log(s(2)) // 2
二、在箭頭函數中,this屬於詞法做用域,直接由上下文肯定,對於普通函數中指向不定的this,箭頭函數中處理this無疑更加簡單,以下:
//ES5普通函數 function Man(){ this.age=22; return function(){ this.age+1; } } var cala=new Man(); console.log(cala())//undefined //ES6箭頭函數 function Man(){ this.age=22; return () => this.age+1; } var cala=new Man(); console.log(cala())//23
三、箭頭函數中沒有arguments(咱們能夠用rest參數替代),也沒有原型,也不能使用new 關鍵字,例如:
//沒有arguments var foo=(a,b)=>{return arguments[0]*arguments[1]} console.log(foo(3,5)) //arguments is not defined //沒有原型 var Obj = () => {}; console.log(Obj.prototype); // undefined //不能使用new 關鍵字 var Obj = () => {"hello world"}; var o = new Obj(); // TypeError: Obj is not a constructor
四、箭頭函數給數組排序
const arr = [10, 50, 30, 40, 20] const s = arr.sort((a, b) => a - b) console.log(s) // [10,20,30,40,50]
尾調用優化
尾調用是指在函數return的時候調用一個新的函數,因爲尾調用的實現須要存儲到內存中,在一個循環體中,若是存在函數的尾調用,你的內存可能爆滿或溢出。
ES6中,引擎會幫你作好尾調用的優化工做,你不須要本身優化,但須要知足下面3個要求:
一、函數不是閉包
二、尾調用是函數最後一條語句
三、尾調用結果做爲函數返回
尾調用實際用途——遞歸函數優化
在ES5時代,咱們不推薦使用遞歸,由於遞歸會影響性能。
可是有了尾調用優化以後,遞歸函數的性能有了提高。
//新型尾優化寫法 "use strict"; function a(n, p = 1) { if(n <= 1) { return 1 * p } let s = n * p return a(n - 1, s) } //求 1 x 2 x 3的階乘 let sum = a(3) console.log(sum) // 6
Object.assign()
Object.assign()方法用於將全部可枚舉屬性的值從一個或多個源對象複製到目標對象。它將返回目標對象。
Object.assign 方法只會拷貝源對象自身的而且可枚舉的屬性到目標對象。該方法使用源對象的[[Get]]和目標對象的[[Set]],因此它會調用相關 getter 和 setter。所以,它分配屬性,而不只僅是複製或定義新的屬性。若是合併源包含getter,這可能使其不適合將新屬性合併到原型中。爲了將屬性定義(包括其可枚舉性)複製到原型,應使用Object.getOwnPropertyDescriptor()和Object.defineProperty() 。
String類型和 Symbol 類型的屬性都會被拷貝。
合併對象
var o1 = { a: 1 }; var o2 = { b: 2 }; var o3 = { c: 3 }; var obj = Object.assign(o1, o2, o3); console.log(obj); // { a: 1, b: 2, c: 3 } console.log(o1); // { a: 1, b: 2, c: 3 }, 注意目標對象自身也會改變。
合併具備相同屬性的對象
var o1 = { a: 1, b: 1, c: 1 }; var o2 = { b: 2, c: 2 }; var o3 = { c: 3 }; var obj = Object.assign({}, o1, o2, o3); console.log(obj); // { a: 1, b: 2, c: 3 }
Map和Set都叫作集合,可是他們也有所不一樣。Set常被用來檢查對象中是否存在某個鍵名,Map集合常被用來獲取已存的信息。
Set是有序列表,含有相互獨立的非重複值。
Array和Set對比
都是一個存儲多值的容器,二者能夠互相轉換,可是在使用場景上有區別。以下:
Array的indexOf方法比Set的has方法效率低下
Set不含有重複值(能夠利用這個特性實現對一個數組的去重)
Set經過delete方法刪除某個值,而Array只能經過splice。二者的使用方便程度前者更優
Array的不少新方法map、filter、some、every等是Set沒有的(可是經過二者能夠互相轉換來使用)
Object和Map對比
Object是字符串-值,Map是值-值
Object鍵爲string類型,Map的鍵是任意類型
手動計算Object尺寸,Map.size能夠獲取尺寸
Map的排序是插入順序
Object有原型,因此映射中有一些缺省的鍵。能夠理解爲Map=Object.create(null)
Set操做集合
let set = new Set() // Set轉化爲數組 let arr = Array.from(set) let arr = [...set] // 實例屬性(繼承自Set) set.constructor === Set set.size // 操做方法 set.add(1) // 添加一個值 set.delete(1) //刪除一個值 set.has(1) //判斷是否有這個值(Array中的indexOf) set.clear() //清除全部值 // 獲取用於遍歷的成員方法(Set的遍歷順序就是插入順序) set.keys() // 返回鍵名的遍歷器 set.values() // 返回鍵值得遍歷器 set.entries() // 返回鍵值對的遍歷器 set.forEach() // 循環遍歷每一個值(和Array的方法一致) for (let key of set.keys()){} for (let val of set.values()){} for (let entry of set.entries()){} // 使用數組方法來處理set值 set = new Set(arr) set = new Set([...set].map((x) => x = x * 2)) set = new Set([...set].filter((x) => x > 2))
Map的方法集合
let map = new Map() // 實例屬性(繼承自Map) map.constructor === Map map.size // 操做方法 map.set(1,2) map.get(1) map.delete(1) map.has(1) map.clear() // 遍歷方法 map.keys() map.values() map.entries() map.forEach() // Map和數組的轉換 map = new Map([['key','val'],[2,1]]) // 要求雙成員數組 let arr = [...map] // 值得注意的是Map的鍵是跟內存綁定的 map.set([1], 's') map.get([1]) let arr = [1] let arr1 = [1] map.set(arr, 's') map.get(arr) map.set(arr1, 's') map.get(arr1)
想要深刻理解Set和Map,能夠查看《深刻理解:ES6中的Set和Map數據結構,Map與其它數據結構的互相轉換》
一、entries() 返回迭代器:返回鍵值對
//數組 const arr = ['a', 'b', 'c']; for(let v of arr.entries()) { console.log(v) } // [0, 'a'] [1, 'b'] [2, 'c'] //Set const arr = new Set(['a', 'b', 'c']); for(let v of arr.entries()) { console.log(v) } // ['a', 'a'] ['b', 'b'] ['c', 'c'] //Map const arr = new Map(); arr.set('a', 'a'); arr.set('b', 'b'); for(let v of arr.entries()) { console.log(v) } // ['a', 'a'] ['b', 'b']
二、values() 返回迭代器:返回鍵值對的value
//數組 const arr = ['a', 'b', 'c']; for(let v of arr.values()) { console.log(v) } //'a' 'b' 'c' //Set const arr = new Set(['a', 'b', 'c']); for(let v of arr.values()) { console.log(v) } // 'a' 'b' 'c' //Map const arr = new Map(); arr.set('a', 'a'); arr.set('b', 'b'); for(let v of arr.values()) { console.log(v) } // 'a' 'b'
三、keys() 返回迭代器:返回鍵值對的key
//數組 const arr = ['a', 'b', 'c']; for(let v of arr.keys()) { console.log(v) } // 0 1 2 //Set const arr = new Set(['a', 'b', 'c']); for(let v of arr.keys()) { console.log(v) } // 'a' 'b' 'c' //Map const arr = new Map(); arr.set('a', 'a'); arr.set('b', 'b'); for(let v of arr.keys()) { console.log(v) } // 'a' 'b'
雖然上面列舉了3種內建的迭代器方法,可是不一樣集合的類型還有本身默認的迭代器,在for of中,數組和Set的默認迭代器是values(),Map的默認迭代器是entries()。
for of循環解構
對象自己不支持迭代,可是咱們能夠本身添加一個生成器,返回一個key,value的迭代器,而後使用for of循環解構key和value。
const obj = { a: 1, b: 2, *[Symbol.iterator]() { for(let i in obj) { yield [i, obj[i]] } } } for(let [key, value] of obj) { console.log(key, value) } // 'a' 1, 'b' 2
字符串迭代器
const str = 'abc'; for(let v of str) { console.log(v) } // 'a' 'b' 'c'
一、find():傳入一個回調函數,找到數組中符合當前搜索規則的第一個元素,返回它,而且終止搜索。
const arr = [1, "2", 3, 3, "2"] console.log(arr.find(n => typeof n === "number")) // 1
二、findIndex():傳入一個回調函數,找到數組中符合當前搜索規則的第一個元素,返回它的下標,終止搜索。
const arr = [1, "2", 3, 3, "2"] console.log(arr.findIndex(n => typeof n === "number")) // 0
三、fill():用新元素替換掉數組內的元素,能夠指定替換下標範圍。
arr.fill(value, start, end)
四、copyWithin():選擇數組的某個下標,從該位置開始複製數組元素,默認從0開始複製。也能夠指定要複製的元素範圍。
arr.copyWithin(target, start, end) const arr = [1, 2, 3, 4, 5] console.log(arr.copyWithin(3)) // [1,2,3,1,2] 從下標爲3的元素開始,複製數組,因此4, 5被替換成1, 2 const arr1 = [1, 2, 3, 4, 5] console.log(arr1.copyWithin(3, 1)) // [1,2,3,2,3] 從下標爲3的元素開始,複製數組,指定複製的第一個元素下標爲1,因此4, 5被替換成2, 3 const arr2 = [1, 2, 3, 4, 5] console.log(arr2.copyWithin(3, 1, 2)) // [1,2,3,2,5] 從下標爲3的元素開始,複製數組,指定複製的第一個元素下標爲1,結束位置爲2,因此4被替換成2