深究:javascript中var、let、const的區別


爲了更好的對它們的區別進行理解,我們必須要先理解幾個概念

一 作用域

從ES6開始,js作用域有三種:全局作用域、函數作用域(也叫局部作用域)和塊級作用域。其中塊級作用域是自從ES6引入了let和const之後纔有的作用域,即塊級作用域在ES5和ES5之前是沒有的

1 全局作用域

  • 在script腳本中定義的最外層函數和變量都屬於全局作用域
  • 通過script引入的js文件中最外層定義的函數和變量都屬於全局作用域
  • 同一個html文件中無論是script標籤裏面的腳本還是引入的js文件腳本,都屬於同一個全局作用域
  • 全局作用域在html頁面打開時創建,頁面關閉時銷燬
  • 在全局作用域中有一個全局對象 window(代表的是一個瀏覽器的窗口,由瀏覽器創建,所以如果在node中執行腳本,則沒有這個對象)
  • 按照script標籤在html中引入的順序,後面定義的變量或函數在之前的script中無法訪問
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
    <title>Title</title>
</head>

<script type="text/javascript">

var a = 1;
let b = 2;
console.log(c) // 報錯:c is not defined

</script>

<script type="text/javascript">

var c = 3;
console.log(a) // 1
console.log(b)	// 2

</script>

<html>

2 函數作用域

  • 在函數內部就是局部作用域,這個代碼的名字只在函數的內部起作用
  • 調用函數時創建函數作用域,函數執行完畢之後,函數作用域銷燬
  • 每調用一次函數就會創建一個新的函數作用域,它們之間是相互獨立的
var a = 10;
function test(){
	var a = 20;
	console.log('內a:' + a); // 內a:20
}
test();
console.log('外a:' + a); // 外a:10

3 塊級作用域

  • 從ES6開始引入了const和let,即通過const和let引入了塊級作用域的概念
  • 塊級作用域只對const和let有效,對var無效
  • 可以理解成一個{ }括起來的範圍就是一個塊級作用域
  • 塊級作用域裏面的let和const聲明的變量只在當前塊級作用域有效
{let a = "hello"}
console.log(a); // 報錯:a is not defined

二 變量提升

瀏覽器在運行js代碼之前會進行預解析,首先解析函數的聲明和定義的變量,把它們初始化爲undefined,解析完之後再對函數、變量進行運行、賦值等。 不論var聲明的變量處於當前作用域的第幾行,都會提升到作用域的頂部

注意:

  • 這個只對var聲明的變量或者函數有效
  • 如果函數未通過var、let或者const聲明,而是直接定義,會將該函數整個進行提升
  • 如果函數通過let或者const聲明也不會進行變量提升
  • 提升到作用域頂部的作用域,指的是聲明變量的作用域(全局作用域或者函數作用域)
  • 同一個html文件引入的不同script腳本中的變量進行提升的時候,不會提升到整個全局作用域的頂部,而是提升到當前script腳本文件的最頂部
console.log(foo); // undefined
var foo = 2;

// 相當於
var foo;  // 聲明且初始化爲undefined
console.log(foo);
foo = 2;
// let定義的變量不會進行變量提升

console.log(a) // 報錯:a is not defined
let a = function (){}
// const定義的變量不會進行變量提升

console.log(b) // 報錯:b is not defined
const b = 1
// let定義的變量不會進行變量提升

console.log(a) // undefined
var a = function (){}
// 整個函數體進行了提升

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

// 相當於
var a = function(){}
console.log(a)
// b函數內的a函數進行了提升,所以a=10是對函數作用域內的a進行了賦值,沒有影響全局作用域中的a
// a函數行提升的時候,因爲它是局部作用域內的函數,所以並不是對全局的a進行了重新賦值

var a = 1;
function b() {
	a = 10;
	return;
	function a(){}
}
b();
console.log(a) // 1
<!--雖然兩個script腳本屬於同一個全局作用域,但是第二個script腳本中的a函數仍然無法在第一個script腳本中訪問 -->

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
    <title>Title</title>
</head>

<script type="text/javascript">
console.log(a) // 報錯:a is not defined
</script>

<script type="text/javascript">
console.log(a) // [Function: a]
function a (){}
</script>

<html>

三 區別

有了以上的基礎知識,我麼再理解三者的區別就更加容易了

1 掛載window的區別

如果在瀏覽器中執行js腳本,有window對象的話,var聲明的變量會掛載在window上,而let和const聲明的變量不會

var a = 100;
console.log(a, window.a);    // 100 100

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

const c = 1;
console.log(c, window.c);    // 1 undefined

2 變量提升的區別

在上面的變量提升章節中講過,只有var聲明的變量或者函數存在變量提升,let和const聲明的變量或者函數是不存在變量提升的。這裏就不再贅述了,如果還有不理解的可以返回變量提升章節查看

3 作用域的區別

let和const聲明形成塊級作用域,而var聲明不會。只要塊級作用域內存在let/const聲明,它所聲明的變量就“綁定”這個塊級作用域內,不再受外部的影響。而且,在塊級作用域內,使用let/cosnt命令聲明變量之前,該變量都是未聲明的狀態,儘管代碼塊外可能已經聲明瞭同名全局變量(該現象的產生是因爲:暫存死區,本篇文章未做展開講解,有興趣的可查閱其它資料)

{
    var a = 100;
    let b = 10;
}
console.log(a) // 100
console.log(b) // 報錯:b is not defined
// 暫存死區

var tmp = 123
if (true) {
  tmp = 'abc' // 報錯:tmp is not defined
  let tmp
}

4 重複聲明同一變量的區別

同一作用域下let和const不能聲明同名變量,而var可以

var a = 100;
console.log(a); // 100

var a = 10;
console.log(a); // 10
let a = 100
let a = 10 // 報錯:Identifier 'a' has already been declared
let a = 100
const a = 10 // 報錯:Identifier 'a' has already been declared

5 let 和 const 區別

從上面看來,好像let和const好像沒啥區別,那當然不會,那麼它倆有什麼區別呢?一下就是const和let不同的一些地方:

  • const用來聲明常量,一旦聲明,其值不能改變
  • 一旦聲明必須賦值,可以賦值null,undefined
  • 如果聲明的是複合類型數據,const聲明的常量指向其內存中地址,const命令只是保證指向的地址不變,而不保證該地址內的數據不變
const a = undefined
const b = null
const c = 2

console.log(a) // undefined
console.log(b) // null
console.log(c) // 2
const a = [1]
a.push(2)
console.log(a) // [1,2]

const b = {
	name: 'huzhenv5'
}
b.name = '三千'
b.id = 1
console.log(b) // { name: '三千', id: 1 }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章