【javascript】詳解變量,值,類型和宿主對象

前言——說一點不相關的事

實習結束,臨近開學了, 想起幾個星期前給第一次發紅包, 給爺爺奶奶, 還有弟弟妹妹。內心有點小開心   ( ̄▽ ̄)"
javascript

 

 

 好, 回到主題,咱們今天要講的是——
html

JS數據類型

 

JS類型分類

 

講到類型, 首先要說的固然是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裏值纔有類型,變量沒有
咱們常常會談到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變量的(不要和前面的東西混淆了哦)

typeof和instanceof, 各有千秋

 

// 檢測基本類型
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

 

能夠看到,純粹的基本類型是不可以檢測出來的, 而要轉化成對應的基本包裝類型才能檢測出來,固然了, 我猜大多數時候你都不會這麼幹

 

檢測null的3種方式

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

 

 

閒話javascript類型轉換

 

字符串轉數字


字符串轉爲數字有兩種方式:
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卻避之惟恐不及


對這些值:

  • 做爲單個值使用的時候會一概返回undefined
JSON.stringify( undefined ); // undefined
JSON.stringify( function(){} ); // undefined

 

  • 在數組中出現會將其重置爲null
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對象來,擺擺手:「走! 走! 走!!!」
javascriptWindow對象放進本身的Global口袋裏,心滿意足地走了。
「明兒去老狐家和safari家吧! IE家太寒磣,我就不去了」

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

DOM元素對象


例如 var a = document.createElement('div'); 取得的就是一個DOM元素對象
DOM元素對象也是瀏覽器提供的東西, 因此它並不像javascript標準裏的其餘對象那樣服從「控制」
它有如下一些特色:

• 沒法寫覆蓋;
• 包含一些預約義的只讀屬性;
• 包含沒法將 this 重載爲其餘對象的方法

基本類型大雜談——聊聊那些坑點

下面講的這些東西, 有些你可能比較少用到, 但它們絕對有理由會坑到你,並且還會。。。。坑死你。。

undefined篇


惱人的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或許會更好理解一些


爲何要這樣作呢? 這固然是有緣由的, 這讓咱們能夠安心地判斷一個變量的類型,即便它不存在也不會報錯, 咱們的程序

Number篇


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;

 

Boolean篇

布爾值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. ""(空字符串)


嗯嗯,就這樣

String篇

強大的模板字符串

你可能遇到過這種問題:
有些時候你會寫一些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

 

【完】

相關文章
相關標籤/搜索