「JS红宝书第4版 学习总结」第5章 基本引用类型

249 阅读23分钟

5.1 前言

引用值(或者对象)是某个特定引用类型的实例,有时候也被称为对象定义,因为它们描述了自己的对象应有的属性和方法。

对象被认为是某个特定引用类型的实例。新对象通过使用 new 操作符后跟一个构造函数(constructor)来创建。

函数也是一种引用类型

5.2 MIND

5.1 基本引用类型 .png

5.3 Date

Date 类型将日期保存为自协调世界时(UTC,Universal Time Coordinated)时间 1970 年 1 月 1 日午夜(零时)至今所经过的毫秒数。使用这种存储格式,Date 类型可以精确表示 1970 年 1 月 1 日之前及之后 285 616 年的日期。

ECMAScript 在创建日期对象时,不给 Date 构造函数传参数的情况下,创建的对象将保存当前日期和时间。传参的情况下,提供了两个辅助方法(Date.parse()Date.UTC())来创建基于其他日期和时间的日期对象。

5.3.1 继承的方法

与其他类型一样,Date 类型重写了 toLocaleString()、toString() 和 valueOf() 方法。

  • toLocaleString() 方法返回与浏览器运行的本地环境一致的日期和时间。
  • toString() 方法通常返回带时区信息的日期和时间,而时间也是以 24 小时制(0~23)表示的。
  • valueOf() 方法根本就不返回字符串,而是日期的毫秒数值。

5.3.2 日期格式化方法

  • toDateString():显示日期中的周几、月、日、年
  • toTimeString():显示日期中的时、分、秒和时区
  • toLocaleDateString():显示日期中的周几、月、日、年(本地)
  • toLocaleTimeString():显示日期中的时、分、秒(本地)
  • toUTCString():显示完整的 UTC 日期

还有一个方法叫 toGMTString(),跟 toUTCString() 一样的为了向后兼容(规范建议新代码使用 toUTCString())

5.3.2 日期 / 时间组件方法

方法说明
getTime()返回日期的毫秒表示;与 valueOf()相同
setTime(milliseconds)设置日期的毫秒表示,从而修改整个日期
getFullYear()返回 4 位数年(即 2019 而不是 19)
getUTCFullYear()返回 UTC 日期的 4 位数年
setFullYear(year)设置日期的年(year 必须是 4 位数)
setUTCFullYear(year)设置 UTC 日期的年(year 必须是 4 位数)
getMonth()返回日期的月(0 表示 1 月,11 表示 12 月)
getUTCMonth()返回 UTC 日期的月(0 表示 1 月,11 表示 12 月)
setMonth(month)设置日期的月(month 为大于 0 的数值,大于 11 加年)
setUTCMonth(month)设置 UTC 日期的月(month 为大于 0 的数值,大于 11 加年)
getDate()返回日期中的日(1~31)
getUTCDate()返回 UTC 日期中的日(1~31)
setDate(date)设置日期中的日(如果 date 大于该月天数,则加月)
setUTCDate(date)设置 UTC 日期中的日(如果 date 大于该月天数,则加月)
getDay()返回日期中表示周几的数值(0 表示周日,6 表示周六)
getUTCDay()返回 UTC 日期中表示周几的数值(0 表示周日,6 表示周六)
getHours()返回日期中的时(0~23)
getUTCHours()返回 UTC 日期中的时(0~23)
setHours(hours)设置日期中的时(如果 hours 大于 23,则加日)
setUTCHours(hours)设置 UTC 日期中的时(如果 hours 大于 23,则加日)
getMinutes()返回日期中的分(0~59)
getUTCMinutes()返回 UTC 日期中的分(0~59)
setMinutes(minutes)设置日期中的分(如果 minutes 大于 59,则加时)
setUTCMinutes(minutes)设置 UTC 日期中的分(如果 minutes 大于 59,则加时)
getSeconds()返回日期中的秒(0~59)
getUTCSeconds()返回 UTC 日期中的秒(0~59)
setSeconds(seconds)设置日期中的秒(如果 seconds 大于 59,则加分)
setUTCSeconds(seconds)设置 UTC 日期中的秒(如果 seconds 大于 59,则加分)
getMilliseconds()返回日期中的毫秒
getUTCMilliseconds()返回 UTC 日期中的毫秒
setMilliseconds(milliseconds)设置日期中的毫秒
setUTCMilliseconds(milliseconds)设置 UTC 日期中的毫秒
getTimezoneOffset()返回以分钟计的 UTC 与本地时区的偏移量(如美国 EST 即“东部标准时间” 返回 300,进入夏令时的地区可能有所差异)

5.4 RegExp

RegExp 类型支持正则表达式,格式形如:

let expression = /pattern/flags;

pattern:模式,包括任何简单或复杂的正则表达式,包括字符类、限定符、分组、向前查找和反向引用
flags:标记,控制正则表达式的行为

  • g:全局模式,表示查找字符串的全部内容,而不是找到第一个匹配的内容就结束。
  • i:不区分大小写,表示在查找匹配时忽略 pattern 和字符串的大小写。
  • m:多行模式,表示查找到一行文本末尾时会继续查找。
  • y:粘附模式,表示只查找从 lastIndex 开始及之后的字符串。
  • u:Unicode 模式,启用 Unicode 匹配。
  • s:dotAll 模式,表示元字符.匹配任何字符(包括\n 或\r)。

匹配第一个"bat"或"cat",忽略大小写的正则表达式可以表示为如下:

// 字面量形式 
let pattern1 = /[bc]at/i;

//  RegExp 构造函数
let pattern2 = new RegExp("[bc]at", "i");

5.4.1 RegExp 实例属性

  • global:布尔值,表示是否设置了 g 标记。
  • ignoreCase:布尔值,表示是否设置了 i 标记。
  • unicode:布尔值,表示是否设置了 u 标记。
  • sticky:布尔值,表示是否设置了 y 标记。
  • lastIndex:整数,表示在源字符串中下一次搜索的开始位置,始终从 0 开始。
  • multiline:布尔值,表示是否设置了 m 标记。
  • dotAll:布尔值,表示是否设置了 s 标记。
  • source:正则表达式的字面量字符串(不是传给构造函数的模式字符串),没有开头和结尾的斜杠。
  • flags:正则表达式的标记字符串。始终以字面量而非传入构造函数的字符串模式形式返回(没有前后斜杠)。

5.4.2 RegExp 实例方法

exec()

主要用于配合捕获组使用。返回数据包括两个属性:index 和 input。

index 是字符串中匹配模式的起始位置,input 是要查找的字符串。数组的第一个元素是匹配整个模式的字符串, 其他元素是与表达式中的捕获组匹配的字符串。如果模式中没有捕获组,则数组只包含一个元素。

案例

let text = "mom and dad and baby";
let pattern = /mom( and dad( and baby)?)?/gi;
let matches = pattern.exec(text);

console.log(matches.index); // 0
console.log(matches.input); // "mom and dad and baby"

console.log(matches[0]);    // "mom and dad and baby"
console.log(matches[1]);    // " and dad and baby"
console.log(matches[2]);    // " and baby"

设置全局标记 g,每次调用 exec() 都会在字符串中向前搜索下一个匹配项。案例如下:

let text = "cat, bat, sat, fat";
let pattern = /.at/g;
let matches = pattern.exec(text);

console.log(matches.index);      // 0
console.log(matches[0]);         // cat
console.log(pattern.lastIndex);  // 3

matches = pattern.exec(text); 
console.log(matches.index);      //5 
console.log(matches[0]);         // bat 
console.log(pattern.lastIndex);  // 8

matches = pattern.exec(text);
console.log(matches.index);      // 10
console.log(matches[0]);         // sat
console.log(pattern.lastIndex);  // 13

设置粘附标记 y,每次调用 exec() 只会在 lastIndex 的位置上寻找匹配项。案例如下:

let text = "cat, bat, sat, fat";
let pattern = /.at/y;

let matches = pattern.exec(text);
console.log(matches.index);     // 0
console.log(matches[0]);        // cat
console.log(pattern.lastIndex); // 3

// 以索引3对应的字符开头找不到匹配项,因此exec()返回null
// exec()没找到匹配项,于是将lastIndex设置为0
matches = pattern.exec(text);
console.log(matches);           // null
console.log(pattern.lastIndex); // 0

// 向前设置lastIndex可以让粘附的模式通过exec()找到下一个匹配项:
pattern.lastIndex = 5;
matches = pattern.exec(text);
console.log(matches.index);     // 5
console.log(matches[0]);        // bat
console.log(pattern.lastIndex); // 8

test()

该方法适用于只想测试模式是否匹配,而不需要实际匹配内容的情况。

案例

let text = "000-00-0000";
let pattern = /\d{3}-\d{2}-\d{4}/;

if (pattern.test(text)) {
    console.log("The pattern was matched.");
}

toLocaleString() 和 toString()

都返回正则表达式的字面量表示。

案例

let pattern = new RegExp("\\[bc\\]at", "gi");
console.log(pattern.toString());       // /\[bc\]at/gi
console.log(pattern.toLocaleString()); // /\[bc\]at/gi

此处 pattern.toLocaleString() === pattern.toString()

valueOf()

返回正则表达式本身。

案例

let pattern = new RegExp("\\[bc\\]at", "gi");
console.log(pattern.valueOf()); // /\[bc\]at/gi

// 或者
let pattern = /\[bc\]at/gi;
console.log(pattern.valueOf()); // /\[bc\]at/gi

此处 pattern.valueOf() == pattern.toString() 且 pattern.valueOf() !== pattern.toString()

5.4.3 RegExp 构造函数属性

RegExp 构造函数的所有属性都没有任何 Web 标准出处,因此不要在生产环境中使 用它们。

RegExp 构造函数本身有几个属性(在其他语言中被称为静态属性),这些属性的特点如下:

  • 适用于作用域中的所有正则表达式
  • 会根据最后执行的正则表达式操作而变化
  • 可以通过两种不同的方式访问它们(每个属性都有一个全名和一个简写)

具体属性如下:

全名简写说明
input$_最后搜索的字符串(非标准特性)
lastMatch$&最后匹配的文本
lastParen$+最后匹配的捕获组(非标准特性)
leftContext$`input 字符串中出现在 lastMatch 前面的文本
rightContext$'input 字符串中出现在 lastMatch 后面的文本

案例

let text = "this has been a short summer";
let pattern = /(.)hort/g;

if (pattern.test(text)) {
    console.log(RegExp.input);        // this has been a short summer
    console.log(RegExp.leftContext);  // this has been a
    console.log(RegExp.rightContext); // summer
    console.log(RegExp.lastMatch);    // short
    console.log(RegExp.lastParen);    // s
}

// 或者
if (pattern.test(text)) {
    console.log(RegExp.$_);           // this has been a short summer
    console.log(RegExp["$`"]);        // this has been a
    console.log(RegExp["$'"]);        // summer
    console.log(RegExp["$&"]);        // short
    console.log(RegExp["$+"]);        // s
}

其他几个构造函数属性,具体表示为如下

RegExp.$1 ~ RegExp.$9

可以存储最多 9 个捕获组的匹配项,分别包含第 1~9 个捕获组的匹配项

案例

let text = "this has been a short summer";
let pattern = /(..)or(.)/g;

if (pattern.test(text)) {
    console.log(RegExp.$1);  // sh
    console.log(RegExp.$2);  // t
}

5.4.4 模式局限

ECMAScript 正则表达式暂未支持的特性:

  • \A 和\Z 锚(分别匹配字符串的开始和末尾)
  • 联合及交叉类
  • 原子组
  • x(忽略空格)匹配模式
  • 条件式匹配
  • 正则表达式注释

更多信息参考 Regular-Expressions.info 网站

5.5 原始值包装类型

ECMAScript 提供了 3 种特殊的引用类型:Boolean、Number 和 String。这些类型具有其他引用类型一样的特点,也具有与各自原始类型对应的特殊行为。每当用 到某个原始值的方法或属性时,后台都会创建一个相应原始包装类型的对象,从而暴露出操作原始值的各种方法。

案例

let s1 = "some text";
let s2 = s1.substring(2);

s1 是一个原始值,原始值本身不是对象,因此逻辑上不应该有方法。但这里却运行了substring()方法,这是因为后台进行了相关处理。

此处针对字符串原始值的处理主要如下:

  1. 创建一个 String 类型的实例
  2. 调用实例上的特定方法
  3. 销毁实例

对布尔值和数值而言,以上 3 步也会在后台发生,只不过使用的是 Boolean 和 Number 包装类型而已。

5.5.1 Boolean

Boolean 是对应布尔值的引用类型。

// 创建 Boolean 实例
let booleanObject = new Boolean(true);

实例方法

  • valueOf():返回一个原始值 true 或 false
  • toString() 与 toLocalString():返回字符串"true"或"false"

注意 Boolean 对象特殊情况:

let falseObject = new Boolean(false);
let result = falseObject && true;
console.log(result); // true

由于 falseObject 是对象,对象在布尔表达式中都会自动转换为 true(逻辑与操作时,如果第一个操作数是对象,则返回第二个操作数,此处第二个操作数值为 true)

5.5.2 Number

Number 是对应数值的引用类型。

// 创建 Number 实例
let numberObject = new Number(10);

实例方法

  • valueOf():返回一个原始数值
  • toString() 与 toLocalString():数值字符串

    toString() 方法可接收一个表示基数的参数,并返回相应基数形式的数值字符串

  • toFixed():返回包含指定小数点位数的数值字符串
  • toExponential():返回以科学记数法(也称为指数记数法)表示的数值字符串
    let num = 10;
    console.log(num.toExponential(1));  // "1.0e+1"
    
  • toPrecision():根据情况返回最合理的输出结果,可能是固定长度,也可能是科学记数法 形式

    这个方法接收一个参数,表示结果中数字的总位数(不包含指数)。 案例:

    let num = 99;
    console.log(num.toPrecision(1)); // "1e+2"
    console.log(num.toPrecision(2)); // "99"
    console.log(num.toPrecision(3)); // "99.0"
    

isInteger() 方法与安全整数

  • isInteger():ES6 新增了 Number.isInteger()方法,用于辨别一个数值是否保存为整数
    console.log(Number.isInteger(1));    // true
    console.log(Number.isInteger(1.00)); // true
    console.log(Number.isInteger(1.01)); // false
    
  • 安全整数:IEEE 754 数值格式有一个特殊的数值范围,在这个范围内二进制值可以表示一个整数值。

    范围:Number.MIN_SAFE_INTEGER(-2 ^ 53 + 1) 到 Number.MAX_SAFE_INTEGER(2 ^ 53 - 1)。

  • 安全整数范围的的鉴别:可以使用 Number.isSafeInteger()方法
    console.log(Number.isSafeInteger(-1 * (2 ** 53)));      // false
    console.log(Number.isSafeInteger(-1 * (2 ** 53) + 1));  // true
    console.log(Number.isSafeInteger(2 ** 53));             // false
    console.log(Number.isSafeInteger((2 ** 53) - 1));       // true
    

5.5.3 String

String 是对应字符串的引用类型。

// 创建 String 实例
let stringObject = new String("hello world");

特性:

  • String 对象的方法可以在所有字符串原始值上调用。3 个继承的方法 valueOf()、toLocaleString() 和 toString() 都返回对象的原始字符串值。
  • 每个 String 对象都有一个 length 属性

5.5.3.1 JavaScript 字符

  • 16 位码元(code unit)组成
  • charAt():返回给定索引位置的字符
  • charCodeAt():查看指定码元的字符编码
    console.log("abcde".charCodeAt(2)); // 99
    
  • fromCharCode():根据给定的 UTF-16 码元创建字符串中的字符
    console.log(String.fromCharCode(0x61, 0x62, 0x63, 0x64, 0x65)); // "abcde"
    
  • Unicode 代理对策略:每个字符使用两个 16 位码元的策略(每个字符使用另外 16 位去选择一个增补平面)
    // "smiling face with smiling eyes" 表情符号的编码是 U+1F60A 
    // 0x1F60A === 128522
    let message = "ab😊de";
    
    console.log(message.length);          // 6
    console.log(message.charAt(1));       // b
    console.log(message.charAt(2));       // �
    console.log(message.charAt(3));       // �
    console.log(message.charAt(4));       // d
    
    console.log(message.charCodeAt(1));  // 98
    console.log(message.charCodeAt(2));  // 55357
    console.log(message.charCodeAt(3));  // 56842
    console.log(message.charCodeAt(4));  // 100
    
    console.log(String.fromCodePoint(0x1F60A)); // 😊
    console.log(String.fromCharCode(97, 98, 55357, 56842, 100, 101)); // ab😊de
    
  • codePointAt():解析既包含单码元字符又包含代理对字符的字符串
    let message = "ab😊de";
    
    console.log(message.codePointAt(1)); // 98
    console.log(message.codePointAt(2)); // 128522
    console.log(message.codePointAt(3)); // 56842
    console.log(message.codePointAt(4)); // 100
    
  • fromCodePoint():接收任意数量的码点,返回对应字符拼接起来的字符串
    console.log(String.fromCharCode(97, 98, 55357, 56842, 100, 101)); // ab😊de
    console.log(String.fromCodePoint(97, 98, 128522, 100, 101));      // ab😊de
    

5.5.3.2 normalize() 方法

案例

let a1 = String.fromCharCode(0x00C5),     // Å 上面带圆圈的大写拉丁字母A
a2 = String.fromCharCode(0x212B),         // Å 长度单位“埃”
a3 = String.fromCharCode(0x0041, 0x030A); // Å U+004:大写拉丁字母A  U+030A:上面加个圆圈

console.log(a1, a2, a3); // Å Å Å

console.log(a1 === a2);  // false
console.log(a1 === a3);  // false
console.log(a2 === a3);  // false

案例中三个字符串看起来相同,实际不相等。为解决这个问题,Unicode 提供了 4 种规范化形式,可以将类似上面的字符规范化为一致的格式。

4 种规范化形式:

  • NFD(Normalization Form D)
  • NFC(Normalization Form C)
  • NFKD(Normalization Form KD)
  • NFKC(Normalization Form KC)

通过比较字符串与其调用 normalize() 的返回值,就可以知道该字符串是否已经规范化了:

let a1 = String.fromCharCode(0x00C5),
    a2 = String.fromCharCode(0x212B),
    a3 = String.fromCharCode(0x0041, 0x030A);

// U+00C5 是对 0+212B 进行 NFC/NFKC 规范化之后的结果 
console.log(a1 === a1.normalize("NFD"));  // false 
console.log(a1 === a1.normalize("NFC"));  // true 
console.log(a1 === a1.normalize("NFKD")); // false 
console.log(a1 === a1.normalize("NFKC")); // true

// U+212B 是未规范化的
console.log(a2 === a2.normalize("NFD"));  // false 
console.log(a2 === a2.normalize("NFC"));  // false 
console.log(a2 === a2.normalize("NFKD")); // false 
console.log(a2 === a2.normalize("NFKC")); // false

// U+0041/U+030A 是对 0+212B 进行 NFD/NFKD 规范化之后的结果 
console.log(a3 === a3.normalize("NFD"));  // true 
console.log(a3 === a3.normalize("NFC"));  // false 
console.log(a3 === a3.normalize("NFKD")); // true 
console.log(a3 === a3.normalize("NFKC")); // false

选择同一种规范化形式可以让比较操作符返回正确的结果:

let a1 = String.fromCharCode(0x00C5),
    a2 = String.fromCharCode(0x212B),
    a3 = String.fromCharCode(0x0041, 0x030A);

console.log(a1.normalize("NFD") === a2.normalize("NFD"));   // true 
console.log(a2.normalize("NFKC") === a3.normalize("NFKC")); // true 
console.log(a1.normalize("NFC") === a3.normalize("NFC"));   // true

5.5.3.3 字符串操作方法

  • concat():将一个或多个字符串拼接成一个新字符串
  • slice()、substr()和 substring():返回调用它们的字符串的一个子字符串(允许2个参数,第一个参数都表示开始的位置)
    • slice() 和 substring() 第二个参数是提取结束的位置(即该位置之前的字符会被提取出来)\
    • substr() 第二个参数表示返回的子字符串数量 \
    • 参数为复数的情况下:
      • slice() 将所有负值参数都当成字符串长度加上负参数值
      • substr() 将第一个负参数值当成字符串长度加上该值,将第二个负参数值转换为 0
      • substring() 将所有负参数值都转换为 0

案例

let stringValue = "hello world";

console.log(stringValue.slice(3));       // "lo world"
console.log(stringValue.substring(3));   // "lo world"
console.log(stringValue.substr(3));      // "lo world"
console.log(stringValue.slice(3, 7));    // "lo w"
console.log(stringValue.substring(3,7)); // "lo w"
console.log(stringValue.substr(3, 7));   // "lo worl"

5.5.3.4 字符串位置方法

  • indexOf():从字符串开头开始查找子字符串
  • lastIndexOf():从字符串末尾开始查找子字符串

参数 searchvalue,fromindex,其中第二个参数非必传,表示从哪个位置开始。

5.5.3.5 字符串包含方法

  • startsWith():检查开始于索引 0 的匹配项
  • endsWith():检查开始于索引(string.length - substring.length)的匹配项
  • includes():检查整个字符串
  • startsWith() 和 includes() 方法接收可选的第二个参数,表示开始搜索的位置
  • endsWidth() 参数 searchString [, length ],其中 length 为非必传,表示匹配项的长度

5.5.3.6 trim() 方法

所有字符串都拥有 trim() 方法。

  • 这个方法会创建字符串的一个副本(原始字符串不变),删除前、后所有空格符,再返回结果。
  • trimLeft()和 trimRight()方法分别用于从字符串开始和末尾清理空格符。

5.5.3.7 repeat() 方法

所有字符串都拥有 repeat() 方法。

这个方法接收一个整数参数,表示要将字符串复制多少次,然后返回拼接所有副本后的结果。

5.5.3.8 padStart() 和 padEnd() 方法

可填充的复制字符串。

  • 如果小于指定长度,则在相应一边填充字符,直至满足长度条件。
  • 这两个方法的第一个参数是长度,第二个参数是可选的填充字符串,默认为空格 (U+0020)。
  • 第二个参数并不限于一个字符。如果提供了多个字符的字符串,则会将其拼接并截断以匹配 指定长度。
  • 如果长度小于或等于字符串长度,则会返回原始字符串。

案例

let stringValue = "foo";

console.log(stringValue.padStart(6));       // "   foo"
console.log(stringValue.padStart(9, "."));  // "......foo"

console.log(stringValue.padEnd(6));         // "foo   "
console.log(stringValue.padEnd(9, "."));    // "foo......"
let stringValue = "foo"; 

console.log(stringValue.padStart(8, "bar")); // "barbafoo"
console.log(stringValue.padStart(2));        // "foo"

console.log(stringValue.padEnd(8, "bar"));   // "foobarba"
console.log(stringValue.padEnd(2));          // "foo"

5.5.3.9 字符串迭代与解构

字符串的原型上暴露了一个 @@iterator 方法,表示可以迭代字符串的每个字符。

案例

let message = "abc";
let stringIterator = message[Symbol.iterator]();

console.log(stringIterator.next()); // {value: "a", done: false} 
console.log(stringIterator.next()); // {value: "b", done: false} 
console.log(stringIterator.next()); // {value: "c", done: false}
console.log(stringIterator.next()); // {value: undefined, done: true}

for-of 循环

for (const c of "abcde") {
    console.log(c);
}

// a
// b
// c
// d
// e

字符串解构

let message = "abcde";
console.log([...message]); // ["a", "b", "c", "d", "e"]

5.5.3.10 字符串大小写转换

包括 4 个方法:toLowerCase()、toLocaleLowerCase()、toUpperCase()和 toLocaleUpperCase()。

如果不知道代码涉及什么语言,则最好使用地区特定的转换方法。

5.5.3.11 字符串模式匹配方法

  • match():法接收一个参数,可以是一个正则表达式字符串,也可以是一个 RegExp 对象(本质上跟 RegExp 对象的 exec() 方法相同)
    let text = "cat, bat, sat, fat";
    let pattern = /.at/;
    
    // 等价于pattern.exec(text)
    let matches = text.match(pattern); 
    console.log(matches.index); // 0 
    console.log(matches[0]); // "cat" 
    console.log(pattern.lastIndex); // 0
    
  • search():唯一的参数与 match() 方法一样:正则表达式字符串或 RegExp 对象。返回模式第一个匹配的位置索引,如果没找到则返回 -1。
    let text = "cat, bat, sat, fat";
    let pos = text.search(/at/);
    console.log(pos);  // 1
    
  • replace():简化子字符串替换操作。两个参数,第一个参数可以是一个 RegExp 对象或一个字符串(这个字符串不会转换为正则表达式),第二个参数可以是一个字符串或一个函数。

    如果第一个参数是字符串,那么只会替换第一个子字符串。要想替换所有子字符串,第一个参数必须为正则表达式并且带全局标记

    let text = "cat, bat, sat, fat";
    let result = text.replace("at", "ond");
    console.log(result);          // "cond, bat, sat, fat"
    
    result = text.replace(/at/g, "ond");
    console.log(result);          // "cond, bond, sond, fond"
    

5.5.3.12 localeCompare() 方法

比较两个字符串,返回如下 3 个值中的一个:

  • 如果按照字母表顺序,字符串应该排在字符串参数前头,则返回负值。(通常是-1,具体还要看与实际值相关的实现)
  • 如果字符串与字符串参数相等,则返回 0。
  • 如果按照字母表顺序,字符串应该排在字符串参数后头,则返回正值。(通常是 1,具体还要看与实际值相关的实现)

案例


let stringValue = "yellow";
console.log(stringValue.localeCompare("brick"));  // 1
console.log(stringValue.localeCompare("yellow")); // 0
console.log(stringValue.localeCompare("zoo"));    // -1

localeCompare()区分大小写,大写字母排在小写字母前面。

5.5.3.13 HTML 方法

方法输出
anchor(name)<a name="name">string</a>
big()<big>string</big>
bold()<b>string</b>
fixed()<tt>string</tt>
fontcolor(color)<font color="color">string</font>
fontsize(size)<font size="size">string</font>
italics()<i>string</i>
link(url)<a href="url">string</a>
small()<small>string</small>
strike()<strike>string</strike>
sub()<sub>string</sub>
sup()<sup>string</sup>

早期浏览器增加了辅助生成 HTML 标签的方法,不过现在基本上已经没有人使用了。

5.6 单例内置对象

内置对象:任何由 ECMAScript 实现提供、与宿主环境无关,并在 ECMAScript 程序开始执行时就存在的对象。

不用显式地实例化内置对象,因为它们已经实例化好了。

5.6.1 Global

  • Global 对象是 ECMAScript 中最特别的对象,因为代码不会显式地访问它
  • Global 对象是一种兜底对象,它所针对的是不属于任何对象的属性和方法
  • 所谓的全局函数与变量其实都是 Global 对象的属性

前面介绍的 isNaN()、isFinite()、parseInt() 和 parseFloat()方法都是 Global 对象的方法,其他方法如下:

5.6.1.1 URL 编码方法

  • encodeURI() 和 encodeURIComponent():用于编码统一资源标识符(URI),以便传给浏览器。
    • ecnodeURI():用于对整个 URI 进行编码,且不会编码属于 URL 组件的特殊字符,比如冒号、斜杠、问号、井号。
    • encodeURIComponent():用于编码 URI 中单独的组件,且会编码所有的非标准字符。 案例
    let uri = "http://www.wrox.com/illegal value.js#start";
    
    // "http://www.wrox.com/illegal%20value.js#start"
    console.log(encodeURI(uri));
    
    // "http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.js%23start" 
    console.log(encodeURIComponent(uri));
    
  • decodeURI()和 decodeURIComponent():针对 decodeURI() 和 decodeURIComponent() 编码方法的解码方法。
    • decodeURI():解码所有被 encodeURI() 编码的字符。
    • decodeURIComponent():解码所有被 encodeURIComponent() 编码的字符。 案例
    let uri = "http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.js%23start";
    
    // http%3A%2F%2Fwww.wrox.com%2Fillegal value.js%23start
    console.log(decodeURI(uri));
    
    // http:// www.wrox.com/illegal value.js#start
    console.log(decodeURIComponent(uri));
    

5.6.1.2 eval() 方法

一个完整的 ECMAScript 解释器。

接收一个参数,即一个要执行的 ECMAScript(JavaScript) 字符串

案例

eval("console.log('hi')");
// 等价于运行 console.log("hi");

通过 eval() 执行的代码属于该调用所在上下文,被执行的代码与该上下文拥有相同的作用域链,意味着定义在包含上下文中的变量可以在 eval() 调用内部被引用:

let msg = "hello world";
eval("console.log(msg)");  // "hello world"

类似地,可以在 eval() 内部定义一个函数或变量,然后在外部代码中引用:

eval("function sayHi() { console.log('hi'); }");
sayHi();  // hi
  • 通过 eval() 定义的任何变量和函数都不会被提升,这是因为在解析代码的时候,它们是被包含在一个字符串中的。它们只是在 eval()执行的时候才会被创建
  • 在严格模式下,eval() 内部创建的变量和函数无法被外部访问。同样,在严格模式下,赋值给 eval 也会导致错误。

5.6.1.3 Global 对象属性

  • undefined、NaN 和 Infinity 等特殊值都是 Global 对象的属性。
  • 所有原生引用类型构造函数,比如 Object 和 Function 也都是 Global 对象的属性。
属性说明
undefined特殊值 undefined
NaN特殊值 NaN
Infinity特殊值 Infinity
ObjectObject 的构造函数
ArrayArray 的构造函数
FunctionFunction 的构造函数
BooleanBoolean 的构造函数
StringString 的构造函数
NumberNumber 的构造函数
DateDate 的构造函数
RegExpRegExp 的构造函数
SymbolSymbol 的伪构造函数
ErrorError 的构造函数
EvalErrorEvalError 的构造函数
RangeErrorRangeError 的构造函数
ReferenceErrorReferenceError 的构造函数
SyntaxErrorSyntaxError 的构造函数
TypeErrorTypeError 的构造函数
URIErrorURIError 的构造函数

5.6.1.4 window 对象

  • ECMA-262 没有规定直接访问 Global 对象的方式,但浏览器将 window 对象实现为 Global 对象的代理。
  • 所有全局作用域中声明的变量和函数都变成了 window 的属性。

案例

var color = "red";

function sayColor() {
  console.log(window.color);
}

window.sayColor(); // "red"

另一种获取 Global 对象的方式:

let global = function() {
  return this;
}();

当一个函数在没有明确(通过成为某个对象的方法,或者通过 call()/apply())指定 this 值的情况下执行时,this 值等于 Global 对象。

5.6.2 Math

ECMAScript 提供了 Math 对象作为保存数学公式、信息和计算的地方。

Math对象上提供的计算要比直接在JavaScript实现的快得多

5.6.2.1 Math 对象属性

属性说明
Math.E自然对数的基数 e 的值
Math.LN1010 为底的自然对数
Math.LN22 为底的自然对数
Math.LOG2E以 2 为底 e 的对数
Math.LOG10E以 10 为底 e 的对数
Math.PIπ 的值
Math.SQRT1_21/2 的平方根
Math.SQRT22 的平方根

5.6.2.2 min() 和 max() 方法

用于确定一组数值中的最小值和最大值。(两个方法都可以接收任意多个参数)

案例

let max = Math.max(3, 54, 32, 16);
console.log(max);  // 54

let min = Math.min(3, 54, 32, 16);
console.log(min);  // 3

求一个数组中最大的数值

let values = [1, 2, 3, 4, 5, 6, 7, 8];
let max = Math.max(...values);
console.log(max);

5.6.2.3 舍入方法

把小数值舍入为整数的 4 个方法:Math.ceil()、Math.floor()、Math.round() 和 Math.fround()。

  • Math.ceil():向上取整
  • Math.floor():向下取整
  • Math.round():四舍五入
  • Math.fround():最接近的单精度(32 位)浮点值

案例

console.log(Math.ceil(25.9));   // 26
console.log(Math.ceil(25.5));   // 26
console.log(Math.ceil(25.1));   // 26

console.log(Math.floor(25.9));  // 25
console.log(Math.floor(25.5));  // 25
console.log(Math.floor(25.1));  // 25

console.log(Math.round(25.9));  // 26
console.log(Math.round(25.5));  // 26
console.log(Math.round(25.1));  // 25

console.log(Math.fround(0.4));  // 0.4000000059604645
console.log(Math.fround(0.5));  // 0.5
console.log(Math.fround(25.9)); // 25.899999618530273

5.6.2.4 random() 方法

返回一个 0~1 范围内的随机数(其中包含 0 但不包含 1,即范围 [0, 1))。

如果为了加密而需要生成随机数(传给生成器的输入需要较高的不确定性),建议使用 window.crypto.getRandomValues()。

5.6.2.5 其他方法

方法说明
Math.abs(x)返回 x 的绝对值
Math.exp(x)返回 Math.E 的 x 次幂
Math.expm1(x)等于 Math.exp(x) - 1
Math.log(x)返回 x 的自然对数
Math.log1p(x)等于 1 + Math.log(x)
Math.pow(x, power)返回x的power次幂
Math.hypot(...nums)返回 nums 中每个数平方和的平方根
Math.clz32(x)返回 32 位整数 x 的前置零的数量
Math.sign(x)返回表示 x 符号的 1、0、-0 或 -1
Math.trunc(x)返回 x 的整数部分,删除所有小数
Math.sqrt(x)返回 x 的平方根
Math.cbrt(x)返回 x 的立方根
Math.acos(x)返回 x 的反余弦
Math.acosh(x)返回 x 的反双曲余弦
Math.asin(x)返回 x 的反正弦
Math.asinh(x)返回 x 的反双曲正弦
Math.atan(x)返回 x 的反正切
Math.atanh(x)返回 x 的反双曲正切
Math.atan2(y, x)返回 y/x 的反正切
Math.cos(x)返回x的余弦
Math.sin(x)返回x的正弦
Math.tan(x)返回x的正切

即便这些方法都是由 ECMA-262 定义的,对正弦、余弦、正切等计算的实现仍然取决于浏览器,因为计算这些值的方式有很多种。结果,这些方法的精度可能因实现而异。

5.7 小结

  • JavaScript 中的对象称为引用值,几种内置的引用类型可用于创建特定类型的对象。

    • 引用值与传统面向对象编程语言中的类相似,但实现不同。
    • Date 类型提供关于日期和时间的信息,包括当前日期、时间及相关计算。
    • RegExp 类型是 ECMAScript 支持正则表达式的接口,提供了大多数基础的和部分高级的正则表达式功能。
  • JavaScript 中的原始值可以被当成对象来使用,3 种原始值包装类型:Boolean、Number 和 String。

    • 每种包装类型都映射到同名的原始类型。
    • 以读模式访问原始值时,后台会实例化一个原始值包装类型的对象,借助这个对象可以操作相应的数据。
    • 涉及原始值的语句执行完毕后,包装对象就会被销毁。
  • 全局上下文中的两个内置对象:Global 和 Math。

    • Global 对象在大多数 ECMAScript 实现中无法直接访问。不过,浏览器将其实现为 window 对象,所有全局变量和函数都是 Global 对象的属性。
    • Math 对象包含辅助完成复杂计算的属性和方法。