1. ES6相關概念(★★)
1.1 什么是ES6
ES 的全稱是 ECMAScript , 它是由 ECMA 國際標準化組織,制定的一項腳本語言的標準化規范。ES6 是ES2015以后的泛稱
1.2 為什么使用 ES6 ?
每一次標準的誕生都意味著語言的完善,功能的加強。JavaScript語言本身也有一些令人不滿意的地方。
- 變量提升特性增加了程序運行時的不可預測性
- 語法過于松散,實現相同的功能,不同的人可能會寫出不同的代碼
2. ES6新增語法
2.1 let(★★★)
ES6中新增了用于聲明變量的關鍵字
let聲明的變量只在所處于的塊級有效
if (true) { let a = 10;}
console.log(a) // a is not defined
注意: 使用let關鍵字聲明的變量才具有塊級作用域,使用var聲明的變量不具備塊級作用域特性。
不存在變量提升
console.log(a); // a is not defined
let a = 20;
暫時性死區
利用let聲明的變量會綁定在這個塊級作用域,不會受外界的影響
var tmp = 123;if (true) { tmp = 'abc';let tmp; }
經典面試題
var arr = [];for (var i = 0; i < 2; i++) {arr[i] = function () {console.log(i); }}arr[0]();arr[1]();
經典面試題圖解: 此題的關鍵點在于變量i是全局的,函數執行時輸出的都是全局作用域下的i值。
let arr = [];for (let i = 0; i < 2; i++) {arr[i] = function () {console.log(i); }}arr[0]();arr[1]();
經典面試題圖解: 此題的關鍵點在于每次循環都會產生一個塊級作用域,每個塊級作用域中的變量都是不同的,函數執行時輸出的是自己上一級(循環產生的塊級作用域)作用域下的i值.
所以,下面的代碼終于實現了
<body><ul><li>1</li><li>2</li><li>3</li></ul><script>var lis=document.querySelectorAll('li')for(let i=0;i<lis.length;i++){lis[i].onclick=function(){lis[i].style.color='red'}}</script>
</body>
小結
- let關鍵字就是用來聲明變量的
- 使用let關鍵字聲明的變量具有塊級作用域
- 在一個大括號中 使用let關鍵字聲明的變量才具有塊級作用域 var關鍵字是不具備這個特點的
- 防止循環變量變成全局變量
- 使用let關鍵字聲明的變量沒有變量提升
- 使用let關鍵字聲明的變量具有暫時性死區特性
2.2 const(★★★)
聲明常量,常量就是值(內存地址)不能變化的量
變量的值可以變化
let m=10
m=20
m='aa'
常量一旦聲明并且賦值后,其值就不可以改變
常量主要是防止開發人員不小心將某些固定的值改變,比如 PI
具有塊級作用域
if (true) { const a = 10;}
console.log(a) // a is not defined
看看下面代碼的輸出結果是什么?
if (true) {const n = 10if (true) {const n = 20console.log(n)}console.log(n)}console.log(n)
干貨小灶
- 常量具有塊級作用域
- 內部作用域可以訪問外部作用域中的常量,所以如果不聲明 const n=20,則內部作用域會輸出10
- 外部作用域無法訪問內部作用域中的常量,所以外部作用域中根部就不知道內部作用域中聲明了常量n
聲明常量時必須賦值
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.
小結
- const聲明的變量是一個常量
- 既然是常量不能重新進行賦值,如果是基本數據類型,不能更改值,如果是復雜數據類型,不能更改地址值
- 聲明 const時候必須要給定值
2.3 let、const、var 的區別
- 使用 var 聲明的變量,其作用域為該語句所在的函數內,且存在變量提升現象
- 使用 let 聲明的變量,其作用域為該語句所在的代碼塊內,不存在變量提升
- 使用 const 聲明的是常量,在后面出現的代碼中不能再修改該常量的值
2.4 解構賦值(★★★)
解構賦值時新增的語法,能夠方便我們為變量賦值,但是沒有他,仍然不影響代碼編寫
ES6中允許從數組中提取值,按照對應位置,對變量賦值,對象也可以實現解構
數組解構
數組解構賦值時,聲明的變量使用[]包含起來
let [a, b, c] = [1, 2, 3];console.log(a)//1console.log(b)//2console.log(c)//3
//如果解構不成功,變量的值為undefined
如果變量數量多于數組中元素個數
var arr=['李白','杜甫','白居易']let [a,b,c,d]=arrconsole.log(a,b,c,d)
多出的變量值為 undefined
對象解構
對象解構賦值,聲明的變量要使用{}包含起來
let person = { name: 'zhangsan', age: 20 }; let { name, age } = person;console.log(name); // 'zhangsan' console.log(age); // 20let {name: myName, age: myAge} = person; // myName myAge 屬于別名console.log(myName); // 'zhangsan' console.log(myAge); // 20
復雜對象解構賦值
// 復雜的對象解構賦值let student = {name: '張三',age: 18,address: {province: '河北',city: '石家莊'}}let {name:myname,age:myage,address:{province,city}}=studentconsole.log(myname,myage,province,city)
小結
- 解構賦值就是把數據結構分解,然后給變量進行賦值
- 如果結構不成功,變量跟數值個數不匹配的時候,變量的值為undefined
- 數組解構用中括號包裹,多個變量用逗號隔開,對象解構用花括號包裹,多個變量用逗號隔開
- 利用解構賦值能夠讓我們方便的去取對象中的屬性跟方法
2.5 箭頭函數(★★★)
ES6中新增的定義函數的方式,目的是簡化函數的定義
() => {} //():代表是函數; =>:必須要的符號,指向哪一個代碼塊;{}:函數體
const fn = () => {}//代表把一個函數賦值給fn
函數體中只有一句代碼,且代碼的執行結果就是返回值,可以省略大括號
function sum(num1, num2) { return num1 + num2; }//es6寫法const sum = (num1, num2) => num1 + num2;
如果形參只有一個,可以省略小括號
function fn (v) {return v;}
//es6寫法const fn = v => v;
案例參考:
// 將function關鍵字去除,并且不需要函數名稱,然后在()后面加上=>就改成了箭頭函數var f = function () {console.log('f')}f()var f1 = () => {console.log('f1')}f1()// 函數如果有參數function f2(m, n) {console.log(m + n)}f2(3, 4)var f3 = (m, n) => console.log(m + n)f3(4, 5)// 如果函數只有一個參數function f4(m) {console.log(m * 10)}f4(5)// var f5 = (m) => console.log(m * 10)// 如果參數只有一個,可以省略掉小括號var f5 = m => console.log(m * 10)f5(10)// 函數中有返回值function f6(m, n) {return (m + n)}console.log(f6(10, 20))// 在箭頭函數中,如果想向外返回數據,如果是1行數據,可以省略{},就不需要returnvar f7 = (m, n) => m + nconsole.log(f7(30, 40))// 函數體重有多行代碼var arr = [1, 2, 3, 4, 5]function sum(number) {let sum = 0number.forEach(function (item) {sum += item})return sum}console.log(sum(arr))var sum_arrow = numbers => {let sum = 0numbers.forEach(item => {sum += item})return sum}console.log(sum_arrow(arr))
箭頭函數不綁定this關鍵字,箭頭函數中的this,指向的是函數定義位置的上下文this
const obj = { name: '張三'} function fn () { console.log(this);//this 指向 是obj對象return () => { console.log(this);//this 指向 的是箭頭函數定義的位置,那么這個箭頭函數定義在fn里面,而這個fn指向是的obj對象,所以這個this也指向是obj對象} } const resFn = fn.call(obj); resFn();
上面輸出了兩次,都是obj對象
如果將fn函數中的箭頭函數改成普通函數
小結
- 箭頭函數中不綁定this,箭頭函數中的this指向是它所定義的位置,可以簡單理解成,定義箭頭函數中的作用域的this指向誰,它就指向誰
- 箭頭函數的優點在于解決了this執行環境所造成的一些問題。比如:解決了匿名函數this指向的問題(匿名函數的執行環境具有全局性),包括setTimeout和setInterval中使用this所造成的問題
經典面試題
var age = 100;var obj = {age: 20,say: () => {alert(this.age)}
}obj.say();//箭頭函數this指向的是被聲明的作用域里面,而對象沒有作用域的,所以箭頭函數雖然在對象中被定義,但是this指向的是全局作用域
2.6 剩余參數(★★)
剩余參數語法允許我們將一個不定數量的參數表示為一個數組,不定參數定義方式,這種方式很方便的去聲明不知道參數情況下的一個函數
/*當函數的參數個數不確定的時候,可以使用剩余參數1)參數前面使用...指定此參數為剩余參數2)調用函數,傳遞實參時,參數以“,”分割,參數個數不受限制3)參數類型不受限制4)參數傳遞進函數后,以數組的形式存儲在形參中*/// 使用箭頭函數改寫// var fn = (...ary) => console.log(ary)function fn(...ary) {console.log(ary)} fn()fn(1)fn(1, 3)fn(33, 1, 44, 'a', false)fn('p', { name: 'yhb' }, [1, 2, 3])
也可以將剩余參數與普通參數一起使用
// 創建函數,接收一個空數組,和不確定個數的數字,然后將數字作為數組的元素添加到數組中function ary_pad(ary, ...elements) {elements.forEach(function (item) {ary.push(item)})}// 使用箭頭函數改寫//var ary_pad = (ary, ...elements) => {// elements.forEach(item => ary.push(item))//}let tmp = []ary_pad(tmp, 1, 2, 3,4,5,'a')console.log(tmp)
求和案例
const sum = (...args) => {let total = 0args.forEach(item => total += item)return total
}
console.log(sum(10,20,50))
剩余參數和解構配合使用
let students = ['wangwu', 'zhangsan', 'lisi'];
let [s1, ...s2] = students;
console.log(s1); // 'wangwu'
console.log(s2); // ['zhangsan', 'lisi']
3. ES6 的內置對象擴展
JS中有很多內置對象,如Arra、Math、Date、String 等,ES6 在原來的基礎上,新增了一些個方法
3.1 Array 的擴展方法(★★)
擴展運算符(展開語法)
擴展運算符可以將數組或者對象轉為用逗號分隔的參數序列,其作用于剩余參數相反
let ary = [1, 2, 3];...ary // 1, 2, 3console.log(...ary); // 1 2 3,相當于下面的代碼console.log(1,2,3);
問?為什么下面的寫法就不行
let res=...ary
案例:
console.log(Math.max(4, 3, 11, 2, 66, 7))
let ary = [4, 3, 11, 2, 66, 7]
console.log(Math.max(...ary))console.log('------------')
var sum = (x, y) => x + y
console.log(sum(4,5))
console.log(sum(...[5,6])) // 5,6
擴展運算符可以應用于合并數組
// 方法一 let ary1 = [1, 2, 3];let ary2 = [3, 4, 5];let ary3 = [...ary1, ...ary2];// 方法二 ary1.push(...ary2);
將類數組或可遍歷對象轉換為真正的數組
類型轉換數組度娘很多答案,在這里小編不過多介紹
案例:
// 類數組
let divs = document.querySelectorAll('div')
// 如果沒有這行代碼,第3行代碼就會報錯
divs=[...divs]
divs.push(2)
console.log(divs)
// 真正的數組
var ary = [1, 2, 3]
ary.push(5)
console.log(ary)
構造函數方法:Array.from()
Array.from
方法用于將兩類對象轉為真正的數組:類似數組的對象(array-like object)和可遍歷(iterable)的對象(包括 ES6 新增的數據結構 Set 和 Map)
什么是類數組對象
使用querySelectorAll、getElementsByClassName 等api獲取的元素的集合,就是類數組
/*lis 是一個類數組對象*/let lis = document.querySelectorAll('li')console.log(lis)console.log(lis.length)// 判斷 lis 是不是數組console.log(lis instanceof Array) //false// 查看arr 的對象原型console.log(lis.__proto__)// 真實的數組var arr = [1, 2, 3, 4]console.log(arr)console.log(arr.length)// 判斷 arr 是不是數組console.log(arr instanceof Array) //true// 查看arr 的對象原型console.log(arr.__proto__)
將類數組對象轉換位數組
// 將類數組對象轉換位真正的數組對象let ary_lis=Array.from(lis)console.log(ary_lis instanceof Array) //true
還有就是下面這種字面量對象,也是一種類數組對象
//定義一個集合
let arrayLike = {'0': 'a','1': 'b','2': 'c',length: 3
};
//轉成數組
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
方法還可以接受第二個參數,作用類似于數組的map方法,用來對每個元素進行處理,將處理后的值放入返回的數組
/*length 屬性決定了對象中有幾個屬性的值被轉換成數組中的元素,也就是說length屬性規定了數組中元素的個數,數組會按照這個值去分配空間*/let arrayLike = {"0": 1,"1": 2,"2": 3,"3": "a","4": false,"length":3}let ary = Array.from(arrayLike, item => {if(typeof item=='number'){return item}})console.log(ary)
注意:如果是對象,那么屬性需要寫對應的索引
實例方法:find()
用于找出第一個符合條件的數組成員,如果沒有找到返回undefined
let ary = [{id: 1,name: '張三'}, { id: 2,name: '李四'}]; let target = ary.find((item, index) => item.id == 2);//找數組里面符合條件的值,當數組中元素id等于2的查找出來,注意,只會匹配第一個
實例方法:findIndex()
用于找出第一個符合條件的數組成員的位置,如果沒有找到返回-1
let ary = [1, 5, 10, 15];
let index = ary.findIndex((value, index) => value > 9);
console.log(index); // 2
實例方法:includes()
判斷某個數組是否包含給定的值,返回布爾值。
[1, 2, 3].includes(2) // true
[1, 2, 3].includes(4) // false
3.2 String 的擴展方法
模板字符串(★★★)
ES6新增的創建字符串的方式,使用反引號定義
let name = `zhangsan`;
模板字符串中可以解析變量
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); // 哈哈哈哈 追不到我吧 我就是這么強大 哈哈哈哈
實例方法:startsWith() 和 endsWith()
- startsWith():表示參數字符串是否在原字符串的頭部,返回布爾值
- endsWith():表示參數字符串是否在原字符串的尾部,返回布爾值
let str = 'Hello world!';
str.startsWith('Hello') // true
str.endsWith('!') // true
實例方法:repeat()
repeat方法表示將原字符串重復n次,返回一個新字符串
console.log('x'.repeat(3));
3.3 Set 數據結構(★★)
https://es6.ruanyifeng.com/#docs/set-map
ES6 提供了新的數據結構 Set。它類似于數組,但是成員的值都是唯一的,沒有重復的值。
Set本身是一個構造函數,用來生成 Set 數據結構
const s = new Set();
先熟悉set的基本api
// var arr=new Array()// 創建set類型對象var s = new Set()s.add(1)s.add(2)s.add(2) // 不能加入重復成員console.log(s)// 返回成員個數console.log(s.size)// 刪除成員2s.delete(2)console.log(s.size)
Set函數可以接受一個數組作為參數,用來初始化。
const set = new Set([1, 2, 3, 4, 4]);//{1, 2, 3, 4}
也可以先創建空set,然后使用 add 方法添加元素
const s = new Set()
var ary=[2, 3, 5, 4, 5, 2, 2]
ary.forEach(item => s.add(item))
console.log(s)
注意:上面如果省略 ary 變量,寫成這樣,就會出錯
const s = new Set() // 不能省略分號[2, 3, 5, 4, 5, 2, 2].forEach(item => s.add(item))console.log(s)
實例方法
- 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))
數組合并去重排序案例
var array = [1, 2, 1, 1, '1', '一'];var array1 = [1, '二', 2, '一', 3, 4, '5', '1', '6'];// 創建set,將array 數組中的元素放進去,利用set結構不允許重復的特點,重復的元素就會自動被排除
var s1=new Set(array)
array1.forEach(item=>s1.add(item))
var newArr=Array.from(s1)
newArr.sort()
console.log(newArr)