1. 程式人生 > >ES6的變數宣告

ES6的變數宣告

在ES5中,變數宣告只有var和function以及隱式宣告三種,在ES6中則增加了let,const,import和class四種,以下來介紹著七種變數的宣告。

var

ES5中最原始的變數宣告,用於宣告變數,其實JavaScript是弱型別語言,對資料型別變數要求不太嚴格,所以不必宣告每一個變數的型別(這就是下面說的隱式宣告,當然這並不是一個好習慣),在使用變數之前先進行宣告是一種好的習慣。

1.作用域

使用var宣告的變數的作用域是函式作用域(在ES5時代,只有函式作用域和全域性作用域兩種作用域),在一個函式內用var宣告的變數,則只在這個函式內有效。

function test
()
{ var a; console.log(a);//undefined } console.log(a);//ReferenceError: a is not defined

2.變數宣告提升

用var宣告變數時,只要在一個函式作用域內,無論在什麼地方宣告變數,都會把變數的宣告提升到函式作用域的最前頭,所以無論使用變數在變數宣告前還是聲明後,都不會報錯(當然只是宣告提前,賦值並沒有提前,所以如果使用在宣告之前,會輸出undefined,但不會報錯)。

function test(){
    console.log(a);//undefined
    var a=3;
}

隱式宣告

當沒有宣告,直接給變數賦值時,會隱式地給變數宣告,此時這個變數作為全域性變數存在。

function test(){
    a=3;
    console.log(a);//3
}
test();
console.log(a);//3

當然要注意,隱式宣告的話就沒有變數宣告提前的功能了,所以下面的使用是會報錯的。

function test(){
    console.log(a);//ReferenceError: a is not defined
    a=3;
}

function

用function宣告的是函式物件,作用域與var一樣,是函式作用域。

function test()
{ function a(){ console.log('d'); } a();//'d' } a();//ReferenceError: a is not defined

同樣,function宣告也有變數宣告提升,下面是兩個特殊的例子:

function hello1(a){
       console.log(a); //[Function: a]
    function a(){}
    console.log(a);//[Function: a]
}
hello1('test');   

function hello2(a){
       console.log(a); //test
    var a=3console.log(a);//3
}
hello2('test');

這裡有涉及到函式中形參的宣告,我們可以將以上兩個例子看成:

function hello1(a){
    var a='test;
       console.log(a); //[Function: a]
    function a(){}
    console.log(a);//[Function: a]
}
hello1('test');   

function hello2(a){
    var a='test;
       console.log(a); //test
    var a=3;
    console.log(a);//3
}
hello2('test');

可以看到函式物件的宣告也提前了,但是在形參變數宣告之後(形參的變數宣告在所有宣告之前)。

當函式物件和普通物件同時宣告時,函式物件的宣告提前在普通物件之後。

function test(){     
    console.log(a);//[Function: a]
    function a(){}
    var a;
    console.log(a);//[Function: a]
}

let

ES6新增的宣告變數的關鍵字,與var類似。

當然,與var也有很大區別:

1.作用域不同

let宣告的變數的作用域是塊級作用域(之前的js並沒有塊級作用域,只有函式作用域和全域性作用域),var宣告的變數的作用域是函式作用域。

{
  let a = 10;
  var b = 1;
}

a // ReferenceError: a is not defined.
b // 1

2.不存在變數宣告提升

用var宣告變數時,只要在一個函式作用域內,無論在什麼地方宣告變數,都會把變數的宣告提升到函式作用域的最前頭,所以無論使用變數在變數宣告前還是聲明後,都不會報錯。而let不一樣,與java以及其他語言一樣,let宣告的變數,在未宣告之前變數是不存在的。(js的語法越來越向java靠攏)

console.log(a); // undefined,但是不報錯。
console.log(b); // ReferenceError: b is not defined.

var a = 2;
let b = 2;

注意:在使用babel時可能會遇到這樣的情況:

console.log(b); //undefined
let b = 2;

babel在翻譯es6時,似乎直接將let變為了var,所以執行時也有變數宣告提升了,但是在Chrome下執行時是正確的。

3.暫時性死區

所謂暫時性死區,意思是,在一個塊級作用域中,變數唯一存在,一旦在塊級作用域中用let聲明瞭一個變數,那麼這個變數就唯一屬於這個塊級作用域,不受外部變數的影響,如下面所示。

無論在塊中的任何地方聲明瞭一個變數,那麼在這個塊級作用域中,任何使用這個名字的變數都是指這個變數,無論外部是否有其他同名的全域性變數。

暫時性死區的本質就是,只要一進入當前作用域,所要使用的變數就已經存在了,但是不可獲取,只有等到宣告變數的那一行程式碼出現,才可以獲取和使用該變數。

暫時性死區的意義也是讓我們標準化程式碼,將所有變數的宣告放在作用域的最開始。

var a = 123;  
{
 console.log(a);//ReferenceError
  let a;
}

4.不允許重複宣告

在相同的作用域內,用let宣告變數時,只允許宣告一遍。 (var是可以多次宣告的)

// 正確
function () {
  var a = 10;
  var a = 1;
}

// 報錯,Duplicate declaration "a"
function () {
  let a = 10;
  var a = 1;
}

// 報錯,Duplicate declaration "a"
function () {
  let a = 10;
  let a = 1;
}

const

const用來宣告常量,const宣告的常量是不允許改變的,只讀屬性,這意味常量宣告時必須同時賦值, 只宣告不賦值,就會報錯,通常常量以大寫字母命名。

阮一峰大神的書裡說,在嚴格模式下,重新給常量賦值會報錯,普通模式下不報錯,但是賦值無效。但是測試了一下,無論是嚴格還是非嚴格模式,都會報錯。

const A = 1;
A = 3;// TypeError: "A" is read-only

const和let類似,也是支援塊級作用域,不支援變數提升,有暫時性死區.

注意:如果宣告的常量是一個物件,那麼對於物件本身是不允許重新賦值的,但是對於物件的屬性是可以賦值的。

const foo = {};
foo.prop = 123;

foo.prop// 123

foo = {} // TypeError: "foo" is read-only

import

ES6採用import來代替node等的require來匯入模組。

import {$} from './jquery.js'

$物件就是jquery中export暴露的物件。

import命令接受一個物件(用大括號表示),裡面指定要從其他模組匯入的變數名。注意:大括號裡面的變數名,必須與被匯入模組對外介面的名稱相同。

如果想為輸入的變數重新取一個名字,import命令要使用as關鍵字,將輸入的變數重新命名。

import { New as $ } from './jquery.js';

注意,import命令具有提升效果,會提升到整個模組的頭部,首先執行。

class

ES6引入了類的概念,有了class這個關鍵字,當然,類只是基於原型的面向物件模式的語法糖,為了方便理解和開發而已,類的實質還是函式物件,類中的方法和物件其實都是掛在對應的函式物件的prototype屬性下。

我們定義一個類:

//定義類
class Person {
  constructor(name, age) {
        this.name = name;
        this.age = age;
  }    
  setSex(_sex) {
        this.sex=_sex;
  }
}

constructor方法,就是構造方法,也就是ES5時代函式物件的主體,而this關鍵字則代表例項物件,將上述類改寫成ES5格式就是:

function Person(name, age){
          this.name = name;
        this.age = age;
}

Person.prototype. setSex = function (_sex) {
          this.sex=_sex;
}

所以說,類不算什麼新玩意,大多數類的特性都可以通過之前的函式物件與原型來推導。

1.所有類都有constructor函式,如果沒有顯式定義,一個空的constructor方法會被預設新增(有點類似java了)。當然所有函式物件都必須有個主體。

2.生成類的例項物件的寫法,與ES5通過建構函式生成物件完全一樣,也是使用new命令。

class B {}
let b = new B();

3.在類的例項上面呼叫方法,其實就是呼叫原型上的方法,因為類上的方法其實都是新增在原型上。

b.constructor === B.prototype.constructor // true

4.與函式物件一樣,Class也可以使用表示式的形式定義。

let Person = class Me {
      getClassName() {
        return Me.name;
      }
};

相當於

var Person = function test(){}

5.Class其實就是一個function,但是有一點不同,Class不存在變數提升,也就是說Class宣告定義必須在使用之前。

全域性變數

全域性物件是最頂層的物件,在瀏覽器環境指的是window物件,在Node.js指的是global物件。

ES5之中,全域性物件的屬性與全域性變數是等價的,隱式宣告或者在全域性環境下宣告的變數是掛在全域性物件上的。

ES6規定,var命令,function命令以及隱式宣告的全域性變數,依舊是全域性物件的屬性;而let命令、const命令、class命令宣告的全域性變數,不屬於全域性物件的屬性。

var a = 1;
console.log(window.a) // 1

let b = 1;
console.log(window.b) // undefined

函式的形參

函式的形參,隱藏著在函式一開始聲明瞭這些形參對應的變數。

function a(x,y){}
可以看成  
function a(){
    var x=arguments.length <= 0 || arguments[0] === undefined ? undefined : arguments[0];
    var y=arguments.length <= 1 || arguments[1] === undefined ? undefined : arguments[1];
}

當然在ES6下預設宣告就是用的let了,所以函式a變成:

function a(){
    let x=arguments.length <= 0 || arguments[0] === undefined ? undefined : arguments[0];
    let y=arguments.length <= 1 || arguments[1] === undefined ? undefined : arguments[1];
}

所以在ES6中會有以下幾個問題:

function a(x = y, y = 2) {
  return [x, y];
}

a(); // 報錯,給X賦值時y還未被let宣告。  



function a(x,y) {
  let x;//相當於重複宣告,報錯。
}

原文地址:http://www.jianshu.com/p/f56af9d7abc6 謝謝分享