原始值 (栈内存stack-拷贝)
- Number
- String
- Boolean
- undefined
- null 空值(初始化组件/函数、销毁函数、占位时可用)
JS是根据值来判断数据类型的
原始值是永久保存、不可改变的,只会覆盖
var a = 3;
var b = a; //3 拷贝a的值
//原本a的值改变(重新开了一个空间,令a=1)
//但是3还是存在的(占内存),只是没有名字
a = 1;
console.log(b); //3
引用值 (堆内存heap)
值存在于堆内存中(散列结构),指针(地址)存在于栈内存中
- object
- array
- function
- date
- RegExp
var arr1 = [1, 2, 3, 4];
var arr2 = arr1;
arr1.push(5)
结果一致
如果让arr1 = [1, 2],就会有不一样的结果
数组重新赋值了就和原来的值没关系了,重新赋值和修改值是不一样的
typeof()
能判断出来的值如下:
- number
- string
- boolean
- 对象object/数组array/null(bug) -> Object(引用类型)
- undefined
- function
null是空对象的指针/占位符,最早是引用类型,后来修改为原始值,但是由于不好修改,依旧保留这个bug
console.log(typeof(1 - '1')); //number
不是加法,不会转换成字符串,这里是隐式类型转换
console.log(a); //报错
console.log(typeof(a)); //undefined
console.log(typeof(typeof(a))); //str
console.log(typeof(typeof(123))); //str
typeof检查出来的数据类型都是字符串
封装typeof
function myTypeof(val){
var toStr = Object.prototype.toString;
var type = typeof(val);
var res = {
'[object Array]': 'array',
'[object Object]': 'object',
'[object Number]': 'object number',
'[object String]': 'object string',
'[object Boolean]': 'object boolean'
}
if (val === null) {
return 'null';
}else if(type === 'object'){
var ret = toStr.call(val);
return res[ret];
}else{
return type;
}
}
console.log(myTypeof([]));
类型转换
显式
Number()
var a = '123';
console.log(typeof(Number(a)) + ':' + Number(a)); //number:123
var a = 'true';
console.log(typeof(Number(a)) + ':' + Number(a)); //number:NaN
var a = '1A';
console.log(typeof(Number(a)) + ':' + Number(a)); //number:NaN
var a = true;
console.log(typeof(Number(a)) + ':' + Number(a)); //number:1
var a = null;
console.log(typeof(Number(a)) + ':' + Number(a)); //number:0
var a = undefined;
console.log(typeof(Number(a)) + ':' + Number(a)); //number:NaN
parseInt(val, 进制) 取整
从左往右取整,遇到字母就停止了,不会四舍五入
取不到数字一律NaN处理
var a = '12ad2';
console.log(typeof(parseInt(a)) + ':' + parseInt(a));//number:12
var a = '3.99';
console.log(typeof(parseInt(a)) + ':' + parseInt(a));//number:3
var a = true;
console.log(typeof(parseInt(a)) + ':' + parseInt(a));//number:NaN
var a = null;
console.log(typeof(parseInt(a)) + ':' + parseInt(a));//number:NaN
paeseFloat()
解析一个字符串,并返回一个浮点数,用法和parseInt()差不多
num.toFixed(n) 保留几位小数,四舍五入
String()
转换为字符串,但是一般加个空串就可以转换类型了
console.log(typeof(123 + ''));
val.toString(进制)
和String()没什么区别,另外这个方法也可以进行进制转换。
undefined、null不能用toString()方法(无法隐式的包装类,也没有原型)
var num = 1;
num.toString(); //new Number(1).toString()
原始值是没有属性的,包装类之后,Number调用自己的toString()方法
要注意Number.prototype上的toString方法和Object.prototype上的方法打印出来的值不一样
Object.prototype.toString.call(1); //打印出来的是[object Number]
Number.prototype.toString.call(1); // 打印出来的是'1'
Object.prototype上的toString()打印的是类型
Number.prototype.toString()打印的是值
所以Number.prototype.toString()就是对Object.prototype.toString()原型方法的重写
document.write
document.write打印出来的东西会经过隐式转换,转换成字符串
var num = 1;
var obj = {};
var obj2 = Object.create(null);
document.write(num); //1
document.write(obj); //[object Object]
document.write(obj2); //报错,没有原型,无法经过toString进行隐式类型转换
Boolean()
undefined、null、NaN、''、0、false是false 其他都是true
隐式
除了+,number和str进行运算比较,都是str->number
var a = '123';
a++;
console.log(a); //124
var a = 'a' + 1;
console.log(a); //a1
var a = 2 > 1 == 1;
console.log(a); //true
2 > 1 ? -> true
true == 1 -> true
var a = undefined == 0;
console.log(a); //false
要注意 undefined/null != 0
但是 undefined == null
例:
if(typeof(a) && (-true) + (+undefiened) + ''){
console.log('通过了');// √
}else{
console.log('没通过');
}
'undefined' && (-1) +NaN+ ''
'undefined' && 'NaN'
都是非空字符串,所以为真
console.log(!!' ' + !!'' - !!false || '未通过'); //1
1 + 0 - 0 -> true
!!' '不是空串
window.a || (window.a = '1');
console.log(window.a);
先看括号,括号的优先级是最高的
(window.a = '1') 把'1'赋值给a
所以或运算符前面的window.a已经有值了
不看后面的直接返回
isNaN()
先经过Number()进行隐式类型转换,再和NaN对比,取bool值。
'a'/NaN/undefined -> true
null -> false,因为null经过Number()转换结果是0
function isNaN1(num){
var res = Number(num);
//更改为var res = Number(num) + '';
if(res == 'NaN'){
return true;
}else{
return false;
}
}
console.log(isNaN1('abc')); //false 因为NaN !== NaN
包装类
原始值没有自己的方法和属性
var a = 1;
console.log(a.length); //undefined
var b = new Number(1); //变成实例化对象后的数字
//成为对象之后,理所当然的可以设置属性和方法
b.len = 1;
b.add = function(){
console.log(2);
}
console.log(b); // Number {1, len: 1, add: ƒ}
//对象 + 数字 可以进行运算
var d = b + 1;
//但是运算之后,对象又回到原始值
console.log(d); //2
包装:new Number() / new String() / new Boolean()
undefined和null也可以被包装
var test = new Number(undefined); // Number {NaN}
var test = new Number(null); // Number {0}
var test = new String(undefined); // String {'undefined'}
var test = new String(null); // String {'null'}
但是不能设置属性和方法
console.log(undefined.length); //报错
原始值没有方法和属性 -> 包装类
var a = 123;
a.len = 3;
//JS自动纠正,尽可能合理化
// new Number(123).len = 3 赋值就赋值,但是new Number(a)无法保存,不可能系统声明一个变量保存,就删了a.len
//所以a.len不存在,undefined
console.log(a.len); //undefined
相当于:
var obj = {
name: 'obj'
}
console.log(obj);
delete obj.name;
console.log(obj); //{}
console.log(obj.name); //undefined
解决办法: 把a变成一个对象,自然可以加属性
var a = new Number(123);
a.len = 3;
console.log(a.len); //3
和number不同,字符串有length属性的原因是:
str.length -> new String(str).length
new String()有length属性,不需要保存,直接用
字符串是没有length属性的,中间经过一层包装类
var str = 'abc';
console.log(str.length); //3
//console.log(new String(str).length);
数组可以用length截断,str不行
var str = 'abc';
str.length = 1; //new String(str).length = 1 无法保存,删除
console.log(str.length); //3
//console.log(new String(str).length); 还是访问原来str.length的值