JavaScript定义了以下7种原始类型及对象类型“object”,可以使用typeof关键字来判断一个变量的类型。
• Boolean
• Null
• Undefined
• Number
• BigInt
• String
• Symbol
$ node > typeof 1 'number' > typeof "1" 'string' > typeof true 'boolean' > typeof 1n 'bigint' > typeof undefined 'undefined'
原始类型的值都是不可变的,这种不可变指的是无法对原始类型的值本身进行修改。如果把基本类型的值赋给变量则可以自由修改,此时修改的是变量的值而非基本类型值本身。
3 = 1 // 输出 Thrown: 3 = 1 ^ SyntaxError: Invalid left-hand side in assignment > var num = 3; > num = 2; // 正确
Boolean(布尔)类型只包含两个值,分别是true和false。布尔值通常用在控制结构中,如if的条件跳转。当if条件中的表达式不是一个布尔类型的值时,JavaScript会产生一个隐式的类型转换,本书后面的内容还会做相关介绍。
if(true){ } // 通常不会写出上面形式的代码,而往往是下面的形式 var a = xxxx; if(a){ } // if中的隐式转换 if(1) // 等价于 if(true) if('1') // 等价于 if(true) if(0) // 等价于 if(false) if('0') // 等价于 if(false) if('') // 等价于 if(false)
JavaScript不区分整数和浮点数,所有的数字都统一使用64位浮点数表示,这意味着JavaScript的两个整数相除不会像C语言那样只返回整数部分。
> 3/2 // 输出 // 1.5
JavaScript预定义了一些特殊值,如表3-2所示。
表3-2 JavaScript中的预定义数字
MAX_SAFE_INTEGER是JavaScript能够安全表示的最大整数,超过这个数字之后无法保证准确。
var a = Number.MAX_SAFE_INTEGER+1 a==a+1 // true
BigInt的特点是不受数字大小的限制,可以通过在整数后面加“n”或者使用BigInt()构造函数来声明一个BigInt类型的变量,BigInt在使用时不能和Number类型混用。
a = 10n; // 声明一个BigInt a = BigInt(10); // 声明一个BigInt a+10 // TypeError: Cannot mix BigInt and other types, use explicit conversions
JavaScript中的字符串是一种类数组结构,可以使用下标“[]”来读取指定位置的字符,但无法使用下标对字符串进行修改,因为基本类型的值是不可改变的。
var str = "hello"; // str的值不会改变,但也不会有错误信息 str[0] = "w"; // 在严格模式下运行上面的代码 "use strict" str[0] = "w"; // TypeError: Cannot assign to read only property '0' of string 'hello'
使用引号来声明一个字符串,JavaScript中一共有3种引号可以使用,分别是单引号、双引号及反引号。
// 以下三者等价 'string text' "string text" `string text`
通常认为单引号和双引号等价,当仅用来声明一个简单字符串的时候,这3种引号的使用没有任何区别。引号内部再出现相同符号的时候需要转义,否则解释器将因不能正确区分字符串的起始结束范围而导致错误。
"he said "yes"" ; // 错误,内部的双引号需要转义 'he said "yes"' ; // 正确 "he said 'yes'"; // 正确 "he said \"yes\""; // 正确 'he said \'yes\' and cried'; // 正确
String类的prototype定义了许多字符串操作的方法,如表3-3所示。因为String作为原始类型不可变的特性,凡是会返回字符串的方法都会返回一个全新的字符串。
表3-3 定义在String.prototype上的部分常用方法
表3-3并没有涵盖所有的字符串方法,有些方法已经不鼓励使用,如String.prototype.substr(),该方法可以被String.prototype.substring()代替。还有一些属于少用的国际化相关的方法,例如String.prototype.toLocaleLowerCase(),也没有包含在表格内。
有些情况下,在处理字符串时将其转换成数组会带来便利。String.prototype.split()和Array.prototype.join()可以实现字符串和数组之间的相互转换。
"abc".split(""); // 返回 // ["a","b","c"] ["a","b","c"].join(""); // 返回 // "abc"
多行字符串通常有两种需求情景,一种是在代码编辑器中,有些时候一个字符串的长度可能会超过编辑器窗口的宽度导致不易查看(如在JavaScript代码中经常需要处理HTML字符串),需要将其拆成多行显示;另一种是真实的业务需要将一个字符串变成多行的形式。
对于第一种,直接使用加号或者连接符号即可将一行字符串拆分成多行显示。第二种则可以使用反引号,也就是模板字符串来解决。
// 下面两种方式只是看起来像是多行字符串 // 但JavaScript在处理时仍然认为这是一个单行字符串 var str1 = "I am "+ "dangerous"; var str2 = "I am \ dangerous"; console.log(str1); // I am dangerous console.log(str2); // I am dangerous
使用反引号声明的字符串称为模板字符串,这是一个来自于ES6的特性。模板字符串可以直接写成多行的形式,JavaScript在处理时也会当作多行字符串来处理。
var string = `I am dangerous `; console.log(string); // 输出 // I // am // dangerous
模板字符串还支持在内部使用表达式,可以自动解析位于${}中的表达式并对其求值后输出到原有位置,比使用加号的字符串拼接和占位符更加直观。
var hostname = "localhost"; var port = 8080; // 以下三个打印语句是等价的 console.log(`Server running at http://${hostname}:${port}/`); console.log("Server running at http://%s:%f/",hostname,port); console.log("Server running at http://"+hostname+":"+port+"/"); // 输出 // Server running at http://localhost:8080
正则表达式是一门特殊的编程语言,有自己的语法规范,很多编程语言都内置了正则表达式引擎。
JavaScript提供了6个(严格来说是7个)有关正则表达式的方法,如表3-4所示。
表3-4 正则表达式相关的方法
可以通过直接声明或者构造函数来创建一个正则表达式对象。
// regex1 是一个正则表达式对象 // 不要把它当作字符串 var regex1 = /abc/; // 使用构造函数 var regex2 = new RegExp("abc"); var regex3 = new RegExp(/abc/);
使用构造函数和使用两个“/”声明的正则表达式对象之间在功能上没有区别,但在执行上有差异。直接赋值的正则表达式会在脚本加载后编译,而使用构造函数的正则表达式则是在执行时被编译。正则表达式中的标志位如表3-5所示。
表3-5 正则表达式中的标志位
正则表达式中的匹配模式如表3-6所示。
表3-6 正则表达式中的匹配模式
续表
为了理解正则表达式是如何工作的,这里介绍一个简单的正则匹配方法,其主要功能是支持。和*的匹配。这其实是一个经典的Leetcode问题(No.10),可以借助递归的思路解决,如表3-7所示。
表3-7 正则表达式匹配示例
// 实现正则匹配规则 String.prototype.isEmpty=function(){ return this.length === 0; } function isMatch(text,pattern){ if(pattern.isEmpty()) return text.isEmpty(); // 判断第一个字符是否相同 function first_match(){ return (!text.isEmpty() && (pattern.charAt(0) == text.charAt(0) || pattern.charAt(0) == '.')); } if(pattern.length >=2 && pattern[1] == '*'){ return (isMatch(text, pattern.substr(2)) || (first_match() && isMatch(text.substr(1), pattern))); }else{ return first_match() && isMatch(text.substr(1),pattern.substr(1)) } }
JavaScript中可以使用==和===两种运算来判断两个变量是否相等,二者的区别在于==在比较的过程中如果两边的类型不同,会发生隐式的类型转换。
// 在使用 == 时会发生隐式类型转换 3 == "3" // true 1 == true // true // === 不会发生类型转换 3 === "3" // false 1 === true // false
除非特意去记忆并且经常温习,大多数人并不能准确地记住所有可能发生的隐式转换。例如,下面的问题经常在面试中出现,它们的结果都是true。
0 == '' 0 == '0' '' == '0' false == 'false' false == '0' false == undefined false == null null == undefined '\t\r\n' == 0
几乎所有JavaScript相关编程书籍都不推荐使用==,尽管隐式类型转换有时会带来一些便利,但这些便利通常微不足道并且往往可以被一两行代码代替。