We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
之前面试了几个开发者,他们确实做过不少项目,能力也是不错的,但是发现js基础不扎实, 于是决定写一下这篇javascrip数据类型相关的基础文章,其实也不仅仅是因为面试了他们,之前自己在面试的时候,也曾经被虐过,面试官说过的最深刻的一句话我到现在都记得。
基础很重要,只有基础好才会很少出bug,大多数的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
本篇文章会以一个面试官问问题的角度来进行分析讲解
面试官:说一说javascript中有哪些数据类型?
javascript
JavaScript 中共有七种内置数据类型,包括基本类型和对象类型。
JavaScript
基本类型分为以下六种:
注意:
string 、number 、boolean 和 null undefined 这五种类型统称为原始类型(Primitive),表示不能再细分下去的基本类型;
string
number
boolean
null
undefined
symbol是ES6中新增的数据类型,symbol 表示独一无二的值,通过 Symbol 函数调用生成,由于生成的 symbol 值为原始类型,所以 Symbol 函数不能使用 new 调用;
symbol
Symbol
new
null 和 undefined 通常被认为是特殊值,这两种类型的值唯一,就是其本身。
对象类型也叫引用类型,array和function是对象的子类型。对象在逻辑上是属性的无序集合,是存放各种值的容器。对象值存储的是引用地址,所以和基本类型值不可变的特性不同,对象值是可变的。
array
function
面试官:说说你对javascript是弱类型语言的理解?
JavaScript 是弱类型语言,而且JavaScript 声明变量的时候并没有预先确定的类型,变量的类型就是其值的类型,也就是说变量当前的类型由其值所决定,夸张点说上一秒种的String,下一秒可能就是个Number类型了,这个过程可能就进行了某些操作发生了强制类型转换。虽然弱类型的这种不需要预先确定类型的特性给我们带来了便利,同时也会给我们带来困扰,为了能充分利用该特性就必须掌握类型转换的原理。
String
Number
面试官:javascript中强制类型转换是一个非常易出现bug的点,知道强制转换时候的规则吗?
注:规则最好配合下面什么时候发生转换使用这些规则看效果更佳。
ToPrimitive
ToPrimitive对原始类型不发生转换处理,只针对引用类型(object)的,其目的是将引用类型(object)转换为非对象类型,也就是原始类型。
ToPrimitive 运算符接受一个值,和一个可选的期望类型作参数。ToPrimitive 运算符将值转换为非对象类型,如果对象有能力被转换为不止一种原语类型,可以使用可选的 期望类型 来暗示那个类型。
转换后的结果原始类型是由期望类型决定的,期望类型其实就是我们传递的type。直接看下面比较清楚。 ToPrimitive方法大概长这么个样子具体如下。
type
/** * @obj 需要转换的对象 * @type 期望转换为的原始数据类型,可选 */ ToPrimitive(obj,type)
obj
toString
return
valueOf
TypeError
Date
对于Date数据类型,我们更多期望获得的是其转为时间后的字符串,而非毫秒值(时间戳),如果为number,则会取到对应的毫秒值,显然字符串使用更多。 其他类型对象按照取值的类型操作即可。
ToPrimitive转成何种原始类型,取决于type,type参数可选,若指定,则按照指定类型转换,若不指定,默认根据实用情况分两种情况,Date为string,其余对象为number。那么什么时候会指定type类型呢,那就要看下面两种转换方式了。
Object.prototype.toString()
toString() 方法返回一个表示该对象的字符串。
toString()
每个对象都有一个 toString() 方法,当对象被表示为文本值时或者当以期望字符串的方式引用对象时,该方法被自动调用。
这里先记住,valueOf() 和 toString() 在特定的场合下会自行调用。
valueOf()
Object.prototype.valueOf()方法返回指定对象的原始值。
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运算符转换规则:
NaN
true
false
注意:对象这里要先转换为原始值,调用ToPrimitive转换,type指定为number了,继续回到ToPrimitive进行转换。
String 运算符转换规则
'null'
'true'
'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 运算符转换规则
ToBoolean
除了下述 6 个值转换结果为 false,其他全部为 true:
假值以外的值都是真值。其中包括所有对象(包括空对象)的转换结果都是true,甚至连false对应的布尔对象new Boolean(false)也是true
new Boolean(false)
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详细举例说明如下:
'2'+obj2
obj2.valueof()
return 'a'
2a
对下面'2'+obj1详细举例说明如下:
'2'+obj1
{ a: 1, b: 2 }
return [object Object]
2[object Object]
代码中几种特殊对象的转换规则基本相同,就不一一说明,大家可以想一下流程。
注意:不管是对象还不是对象,都有一个转换为原始值的过程,也就是ToPrimitive转换,只不过原始类型转换后不发生变化,对象类型才会发生具体转换。
是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。
== 抽象相等比较与+运算符不同,不再是String优先,而是Number优先。 下面列举x == y的例子
x == y
x
y
1 == 2 //false
ToPrimitive()
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
ToNumber
//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
instanceof
通过 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
操作符对对象类型及其子类型,例如函数(可调用对象)、数组(有序索引对象)等进行判定,则除了函数都会得到 object 的结果。
综上可以看出typeOf对于判断类型还有一些不足,在对象的子类型和null情况下。
typeOf
通过 instanceof 操作符也可以对对象类型进行判定,其原理就是测试构造函数的 prototype 是否出现在被检测对象的原型链上。
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 仍然无法优雅的判断一个值到底属于数组还是普通对象。
arr
new Array()
arr.__proto__ === Array.prototype
Array
Object
Array.prototype.__proto__ === Object.prototype
还有一点需要说明下,有些开发者会说 Object.prototype.__proto__ === null,岂不是说 arr instanceof null 也应该为 true,这个语句其实会报错提示右侧参数应该为对象,这也印证 typeof null 的结果为 object 真的只是javascript中的一个 bug 。
Object.prototype.__proto__ === null
arr instanceof null
typeof null
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]'
我们可以发现该方法在传入任何类型的值都能返回对应准确的对象类型。用法虽简单明了,但其中有几个点需要理解清楚:
[[Class]]
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()可以将数据都转为字符串,但是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()可以将null和undefined转换为字符串,但是没法转进制字符串
String()
console.log(String(null)); // null console.log(String(undefined)); // undefined
The text was updated successfully, but these errors were encountered:
Merge pull request #1 from koala-coding/master
0f4a136
merge
输出NaN的原因: 在函数inner里面使用var声明了var b = 3, var具有声明提升的特性 进入inner函数之后会先声明b变量,但未赋值。 而后执行b++ 等价于 b = undefined+1 隐式转换成NaN。 如果在 var b = 3; //下面加一行 console.log(b) 则打印输出 3
Sorry, something went wrong.
No branches or pull requests
前言
之前面试了几个开发者,他们确实做过不少项目,能力也是不错的,但是发现js基础不扎实, 于是决定写一下这篇javascrip数据类型相关的基础文章,其实也不仅仅是因为面试了他们,之前自己在面试的时候,也曾经被虐过,面试官说过的最深刻的一句话我到现在都记得。
这里给出两道我们公司数据类型基础相关的面试题和答案,如果都能做对并且知道为什么(可以选择忽略本文章):
本篇文章会以一个面试官问问题的角度来进行分析讲解
js中的数据类型
JavaScript
中共有七种内置数据类型,包括基本类型和对象类型。基本类型
基本类型分为以下六种:
注意:
string
、number
、boolean
和null
undefined
这五种类型统称为原始类型(Primitive),表示不能再细分下去的基本类型;symbol
是ES6中新增的数据类型,symbol
表示独一无二的值,通过Symbol
函数调用生成,由于生成的 symbol 值为原始类型,所以Symbol
函数不能使用new
调用;null
和undefined
通常被认为是特殊值,这两种类型的值唯一,就是其本身。对象类型
对象类型也叫引用类型,
array
和function
是对象的子类型。对象在逻辑上是属性的无序集合,是存放各种值的容器。对象值存储的是引用地址,所以和基本类型值不可变的特性不同,对象值是可变的。js弱类型语言
JavaScript
是弱类型语言,而且JavaScript
声明变量的时候并没有预先确定的类型,变量的类型就是其值的类型,也就是说变量当前的类型由其值所决定,夸张点说上一秒种的String
,下一秒可能就是个Number
类型了,这个过程可能就进行了某些操作发生了强制类型转换。虽然弱类型的这种不需要预先确定类型的特性给我们带来了便利,同时也会给我们带来困扰,为了能充分利用该特性就必须掌握类型转换的原理。js中的强制转换规则
注:规则最好配合下面什么时候发生转换使用这些规则看效果更佳。
ToPrimitive
(转换为原始值)ToPrimitive
对原始类型不发生转换处理,只针对引用类型(object)的,其目的是将引用类型(object)转换为非对象类型,也就是原始类型。ToPrimitive
运算符接受一个值,和一个可选的期望类型作参数。ToPrimitive
运算符将值转换为非对象类型,如果对象有能力被转换为不止一种原语类型,可以使用可选的 期望类型 来暗示那个类型。转换后的结果原始类型是由期望类型决定的,期望类型其实就是我们传递的
type
。直接看下面比较清楚。ToPrimitive
方法大概长这么个样子具体如下。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数据类型特殊说明:
对于
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
实现:对照代码会更清晰一些:
Number
Number
运算符转换规则:null
转换为 0undefined
转换为NaN
true
转换为 1,false
转换为 0NaN
String
String
运算符转换规则null
转换为'null'
undefined
转换为undefined
true
转换为'true'
,false
转换为'false'
Boolean
ToBoolean
运算符转换规则除了下述 6 个值转换结果为
false
,其他全部为true
:假值以外的值都是真值。其中包括所有对象(包括空对象)的转换结果都是
true
,甚至连false
对应的布尔对象new Boolean(false)
也是true
js转换规则不同场景应用
什么时候自动转换为string类型
在没有对象的前提下
字符串的自动转换,主要发生在字符串的加法运算时。当一个值为字符串,另一个值为非字符串,则后者转为字符串。
+
时候对下面
'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类型转换开发过程中可能出错的点:
预期输出结果120 实际输出结果10020
什么时候自动转换为Number类型
有加法运算符,但是无
String
类型的时候,都会优先转换为Number
类型例子:
除了加法运算符,其他运算符都会把运算自动转成数值。
例子:
判断等号也放在
Number
里面特殊说明== 抽象相等比较与+运算符不同,不再是
String
优先,而是Number
优先。下面列举
x == y
的例子x
,y
均为number
,直接比较没什么可解释的了
ToPrimitive()
type为number
进行转换,再进行后面比较boolean
,按照ToNumber
将boolean
转换为1或者0,再进行后面比较4.如果
x
为string
,y
为number
,x
转成number
进行比较什么时候进行布尔转换
if(obj)
,while(obj)
等判断时或者 三元运算符只能够包含布尔值条件部分的每个值都相当于
false
,使用否定运算符后,就变成了true
js中的数据类型判断
三种方式,分别为
typeof
、instanceof
和Object.prototype.toString()
typeof
通过
typeof
操作符来判断一个值属于哪种基本类型。上面代码的输出结果可以看出,
null
的判定有误差,得到的结果如果使用
typeof
,null
得到的结果是object
操作符对对象类型及其子类型,例如函数(可调用对象)、数组(有序索引对象)等进行判定,则除了函数都会得到
object
的结果。综上可以看出
typeOf
对于判断类型还有一些不足,在对象的子类型和null
情况下。instanceof
通过
instanceof
操作符也可以对对象类型进行判定,其原理就是测试构造函数的prototype
是否出现在被检测对象的原型链上。复制代码注意:
instanceof
也不是万能的。举个例子:
在这个例子中,
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()
方法得到对象内部属性[[Class]]
null
和undefined
能够输出结果是内部实现有做处理NaN相关总结
NaN
的概念NaN
是一个全局对象的属性,NaN
是一个全局对象的属性,NaN
是一种特殊的Number
类型。什么时候返回NaN (开篇第二道题也得到解决)
一些例子:
误区
toString
和String
的区别toString
toString()
可以将数据都转为字符串,但是null
和undefined
不可以转换。toString()
括号中可以写数字,代表进制二进制:.toString(2);
八进制:.toString(8);
十进制:.toString(10);
十六进制:.toString(16);
String
String()
可以将null
和undefined
转换为字符串,但是没法转进制字符串The text was updated successfully, but these errors were encountered: