js数据类型

js 数据类型

js中有六种原始类型:

  • number
  • boolean
  • null
  • undefined
  • string
  • symbol
    一种引用类型
  • object

引用类型和基本类型有什么区别,以及深浅拷贝之类就暂时不表了。

注意点:

  1. undefined和not defined是不同的 。undefined代表的是定义了但是没有赋值,not defined表示没有定义。但是typeof 都返回undefined。
  2. 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
  1. 使用typeof检测基本类型基本可以正确的判断出类型的,但是判断引用类型时就没有那么准确了。因为数组、对象、函数实际上都是引用类型。但function对象和普通的对象相比,其内部有一个[[call]]方法,用来表示这个对象是可调用的。所以返回时function。实际函数就是一个可调用对象。如果要准确的判断各个引用类型就需要使用instanceof
  2. 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方法被调用时,会执行下面的操作步骤

  1. 获取this对象的[[Class]]属性的值。
  2. 计算出三个字符串"[object ", 第一步的操作结果Result(1), 以及 "]"连接后的新字符串。
  3. 返回第二步的操作结果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"
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,794评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,050评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,587评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,861评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,901评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,898评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,832评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,617评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,077评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,349评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,483评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,199评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,824评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,442评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,632评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,474评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,393评论 2 352

推荐阅读更多精彩内容