浅谈javascript的数据类型

阅读时注意

这篇文章实在太久远了,可能充斥以下内容:

  • 令人不适且非常奇怪的口语/萌二式文笔
  • 受年龄经验导致浅显且幼稚的“技术”类文章
  • 情绪宣泄、个人隐私、对生活的无端负能量抱怨
  • 中二病

阅读这些文章可能让你产生不适或嘲笑感,请谨慎甄别。

这次仍然聊一下js的东西(

今天依然写着项目,我写了个js的分页组件,一共有一个css和一个js文件,我在想是把这两个文件里的内容写到一个jsp里然后在需要的界面里include,还是单纯的方成两个文件让其他页面去<script src=””>这样调用……

最后果然选择了前者,很简单,因为后者需要额外的两次http请求来引入css和js。

随后我就想到了页面压缩,无非就是压缩掉空格回车之类的东西,能省一个字节就是一个字节吧。

说到页面压缩,那首先就要看看百度了,于是我又打开了百度的首页(hhhhh我什么跳转逻辑),看了下源代码,果不其然,一片狼藉↓123

 

我就随便的往下翻了翻,结果翻到了一个好玩的代码:

asd

 

我们可以看到一句代码:
new Image().src = "/home/page/data/pageserver?errno=7004&_t"+new Date()*1;

 

可以看到首先是new Image().src = xxxxx,这个梗比较常见了,创建一个image并且赋值src属性,但是并不对这个image对象做任何事情,这么做的话,浏览器就会做到“预加载”这张图片,方便你以后调用的话,直接读取图片的缓存。

 

而最精华的是最后面的new Date()*1;这句话。

 

我们直接不废话,从chrome控制台里打印一下new Date()*1是什么结果↓

aaa

看的出来,打印出了一串数字,这个就是常见的时间戳了,一般我们都是用new Date().getTime()或者Date.now()来获得的,而这里的代码却用一个比较冷门的方法,new Date() * 1来获得。

为什么可以变成数字而不是报错或者是输出一个字符串的时间戳呢?

 

我们在写出几种方法,来一个个预测一下:

333

首先,我们打印出new Date()的toString方法

关于toString,这个我们就再熟悉不过了,在js,java,c#和一些语言里都有这个方法,一般的用处就是打印出一个对象的属性信息,否则默认js都是输出“[object Object]”,java里则是类名+hashCode。

 

但是下面就是两个非常有趣的语句了,同样的,我们 + 1和* 1,返回的结果完全不同,一个是字符串的拼接,而另一个则是数字的时间戳。

 

神奇吧!

 

这其中就涉及到了ecmascript的内核,为什么+1 和 *1不一样呢?而为什么new Date() * 1会得到个时间戳呢?

 

首先,我们先了解一个ECMAScript的内核方法“ToPrimitive()”(这个你在js里是找不到的,因为是内核级的方法)。

这个方法的作用是,返回一个对象的原始值。

首先,我们java里有什么八大基本类型+对象啦balabala,而js里也有这种“基本类型”,比如Number啦,Boolean啦,String啦,都算是“基本类型”,而Object,显然就是“非基本类型”。

 

在我们正常的物理世界里,你如果说1+1等于多少,肯定别人会理解,等于2嘛,但是你跟别人说1+苹果等于多少,别人肯定以为你是傻逼23333,“苹果”是一个对象类型,而不是“基本类型”,1加上苹果显然是从逻辑上不通的,当然,在js里我们都叫做原始类型,java里一般都叫基本类型啦,反正就是这个玩意,怎么叫都行关我屁事2333

 

所以在js里,1+Object也肯定不可能的,你问js能不能1+Object,肯定不能啊,所以js只好尝试把你的“苹果”这个对象强行翻译成一个数字或者字符串的这种“原始值”了╮(╯_╰)╭,当使用“加法运算符(+)”的时候,js会调用ToPrimitive()方法来把这个Object对象“翻译”成基本类型,这样才可以继续计算。

 

当然,如前面说的,我们是看不到ToPrimitive方法的,因为他是内核级别的方法,我们可以把这个方法里面执行的逻辑大概解释一下:

首先,ToPrimitive传入两个参数,一个参数是要ToPrimitive的对象,另一个则是“暗示(hint)”返回的结果。

第二个可能大家有点小迷糊,暗示是什么鬼,其实就是传入返回结果的类型(如java的.class),这里暗示值可以是String或者Number,相当于告诉ToPrimitive方法,“你执行完后要给我返回个数字或者字符串啊”(妈的智障)。

假如没有声明暗示的话,ToPrimitive会默认以Number作为返回暗示,也就是尽可能的返回一个数字而不是字符串或者其他玩意。

 

接下来,ToPrimitive会根据暗示来依次调用对象的valueOf()和toString()方法,后者(toString)我们很熟悉了,前者(valueOf)我们可能很少见。

首先js里的这个valueOf可不是java里那个valueOf的意思哦,js里的valueOf的抽象意思是“尝试返回对象的原始类型值”,而toString的抽象意思是“尝试返回对象的字符串类型的原始类型值”。

我们一般覆盖一个对象的toString方法,来输出我们自定义的字符串,但是!js是灵活的,虽然抽象里toString是返回个string,但是老子偏不!我就要返回个数字给你看hhhh↓

444

OTLBAAPYL(1EBIQV0QQIWSO果然,如我们预料的,我们创建一个obj对象,并且覆盖了toString方法,返回了一个数字“233”,那么我们把这个obj + 1,返回的结果则是234,而不是“2331”。

不要用这种奇怪的返回结果和加法来做奇怪的事情,这样你写的爽别人会揍人的2333!

而上边我们说了,ToPrimitive会根据暗示类型来决定调用toString还是valueOf,那么我们再来了解下valueOf。

valueOf很少有人用,因为没什么卵用,他和toString的区别不大(讲道理的话),只是ToPrimitive方法会根据暗示结果来不同的调用两个方法。

不逼逼,我们测试一下!↓

678

可以看到,我们创建了一个object,并且覆盖了toString和valueOf两个方法,我们在toString里返回数字233,在valueOf返回数字450,接下来执行obj + 1会发现,返回的是451,而不是234了。

这也就验证了上边所说的ToPrimitive执行的顺序,默认的暗示是“数字型的”(我们干涉不了暗示的,因为是内核级别写死的,所以一个object创建后,永远是数字型的暗示),所以ToPrimitive会优先去调用valueOf方法,发现有值之后,就会不屌toString方法而直接返回450这个值。

而我们默认情况下,不覆盖valueOf的话,ToPrimitive则会去找toString方法来寻求返回结果,所以说,在“数字型的”的暗示里,ToPrimitive会顺序调用valueOf -> toString,而我们也可以反过来推倒,如果是“字符串”的暗示模式下,则会直接调用toString而不去屌valueOf。

 

那么,前面说过,默认的暗示模式是数字型,那么js里有什么对象不是数字型的暗示模式呢?

 

在ECMAScript里的文档,我似乎只找到了Date对象是“字符串暗示”的模式。

 

这也就验证了一开始的new Date() * 1和new Date() + 1 返回的不同。

顺便说一嘴,乘法操作符(*)和加法操作符(+)不同的是,乘法是不允许两个字符串相乘的,所以在乘法里,会尽可能的尝试得到运算左右两端的“数字原始值”(ToPrimitive,并且强制暗示为数字型,并不会理会Date对象的默认暗示类型为字符串),而且乘法输出的值也永远是数字或NaN而不可能是字符串。

但是加法在底层的实现则稍微复杂了一些,他会根据ToPrimitive(默认暗示)里的逻辑去得到“原始值”,然后根据加法运算左右两端的值,返回两个数字相加,或者字符串的拼接结果。

而Date也覆盖了valueOf方法,返回的就是前面看到的一个时间戳,那么到此为止,我们所有的谜题都解开啦wwww~~

 

好了,今天的学生小课堂就到这里2333,不知道屏幕前的你有没有读懂呢?(并没人看=。=)虽然本篇文章并没什么实际的卵用hhhh,只是从一个new Date()*1引申出的js底层数据类型的问题,但也是一种装逼的技巧对吧,你以后在别人面前写个new Date()*1,而不是new Date().getTime(),那逼格多高2333,或者你建个对象,然后用对象进行加减乘除运算,看的你同事一脸懵逼2333,他肯定想,对象为什么能做运算嘛23333,那逼格多高!

One Response to “ 浅谈javascript的数据类型 ”

  1. 可以可以

发表评论