js 数据类型
js中有六种原始类型:
- number
- boolean
- null
- undefined
- string
- symbol
一种引用类型 - object
引用类型和基本类型有什么区别,以及深浅拷贝之类就暂时不表了。
注意点:
- undefined和not defined是不同的 。undefined代表的是定义了但是没有赋值,not defined表示没有定义。但是typeof 都返回undefined。
- js中的string是不可变的,他的成员函数不会改变原值,而是创建并返回一个新的字符串。
c = a.toUpperCase();
a === c // false
1 typeof
typeof false // boolean
typeof null // object
typeof undefined // undefined
typeof 2 // number
typeof NaN // number
typeof Infinity // number
typeof 'kimi' // string
typeof Symbol('kimi') // symbol
typeof Date // funtion
typeof [2,2,3] // object
typeof {a:1} // object
- 使用typeof检测基本类型基本可以正确的判断出类型的,但是判断引用类型时就没有那么准确了。因为数组、对象、函数实际上都是引用类型。但function对象和普通的对象相比,其内部有一个[[call]]方法,用来表示这个对象是可调用的。所以返回时function。实际函数就是一个可调用对象。如果要准确的判断各个引用类型就需要使用instanceof。
- null返回object。这是js的一个bug,js最初版本使用的是32位系统,为了性能考虑用低位存储变量的类型信息,000开头表示对象,然后null为全零。所以返回object。现在内部类型判断代码已经改变。但是这个bug仍然在。所以null其实算做基本类型
null instanceof object // false
2 字面量、String()、new String()
var abc = 'abc',
str1 = String(abc),
str2 = new String(abc);
//判断下面输出
str1 === abc //true
str2 === abc //false
typeof str1 // string
typeof str2 // object
String() 和 字面量的方式返回的都是基本类型,String() 用来做类型转换
而new String()返回object
Q:为什么 'kimi'基本类型可以去调用length或者toString方法和属性呢?
在这里,只要引用了字符串'kimi'的属性或方法,JavaScript就会将字符串值通过调用new String(s)的方式转换成对象,这个对象继承了字符串的方法,并用来处理属性的作用。一旦属性引用结束,这个新创建的对象就会销毁(其实在实现上并不一定创建和销毁这个临时对象,然而整个过程看起来就是这样的)
类似代码:
var a1 = new String("kimi");
var a2 = a1.substring(0);
a1 = null;
console.log(a2); // kimi
存取字符串,数字或布尔值的属性时创建的临时对象称作包装对象
同字符串一样,数字和布尔值也具有各自的方法:通过Number和Boolean构造函数创建一个临时对象,这些方法的调用均是来自这个临时对象.null和undefined没有包装对象:访问它们的属性会造成一个错误。
var s = “test”;
s.len = 4;
console.log(s.len); // undefined
// 由于包装对象使用完毕会自己销毁,所以添加的属性也读取不到。
Q:为什么2.toString()会报错?
在这里的 . 它既可以理解为小数点,也可以理解为对方法的调用,按照规范,解释器把它判断为一个小数点。 2.toString() 在解释器看来其实是:(2.)toString();
如果想解决可以使用如下方式都可以正确输出
2..toString()
(2).toString();
2 .toString(); //加个空格
2.0.toString();
3 准确类型判断方法
1. instanceof 配合 typeof
const Person = function() {}
const p1 = new Person()
p1 instanceof Person
let str = 'hello'
str instanceof String // false
str.__proto__ // String
// str字面量有__proto__属性是因为转换成包装对象。但是本身是个基本类型,使用不了instanceof
let str1 = new String('hello')
str1 instanceof String // true
综上如果是基本类型可以使用typeof,引用类型使用instanceof
2. Object.prototype.toString.call()
Object.prototype.toString.call(2) // [object Number]
Object.prototype.toString.call('kimi') // [object String]
Object.prototype.toString.call(true) // [object Boolean]
Object.prototype.toString.call(Symbol('kimi')) // [object Symbol]
Object.prototype.toString.call(new Date()) // [object Date]
Object.prototype.toString.call(new RegExp()) // [object RegExp]
Object.prototype.toString.call()、toString()、 toString.call()
所有的对象最后都会继承Object.prototype.toString这个方法。而在每个子类中都会改写这个方法。Array、Function的原型上都已经改写了这个方法。每个对象上调用toString方法时会先调用自身的toString方法,如果找不到会沿着原型链往上找,如果一直没找到最终会找到Object.prototype.toString这个方法
具体看下每个对象调用toString方法的结果:
1 对象 object
var obj = {a: 1};
obj.toString(); // "[object Object]"
Object.prototype.toString.call(obj); // "[object Object]"
Object.prototype.toString() 在toString方法被调用时,会执行下面的操作步骤
- 获取this对象的[[Class]]属性的值。
- 计算出三个字符串"[object ", 第一步的操作结果Result(1), 以及 "]"连接后的新字符串。
- 返回第二步的操作结果Result(2)。
[[Class]]是一个内部属性,所有的对象(原生对象和宿主对象)都拥有该属性.在规范中,[[Class]]是这么定义的: 内部属性 描述 [[Class]] 一个字符串值,表明了该对象的类型。其过程简单说来就是:1、获取对象的类名(对象类型)。2、然后将[object、获取的类名、]组合并返回。
那么同时我们可以想是否任何对象object都可以通过this绑定调用Object.prototype.toString()方法,这样可以准确的检测出类型
Object.prototype.toString.call({}); // [object Object]
Object.prototype.toString.call([]); // [object Array]
Object.prototype.toString.call(function(){}); // [object Function]
Object.prototype.toString.call(''); // [object String]
Object.prototype.toString.call(1); // [object Number]
Object.prototype.toString.call(true); // [object Boolean]
Object.prototype.toString.call(null); // [object Null]
Object.prototype.toString.call(undefined); // [object Undefined]
// 相当于Object.prototype.toString.call(undefined);
Object.prototype.toString.call(); // [object Undefined]
Object.prototype.toString.call(new Date()); // [object Date]
Object.prototype.toString.call(/at/); // [object RegExp]
// toString() 方法能识别以下类型是因为引擎为它们设置好了 toStringTag 标签
Object.prototype.toString.call(new Map()); // "[object Map]"
Object.prototype.toString.call(function* () {}); // "[object GeneratorFunction]"
Object.prototype.toString.call(Promise.resolve()); // "[object Promise]"
// 自己创建的类不能识别,toString() 找不到 toStringTag 属性时只好返回默认的 Object 标签
class ValidatorClass {}
Object.prototype.toString.call(new ValidatorClass()); // "[object Object]"
// 可以加上 toStringTag 属性让他识别
class ValidatorClass {
get [Symbol.toStringTag]() {
return "Validator";
}
}
Object.prototype.toString.call(new ValidatorClass()); // "[object Validator]"
因为Object是所有子类的父类,所以任何类型的对象object都可以通过this绑定调用Object.prototype.toString()方法,返回该对象的字符串表示
2 数组 array
toString():返回由数组中每个值的字符串形式拼接而成的一个以逗号分隔的字符串
// 如果是多维数组会递归调用toString()方法
var array = [1, 's', true, {a: 2}];
array.toString();//"1,s,true,[object Object]"
Array.prototype.toString.call(array);//"1,s,true,[object Object]"
那么别的对象是否可以调用数组的toString方法呢?
Array.prototype.toString.call({}); // [object Object]
Array.prototype.toString.call(function(){}) // [object Function]
Array.prototype.toString.call(1) // [object Number]
Array.prototype.toString.call('') // [object String]
Array.prototype.toString.call(true) // [object Boolean]
Array.prototype.toString.call(/s/) // [object RegExp]
// 特殊
<!--以下都是 Cannot convert undefined or null to object at toString-->
Array.prototype.toString.call(); // 相当于Array.prototype.toString.call(undefined)
Array.prototype.toString.call(undefined);
Array.prototype.toString.call(null);
数组对象通过this绑定调用Array.prototype.toString()方法,返回数组值的字符串拼接
非数组对象通过this绑定调用Array.prototype.toString()方法,返回的是该对象的字符串表示
另外null和undefined不可以通过绑定调用Array.prototype.toString()方法。
3 函数 function
toString():返回函数的代码
function foo(){
console.log('function');
};
foo.toString();
<!--"function foo(){-->
<!-- console.log('function');-->
<!--}"-->
Function.prototype.toString.call(foo);
<!--"function foo(){-->
<!-- console.log('function');-->
<!--}"-->
// Object Function Array本质都是构造函数
Object.toString();
// "function Object() { [native code] }"
Function.toString();
// "function Function() { [native code] }"
Array.toString();
// "function Array() { [native code] }"
非函数对象是否可以使用函数的toString方法
Function.prototype.toString.call({})
<!--Function.prototype.toString requires that 'this' be a Function-->
除了上述提到的Object和Array两种情况,其他类型都不支持非自身实例通过this绑定调用该Object子类原型对象上的toString()方法,这说明它们在重写toString()方法时,明确限定了调用该方法的对象类型,非自身对象实例不可调用。所以,一般我们只使用Object.prototype.toString.call/apply()方法。
4 日期 Date
toString():返回带有时区信息的日期和时间
var date = new Date();
date.toString();
//"Fri May 11 2019 14:55:43 GMT+0800 (中国标准时间)"
Date.prototype.toString.call(date);
//"Fri May 11 2019 14:55:43 GMT+0800 (中国标准时间)"
5 正则 RegExp
toString():返回正则表达式的字面量
var re = /cat/g;
re.toString();// "/cat/g"
RegExp.prototype.toString.call(re);// "/cat/g"
6 字符串 string
toString():返回字符串的一个副本
var str = "a";
str.toString(); //"a"
String.prototype.toString.call(str); //"a"
7 数字 number
toString():返回字符串形式的数值
var num = 520;
num.toString(); //"520"
Number.prototype.toString.call(num); //"520"
8 布尔 boolean
toString():返回字符串"true"或"false"
var boo = true;
boo.toString(); //"true"
Boolean.prototype.toString.call(boo); //"true"
9 null和undefined
null和undefined没有相应的构造函数,所以它们没有也无法调用toString()方法,也就是说它们不能访问任何属性和方法,只是基本类型而已。
10 全局对象window(Window类)
toString(): 返回对象的字符串表示
window.toString();
<!--"[object Window]"-->
Winodw类并没有在Window.prototype原型对象上重写toString()方法,它会顺着原型链查找调用Object.prototype.toString()。
所以上述代码为:
Object.prototype.toString.call(window);
<!--"[object Window]"-->
toString.call()
toString.call()和toString()直接调用一样都返回[object Undefined],
toString.call() // [object Undefined]
toString() // [object Undefined]
window.toString.call() // [object Undefined]
window.toString.call(undefined) // [object Undefined]
Object.prototype.toString.call() // [object Undefined]
Object.prototype.toString.call(undefined) // [object Undefined]
所以toString.call()相当于
window.toString.call() -> window.toString.call(undefined) -> Object.prototype.toString.call() ->Object.prototype.toString.call(undefined)
所以用Object.prototype.toString.call() 判断类型可以简写为toString.call()来判断
但是要注意如果已经有定义了toString函数就不可以使用。
function toString(){
console.log("kimi")
}
toString(); //"kimi"
toString.call({}); //"kimi"
toString.call([]); //"kimi"
网友评论