ES5 中給我們新增了一些方法,可以很方便的操作數組或者字符串,這些方法主要包括:
- 數組方法
- 字符串方法
- 對象方法
1.1 數組方法
迭代(遍歷)方法:forEach()、map()、filter()、some()、every();
array.forEach(function(currentValue, index, arr))
-
- 讓數組中每個元素都執行一次 fn,相當於 for 循環
- 不會占用全局變量,結合箭頭函數賊舒服
- currentValue:當前項的值
- index:當前項的索引
- arr:當前數組對象本身
array.map(function(currentValue, index, arr))
-
- map() 方法會得到一個新數組, 新數組中包含每次函數調用返回的結果
- 它返回的是一個新數組
array.filter(function(currentValue, index, arr))
-
- filter() 方法創建一個新的數組,新數組中的元素是通過檢查指定數組中符合條件的所有元素,主要用於篩選數組
- 注意它直接返回一個新數組
- currentValue: 當前項的值
- index:當前項的索引
- arr:當前數組對象本身
array.some(function(currentValue, index, arr))
-
- some() 方法用於檢測數組中的元素是否滿足指定條件,即查找數組中是否有滿足條件的元素。
- 它的返回值是布爾值,如果查找到這個元素,就返回true , 如果查找不到就返回false。
- 如果找到第一個滿足條件的元素,則終止循環,不再繼續查找。
- currentValue: 當前項的值
- index:當前項的索引
- arr:當前數組對象本身
array.every(function(currentValue, index, arr))
-
- every() 方法檢測數組中的所有元素是否滿足指定條件。
- 所有的元素都返回true,結果才是true
1.2 字符串方法
trim() 方法會從一個字符串的兩端刪除空白字符。
str.trim()
- trim() 方法並不影響原字符串本身,它返回的是一個新的字符串。
1.3 對象方法
-
Object.keys() 用於獲取對象自身所有的屬性
Object.keys(obj)
-
效果類似 for…in
-
返回一個由屬性名組成的數組
-
-
Object.defineProperty() 定義對象中新屬性或修改原有的屬性。
Object.defineProperty(obj, prop, descriptor)
-
obj:必需。目標對象
-
prop:必需。需定義或修改的屬性的名字
-
descriptor:必需。目標屬性所擁有的特性,以對象形式 { } 書寫
-
value: 設置屬性的值 默認為undefined
-
writable: 值是否可以重寫。true | false 默認為false
-
enumerable: 目標屬性是否可以被枚舉。true | false 默認為 false
-
configurable: 目標屬性是否可以被刪除或是否可以再次修改特性 true | false 默認為false
-
-
ES6的新增語法
1. let
ES6中新增的用於聲明變量的關鍵字。
-
let聲明的變量只在所處於的塊級有效。
if (true) { let a = 10; } console.log(a) // a is not defined
注意:使用 var 聲明的變量不具備塊級作用域特性,使用 let 關鍵字聲明的變量具有塊級作用域,可以防止循環變量變成全局變量。
-
不存在變量提升
console.log(a); // a is not defined let a = 20;
-
暫時性死區
var num = 10; if(true) { console.log(num); // num is not defined let num = 20; } // 在if的{}中不會向上一級查找num,因為在這個塊級作用域中用了let關鍵字聲明了num,變量num就和{}這個塊級進行了整體綁定,所以在聲明之前使用它會報錯
-
經典面試題
// 題1 var arr = []; for(var i = 0; i < 2; i++) { arr[i] = function() { console.log(i); } } arr[0](); // 2 arr[1](); // 2 // 解析:變量 i 是全局的,函數執行時輸出的都是全局作用域下的 i 值。 // 題2 let arr = []; for(let i = 0; i < 2; i++) { arr[i] = function() { console.log(i); } } arr[0](); // 0 arr[1](); // 1 // 解析:每次循環都會產生一個塊級作用域,每個塊級作用域中的變量都是不同的,函數執行時輸出的是自己上一級(循環產生的塊級作用域)作用域下的值。
2. const
const 用於聲明常量,常量就是值(內存地址)不能變化的量。
-
具有塊級作用域
if (true) { const a = 10; } console.log(a) // a is not defined
-
聲明常量時必須賦值
const PI; // Missing initializer in const declaration
-
常量賦值后,值不能修改。
const PI = 3.14; PI = 100; // Assignment to constant variable.
const ary = [100, 200]; ary[0] = 'a'; ary[1] = 'b'; console.log(ary); // ['a', 'b']; ary = ['a', 'b']; // Assignment to constant variable.
let、const、var 的區別
-
使用 var 聲明的變量,其作用域為該語句所在的函數內,且存在變量提升現象。
-
使用 let 聲明的變量,其作用域為該語句所在的代碼塊內,不存在變量提升。
-
使用 const 聲明的是常量,在后面出現的代碼中不能再修改該常量的值。
var | let | const |
---|---|---|
函數級作用域 | 塊級作用域 | 塊級作用域 |
變量提升 | 不存在變量提升 | 不存在變量提升 |
值可更改 | 值可更改 | 值不可更改 |
3. 解構賦值
ES6中允許從數組中提取值,按照對應位置,對變量賦值。對象也可以實現解構。
按照一定模式,從數組中或對象中提取值,將提取出來的值賦值給另外的變量。
3.1 數組解構
let [a, b, c] = ['pink', 'yellow', 'blue']; //正常解構賦值 console.log(a, b, c);
如果解構不成功,變量的值為undefined。
let [foo] = []; let [bar, foo] = [1]; let [, c] = ['red', 'yellow']; //選擇性解構賦值
3.2 對象解構
let {name, age} = {name: 'andy', age: 18}; // 正常解構賦值,使用變量的名字匹配對象的屬性,匹配成功,將對象屬性的值賦值給變量。 console.log(name, age); // andy 18 let {name: myName, age: myAge} = {name: 'andy', age: 18}; //myName myAge 屬於別名 let {name = 'lily', age } = {name: 'andy', age: 18}; //給name設置了默認值'lily'
4. 箭頭函數
ES6中新增的定義函數的方式。
// 語法:() => {} const fn = () => {} fn();
-
函數體中只有一句代碼,且代碼的執行結果就是返回值,可以省略大括號
function sum(a, b) { return a + b; } const sum = (a, b) => a + b; sum(10, 20);
-
如果形參只有一個,可以省略小括號
function fn (v) { return v; } const fn = v => v;
-
箭頭函數不綁定 this 關鍵字,它的 this 值是繼承它的父作用域的,所以箭頭函數不能作為構造函數。
箭頭函數的 this 值是詞法作用域,也就是說它在定義的時候就被指定了的,而且也不會隨着它調用方法的改變而改變。
所以箭頭函數中的this,指向的是函數定義位置的上下文this。
const obj = {name: 'andy'}; function fn() { console.log(this); // obj return () => { console.log(this); // obj } } const resFn = fn.call(obj); resFn();
注:在對象的方法中也不要用箭頭函數(如果用箭頭函數,就沒法用this),注冊事件的函數也不要用箭頭函數。
- 面試題解析:
var obj = { age: 20, say: () => { console.log(this.age) } } obj.say(); // undefined // 解析:箭頭函數中沒有自己的this,它的this指向箭頭函數定義區域的this。此例中的箭頭函數定義在了obj這個對象中,它是一個對象,不能產生作用域,所以這個箭頭函數被定義在了全局作用域下,所以在調用這個方法的時候this指向window
5. 剩余參數
剩余參數語法允許我們將一個不定數量的參數表示為一個數組。
const sum = (...args) => { let total = 0; args.forEach(item => total += item); return total; } console.log(sum(10, 20)); // 30 console.log(sum(1, 2, 3, 4)); // 10
剩余參數和解構配合使用
let students = ['andy', 'jack', 'lily']; let [s1, ...s2] = students; console.log(s1, s2); // 'andy', ['jack', 'lily']
6. ES6的內置對象擴展
6.1 擴展運算符(展開語法)
-
擴展運算符可以將數組或者對象轉為用逗號分隔的參數序列。
let ary = [1, 2, 3]; ...ary // 1, 2, 3 console.log(...ary); // 1 2 3
-
擴展運算符可以應用於合並數組。
// 方法一 let ary1 = [1, 2, 3]; let ary2 = [3, 4, 5]; let ary3 = [...ary1, ...ary2]; // 方法二 ary1.push(...ary2);
-
將類數組或可遍歷對象轉換為真正的數組
let oDivs = document.getElementsByTagName('div');
oDivs = [...oDivs];
6.2 構造函數方法:Array.from()
-
將類數組或可遍歷對象轉換為真正的數組
let arrayLike = { '0': 'a', '1': 'b', '2': 'c', length: 3 }; let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
-
方法還可以接受第二個參數,作用類似於數組的map方法,用來對每個元素進行處理,將處理后的值放入返回的數組。
let arrayLike = { "0": 1, "1": 2, "length": 2 } let newAry = Array.from(aryLike, item => item *2)
6.3 實例方法:find()
用於找出第一個符合條件的數組成員,如果沒有找到返回undefined
let ary = [ { id: 1, name: 'andy' }, { id: 2, name: 'lily' } ] let target = ary.find(item => item.id === 2);
6.4 實例方法:findIndex()
用於找出第一個符合條件的數組成員的位置,如果沒有找到返回-1
let ary = [1, 5, 10, 15]; let index = ary.findIndex((value, index) => value > 9); console.log(index); // 2
6.5 實例方法:includes()
判斷字符串是否包含另一個字符串,傳統的 js 方法: indexOf 返回值是否等於 -1 ,如果等於 -1說明不包含
es6 中提供了方法 includes() 返回布爾值,表示是否包含給定的值。
[1, 2, 3].includes(2) // true 1, 2, 3].includes(4) // false let s = 'hello world' s.includes('hello', 6) // false 第一個參數是需要匹配的數據,第二個參數是從第幾個開始
7. 模板字符串
ES6新增的創建字符串的方式,使用反引號定義 ``。
-
模板字符串中可以解析變量。
let name = '張三'; let sayHello = `hello,my name is ${name}`; // hello, my name is zhangsan
-
模板字符串中可以保留空格和換行
let result = { name: 'zhangsan', age: 20, sex: '男' } let html = ` <div> <span>${result.name}</span> <span>${result.age}</span> <span>${result.sex}</span> </div> `;
-
在模板字符串中可以調用函數。
const sayHello = function () { return '哈哈哈哈 追不到我吧 我就是這么強大'; }; let greet = `${sayHello()} 哈哈哈哈`; console.log(greet); // 哈哈哈哈 追不到我吧 我就是這么強大 哈哈哈哈
8. String 的擴展方法
8.1 實例方法:startsWith()和endsWith()
- startsWith():表示參數字符串是否在原字符串的頭部,返回布爾值
- endsWith():表示參數字符串是否在原字符串的尾部,返回布爾值
let str = 'Hello world!'; str.startsWith('Hello') // true str.endsWith('!') // true
8.2 實例方法:repeat()
repeat() 方法表示將原字符串重復n次,返回一個新字符串。
'x'.repeat(3) // "xxx" 'hello'.repeat(2) // "hellohello"
9.Set 數據結構
ES6 提供了新的數據結構 Set。它類似於數組,但是成員的值都是唯一的,沒有重復的值。
-
Set本身是一個構造函數,用來生成 Set 數據結構。
const s = new Set();
-
Set函數可以接受一個數組作為參數,用來初始化。
const set = new Set([1, 2, 3, 4, 4]);
例如:利用set去重,得到一個新數組
let set = new Set([1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1]) console.log(set) console.log([...set])
注:set 去重之后得到的 set 類型的數據,所以還得把這個 set 數據類型轉換成數組類型,這個時候又可以用到展開符這個好東西了
實例方法
-
-
add(value):添加某個值,返回 Set 結構本身
-
delete(value):刪除某個值,返回一個布爾值,表示刪除是否成功
-
has(value):返回一個布爾值,表示該值是否為 Set 的成員
-
clear():清除所有成員,沒有返回值
-
const s = new Set(); s.add(1).add(2).add(3); // 向 set 結構中添加值 s.delete(2) // 刪除 set 結構中的2值 s.has(1) // 表示 set 結構中是否有1這個值 返回布爾值 s.clear() // 清除 set 結構中的所有值
遍歷
Set 結構的實例與數組一樣,也擁有 forEach 方法,用於對每個成員執行某種操作,沒有返回值。
s.forEach(value => console.log(value))
10. Promise
promise 用於更優雅的處理異步請求,其實就是對於回調函數的另一種寫法,可以幫助我們避免回調地獄。
回調地獄:比如第二個請求依賴第一個請求的結果進行請求,第三個請求依賴第二個,這樣一層一層嵌套,。。。 為了解決回調順序的不確定性以及地獄,就有了 promise 機制。
1、Promise 一般來講是一個對象, .then方法其實就是 promise 對象的一個方法
2、Promise 構造函數接受一個函數作為參數,在這個函數中,可以進行異步的操作的代碼
3、在這個函數中執行完成異步操作之前,並不是要調用用戶提供的回調函數(不關心回調到底是什么),而是在這個函數中,異步完成之后,只需要去修改當前的promise 的狀態
4、Promise的狀態:
-
- pendding :掛起,當前 promise 執行的任務,正在執行中
- fullfilled: 完成,當前 promise 對象執行的任務,已經完成,並且是成功狀態
- rejected: 完成,當前 promise 對象執行的任務,已經完成,並且是失敗的狀態
5、Promise 對象的 .then 方法中接收到的成功的回調函數,會在當前的 promise 對象處於成功(fullfilled)狀態的時候自動執行;
Promise對象的 .then 方法中接收到的失敗的回調函數,會在當前的 promise 對象處於失敗(rejected)狀態的時候自動執行
6、resolve 是一個函數,調用這個函數,就可以把當前 promise 對象標記成功
reject 也是一個函數,調用這個函數,可以把當前 promise 對象標記失敗
var p = new Promise(function(resolve, reject) { resolve() // 成功時調用 reject() // 失敗時調用 }); p.then(function(res) { // 從resolve得到正常結果 }, function(res) { // 從reject得到錯誤信息 })
resolve 和 reject在調用的時候,可以傳遞數據,這個數據會最終被傳遞到成功或者失敗的回調函數中。
// 封裝一個支持Promise APi的延時函數 function timeOut(time){ return new Promise(function(resolve, reject){ setTimeout(function(){ // 這個回調函數中,不需要涉及任何具體的業務操作 // resolve(123); // 可傳遞數據,這個數據會最終被傳遞到成功回調函數中 reject(); }, time); }); }
7、在 then 方法中,我們可以直接 return 數據而不是 Promise 對象,在后面的 then 中就可以接收到數據了。
then 參數中的函數返回值:
1. 返回 Promise 實例對象
-
-
返回的該實例對象會調用下一個 then
-
2. 返回普通值
-
-
返回的普通值會直接傳遞給下一個 then,通過 then 參數中函數的參數接受該值
-
timeOut(1000) .then(num => { console.log(num + '這是1s后打印的內容') return timeOut(1000) }) .then(num => { console.log(num + '這是2s后打印的內容') return timeOut(1000) }) .then(num => { console.log(num + '這是3s后打印的內容') return timeOut(1000) })
8、語法
語法一:
promise.then( function() { console.log('成功的回調') }, function() { console.log('失敗的回調') } )
語法二:
Promise .then(function() { console.log('成功的回調') }) .catch(function() { consloe.log('失敗的回調') })
語法二和語法一的效果一樣。不過它還有另外一個作用:在執行 resolve 的回調(也就是上面then中的參數)時,如果拋出異常了(代碼出錯了),那么並不會報錯卡死,而是會進到這個catch方法中。
8、promise 的其他用法
- Promise.all: 只有當傳入的所有 promise 的結果為 reslove,才會執行這個 all 的回調函數(即觸發 then 方法執行里面的代碼),否則只要有一個是 reject,就會執行 catch 方法。
Promise .all([t1, t2, t3, t4, t5]) .then(function(){ console.log("所有異步操作完成了"); } .catch(function() { console.log("嗷嗷~~~有操作出錯了"); }) )
- Promise.race:由第一個 promise 返回的狀態決定,如果第一個 promise 返回的結果為 reslove 就會執行這個race的回調
Promise.race([t1, t2, t3, t4, t5]).then(function(){ console.log("有一個異步率先完成了"); })
常用API:
-
- .then() 得到異步任務的成功信息
- .catch() 獲取異常信息
- .finally() 成功與否都會執行(還不是正式標准)
- Promise.all() 並發處理多個異步任務,所有任務都執行完成才能得到結果
- Promise.race() 只要有一個任務完成就能得到結果
Promise.all([p1, p2, p3]).then(res => { console.log(res); //三個結果的數組 }) Promise.race([p1, p2, p3]).then(res => { console.log(res); })
示例:基於Promise處理Ajax請求:
function queryData(url) { return new Promise(function(resolve, reject){ var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if(xhr.readystate !== 4) return; if(xhr.readystate === 4 && xhr.status === 200){ resolve(xhr.responseText); }else{ reject('出錯了'); } } xhr.open('get', url); xhr.send(null); }) } queryData('http://localhost:3000/data') .then(function(res){ console.log(res); }, function(err){ console.log(err) }) //發送多次請求 queryData('url1') .then(function(data){ return queryData('url2'); }) .then(function(data){ return queryData('url3'); }) .then(function(data){ console.log(data); })
這里順便叨叨一下 Promise 的語法糖:async/await 的基本用法
-
- async 關鍵字用於函數上(async 函數的返回值是 Promise 實例對象)
- await 關鍵字用於 async 函數中(await 可以得到異步的結果)
async function queryData(id){ const ret = await axios.get('/data'); return ret; } queryData.then(ret => { console.log(ret); })
使用async后,代碼變得更簡潔,代碼的邏輯和數據流都變得更加清晰。由於 async 的原理還是以 Promise 為基礎,所以部分時候,async 是要和 Promise 配合使用的,它並不能完全脫離 Promise 獨立運行;await 必須在 async 函數里使用,不能單獨使用。
多個異步請求的場景
async function queryData(id){ const info = await axios.get('/async1'); const ret = await axios.get(`/async2?info=`+info.data); return ret; } queryData.then(ret => { console.log(ret); })
11. 模塊化
模塊化就是把單獨的一個功能封裝到一個模塊(文件)中,模塊之間相互隔離,但是可以通過特定的接口公開內部成員,也可以依賴別的模塊。
模塊化開發的好處:方便代碼的重用,從而提升開發效率,並且方便后期的維護。
ES6默認導出與默認導入
-
- 默認到處語法 export default 默認導出的成員
- 默認導入語法 import 接收名稱 from ‘模塊標識符’
//當前文件模塊為 m1.js//定義私有成員a和c let a = 10; let c = 20; let d = 30; function show(){} //將本模塊中的私有成員暴露出去,供其他模塊使用 export default { a, c, show } //導入模塊成員 import m1 from './m1.js' console.log(m1) //打印輸出的結果為{a: 10, b: 20, show: [function: show]} //外界訪問不到d,因為它沒有被暴露出去
注意:每個模塊中,只允許使用唯一的一次 export default,否則會報錯
按需導出與按需導入
-
- 默認導出語法 export let s1 = 10
- 默認導入語法 import { s1 } from '模塊標識符'
//當前文件模塊為 m1.js export let s1 = 'aaa' export let s2 = 'bbb' export function say = function(){} //導入模塊成員 import {s1, s2 as ss2, say} from './m1.js' console.log(s1); // aaa console.log(ss2); // bbb console.log(say); // [function: say]
注意:每個模塊中,可以使用多次按需導出
直接導入並執行模塊代碼
只執行模塊中的代碼,並不需要得到模塊中向外暴露的成員,此時可以直接導入並執行模塊代碼。
//當前文件模塊為 m2.js //在當前模塊中執行一個 for 循環操作 for(let i = 0; i < 3; i++) { console.log(i); } //直接導入並執行模塊代碼 import './m2.js'