實習結束,臨近開學了, 想起幾個星期前給第一次發紅包, 給爺爺奶奶, 還有弟弟妹妹。內心有點小開心 ( ̄▽ ̄)"
javascript
好, 回到主題,咱們今天要講的是——
html
講到類型, 首先要說的固然是JS的類型分類, 對於這一點,《javascript高級語言程序設計》和《你不知道的javasvript》的闡述是有差別的(但想表達的意思是相同的)java
我更傾向於前一本書(紅寶書)的定義:
javascript的數據類型可分爲兩種: 基本類型和引用類型:
基本類型: String, Number, Boolean, Null, undefined 和Symbol
引用類型: Object, Array, Function, RegExp, Date, 封裝類型(基本包裝類型)node
我前面說了, 兩本書對類型分類的闡述是有差別的, 關鍵出如今引用類型的Object身上, 由於Object是個特殊的存在, 事實上咱們知道全部其餘的引用類型如Array, Function, RegExp等都屬於Object, 也就是說Object是這些的「父類型"數組
對此:
《你不知道的javasvript》裏把其餘引用類型一併歸結爲Object
《javascript高級語言程序設計》則是根據「外觀」把Object和Array等放在平行的位置,就是說「看上去像」 { }的是對象, 而像[ ]這樣的, 雖然也是對象, 但咱們仍是叫它數組吧瀏覽器
在瞭解JS弱類型以前,咱們須要瞭解一點—— JS裏值纔有類型,變量沒有
咱們常常會談到JS的類型,其實是針對變量的值的,而不是變量。安全
例如
假設a = 1,則準確的說typeof a; 中的typeof是用來檢測a的值的類型,而不是變量a的類型的函數
咱們平時常常看到的檢測某個變量(值)的類型, 實際上是一種有意無心的省略(省略了「值」),這可能會帶來誤解學習
瞭解了這一點以後再讓咱們看看什麼叫作JS的弱類型:
咱們上面說到JS裏值纔有類型,變量沒有,也就是JS裏不會對變量作類型「強制」 :不會要求一個變量從頭至尾都只能保持初始化時的類型:測試
var a = 'penghuwan'; console.log(typeof a); // string a = 1; console.log(typeof a); // number
因此說: 弱類型的特性是針對JS變量的(不要和前面的東西混淆了哦)
// 檢測基本類型 console.log('檢測string', typeof 'penghuwan'); // 檢測string string console.log('檢測number', typeof 1); // 檢測number number console.log('檢測boolean',typeof true); // 檢測boolean boolean console.log('檢測undefined', typeof undefined); // 檢測undefined undefined console.log('檢測symbol', typeof Symbol()); // 檢測symbol symbol console.log('檢測null', typeof null); // 檢測null object // 檢測引用類型 console.log('檢測object', typeof {}); // 檢測object object
typeof
檢測一個數值的類型, 返回的是一個字符串(小寫),去表示這個變量數值的類型
優勢: 能檢測出除了null外的全部內置類型
typeof的缺點:
1.不能檢測除了function以外的引用類型
(function仍是能夠的哦!)
var fn = function () {}; var array = [1,2,3]; var reg = /\./; console.log(typeof fn); // function console.log(typeof array); // object console.log(typeof reg); // object
2. 檢測null會檢測出object
讓咱們看看《你不知道的javascript》中做者的原話:
「這個 bug 由來已久,在 JavaScript 中已經存在了將近二十年,也許永遠也不會修復,由於這牽涉到太多的 Web 系統,「修復」它會產生更多的bug,令許多系統沒法正常工做」
沒錯, 爲了「向後兼容」, 咱們是無法用直接的手段檢測出null,下面我將會以比較多的篇幅介紹如何檢測null
【注意】: 返回的字符串都是小寫的哦!是 'string' 不是 'String'
instanceof
檢測某個變量是不是某個對象的實例, 返回一個布爾型的數值
var obj = {}; var array = [1,2,3]; var fn = function () {}; var reg = /\./; // 檢測具體的引用類型 console.log(obj instanceof Object); // true console.log(array instanceof Array); // true console.log(fn instanceof Function); // true console.log(reg instanceof RegExp); // true // 引用類型用Object去檢測也是返回true的 console.log(array instanceof Object); // true console.log(fn instanceof Object); // true console.log(reg instanceof Object); // true
優缺點
優勢: 能檢測出引用類型的具體類型, 不像typeof同樣只能檢測出object, 而是檢測出更加具體的類型如Array, RegExp等
缺點:
1.返回布爾值, 形式不夠靈活
2. 不能檢測基本類型
如:
var strObj = new String('penghuwan'); console.log(str instanceof String); // false console.log(strObj instanceof String) // true
能夠看到,純粹的基本類型是不可以檢測出來的, 而要轉化成對應的基本包裝類型才能檢測出來,固然了, 我猜大多數時候你都不會這麼幹
1.這個是《你不知道的javascript》的解決方案
var a = null; if(!a && typeof a === 'object') { console.log('null被檢測出來啦!!') } // null被檢測出來啦!!
寫這篇博客的時候隨口問了下旁邊的室友:
知道JS中怎麼檢測null不? 你確定不知道!
(此時我正作得意洋洋抖腿狀)
而後他的表情是這樣的:
說時遲那時快, 他一頓操做將我火速打臉。。。代碼以下:
2. 經過null 包含的[[ class ]]內部屬性檢測(只作展現,不要這樣作哦!!)
var a = Object.prototype.toString.call(null); console.log(a); // 輸出 [object Null]
我立刻意識到他是想用下面這種方式檢測, 通過測試發現可以成功
if(Object.prototype.toString.call(null) === '[object Null]') { console.log('null被檢測出來啦'); } // 打印: null被檢測出來啦
當時打臉場景以下:
其實我是不服氣的,由於以爲這段代碼有點醜陋,因而又想了一種:
3. 經過JSON.stringfy(XXX) === 'null'檢測null
if(JSON.stringify(null) === 'null') { console.log('null被檢測出來啦'); } // 打印: null被檢測出來啦
【注意】在《javascript高級語言程序設計》中叫作「基本包裝類型」, 在《你不知道的javascript》中叫作「封裝類型」, 其實是同一個意思, 本文主要之後者爲名
javascript的一句毒奶名言: 萬物皆對象! 但其實咱們發現: boolean, Number, String這些基本類型,好像和對象不要緊嘛。是的, 它們基本類型的性質決定了它們和對象有本質的不一樣
但它們「背後」仍有股「來自對象」的 神祕力量的做用着...
(此處播放《走進科學》欄目讓人不寒而慄的背景音樂...)
有一天彭先生忽然想起 var str = 'penghuwan'; str.substring(2)這種司空見慣的用法裏,substring(2)是哪裏來的? str是字符串,不是對象啊! 那又怎麼會擁有對象纔有的方法呢!!?
沒錯, 即便是boolean, number, string這種看似單純地像一張白紙的基本類型, 在幕後也和「對象」有着骯髒的py交易。。。。 (我說的是朋友交易哦)
爲了可以自由靈活地操做 Boolean, Number 和String這三個很是經常使用的 基本類型值(也就是有大量調用方法作處理的需求)
在訪問這三個基本類型值的時候, javascript 就會建立一個「不可見」的封裝類型,而後在讀取完畢時候銷燬
例如:
var s1 = "text"; var s2 = s1.substring(2);
(內部)至關於:
var s1 = new String("text"); var s2 = s1.substring(2); s1 = null
基本類型對應的封裝類型的對象只在訪問的時候建立,訪問完畢就會銷燬!該對象的生存期只有一瞬間, 用例子作個 對比:
// 顯示建立封裝類型的對象, 且在這段代碼中始終存在 var str = new String('1'); str.color = 'red'; console.log(str.color); // 輸出red
var str = '1' // 隱式建立了封裝類型的對象,該對象只在str.color = 'red';這一條語句存在,隨後立刻銷燬 str.color = 'red'; // 下面這條console語句裏面的訪問會建立另一個封裝對象 console.log(str.color); // 輸出undefined
【注意】
1.訪問字符串屬性(方法)的時候建立的「封裝類型對象」是不可見的
2. 只有訪問一個保存了基本類型值的變量纔會建立「封裝類型對象」! 對於「直接的值」是不會建立封裝類型對象的
例如:
console.log(1.toString()); // 報錯!! 不能直接對值操做 // 這樣搞纔是對滴~~~~ var a = 1; console.log(a.toString()); // 輸出1
字符串轉數字
字符串轉爲數字有兩種方式:
1. 經過Number()轉化
2. 經過parseInt解析
二者的不一樣:
1. Number: 當字符串內容包括數字外的內容時候(如"42px"),轉化失敗返回NaN
2. parseInt: 當字符串內容包括數字外的內容時候, 返回當前從左往右解析成功的數字
var a = "42"; var b = "42px"; Number( a ); // 42 parseInt( a ); // 42 Number( b ); // NaN parseInt( b ); // 42
其餘類型轉化爲字符串
可調用toString方法轉化
var a = [1,2,3]; a.toString(); // "1,2,3"
// 經過「幕後」的封裝類型調用toString() var a = 1; console.log(a.toString()); // 輸出 字符串的1
【注意】對undefined和 null 這兩個特殊的類型你沒法調toString(),由於它們根本就沒有這個方法
var a = undefined; console.log(a.toString()) // 報錯!根本找不到方法!
var b = null; console.log(b.toString()) // 報錯!根本找不到方法!
###固然不少時候咱們會用更直接的方法: XXX + ""(加一個空串)去實現隱式的類型轉化
JSON對象轉化爲字符串
(啊!首先我要先喊一句JSON大法好!)
咱們知道,強大的JSON.stringify能夠將許多值轉化爲字符串, 但僅限於JSON安全的值(JSON-safe)
如:
JSON.stringify( 42 ); // "42" JSON.stringify( "42" ); // ""42"" (含有雙引號的字符串) JSON.stringify( null ); // "null" JSON.stringify( true ); // "true"
對於非JSON安全的值(function, undefined和Symbol)
JSON.stringify卻避之惟恐不及
對這些值:
JSON.stringify( undefined ); // undefined JSON.stringify( function(){} ); // undefined
JSON.stringify( [1,undefined,function(){},4] ); // "[1,null,null,4]"
JSON.stringify( { a:2, b:function(){} } ); // "{"a":2}"
如今你應該知道爲何JSON.parse(JSON.stringify(XXX))這種深拷貝的神操做要求XXX對象裏面不能有函數了吧
什麼叫JSON安全的值(爲何函數「不安全」?)
你覺得我會說答案? 對不起我也不知道 [哭臉], 不過等我繼續努力學習,知道了後會來告訴你們的.....(或者評論區有高人的話幫忙一下咯)
console對象,window對象,DOM元素對象並不被javaScript真正「擁有」
javascript通常是不能獨立運行的, 而要依賴於宿主環境,常見的宿主環境有:
1. 瀏覽器 (最多見)
2. node
console對象對許多JSer們來講都很熟悉, 通常想到console你可能便會情不自禁地想起「console.log」這個刻骨銘心的方法(固然如今基本調試都用debugger了嘿嘿。。。)
console對象,window對象,DOM元素對象並不被javaScript真正「擁有」
console對象
正由於console和JS的聯繫如此的 密切, 因此一些人可能誤覺得console對象是JS標準裏的東西。
這裏我要說一句:
console對象並非屬於JS的而是瀏屬於瀏覽器的
也正因如此, 各大瀏覽器關於console對象API的實現也各不相同(固然log這種基本方法都有。。)
1. 低版本的IE甚至沒有console對象(固然也就沒有了與之對應的調試功能)
2.谷歌和火狐console對象下的方法基本相同,但也是有差別的。例如:
谷歌console下有memory方法火狐沒有
連連看! 找不一樣
這是我大谷歌中打印的console對象:
這是火狐打印的console對象
Window對象
學習JS的筒子們通常都知道, 「JS有有個保存全局變量的頂層對象, 它叫Window對象,或者叫作global對象」
我一直以來也有一個困惑:「既然(若是)Window對象和global對象是同一個東西的話,幹嗎要取兩個名稱,大家玩我啊?」
閱讀了相關資料後, 我發現:「Window對象和global對象是同一個東西」的說法並非很精確
最重要的一點是:他兩隸屬的「政治陣營」不同
1.Global對象是ECMAscript標準中定死的全局對象
2.Window對象是瀏覽器的一個實例,因此你容易推測出:不一樣的瀏覽器對Window的實現應該是不同的,至少在許多細節上會有不一樣, 也就是這些不一樣的瀏覽器分別擁有並不太同樣的Window對象
而javascript在宿主環境(瀏覽器)上運行的時候, 會把當前瀏覽器Window對象做爲本身的Global對象,這時候,「從表面上看」, Window對象和Global對象「是同一個」
因此說javascript運行程序就是一個處處混吃混喝的主,找到哪一個「東家」(宿主環境/瀏覽器),就把東西的Window對象「偷」出來, 當成本身的Global對象
如下場景是我想象出來的:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
javascript敲了敲門:「誒,老谷啊(谷歌)! 我又上你家串門來了,那個啥。。。。。」
門開了
谷歌瀏X器瞧見是JS, 從懷裏掏出Window對象來,擺擺手:「走! 走! 走!!!」
javascript把Window對象放進本身的Global口袋裏,心滿意足地走了。
「明兒去老狐家和safari家吧! IE家太寒磣,我就不去了」
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
DOM元素對象
例如 var a = document.createElement('div'); 取得的就是一個DOM元素對象
DOM元素對象也是瀏覽器提供的東西, 因此它並不像javascript標準裏的其餘對象那樣服從「控制」
它有如下一些特色:
• 沒法寫覆蓋;
• 包含一些預約義的只讀屬性;
• 包含沒法將 this 重載爲其餘對象的方法
下面講的這些東西, 有些你可能比較少用到, 但它們絕對有理由會坑到你,並且還會。。。。坑死你。。。
惱人的undefined
爲了進一步明確undefined的行爲, 這裏我引入 undeclared的概念:
1.undefined表示變量聲明瞭, 但沒有賦值, 不會報錯
2.undeclared表示變量歷來就沒有聲明, 這會致使報錯
var b; console.log(b); // 輸出undefined
console.log(a); // 直接報錯
究竟是什麼讓咱們對undefined一臉懵蔽?
首先我要告訴你上面1,2兩條已經足以表徵undefined和 undeclared的不一樣了,但不少時候咱們仍會搞混,爲何呢?
由於javascript會 故!意!搞!事!(此處有褒有貶)
1. 通常狀況下使用一個未聲明(undeclared)的變量是會直接報錯的,但typeof運算符的「安全機制」會規避這一點, 例如:
// 此處a未聲明 typeof a;
中使用了不曾聲明過的變量a,可是無報錯發生,並且此時返回的是undefined!!
也就是說typeof的安全機制把 undeclared的行爲改變了,且和undefined同樣, 這會讓咱們感到困惑(要注意typeof中的這種變量行爲只是一種特殊狀況,不要奉爲圭籌)
2. 輸出undeclared變量的時候會報錯,可是此時的輸出具備迷惑性:
console.log(a) // a未聲明時輸出 Uncaught ReferenceError: a is not defined
實際上,這裏的not defined若是改成undeclared或許會更好理解一些
爲何要這樣作呢? 這固然是有緣由的, 這讓咱們能夠安心地判斷一個變量的類型,即便它不存在也不會報錯, 咱們的程序
1. 浮點數計算失真問題
由於有些小數表示成二進制是無限循環的, 這致使沒法「恰好」計算正確
console.log(0.2 + 0.5 === 0.7); // true console.log(0.5+ 0.5 === 1.0); // true console.log(0.2 + 0.4 === 0.6); // false console.log(0.2 + 0.1 === 0.3); // false
2. 詭異而有趣的NaN
首先你要搞清楚的一個問題是NaN的含義
讓咱們猜猜它是什麼意思:
1. NaN不是number,也就是number以外的類型, 例如字符串,布爾值等等
2. NaN屬於number類型,只不過是一種很是特殊的number的值, 爲NaN
NaN屬於第2種而不是第1種!!也就是說字符串, 布爾值表面上是Not A Number(也即NaN的表面意思) , 但它們和NaN是八竿子打不着的關係,不要弄混了。
而在這裏,爲了讓咱們能把NaN的概念變得混亂, javascript的一個糟心的API卻開始了他的表演。。。
沒錯, ES5裏面全局的isNaN方法不能區分純粹的NaN和字符串等「非數字」
console.log(isNaN(NaN)); // true console.log(isNaN("看清楚老子是字符串不是NaN!!")); // true
ES6把isNaN方法歸入到了Number封裝對象中, 並對這個糟糕的情況進行了改進:
console.log(Number.isNaN(NaN)); // true console.log(Number.isNaN("看清楚老子是字符串不是NaN!!")); // false
NaN的自反特性
NaN最詭異的地方在於: NaN是不等於NaN的!
console.log(NaN === NaN) // false
在全部數值類型中獨一無二的逆天特性
在ES6前怎麼檢測"純粹"的NaN呢? (不要誤判字符串等)
1.事實上是這樣作的
// 省略諸多內容, 只留關鍵一句 return typeof n === "number" && window.isNaN( n )
其實還有另一種更簡潔的方式,
2.利用NaN的自反特性:
// 省略諸多內容, 只留關鍵一句 return n !== n;
布爾值false的替身:能充當false的「假值」
這要從if條件語句開始提及:
if(someValue){ }
做爲一個JSer, 你絕對知道把undefined, null, 0 填上去會發生什麼
對, 由於類型轉換的機制, 它們最終都會等同於false
而undefined, null, 0就是我上面所說的 「假值」
但顯然又又又又又又有東西會讓咱們搞混(啊!javascript你怎麼老這樣啊!!)
在這裏我問你們
1."" "undefined", "null", "0", 是假值嗎?(在判斷條件下能被類型轉換爲false嗎)
2. new Boolean(false), new Number(0), new String("")是假值嗎?
3. [] (空數組), {}(空對象) function () { } 是假值嗎?
若是你是一個有經驗的JS開發者,這有可能不是什麼難題, 但我認爲這些「判斷題」對初學JS的人來講的確不那麼友好
下面我給三點論斷讓你們可以正確判斷
1. 除空字符串("")外的字符串的都不是假值,而是真值 (一擊擊破 "" "undefined", "null", "0"所形成的認知混亂)
2. 凡是對象的都不是假值, 而是真值 (一擊擊破. [] , {}, function () { } 所形成的認知混亂)
【注意】對於2中請注意數組和函數本質上也是對象!
3.真正的假值只有屈指可數的那幾個:
1. undefined
2. null
3. false
4. +0 、 -0 和 NaN
5. ""(空字符串)
嗯嗯,就這樣
強大的模板字符串
你可能遇到過這種問題:
有些時候你會寫一些HTML字符串
而後當它變長一些的時候你決定要換行(其實主要是以爲不換行太難看了)
而後換行後你就看到了這一幕:
嗯嗯, 看到下面的那個</div>顏色變了你就知道這絕對不是什麼好兆頭!!(實際上運行也會報錯的,由於編譯時候會認爲下面的</div>前面缺乏字符串 ' 的符號)
因而你可能這樣幹
var str = '<div>' + '</div>'
但仔細想想, 你的HTML代碼哪怕只長一點點就可能變成這樣:
var str = '<html>' + '<head>' + '<meta charset="utf-8" />' + '<title></title>' + '</head>' + '</html>'
簡直恐怖!!你把大量的工做花費在了寫+號上和寫單引號上 (雖然以我歪曲的審美以爲這段代碼看起來挺「漂亮」的)
而當你使用模板字符串時就不用換行了:
var str = `<html> <head> <meta charset="utf-8" /> <title></title> </head> </html>`
固然了, 更多時候咱們習慣於用模板字符串作字符串的動態插值
它能夠把
var name = 'penghuwan' console.log('my name is ' + name);
變成
var name = 'penghuwan' console.log(`my name is ${ name }`);
形式上更加方便簡潔, 可讀性也大大加強了
總結一下模板字符串的做用:
1.容許多行
2. 簡潔插值
【注意】: 模板字符串是ES6的特性
參考資料:
《你不知道的javascript》 — — [美] Kyle Simpson
《javascript高級語言程序設計》— — [美] Nicholas C·Zakas
【完】