推荐大家的使用本篇文章的方式:
先看问题自己会不会,如果会的话,要自己说一遍,组织好语言。
一. 数据类型
1. JS数据类型的种类
总共7钟。
-
Undefined 未定义
表示变量不含有任何值,是未定义的状态。 -
NULL 空
-
Boolean 布尔值
-
Number 数字
-
String 字符串
-
Object 对象
分三个子类型 object、array、function -
Symbol 符号 (es6新加)
类型:
分为两种,基本数据类型(number boolean string symbol)和引用数据类型(object 比如:array、function、data)。
两种类型的区别在于存储位置的不同:
● 基本数据类型直接存储在栈(stack)中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储;
● 引用数据类型存储在堆(heap)中的对象,占据空间大、大小不固定。如果存储在栈中,将会影响程序运行的性能;引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
2. undefined && null
null与undefined都可以表示“没有”,含义非常相似。将一个变量赋值为undefined或null,老实说,语法效果几乎没区别。
undefined == null //返回true
undefined === null //返回false
Number(null) //0
Number(undefined) //NaN
console.log(typeof undefined)// undefined
console.log(typeof null)// object
undefined 代表的含义是未定义,null 代表的含义是空对象。一般变量声明了但还没有定义的时候会返回 undefined,null主要用于赋值给一些可能会返回对象的变量,作为初始化。
// 变量声明了,但没有赋值
var i;
i // undefined
// 调用函数时,应该提供的参数没有提供,该参数等于 undefined
function f(x) {
return x;
}
f() // undefined
// 对象没有赋值的属性
var o = new Object();
o.p // undefined
// 函数没有返回值时,默认返回 undefined
function f() {
}
f() // undefined
3. 数据类型的检测方式
- typeof
会把array和null都判断为object,其余都判断正确
console.log(typeof 2); // number
console.log(typeof true); // boolean
console.log(typeof 'str'); // string
console.log(typeof function(){
}); // function
console.log(typeof {
}); // object
console.log(typeof []); // object
console.log(typeof null); // object
console.log(typeof undefined); //undefined
typeof NaN; // "number"
可以利用tyepeof undefined这一点就用来检查一个没有声明的变量而不报错。
typeof array返回object:
这表示在 JavaScript 内部,数组本质上只是一种特殊的对象
typeof null返回object:
这是由于历史原因造成的。1995年的 JavaScript 语言第一版,只设计了五种数据类型(对象、整数、浮点数、字符串和布尔值),没考虑null,只把它当作object的一种特殊值。后来null独立出来,作为一种单独的数据类型,为了兼容以前的代码,typeof null返回object就没法改变了。
- instanceof
返回一个布尔值,表示对象是否为某个构造函数的实例。左边是实例对象,右边是构造函数。可区分数组和对象。只能判断引用数据类型,不能判断基本数据类型。运行机制是判断在其原型链中能否找到该类型的原型,所以还是会将数组判定成Object类型。
console.log(2 instanceof Number); // false
console.log(true instanceof Boolean); // false
console.log('str' instanceof String); // false
console.log([] instanceof Array); // true
console.log([] instanceof Object); // true
console.log(null instanceof Object); // false
console.log(function(){
} instanceof Function); // true
console.log({
} instanceof Object); //true
利用instanceof运算符,还可以巧妙地解决,调用构造函数时,忘了加new命令的问题。
function Fubar (foo, bar) {
if (this instanceof Fubar) {
this._foo = foo;
this._bar = bar;
} else {
return new Fubar(foo, bar);
}
}
上面代码使用instanceof运算符,在函数体内部判断this关键字是否为构造函数Fubar的实例。如果不是,就表明忘了加new命令。
- constructor
有两个作用,一是判断数据的类型,二是对象实例通过constrcutor对象访问它的构造函数。需要注意,如果创建一个对象来改变它的原型,constructor 就不能来判断数据类型了。
console.log((2).constructor === Number); // true
console.log((true).constructor === Boolean); // true
console.log(('str').constructor === String); // true
console.log(([]).constructor === Array); // true
console.log(([]).constructor === Object); // false
console.log((function (){
}).constructor === Function); // true
console.log(({
}).constructor === Object); //true
function Fn(){
};
Fn.prototype = new Array();
var f = new Fn();
console.log(f.constructor===Fn); // false
console.log(f.constructor===Array); // true
- Object.prototype.toString.call()
Object.prototype.toString.call() 使用 Object 对象的原型方法 toString 来判断数据类型:
var a = Object.prototype.toString;
console.log(a.call(2));
console.log(a.call(true));
console.log(a.call('str'));
console.log(a.call([]));
console.log(a.call(function(){
}));
console.log(a.call({
}));
console.log(a.call(undefined));
console.log(a.call(null));
同样是检测对象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方法。
4. 判断数组的方式
- 原型链
obj.__proto__ === Array.prototype;
- Object.prototype.toString.call()
Object.prototype.toString.call(obj).slice(8,-1) === 'Array';
- Array.isArray()
Array.isArray(obj);
- instance
obj instance of Array
- Array.prototype.isPrototypeOf(obj)
Array.prototype.isPrototypeOf(obj)
5. 0.1+0.2 ! == 0.3
let n1 = 0.1, n2 = 0.2
console.log(n1 + n2) // 0.30000000000000004
(n1 + n2).toFixed(2) // 注意,toFixed为四舍五入
toFixed(num) 方法可把 Number 四舍五入为指定小数位数的数字。那为什么会出现这样的结果呢?
计算机是通过二进制的方式存储数据的,所以计算机计算0.1+0.2的时候,实际上是计算的两个数的二进制的和。0.1的二进制是0.0001100110011001100…(1100循环),0.2的二进制是:0.00110011001100…(1100循环),这两个数的二进制都是无限循环的数。那JavaScript是如何处理无限循环的二进制小数呢?
一般我们认为数字包括整数和小数,但是在 JavaScript 中只有一种数字类型:Number,它的实现遵循IEEE 754标准,使用64位固定长度来表示,也就是标准的double双精度浮点数。在二进制科学表示法中,双精度浮点数的小数部分最多只能保留52位,再加上前面的1,其实就是保留53位有效数字,剩余的需要舍去,遵从“0舍1入”的原则。
根据这个原则,0.1和0.2的二进制数相加,再转化为十进制数就是:0.30000000000000004。
6. 如何获取安全的undefined值?
因为 undefined 是一个标识符,所以可以被当作变量来使用和赋值,但是这样会影响 undefined 的正常判断。表达式 void ___ 没有返回值,因此返回结果是 undefined。void 并不改变表达式的结果,只是让表达式不返回值。因此可以用 void 0 来获得 undefined。
7. NaN
NaN 指“不是一个数字”(not a number),NaN 是一个“警戒值”(sentinel value,有特殊用途的常规值),用于指出数字类型中的错误情况,即“执行数学运算没有成功,这是失败后返回的结果”.
typeof NaN; // "number"
NaN 是一个特殊值,它和自身不相等,是唯一一个非自反(自反,reflexive,即 x === x 不成立)的值。而 NaN !== NaN 为 true。
判断NaN
● 函数 isNaN 接收参数后,会尝试将这个参数转换为数值,任何不能被转换为数值的的值都会返回 true,因此非数字值传入也会返回 true ,会影响 NaN 的判断。
● 函数 Number.isNaN 会首先判断传入参数是否为数字,如果是数字再继续判断是否为 NaN ,不会进行数据类型的转换,这种方法对于 NaN 的判断更为准确。
8. 类型转换
8.1 ==操作符的强制类型转换规则
对于 == 来说,如果对比双方的类型不一样,就会进行类型转换。假如对比 x 和 y 是否相同,就会进行如下判断流程:
- 首先会判断两者类型是否相同,相同的话就比较两者的大小;
- 类型不相同的话,就会进行类型转换;
- 会先判断是否在对比 null 和 undefined,是的话就会返回 true
- 判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number
- 判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断
- 判断其中一方是否为 object 且另一方为 string、number 或者 symbol,是的话就会把 object 转为原始类型再进行判断
8.2 其他值到字符串的转换
● Null 和 Undefined 类型 ,null 转换为 “null”,undefined 转换为 “undefined”,
● Boolean 类型,true 转换为 “true”,false 转换为 “false”。
● Number 类型的值直接转换,不过那些极小和极大的数字会使用指数形式。
● Symbol 类型的值直接转换,但是只允许显式强制类型转换,使用隐式强制类型转换会产生错误。
● 对普通对象来说,除非自行定义 toString() 方法,否则会调用 toString()(Object.prototype.toString())来返回内部属性 [[Class]] 的值,如"[object Object]"。如果对象有自己的 toString() 方法,字符串化时就会调用该方法并使用其返回值。
8.3 其他值到数字值的转换规则
● Undefined 类型的值转换为 NaN。
● Null 类型的值转换为 0。
● Boolean 类型的值,true 转换为 1,false 转换为 0。
● String 类型的值转换如同使用 Number() 函数进行转换,如果包含非数字值则转换为 NaN,空字符串为 0。
● Symbol 类型的值不能转换为数字,会报错。
● 对象(包括数组)会首先被转换为相应的基本类型值,如果返回的是非数字的基本类型值,则再遵循以上规则将其强制转换为数字。
9. Object.is() 与比较操作符 三等号、双等号 的区别?
● 使用双等号进行相等判断时,如果两边的类型不一致,则会进行强制类型转化后再进行比较。
● 使用三等号进行相等判断时,如果两边的类型不一致时,不会做强制类型准换,直接返回 false。
● 使用 Object.is 来进行相等判断时,一般情况下和三等号的判断相同,它处理了一些特殊的情况,比如 -0 和 +0 不再相等,两个 NaN 是相等的。
10. 如何判断一个对象是空对象
●使用JSON自带的.stringify方法来判断:
if(Json.stringify(Obj) == '{}' ){
console.log('空对象');
}
●使用ES6新增的方法Object.keys()来判断:
if(Object.keys(Obj).length < 0){
console.log('空对象');
}