摘要:表達式沒有返回值,因此返回結果是。并不改變表達式的結果,只要讓表達式不返回值按慣例我們用來獲得這主要源自語言,當然使用或其他表達式也是可以的。不是數字的數字如果數學運算的操作數不是數字類型,就無法返回一個有效的數字,這種情況下返回值為。
這里的內容是讀書筆記,僅供自己學習所用,有欠缺的地方歡迎留言提示。
第一部分 類型和語法第1章 類型
ECMAScript語言類型包括Undefined、Null、Boolean、String、Number和Object。
類型:對語言引擎和開發人員來說,類型是值得內部特征,它定義了值得行為,以使其區別于其他值。
喜歡強類型(又稱靜態類型)語言得人也許回認為“類型”一詞用在這里不妥。
1.1 類型
強制類型轉換是JavaScript開發人員最頭疼得問題之一。
1.2 內置類型
JavaScript有七種內置內容(后面跟著typeof的類型值):
空置 null ? ? "object"
未定義 undefined ? ? "undefined"
布爾值 boolean ? ? "boolean"
數字 number ? ? "number"
字符串 string ? ?"string"
對象 object ? ? "object"
符號 symbol (ES6中新增) ? ?"symbol"
除了對象值類,其他統稱為“基本類型”,可以用typeof運算符來查看值得類型,它返回得是類型的字符串值,有意思的是,這七種類型和它們的字符串值并不一一對應。最特殊的就是null,但這個bug在JavaScript中存在了將近二十年,也許永遠也不會修復了,因為這牽涉到了太多的Web系統,“修復”它會產生更多的bug,令許多系統無法正常工作。
所以需要使用符合條件來檢測null值得類型:
let a = null; (!a && typeof a === "object"); // true
function和數組實際上都是object的一個“子類型”,所以用typeof獲取類型值時,返回都是"object"。
tip:判斷是否為null,用(!a && typeof a === "object")來判斷。
1.3 值和類型
JavaScript中的變量是沒有類型的,只有值才有。變量可以隨時持有任何類型的值。
換個角度來理解就是,JavaScript不做“類型強制”;也就是說,語言引擎不要求變量總是持有與其初始值同類型的值。
在對變量執行typeof操作時,得到的結果并不是該變量的類型,而是該變量持有的值的類型。
typeof typeof 42; // "string"
typeof 42首先返回字符串"number",然后typeof "number" 返回"string"。
1.3.1 undefined和undeclard
變量在未持有值的時候為undefined。此時typeof返回"undefined":
undeclared(未聲明),undefined與undeclared是完全不一樣的。
已在作用域中聲明但還沒有賦值的變量,是undefined的;相反,還沒有在作用域中聲明過的變量,是undeclared的。
let a; a; // undefiend b; // ReferenceError: b is not defined
瀏覽器對這類情況的處理很讓人抓狂,"b is not defined"容易讓人誤以為是"b is undefired"。這里再明確一次,"undefined"和"is not defined"是兩碼事。此時如果瀏覽器報錯成"b is not find"或者"b is not declared"會更明確。
更讓人抓狂的是typeof處理undeclared變量的方式,如下:
let a; typeof a; // "undefined" typeof b; // "undefined" 而且還沒有報錯
對于undeclared(或者not defined)變量,typeof照樣返回"undefined"。請注意雖然b是一個undeclared變量,但typeof b 并沒有報錯。這是因為typeof有一個特殊的安全防范機制。
與undeclared變量不同,訪問不存在的對象屬性(設置是在全局對象window上)不會產生ReferenceError錯誤。
1.4 小結
JavaScript有七種內置類型:null、undefined、boolean、number、string、object和symbol,可以使用typeof來查看。
變量沒有類型,但它們持有的值有類型。類型定義了值的行為特征。
很多開發人員將undefined和undeclared混為一談,但在JavaScript中它們是兩碼事。undefined是值的一種。undeclared則表示變量還沒有被聲明過。
遺憾的是,JavaScript卻將它們混為一談,在我們試圖訪問"undeclared"變量時這樣報錯:ReferenceError: a is not defined,并且typeof對undefined和undeclared變量都返回"undefined"。
然而,通過typeof的安全防范機制(阻止報錯)來檢查undeclared變量,有時是個不錯的方法。
第2章 值
數組(array)、字符串(string)和數字(number)是一個程序最基本的組成部分,但在JavaScript中,它們可謂讓人喜憂參半。
2.1 數組
和其他強類型語言不同,在JavaScript中,數組可以容納任何類型的值,可以是字符串、數字、對象(object),甚至是其他數組(多維數組就是通過這種方式來實現的)。
對數組生命后即可向其中加入值,不需要預先設定大小。
需要注意的是,使用delete運算符可以將單元從數組中刪除,但是請注意,單元刪除后,數組的length屬性并不會發生變化。
數組通過數字進行索引,但有趣的是它們也是對象,所以也可以包含字符串鍵值和屬性(但這些并不計算在數組長度內)。
類數組
有時需要將數組(一組通過數字索引的值)轉換為真正的數組,這一般通過數組工具函數(如indexOf(..)、contat(..)、forEach(..)等)來實現。
2.2 字符串
字符串經常被當成字符數組。字符串的內部實現究竟有沒有數組炳皓說,但JavaScript中的字符串和字符數組并不是一個回事,最多只是看上去相似而已。
字符串和數組的確很相似,它們都是類數組,都有length屬性以及indexOf(..)(從ES5開始數組支持此方法)和concat(..)方法。
let a = "foo"; let b = ["f", "o", "o"]; a.length; // 3 b.length; // 3 a.indexOf("o"); // 1 b.indexOf("o"); // 1 let c = a.concat("bar"); // "foobar" let d = b.concat(["b", "a", "r"]); // ["f", "o", "o", "b", "a", "r"] a === c; // false b === d; // false 對象、數組存的是地址 a[1] = "O"; b[1] = "O"; a; // "foo" b; // ["f", "o", "o"]
JavaScript中字符串是不可變的,而數組是可變的。并且a[1]在JavaScript中并非總是合法語法,在老版本的IE中就不被允許(現在可以了)。正確的方法應該是a.charAt(1)。
字符串不可變是指字符串的成員函數不會改變其原始值,而是創建并返回一個新的字符串。而數組的成員函數在其原始值上進行操作。
c = a.toUpperCase(); a === c; // false a; // "foo" c; // "FOO"
許多數組函數用來處理字符串很方便。雖然字符串沒有這些函數,但可以通過“借用”數組的非變更方法來處理字符串。
a.join; // undefined a.map; // undefined let c = Array.prototype.join.call(a, "-"); let d = Array.prototype.map.call(a, function(v) { return v.toUpperCase() + "."; }).join(""); c; // "f-o-o" d; // "F.O.O."
另一個不同點在于字符串反轉。數組有一個字符串沒有的可變更成員函數reverse():
a.reverse(); // undefined b.reverse(); // ["o", "o", "f"] // 可惜我們無法"借用"數組的可變更成員函數,因為字符串是不可變的: Array.prototype.reverse.call(a); // 返回值仍然是字符串"foo"的一個封裝對象
tip:通過Array.ptototype.xxxx.call(str)的方式可以讓字符串使用數組函數,但是reverse()不適用。
一個變通(破解)的方法是先將字符串轉換為數組,待處理完后再將結果換回字符串:
let c = a // 將a的值轉換為字符串數組 .split("") // 將數組中的字符進行倒轉 .reverse() // 將數組中的字符拼接回字符串 .join(""); c; // "oof" // 這種方法簡單粗暴,但對簡單的字符串卻完全適用。 // 對于包含復雜字符(Unicode,如星號、多字節字符等)的字符串并不適用。
tip:對于大多數字符串反轉,可以用str.split().reverse().join("")的方法。
2.3 數字
JavaScript只有一種數值類型:number(數字),包括“整數”和帶小數的十進制數。此處“整數”之所以加引號是因為和其他語言不同,JavaScript沒有真正意義上的整數,這也是它一直依賴為人詬病的地方。
JavaSctipt中的“整數”就是沒有小數的十進制數。所以42.0即等同于“整數”42。
2.3.1 數字的語法
JavaScript中的數字常量一般用十進制表示。例如:
let a = 42; let b = 42.3; // 數字前面的0可以省略 let a = 0.42; let b = .42; // 小數點后小數部分最后面的0也可以省略 let a = 42.0; let b = 42.;
特別大和特別小的數字默認用指數格式顯示,與toExponential()函數的輸出結果相同。例如:
let a = 5E10; a; // 50000000000 a.toExponential(); // "5e+10" 這個是字符串 a == a.toExponential(); // true a === a.toExponential(); // false
由于數字值可以使用Number對象進行封裝,因此數字值可以調用Number.prototype中的方法。例如,toFixed(..)方法可以指定小數部分的顯示位數。
toPrecision(..)方法用來指定有效數位的顯示位數。
let a = 42.59; a.toFixed(0); // "43" a.toFixed(1); // "43.6" a.toPrecision(1); // "4e+1" a.toFixed(2); // "42.6" a.toPrecision(2); // "42.59" a.toFixed(3); // "42.590" a.toPrecision(3); // "42.6" a.toPrecision(4); // "42.59" a.toPrecision(5); // "42.590"
2.3.2 較小的數值
二進制浮點數最大的問題(不僅JavaScript,所有遵循IEEE754規范的語言都是如此),是回出現如下情況:
0.1 + 0.2 === 0.3; // false
從數學角度來說,上面的條件判斷應該為true,可結果為什么是false呢?
簡單來說,二進制浮點數中的0.1和0.2并不是十分精確,它們相加的結果并非剛好等于0.3,而是一個比較接近的數字0.3000000000000004,所以條件判斷結果為false。
問題是,如果一些數字無法做到完全精確,是否意味著數字類型毫無用處呢?答案當然是否定的。
在處理帶有小數的數字時需要特別注意。很多(也許是絕大多數)程序只需要處理整數,對打不超過百萬或者萬億,此時使用JavaScript的數字類型是絕對安全的。
那么應該怎樣來判斷0.1+0.2和0.3是否相等呢?
最常見的方法是設置一個誤差范圍值,通常稱為“機器精度”,對JavaScript的數字來說,這個值通常是2^-52。從ES6開始,該值定義在Number.EPSILON中,我們可以直接拿來用,也可以在ES6之前的版本寫polyfill:
function numbersCloseEnoughToEqual(n1, n2) { return Math.abs(n1 - n2) < Number.EPSILON; } let a = 0.1 + 0.2; let b = 0.3; numbersCloseEnoughToEqual(a, b); // true
tip: 小數位運算,因浮點存儲的原因會造成誤差,可以用小于Number.EPSILON來判斷是否正確。
2.3.3 整數的安全范圍
數字的呈現方式決定了“整數”的安全值范圍遠遠小于Number.MAX_VALUE。
能夠被“安全”呈現的最大整數是2^53 - 1,即9007199254740991,在ES6中被定義為Number.MAX_SAFE_INTEGER。最小整數時-9007199254740991,在ES6中被定義為Number.MIN_SAFE_INTEGER。
2.3.4 整數檢測
要檢測一個值是否是整數,可以使用ES6中的Number.isInteger(..)方法。
Number.isInteger(42); // true Number.isInteger(42.0); // true Number.isInteGer(42.3); // false
檢測一個值是否是安全的整數,可以使用ES6中的Number.isSafeInteger(..)方法:
Number.isSafeInteger(Math.pow(2, 53)); // false Number.isSafeInteger(Math.pow(2, 53) - 1); // true
2.3.5 32位有符號整數
雖然整數最大能夠達到53位,但是有些數字操作(如整位操作)只適用于32位數字,所以這些操作中數字的安全范圍就要小很多,變成從Math.pow(-2, 31)到Math.pow(2, 31) - 1。
a | 0可以將變量a中的數值轉換為32位有符號整數,因為整位運算符|只適用于32位整數(它只關心32位以內的值,其他的數位將被忽略)。因此與0進行操作即可截取a中的32位數位。
2.4 特殊數值
JavaScript數據類型中有幾個特殊的值需要開發人員特別注意和小心使用。
2.4.1 不是值的值
undefined類型只有一個值,即undefined。null類型也只有一個值,即null。它們的名稱既是類型也是值。undefined和null常被用來表示“空的”或是“不是值”的值。二至之間有一些細微的差別。例如:
null 值空值(empty value)
undefined 指沒有值(missing value)
或者
undefined 指從未賦值
null 指曾賦過值,但是目前沒有值
null是一個特殊關鍵字,不是標識符,我們不能將其當作變量來使用和賦值。然而undefined卻是一個標識符,可以被當作變量來使用和賦值。
2.4.2 undefined
在非嚴格模式下,我們可以位全局標識符undefined賦值(這樣的設計實在是欠考慮!):
function foo() { undefined = 2; // 非常糟糕的做法! } function foo() { "use strict"; undefined = 2; // TypeError! }
void運算符
undefined是一個內置標識符(除非被重新定義),它的值為undefined,通過void運算符既可得到該值。
表達式void沒有返回值,因此返回結果是undefined。void并不改變表達式的結果,只要讓表達式不返回值:
let a = 42; console.log(void a, a); // undefined 42
按慣例我們用void 0 來獲得undefined(這主要源自C語言,當然使用void true或其他void 表達式也是可以的)。void 0,void 1和undefined之間并沒有實質上的區別。
2.4.3 特殊的數字
數字類型中有幾個特殊的值。
不是數字的數字
如果數學運算的操作數不是數字類型,就無法返回一個有效的數字,這種情況下返回值為NaN。
NaN意指“不是一個數字”(not a number),這個名字容易引起誤會。將它理解為“無效數值”或者“壞數值”可能更準確些。
let a = 2 / "foo"; // NaN typeof a === "number"; // true
tip:typeof并不能完全判斷是否為數字類型,還包括NaN。
換句話說,“不是數字的數字”仍然是數字類型。
NaN是一個“警戒值”(有特殊用途的常規值),用于指出數字類型中的錯誤情況,即“執行數學運算沒有成功,這是失敗后返回的結果。”
也許有人認為如果要檢查變量的值是否為NaN,可以直接和NaN進行比較,就像比較null和undefeind那樣,實則不然。
null === null; // true undefined = undefined; // true let a = 2 / "foo"; a == NaN; //false
NaN是一個特殊值,他和自身不相等,是唯一一個非自反(自反,reflexive,即x === x不成立)的值。而NaN != NaN竟然為true。
tip: NaN是唯一一個非自反的值。
既然我們無法對NaN進行比較(結果永遠為false),那應該怎樣來判斷它呢?可以使用內建的全局工具函數isNaN(..)來判斷一個值是否是NaN。
let a = 2 / "foo"; isNaN(a); // true
isNaN(..)有一個嚴重的缺陷,它的檢查方式過于死板,就是“檢查參數是否不是NaN,也不是數字”。這樣做的結果并不太準確。
let a = 2 / "foo"; let b = "foo"; window.isNaN(a); // true window.isNaN(b); // true ????? "foo"不是一個數字,但是它也不是NaN // 從ES6開始,使用工具函數Number.isNaN(..) Number.isNaN(a); // true Number.isNaN(b); // false
tip:判斷是否為NaN,用Number.isNaN(..)來判斷;也可以用非自反來判斷。
2.無窮數
熟悉傳統編譯型語言(如C)的開發人員可能都遇到過編譯錯誤(compiler error)或者運行時錯誤(runtime exception),例如“除以0”:
let a = 1 / 0;
然而在JavaScript中上例的結果為Infinity(即Number.POSITIVE_INFINITY)。
如果除法運算中的一個操作數為負數。則結果為-Infinity(即Number.NEGATIVE_INFINITY)。
let a = 1 / 0; // Infinity let b = -1 / 0; // -Infinity Infinity === Infinity; // true
3.零值
JavsScript有一個常規的0(也叫做+0)和一個-0。
加法和減法運算不會得到負零。
tip: JSON.stringify(-0)返回"0",而JSON.parse("-0")返回-0。
-0 === 0 ; // true emmm,有待深究
2.4.4 特殊等式
NaN和-0在相等比較時的表現有些特別。由于NaN和自身不相等,所以必須使用ES6中的Number.isNaN(..),而-0等于0(對于===也是如此),因此我們必須使用isNegZero(..)這樣的工具函數。
ES6中新加入了一個工具方法Object.is(..)來判斷兩個值是否絕對相等,可以用來處理上述所有的特殊情況:
let a = 2/ "foo"; let b = -3 *0; Object.is(a, NaN); // true Object.is(b, -0); // true Object.is(b, 0); // false
tip: 能使用==和===時就盡量不要使用Object.is(..),因為前者效率更高、更為通用,后者主要用來處理那些特殊的相等比較。
2.5 值和引用
在許多編程語言中,賦值和參數傳遞可以通過值復制(value-copy)或者引用復制(reference-copy)來完成,這取決于我們使用什么語法。
JavaScript引用指向的是值。如果一個值有10個引用,這些引用指向的都是同一個值,它們相互之間沒有應用/指向關系。
JavaScript對值和引用的賦值/傳遞在語法上沒有區別,完全根據值的類型來決定。
let a = 2; let b = 2; // b是a的值的一個副本 b++; a; // 2 b; // 3 let c = [1, 2, 3]; let d = c; // d是[1, 2, 3]的一個引用 d.push(4); c; // [1, 2, 3, 4] d; // [1, 2, 3, 4]
簡單值(即標量基本類型值)總是通過值復制的方式來賦值/傳遞,包括null、undefined、字符串、數字、布爾和ES6中的symbol。
復合值——對象(包括數組和封裝對象)和函數,則總是通過引用復制的方式來賦值/傳遞。
由于引用指向的是值本身而非變量,所以一個引用無法更改另一個引用的指向。
let a = [1, 2, 3]; let b = a; b = [4, 5, 6]; // 因為賦值,b改變了自己的引用,并不會改變a的引用,所以b變了,而a沒有。 a; // [1, 2, 3] b; // [4, 5, 6]
2.6 小結
JavaScript中的數組是通過數字索引的一組任意類型的值。字符串和數組類似,但是它們的行為特征不同,在將字符作為數組來處理時需要特別小心。JavaScript中的數字包括“整數”和“浮點型”。
基本類型中定義了幾個特殊的值。
null類型只有一個值null,undefined類型也只有一個值undefined。所有變量在賦值之前默認值都是undefined。void運算符返回undefined。
數字類型有幾個特殊值,包括NaN(意指“not a number”,更準確地說是“invalid number”)、+Infinity、--Infinity和-0。
簡單標量基本類型值(字符串和數字等)通過值復制來賦值/傳遞,而復合值(對象等)通過引用復制來賦值/傳遞。
JavaScript中的引用和其它語言中的引用/指針不同,它們不能指向別的變量/引用,只能指向值。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/110012.html
摘要:強制類型轉換本章介紹了的數據類型之間的轉換即強制類型轉換包括顯式和隱式。強制類型轉換常常為人詬病但實際上很多時候它們是非常有用的。隱式強制類型轉換則沒有那么明顯是其他操作的副作用。在處理強制類型轉換的時候要十分小心尤其是隱式強制類型轉換。 前言 《你不知道的 javascript》是一個前端學習必讀的系列,讓不求甚解的JavaScript開發者迎難而上,深入語言內部,弄清楚JavaSc...
摘要:詞法作用域定義在詞法階段的作用域由你在寫代碼時將變量和塊作用域寫在哪來決定的,因此當詞法分析器處理代碼時會保持作用域不變。欺騙詞法作用域在詞法分析器處理過后依然可以修改作用域。 你不知道的JS(上卷)筆記 你不知道的 JavaScript JavaScript 既是一門充滿吸引力、簡單易用的語言,又是一門具有許多復雜微妙技術的語言,即使是經驗豐富的 JavaScript 開發者,如果沒...
摘要:看下面的代碼和會對操作數執行條件判斷,如果操作數不是布爾值,會先執行類型轉換后再執行條件判斷。大家記住這個規則布爾值如果與其他類型進行抽象比較,會先用將布爾值轉換為數字再比較。 在上一篇中我們聊過了 JS 類型轉換的規則和我發現的一些常見書籍中關于類型轉換的一些小錯誤,當碰到顯示類型轉換的時候大家可以按照這些規則去拆解出答案。但 JS 中存在一些很隱晦的隱式類型轉換,這一篇就來談下我對...
摘要:首先,為了掌握好類型轉換,我們要理解一個重要的抽象操作為什么說這是個抽象操作呢因為這是內部才會使用的操作,我們不會顯示調用到。基本規則中的類型轉換總是返回基本類型值,如字符串數字和布爾值,不會返回對象和函數。 Javascript 里的類型轉換是一個你永遠繞不開的話題,不管你是在面試中還是工作寫代碼,總會碰到這類問題和各種的坑,所以不學好這個那是不行滴。關于類型轉換我也看過不少的書和各...
閱讀 3026·2021-11-24 09:39
閱讀 2255·2021-10-08 10:05
閱讀 2749·2021-09-24 13:52
閱讀 1569·2021-09-22 15:07
閱讀 589·2019-08-30 15:55
閱讀 1808·2019-08-30 15:53
閱讀 687·2019-08-30 15:44
閱讀 3116·2019-08-30 11:20