一、数据类型
js中拥有动态类型,就是相同的变量可以用作不同的类型:类型是值的内部特征,它定义了值的行为,以使其区别于其他值。
1.1 基本数据类型
基本数据类型:Number.Boolean.String.Null.undefned.Symbol (ES6新增)
基本数据类型数据直接存储在栈中,由系统自动分配。按值进行访问,内部的值不可以改变。
var str = "123hello321";
str.toUpperCase(); // 123HELLO321
console.log(str); // 123hello321
1.2 引用数据类型
引用数据类型:Object (Object/Array/Function/Date/RegExp...)
引用数据类型在栈中存储的是指针和字面量,指针指向地址值对应的是真正的数据,由系统动态分配,用完不会立即释放,需要手动赋值null,释放内存地址。
var a = { name: '余光' };
var b;
b = a; //b也拥有了a的name属性
a.name = "yuguang"; //向a中更改name属性,b中也能看见
b.age = 23; //向b中添加age属性,a中也能看见,因为他们指向同一地址值
var c = { //c里面的属性和a.b不共享,因为它们的地址值不一样
name: '余光',
age: 23
};
区别:
变量数据类型:基本类型:保存的是基本类型的数据
引用类型:保存的是地址值
1.3 JS中的变量是没有类型的
var a = 100; // 严格地说 变量a没有类型,它所保存的 100是数字类型的
typeof a === 'number'; // 其实检测是=>typeof 100
a = 'string'
typeof a === 'string'; // true
//变量a既是number类型又是string类型,咋回事?
//因为变量a是没有数据类型的,是他里面能存储的值是number,string类型
二、判断数据类型的方法
1、typeof
不能区分null.Array和Object,均返回object
因为typeof是按照”值“存储在计算机中的二进制来检测的,000开头的都是object,null是0000
console.log(typeof [12, 23]); //->"Object"
typeof undefined // 'undefined'
typeof '10' // 'String'
typeof 10 // 'Number'
typeof false // 'Boolean'
typeof Symbol() // 'Symbol'
typeof Function // ‘function'
typeof null // ‘Object’
typeof [] // 'Object'
typeof {} // 'Object'
typeof null // "object" (因为一些以前的原因而不是'null')
typeof undefined // "undefined"
null === undefined // false
null == undefined // true
null === null // true
null == null // true
!null //true
isNaN(1 + null) // false
isNaN(1 + undefined) // true
2、instanceof
判断对象是否在另一个对象的原型链上(实例对象的隐式原型等于构造函数的显式原型)
坏处:当在原型上定义了一个其他变量,实例化对象也会继承新定义的变量,此时用instanceof判断就会造成影响,如:
function Fn() {
}
Fn.prototype = new Array;//->Fn子类继承了Array这个父类中的属性和方法
var f = new Fn;
console.log(f instanceof Array);//->true
console.log(f.constructor == Array);//->true
不能检测字面量创建出来的基本数据类型,即原始数据类型
3、constructor
a. 每一个实例对象都可以查看他的构造函数,可以判断array和function类型,不会像typeof一样均返回object
console.log([] instanceof Array);//->true
console.log([] instanceof Object);//->true
console.log([].constructor === Array);//->true
console.log([].constructor === Object);//->false
constructor可以避免instanceof检测数组的时候,用Object也是true的问题
console.log({}.constructor === Object);//true
console.log([].constructor === Object);//false
console.log(/^$/ instanceof RegExp);//->true
b. 可以判断原始数据类型和引用数据类型
<script>
let num = 12;
let obj = {};
console.log(num.constructor == Number);//true
console.log(obj.constructor == Object);//true
</script>
c. 原型链被修改后,检测结果可能会受影响,同instanceof
d. 最好在检测时,检测数据用括号括起来
<script>
1.constructor === Number; //报错,Invalid or unexceped token
(1).constructor === Number; //true
{}.constructor === Number; //报错,Invalid or unexceped token
({}).constructor === Number; //true
</script>
乱入:
对于引用数据类型来说,通过两种方式创建的对象没有区别,都是所属类的实例,原型上的方法可以继承,都是对象数据类型的值。
var ary = [];
var ary = new Array;
对于基本数据类型来说,两种方式创建的也都是所属类的实例,但他们的类型不同。通过字面量创建的类型是基本数据类型,通过new出来的类型是对象数据类型。
var num1 = 1;
var num2 = new Number("1");
console.log(typeof num1,typeof num2);//->"number" "object"
4、Object.prototype.toString.call(value)
value传入的待检测的数据,借用call方法改变toString()里this的指向,Object.prototype.toString用来判断数据类型,返回的是一个JSON字符串 "[ object 数据类型]",除了Object显式原型上的toString方法可以用来判断数据类型,其他的toString方法都是将数据转化为字符串类型的。Null和undefined的toString()方法不让使用
在JavaScript中,所有类都继承于Object,因此toString()方法应该也被继承了,但所有类在继承Object的时候,改写了toString()方法。 Object原型上的方法是可以输出数据类型的。因此我们想判断数据类型时,也只能使用原始方法。继而有了此方法:Object.prototype.toString.call(obj)
var num = 123
num.toString() // '123'
var str = 'hello'
str.toString() // 'hello'
var bool = false
bool.toString() // 'false'
var arr = [1, 2, 3]
arr.toString() // '1,2,3'
var obj = {lang:'zh'}
obj.toString() // '[object Object]'
var fn = function(){}
fn.toString() // 'function(){}'
null.toString() // Cannot read property 'toString' of null
undefined.toString() // Cannot read property 'toString' of undefined
三、数据类型转化
3.1 强制类型转化
number() string() boolean()
将数据强制转化为数值,字符串和布尔类型
特殊情况:
undefined | null | ' ' | 0 | |
Boolean() | false | false | false | false |
String() | ||||
Number() | NAN | 0 | 0 | 0 |
undefined和null用string()进行强制类型转化时,不会报错;但是用toString()方法会报错
3.2 隐式类型转化
隐式转化主要涉及到三种类型:
a. toPrimitive()
b. toNumber()
c. toString()
a. toPrimitive()
ToPrimitive(input, PreferredType?)
input是输入的值,PreferredType?可选值,可以为number或string,默认是number类型(Date除外,默认是string)。
过程:先判断input数据类型,如果是原始(基础)数据类型,则直接返回,如果不是,判断PreferredType的值,
若是Number,先调用valueof(),查看返回值,如果返回值依然不是原始数据类型,则调用toString(),若是则返回,若不是,则抛出err。
若是String,则先调用toString(),检查返回值类型,若是原始数据类型则返回,若不是,则调用valueof(),在判断返回值类型,若不是则抛出err。
valueof()在处理boolean/string/number这三类构造函数创造的基本数据类型时,会返回其原始值,其他类型都会返回对象本身。
var num = new Number('123');
num.valueOf(); // 123
var str = new String('12df');
str.valueOf(); // '12df'
var bool = new Boolean('fd');
bool.valueOf(); // true
var a = new Array();
a.valueOf() === a; // true
var b = new Object({});
b.valueOf() === b; // true
toString()会把除了下列的数据类型都返回该数据类型,JSON字符串
var num = new Number('123sd');
num.toString(); // 'NaN'
var str = new String('12df');
str.toString(); // '12df'
var bool = new Boolean('fd');
bool.toString(); // 'true'
var arr = new Array(1,2);
arr.toString(); // '1,2'
var d = new Date();
d.toString(); // "Wed Oct 11 2017 08:00:00 GMT+0800 (中国标准时间)"
var func = function () {}
func.toString(); // "function () {}"
乱入Date对象
Date对象的valueof方法会返回自1970年1月1日来的毫秒数,toString()会返回字符串形式表达的事件
b. toNumber()
参数 | 结果 |
undefined | NAN |
null | 0 |
boolean | true为1,false为0 |
string | 有字符串解析为数字,例如:‘324’转换为324,‘qwer’转换为NaN |
number | 不改变 |
object | 先进行 ToPrimitive(obj, Number)转换得到原始值,在进行ToNumber转换为数字 |
c. toString()
参数 | 结果 |
undefined | ‘undefined' |
null | 'null' |
boolean | true为'true',false为'false' |
string | 不改变 |
number | 数字改为字符串,如1改为’1‘ |
object | 先进行 ToPrimitive(obj, String)转换得到原始值,在进行ToString转换为字符串 |
1、转换成string:+(字符串拼接)
2、转化成Number:+,-,*,/,%,<,>等关系运算符
3、转化成Boolean:!
小栗子
({} + {}) = ?
1、进行ToPrimitive转换,由于没有指定PreferredType类型,{}会使默认值为Number,进行
ToPrimitive(input, Number)运算。
2、所以会执行valueOf方法,({}).valueOf(),返回的还是{}对象,不是原始值。
3、继续执行toString方法,({}).toString(),返回"[object Object]",是原始值。
故得到最终的结果,"[object Object]" + "[object Object]" = "[object Object][object Object]"
再来一个指定类型的例子
2 * {} = ?
1、首先*运算符只能对number类型进行运算,故第一步就是对{}进行ToNumber类型转换。
2、由于{}是对象类型,故先进行原始类型转换,ToPrimitive(input, Number)运算。
3、所以会执行valueOf方法,({}).valueOf(),返回的还是{}对象,不是原始值。
4、继续执行toString方法,({}).toString(),返回"[object Object]",是原始值。
5、转换为原始值后再进行ToNumber运算,"[object Object]"就转换为NaN。
故最终的结果为 2 * NaN = NaN
1、===
1.先判断===两边的类型是否相同,若不同返回false
2.若相同,再判断值是否相同
2、==
用==判断两数据是否相等时,会自动作类型转化
主要分为两类,x、y类型相同时,和类型不相同时。
类型相同时,没有类型转换,主要注意NaN不与任何值相等,包括它自己,即NaN !== NaN。
类型不相同时,
1、x,y 为null、undefined两者中一个,返回true
2、x、y为Number和String类型时,则转换为Number类型比较。
3、有Boolean类型时,Boolean转化为Number类型比较。
4、一个Object类型,一个String或Number类型,将Object类型进行原始转换后,按上面流程进行原始值比较
小栗子
[] == !{}
1、! 运算符优先级高于==,故先进行!运算。
2、!{}运算结果为false,实际上[],{}转化成布尔类型都为true
3、涉及到布尔类型的比较,将他先转化成number,!{}转成0
4、[]会返回空串''
故结果为 '' == 0比较。
5、根据上面第5条,等式左边x = ToNumber('') = 0。
所以结果变为: 0 == 0,返回true,比较结束。
空数组的toString 会返回空串'',而空对象的toString会返回'[object Object]'
空数组和空对象转化成Boolean都是true
0,null,undefined,'',NAN,转成Boolean都是false,其余的都是true