一、變數的宣告 var、let、const
ES6 之前只有var
的變數宣告,ES6之後新增了 let
、和 const
相對於var
,let
對我個人而言 是比較好用的。
let 不用擔心全域性名稱空間的汙染,let的特性如下:
1、let 命名的變數不會被提升
console.log(a)
let a = 10
//a is not defined複製程式碼
2、let 不能重複命名變數
let a = 10
let a = '小明'
console.log(a)
//Identifier 'a' has already been declared
複製程式碼
3、let 命名會形成塊級作用域
{
let a= 10
var b = 20
}
console.log(b) //20
console.log(a) //a is not defined複製程式碼
上面程式碼‘{ }
’就是一個程式碼段 let
只在這裡面起作用 但是var
變數是全域性的 所以正常輸出。
4、let會造成暫時性死區
在一個程式碼塊中只要有let宣告的變數,在這句語句之前這個變數是不允許被使用的,在語法上稱之為 暫時性死區。
ES6規定程式碼塊中存在let、const命令,程式碼塊就對這些命令宣告的變數產生了封閉作用,不受外界影響。
var a= 123;
if (true) {
a= 'abc'; // ReferenceError
let a;
}複製程式碼
for迴圈中的 let 與 var
var arr = []
for(let i=0; i<5; i++){
arr[i] = function(){
console.log(i)
}
}
console.log(i) //i is not defined
arr[3]() //3複製程式碼
在這裡 let
宣告的 i
只在for
迴圈的程式碼段中有效出了for
迴圈就會報錯,而且每次迴圈迴圈體中的 i
都是新的變數,所以arr[3]()
就會輸出3
var arr = []
for(var i=0; i<5; i++){
arr[i] = function(){
console.log(i)
}
}
arr[3]() //5複製程式碼
這裡var宣告的 i 在全域性有效 當呼叫arr[3]()的時候會去訪問全域性唯一的 i 而此時的 i 經過for迴圈已經變成 5 了 所以這裡不管是 arr[3]() 還是 arr[1]() 都是輸出 5
總結 let
宣告變數只在當前的程式碼塊中有效,每一個 { }
就是一個程式碼塊 let
會在宣告的程式碼塊中形成一個 塊級的作用域
const 用來宣告常量 (一次宣告,終生使用--不可被更改)
const a = 10
a = '小明'
console.log(a) // Assignment to constant variable.複製程式碼
const
宣告的物件 不能被重新 賦值 但物件的成員變數 是可以被更改的
const obj = {
name = 'xiaoming'
}
obj ={} //Assignment to constant variable.
複製程式碼
const obj = {
name = 'xiaoming'
}
obj.name = '小明'
console.log(obj.name) // 小明複製程式碼
解釋:obj
中存放的只是 物件在堆區的地址 這個地址是不能變化的,重新給obj
直接賦值的話相當於改變這個地址 設個是不允許的,但是物件內部的成員變數並不是const
定義的所以可以進行更改
說明const也可以形成一個塊級作用域
二、字串的擴充套件
1、模板字串 格式 `` (稱之為 反引號 -- 鍵盤Tab鍵上方的鍵)
1、保留了定義普通字串的效果
var str = `I am xiaoming`複製程式碼
2、定義多行字串 會把字元格式一起列印出來 比如換行
var str = `I
am
xiaoming`
console.log(str)
/*I
am
xiaoming*/複製程式碼
3、帶引數的字串 引數需要用 ${/*引數*/} 的形式
var name = '小明'
var str = `I am ${name}`
console.log(str) //I am 小明複製程式碼
4、有簡單的表示式
var age = 17
var str = `I will ${++age} years old` //I will 18 years old 複製程式碼
5、拼接html標籤
document.body.innerHTML = `<em>this is em </em>`複製程式碼
2、padStart 和 padEnd 字串長度不足時 在頭部或尾部以某種格式字元補全 方法有兩個引數 第一個為 字串的長度 第二個是 補充的字元
var str = 'abc'
str = str.padStart(8,'*')
console.log(str) //*****abc複製程式碼
var str = 'abc'
str = str.padEnd(8,'*')
console.log(str) //abc*****
複製程式碼
如果原字串長度 等於或大於 設定的字串長度那麼將返回 原字串
var str = 'abc'
str = str.padStart(2,'*')
console.log(str) //abc
複製程式碼
3、includes 判斷字串是否包含某字元或字串 返回布林值 true 或 false
var str = `todayisgoodday`
console.log(str.includes('day')) //true
console.log(str.includes('o')) //true複製程式碼
4、startsWith 和 endsWith 判斷字串是否是以 某字串 開頭 或以某字串結尾 返回true 或 false
var str = `todayisgoodday`
console.log(str.startsWith('day')) //false
console.log(str.startsWith('to')) //true
console.log(str.endsWith('day')) //true
console.log(str.endWith('to')) //false複製程式碼
5、repeat 返回一個新字串 該字串是原字串重複n次的結果
var str = 'string/'
str = str.repeat(3) // string/string/string/複製程式碼
如果是小數將自動取整 (只取整數部分,並不是四捨五入)
var str = 'string/'
str = str.repeat(2.3) //string/string/複製程式碼
var str = 'string/'
str = str.repeat(2.9) //string/string/複製程式碼
以上程式碼 不論是2.3還是2.9到最後都只重複兩次
6、tirm ,tirmLeft 和 tirmRight 用於去除字串空格
tirm
去除字串左右的空格
var str = ' string '
str = str.tirm() // str = 'string'
複製程式碼
tirmLeft
去除字串左邊的空格
var str = ' string '
str = str.tirmLeft() // str = 'string '複製程式碼
tirmRight
去除字串右邊的空格
var str = ' string '
str = str.tirmRight() // str = ' string'複製程式碼
三、陣列的擴充套件
1、擴充套件運算子即 ...
三個點
擴充套件運算子作用 將陣列展開,可以理解為將陣列的中括號去掉 暴露出裡面的屬性值
var arr = [1,2,3]
var arr2 = [arr] //arr2 = [[1,2,3]]複製程式碼
上面是沒有使用擴充套件運算子的,和容易理解arr2
的值,直接將arr
作為arr2
的第一個元素了
var arr = [1,2,3]
var arr2 = [...arr] // arr2 = [1,2,3]複製程式碼
這個是使用了擴充套件運算子,可以這樣理解將arr的 [] 的殼給 開啟了裡面的東西就落入了arr2
裡面了。
擴充套件運算子還可以將字串轉換成陣列
var str = 'hello'
var strs = [...str] //strs = ['h','e','l','l','o']複製程式碼
2、Array.from
返回一個陣列,將一個偽陣列(類陣列)的物件轉化成為一個真正的陣列
//NoteList 物件
var lis = document.getElementsByTagName('li')
Array.from(lis).forEach(item => {
......
})複製程式碼
//argumrnts 物件
function(){
var arr = Array.from(arguments)
......
}複製程式碼
只要是類似陣列或者可以遍歷的物件都可以被 Array.from
轉換成真正的陣列
上面程式碼中 lis 物件的結構是
模仿上面的 notelist物件
var test = {
0 : 'a',
1 : 'b',
2 : 'c',
length : 3
}
var arr = Array.from(test)
console.log(arr) // [a,b,c]
複製程式碼
3、Array.of
該方法 用於將一組引數 轉化成為陣列
var arr = Array.of('1','2',3) // ['1','2','3']
var arr = Array.of(2) // [2]
var arr = Array.of() // []複製程式碼
在陣列中 陣列的構造器方法也可以將一組陣列轉化成陣列
var arr = new Array() //[]
var arr = new Array(3) //['','','']
var arr = new Array(2,3,4) //[2,3,4]複製程式碼
可以看出 利用構造器建立陣列時在傳入引數個數不同的情況下 產生的 陣列會有些不同,當只傳入一個數字時,它會把這個數字當成陣列的長度來生成陣列,但是Array.of
正好彌補了引數個數不同而帶來的麻煩,它會將傳入的引數毫無差別的當做陣列中的元素。
4、find
和 findIndex
用來查詢陣列
find 方法 接收一個回撥函式 遍歷陣列中的每一項去檢測是否符合回撥函式中的規則直到找到第一個符合規則的成員,並將該成員返回,如果一直沒有找到,將返回undefined
var ages = [3, 10, 18, 20, 30];
function checkAdult(age) {
return age > 18;
}
console.log(ages.find(checkAdult)); // 20
複製程式碼
findIndex
方法與 find
方法一樣 查詢陣列 但是findIndex
返回的是陣列中第一個符合條件成員的索引
var ages = [3, 10, 18, 20, 30];
function checkAdult(age) {
return age >= 18;
}
ages.find(checkAdult); // 3
複製程式碼
5、includes
用來判斷陣列是否包含某個給定的值,返回true
或false
。 與字串的includes
極為相似
var arr= [1,2,3]
var flag = arr.includes(1) //flag 為true
複製程式碼
6、fill
方法,用給定的值來填充陣列,如果該陣列中有元素,原來的值將會被覆蓋掉
var arr = new Array(5)
arr.fill("a") // arr = ['a','a','a','a','a']
複製程式碼
var arr = [1,2,,,]
arr.fill('a') // arr = ['a','a','a','a','a']複製程式碼
7、indexOf
返回這頂引數在陣列中的索引,如果陣列中沒有改資料則返回 -1
var arr = [1,2,3]
var index = arr.infexOf(1) // index = 0複製程式碼
var arr = [1,2,3]
var index = arr.indexOf(4) //index = -1複製程式碼
四、函式的擴充套件
1、箭頭函式 格式 ()=>{}
ES6 新增箭頭函式命名函式,極大簡化了函式的書寫,在ES6 之前每當我們要建立一個匿名函式的時候要用 function
去定義,ES6之後我們就可以簡單的多了
//ES5
var fn = function(){
.....
}
//ES6
var fn = () => {}複製程式碼
箭頭函式也是可以新增形參的 ,只有一個引數的時候我們可以把 ()
省略掉或是 方法體只有一句話的時候可以把 {}
省略掉,如果這句話是return
返回語句 必須將return省去
//一般情況
var fn = (a,b,c) => {}
//一個引數
var fn = item => {}
//一個引數一句函式體
var fn = item => ++item複製程式碼
例子:將箭頭函式 用在forEach中
[1,2,3,4].forEach(item=>item*2)複製程式碼
箭頭函式中使用this
首先說明箭頭函式是沒有this
的,因此它自身不能進行new
例項化,同時也不能使用call, apply, bind
等方法來改變this
的指向,所以在箭頭函式中使用this
始終指向的是定義它的那個物件,除此之外箭頭函式固定了this
的指向,在箭頭函式中this
的指向不可被更改
var obj = {
move : function(){
document.addEventListener('click',
event => this.do(event.type), false)
}
do : function(){
alert('hello')
}
}
複製程式碼
這裡document
的監聽事件使用來箭頭函式所以其中的this
指向當前的obj
物件,如果不用箭頭函式this
將指向事件源document
注意:箭頭函式中不能使用arguments
2、形參的定義初值
ES5 要給一個引數賦初值 要在函式體中進行判斷undefined如下
function(a , b){
b = b || 'hello'
}
//或是一下寫法
function(a ,b){
if(typeof b = 'undefined') b = 'hello'
}複製程式碼
ES6新增了可以給函式的引數新增初始值,這樣就優化了程式碼,程式碼的可讀性也強
function(a,b='hello'){
.....
}複製程式碼
這樣很清楚的知道引數 b 是可以為空的
3、函式的length屬性
函式的length屬性表示函式預期要傳入的引數的個數(不包含有初值的引數)
(function(a=1){}).length // 0
(function(a,b){}).length // 2
(function(a,b,c=2){}).length // 2複製程式碼
如果有未賦初值的引數在有初值引數後面那麼length將也不把這些引數計入其中
(function(a,b=2,c){}).length // 1複製程式碼
總結:函式的 length 表示函式引數中第一個有初值的引數之前的引數個數
4、立即呼叫函式表示式(IIFE)
IIFE 避免裡全域性變數和函式的汙染,正常的寫法可能會造成變數和函式的汙染造成不必要的的麻煩
var a = 100
var a = 'a'
function f(){
console.log(a)
}
var f = function(){
var a = 0
console.log(a)
}
f()複製程式碼
在這種情況下a變數和f函式都被汙染了,如果你寫了f函式又引入了一個別人的js正好裡面也有個f函式,而兩個函式又都定義在全域性,那麼就像上面的這種情況後面的函式或變數將前面的函式或變數給覆蓋了,就會造成程式的錯誤,這種時候就要用IIFE去解決這種問題
格式一利用():(function(){})()
(function(){
var a = 10
function f(){
console.log(a)
}
f() // 10
})()
console.log(a) //a is not defined複製程式碼
格式二利用 單目運算子 : +function(){}()
-function(){}()
!function(){}()
!function(){
var a = 10
function f(){
console.log(a)
}
f() // 10
}()
console.log(a) //a is not defined複製程式碼
格式三:(function(){}())
(function(){
var a = 10
function f(){
console.log(a)
}
f() // 10
}())
console.log(a) //a is not defined複製程式碼
個人比較偏向於第三種,它讓程式碼看起來更緊湊,更像一個整體
IIFE 也可以帶引數,
畢竟說到底它也還是個函式,要有一般函式的性質
(function(a){
function(){
console.log(a)
}
}(10))複製程式碼
五、物件的擴充套件
1、物件屬性的簡化
ES6允許直接在物件中寫入變數
var name = 'xiaoming'
var obj = {
name
}複製程式碼
當物件中只有變數的時候,說明物件的屬性名是變數,屬性的值是變數的值
2、方法的簡化
ES6不僅簡化了屬性的寫法,還簡化了物件中方法的寫法
//ES5
var obj = {
add: function(){
console.log('add')
}
}
//ES6
var obj = {
add(){
console.log('add')
}
}複製程式碼
3、屬性名錶達式
使用字面量(大括號)定義物件,有兩種方法定義物件的屬性
//方法一 (識別符號)
obj.name = 'xiaoming'
//方法二 (表示式)
obj['n'+'ame'] = 'xiaoming'複製程式碼
在ES6之後允許使用 表示式給物件的屬性賦值,但是ES5只能使用識別符號的方法給物件的屬性賦值
var str = age
var obj= {
name : 'xiaoming',
[str] : 16,
['he'+'ight'] : 180cm'
}
console.log(obj.name) // xiaoming
console.log(obj.str) // 16
console.log(obj.age) // 16
console.log(obj.height) // 180cm複製程式碼
ES6還允許使用運算子的形式個物件的方法命名
var obj = {
['a'+'d'+'d'] : function(){
console.log('this is add function')
}
}
obj.add() // this is add function複製程式碼
4、Object.assign
Object.assign 用於物件的合併,將源物件的所有的可列舉屬性,複製到目標物件。
該方法接收兩個及以上引數,第一個引數是目標物件,assign這個方法把其它的引數中的屬性全部加在第一個引數身上,
如果其他引數中有重複的屬性,將會用這個屬性最後一次出現在的物件中的值。
assign這個方法的返回值就是第一個引數的引用,也就是返回了第一個引數。也就是說是對第一個引數的修改。
var obj1 = {}
var obj2 = {
name : 'xiaoming'
}
var obj3 = {
age = '18'
}
Object.assign(obj1,obj2,obj3) //obj1 = {name: 'xiaoming',age:'18'}複製程式碼
注意assign還有以下特點
1、 assign不能拷貝繼承過來的屬性
2、assign也不拷貝不可列舉的屬性
var obj1 = {}
var obj2 = {
name : 'xiaoming',
age : 18
}
obj2.defineProperty(obj2,'sex',{
value: 'man',
configurable : true,
writable : true,
enumerable : false
})
Object.assign(obj1,obj2) // obj1 = {name:'xiaoming',age:18}複製程式碼
此時obj1中並不會出現sex屬性,defineProperty 方法使用來精細化設定屬性的,這裡試將sex屬性設定為了不可列舉的,所以assign沒有拷貝到sex屬性
淺拷貝
assign實行的是淺拷貝也就是說如果物件的屬性是引用資料型別的時候,拷貝過去的只是物件或陣列的指向(可以理解為地址)
var obj1 = {}
var obj2 = {
name : 'xiaoqiang',
friends : ['one','two']
}
Object.assign(obj1,obj2)
obj2.friends.push('three')
console.log(obj1) //{name: 'xiaoqiang',friends:['one','two','three']}
console.log(obj2) //{name: 'xiaoqiang',friends:['one','two','three']}複製程式碼
當你改變其中一個物件中的值的時候兩個物件中的會都改變了,因為兩者屬性儲存的是同一個記憶體地址,也就是說指向同一塊記憶體空間,一個改變了這個空間的值另一個當然也會改變
5、屬性的遍歷
ES6現在有五種可用來遍歷物件的方法
1、for...in
遍歷物件本身以及原型鏈上面可列舉的屬性
2、Object.keys(obj)
: 返回一個陣列包含物件自身所有可列舉的屬性鍵(不包括繼承過來的)
3、Object.getOwnPropertyNames(obj)
: 返回一個陣列包含物件自身全部的屬性的鍵名(包括不可列舉的屬性)
4、Object.getOwnPropertySymbols
返回一個陣列,包含物件自身的所有 Symbol 屬性的鍵名。
5、Reflect.ownKeys
返回一個陣列,包含物件自身的所有鍵名,不管鍵名是 Symbol 或字串,也不管是否可列舉。
6、繼承相關的方法
1、Object.create()
使用該方法比較適合於對字面量物件的繼承
var obj = {
name : 'xiaoming'
}
var obj2 = Object.create(obj)
console.log(obj2.__proto__ == obj) //true
複製程式碼
以上程式碼中的 obj2.__proto__
(前後各兩個下劃線)指向的是obj2 的原型,它與obj相等說明obj2繼承於obj
2、getPrototypeOf
getPrtotypeOf 用來獲取建立該物件的構造器的原型(prototype)屬性。
var obj = {
name : 'xiaoming'
}
console.log(Object.getPrototypeOf(obj) == Object.prototype) // true複製程式碼
3、防篡改方法
防篡改方法對物件起到一定的保護作用,一共分為三個等級:
1、preventExtensions() 不允許新增,但是可以修改,也可以刪除
var obj = {
name : 'xiaoming'
}
Object.preventExtensions(obj)
obj.age = 12
obj.name = '小明'
console,log(obj) // {name:'小明'}複製程式碼
2、seal: 不允許新增,也不允許刪除,但是可以修改如下
var obj = {
name : 'xiaoming',
age : 16
}
Object.seal(obj)
delete obj.age
obj.name = '小明'
console,log(obj) // {name:'小明',age:16}複製程式碼
3、freeze: 不允許新增,也不允許刪除和修改
var obj = {
name : 'xiaoming',
age : 16
}
Object.seal(obj)
delete obj.age
obj.name = '小明'
obj.sex = 'man'
console,log(obj) // {name:'xiaoming',age:16}
複製程式碼
六、class 以及 extends 語法
ES6允許在js中使用class關鍵字去宣告一個類,也可以使用extends去實現類之間的繼承,當然要有super語法了
基本格式:
class 類名 {
constructor {
this.屬性 = 引數
}
方法名(){}
}複製程式碼
需要注意幾個點
1、類名定義採用 大駝峰命名法(每個單詞的首字母大寫),類名後面緊跟 { } 不用加 ()
2、在類的 { } 中不能直接寫語句(不象構造器),方法語句要寫在方法中,屬性定義在 constructor 裡面
3、方法與方法之間沒有逗號
使用extends實現繼承
基本格式:
class 子類 extends 父類 {
constructor() {
super()
this.屬性 = 引數
}
方法名(){}
}複製程式碼
注意:必須在 constructor
中使用super
方法 才能把父類的屬性繼承過來 ,使用了 extends
之後父類的方法會自動繼承
類中的靜態方法static
靜態方法可以直接通過 類名.方法() 的格式呼叫 ,js中的靜態方法也很簡單就是,在class中的方法名前面 加上 static 關鍵字
class P {
static say(){}
}
var obj = new P()
P.say()
obj .say() // say is not a function複製程式碼
注意:靜態方法只能 類名. 的格式呼叫,new出來的實體物件不能呼叫靜態方法
七、set與map
ES6新增的兩種資料結構 set 和 map
1、set 和陣列相似,不過set中存放的是唯一的值
通過new構造的方式建立一個 set 物件可以傳入一個陣列給其賦初值
var set = new Set([1,2,3])
複製程式碼
add方法可以給set物件新增資料
set.add(4)
set.add(5)複製程式碼
delete方法刪除set中的資料
set.delete(5)複製程式碼
遍歷set
遍歷set要使用ES6新增的 for...of ,不能使用forEach,不能使用 fo...in
for(var item of set){
console.log(item)
}
//1
//2
//3
//4複製程式碼
2、map 類似於物件,map中以鍵值對的方式存放資料
使用Map構造器建立map物件,可以放入一個類似於二維陣列的引數給其賦初值
var map = new Map([
['a','hello'],
[1,'hi']
])複製程式碼
說明:內層陣列中 的第一個引數將來會變成map中的鍵,第二個引數,是 該鍵名對應的值
通過set方法給Map物件新增屬性,set接收兩個引數,第一個是鍵名,第二個是鍵值
map.set('key','value')複製程式碼
通過get方法獲取到Map物件中的屬性值,get接收一個引數為map中的鍵名
map.get('key') //value複製程式碼
八、嚴格模式
嚴格模式字面意思理解就是,對於語法有更加規範的要求。
嚴格模式和非嚴格模式有什麼區別:
1、在嚴格模式下不能使用沒有var的變數
"use strict"
a = 10
console.log(a) // a is not defined複製程式碼
2、在嚴格模式下不能8進位制的數字
"use strict"
a = 05 //Octal literals are not allowed in strict mode.複製程式碼
3、在嚴格模式下不能把函式定義在if語句中
"use strict"
if(true){
function f(){
console.log('f')
}
}
f() //f is not defined複製程式碼
4、在嚴格模式下函式不能有重名的形參
"use strict"
function f(a,a){
console.log('f')
} //Duplicate parameter name not allowed in this context
複製程式碼
5、在嚴格模式下不能arguments就不能跟蹤形參了
"use strict"
function f(a,b){
console.log(a) //1
arguments[0] = 'yi'
console.log(a) //1
console.log(arguments[0]) //yi
}
f(1,2)複製程式碼
6、在嚴格模式下不能function中的this就再在是window,而是undefined
怎樣啟動嚴格模式
在程式碼快開始 新增 "use strict"
<script>
"use strict"
</script>複製程式碼
function(){
"use strict"
}複製程式碼
結尾:
當然在ES6中還有很多新增的語法,比如promise,Symbol, async,Module 等等,要想真正掌握ES6需要是長時間的練習才行,我這裡只是簡單的總結,學習ES6的新同學,推薦去讀阮一峰老師的《ECMAScript6 入門》