談談ES6語法(彙總上篇)

ES6能夠說是一個泛指,指5.1版本之後的JavaScript的下一代標準,涵蓋了ES2015,ES2016,ES2017等;亦指下一代JavaScript語言javascript

背景

嗯~ES6的語法有什麼好談的,無聊了吧?java

確實,語法糖的東西真的是學起來如嚼蠟 -- 淡無味;可是要用別人的東西來開發的,你學仍是學呢?git

因此,仍是簡單談下吧...es6

本次的ES6語法的彙總總共分爲上、中、下三篇,本篇文章爲上篇。github

var、let和const

var是以前就有的了,在這裏提出來主要是爲了比較其和let與const數組

區別

1. 塊級做用域函數

for(var i = 0; i < 3; i++) {
	setTimeout(() => {
		console.log(i); // 輸出3個3
	}, 0)
}
複製代碼

解析:變量i是var聲明的,在全局範圍內是都有效,全局只有一個變量i。每次循環,變量的值會發生改變。循環內的i是指向全局的i。優化

for(let i = 0; i < 3; i++) {
	setTimeout(() => {
		console.log(i); // 輸出0, 1, 2
	}, 0)
}
複製代碼

解析:變量i是let聲明的,當前的i只在本輪循環有效,因此每次循環的i其實都是一個新變量。JavaScript引擎內部會記住上一輪的值,初始化本輪的變量i時,就在上一輪循環的基礎上進行計算。ui

2. 不存在變量提高this

console.log(a); // undefined
var a = 100;
複製代碼

var命令會發生變量提高現象,即變量能夠在聲明以前使用,值爲undefined;而let糾正了這種行爲,不能產生變量提高。

console.log(a); // 報錯
let a = 100;
複製代碼

3. 暫時性死區

只要塊級做用域內,存在let命令,它所聲明的變量就綁定(binding)在這個區域,再也不受外部影響。

如:

var temp = 123;
if(true) {
	temp = 'abc'; // 引入錯誤
	let temp; 
}
複製代碼

在上面中,if後面的大括號內容就造成了一個區域。而temp此時是找不到外層的,由於內部有個temp且你在內部let temp聲明前賦值了。

在看一個隱晦的例子:

function bar(x = y, y = 2) {
	return [x, y]
}
bar(); // 報錯
複製代碼

在上面的例子中bar裏面進行賦值操做的時候,就產生了一個封閉的區域了,能夠認爲x 和 y經過let聲明,但是上面的問題是,x = y的引用在y = 2的聲明以前。

能夠修正以下:

function bar(y = 2, x = y) {
	return [x, y];
}
bar(); // [2, 2]
複製代碼

4. 不可重複聲明

var a = 100;
var a = 1000;
console.log(a); // 1000
複製代碼
let a = 100;
let a = 1000; // 報重複聲明錯誤
複製代碼

5. ES6聲明的變量不會掛在頂層對象

嗯~ES6變量的聲明是指哪些聲明呢?

let, const, import, class聲明。

var, function聲明是ES6以前的。

因此目前JavaScript有六種聲明變量的方式了~

var job = 'teacher';
console.log(window.job); // teacher
複製代碼
let job = 'teacher';
console.log(window.job); // undefined
複製代碼

const命令注意點

  1. let能夠先聲明稍後賦值;而const聲明以後必須立馬賦值,不然會報錯
let a;
a = 100; // this is ok
複製代碼
const a; // 報沒初始化數據的錯誤
複製代碼
  1. const聲明瞭簡單的數據類型就不能更改了;聲明瞭引用類型(數組,對象等),指針指向的地址不能更改,可是內部的數據能夠更改的
const str = 'this is a string';
str = 'this is another string'; // 報了個「給不變的變量分配值」的錯誤
複製代碼
const obj = {
	name: 'jia'
}
obj.name = 'ming'; // this is ok
obj = {}; // 報了個「給不變的變量分配值」的錯誤
複製代碼

let和const的使用場景

  1. let使用場景:變量,用以代替var

  2. const使用場景:常量、聲明匿名函數、箭頭函數的時候。

// 常量
const PI = 3.14;

// 匿名函數
const fn1 = function() {
	// do something
}

// 箭頭函數
const fn2 = () => {
	// do something
}
複製代碼

變量的解構賦值

解構能夠理解就是一個做用:簡化你變量賦值的操做。

數組場景

let [name, job] = ['jiaming', 'teacher'];
console.log(name); // jiaming
複製代碼

本質上,這種寫法屬於模式匹配,只要等號兩邊的模式相同(重點),左邊的變量就會被賦予對應的值。再好比:

let [ , , third] = ["foo", "bar", "baz"];
console.log(third); // "baz"

let [head, body, ...tail] = [1, 2, 3, 4, 5];
console.log(tail); // [3, 4, 5]
複製代碼

也可使用默認值。可是默認值生效的前提是:ES6內部使用嚴格相等運算符(===),判斷一個位置是否有值。因此,只有當一個數組成員嚴格等於undefined,默認值纔會生效。

let [x, y = 'b'] = ['a']; // x='a', y='b'

let [z = 1] = [undefined];
console.log(z); // 1

let [k = 1] = [null];
console.log(k); // null
複製代碼

對象場景

const state = {
	name: 'jiaming',
	job: 'teacher'
};
let {
	name,
	job
} = state;
// 上面的場景很熟悉吧
console.log(job); // teacher
複製代碼

上面的例子若是寫具體的話,是這樣的:

const state = {
	name: 'jiaming',
	job: 'teacher'
};
let {
	name: name, // 第一個name是匹配模式,第二個name纔是變量,二者同名簡化成一個便可
	job: job
} = state;
複製代碼

咱們來改寫下:

const state = {
	name: 'jiaming',
	job: 'teacher'
};
let {
	name: job,
	job: name
} = state;
console.log(job); // jiaming
複製代碼

對象也可使用默認值,可是前提是:對象的屬性值嚴格等於undefined

以下:

var {x = 3} = {x: undefined};
console.log(x); // 3

var {y = 3} = {y: null};
console.log(y); // null
複製代碼

字符串場景

字符串之因此可以被解構賦值,是由於此時字符串被轉換成了一個相似數組的對象。

const [a, b, ...arr] = 'hello';
console.log(arr); // ["l", "l", "o"]
複製代碼
let {length: len} = 'hello';
console.log(len); // 5
複製代碼

數值和布爾值場景

解構賦值時,若是等號右邊是數值和布爾值,則會先轉換爲對象(分別是基本包裝類型Number和基本包裝類型Boolean)。不過這種場景用得很少~

let {toString: s} = 123;
console.log(s); // function toString() { [native code] }
console.log(s === Number.prototype.toString); // true
複製代碼
let {toString: s} = true;
console.log(s === Boolean.prototype.toString); // true
複製代碼

解構賦值的規則是,只要等號右邊的值不是對象或數組,就先將其轉爲對象。因爲undefined和null沒法轉爲對象,因此對它們進行解構賦值,都會報錯

兩種使用場景

1. 交換兩變量值

let [a, b] = ['reng', 'jia'];
[a, b] = [b, a];
console.log(b); // 'reng'
複製代碼

2. 將字符串轉換爲數組

let [...arr] = 'reng';
console.log(arr); // ["r", "e", "n", "g"]
console.log(arr.splice(0, 2)); // ["r", "e"] 返回刪除的數組(能使用數組的方法了)
複製代碼

字符串擴展

針對字符串擴展這個,我的感受模版字符串使用的頻率比較高。模版字符串解放了拼接字符串帶來的繁瑣操做的體力勞動。

let name = 'jiaming';
let str = 'Hello! My name is '+ name + '. Nice to meet you!';
let strTemp = `Hello! My name is ${ name }. Nice to meet you!`
複製代碼

對於新增的字符串方法,能夠記下下面這幾個:

  • includes(): 返回布爾值,表示是否找到了參數字符串
  • startWith(): 返回布爾值,表示參數字符串是否在原字符串的頭部
  • endWith(): 返回布爾值,表示參數字符串是否在原字符串的尾部
  • trimStart(): 返回字符串,表示消除參數字符串開頭的空格
  • trimEnd(): 返回字符串,表示消除參數字符串結尾的空格

數值擴展

留意下在Number對象上提供的新方法:

  • Number.isFinite(): 返回布爾值,表示參數值是否有限的
  • Number.isNaN(): 返回布爾值,用來檢查一個值是否爲NaN
Number.isNaN(NaN) // true
Number.isNaN(15) // false
複製代碼
  • Number.isInteger(): 返回布爾值,用來判斷一個數值是否爲整數

關於Math對象上的方法,遇到要用到時候,查API吧,否則記太多,腦瓜子會疼~

函數擴展

rest參數

ES6引入rest參數(形式是...變量名),用於獲取多餘的參數,這樣就不須要使用arguments對象了。rest參數搭配的變量是一個數組(arguments是一個類數組來的),該變量將多餘的參數放入數組中。

arguments對象是一個類數組,還得經過Array.prototype.slice.call(arguments)將其轉換爲真數組;而rest參數直接就可使用數組的方法了。

function add(...arr) {
	console.log(arr); // [2, 5, 3]
	let sum = 0;
	for(var val of arr) {
		sum += val;
	}
	return sum;
}
console.log(add(2, 5, 3)); // 10
複製代碼

箭頭函數

ES6容許使用「箭頭」(=>)定義函數。

const f = v => v; // 注意是有返回值return的啊

// 等同於
const f = function (v) {
	return v;
}
複製代碼

若是箭頭函數的代碼塊部分多於一條語句,就要使用大括號將它們括起來,而且使用return語句返回結果。

const sum = (num1, num2) => num1 + num2;

// 等價於,使用了大括號,那箭頭函數裏面就要使用return了
const sum = (num1, num2) => { return num1 + num2 }

// 等價於
const sum = function(num1, num2) {
	return num1 + num2
}
複製代碼

使用箭頭函數注意點:

  1. 函數體內的this對象,就是定義所在的對象,而不是使用時所在的對象。
  2. 不能夠看成構造函數,也就是說,不可使用new命令,不然會拋出一個錯誤。
  3. 不可使用arguments對象,該對象在函數體內不存在的,若是要用,能夠用rest參數代替。
  4. 不可使用yield命令,所以箭頭函數不能用做Generator函數。
function foo() {
	setTimeout(() => {
		console.log('id:', this.id); // id: 42
	}, 100);
}

var id = 21;

foo.call({ id: 42 });
複製代碼
// 錯誤使用箭頭函數的例子

const cat = {
	lives: 9,
	jumps: () => { // 箭頭函數的錯誤使用,由於對象不構成單獨的做用域
		this.lives--; // this 指向window
	}
}

var button = document.getElementById('press'); // 一個節點對象
button.addEventListener('click', () => { // 箭頭函數的this指向window
 	this.classList.toggle('on');
});

// 箭頭函數改爲`function`匿名函數指向就正確了。
複製代碼

箭頭函數適合處理簡單的計算,若是有複雜的函數體或讀寫操縱不建議使用,這樣能夠提升代碼的可讀性。

關於尾遞歸和其優化能夠直接看阮先生的文檔

找下茬

假設有這麼一個需求,須要對二維數組的元素進行反轉並被1減。咱們來看下下面代碼,哪一個能實現此需求呢?

// 代碼一
const A = [[0,1,1],[1,0,1],[0,0,0]];
const flipAndInvertArr = function(A) {
    A.map(item=>{
        item.reverse().map(r=>1-r)
    })
}
複製代碼
// 代碼二
const A = [[0,1,1],[1,0,1],[0,0,0]];
const flipAndInvertArr = A=> A.map(res =>res.reverse().map(r => 1 - r));
複製代碼

運行以後,發現代碼二是能實現需求的:

let resultArr = flipAndInvertArr(A);
console.log(resultArr); // [[0, 0, 1], [0, 1, 0], [1, 1, 1]]
複製代碼

嗯~上面已經提到過,箭頭函數體加上大括號後,是須要本身手動return的~

咱們來改寫下代碼一,以便符合需求:

const A = [[0,1,1],[1,0,1],[0,0,0]];
const flipAndInvertArr = function(A) {
    return (A.map(item=>{
        return item.reverse().map(r=>1-r)
    }))
}
let result = flipAndInvertArr(A);
console.log(result); // [[0, 0, 1], [0, 1, 0], [1, 1, 1]]
複製代碼

驚喜不,意外不~

參考和後話

本次的ES6語法的彙總總共分爲上、中、下三篇,本篇文章爲上篇。

文章首發在github上--談談ES6語法(彙總上篇)。更多的內容,請戳個人博客進行了解,能留個star就更好了💨

相關文章
相關標籤/搜索