let和const
原先聲明變量的形式
var test = 5; //全局變量
function a()
{var cc=3; //局部變量alert(test);
}
function b(){alert(test);}test = 5;//全局變量
function a()
{aa=3; //全局變量alert(test);
}
在es6之前,作用域只有全局作用域和函數作用域,沒有塊級作用域,所以考慮如下代碼
{ var a = 6;var a = 8}
console.log(a) // 答案是8
我們本來想著用{}表示一塊作用域,外面應該訪問不到里面的變量,但是事實是var聲明的變量泄漏到全局作用域了。所以引出let
let有三個個特點:
- 不能重復賦值
- 作用域是局部作用域
- 不存在變量提升
{let a=10;let a=11}
Uncaught SyntaxError: Identifier 'a' has already been declared
{let a = 10}
a
Uncaught ReferenceError: a is not definedat <anonymous>:1:1
作用域是塊級作用域
var a = [];
for (var i = 0; i < 10; i++) {a[i] = function () {console.log(i);};
}
a[0](); // 10
因為var定義的是全局,所以在內存中只維護一塊空間,每次+1的時候這塊空間的值發生改變,而數組中的每個函數的引用都指向這一塊空間,當最后調用的時候那塊空間的值就是10了,所以結果就是10了
var a = [];
for (let i = 0; i < 10; i++) {a[i] = function () {console.log(i);};
}
a[0](); // 0
let聲明的是塊級作用域,所以每一次循環的i其實都是一個新的變量,每次循環都會創建新的內存空間,每個放到數組中的函數都指向對應的內存空間的值,等調用的時候就調用指向的值。那么問題來了,如果每一輪循環的變量i都是重新聲明的,那它怎么知道上一輪循環的值,從而計算出本輪循環的值?這是因為 JavaScript 引擎內部會記住上一輪循環的值,初始化本輪的變量i時,就在上一輪循環的基礎上進行計算
變量提升
console.log(foo); // undefined
var foo = 2;
在es6之前,變量使用在定義之前得到的結果是undefiend,這種現象叫做變量提升,這其實不符合正常的編程邏輯。
js引擎在解釋的時候從上到下,先找定義的變量,也就是說下面代碼其實是這樣的
var foo;
console.log(foo); // undefined
foo = 2;
有了let之后
console.log(foo); // 報錯
let foo = 2;
塊級作用域
內層變量可能會覆蓋外層變量
var tmp = 5;function f() {console.log(tmp);if (false) {var tmp = 'hello world';}
}f(); // undefined
至于結果為啥是undefined參考變量提升的解釋。在內部定義的var tmp = 'hello world';
的在解釋的時候在函數作用域內是先var tmp
的
用來計數的循環變量泄露為全局變量
for (var i = 0; i < 5; i++) {console.log(s[i]);
}console.log(i); // 5
變量i只用來控制循環,但是循環結束后,它并沒有消失,泄露成了全局變量。
const
const定義的變量有兩點需要注意:
- 不能只用來聲明,聲明的時候就必須賦值
- 不能修改
const的作用域與let命令相同:只在聲明所在的塊級作用域內有效。
模板字符串
在es6之前,我們拼接字符串都有+
,這種方式是繁瑣的,可以使用反引號(`)標識。
反引號主要有3個作用:
- 當作普通字符串
- 定義多行字符串(相當于python的三引號)
- 在字符串中嵌入變量
d
10
`iloce${d}`
"iloce10"
箭頭函數
箭頭函數就是函數的一種縮寫形式
//無形參
var f = () => 5;
// 等同于
var f = function () { return 5 };//多個形參
var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {return num1 + num2;
};
箭頭函數有一點需要注意
在es6之前,函數體內的this對象,就是使用時所在的對象,而不是定義時所在的對象
var person = {name:'jack',age:18, fav:function(){ console.log(this)}}person.fav();
用了箭頭函數之后,函數體內的this對象,就是定義時所在的對象,而不是使用時所在的對象
var person2 = {name:'jack',age:18,fav: ()=>{// 當前this指向了定義時person2對象時所處的對象(window)console.log(this);}}person2.fav();
為了解決這個問題, 使用對象的單體模式:
var person2 = {name:'jack',age:18,fav() {// 當前this指向了定義時person2對象時所處的對象(window)console.log(this);}}person2.fav();
面向對象
在es6之前創建對象是這樣做的
function Animal(name,age){this.name = name;this.age = age;}Animal.prototype.showName = function(){console.log(this.name);console.log(this.age);}var a = new Animal('小黃',5);a.showName();
我們在python中通過class就能定義一個對象,在es6中也可以這樣做
class People {// 類似python中的initconstructor(name,age){this.age = age;this.name = name;}eat(){console.log('111')}study() {console.log('1222')}
}
定義“類”的方法的時候,前面不需要加上function這個關鍵字,直接把函數定義放進去了就可以了。另外,方法之間不需要逗號分隔,加了會報錯。