JavaScript中的變量聲明

在ES5中,變量聲明只有var和function以及隱式聲明三種,在ES6中則增長了let,const,import和class四種。javascript

var


ES5中最原始的變量聲明,用於聲明變量,其實JavaScript是弱類型語言,對數據類型變量要求不太嚴格,因此沒必要聲明每個變量的類型(這就是下面說的隱式聲明,固然這並非一個好習慣),在使用變量以前先進行聲明是一種好的習慣。 java

1.做用域 node

使用var聲明的變量的做用域是函數做用域(在ES5時代,只有函數做用域和全局做用域兩種做用域),在一個函數內用var聲明的變量,則只在這個函數內有效。jquery

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

2.變量聲明提高es6

用var聲明變量時,只要在一個函數做用域內,不管在什麼地方聲明變量,都會把變量的聲明提高到函數做用域的最前頭,因此不管使用變量在變量聲明前仍是聲明後,都不會報錯(固然只是聲明提早,賦值並無提早,因此若是使用在聲明以前,會輸出undefined,但不會報錯)。 瀏覽器

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

隱式聲明


當沒有聲明,直接給變量賦值時,會隱式地給變量聲明,此時這個變量做爲全局變量存在。 babel

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

固然要注意,隱式聲明的話就沒有變量聲明提早的功能了,因此下面的使用是會報錯的。 markdown

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;//至關於重複聲明,報錯。
}
相關文章
相關標籤/搜索