7個令人驚訝的JavaScript特性詳解
從任何一個代碼塊中 break
你應該已經(jīng)知道你可以從任意循環(huán)中 break 和 continue —— 這是一個相當標準的程序設計語言結(jié)構(gòu)。但你可能沒有意識到,你可以給循環(huán)添加一個 label ,然后跳出任意層循環(huán):
outer: for(var i = 0; i < 4; i++) {
while(true) {
continue outer;
}
}label 特性同樣適用于 break 和 continue。你在 switch 語句中肯定見過 break:
switch(i) {
case 1:
break;
}順便說一句,這是為什么 Crockford 建議你的 case 不應該縮進 —— 因為 break 跳出的是 switch 而不是 case,但是我認為縮進 case 的可讀性更好。你也可以給 switch 語句添加 label:
myswitch: switch(i) {
case 1:
break myswitch;
}你可以做的另一件事是創(chuàng)建任意塊(我知道你可以在 C# 里面這么寫,我期望其他語言也可以)。
{
{
console.log("I'm in an abritrary block");
}
}因此,我們可以把 label 和 break 放在一起,用來從任意代碼塊中跳出。
outer: {
inner: {
if (true) {
break outer;
}
}
console.log("I will never be executed");
}注意到,這只適用于 break —— 因為你只能在一個循環(huán)中 continue。我從未見過 label 被使用在 JavaScript 中,我想知道為什么 —— 我想可能因為如果我需要 break 兩層,說明把這個代碼塊放在一個函數(shù)里可能更好,這樣我可以使用一個單層的 break 或者一個提前的 return 來達到同樣的目的。
盡管如此,如果我想要保證每個函數(shù)只有一個 return 語句(這不是我的菜),那么我可以使用帶 label 的 brock。例如,看下面這個多個 return 語句的函數(shù):
function(a, b, c) {
if (a) {
if (b) {
return true;
}
doSomething();
if (c) {
return c;
}
}
return b;
}而如果使用 label:
function(a, b, c) {
var returnValue = b;
myBlock: if (a) {
if (b) {
returnValue = true;
break myBlock;
}
doSomething();
if (c) {
returnValue = c;
}
}
return returnValue;
}還有另一種選擇,用更多代碼塊……
function(a, b, c) {
var returnValue = b;
if (a) {
if (b) {
returnValue = true;
} else {
doSomething();
if (c) {
returnValue = c;
}
}
}
return returnValue;
}我最喜歡原版,然后是使用 else 的版本,最后才是使用 label 的版本 —— 但是,這可能是因為我的寫碼習慣?
解構(gòu)一個已存在的變量
首先,有個怪異的寫法我無法解釋。貌似 ES3 中你可以添加一個小括號到一個簡單的賦值語句左邊的變量上,而這樣寫不會有問題:
var a; (a) = 1; assertTrue(a === 1);
如果你能想到為什么這樣寫可以,請在底下評論!
解構(gòu)的過程是一個將變量從一個數(shù)組或者一個對象中拉取出來的過程。最常見的是以下例子:
function pullOutInParams({a}, [b]) {
console.log(a, b);
}
function pullOutInLet(obj, arr) {
let {a} = obj;
let [b] = arr;
console.log(a, b);
}
pullOutInParams({a: "Hello" }, ["World"]);
pullOutInLet({a: "Hello" }, ["World"]);而你可以不使用 var 或 let 或 const。對數(shù)組你可以讓下面的代碼如你的期望運行:
var a; [a] = array;
但是,對于對象,你必須將整個賦值語句用小括號括起來:
var a;
({a} = obj);必須這樣寫的理由是,不加括號無法區(qū)分代碼是解構(gòu)賦值還是塊級作用域,因為你可以使用匿名代碼塊而 ASI(automatic semi-colon insertion,自動插入括號)會將變量轉(zhuǎn)成可以執(zhí)行的表達式(如下面的例子所示,能夠產(chǎn)生副作用……),這樣就產(chǎn)生了歧義。
var a = {
get b() {
console.log("Hello!");
}
};
with(a) {
{
b
}
}回到原始的例子,我們給我們的賦值語句里的變量加了圓括號 —— 你可能認為它也適用于解構(gòu),但它不是。
var a, b, c;
(a) = 1; //這句不是變量解構(gòu)
[b] = [2];
({c} = { c : 3 });對數(shù)值進行解構(gòu)
解構(gòu)的另一個方面你可能也沒有意識到,屬性名不是必須要是不帶引號的字符串,它們也可以是數(shù)值:
`var {1 : a} = { 1: true };`或者帶引號的字符串:
`var {"1" : a} = { "1": true };`或者你可能想要用一個計算的表達式作為名字:
var myProp = "1";
var {[myProp] : a} = { [myProp]: true };這會很容易寫出造成困惑的代碼:
var a = "a";
var {[a] : [a]} = { a: [a] };類聲明是塊級作用域的
函數(shù)聲明會被提升,意味著你可以將函數(shù)聲明寫在函數(shù)調(diào)用之后:
func();
function func() {
console.log("Fine");
}函數(shù)表達式與此相反,因為賦值一個變量的時候,變量聲明被提升,但是具體賦值沒有被提升。
func(); // func 被聲明, 但是值為 undefined, 所以這里拋出異常: "func is not a function"
var func = function func() {
console.log("Fine");
};類(Classes)成為 ES6 流行的部分,并且已被廣泛吹捧為函數(shù)的語法糖。所以你可能會認為以下代碼是可以工作的:
new func();
class func {
constructor() {
console.log("Fine");
}
}然而,盡管它基本上是語法糖,但前面的代碼是不能工作的。這實際上等價于:
new func();
let func = function func() {
console.log("Fine");
}這意味著我們的 func 調(diào)用在暫時性死區(qū)(TDZ),這會導致引用錯誤。
同名參數(shù)
我認為不可能指定同名的參數(shù),然而,卻可以!
function func(a, a) {
console.log(a);
}
func("Hello", "World");
// 輸出 "World"在嚴格模式下不行:
function func(a, a) {
"use strict";
console.log(a);
}
func("Hello", "World");
// 在 chrome 下報錯 - SyntaxError: Strict mode function may not have duplicate parameter namestypeof 不安全
在 ES6 之前,眾所周知使用 typeof 總是能安全地找出某個變量的定義,不管它是否被聲明:
if (typeof Symbol !== "undefined") {
// Symbol 可用
}
// 下面的代碼拋異常,如果 Symbol 沒有被聲明
if (Symbol !== "undefined") {
}但是,現(xiàn)在這個在不使用 let 或者 const 聲明變量的時候才好使。因為有了 TDZ,會導致變量未聲明時產(chǎn)生引用錯誤。從本質(zhì)上講,變量被提升到塊級作用域的開始,但是在聲明前的任何訪問都會產(chǎn)生引用錯誤。在 JSHint 的作用域管理中,我必須記錄一個變量的用法,如果它使用 let 或者 const 聲明于當前塊級作用域或者它的父級作用域,提前訪問就會有引用錯誤。而如果是使用 var 語句聲明的,那么它就是可用的,但是 JSHint 會給出一個警告,而如果它沒有被聲明,那么它使用全局作用域,JSHint 可能會有另外一種警告。
if (typeof Symbol !== "undefined") {
// Symbol 不可用,產(chǎn)生 reference error
}
let Symbol = true;新數(shù)組
我總是避免使用 new Array 構(gòu)造函數(shù),一部分原因是因為它的參數(shù)既可以是一個長度又可以是一個元素列表:
new Array(1); // [undefined] new Array(1, 2); // [1, 2]
但是,一個同事最近使用它遇到了一些我以前沒有見過的東西:
var arr = new Array(10);
for(var i = 0; i < arr.length; i++) {
arr[i] = i;
}
console.dir(arr);上面的代碼產(chǎn)生一個 0 到 9 的數(shù)組。然而,如果將它重構(gòu)為使用 map:
var arr = new Array(10);
arr = arr.map(function(item, index) { return index; });
console.dir(arr);現(xiàn)在我得到了一個數(shù)組,第 8 個元素等于 8,但是其他所有的值依然是 undefined??匆幌?map 的 polyfill 實現(xiàn),它循環(huán)每一個元素(這是為什么 index 是正確的),但是它使用的是 in 來檢查一個屬性是否被設置。你如果使用數(shù)組直接量,也會得到同樣的結(jié)果。
var arr = []; arr[9] = undefined; // or var arr = []; arr.length = 10;
以上就是7個令人驚訝的JavaScript特性詳解的詳細內(nèi)容,更多關于JavaScript特性的資料請關注腳本之家其它相關文章!
相關文章
6種JavaScript判斷對象自身為空的方法小結(jié)
這篇文章主要為大家詳細介紹了6種JavaScript判斷對象自身為空的方法,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下2023-12-12
JavaScript進制數(shù)之間的互相轉(zhuǎn)換
這篇文章主要介紹了JavaScript進制數(shù)之間的互相轉(zhuǎn)換,進制轉(zhuǎn)換是人們利用符號來計數(shù)的方法,下文基于JavaScript實現(xiàn)進制數(shù)之間的轉(zhuǎn)換,有一定的知識性參考價值,需要的小伙伴可以參考一下2022-05-05
使用Plupload實現(xiàn)直接上傳附件至七牛云存儲
這篇文章主要介紹了使用Plupload實現(xiàn)直接上傳附件至七牛云存儲,需要的朋友可以參考下2014-12-12
JavaScript html5 canvas實現(xiàn)圖片上畫超鏈接
這篇文章主要為大家詳細介紹了JavaScript html5 canvas實現(xiàn)圖片上畫超鏈接,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-10-10
微信小程序自定義navigationBar頂部導航欄適配所有機型(附完整案例)
這篇文章主要介紹了微信小程序自定義navigationBar頂部導航欄適配所有機型(附完整案例),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-04-04

