以前面試了幾個開發者,他們確實作過很多項目,能力也是不錯的,可是發現js基礎不紮實, 因而決定寫一下這篇javascrip數據類型相關的基礎文章,其實也不只僅是由於面試了他們,以前本身在面試的時候,也曾經被虐過,面試官說過的最深入的一句話我到如今都記得。javascript
基礎很重要,只有基礎好纔會不多出
bug
,大多數的bug
都是基礎不紮實形成的。java
做者簡介:koala,專一完整的 Node.js 技術棧分享,從 JavaScript 到 Node.js,再到後端數據庫,祝您成爲優秀的高級 Node.js 工程師。【程序員成長指北】做者,Github 博客開源項目 github.com/koala-codin…git
這裏給出兩道咱們公司數據類型基礎相關的面試題和答案,若是都能作對而且知道爲何(能夠選擇忽略本文章):程序員
//類型轉換相關問題
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
複製代碼
本篇文章會以一個面試官問問題的角度來進行分析講解github
面試官:說一說
javascript
中有哪些數據類型?面試
JavaScript
中共有七種內置數據類型,包括基本類型和對象類型。數據庫
基本類型分爲如下六種:express
注意:後端
string
、number
、boolean
和 null
undefined
這五種類型統稱爲原始類型(Primitive),表示不能再細分下去的基本類型;數組
symbol
是ES6中新增的數據類型,symbol
表示獨一無二的值,經過 Symbol
函數調用生成,因爲生成的 symbol 值爲原始類型,因此 Symbol
函數不能使用new
調用;
null
和 undefined
一般被認爲是特殊值,這兩種類型的值惟一,就是其自己。
對象類型也叫引用類型,array
和function
是對象的子類型。對象在邏輯上是屬性的無序集合,是存放各類值的容器。對象值存儲的是引用地址,因此和基本類型值不可變的特性不一樣,對象值是可變的。
面試官:說說你對
javascript
是弱類型語言的理解?
JavaScript
是弱類型語言,並且JavaScript
聲明變量的時候並無預先肯定的類型,變量的類型就是其值的類型,也就是說變量當前的類型由其值所決定,誇張點說上一秒種的String
,下一秒可能就是個Number
類型了,這個過程可能就進行了某些操做發生了強制類型轉換。雖然弱類型的這種不須要預先肯定類型的特性給咱們帶來了便利,同時也會給咱們帶來困擾,爲了能充分利用該特性就必須掌握類型轉換的原理。
面試官:
javascript
中強制類型轉換是一個很是易出現bug
的點,知道強制轉換時候的規則嗎?
注:規則最好配合下面何時發生轉換使用這些規則看效果更佳。
ToPrimitive
(轉換爲原始值)ToPrimitive
對原始類型不發生轉換處理,只針對引用類型(object)的,其目的是將引用類型(object)轉換爲非對象類型,也就是原始類型。
ToPrimitive
運算符接受一個值,和一個可選的指望類型做參數。ToPrimitive
運算符將值轉換爲非對象類型,若是對象有能力被轉換爲不止一種原語類型,可使用可選的 指望類型 來暗示那個類型。
轉換後的結果原始類型是由指望類型決定的,指望類型其實就是咱們傳遞的type
。直接看下面比較清楚。 ToPrimitive
方法大概長這麼個樣子具體以下。
/** * @obj 須要轉換的對象 * @type 指望轉換爲的原始數據類型,可選 */
ToPrimitive(obj,type)
複製代碼
string
:obj
的toString
方法,若是爲原始值,則return
,不然進行第2步obj
的valueOf
方法,若是爲原始值,則return
,不然進行第3步TypeError
異常number
:obj
的valueOf
方法,若是爲原始值,則return
,不然進行第2步obj
的toString
方法,若是爲原始值,則return
,不然第3步TypeError
異常Date
,則type被設置爲String
Number
對於Date
數據類型,咱們更多指望得到的是其轉爲時間後的字符串,而非毫秒值(時間戳),若是爲number
,則會取到對應的毫秒值,顯然字符串使用更多。 其餘類型對象按照取值的類型操做便可。
ToPrimitive
總結ToPrimitive
轉成何種原始類型,取決於type,type參數可選,若指定,則按照指定類型轉換,若不指定,默認根據實用狀況分兩種狀況,Date
爲string
,其他對象爲number
。那麼何時會指定type類型呢,那就要看下面兩種轉換方式了。
Object.prototype.toString()
toString()
方法返回一個表示該對象的字符串。
每一個對象都有一個 toString()
方法,當對象被表示爲文本值時或者當以指望字符串的方式引用對象時,該方法被自動調用。
這裏先記住,valueOf()
和 toString()
在特定的場合下會自行調用。
Object.prototype.valueOf()
方法返回指定對象的原始值。
JavaScript
調用 valueOf()
方法用來把對象轉換成原始類型的值(數值、字符串和布爾值)。可是咱們不多須要本身調用此函數,valueOf
方法通常都會被 JavaScript
自動調用。
不一樣內置對象的valueOf
實現:
對照代碼會更清晰一些:
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
運算符轉換規則:
null
轉換爲 0undefined
轉換爲 NaN
true
轉換爲 1,false
轉換爲 0NaN
注意:對象這裏要先轉換爲原始值,調用
ToPrimitive
轉換,type指定爲number
了,繼續回到ToPrimitive
進行轉換。
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
複製代碼
ToBoolean
運算符轉換規則
除了下述 6 個值轉換結果爲 false
,其餘所有爲true
:
假值之外的值都是真值。其中包括全部對象(包括空對象)的轉換結果都是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
複製代碼
面試官問:知道了具體轉換成什麼的規則,可是都在什麼狀況下發生什麼樣的轉換呢?
在沒有對象的前提下
字符串的自動轉換,主要發生在字符串的加法運算時。當一個值爲字符串,另外一個值爲非字符串,則後者轉爲字符串。
'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
轉換,只不過原始類型轉換後不發生變化,對象類型纔會發生具體轉換。
var obj = {
width: '100'
};
obj.width + 20 // "10020"
複製代碼
預期輸出結果120 實際輸出結果10020
有加法運算符,可是無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
複製代碼
面試官問:如何判斷數據類型?怎麼判斷一個值究竟是數組類型仍是對象?
三種方式,分別爲 typeof
、instanceof
和Object.prototype.toString()
經過 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
操做符也能夠對對象類型進行斷定,其原理就是測試構造函數的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
是一種特殊的Number
類型。
一些例子:
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
複製代碼
關注公衆號:『程序員成長指北』, 獲取更多精選文章