從新認識JavaScript(一)

上一篇:從新認識JavaScript(零)前端

上一篇文章中,介紹了JavaScript的一些概念,以及在HTML頁面中如何使用JavaScript。這一篇,開始瞭解JavaScript的基礎知識。面試

什麼是變量

變量,就是用於存放數值的容器。瀏覽器

這個數值能夠是一個能夠用於累加計算的數字,或者是一個句子中的字符串。變量的獨特之處在於它存放的數值是能夠改變的。先來看一個簡單的例子:閉包

<button>點我</button>
複製代碼
// querySelector返回文檔中與指定選擇器或選擇器組匹配的第一個HTML元素
const button = document.querySelector('button');

button.onclick = function () {
    /* prompt方法用於顯示可提示用戶進行輸入的對話框,返回用戶輸入的字符串 取消返回null */
    let name = prompt('你叫什麼名字?');
    alert(`歡迎你,${name}!`);
};
複製代碼

在這個例子裏面,第一行代碼會彈出一個輸入框,讓用戶輸入一個名字,而後將這個名字存在一個變量中。第二行代碼會彈出一個帶有剛纔輸入名字的信息。變量name則存儲了剛纔用戶輸入的名字。函數

變量的另外一個特性是能夠存儲任何東西,不止是數字和字符串。變量能夠存儲更復雜的數據,甚至是函數。post

這裏仍有一個重要的概念須要區分。變量不是數值自己,它僅僅是一個用於存儲數值的容器。ui

聲明變量

想要使用一個變量,那麼第一件事固然是建立它。更專業的說法是聲明一個變量。聲明一個變量的方法是在varlet關鍵字後面加上這個變量的名字:this

var myName;
let myAge;
複製代碼

提示:在JavaScript中,全部代碼指令都會以;結尾。若是忘記加分號,你的單行代碼可能執行正常,可是多行代碼在一塊兒可能會執行出錯。因此最好養成主動以分號區分代碼結尾的習慣。spa

能夠經過使用變量的方式來驗證這個變量的數值是否在執行環境中已經存在。例如:設計

myName;
myAge;
複製代碼

以上這兩個變量並無數值,它們是空的容器。當使用這兩個變量時,會獲得一個undefined的返回值。若是使用一個不存在的變量,則會獲得一個報錯信息。

提示:「一個變量存在,但它沒有數值」和「一個變量不存在」徹底是兩回事。不存在乎味着沒有存放數值的容器,另外一個則意味着這是一個空的容器。

初始化變量

一旦聲明瞭一個變量,就可使用它。方法是在變量名後面跟上一個=,再在後面加上數值:

myName = '伊麗莎不白';
myAge = 32;
複製代碼

如今再使用這兩個變量,會發現它們已經存儲了剛剛賦於它們的數值。一樣,也能夠在聲明變量的時候初始化變量:

let myName = '伊麗莎不白';
複製代碼

var 與 let 的區別

這是JavaScript的一個歷史遺留問題,一樣也是絕大多數前端開發面試必問的一個問題。值得認真對待。

最初在建立JavaScript時,是隻有var的。在大多數狀況下,這種方法能夠接受,但有時在工做方式上會有一些問題:它的設計會使人困惑。所以let是現代版本JavaScript建立的一個新關鍵字,用於建立與var工做方式有些不一樣的變量,解決了過程當中的問題。

下面解釋這些差別。

定義

首先來看varlet的定義。

  • var聲明語句聲明一個變量,並可選的將其初始化爲一個值。
  • let語句聲明一個塊級做用域的本地變量,而且可選的將其初始化爲一個值。

描述

var變量聲明,不管發生在什麼地方,都在執行任何代碼以前處理。用var聲明的變量的做用域是它當前的執行上下文,能夠是嵌套的函數,也能夠是聲明在任何函數外的變量。若是從新聲明一個變量,這個變量不會丟失它的值。

變量提高

因爲變量聲明老是在任意代碼執行以前處理的,因此在代碼中任意位置聲明變量老是等效於在代碼開頭聲明。這意味着能夠在變量聲明以前使用變量:

myName = '伊麗莎不白';
var myName;

/* 能夠理解爲 var myName; myName = '伊麗莎不白'; */
複製代碼

所以,建議始終在做用域頂部聲明變量(全局代碼的頂部和函數代碼的頂部),這能夠清楚的知道哪些變量是函數做用域,哪些變量在做用域鏈上解決。

重要的是,提高僅影響變量聲明,而不會影響其數值的初始化:

var a = 'A';
// => A
var b = a;
// => A
複製代碼
var b = a;
// => undefined
var a = 'A';
// => A
複製代碼

做用域限制

聲明變量的做用域限制在其聲明位置的上下文中:

function init () {
    var a = 1;
}

init();

console.log(a);
// => ReferenceError,變量a做用域在init函數內部,未在init函數外部聲明
複製代碼

重複聲明

能夠在代碼中根據須要屢次聲明名稱相同的變量:

var myName = '伊麗莎不白';
var myName = '不白';
// => 不白
複製代碼

let容許聲明一個做用域被限制在塊級中的變量、語句或表達式。與var不一樣的是,let聲明的變量只能是全局或者整個函數塊的。

做用域規則

let聲明的變量只在其聲明的塊或子塊中可用,這一點與var類似。兩者最主要的區別在於var聲明的變量的做用域是整個封閉函數。

function varTest () {
    var a = 1;
    if (true) {
        // 變量提高,a爲相同的變量
        var a = 2;
        console.log(a);
        // => 2
    }
    console.log(a);
    // => 2
}

function letTest () {
    let a = 1;
    if (true) {
        // a爲不一樣的變量
        let a = 2;
        console.log(a);
        // => 2
    }
    console.log(a);
    // => 1
}
複製代碼

簡化內部函數代碼

當用到內部函數時,let會讓代碼變得更簡單。下面用一段很是常見的面試代碼來舉例:

for (var i = 0; i < 5; i++) {
    var btn = document.createElement('button');
    btn.innerHTML = `按鈕${i}`;
    
    var j = i;
    btn.onclick = function (evt) {
        console.log(`點擊了按鈕${j}`);
    };
}
複製代碼

上面這段代碼的意圖是建立5個按鈕,點擊不一樣的按鈕可以打印出當前按鈕的序號。然而結果將老是打印出點擊了按鈕5。由於j是函數級變量,5個內部函數都指向了同一個j,而j最後一次的賦值是5。

若是想要實現預期的效果,只須要將j的聲明語句改爲let。這樣j變成了塊級域,因此5個內部函數指向了不一樣的j。

在程序或函數的頂層,let不會像var同樣在全局對象上建立一個變量,例如:

var a = 1;
let b = 2;
console.log(this.a);
// => 1
console.log(this.b);
// => undefined
複製代碼

模仿私有接口

在處理構造函數的時候,能夠經過let聲明而不是閉包來建立私有接口。

{
    let _age = 32;
    
    var Person = function () {
        this.name = '伊麗莎不白';
    };
    
    Person.property.growUp = function () {
        _age++;
        console.log(_age);
    };
}

var p = new Person();
p.growUp();
// => 33
console.log(_name);
// => ReferenceError
複製代碼

這裏須要注意一點,由於_name是被Person的實例所持有的,因此在使用中會存在內存泄漏的風險。閉包也是同理。

重複聲明

在同一個函數或塊做用域中重複聲明一個變量會引發SyntaxError。

let myName;
let myName;
// => SyntaxError
複製代碼

switch語句中只有一個塊做用域,可能會所以引起錯誤:

let a = 1;
switch (a) {
    case 0:
        let b;
        break;
    case 1:
        let b;
        // => SyntaxError
        break;
}
複製代碼

然而,一個嵌套在case字句中的塊會建立一個新的塊做用域,不會產生上面提到的錯誤:

let a = 1;
switch (a) {
    case 0: {
        let b;
        break;
    }
    case 1: {
        let b;
        // => SyntaxError
        break;
    }
}
複製代碼

暫存死區

let被建立在包含該聲明的做用域的頂部,稱之爲「提高」。與var不一樣,經過let聲明的變量直到它們的定義被執行時才初始化。該變量處在一個自塊頂部到初始化處理的「暫存死區」中,這之間對變量的訪問會形成錯誤:

console.log(a);
// => undefined
console.log(b);
// => ReferenceError
var a = 1;
let b = 2;
複製代碼

let後面跟一個函數傳遞的參數時將致使循環內部報錯:

function go (n) {
    for (let n of n.a) {
        console.log(n);
    }
}

go({ a: [1, 2, 3] });
// => ReferenceError
複製代碼

更新變量

一旦變量賦值,還能夠經過簡單的給它一個不一樣的值來更新它。

let myAge = 32;
myAge = 18;
// => 18
複製代碼

變量類型

能夠爲變量設置不一樣的數據類型,例如:

let myName = '伊麗莎不白';
// => String
let myAge = 32;
// => Number
let isAlive = true;
// => Boolean
let myTels = [10086, 10010];
// => Array
let myCat = {
    name: 'Pie',
    age: 4
}
// => Object
複製代碼

動態類型

與C或Java不一樣,JavaScript是一種「動態類型語言」,這意味着在使用過程當中不須要爲變量指定一個數據類型。

聲明一個變量並賦給它一個帶引號的值,那麼瀏覽器將會知道它是一個字符串:

let myName = '伊麗莎不白';
複製代碼

即時包含數字,依然是一個字符串:

let myAge = '32';
// => String
myAge = 32;
// => Number
複製代碼

總結

到如今爲止,對變量已經有了一個較爲全面的認識,尤爲是對varlet的使用。下一篇文章將繼續深刻JavaScript,介紹數字與字符串。

下一篇:從新認識JavaScript(二)

相關文章
相關標籤/搜索