JS-原始值/引用值/类型转换

136 阅读5分钟

原始值 (栈内存stack-拷贝)

  1. Number
  2. String
  3. Boolean
  4. undefined
  5. null 空值(初始化组件/函数、销毁函数、占位时可用)

JS是根据值来判断数据类型的

原始值是永久保存、不可改变的,只会覆盖

var a = 3;
var b = a; //3 拷贝a的值
//原本a的值改变(重新开了一个空间,令a=1)
//但是3还是存在的(占内存),只是没有名字
a = 1; 
console.log(b); //3

引用值 (堆内存heap)

值存在于堆内存中(散列结构),指针(地址)存在于栈内存中

  1. object
  2. array
  3. function
  4. date
  5. RegExp
var arr1 = [1, 2, 3, 4];
var arr2 = arr1;
arr1.push(5)

结果一致

如果让arr1 = [1, 2],就会有不一样的结果

数组重新赋值了就和原来的值没关系了,重新赋值和修改值是不一样的

typeof()

能判断出来的值如下:

  1. number
  2. string
  3. boolean
  4. 对象object/数组array/null(bug) -> Object(引用类型)
  5. undefined
  6. 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的值