JS 变量类型
JS中有 6 种原始值,分别是:
- boolean
- number
- string
- undefined
- symbol
- null
引用类型:
- 对象
- 数组
- 函数
JS中使用typeof能得到哪些类型?
其中一个奇怪的 null,虽然是基本变量,但是因为设计的时候null
是全0,而对象是000
开头,所以有这个误判。
- boolean
- number
- string
- undefined
- symbol
- object
- function
- bigint
instanceof 能正确判断对象的原理是什么?
判断一个对象与构造函数是否在一个原型链上
const Person = function() {} const p1 = new Person() p1 instanceof Person // true var str = 'hello world' str instanceof String // false var str1 = new String('hello world') str1 instanceof String // true
实现一个类型判断函数
- 判断null
- 判断基础类型
- 使用
Object.prototype.toString.call(target)
来判断引用类型
注意: 一定是使用call
来调用,不然是判断的Object.prototype的类型
之所以要先判断是否为基本类型是因为:虽然Object.prototype.toString.call()
能判断出某值是:number/string/boolean,但是其实在包装的时候是把他们先转成了对象然后再判断类型的。 但是JS中包装类型和原始类型还是有差别的,因为对一个包装类型来说,typeof的值是object
/** * 类型判断 */ function getType(target) { //先处理最特殊的Null if(target === null) { return 'null'; } //判断是不是基础类型 const typeOfT = typeof target if(typeOfT !== 'object') { return typeOfT; } //肯定是引用类型了 const template = { "[object Object]": "object", "[object Array]" : "array", // 一些包装类型 "[object String]": "object - string", "[object Number]": "object - number", "[object Boolean]": "object - boolean" }; const typeStr = Object.prototype.toString.call(target); return template[typeStr]; }
转Boolean
以下都为假值,其他所有值都转为 true,包括所有对象(空对象,空数组也转为真)。
- false
- undfined
- null
- ''
- NaN
- 0
- -0
对象转基本类型
对象在转换基本类型时,会调用valueOf
, 需要转成字符类型时调用toString
。
var a = { valueOf() { return 0; }, toString() { return '1'; } } 1 + a // 1 '1'.concat(a) //"11"
也可以重写 Symbol.toPrimitive
,该方法在转基本类型时调用优先级最高。 Symbol.toPrimitive 指将被调用的指定函数值的属性转换为相对应的原始值。
类型转换
运算中其中一方为字符串,那么就会把另一方也转换为字符串
如果一方不是字符串或者数字,那么会将它转换为数字或者字符串
1 + '1' // '11' true + true // 2 4 + [1,2,3] // "41,2,3"
还需要注意这个表达式'a' + + 'b'
'a' + + 'b' // -> "aNaN"
因为 + 'b' 等于 NaN,所以结果为 "aNaN",你可能也会在一些代码中看到过 + '1' 的形式来快速获取 number 类型。
100 +
问题
'100' + 100 // "100100" 100 + '100' // "100100" 100 + true // 101 100 + false // 100 100 + undefined //NaN 100 + null // 100
"a common string"为什么会有length属性
通过字面量的方式创建:var a = 'string';,这时它就是基本类型值;通过构造函数的方式创建:var a = new String('string');这时它是对象类型。
基本类型是没有属性和方法的,但仍然可以使用对象才有的属性方法。这时因为在对基本类型使用属性方法的时候,后台会隐式的创建这个基本类型的对象,之后再销毁这个对象
== 操作符
对于 == 来说,如果对比双方的类型不一样的话,就会进行类型转换
判断流程:
- 首先会判断两者类型是否相同。相同的话就是比大小了
- 类型不相同的话,那么就会进行类型转换
- 会先判断是否在对比 null 和 undefined,是的话就会返回 true
- 判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number
1 == '1' ↓ 1 == 1
- 判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断
'1' == true ↓ '1' == 1 ↓ 1 == 1
- 判断其中一方是否为 object 且另一方为 string、number 或者 symbol,是的话就会把 object 转为原始类型再进行判断
'1' == { a: 'b' } ↓ '1' == '[object Object]'
- 两边都是对象的话,那么只要不是同一对象的不同引用,都为false
注意,只要出现NaN,就一定是false,因为就连NaN自己都不等于NaN
对于NaN,判断的方法是使用全局函数 isNaN()
=== 操作符
不转类型,直接判断类型和值是否相同。
但是 NaN === NaN 还是false
{} 等于true还是false
var a = {}; a == true // -> ? a == false // -> ?
答案是两个都为false
因为 a.toString() -> '[object Object]' -> NaN
1 与 Number(1)有什么区别
var a = Number(1) // 1 var b = new Number(1) // Number {[[PrimitiveValue]]: 1} typeof (a) // number typeof (b) // object a == b // true
- var a = 1 是一个常量,而 Number(1)是一个函数
- new Number(1)返回的是一个对象
- a==b 为 true 是因为所以在求值过程中,总是会强制转为原始数据类型而非对象,例如下面的代码:
typeof 123 // "number" typeof new Number(123) // "object" 123 instanceof Number // false (new Number(123)) instanceof Number // true 123 === new Number(123) // false
console.log(!!(new Boolean(false))输出什么 [易混淆]
true
布尔的包装对象 Boolean 的对象实例,对象只有在 null 与 undefined 时,才会认定为布尔的 false 值,布尔包装对象本身是个对象,对象->布尔 都是 true,所以 new Boolean(false)其实是布尔的 true,看下面这段代码:
if(new Boolean(false)){ alert('true!!'); }
只有使用了 valueOf 后才是真正的转换布尔值,与上面包装对象与原始资料转换说明的相同:
!!(new Boolean(false)) //true (new Boolean(false)).valueOf() //false
如何判断一个数据是不是Array
Array.isArray(obj)
- ECMAScript 5种的函数,当使用ie8的时候就会出现问题。
obj instanceof Array
- 当用来检测在不同的window或iframe里构造的数组时会失败。这是因为每一个iframe都有它自己的执行环境,彼此之间并不共享原型链,所以此时的判断一个对象是否为数组就会失败。此时我们有一个更好的方式去判断一个对象是否为数组。
Object.prototype.toString.call(obj) == '[object Array]'
- 这个方法比较靠谱
obj.constructor === Array
- constructor属性返回对创建此对象的函数的引用
Object.prototype.toString
如果是原始类型,他会将原始类型包装为引用类型,然后调用对应方法
function dd(){} var toString = Object.prototype.toString; toString.call(dd); //[object Function] toString.call(new Object); //[object Object] toString.call(new Array); //[object Array] toString.call(new Date); //[object Date] toString.call(new String); //[object String] toString.call(Math); //[object Math] toString.call(undefined); //[object Undefined] toString.call(null); //[object Null] toString.call(123) //[object Number] toString.call('abc') //[object String]
obj.toString() 和Object.prototype.toString.call(obj)
同样是检测对象obj调用toString方法,obj.toString()的结果和Object.prototype.toString.call(obj)的结果不一样,这是为什么?
这是因为toString为Object的原型方法,而Array ,function等类型作为Object的实例,都重写了toString方法。不同的对象类型调用toString方法时,根据原型链的知识,调用的是对应的重写之后的toString方法(function类型返回内容为函数体的字符串,Array类型返回元素组成的字符串.....),而不会去调用Object上原型toString方法(返回对象的具体类型),所以采用obj.toString()不能得到其对象类型,只能将obj转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用Object上原型toString方法。
输出以下代码运行结果
1 + "1" 2 * "2" [1, 2] + [2, 1] "a" + + "b"
- 1 + "1"
- 加性操作符:如果只有一个操作数是字符串,则将另一个操作数转换为字符串,然后再将两个字符串拼接起来
- 所以值为:“11”
- 2 * "2"
- 乘性操作符:如果有一个操作数不是数值,则在后台调用 Number()将其转换为数值
- [1, 2] + [2, 1]
- Javascript中所有对象基本都是先调用valueOf方法,如果不是数值,再调用toString方法。
- 所以两个数组对象的toString方法相加,值为:"1,22,1"
- "a" + + "b"
- 后边的“+”将作为一元操作符,如果操作数是字符串,将调用Number方法将该操作数转为数值,如果操作数无法转为数值,则为NaN。
- 所以值为:"aNaN"