目录
注:适合有基础并需要系统复习的同学
5.1 Date 类型
要创建一个日期对象,使用 new 操作符和 Date 构造函数即可:
//取得开始时间
var start = Date.now();
//调用函数
doSomething();
//取得停止时间
var stop = Date.now(),
result = stop – start;
5.1.1 继承的方法
Date 类型重写了 toLocaleString()、toString()和 valueOf()方法。但与 其他类型不同,重写后这些方法的返回值不一样。Date 类型的 toLocaleString()方法返回与浏览器 运行的本地环境一致的日期和时间。这通常意味着格式中包含针对时间的 AM(上午)或 PM(下午), 但不包含时区信息(具体格式可能因浏览器而不同)。toString()方法通常返回带时区信息的日期和时 间,而时间也是以 24 小时制(0~23)表示的。下面给出了 toLocaleString()和 toString()返回的 2019 年 2 月 1 日零点的示例(地区为"en-US"的 PST,即 Pacific Standard Time,太平洋标准时间): toLocaleString() - 2/1/2019 12:00:00 AM toString() - Thu Feb 1 2019 00:00:00 GMT-0800 (Pacific Standard Time)
5.1.2 日期格式化方法
Date 类型有几个专门用于格式化日期的方法,它们都会返回字符串:
toDateString()显示日期中的周几、月、日、年(格式特定于实现);
toTimeString()显示日期中的时、分、秒和时区(格式特定于实现);
toLocaleDateString()显示日期中的周几、月、日、年(格式特定于实现和地区);
toLocaleTimeString()显示日期中的时、分、秒(格式特定于实现和地区);
toUTCString()显示完整的 UTC 日期(格式特定于实现)。
这些方法的输出与 toLocaleString()和 toString()一样,会因浏览器而异。因此不能用于在用户界面上一致地显示日期。
5.1.3 日期/时间组件方法
5.2 RegExp 类型
ECMAScript 通过 RegExp 类型支持正则表达式。正则表达式使用类似 Perl 的简洁语法来创建: let expression = /pattern/flags;
这个正则表达式的 pattern(模式)可以是任何简单或复杂的正则表达式,包括字符类、限定符、 分组、向前查找和反向引用。每个正则表达式可以带零个或多个 flags(标记),用于控制正则表达式 的行为。下面给出了表示匹配模式的标记:
g:全局模式,表示查找字符串的全部内容,而不是找到第一个匹配的内容就结束。
i:不区分大小写,表示在查找匹配时忽略 pattern 和字符串的大小写。
m:多行模式,表示查找到一行文本末尾时会继续查找。
y:粘附模式,表示只查找从 lastIndex 开始及之后的字符串。
u:Unicode 模式,启用 Unicode 匹配。
s:dotAll 模式,表示元字符.匹配任何字符(包括\n 或\r)。 使用不同模式和标记可以创建出各种正则表达式,比如:
// 匹配字符串中的所有"at"
let pattern1 = /at/g;
// 匹配第一个"bat"或"cat",忽略大小写
let pattern2 = /[bc]at/i;
// 匹配所有以"at"结尾的三字符组合,忽略大小写
let pattern3 = /.at/gi;
所有元字符在模式中也必须转义,包括:
( [ { \ ^ $ | ) ] } ? * + .
// 匹配第一个"bat"或"cat",忽略大小写
let pattern1 = /[bc]at/i;
// 匹配第一个"[bc]at",忽略大小写
let pattern2 = /\[bc\]at/i;
// 匹配所有以"at"结尾的三字符组合,忽略大小写
let pattern3 = /.at/gi;
// 匹配所有".at",忽略大小写
let pattern4 = /\.at/gi;
任何使用字面量定义的正则表达式也可 以通过构造函数来创建,比如:
// 匹配第一个"bat"或"cat",忽略大小写
let pattern1 = /[bc]at/i;
// 跟 pattern1 一样,只不过是用构造函数创建的
let pattern2 = new RegExp("[bc]at", "i");
5.2.1 RegExp实例属性
每个 RegExp 实例都有下列属性,提供有关模式的各方面信息。
global:布尔值,表示是否设置了 g 标记。
ignoreCase:布尔值,表示是否设置了 i 标记。
unicode:布尔值,表示是否设置了 u 标记。
sticky:布尔值,表示是否设置了 y 标记。
lastIndex:整数,表示在源字符串中下一次搜索的开始位置,始终从 0 开始。
multiline:布尔值,表示是否设置了 m 标记。
dotAll:布尔值,表示是否设置了 s 标记。
source:正则表达式的字面量字符串(不是传给构造函数的模式字符串),没有开头和结尾的 斜杠。
flags:正则表达式的标记字符串。始终以字面量而非传入构造函数的字符串模式形式返回(没 有前后斜杠)。
let pattern1 = /\[bc\]at/i;
console.log(pattern1.global); // false
console.log(pattern1.ignoreCase); // true
console.log(pattern1.multiline); // false
console.log(pattern1.lastIndex); // 0
console.log(pattern1.source); // "\[bc\]at"
console.log(pattern1.flags); // "i"
5.2.2 RegExp实例方法
RegExp 实例的主要方法是 exec(),主要用于配合捕获组使用。这个方法只接收一个参数,即要应 用模式的字符串。如果找到了匹配项,则返回包含第一个匹配信息的数组;如果没找到匹配项,则返回 null。
let text = "cat, bat, sat, fat";
let pattern = /.at/;
let matches = pattern.exec(text);
console.log(matches.index); // 0
console.log(matches[0]); // cat
console.log(pattern.lastIndex); // 0
matches = pattern.exec(text);
console.log(matches.index); // 0
console.log(matches[0]); // cat
console.log(pattern.lastIndex); // 0
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
正则表达式的另一个方法是 test(),接收一个字符串参数。如果输入的文本与模式匹配,则参数 返回 true,否则返回 false。这个方法适用于只想测试模式是否匹配,而不需要实际匹配内容的情况。
let text = "000-00-0000";
let pattern = /\d{3}-\d{2}-\d{4}/;
if (pattern.test(text)) {
console.log("The pattern was matched.");
}
5.2.3 RegExp构造函数属性
下表列出了 RegExp 构造函数的属性:
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 构造函数的所有属性都没有任何 Web 标准出处,因此不要在生产环境中使用它们。
5.2.4 模式的局限性
下列特性目前还没有得到 ECMAScript 的支持(想要了解更多信息,可以参考 Regular-Expressions.info 网站):
\A 和\Z 锚(分别匹配字符串的开始和末尾)
联合及交叉类
原子组
x(忽略空格)匹配模式
条件式匹配
正则表达式注释
5.3 原始包装类型
为了方便操作原始值,ECMAScript 提供了 3 种特殊的引用类型:Boolean、Number 和 String。每当用到某个原始值的方法或属性时,后台都会创建一个相应原始包装类型的对象,从而暴露出操作原始值的 各种方法.
// 例:
let s1 = "some text";
let s2 = s1.substring(2);
/**
在这里,s1 是一个包含字符串的变量,它是一个原始值。第二行紧接着在 s1 上调用了 substring()
方法,并把结果保存在 s2 中。我们知道,原始值本身不是对象,因此逻辑上不应该有方法。而实际上
这个例子又确实按照预期运行了。这是因为后台进行了很多处理,从而实现了上述操作
*/
// 可以把这 3 步想象成执行了如下 3 行 ECMAScript 代码:
let s1 = new String("some text"); // 创建一个 String 类型的实例;
let s2 = s1.substring(2); // 调用实例上的特定方法;
s1 = null; // 销毁实例。
引用类型与原始值包装类型的主要区别在于对象的生命周期。在通过 new 实例化引用类型后,得到 的实例会在离开作用域时被销毁,而自动创建的原始值包装对象则只存在于访问它的那行代码执行期 间。这意味着不能在运行时给原始值添加属性和方法。比如下面的例子:
let s1 = "some text";
s1.color = "red";
console.log(s1.color); // undefined
/**
这里的第二行代码尝试给字符串 s1 添加了一个 color 属性。可是,第三行代码访问 color 属性时,
它却不见了。原因就是第二行代码运行时会临时创建一个 String 对象,而当第三行代码执行时,这个对
象已经被销毁了。实际上,第三行代码在这里创建了自己的 String 对象,但这个对象没有 color 属性。
*/
Object 构造函数作为一个工厂方法,能够根据传入值的类型返回相应原始值包装类型的实例:
let obj = new Object("some text");
console.log(obj instanceof String); // true
let obj2 = new Object(123);
console.log(obj2 instanceof Number); // true
let obj3 = new object(true);
console.log(obj3 instanceof Boolean) // true
注意,使用 new 调用原始值包装类型的构造函数,与调用同名的转型函数并不一样:
let value = "25";
let number = Number(value); // 转型函数
console.log(typeof number); // "number"
let obj = new Number(value); // 构造函数
console.log(typeof obj); // "object"
5.3.1 Boolean类型
Boolean 是对应布尔值的引用类型。要创建一个 Boolean 对象,就使用 Boolean 构造函数并传入 true 或 false,如下例所示:
Boolean 的实例会重写 valueOf()方法,返回一个原始值 true 或 false。toString()方法被调 用时也会被覆盖,返回字符串"true"或"false".
包装类型Boolean与原始值Boolean的区别:
let falseObject = new Boolean(false);
let result = falseObject && true;
console.log(result); // true
let falseValue = false;
result = falseValue && true;
console.log(result); // false
/ **
// 所有对象在布尔表达式中都会自动转换为 true,因此 falseObject 在这个表达式里实际上表示一个 true 值。
*/
typeof 操作符对原始值返回 "boolean",但对引用值返回"object"。同样,Boolean 对象是 Boolean 类型的实例,在使用 instaceof 操作符时返回 true,但对原始值则返回 false,如下所示:
console.log(typeof falseObject); // object
console.log(typeof falseValue); // boolean
console.log(falseObject instanceof Boolean); // true
console.log(falseValue instanceof Boolean); // false
注:理解原始布尔值和 Boolean 对象之间的区别非常重要,强烈建议永远不要使用后者。
5.3.2 Number类型
Number 是对应数值的引用类型。要创建一个 Number 对象,就使用 Number 构造函数并传入一个 数值,如下例所示:
与 Boolean 类型一样,Number 类型重写了 valueOf()、toLocaleString()和 toString()方 法。valueOf()方法返回 Number 对象表示的原始数值,另外两个方法返回数值字符串。toString() 方法可选地接收一个表示基数的参数,并返回相应基数形式的数值字符串:
let num = 10;
console.log(num.toString()); // "10"
console.log(num.toString(2)); // "1010"
console.log(num.toString(8)); // "12"
console.log(num.toString(10)); // "10"
console.log(num.toString(16)); // "a"
Number 类型还提供了几个用于将数值格式化为字符串的方法:
toFixed()方法返回包含指定小数点位数的数值字符串,如:
let num = 10;
console.log(num.toFixed(2)); // "10.00"
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"
/**
首先要用 1 位数字表示数值 99,得到"1e+2",也就是 100。因为 99 不能只用 1 位
数字来精确表示,所以这个方法就将它舍入为 100,这样就可以只用 1 位数字(及其科学记数法形式)
来表示了。用 2 位数字表示 99 得到"99",用 3 位数字则是"99.0"。本质上,toPrecision()方法会
根据数值和精度来决定调用 toFixed()还是 toExponential()。
*/
isInteger()方法与安全整数、Number.isInteger()方法,用于辨别一个数值是否保存为整数:
console.log(Number.isInteger(1)); // true
console.log(Number.isInteger(1.00)); // true
console.log(Number.isInteger(1.01)); // false
与Boolean包装类型类似:
let numberObject = new Number(10);
let numberValue = 10;
console.log(typeof numberObject); // "object"
console.log(typeof numberValue); // "number"
console.log(numberObject instanceof Number); // true
console.log(numberValue instanceof Number); // false
5.3.3 String类型
部分方法示例:
// 1. chartAt()
let message = "abcde";
console.log(message.charAt(2)); // "c"
// 2. chartCodeA()
let message = "abcde";
// Unicode "Latin small letter C"的编码是 U+0063
console.log(message.charCodeAt(2)); // 99
// 3. fromCharCode()
console.log(String.fromCharCode(0x61, 0x62, 0x63, 0x64, 0x65)); // "abcde"
部分字符串操作方法示例:
// 1. concat()
let stringValue = "hello ";
let result = stringValue.concat("world");
console.log(result); // "hello world"
console.log(stringValue); // "hello"
// 2. slice()
// substring()
// substr() 第二个参数表示返回的字符个数
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"
// 3. indexOf()
// lastIndexOf()
let stringValue = "hello world";
console.log(stringValue.indexOf("o")); // 4
console.log(stringValue.lastIndexOf("o")); // 7
// 4.startsWith()
// endsWith()
// includes()
let message = "foobarbaz";
console.log(message.startsWith("foo")); // true
console.log(message.startsWith("bar")); // false
console.log(message.endsWith("baz")); // true
console.log(message.endsWith("bar")); // false
console.log(message.includes("bar")); // true
console.log(message.includes("qux")); // false
// 5. trim()
let stringValue = " hello world ";
let trimmedStringValue = stringValue.trim();
console.log(stringValue); // " hello world "
console.log(trimmedStringValue); // "hello world"
// 6. repeat()
let stringValue = "na ";
console.log(stringValue.repeat(16) + "batman");
// na na na na na na na na na na na na na na na na batman
// 7. padStart()
// padEnd()
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......"
5.4 单体内置对象
开发者不用显式地实例化内置对象,因为它们已经实例 化好了。前面我们已经接触了大部分内置对象,包括 Object、Array 和 String。本节介绍 ECMA-262 定义的另外两个单例内置对象:Global 和 Math。
5.4.1 Global对象
Global 对象是 ECMAScript 中最特别的对象,因为代码不会显式地访问它。ECMA-262 规定 Global 对象为一种兜底对象,它所针对的是不属于任何对象的属性和方法。在全局作用域中定义的变量和函数都会变成 Global 对象的属性.包括 isNaN()、isFinite()、parseInt()和 parseFloat(),
Global 对象上还有另外一些方法:
1. URL 编码方法 encodeURI()和 encodeURIComponent()方法用于编码统一资源标识符(URI),以便传给浏览器。有效的 URI 不能包含某些字符,比如空格。使用 URI 编码方法来编码 URI 可以让浏览器能够理解它们, 同时又以特殊的 UTF-8 编码替换掉所有无效字符。
与 encodeURI()和 encodeURIComponent()相对的是 decodeURI()和 decodeURIComponent()。 decodeURI()只对使用 encodeURI()编码过的字符解码。
2. eval()方法
这个方法就是一个完 整的 ECMAScript 解释器,它接收一个参数,即一个要执行的 ECMAScript(JavaScript)字符串。
eval("console.log('hi')");
// 上面这行代码的功能与下一行等价:
console.log("hi");
let msg = "hello world";
eval("console.log(msg)"); // "hello world"
eval("function sayHi() { console.log('hi'); }");
sayHi();
// 通过 eval()定义的任何变量和函数都不会被提升,这是因为在解析代码的时候,它们是被包含在
一个字符串中的。它们只是在 eval()执行的时候才会被创建。
eval("let msg = 'hello world';");
console.log(msg); // Reference Error: msg is not defined
3. Global 对象属性
4. window 对象
虽然 ECMA-262 没有规定直接访问 Global 对象的方式,但浏览器将 window 对象实现为 Global 对象的代理。因此,所有全局作用域中声明的变量和函数都变成了 window 的属性.
var color = "red";
function sayColor() {
console.log(window.color);
}
window.sayColor(); // "red"
/**
这里定义了一个名为color的全局变量和一个名为sayColor()的全局函数。在sayColor()内部,
通过 window.color 访问了 color 变量,说明全局变量变成了 window 的属性。接着,又通过 window
对象直接调用了 window.sayColor()函数,从而输出字符串。
*/
获取globel对象的方法:
let global = function() {
return this;
}();
5.4.2 Math对象
1. Math 对象属性
Math 对象有一些属性,主要用于保存数学中的一些特殊值:
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(...val);
3. 舍入方法
接下来是用于把小数值舍入为整数的 4 个方法:Math.ceil()、Math.floor()、Math.round() 和 Math.fround():
// Math.ceil()方法始终向上舍入为最接近的整数。
console.log(Math.ceil(25.9)); // 26
console.log(Math.ceil(25.5)); // 26
console.log(Math.ceil(25.1)); // 26
// Math.round()方法执行四舍五入
console.log(Math.round(25.9)); // 26
console.log(Math.round(25.5)); // 26
console.log(Math.round(25.1)); // 25
// Math.fround()方法返回数值最接近的单精度(32 位)浮点值表示
console.log(Math.fround(0.4)); // 0.4000000059604645
console.log(Math.fround(0.5)); // 0.5
console.log(Math.fround(25.9)); // 25.899999618530273
// Math.floor()方法始终向下舍入为最接近的整数。
console.log(Math.floor(25.9)); // 25
console.log(Math.floor(25.5)); // 25
console.log(Math.floor(25.1)); // 25
4. random()方法
Math.random()方法返回一个 0~1 范围内的随机数,其中包含 0 但不包含 1。
如果想从 1~10 范围内随机选择一个数:
let num = Math.floor(Math.random() * 10 + 1);
注: Math.random()方法在这里出于演示目的是没有问题的。如果是为了加密而需要 生成随机数(传给生成器的输入需要较高的不确定性),那么建议使用 window.crypto. getRandomValues()。
5. 其他方法
略
5.5 小结
JavaScript 中的对象称为引用值,几种内置的引用类型可用于创建特定类型的对象。
- 引用值与传统面向对象编程语言中的类相似,但实现不同。
- Date 类型提供关于日期和时间的信息,包括当前日期、时间及相关计算。
- RegExp 类型是 ECMAScript 支持正则表达式的接口,提供了大多数基础的和部分高级的正则表 达式功能。
JavaScript 比较独特的一点是,函数实际上是 Function 类型的实例,也就是说函数也是对象。因 为函数也是对象,所以函数也有方法,可以用于增强其能力。 由于原始值包装类型的存在,JavaScript 中的原始值可以被当成对象来使用。有 3 种原始值包装类 型:Boolean、Number 和 String。它们都具备如下特点。
- 每种包装类型都映射到同名的原始类型。
- 以读模式访问原始值时,后台会实例化一个原始值包装类型的对象,借助这个对象可以操作相 应的数据。
- 涉及原始值的语句执行完毕后,包装对象就会被销毁。
当代码开始执行时,全局上下文中会存在两个内置对象:Global 和 Math。其中,Global 对象在 大多数 ECMAScript 实现中无法直接访问。不过,浏览器将其实现为 window 对象。所有全局变量和函数都是 Global 对象的属性。Math 对象包含辅助完成复杂计算的属性和方法。