
1. 語句和表達(dá)式
Javascript中語句相當(dāng)句子,表達(dá)式相當(dāng)于短語,例如:var a = 1;,整個這一個包含兩個表達(dá)式var a, a = 1 , 合起來組成了一個語句
1.1 語句結(jié)果值
對于每一個語句存在一個結(jié)果值,通常結(jié)果值是undefined,實際上結(jié)果是最后一個語句的最后一個外部變量相關(guān)表達(dá)式的值,在Chrome控制臺中可以看到:
// 直接輸出1,因為是操作外部變量a的表達(dá)式
if (true) {
a = 1;
}
// 控制臺輸出undefined,因為a并不是語句外部的變量
if (true) {
var a = 1;
}
// 控制臺輸出2,因為最后一個是語句2,是操作語句2的一個外部變量a的表達(dá)式
if (true) { // 語句1
a = 1;
if (true) { // 語句2
var a = 2;
}
}
// 這個時候控制臺輸出undefined,因為最后一個是語句2,a不是語句2的外部變量
if (true) { // 語句1
a = 1;
if (true) { // 語句2
a = 2;
}
}
如果我們想獲取到當(dāng)前的語句結(jié)果值,可以使用eval函數(shù)獲取,其中ES7規(guī)范提供了新的do表達(dá)式獲?。ú]有進(jìn)行測試)
var b = eval('if(true){ a = 2}');
console.log(b); // 2;
var c = do { if(true) { a = 2}};
console.log(c); // 2;
1.2 表達(dá)式的副作用
表達(dá)式在使用的過程中可能會產(chǎn)生副作用,也就是會改變變量的值
var a = 1;
var b = a++;
console.log(b); // 1
console.log(a); // 2
上面的例子中a首先將值賦給了b,然后進(jìn)行了自增,對自身的值進(jìn)行了改變,這里b只能獲取到a自增之前的值,如果想在一個賦值語句中讓b賦值為a自增后的值,除了在a自增后進(jìn)行再次進(jìn)行賦值以外,可以可以使用,來簡化寫法
var a = 1;
var b = (a++, a);
console.log(b); // 2
console.log(a); // 2;
1.3 代碼塊
- 使用大括號
{}可以創(chuàng)建字面量的對象,也可以創(chuàng)建一個代碼塊,并創(chuàng)建一個標(biāo)簽,結(jié)合break和continue,使得語句運(yùn)行可以跳到這個定義的標(biāo)簽處,如果使用continue label,會繼續(xù)執(zhí)行下一次語句,如果用break label,則會直接跳過當(dāng)前語句
// 如果使用continue
{
label: for(let i = 0; i< 2; i++){
for(let j = 0; j < 2; j++) {
console.log(`i:${i}-j:${j}`);
if(j === 1) {
continue label;
}
}
}
}
// 上面的輸出為
// i:0-j:0
// i:0-j:1
// i:1-j:0
// i:1-j:1
// 如果使用break
{
label: for(let i = 0; i< 2; i++){
for(let j = 0; j < 2; j++) {
console.log(`i:${i}-j:${j}`);
if(j === 1) {
break label;
}
}
}
}
// 上面的輸出為
// i:0-j:0
// i:0-j:1
- 使用大括號
{}在進(jìn)行運(yùn)算的時候,如果大括號在語句的最前端,則它會被解析為一個代碼塊,從而會忽略掉該部分內(nèi)容
{} + '2' === 2; // 語句結(jié)果值為true, 相當(dāng)于執(zhí)行了一元運(yùn)算符 +'2' 操作
- ES6中運(yùn)用大括號
{}可以進(jìn)行解構(gòu)賦值操作
// 對于對象賦值解構(gòu)
var obj = {a: 1, b: 2};
var {a, b} = obj;
console.log(a); // 1
console.log(b): // 2
// 對于函數(shù)參數(shù)解構(gòu)
function f({a, b, c}){
console.log(a, b, c);
}
f({a:1, b: 2, c: 3}); // 1 2 3
1.4 try finally語句
try finally的finally語句中的代碼一定會進(jìn)行執(zhí)行,但是如果finally存在拋出異?;蛘叻祷刂担敲丛镜姆祷刂禃粡U棄/覆蓋,最終結(jié)果為finally中的返回值
function f() {
try {
return 1;
} finally {
// throw Error('err');
return 2;
}
}
console.log(f()); // 2,原本返回值1被覆蓋
1.5 switch語句
switch語句中的case比較為嚴(yán)格比較(===比較),如果需要使用寬松比較(==),可以將switch值設(shè)置為true,在case條件中使用寬松比較
var a = '2';
switch(a) {
case 1:
console.log('case 1');
break;
default:
console.log('default');
}
// 輸出 default
var a = '1';
switch(true) {
case a == 1:
console.log('case 1');
break;
default:
console.log('default');
}
// 輸出 case 1
2. 運(yùn)算符
2.1 優(yōu)先級
運(yùn)算符的優(yōu)先級影響不同運(yùn)算符之間的組合規(guī)則,優(yōu)先級越高的,越先組合在一起,其中&& > || > ?:
var a = false ? 3 : 4 && 5;
console.log(a); // 5 由于&&優(yōu)先級大于?: , 所以相當(dāng)于false ? 3: (4 && 5)
2.2 關(guān)聯(lián)
運(yùn)算符的關(guān)聯(lián)影響相同運(yùn)算符之間的組合規(guī)則,&& 和|| 是左關(guān)聯(lián),也就是左邊先組合,?: 是右關(guān)聯(lián),也就是右邊先組合
var a = 1 && 2 && 3;
console.log(a); // 3 相當(dāng)于 (1 && 2) && 3
var a = true ? true: false ? false: true
console.log(a); // true 相當(dāng)于 true ? true: (false ? false: true);
3. 其他
3.1 自動分號ASI
Javascript為了提高容錯率,如果在每一行換行處缺少分號,會自動在換行后增加分號
3.2 函數(shù)中的arguments
函數(shù)中的arguments在非嚴(yán)格模式下和形參存在關(guān)聯(lián),修改后會對形參進(jìn)行修改,在嚴(yán)格模式下,則不存在關(guān)聯(lián)關(guān)系,不會影響到形參的值
function f(a) {
arguments[0] = 2;
console.log(a);
console.log(arguments[0]);
}
f(1); // 2 2
'use strict'
function f(a) {
arguments[0] = 2;
console.log(a);
console.log(arguments[0]);
}
f(1); // 1 2
3.3 宿主環(huán)境
宿主環(huán)境是指Javascript的運(yùn)行環(huán)境,不同的宿主環(huán)境中會有不同的內(nèi)置對象,例如:瀏覽器中有HTMLDivElement,而node環(huán)境中則沒有;瀏覽器中全局變量為window,而node環(huán)境中為global
同時控制臺對象(console對象)也是根據(jù)宿主環(huán)境進(jìn)行變化的,瀏覽器中是開發(fā)者控制臺輸出,而node中是標(biāo)準(zhǔn)的輸出
3.4 全局DOM變量
在瀏覽器環(huán)境中,HTML的節(jié)點(diǎn)中定義id屬性,會創(chuàng)建一個全局變量
// html中
<input id="a"/>
// js中
console.log(a); // 會直接打印該DOM節(jié)點(diǎn)對象
3.5 原生函數(shù)
Javascript的內(nèi)置函數(shù),不建議直接操作內(nèi)置函數(shù)(包括增加新的屬性和原型鏈擴(kuò)展),如果實在需要進(jìn)行擴(kuò)展,可以加上判斷條件后會相對安全
(function(){
if(!Array.prototype.a) {
Array.prototype.a = function() {};
}
})()
但是即使加上判斷條件也無法確保該方法執(zhí)行結(jié)果一定為我們所想要的擴(kuò)展方法,所以,可以加上測試代碼來確保擴(kuò)展方法的正確性。
(function(){
if(!Array.prototype.a) {
Array.prototype.a = function() { return 1};
return;
} else { // 例如我們想要a方法返回1
var a = [];
if (a.a === 1) { // 等于1的時候滿足我們的需求
return;
}
}
throw Error('error');
})()
3.6 <script>元素
<script>元素之間共享一個全局變量:
<script>
var a = 1;
</script>
<script>
console.log(a); // 1
</script>
但是變量聲明的提升作用不會跨<script>提升到最前:
<script>
console.log(a); // 拋出異常
</script>
<script>
var a = 1;
</script>
每個運(yùn)行過程是獨(dú)立的,一個出錯不會導(dǎo)致其他的停止運(yùn)行:
<script>
throw Error('error');
</script>
<script>
var a = 1;
console.log(1); // 仍然會執(zhí)行輸出結(jié)果
</script>
script中的語句不能包含</script>字符串,否則會作為script的結(jié)束標(biāo)簽解析而結(jié)束
3.7 保留字
Javascript中關(guān)鍵字,預(yù)留關(guān)鍵字,基本類型等不能作為變量名使用
3.8 限制
Javascript對于一些數(shù)據(jù)存在限制,例如字符串的最大長度,函數(shù)的參數(shù)最大個數(shù),變量名的最大長度,阻塞的最大時常等
4. 參考
《你不知道的Javascript(中卷)》