前言
之前面試了幾個開發者,他們確實做過不少項目,能力也是不錯的,但是發現js基礎不扎實, 于是決定寫一下這篇javascrip數據類型相關的基礎文章,其實也不僅僅是因為面試了他們,之前自己在面試的時候,也曾經被虐過,面試官說過的最深刻的一句話我到現在都記得。
基礎很重要,只有基礎好才會很少出
bug
,大多數的bug
都是基礎不扎實造成的。
這里給出兩道我們公司數據類型基礎相關的面試題和答案,如果都能做對并且知道為什么(可以選擇忽略本文章):
//類型轉換相關問題
var bar=true;
console.log(bar+0);
console.log(bar+"xyz");
console.log(bar+true);
console.log(bar+false);
console.log('1'>bar);
console.log(1+'2'+false);
console.log('2' + ['koala',1]);
var obj1 = {
a:1,
b:2
}
console.log('2'+obj1);
var obj2 = {
toString:function(){
return 'a'
}
}
console.log('2'+obj2)
//輸出結果 1 truexyz 2 1 false 12false 2koala,1 2[object Object] 2a
//作用域和NaN 這里不具體講作用域,意在說明NaN
var b=1;
function outer(){
var b=2;
function inner(){
b++;
console.log(b);
var b=3;
}
inner();
}
outer();
//輸出結果 NaN
本篇文章會以一個面試官問問題的角度來進行分析講解
js中的數據類型
面試官:說一說
javascript
中有哪些數據類型?
JavaScript
中共有七種內置數據類型,包括基本類型和對象類型。
基本類型
基本類型分為以下六種:
- string(字符串)
- boolean(布爾值)
- number(數字)
- symbol(符號)
- null(空值)
- undefined(未定義)
注意:
-
string
、number
、boolean
和null
undefined
這五種類型統稱為原始類型(Primitive),表示不能再細分下去的基本類型; -
symbol
是ES6中新增的數據類型,symbol
表示獨一無二的值,通過Symbol
函數調用生成,由于生成的 symbol 值為原始類型,所以Symbol
函數不能使用new
調用; -
null
和undefined
通常被認為是特殊值,這兩種類型的值唯一,就是其本身。
對象類型
對象類型也叫引用類型,array
和function
是對象的子類型。對象在邏輯上是屬性的無序集合,是存放各種值的容器。對象值存儲的是引用地址,所以和基本類型值不可變的特性不同,對象值是可變的。
js弱類型語言
面試官:說說你對
javascript
是弱類型語言的理解?
JavaScript
是弱類型語言,而且JavaScript
聲明變量的時候并沒有預先確定的類型,變量的類型就是其值的類型,也就是說變量當前的類型由其值所決定,夸張點說上一秒種的String
,下一秒可能就是個Number
類型了,這個過程可能就進行了某些操作發生了強制類型轉換。雖然弱類型的這種不需要預先確定類型的特性給我們帶來了便利,同時也會給我們帶來困擾,為了能充分利用該特性就必須掌握類型轉換的原理。
js中的強制轉換規則
面試官:
javascript
中強制類型轉換是一個非常易出現bug
的點,知道強制轉換時候的規則嗎?
注:規則最好配合下面什么時候發生轉換使用這些規則看效果更佳。
ToPrimitive
(轉換為原始值)
ToPrimitive
對原始類型不發生轉換處理,只針對引用類型(object)的,其目的是將引用類型(object)轉換為非對象類型,也就是原始類型。
ToPrimitive
運算符接受一個值,和一個可選的期望類型作參數。ToPrimitive
運算符將值轉換為非對象類型,如果對象有能力被轉換為不止一種原語類型,可以使用可選的 期望類型 來暗示那個類型。
轉換后的結果原始類型是由期望類型決定的,期望類型其實就是我們傳遞的type
。直接看下面比較清楚。
ToPrimitive
方法大概長這么個樣子具體如下。
/**
* @obj 需要轉換的對象
* @type 期望轉換為的原始數據類型,可選
*/
ToPrimitive(obj,type)
type不同值的說明
- type為
string
:
- 先調用
obj
的toString
方法,如果為原始值,則return
,否則進行第2步 - 調用
obj
的valueOf
方法,如果為原始值,則return
,否則進行第3步 - 拋出
TypeError
異常
- type為
number
:
- 先調用
obj
的valueOf
方法,如果為原始值,則return
,否則進行第2步 - 調用
obj
的toString
方法,如果為原始值,則return
,否則第3步 - 拋出
TypeError
異常
- type參數為空
- 該對象為
Date
,則type被設置為String
- 否則,type被設置為
Number
Date數據類型特殊說明:
對于Date
數據類型,我們更多期望獲得的是其轉為時間后的字符串,而非毫秒值(時間戳),如果為number
,則會取到對應的毫秒值,顯然字符串使用更多。
其他類型對象按照取值的類型操作即可。
ToPrimitive
總結
ToPrimitive
轉成何種原始類型,取決于type,type參數可選,若指定,則按照指定類型轉換,若不指定,默認根據實用情況分兩種情況,Date
為string
,其余對象為number
。那么什么時候會指定type類型呢,那就要看下面兩種轉換方式了。
toString
Object.prototype.toString()
toString()
方法返回一個表示該對象的字符串。
每個對象都有一個 toString()
方法,當對象被表示為文本值時或者當以期望字符串的方式引用對象時,該方法被自動調用。
這里先記住,valueOf()
和 toString()
在特定的場合下會自行調用。
valueOf
Object.prototype.valueOf()
方法返回指定對象的原始值。
JavaScript
調用 valueOf()
方法用來把對象轉換成原始類型的值(數值、字符串和布爾值)。但是我們很少需要自己調用此函數,valueOf
方法一般都會被 JavaScript
自動調用。
不同內置對象的valueOf
實現:
- String => 返回字符串值
- Number => 返回數字值
- Date => 返回一個數字,即時間值,字符串中內容是依賴于具體實現的
- Boolean => 返回Boolean的this值
- Object => 返回this
對照代碼會更清晰一些:
var str = new String('123');
console.log(str.valueOf());//123
var num = new Number(123);
console.log(num.valueOf());//123
var date = new Date();
console.log(date.valueOf()); //1526990889729
var bool = new Boolean('123');
console.log(bool.valueOf());//true
var obj = new Object({valueOf:()=>{
return 1
}})
console.log(obj.valueOf());//1
Number
Number
運算符轉換規則:
-
null
轉換為 0 -
undefined
轉換為NaN
-
true
轉換為 1,false
轉換為 0 - 字符串轉換時遵循數字常量規則,轉換失敗返回
NaN
注意:對象這里要先轉換為原始值,調用
ToPrimitive
轉換,type指定為number
了,繼續回到ToPrimitive
進行轉換。
String
String
運算符轉換規則
-
null
轉換為'null'
-
undefined
轉換為undefined
-
true
轉換為'true'
,false
轉換為'false'
- 數字轉換遵循通用規則,極大極小的數字使用指數形式
注意:對象這里要先轉換為原始值,調用
ToPrimitive
轉換,type
就指定為string
了,繼續回到ToPrimitive
進行轉換(上面有將到ToPrimitive
的轉換規則)。
String(null) // 'null'
String(undefined) // 'undefined'
String(true) // 'true'
String(1) // '1'
String(-1) // '-1'
String(0) // '0'
String(-0) // '0'
String(Math.pow(1000,10)) // '1e+30'
String(Infinity) // 'Infinity'
String(-Infinity) // '-Infinity'
String({}) // '[object Object]'
String([1,[2,3]]) // '1,2,3'
String(['koala',1]) //koala,1
Boolean
ToBoolean
運算符轉換規則
除了下述 6 個值轉換結果為 false
,其他全部為true
:
- undefined
- null
- -0
- 0或+0
- NaN
- ''(空字符串)
假值以外的值都是真值。其中包括所有對象(包括空對象)的轉換結果都是true
,甚至連false
對應的布爾對象new Boolean(false)
也是true
Boolean(undefined) // false
Boolean(null) // false
Boolean(0) // false
Boolean(NaN) // false
Boolean('') // false
Boolean({}) // true
Boolean([]) // true
Boolean(new Boolean(false)) // true
js轉換規則不同場景應用
面試官問:知道了具體轉換成什么的規則,但是都在什么情況下發生什么樣的轉換呢?
什么時候自動轉換為string類型
-
在沒有對象的前提下
字符串的自動轉換,主要發生在字符串的加法運算時。當一個值為字符串,另一個值為非字符串,則后者轉為字符串。
'2' + 1 // '21'
'2' + true // "2true"
'2' + false // "2false"
'2' + undefined // "2undefined"
'2' + null // "2null"
- 當有對象且與對象
+
時候
//toString的對象
var obj2 = {
toString:function(){
return 'a'
}
}
console.log('2'+obj2)
//輸出結果2a
//常規對象
var obj1 = {
a:1,
b:2
}
console.log('2'+obj1);
//輸出結果 2[object Object]
//幾種特殊對象
'2' + {} // "2[object Object]"
'2' + [] // "2"
'2' + function (){} // "2function (){}"
'2' + ['koala',1] // 2koala,1
對下面'2'+obj2
詳細舉例說明如下:
- 左邊為
string
,ToPrimitive
原始值轉換后不發生變化 - 右邊轉化時同樣按照
ToPrimitive
進行原始值轉換,由于指定的type是number
,進行ToPrimitive
轉化調用obj2.valueof()
,得到的不是原始值,進行第三步 - 調用
toString()
return 'a'
- 符號兩邊存在
string
,而且是+
號運算符則都采用String
規則轉換為string
類型進行拼接 - 輸出結果
2a
對下面'2'+obj1
詳細舉例說明如下:
- 左邊為
string
,ToPrimitive
轉換為原始值后不發生變化 - 右邊轉化時同樣按照
ToPrimitive
進行原始值轉換,由于指定的type是number
,進行ToPrimitive
轉化調用obj2.valueof()
,得到{ a: 1, b: 2 }
- 調用
toString()
return [object Object]
- 符號兩邊存在
string
,而且是+號運算符則都采用String
規則轉換為string
類型進行拼接 - 輸出結果
2[object Object]
代碼中幾種特殊對象的轉換規則基本相同,就不一一說明,大家可以想一下流程。
注意:不管是對象還不是對象,都有一個轉換為原始值的過程,也就是ToPrimitive
轉換,只不過原始類型轉換后不發生變化,對象類型才會發生具體轉換。
string類型轉換開發過程中可能出錯的點:
var obj = {
width: '100'
};
obj.width + 20 // "10020"
預期輸出結果120 實際輸出結果10020
什么時候自動轉換為Number類型
-
有加法運算符,但是無
String
類型的時候,都會優先轉換為Number
類型例子:
true + 0 // 1 true + true // 2 true + false //1
除了加法運算符,其他運算符都會把運算自動轉成數值。
例子:
'5' - '2' // 3
'5' * '2' // 10
true - 1 // 0
false - 1 // -1
'1' - 1 // 0
'5' * [] // 0
false / '5' // 0
'abc' - 1 // NaN
null + 1 // 1
undefined + 1 // NaN
//一元運算符(注意點)
+'abc' // NaN
-'abc' // NaN
+true // 1
-false // 0
注意:
null
轉為數值時為0,而undefined
轉為數值時為NaN
。
判斷等號也放在Number
里面特殊說明
== 抽象相等比較與+運算符不同,不再是String
優先,而是Number
優先。
下面列舉x == y
的例子
- 如果
x
,y
均為number
,直接比較
沒什么可解釋的了
1 == 2 //false
- 如果存在對象,
ToPrimitive()
type為number
進行轉換,再進行后面比較
var obj1 = {
valueOf:function(){
return '1'
}
}
1 == obj1 //true
//obj1轉為原始值,調用obj1.valueOf()
//返回原始值'1'
//'1'toNumber得到 1 然后比較 1 == 1
[] == ![] //true
//[]作為對象ToPrimitive得到 ''
//![]作為boolean轉換得到0
//'' == 0
//轉換為 0==0 //true
- 存在
boolean
,按照ToNumber
將boolean
轉換為1或者0,再進行后面比較
//boolean 先轉成number,按照上面的規則得到1
//3 == 1 false
//0 == 0 true
3 == true // false
'0' == false //true
4.如果x
為string
,y
為number
,x
轉成number
進行比較
//'0' toNumber()得到 0
//0 == 0 true
'0' == 0 //true
什么時候進行布爾轉換
- 布爾比較時
-
if(obj)
,while(obj)
等判斷時或者 三元運算符只能夠包含布爾值
條件部分的每個值都相當于false
,使用否定運算符后,就變成了true
if ( !undefined
&& !null
&& !0
&& !NaN
&& !''
) {
console.log('true');
} // true
//下面兩種情況也會轉成布爾類型
expression ? true : false
!! expression
js中的數據類型判斷
面試官問:如何判斷數據類型?怎么判斷一個值到底是數組類型還是對象?
三種方式,分別為 typeof
、instanceof
和Object.prototype.toString()
typeof
通過 typeof
操作符來判斷一個值屬于哪種基本類型。
typeof 'seymoe' // 'string'
typeof true // 'boolean'
typeof 10 // 'number'
typeof Symbol() // 'symbol'
typeof null // 'object' 無法判定是否為 null
typeof undefined // 'undefined'
typeof {} // 'object'
typeof [] // 'object'
typeof(() => {}) // 'function'
上面代碼的輸出結果可以看出,
-
null
的判定有誤差,得到的結果
如果使用 typeof
,null
得到的結果是object
- 操作符對對象類型及其子類型,例如函數(可調用對象)、數組(有序索引對象)等進行判定,則除了函數都會得到
object
的結果。
綜上可以看出typeOf
對于判斷類型還有一些不足,在對象的子類型和null
情況下。
instanceof
通過 instanceof
操作符也可以對對象類型進行判定,其原理就是測試構造函數的prototype
是否出現在被檢測對象的原型鏈上。
[] instanceof Array // true
({}) instanceof Object // true
(()=>{}) instanceof Function // true
復制代碼注意:instanceof
也不是萬能的。
舉個例子:
let arr = []
let obj = {}
arr instanceof Array // true
arr instanceof Object // true
obj instanceof Object // true
在這個例子中,arr
數組相當于 new Array()
出的一個實例,所以 arr.__proto__ === Array.prototype
,又因為 Array
屬于 Object
子類型,即Array.prototype.__proto__ === Object.prototype
,因此 Object
構造函數在 arr
的原型鏈上。所以 instanceof
仍然無法優雅的判斷一個值到底屬于數組還是普通對象。
還有一點需要說明下,有些開發者會說 Object.prototype.__proto__ === null
,豈不是說 arr instanceof null
也應該為 true
,這個語句其實會報錯提示右側參數應該為對象,這也印證 typeof null
的結果為 object
真的只是javascript
中的一個bug
。
Object.prototype.toString()
可以說是判定 JavaScript
中數據類型的終極解決方法了,具體用法請看以下代碼:
Object.prototype.toString.call({}) // '[object Object]'
Object.prototype.toString.call([]) // '[object Array]'
Object.prototype.toString.call(() => {}) // '[object Function]'
Object.prototype.toString.call('seymoe') // '[object String]'
Object.prototype.toString.call(1) // '[object Number]'
Object.prototype.toString.call(true) // '[object Boolean]'
Object.prototype.toString.call(Symbol()) // '[object Symbol]'
Object.prototype.toString.call(null) // '[object Null]'
Object.prototype.toString.call(undefined) // '[object Undefined]'
Object.prototype.toString.call(new Date()) // '[object Date]'
Object.prototype.toString.call(Math) // '[object Math]'
Object.prototype.toString.call(new Set()) // '[object Set]'
Object.prototype.toString.call(new WeakSet()) // '[object WeakSet]'
Object.prototype.toString.call(new Map()) // '[object Map]'
Object.prototype.toString.call(new WeakMap()) // '[object WeakMap]'
我們可以發現該方法在傳入任何類型的值都能返回對應準確的對象類型。用法雖簡單明了,但其中有幾個點需要理解清楚:
- 該方法本質就是依托
Object.prototype.toString()
方法得到對象內部屬性[[Class]]
- 傳入原始類型卻能夠判定出結果是因為對值進行了包裝
-
null
和undefined
能夠輸出結果是內部實現有做處理
NaN相關總結
NaN
的概念
NaN
是一個全局對象的屬性,NaN
是一個全局對象的屬性,NaN
是一種特殊的Number
類型。
什么時候返回NaN (開篇第二道題也得到解決)
- 無窮大除以無窮大
- 給任意負數做開方運算
- 算數運算符與不是數字或無法轉換為數字的操作數一起使用
- 字符串解析成數字
一些例子:
Infinity / Infinity; // 無窮大除以無窮大
Math.sqrt(-1); // 給任意負數做開方運算
'a' - 1; // 算數運算符與不是數字或無法轉換為數字的操作數一起使用
'a' * 1;
'a' / 1;
parseInt('a'); // 字符串解析成數字
parseFloat('a');
Number('a'); //NaN
'abc' - 1 // NaN
undefined + 1 // NaN
//一元運算符(注意點)
+'abc' // NaN
-'abc' // NaN
誤區
toString
和String
的區別
toString
-
toString()
可以將數據都轉為字符串,但是null
和undefined
不可以轉換。console.log(null.toString()) //報錯 TypeError: Cannot read property 'toString' of null console.log(undefined.toString()) //報錯 TypeError: Cannot read property 'toString' of undefined
-
toString()
括號中可以寫數字,代表進制二進制:.toString(2);
八進制:.toString(8);
十進制:.toString(10);
十六進制:.toString(16);
String
-
String()
可以將null
和undefined
轉換為字符串,但是沒法轉進制字符串console.log(String(null)); // null console.log(String(undefined)); // undefined
作者:koala
鏈接:https://segmentfault.com/a/1190000019760110
求點贊,求關注~