内容均摘自JavaScript高级程序设计第四版,仅用于记录学习过程。
一. 理解引用类型
5.1 理解引用类型
引用值(或对象)是某个特定的引用类型的实例
- 在ECMAScript 中,
引用类型是把数据和功能组织到一起的结构 - 常被人
错误地称"类" 引用类型有时也被称为对象定义,因为它们描述了自己的对象应有的属性和方法
新对象通过使用new操作符后跟一个构造函数来创建,构造函数就是用来创建新对象的函数,比如下面这行代码:
let now = new Date();
- 创建了引用类型
Date的一个新实例,并将它保存在变量now中 Date()是构造函数,负责创建一个只有默认属性和方法的简单对象
二. Date
5.2 Date
Date类型将日期保存为1970年1月1日零时至今所经过的毫秒数
创建日期对象,使用new操作符来调用Date构造函数:
let now = new Date();
- 不给
Date构造函数传参数的情况下,创建的对象将保存当前日期和时间 - 若要基于其他日期和时间创建日期对象,必须传入其毫秒表示,ECMAScript 为此提供了两个辅助方法:
Date.parse() 和 Date.UTC()
Date.parse()
Date.parse() :接收一个表示日期的字符串参数,尝试将这个字符串转换为表示该日期的毫秒数
- 支持的日期格式:
- “月/日/年”,如"5/23/2021"
- “月名 日,年”,如"May 23,2021"
- “周几 月名 日 年 时:分:秒 时区”,如"Tue May 23 2021 00:00:00 GMT-0700"
若直接把表示日期的字符串传给Date(),Date会在后台调用Date.parse()
比如,要创建一个表示"2021年5月23日"的日期对象,可以使用以下代码:
let date = new Date(Date.parse("5/23/2021"));
let date = new Date("5/23/2021");
??:若传的字符串不表示日期,则Date.parse()返回NaN
Date.UTC()
Date.UTC()也是返回日期的毫秒数
- 参数:年、月(以0为起点)、日(1~31)、时(0~23)、分、秒、毫秒
(只有年和月是必需的,日不提供则默认为1,其他不提供则默认为0)
Date.UTC() 也会被Date()隐式调用,但隐式调用下创建的是本地日期,不是GMT日期
下面是两个例子:
let date = new Date(Date.UTC(2000, 0));
let date = new Date(2000, 0);
let date = new Date(Date.UTC(2021, 7, 1, 5, 55, 55));
Date.now()
返回表示方法执行时的日期和时间的毫秒数 这个方法可以用在代码分析中:
let start = Date.now();
doSomething();
let stop = Date.now();
result = stop - start;
5.2.1 继承的方法
与其他引用类型一样,Date重写了toLocaleString()、toString()、valueOf()方法
toLocaleString():返回与浏览器运行的本地环境一致的日期和时间,格式中包含AM(上午) 或 PM(下午),但不包含时区信息,具体格式可能因浏览器而不同toString():返回带时区信息的日期和时间
下面给出这两种方法返回的示例: 2021年8月2日零点
8/2/2021 12:00:00 AM
Mon Aug 2 2021 00:00:00 GMT-0800 (Pacific Standard Time)
let date1 = new Date(2021, 0, 1);
let date2 = new Date(2021, 1, 1);
console.log(date1 < date2);
5.2.2 日期格式化方法
toDateString():显示日期中的周几、月、日、年(格式特定于实现)toTimeString():显示日期中的时、分、秒、时区(格式特定于实现)toLocalDateString():显示日期中的周几、月、日、年(格式特定于实现和地区)toLocaleTimeString():显示时期中的时、分、秒(格式特定于实现和地区)toUTCString():显示完整的UTC日期(格式特定于实现)
这些方法的输出会因浏览器而异,因此不能用于在用户界面上一致地显示日期。
5.2.3 日期/时间组件方法
Date引用类型的其他方法涉及取得或设置日期值的特定部分 ??:UTC日期指的是没有时区偏移(将日期转换为GMT)时的日期  
三. RegExp
5.3 RegExp
ECMAScript 通过 RegExp 类型支持正则表达式 正则表达式的创建:
let expression = /pattern/flags;
pattern(模式)可以是任何正则表达式,包括字符类、限定符、分组、向前查找、反向引用 每个正则表达式可以不带或带多个flags(标记),用于控制正则表达式的行为
匹配模式的标记:
g:全局模式,查找字符串的全部内容,而不是找到第一个匹配的内容就结束I:不区分大小写,查找匹配时忽略大小写m:多行模式,查找到一行文本末尾时会继续查找y:粘附模式,只查找从lastIndex开始及之后的字符串u:Unicode模式,启用 Unicode 匹配s:dotAll模式,匹配任何字符,包括\n或\r
let p1 = /at/g;
let p2 = /[bc]at/i;
元字符在模式中必须转义,因为元字符在正则表达式中都有多种特殊功能,所以要匹配这些字符本身就必须使用反斜杠来转义 包括:
() [] {} \ ^ $ | ? * + .
let p1 = /\[bc\]at/i;
let p2 = /\.at/gi;
上面例子中的正则表达式都是使用字面量形式定义的 也可以使用 RegExp构造函数来创建
let p = /[bc]at/i;
let p = new RegExp("[bc]at", "i");
- 因为
RegExp引用类型的模式参数是字符串,所以在某些情况下需要二次转义  - 使用
RegExp也可以基于已有的正则表达式实例,并可选择性地修改它们的标记:
const r1 = /cat/g;
const r2 = new RegExp(r1);
const r3 = new RegExp(r1, "i");
console.log(r2);
console.log(r3);
5.3.1 RegExp实例属性
每个 RegExp 实例都有下列属性:
global:布尔值,是否设置了g标记ignoreCase:布尔值,是否设置了i标记unicode:布尔值,是否设置了u标记sticky:布尔值,是否设置了y标记multiline:布尔值,是否设置了m标记dotAll:布尔值,是否设置了s标记lastIndex:整数,在源字符串中下一次搜索的开始位置source:正则表达式的字面量字符串,没有开头和结尾的斜杠flags:正则表达式的标记字符串
通过这些属性可以全面了解正则表达式的信息,不过实际开发中用的不多
5.3.2 RegExp 实例方法
exec()
RegExp实例的主要方法是exec(),主要用于配合捕获组使用
- 只接收一个参数,要作为
模式的字符串 - 如果找到了匹配项,返回包含第一个匹配信息的数组
- 如果没找到匹配项,返回
null
返回的数组是Array的实例,包含两个额外的属性: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);
console.log(matches.input);
console.log(matches[0]);
console.log(matches[1]);
console.log(matches[2]);
这个例子中,模式包含两个捕获组:
- 最内部的匹配项
" and baby" - 外部的匹配项
" and dad" 或 " and dad and baby"
数组的第一个元素是匹配的整个字符串,第二个元素是匹配第一个捕获组的字符串,第三个元素是匹配第二个捕获组的字符串。
全局标记
如果模式设置了全局标记,则每一次调用exex()都会返回一个匹配的信息,若不设置全局标记,无论调用多少次exex(),也只会返回第一个匹配的信息
看下例:
let text = "cat, bat, sat, fat";
let pattern = /.at/;
let matches = pattern.exec(text);
console.log(matches.index);
console.log(matches[0]);
console.log(pattern.lastIndex);
matches = pattern.exec(text);
console.log(matches.index);
console.log(matches[0]);
console.log(pattern.lastIndex);
上面例中的模式没有设置全局标记,因此调用exex()只会返回第一个匹配项("cat") ??:lastIndex在非全局模式下始终不变 (lastIndex表示在查找字符串中下一次搜索开始的位置)
如果在这个模式上设置了g标记,则每次调用exec()都会在字符串中向前搜索下一个匹配项
看下例:
let text = "cat, bat, sat, fat";
let pattern = /.at/g;
let matches = pattern.exec(text);
console.log(matches.index);
console.log(matches[0]);
console.log(pattern.lastIndex);
matches = pattern.exec(text); console.log(matches.index);
console.log(matches[0]);
console.log(pattern.lastIndex);
...
这次模式设置了全局标记,因此每次调用 exec()都会返回字符串中的下一个匹配项,直到搜索到 字符串末尾 ??:lastIndex在全局模式下每次调用exec()都会更新 (lastIndex表示在查找字符串中下一次搜索开始的位置)
粘附标记
如果模式设置了粘附标记 y,则每次调用exec() 就只会在 lastIndex 的位置上寻找匹配项,不会跳过往后查找 粘附标记覆盖全局标记
let text = "cat, bat, sat, fat";
let pattern = /.at/y;
let matches = pattern.exec(text);
console.log(matches.index);
console.log(matches[0]);
console.log(pattern.lastIndex);
matches = pattern.exec(text);
console.log(matches);
console.log(pattern.lastIndex);
pattern.lastIndex = 5;
matches = pattern.exec(text);
console.log(matches.index);
console.log(matches[0]);
console.log(pattern.lastIndex);
test()
正则表达式的另一个方法是test()
- 接收一个字符串参数
- 如果输入的
文本与模式匹配,则返回true,否则返回false
test()常用于if语句中:
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()都返回正则表达式的字面量表示,valueOf()方法返回正则表达式本身 比如:
let pattern = new RegExp("\\[bc\\]at", "gi");
console.log(pattern.toString());
console.log(pattern.toLocaleString());
console.log(pattern.valueOf());

5.3.3 RegExp构造函数属性
RegExp构造函数本身也有几个属性(在其他语言中,这种属性被称为静态属性) 这些属性适用于作用域中的所有正则表达式 并且这些属性可以通过两种不同的方式访问(全名和简写) 
四. 原始值包装类型
5.4 原始值包装类型
ECMAScript 提供了 3 种特殊的引用类型: Boolean、Number 、String 这些类型具有本章介绍的其他引用类型一样的特点,也具有与各自原始类型对应的特殊行为
每当用到某个原始值的方法和属性时,后台都会创建一个相应的原始包装类型的对象,从而暴露出操作原始值的各种方法
let s1 = "some text";
let s2 = s1.substring(2);
变量s1包含一个字符串,它是一个原始值 第二行在变量s1上调用了substring()方法,把结果保存在变量s2中
我们知道,原始值本身不是对象,逻辑上不应该有方法,这是因为后台进行了很多处理,从而实现上述操作。
- 具体来说,当第二行访问变量
s1时,是以读模式访问的,也就是要从内存中读取变量s1保存的值 - 在以
读模式访问字符串的时候,后台都会执行以下3步:
- 创建一个
String类型的实例 - 调用
实例上的特定方法 销毁实例
let s1 = new String("some text");
let s2 = s1.substring(2);
s1 = null;
这种行为可以让原始值拥有对象的行为
引用类型与原始值包装类型的主要区别在于对象的生命周期
可以显式地使用Boolean、Number 、String构造函数创建原始值包装对象,确定是必要时再这么做,否则容易让开发者分不清到底它们是原始值还是引用值
Object构造函数作为一个工厂方法,可以根据传入值的类型返回相应原始值包装类型的实例
let obj1 = new Object("some text");
console.log(obj1 instanceof String);
let obj2 = new Object(1);
console.log(obj2 instanceof Number);
let obj3 = new Object(true);
console.log(obj3 instanceof Boolean);
??:使用new调用原始包装类型的构造函数,与调用同名的转型函数并不一样
let value = "25";
let num1 = Number(value);
console.log(typeof num1);
let num2 = new Number(value);
console.log(typeof num2);
在本例中,变量num1保存的是一个值为25的原始数值,而变量num2保存的是一个Number的实例
不推荐显式创建原始值包装类型的实例,虽然它们对于操作原始值的功能很重要
5.4.1 Boolean
Boolean是布尔值的引用类型 要创建一个Boolean对象,使用Boolean(),并传入 true 或 false,如下例所示:
let booleanObj = new Boolean(true);
在布尔表达式中使用Boolean对象
Boolean 对象在布尔表达式中使用时容易引起误会,用得很少:
let falseObj = new Boolean(false);
let result = falseObj && true;
console.log(result);
let falseVal = false;
result = falseVal && true;
console.log(result);
在本例中,第2行对falseObj和true进行布尔运算,但这个运算是对falseObj对象,而不是对它表示的值false求值,这里就要??:所有对象在布尔表达式中都会自动转换为true 所以true && true当然也为true
原始布尔值和布尔对象的区别
原始值和引用值(Boolean对象)还有一些区别:
typeof操作符对原始值返回"boolean",对引用值返回objectinstanceof操作符对原始值返回false,对引用值返回true
console.log(falseObj instanceof Boolean);
console.log(falseVal instanceof Boolean);
理解原始布尔值和Boolean对象的区别很重要 强烈建议永远不要使用Boolean对象
5.4.2 Number
Number是数值的引用类型 要创建一个Number对象,使用Number(),并传入 一个数值,如下例所示:
let numberObj = new Number(10);
Number引用类型重写了valueOf()、toLocaleString()、toString()
valueOf()返回Number对象表示的原始数值toLocaleString()、toString()返回数值字符串toString()可以接收一个表示基数的参数
let num = 10;
console.log(num.toString());
console.log(num.toString(2));
console.log(num.toString(16));
将数值格式化为字符串的方法
toFixed():返回包含指定小数点位数的数值字符串,此方法会自动四舍五入
let num = 10;
console.log(num.toFixed(2));
toExponential():返回以科学计数法表示的数值字符串
let num = 10;
console.log(num.toExponential(1));
console.log(num.toExponential(3));
toPrecision():根据情况返回最合理的输出结果,接收一个 参数,表示结果中数字的总位数(不包含指数)
let num = 99;
console.log(num.toPrecision(1));
console.log(num.toPrecision(2));
console.log(num.toPrecision(3));
在本例中,第二行:要用1位数字表示99,99不能只用1位数字来精确表示,所以这个方法将它舍入为100 本质上,toPrecision()方法会根据数值和精度来决定调用 toFixed() 还是 toExponential()
原始数值和数值对象的区别
typeof操作符对原始值返回"number",对引用值返回objectinstanceof操作符对原始值返回false,对引用值返回true
let numObj = new Number(10);
let numVal = 10;
console.log(numObj instanceof Number);
console.log(numVal instanceof Number);
理解原始数值和Number对象的区别很重要 同样建议不要使用Number对象
isInteger()方法与安全整数
ES6新增了Number.isInteger()方法,用于辨别一个数值是否保存为整数
console.log(Number.isInteger(1));
console.log(Number.isInteger(1.00));
在Number.MIN_SAFE_INTERGER(-253 + 1)到Number.MAX_SAFE_INTERGER(253 - 1)范围内,二进制值可以表示为一个整数值 超出此范围的数值,即使尝试保存为整数,IEEE 754 编码格式也意味着二进制值可能会表示一个完全不同的数值 可以使用Number.isSageInteger()鉴别整数是否在这个范围内
console.log(Number.isSafeInteger(-1 * (2 ** 53)));
console.log(Number.isSafeInteger(-1 * (2 ** 53) + 1));
5.4.3 String
String是字符串的引用类型 要创建一个String对象,使用String(),并传入 一个字符串,如下例所示:
let stringObj = new String("hello world");
String对象的方法可以在所有的字符串原始值上调用,3个继承的方法valueOf()、toLocaleString() 、toString()都返回对象的原始字符串值
length属性表示字符串中字符的数量,需要??:即使字符串中包含双字节字符,也仍然会按单字符来计数
String类型提供了很多方法来解析和操作字符串:
JavaScript 字符
字符串由16位码元组成,对多数字符来说,每16位码元对应1个字符。
- 也就是说
length属性表示字符串包含多少16位码元:
let message = "abc";
console.log(message.length);
charAt()方法返回给定索引位置的字符,具体来说,这个方法查找指定索引位置的16位码元,并返回该码元对应的字符:
let message = "abc";
console.log(message.charAt(2));
拼接字符串方法 concat()
concat():用于将一个或多个字符串拼接成一个新字符串
let stringVal = "hello ";
let result = stringVal.concat("world");
console.log(result);
console.log(stringValue);
??:stringVal的值是不变的 concat()也可以接收任意多个参数,可以一次性拼接多个字符串:
let stringVal = "hello ";
let result = stringVal.concat("world","!","hahaha");
console.log(result);
(但更常用的还是+拼接多个字符串)
提取子字符串的方法 slice()、substr()、substring()
slice()、substr()、substring()都会返回调用它们的字符串的一个子字符串,都接收1或2个参数,都不会修改原字符串
第一个参数:表示子字符串开始的位置第二个参数:
- 对
slice()和substring()而言,表示提取结束的位置(该位置之前的字符会被提取) - 对
substr()而言,表示提取的子字符串的长度
(若省略第二个参数,默认提取到字符串末尾) ??:接收两个参数时,以较小的那个参数作为起点,较大的参数作为终点
let stringVal = "hello world";
console.log(stringVal.slice(3));
console.log(stringVal.slice(3,7));
console.log(stringVal.substring(3,7));
console.log(stringVal.substr(3,7));
当某个参数为负数时:
slice():将所有负数参数都当成字符串长度+负数值
let stringVal = "hello world";
console.log(stringVal.slice(-3));
console.log(stringVal.slice(3, -4));
let stringVal = "hello world";
console.log(stringVal.substring(-3));
console.log(stringVal.substring(3, -4));
substr():将第一个负参数值当成字符串长度+此值,第二个负参数值当成0
let stringVal = "hello world";
console.log(stringVal.substr(-3));
console.log(stringVal.substr(3, -4));
定位子字符串位置的方法 indexOf() 、lastIndexOf()
indexOf() 和 lastIndexOf()从字符串中搜索传入的字符串,并返回位置 (如果没找到,则返回-1) 区别在于:
indexOf()从字符串开头开始查找子字符串lastIndexOf()从字符串末尾开始查找子字符串
let stringVal = "hello world";
console.log(stringVal.indexOf("o"));
console.log(stringVal.lastIndexOf("o"));
indexOf() 和 lastIndexOf()都接收可选的第二个参数,表示开始搜索的位置 像这样使用第二个参数并循环调用 indexOf() 或 lastIndexOf(),可以在字符串中找到所有的目标子字符串,如下所示:
let stringVal = "Pain makes us stronger";
let positions = [];
let pos = stringVal.indexOf("s");
while(pos != -1) {
positions.push(pos);
pos = stringVal.indexOf("s", pos+1);
}
console.log(positions);
本例中,首先取得第一个 "s"的位置,然后进入循环,将上一次的位置+1再传给indexOf() 将每一个"s"的位置都保存在positions数组中
包含字符串的方法 startsWith()、endsWith()、includes()
ES6新增了3个用于判断字符串中是否包含另一个字符串的方法: 这些方法都会从字符串中搜索传入的字符串,并返回一个表示是否包含的布尔值 区别在于:
startsWith():检查开始于索引0的匹配项endsWith():检查开始于索引string.length - substring.length的匹配项includes():检查整个字符串
let message = "foobarbaz";
console.log(message.startsWith("foo"));
console.log(message.startsWith("bar"));
console.log(message.endsWith("baz"));
console.log(message.endsWith("bar"));
console.log(message.includes("bar"));
console.log(message.includes("qux"));
startsWith()、includes()接收可选的第二个参数,表示开始搜索的位置,这两方法会从指定位置向字符串末尾搜索,忽略该位置前的所有字符endsWith()接收可选的第二个参数,表示应该当作字符串末尾的位置
let message = "foobarbaz";
console.log(message.endsWith("bar", 6));
trim() 方法
trim()方法会创建字符串的一个副本,删除前后所有空格符,再返回结果
let stringVal = " hello world ";
let trimmedStringVal = stringVal.trim();
console.log(stringVal);
console.log(trimmedStringVal);
还有trimLeft():从字符串开始清理空格符 trimRight():从字符串末尾清理空格符
repeat() 方法
repeat()接收一个整数参数,表示要将字符串复制多少次,然后返回拼接所有副本后的结果
let stringVal = "ha ";
console.log(stringVal.repeat(5));
padStart() 和 padEnd() 方法
padStart() 和 padEnd()会复制字符串,如果小于指定长度,则在相应一边填充字符,直至满足长度条件 这两个方法接收两个参数
- 第一个参数是
长度 - 第二个参数是可选的
填充字符,默认为空格
let stringVal = "foo";
console.log(stringVal.padStart(6));
console.log(stringVal.padStart(9,"!"));
console.log(stringVal.padEnd(9,"!"));
第二个参数并不限于一个字符,可提供多个字符的字符串,则会将其拼接并截断以匹配指定长度 如果长度小于或等于字符串长度,则会返回原始字符串
let stringVal = "foo";
console.log(stringVal.padStart(8, "bar"));
console.log(stringVal.padStart(2));
字符串迭代与解构
字符串的原型上暴露了一个@@iterator方法,表示可以迭代字符串的每个字符 可以像下面这样手动使用迭代器:
let message = "abc";
let stringIterator = message[Symbol.iterator]();
console.log(stringIterator.next());
console.log(stringIterator.next());
console.log(stringIterator.next());
console.log(stringIterator.next());
在 for-of 循环中可以通过这个迭代器访问每个字符:
for (const letter of "abc") {
console.log(letter);
}
有了这个迭代器之后,字符串就可以通过解构操作符来解构了,比如更方便地把字符串分割为字符数组:
let message = "abc";
console.log([...message]);
字符串大小写转换
toLowerCase() toLocaleLowerCase() toUpperCase() toLocaleUpperCase()
let stringValue = "hello world";
console.log(stringValue.toLocaleUpperCase());
console.log(stringValue.toUpperCase());
console.log(stringValue.toLocaleLowerCase());
console.log(stringValue.toLowerCase());
toLocaleLowerCase()和 toLocaleUpperCase()方法旨在基于特定地区实现 在很多地区,地区特定的方法与通用的方法是一样的。但在少数语言中(如土耳其语),要使用地区特定的方法才能实现正确转换
字符串模式匹配方法
String类型为在字符串中实现模式匹配提供了几个方法
match():本质上跟RegExp对象的exec()方法相同
match()方法接收一个参数,可以是一个正则表达式字符串,也可以是一个RegExp对象
let text = "cat, bat, sat, fat";
let pattern = /.at/;
let matches = text.match(pattern); console.log(matches.index);
console.log(matches[0]);
console.log(pattern.lastIndex);
match()返回的数组与exec() 返回的数组是一样的:第一个元素是与整个模式匹配的字符串,其余元素则是与表达式中的捕获组匹配的字符串(如果有的话)
search():接收唯一的参数,可以是一个正则表达式字符串,也可以是一个RegExp对象
- 返回模式第一个匹配的
位置索引,没找到返回-1 search()始终从字符串开头向后匹配模式
let text = "cat, bat, sat, fat";
let pos = text.search(/at/);
console.log(pos);
replace()
为简化子字符串替换操作,ECMAScript 提供了 replace()方法
- 接收两个参数
- 第一个参数可以是一个
RegExp 对象或一个字符串(这个字符串不会转换为正则表达式) - 第二个参数可以是
一个字符串或一个函数
let text = "cat, bat, sat, fat";
let result = text.replace("at", "ond");
console.log(result);
如果第一个参数是字符串,那只会替换第一个子字符串 要想替换所有子字符串,第一个参数必须为正则表达式并且带全局标记
let text = "cat, bat, sat, fat";
let result = text.replace(/at/g, "ond");
console.log(result);
split()
最后一个与模式匹配相关的字符串方法是 split() 这个方法会根据传入的分隔符将字符串拆分成数组
- 接收两个参数
- 第一个参数作为
分隔符,可以是字符串,也可以是RegExp对象 - 第二个可选参数是
数组的大小,确保返回的数组不会超过指定大小
let colorTxt = "red, blue, green";
let color1 = colorTxt.split(",");
let color2 = colorTxt.split(",", 2);
localeCompare()
localeCompare()比较两个字符串,返回如下3个值中的一个
- 如果按照字母表顺序,
字符串应该排在字符串参数前面,则返回负值(通常是-1) - 如果字符串与字符串参数相等,则返回
0 - 如果按照字母表顺序,
字符串应该排在字符串参数后面,则返回正值(通常是1)
let stringValue = "yellow";
console.log(stringValue.localeCompare("brick"));
console.log(stringValue.localeCompare("yellow"));
console.log(stringValue.localeCompare("zoo"));
"brick"按字母表顺序应该排在"yellow"前头,因此localeCompare() 返回 1
五. 单例内置对象
5.5 单例内置对象
ECMA-262 对内置对象的定义是: “任何由 ECMAScript 实现提供、与宿主环境无关,并在 ECMAScript 程序开始执行时就存在的对象” 这意味着:开发者不用显式地实例化内置对象,因为它们已经实例化好了
前面我们已经解除了大部分内置对象,包括Object、Array 、String 这里要介绍另外两个单例内置对象:Global、Math
5.5.1 Global
Global对象最特别,因为代码不会显式地访问它 在全局作用域中定义的变量和函数都会变成Global对象的属性,前面介绍的函数包括isNaN()、isFinite()、parseInt()等,实际上都是Global对象的方法
Global 对象上还有一些其他方法:
URL编码方法
encodeURI()、encodeURIComponent() 用于编码统一资源标识符(URI),以传给浏览器
有效的URI不能包含某些字符,比如空格
encodeURI():对整个URI进行编码,比如"www.wrox.com/illegal value.js"encodeURIComponent():对URI中单独的组件进行编码,比如前面URL中的"illegal value.js"
区别在于:
encodeURI()不会编码属于URL组件的特殊字符,比如冒号、斜杠、问号、并号encodeURIComponent()会编码它发现的所有非标准字符
let uri = "http://www.wrox.com/illegal value.js#start";
console.log(encodeURI(uri));
console.log(encodeURIComponent(uri));
本例中,使用encodeURI()编码,只有空格被替换为%20,而使用encodeURIComponent()编码,所有非字母字符都替换成了相应的编码形式
与 encodeURI() 和 encodeURIComponent() 相对的是 decodeURI() 和 decodeURIComponent()
decodeURI() 只对使用 encodeURI() 编码过的字符解码decodeURIComponent()解码所有被 encodeURIComponent() 编码的字符,基本上就是解码所有特殊值
eval()
eval()是一个完整的ECMAScript 解释器 接收一个参数,即要执行的JavaScript字符串
eval("console.log('hi')");
console.log('hi');
当解释器发现 eval() 调用时,会将参数解释为实际的 ECMAScript 语句,然后将其插入到该位置
Global 对象属性
 
获取Global对象
- 通过
window对象, 浏览器将window对象实现为Global对象的代理,因此所有全局作用域中声明的变量和函数都变成了window的属性 - 另一种获取
Global对象的方式:
let global = function() {
return this;
}();
这段代码创建了一个立即调用的函数,返回了this的值 当一个函数在没有明确成为某个对象的方法,或通过call() / apply() 指定this值的情况下执行的时候,this值就是Global对象
5.5.2 Math
Math对象的属性

min() 和 max()
min() 和 max() 用于确定一组数值中的最小值和最大值 接收任意多个参数
let max = Math.max(3, 54, 32, 16);
console.log(max);
let min = Math.min(3, 54, 32, 16);
console.log(min);
要知道数组中的最大值和最小值,可以像下面这样使用扩展操作符:
let values = [1, 2, 3, 4, 5, 6, 7, 8];
let max = Math.max(...val);
舍入方法
把小数值舍入为整数:
Math.ceil():始终向上舍入为最接近的整数Math.floor():始终向下舍入为最接近的整数Math.round():四舍五入Math.fround():返回数值最接近的单精度(32 位)浮点值表示
random() 方法
Math.random()方法返回一个0~1范围内的随机数,包含0但不包含1
可以基于如下公式使用 Math.random()从一组整数中随机选择一个数:
number = Math.floor(Math.random() * totalNumber + minVal)
totalNumber为可选的总数,minVal为最小的可能值 这里使用了 Math.floor() 方法,因为 Math.random() 始终返回小数
因此,如果想从1~10范围内随机选择一个数: 可选的总数有10个值,最小的可能值为1
let num = Math.floor(Math.random() * 10 + 1);
其他方法

六. 小结
js中的对象称为引用值,几种内置的引用类型可用于创建特定类型的对象
引用类型与类相似,但实现不同Date引用类型提供关于日期和时间的信息RegExp引用类型是支持正则表达式的接口
js比较独特的一点是:函数实际上是 Function类型的实例,也就是说函数也是对象。因 为函数也是对象,所以函数也有方法,可以用于增强其能力。
由于原始值包装类型,js中的原始值可以被当成对象使用,有3种原始值包装类型
Boolean、Number、String- 每种包装类型都映射到同名的原始类型
- 以
读模式访问原始值时,后台会实例化一个原始值包装类型的对象,借助这个对象可以操作响应的数据 - 涉及原始值的语句执行完毕后,包装对象就会被
销毁
当代码开始执行时,全局上下文中会存在两个内置对象: Global 和 Math
Global对象无法直接访问,但浏览器将其实现为window对象,所有全局变量和函数都是Global对象的属性
|