一、js的基本语法
- 区分大小写
这一点意味着 变量a和变量A是完全不同。
- 变量命名规则
首字母必须是字母、下划线(-)或者美元符号($)。
其他字母可以是下划线(_)、美元符号($)、字母或者数字。
一般采用(小)驼峰法:第一个字母小写,其余有意义的单词首字母大写。
不能是关键字或保留字。
二、数据类型
ECMAScript标准规定了7
种数据类型,其把这7
种数据类型又分为两种:原始类型和对象类型(又称合成(complex)类型 )。
(在es10
中加入了第七种原始类型BigInt
,现已被最新Chrome
支持)
2.1 基本概念
原始类型
Null
Undefined
Boolean
Number
String
Symbol
bigInt
引用类型
Object
关于这些数据类型的概念可以在《JavaScript高级编程设计》上找到。我懒,就不写了。
为了方便(懒),下面我会将《JavaScript高级编程设计》略成为《高级设计》
2.2 和数据类型相关常见的几个问题
有关JavaScript的数据类型是很多面试中高频出现的问题,下面我总结(CV)了几个常见的问题。
2.2.1 Null
与Undefined
的异同
回答这个问题,我们首先先了解一下这两种数据类型的概念。
Null
是一种原始数据类型,只有一个值就是 null。从逻辑上指的是一个空的对象指针(《高级设计》中的说法),我们可以用它来指空。
Undefined
也是一种原始数据类型,也只有一个值undefined。当一个变量已经(定义)但是没有初始值的时候,其默认值就是undefined。我们可以用它来指应该有值但是没有赋值。
写到这里我突然想到之前某知名老师举了一个例子,很灵性。他说按照正常的伦理道德,如果你是一个男单身狗,你的男朋友对你来说就是Undefined
,你的女朋友对你来说就是Null
。
其实在JavaScript最初只有Null
这个原始类型,但是,JavaScript的设计者Brendan Eich,觉得这样做还不够,有两个原因。
首先,null像在Java里一样,被当成一个对象。但是,JavaScript的数据类型分成原始类型(primitive)和合成类型(complex)两大类,Brendan Eich觉得表示"无"的值最好不是对象。
其次,JavaScript的最初版本没有包括错误处理机制,发生数据类型不匹配时,往往是自动转换类型或者默默地失败。Brendan Eich觉得,如果null自动转为0,很不容易发现错误。
因此,Brendan Eich又设计了一个undefined。
好了,不扯了~我们现在来回答一下上面的问题:
我们认为 Null
和Undefined
的相同点:
他们都是原始数据类型,并且都只要一个值,都代表空的意思。
他们的不同点:
从字面量上来说,undefined
指的是未定义的,而Null
指的是空,不存在的值。
我们可以广义的理解这个未定义,任何无法描述准确的说程序没法描述的东西,一般都可以用Undefined
来描述。比如,当声明了一个变量,却没有初始化这个变量,当程序运行到这一步的时候,程序无法用某个东西表示这个变量的值,就会出现错误。(这也就是JavaScript增加Undefined
这个原始类型的初衷---防止程序报错!)。
Null
表示"没有对象",即此处不存在值。如果我们使用JavaScript给我们提供的typeof 操作符的话我们会发现
var a;
var car = null;
typeof a //==> 'undefined';
typeof car //==> 'object';
从上面我们可以看出,Undefined
就是程序没法描述的东西,Null
是可以描述的,是表示一个空的对象的指针。
从日常的用法上说。
Null
的典型用法:
函数的参数,表示该函数的参数不是对象,
或者作为对象原型链的终点。
Undefined
的典型用法:
当一个变量定义了,但是没有赋值的话,那么这个变量的值就是undefined;
当调用一个函数,应该提供一个参数没有提供的,该参数就是undefined;
对象的属性没有赋值的时候,那么这个属性的值就是undefined;
当函数没有返回值的时候,我们默认也会返回undefined。
2.2.2 js中引用类型和原始类型的区别
原始类型和引用类型的最大的区别就是 不可变性。
在JavaScript
中,每个变量在内存中都需要一个空间来存储。
内存空间又被分为两种,栈内存与堆内存。
栈内存:
- 存储的值大小固定
- 空间较小
- 可以直接操作其保存的变量,运行效率高
- 由系统自动分配存储空间
JavaScript
中的原始类型的值被直接存储在栈中,在变量定义时,栈就为其分配好了内存空间
由于栈中的内存空间的大小是固定的,那么注定了存储在栈中的变量就是不可变的。
堆内存:
- 存储的值大小不定,可动态调整
- 空间较大,运行效率低
- 无法直接操作其内部存储,使用引用地址读取
- 通过代码进行分配空间
相对于上面具有不可变性的原始类型,我习惯把对象称为引用类型,引用类型的值实际存储在堆内存中,它在栈中只存储了一个固定长度的地址,这个地址指向堆内存中的值。
对于同一个引用类型的变量值,它拥有一个不变的堆内存地址,但是它本身是可以发生变化的。
以上就是原始类型和引用类型的区别。
2.2.3 布尔类型
Boolean
类型是JavaScript中很常用到的一种原始类型,它有两个值 true 和 false。
值得注意的是,由于JavaScript的区分大小写的,所以True和true代表的意义不用,前者只是一个普通的标识符(变量),后者表示的是Boolean值。
我们可以对任意类型使用Boolean()函数,且总是会返回一个Boolean值。
我们在日常的使用中通常会使用布尔操作符转换一个变量并作为一个判断语句的条件。所以我们必须比较熟练的掌握各种类型和Boolean类型之间的转换。下标给出了各种数据类型及其对应的转化规则。
数据类型 | 转换为true | 转化为false |
---|---|---|
Boolean | true | false |
Number | 任何非零数字值(包括无穷大) | 0和NaN |
String | 任何非空字符串 | ""(空字符串) |
Undefined | n/a(代表无意义) | Undefined |
Object | 任何对象 | null |
Null | n/a(代表无意义) | null |
这里我们要特别注意一个情况,就是空对象这种情况。
var a = {};
Boolean(a);//=>true
鉴于这种情况,我们在日常的开发中,如果我们期望后端给我们定义的一个变量传递一个对象,但是可能出现空值得情况。这时候我们就希望后端给这个值赋值为null而不是什么都不处理。这样做的好处就是我们可以判断直接使用下面这种方式来判断。
if(a != null){
//逻辑代码
}
2.2.4 Number类型
在JavaScript中,Number类型是使用IEEE745格式来表示整数和浮点数字的。这种格式造成了一个血案-- 0.1+0.2!=0.3。这是我们暂时不提,稍后分析。
JavaScript中为我们定义了多种数值字面量格式,其中最常用的就是十进制整数。
除了十进制表示外,整数还可以通过八进制或十六进制的字面值表示。其中,八进制字面值的第一个字符必须是0,然后面是八进制数字序列(0~7),如果字面值中的数字超出范围,那么前导零将被忽略,后面的数值将当做十进制数值解析。值得一提的是,不同JavaScript引擎对八进制的解析各有不同。
var octalNum1 = 070;// 八进制的56
var octalNum2 = 080;// 无效的八进制字面值 80
var octalNum3 = 08;// 无效的八进制数值 8
十六进制字面值得前两位必须是0x,后跟任何十六进制数字(09及AF)。
var ten = 10;//十进制表示法。
var octalNum = 01;//代表8,但是这个在不同的JavaScript引擎中表现是不一样的。在谷歌浏览器中的值为1
var hexNum = 0x10;//16
有一点需要注意的是,在进行算术计算的时候,所有以八进制和十六进制表示的数值最终都将转化为十进制数值。
JavaScript中的浮点数值,必须包含一个小数点,并且小数点后面必须有一个数字。
对于极大或极小的浮点数值,可以用e表示法。
Number
类型有三个特殊的值:InFinity、-InFinity、NaN。他们其实代表了两种不同的情况,一种是超出数值范围,另一种是非数值类型。
isFinite()
由于内存的限制,JavaScript是存在最大值和最小值的。我们可以使用Number.MIN_VALUE
和Number.MAX_VALUE
获取最小值和最大值。如果某个计算的结果超过JavaScript数值的范围,那么这个数值会被自动转化为一个特殊的Infinity值,如果这个值是负数那么就会是-Infinity值。正负Infinity是相等的,并且Infinity是无法参数计算的。
由于上面的原因,我们在进行数值计算的时候,需要判断某个结果是否是有效的数值类型。JavaScript为我们提供了一个函数 -- isFinite。这个函数返回的是一个Boolean类型的值。
isNaN()
NaN,即非数值,是一个特殊的数值。这个数值用来表示一个本来要返回数值的操作数未返回数值的情况。它的出现其实和Undefined的出现有点类似的地方。就是防止程序报错,阻塞下面的JavaScript程序的运行。
NaN有两个特点:
- 任何有NaN参与的运算都返回NaN;
- NaN不等于任何值,包括自己本身。
对于某个值是否是NaN,JavaScript为我们提供了一个函数---isNaN()。
这个函数可以传入任何类型。
其原理:在函数内部拿到值后,会尝试着按下面的规则将值转化为数值,任何不能转化为数值的值都会适合函数返回true。
数据类型 | 返回值 |
---|---|
Number | false |
Undefined | true |
Null | false |
String | 1.使用正确的e表示法表示的数字字符串 例如"1.2e22","1.2e+2"<br />2.使用合法进制字面数值的 例如 "0xA"<br />3.纯数字字符串 <br />4.空字符<br />符合以上规则的都会返回false,其他的情况都会返回true。 |
Boolean | false |
Object | 先调用对象的valueOf()方法,然后确定返回值是否可以转化为数值,如果不能,调用toString()方法,再测试返回值。如果还是不行,就返回true。 |
和布尔类型类似,其他类型也可以通过函数转换为数值类型。JavaScript为我们提供了下面三个函数:Number()、parseInt()、parseFloat()。
Number()
Number()转型函数,可以转化任何数据类型。其转化规则为:
数据类型 | 返回值 |
---|---|
Boolean | true => 1<br />false => 0 |
Null | 0 |
Undefined | NaN |
Number | 本身 |
String | 1. 纯数字 => 十进制表示的数值(前导零会被忽略)<br />2. 有浮点格式 => 转化为对应浮点值 (前导零会被忽略)<br />3. 空字符串 => 0<br />4. 十六进制格式 => 转化为十进制整数值<br />5. e表示法的数值 => 转化为十进制整数值<br />6. 如果字符串包含除上述情况的字符,都返回NaN |
Object | 先调用对象的valueOf()方法,如果返回值是NaN,如果是,则调用toString()方法,再按上述测试返回的字符串。如果还是不行,就返回NaN。 |
parseInt()
parseInt()转型函数,是专门用于把字符串转化为数值的。其转换规则是:如果第一个字符不是数字或者负号,就转化为NaN。如果第一个是数字或者负号就会继续解析,直到字符串结束或者遇到一个非零字符串。
parseInt()转换空字符串的结果是NaN。这也是和Number()函数的不同点。
parseInt()可以解析多种进制的字符串。例如十进制,八进制,十六进制。这就需要在第二个参数中出入指定的进制数。这种方式也是推荐的方式!
var num = '0xAF';
var num1 = parseInt(num,16);//如果指定转换进制数,这个字符串就可以被转换为175。当然不传第二个参也行,但是最好还是传入比较好。如果传入的前面的"0x"是可以省略的。
var num2 = parseInt('AF',16)//175
parseFloat()
和parseInt()函数类似,parseFloat()也是专门解析字符串的函数。其转换规则是:从第一个字符串开始解析每个字符,直到结尾或者解析到一个无效的浮点字符。字符串中的第一个小数点是有效的,第二个就无效了。
与parseInt()不同的是,parseFloat()函数只能解析十进制格式的字符和e表示法的字符。它只有一个参数。
写到这里我们可以稍微总结一下 Number类型相关的三个函数的区别了。
- Number() 函数可以解析所有的类型的数据,parseInt()和parseFloat()是专门为字符串准备的。
- parseInt()转换空字符串的结果是NaN。这也是和Number()函数的不同点。
- 与parseInt()不同的是,parseFloat()函数只能解析十进制格式的字符和e表示法的字符。它只有一个参数。
2.2.5 String类型
其实和字符串相关的基础知识主要有两个:字符串拼接的底层原理 和 String() 转换规则
字符串拼接的底层原理
关于这个问题,我们可以先来看下面的代码
var str1 = 'string1';
str1 = str1 +'2';
上面是一个字符串拼接的逻辑代码。表面上两个str1
应该是同一个值,但是实际上却不是的。由于字符串不可变的原则,两个str1
不可能是一样的。这是为什么呢?我们来看一下它的底层实现原理就知道了。
首先我们会先在内存中开辟出一个可以容纳7个字符的空间用来存放变量str1
对用的值--"string1",接着会重新开辟一个可以容纳8个字符的空间用来存放string12
,然后JavaScript会将str1
和'2'所占的内存销毁掉,最后将str1
重新指向新开辟的内存。
String()转换规则
几乎所有类型的数据都会有一个 toString()方法,这个方法唯一要做的就是返回相对于的字符串表现。当然这里面有两个数据类型特殊:Null
和Undefined
。
为了解决这种特例情况,JavaScript为我们提供了String()函数。其转换规则其实很简单:
如果值有toString()方法,就返回该方法的返回值;
如果值是null,则返回"null";
如果值是undefined,则返回"undefined"。
这里面有一个特殊的知识点:
当调用数值的toString()的时候,我们可以传入一个参数,输出数值的基数。通过这种方式,我们可以输出二进制、八进制、十六进制等任意合法的进制类型表示的字符串。
2.2.6 Object类型
ECMAScript 中的对象其实就是一组数据和功能的合集。Object类型所具有的任何属性和方法也同样存在于更具体的对象中(实例)。
这个我们会单独开辟章节来讲。