Appearance
变量类型
变量类型包括
- 六种原始类型,包括Boolean、String、Number、Null、Undefined、Symbol
- 引用类型,Object对象
类型判断用到哪些方法?
- typeof,可以得到undefined boolean number string object function、symbol等类型结果
- 注意对于数组、null和object而言,typeof都会返回
'object'
- 注意对于数组、null和object而言,typeof都会返回
- instanceof,用于实例和构造函数的对应,其工作机制是:检测右侧构造函数的
prototype
属性是否出现在左侧实例对象的原型链上。 Object.prototype.toString.call(xxx)
将会得到一个包含变量类型的字符串
数据类型转换 == 和 ===
主要需要注意的是 == 操作符的比较规则,当两个数据的类型不一致时会发生类型转换,且可能同时发生类型转换,对应规则如下图所示
总结一下==
比较的规则
null == undefined
,反之亦然;- 字符串与数字比较,会将字符串转换成数字然后进行比较
- 布尔值与任何类型比较,都先将布尔值转换成数字,然后再进行比较
- 字符串与对象比较,先将对象传入toPrimitive,然后与字符串进行对比;数字同理
关于对象转原始类型ToPrimitive(input, PreferredType?)
,可以参考:
其中hint的取值可以为number
、string
或default
,表示当前语境传入的值(或者说是表示这是一个什么类型的运算),常见的运算中
js
var a = {
[Symbol.toPrimitive](hint) {
console.log(hint)
}
}
+a // hint为number
a + 1 // hint为default
// 下面情况hint为string
var o = {}
o[a] = 'xx' // string
alert(a) // string
parseInt(a) // string
ToPrimitive
的具体流程如下:
- 如果对象存在
Symbol.toPrimitive
,调用obj[Symbol.toPrimitive](hint)
,- 其中hint为当前语境传入的值(或者说是表示这是一个什么类型的运算),取
number
、string
或default
; *如果不存在Symbol.toPrimitive
,且hint
取值是 "string": - 无论是否存在,先调用
obj.toString()
,如果返回了原始值,则使用该值;否则调用obj.valueOf()
。
- 其中hint为当前语境传入的值(或者说是表示这是一个什么类型的运算),取
- 否则(也就是 hint 取值是 "number" 或 "default" 的情况):
- 无论是否存在,先调用
obj.valueOf()
,如果返回了原始值,则使用该值;否则调用obj.toString()
。
- 无论是否存在,先调用
需要注意
Symbol.toPrimitive
方法或最后调用的obj.toString
只能返回原始值,否则会抛出TypeError
- 我们创建的字面量对象
{}
和[]
不包含Symbol.toPrimitive
方法,且valueOf
方法返回的是自身,仍旧为引用值 - 但是
Date
对象实现了默认的Symbol.toPrimitive
方法,且返回的返回的是date.toString
的值
js
let user = {
name: "John",
money: 1000,
[Symbol.toPrimitive](hint) {
// 手动修改toPrimitive的值
return false
}
};
console.log(user == false) // true
// 先调用valueOf,返回[],需要再调用toString,返回'', 表达式变成'' == false,
// 然后将布尔值转换为数字, '' == 0,
// 字符串与数字比较时将字符串转换为数字,表达式变为 0 == 0,返回true
console.log([] == false)
最后,记住上面的比较只会发生在原始值和对象之间,当二者均为引用对象时,比较的是内存地址而不是上面的运算规则
实战一下
js
console.log(null == undefined) // true
console.log(undefined == null) // true
console.log(undefined == false) // false NaN !== 0
console.log(undefined == true) // false NaN !== 1
console.log([] == '') // true, '' == '' => 0 == 0
console.log([] == true) // false, '' == true => 0 === 1
console.log([] == ![]) // true, Boolean([])为true, [] == false => 0 == 0
console.log([] == []) // false, 数组地址不同
console.log([0] == false) // true '0' == 0 => 0 == 0
console.log({} == {}) // false,对象地址不同
console.log({} == !{}) // false, {} == false => '[object Object]' == 0 => 1 == 0
null 和 undefined的区别
- null 表示一个对象是“没有值”的值,也就是值为“空”;
- undefined 表示一个变量声明了没有初始化(赋值);
新增数据类型Symbol
Symbol 是ES6新增的一种基础数据类型,代表的是 唯一且不与其他任何类型相同的值。
js
// 创建一个唯一的symbol
var sy1 = Symbol("foo")
var sy2 = Symbol("foo")
console.log(sy1 === sy2) // false
// 创建一个可复用的symbol,其原理为:从全局symbol注册表中查找对应的symbol,如果没有则新建并返回;如果有则直接返回
var gSy = Symbol.for("foo")
var gSy2 = Symbol.for("foo")
console.log(sy1 == gSy) // false
console.log(gSy2 == gSy) // true
console.log(Symbol.keyFor(gSy)) // foo
Symbol的一个作用是当做对象的属性名。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。
但是需要注意的是:Symbol 作为属性名,该属性不会出现在for...in、for...of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回,通过Object.getOwnPropertySymbols
方法可以获取指定对应的所有Symbol属性名
Symbol的另外一个作用是:可以创建唯一的枚举值,如redux中的actionType等。
函数参数传递
JS中的变量类型可以分为基础类型和引用类型,需要注意的是:JS中的函数参数传递,都是按值进行的
- 原始类型的处理方式是,将参数的值完全复制一份,按值传递,
- 对于传递过来的变量进行修改,不会影响到原变量。
- 引用类型的处理方式是,将参数的地址复制一份,按值传递地址(更准确的说是引用复制传递)
- 对于变量的成员进行修改时,会直接影响原变量;
- 而如果对传递过来的变量进行重新赋值,则不会影响原变量,并且此后再修改变量的成员,也不会影响原变量。
js
var obj = {
x: 1
}
function foo(o){
o.x = 100
}
function foo2(o) {
o = 100
}
console.log(obj) // {x:1}
foo(obj)
console.log(obj) // {x:100}
foo2(obj)
console.log(obj) // // {x:100}
这种设计的意义在于:
- 按值传递的类型,复制一份存入栈内存,这类类型一般不占用太多内存,而且按值传递保证了其访问速度。
- 按共享传递的类型,是复制其引用,而不是整个复制其值(C 语言中的指针),保证过大的对象等不会因为不停复制内容而造成内存的浪费。
数字精度问题
0.1 + 0.2
不等于0.3
,这是因为,计算机的二进制实现和位数限制有些数无法有限表示。 JS采用双精度存储
- 1位用来表示符号位
- 11位用来表示指数
- 52位表示尾数
当表示浮点数时可能出现尾数无限循环,因此只能四舍五入,由于只存在0和1,这就会导致计算机浮点数运算出现误差。
解决办法
- 现将浮点数转换成整数,计算之后在除以扩大的倍数,如
(0.1*10+2*10)/10
- 手动实现计算库,按位进行计算
判断数组的几种方式
instanceOf
,主要是判断左侧对象的原型链上能不能找到右侧构造函数的原型
js
[] instanceOf Array // true
[] instanceOf Object // true
Object.prototype.toString
,常用语判断浏览器内置对象
Object.prototype.toString.call([]) // [object Array]
Array.isArray
主要用来检测对象是否为数组,他的优点在于可以检测来自iframe中的对象
js
var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
xArray = window.frames[window.frames.length-1].Array;
var arr = new xArray(1,2,3); // [1,2,3]
// Correctly checking for Array
Array.isArray(arr); // true
Object.prototype.toString.call(arr); // true
// Considered harmful, because doesn't work though iframes
arr instanceof Array; // false