var
- 函式作用域let
、const
- 区块作用域JavaScript 宣告变数的方式有三种:var、let、const。let
是 ES6 出现後,被用来改善现有语法的宣告方式,在 ES6 未出现之前,网页不存在「区块域」的概念,因此大多数都使用 var
的方式宣告变数,不过这样有个问题,就是用 var
宣告变数会污染到全域变数,使用 let
可以只在部分区域运作。
var 与 let 宣告变数范例:
var a = '无敌哆啦A梦'; // 这里的 a 为全域变数
function doraemon () {
let a = '无敌大雄'; // 这里的 a 为区域变数
a = '变身哆啦美';
console.log(a); // 变身哆啦美
}
doraemon();
console.log(a) // 无敌哆啦A梦
从范例来看,结果是「变身哆啦美」与「无敌哆啦A梦」,这里的观念在於只存在大括号里这件事。
第一个 console.log(a)
是「变身哆啦美」,原因是因为在里面我们重新给 a
赋予了一个「变身哆啦美」的值,而到了第二个 console.log(a)
的时候,答案却变成「无敌哆啦A梦」,这是因为function doraemon
里面的 a
只会存活在 {}
这个大括号里,因此里面我们替 a
赋予的值,没有更改到外面。
接着实际操作 let
与 var
,看看区块作用域与函式作用域的差别:
// var 宣告
// 宣告变数 a
var a = 10;
if(true){
var b = 20;
}
// 输出: a = 10
console.log(a)
// 输出: b = 20
console.log(b)
// let 宣告
// 宣告变数 a
var a = 10;
if(true){
let b = 20;
}
// 输出: a = 10
console.log(a)
// 因为变数 b 使用 let 宣告,离开 if 区域便无法被存取
console.log(b) //会显示没有定义
上面的例子可以看到 var
与 let
的差异在於区块的概念,let
在 if
区域里面宣告的变数只留在 if
括号里,并且在同一个区块内,let
与 const
不能重复宣告变数,var
则可以重复宣告。
var
区域污染的现象在回圈尤其明显,这是一个 var
与 for
的例子:
<ul class="list">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
const list = document.querySelectorAll('.list li').length;
for(var i = 0; i < list; i++){
document.querySelectorAll('.list li')[i].addEventListener('click', function(){
alert(i+1);
})
}
以上范例程序码运作方式为,透过点击了解我们点击到的是哪一个按钮,也就是当我们点击 <li>1</li>
的时候,我们预期他会显示 1,点击 <li>2</li>
的时候预期会显示 2 。
但在这个范例中会发现,不管我们点击哪一个,alert
都会显示 4,这是因为值被「全域变数」影响。for
回圈跑完以後,会直接显示全部跑完的结果,而不是个别处理。在这个过程中,var
已经直接将 i
宣告为「全域变数」,并不断透过 for
进行累加,因此全部跑完的结果是 4,但这不是我们预期的结果。
那要如何让以上程序范例码成为我们预期的呢?这时候使用 let
便可以达到个别处理的结果。
const list = document.querySelectorAll('.list li').length;
for(let i = 0; i < list; i++){
document.querySelectorAll('.list li')[i].addEventListener('click', function(){
alert(i+1);
})
}
在这边我们把 var
改成 let
,上面说到 let
有区域特性,它只会在大括号里运作,因此每一次的执行结果都会显示一次。
当我们点击 <li>1</li>
的时候,就会执行一次括号内的程序码,在 let i = 0
的作用域里面(括号内)执行 i++
,因此 alert
此时会跳出 1,当我们点击 <li>2</li>
的时候,就会再执行一次括号内的程序码,在 let i = 1
的作用域里面执行 i++
,alert
於是跳出 2,然後结束。依此类推,最後即是我们所预期的呈现。
const
是宣告常数,常用在一些不能被变更的变数,譬如:url 网址。确定不再做更动的时候,可以使用 const
方法。
尝试执行以下程序码呈现的结果:
const me = '齐天大圣孙悟空';
let me = '猪八戒';
// Uncaught SyntaxError: Identifier 'me' has already been declared
在这边会显示上面这个 me,已经有被宣告变数,由上面的范例码可以知道,const
不能再被重新宣告变数覆盖。
另外,let
可以直接宣告变数,const
则是一定要有值。
let a;
// undefined
const b;
// Uncaught SyntaxError: Missing initializer in const declaration
let
直接宣告变数,只是显示没有东西,而直接宣告变数的 const
,会显示缺少值。
虽然使用 const
宣告变数就无法更动,但如果使用 const
宣告物件,里面的属性仍然是可以更动的,因为物件有传参考的特性,因此依然可以修改。
const color = {
light: 'white',
dark: 'black'
};
color.midtones = 'grey';
// {light: "white", dark: "black", midtones: "grey"}
最後的结果,midtones: "grey"
是能够被加上去的。
有一点要注意,已经宣告的物件则无法再被重新宣告。
let newColor = {
light: 'yellow',
dark: 'crimson'
};
color = newColor // 错误
如果不希望 const
宣告的物件被修改,还有另一项法宝 freeze()
。freeze()
的功能是防止新增属性或是属性遭到修改,就像它英文的意思一样,「冻结」属性。
const color = {
light: 'white',
dark: 'black'
};
Object.freeze(color);
color.midtones = 'grey';
// {light: "white", dark: "black"}
同样的范例,可以看到与上面的结果不同,在物件下方添加 freeze()
,属性 midtones
则无法被加入,於是可以防止物件被添加或修改。
参考资料:
ES6 开始的新生活 let, const
<<: Day 3 Swift语法-基础篇(1/3)-基本运算符及字串
>>: Day 15 - 使用 Helm 打包 Kubernetes 应用程序
(这个标题有点耸动跟钓鱼,但不知道为什麽我就是很想用它,在文章开头先讲明。) 即使已经证实了苹果在i...
在介绍过监控、yaml 控管、网路的端点暴露与附载平衡後,官方有给我们一些在,生产环境部署的建议。透...
Youtube 频道:https://www.youtube.com/c/kaochenlong ...
Provider Provider封装了 InheritedWidget 功能,提供更高效且易懂的使...
基本上大叔宅男不是很想放男团K-pop, XD 本篇会提到的 定义方法 回圈(loop) 定义方法 ...