1.變量聲明let和const
預(yù)解析機制 : var有變量提升的機制
function fn(){
var a=1
console.log(a)
}
fn() //1
//以上代碼等同于:
function fn(){
var a
a=1
console.log(a)
}
fn() //1
把聲明賦值和打印的語句順序交換
function fn(){
console.log(a)
var a=1
}
fn() //undefined
//以上代碼等同于:
function fn(){
var a
console.log(a)
a=1
}
fn() //undefined
ES5的解析:從上到下解析,遇到var關(guān)鍵字,就把該聲明提前到函數(shù)的最頂部,如果不在函數(shù)內(nèi)就提升到全局作用域的最頂部,賦值留在原位,因為先聲明了變量a,然后打印,最后再賦值,所以結(jié)果就是undefined。
但是使用let和const就可以解決變量提升的問題,let代表變量,const代表常量。
let
function fn(){
let a=1
console.log(a)
}
fn() //1
把聲明賦值和打印的語句順序交換
function fn(){
console.log(a)
let a=1
}
fn() //Error: a is not defined
let不存在變量提升的機制,不會被聲明到最頂部,所以未聲明就打印,會報錯:變量未定義。
const
const PI=3.14
PI=3.1415926 //Error: Assignment to constant variable.
const的聲明必須賦值,賦值之后不可修改。那要怎么修改const的值呢?
實際上是const的指向不可修改,但是可以通過更改地址中的內(nèi)容來修改了const的值。
const stu={ name: '小明' }
//正確,改變地址中的內(nèi)容
stu.name='小紅'
//報錯,改變了const的指向
stu={ name: '小紅' }
TDZ(暫時性死區(qū))
if (true) {
// TDZ開始
tmp = 'abc';
console.log(tmp); // Error: tmp is not defined
let tmp; // TDZ結(jié)束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
let 或者 const,會將聲明放入TDZ(暫時性死區(qū)),只有執(zhí)行到變量聲明語句時,變量才會從TDZ中取出來使用??傊?,暫時性死區(qū)的本質(zhì)就是,只要一進入當(dāng)前作用域,所要使用的變量就已經(jīng)存在了,但是不可獲取,只有等到聲明變量的那一行代碼出現(xiàn),才可以獲取和使用該變量。
塊級作用域
ES6在ES5 全局作用域和函數(shù)作用域的基礎(chǔ)上開拓了塊級作用域,使得變量的作用域更加清晰。
內(nèi)層變量覆蓋外層變量
var tmp = new Date();
function f() {
console.log(tmp);
var tmp = 'hello world';
}
f(); // undefined
函數(shù)f執(zhí)行后,輸出結(jié)果為undefined,原因在于變量提升,導(dǎo)致內(nèi)層的tmp變量覆蓋了外層的tmp變量。
用來計數(shù)的循環(huán)變量泄露為全局變量
var s = 'hello';
for (var i = 0; i < s.length; i++) {
console.log(s[i]);
}
console.log(i); // 5
變量i只用來控制循環(huán),但是循環(huán)結(jié)束后,它并沒有消失,泄露成了全局變量。
ES6新增了塊級作用域,即一個{}就是一個塊級作用域。
{ let a=1 }
console.log(a) //報錯
不同塊級作用域可以定義重名的變量
{
let a='hello world'
{ let a='hello world' }
}
立即執(zhí)行函數(shù)就可以用塊級作用域來替代
// IIFE寫法
(function(){
var a=...
...
})()
// 塊級作用域?qū)懛?{
var a=...
...
}
2.變量的解構(gòu)賦值
數(shù)組的解構(gòu)賦值
解構(gòu)賦值語法是一個Javascript表達式,這使得可以將數(shù)據(jù)從數(shù)組或?qū)ο筇崛〉讲煌淖兞恐小?/p>
以前為變量賦值,只能直接指定值。
let a = 1
let b = 2
let c = 3
ES6就可以寫成這樣
let [a, b, c] = [1, 2, 3]
本質(zhì)是上述匹配屬于一種模式匹配, 也就是只要等號兩邊的模式相同,左邊的變量就會被賦予對應(yīng)的值。
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3
let [ , , third] = ["foo", "bar", "baz"];
third // "baz"
let [head, ...rest] = [1, 2, 3, 4];
head // 1
rest // [2, 3, 4]
let [bar, foo] = [1];
bar //1
foo //undefined
默認值
解構(gòu)賦值允許指定默認值
let [ a = 1 ]= [ ]
a //1
ES6 內(nèi)部使用嚴格相等運算符(===),判斷一個位置是否有值。所以,只有當(dāng)一個數(shù)組成員嚴格等于undefined,默認值才會生效。
let [x = 1] = [undefined];
x // 1
let [x = 1] = [null];
x // null
null不嚴格等于undefined,默認值不生效。
如果默認值是一個表達式,表示則是惰性求值的,只有在用到的時候才會求值。
function fn(){
return -1
}
let [a = fn()]=[]
console.log(a) //-1
let [b = fn()]=[1]
console.log(b) //1
默認值可以引用其他變量,但該變量必須已經(jīng)聲明。
let [x = 1, y = x] = [] // x=1; y=1
let [x = 1, y = x] = [2] // x=2; y=2
let [x = 1, y = x] = [1, 2] // x=1; y=2
let [x = y, y = 1] = [] // Error: y is not defined
x用y做默認值,但是y還沒有聲明,所以報錯。
對象的解構(gòu)賦值
解構(gòu)不僅可以用于數(shù)組,還可以用于對象。
對象的解構(gòu)賦值與數(shù)組不同的是:數(shù)組的元素是按次序排列的,變量的取值由它的位置決定;而對象的屬性沒有次序,變量必須與屬性同名,才能取到正確的值。
var {foo,bar} = {foo: 'aaa', bar: 'bbb'}
console.log(foo) //aaa
console.log(bar) //bbb
//以上代碼等同于:
let { foo: foo, bar: bar } = { foo: "aaa", bar: "bbb" }
var {baz} = {foo: 'aaa', bar: 'bbb'}
console.log(baz) //undefined
以上未找到baz,解構(gòu)賦值失敗。
let { foo: baz } = { foo: "aaa", bar: "bbb" }
baz // "aaa"
foo // Error: foo is not defined
foo是匹配的模式,baz才是變量。真正被賦值的是變量baz,而不是模式foo。
let obj = {
p: [
'hello',
{ y: 'world' }
]
}
let { p: [x, { y }] } = obj
console.log(x,y) //hello word
字符串的解構(gòu)賦值
字符串的解構(gòu)賦值,符串會轉(zhuǎn)化成一個類數(shù)組的對象。
const [a,b,c,d,e] = 'hello'
console.log(a,b,c,d,e) //h e l l o
類數(shù)組對象有length屬性,所以len是5。
let {length : len} = 'hello';
len //5
函數(shù)參數(shù)的解構(gòu)賦值
函數(shù)的參數(shù)也可以使用解構(gòu)賦值
function add([x, y]){
return x + y;
}
add([1, 2]); // 3
3.字符串的擴展
- 模板字符串
拼接字符串一直是令人頭痛的地方,一旦變量多起來,字符串的拼接就會變得尤為繁瑣,而且可讀性極低,模板字符串簡直就是開發(fā)者的福音。用
${}嵌入字符串中拼接,有幾個變量就用幾個${}
// es5
var name='world'
console.log('hello'+name)
//es6
var name='world'
console.log(`hello${name}`)
- 在ES5中通過反斜杠(
\)來做多行字符串的拼接,ES6反引號(``)可以直接搞定。
// es5
var msg='hello \
world!'
// es6
const template=`
<div>
<span>hello world!</span>
</div>
`
- 對運算的支持
let a=1
let b=2
let result=`${a+b}`
console.log(result) //3
- 字符串是否存在
// es5
let name = 'Github'
let msg = 'hello world,my name is Github'
console.log(msg.indexOf(name)) //返回索引值
// es6
let name = 'Github'
let msg = 'hello world,my name is Github'
console.log(msg.includes(name)) //返回布爾值,更直觀
- 復(fù)制字符串
console.log('hello world|'.repeat(3)) // hello world|hello world|hello world|
4.箭頭函數(shù)
ES6提供了箭頭函數(shù),給函數(shù)的創(chuàng)建提供了一種簡便方法。
三大特點:
省略function關(guān)鍵字
省略return關(guān)鍵字
繼承當(dāng)前上下文的this關(guān)鍵字
// es5
[1,2,3].forEach((function (item) {
return item + 1
}).bind(this))
// es6
[1,2,3].forEach(item=>item+=1)
當(dāng)參數(shù)只有一個時,可以省略(),當(dāng)函數(shù)表達式只有一個時,可以省略{}和return
// 一個參數(shù),一個表達式
let person = name => 'hello ' + name
console.log(person('world')) //hello world
// 多個參數(shù),多個表達式
let person = (name,age) => {
const msg = `hello ${name},age is ${age}`
return msg
}
console.log(person('world',18)) //hello world,age is18
在使用別人的輪子時,因為不熟練經(jīng)常會報錯,那么這些框架是怎么拋出錯誤的呢?
let add = (a,b=1) =>{
if(a<=0){
throw new Error('The variable must be greater than 0')
}
return a+b
}
console.log(add(0)); // Uncaught Error: The variable must be greater than 0
5.對象擴展功能
- 在對象的鍵值對重名時,可以這樣處理
// es5
function person(name,age){
return {
name:name,
age:age
}
}
// es6
function person(name,age){
return {
name,
age
}
}
- ES6改進了為對象字面量方法賦值的語法,可以省略
:和function關(guān)鍵字
// es5
function person(name){
name,
sayHi:function(){
console.log('Hi')
}
}
// es6
function person(name){
name,
sayHi(){
console.log('Hi')
}
}
- 合并對象
let obj1={name:'Github'}
let obj2={sex:'男'}
let obj3={age:18}
let obj = Object.assign({},obj1,obj2,obj3)
console.log(obj) // {name:"Github",sex:"男",age:18}
6.擴展運算符(...)
- 組裝數(shù)組或者對象
const arr1=[1,2,3]
const arr2=[...arr1,4,5,6]
console.log(arr2) //[1,2,3,4,5,6]
const obj1={
a:1,
b:2
}
const obj2={...obj1,c:3,d:4}
console.log(obj2); //{a:1,b:2,c:3,d:4}
7.promise
在promise之前,回調(diào)地獄,可讀性差、耦合度高、擴展性低 。promise大大提高了代碼的可讀性,用同步編程的方式來編寫異步代碼,極大的降低了代碼耦合性而提高了程序的可擴展性。
promise執(zhí)行多步操作非常好用,現(xiàn)在模仿一個多步操作的過程。
把大象裝進冰箱需要幾步?答:三步,打開冰箱門,把大象塞進去,關(guān)上冰箱門。
let flag = true;
function step1(resolve, reject) {
console.log('1.開始-打開冰箱門');
if (flag) {
resolve('打開冰箱門--完成');
} else {
reject('打開冰箱門--出錯');
}
}
function step2(resolve, reject) {
console.log('2.開始-把大象塞進去');
if (flag) {
resolve('把大象塞進去--完成');
} else {
reject('把大象塞進去--出錯');
}
}
function step3(resolve, reject) {
console.log('3.開始-關(guān)上冰箱門');
if (flag) {
resolve('關(guān)上冰箱門--完成');
} else {
reject('關(guān)上冰箱門--出錯');
}
}
new Promise(step1).then(function (val) {
console.log(val);
return new Promise(step2);
}).then(function (val) {
console.log(val);
return new Promise(step3);
}).then(function (val) {
console.log(val);
return val;
});
/*
1.開始-打開冰箱門
打開冰箱門--完成
2.開始-把大象塞進去
把大象塞進去--完成
3.開始-關(guān)上冰箱門
關(guān)上冰箱門--完成
*/
8.class類
class聲明和使用
// 類的聲明
class Coder{
getName(name){
console.log(name);
}
}
// 類的使用
let Github=new Coder
Github.getName('Github') // Github
聲明一個JsCoder的新類并繼承Coder類
class Coder{
getName(name){
console.log(name);
}
}
class JsCoder extends Coder{}
let js=new JsCoder
js.getName('js')
9.模塊化
前后端分離,前端業(yè)務(wù)邏輯也在日益復(fù)雜,前端也在借鑒后端的思想,ES6為我們增加了模塊化操作來方便我們引用第三方庫,尤其是在開發(fā)vue項目時,需要依賴大量第三方包,需要我們使用到模塊化的思想來開發(fā)項目。
- import : 引入模塊
- export : 輸出模塊
// a.js 導(dǎo)出
export var name='hello world'
export function add(a,b){
return a+b;
}
// b.js 導(dǎo)入
import { name,add } from "./a.js" //也可以分開導(dǎo)入
console.log(name) //hello world
console.log(add(1,2)) //3
總結(jié)
ES6的內(nèi)容遠不止這些,不過也算是高頻使用了,更多內(nèi)容可以去看一下阮大神的ES6在線書籍。對于即將入門vue的小伙伴來說,ES6的學(xué)習(xí)對你來說是必不可少的,ES6的語法為我們前端開發(fā)者提供了更多的便利,也是學(xué)習(xí)vue框架的一塊敲門磚,祝大家能盡快地掌握ES6語法,提高開發(fā)效率,本人也是剛學(xué)習(xí)ES6不久,如果文中內(nèi)容有誤,請不要吝嗇您的意見或建議,歡迎留評。