【JS红宝书学习笔记】第5章 基本引用类型

第5章 基本引用类型

引用值(或者对象)是某个特定引用类型的实例。引用类型是把数据和功能组织到一起的结构,经常被人错误地称作“类”。引用类型有时候也被称为对象定义,因为它们描述了自己的对象应有的属性和方法。
对象被认为是某个特定引用类型的实例。新对象通过使用 new 操作符后跟一个构造函数(constructor)来创建。构造函数就是用来创建新对象的函数,比如下面这行代码:

let now = new Date();

这行代码创建了引用类型 Date 的一个新实例,并将它保存在变量 now 中。Date()在这里就是构造函数,它负责创建一个只有默认属性和方法的简单对象。ECMAScript 提供了很多像 Date 这样的原生引用类型,帮助开发者实现常见的任务。

1. 日期

要创建日期对象,就使用 new 操作符来调用 Date 构造函数:

let now = new Date();

在这里插入图片描述

要创建一个表示“2019 年 5 月 23 日”的日期对象,可以使用以下代码:

let someDate = new Date(Date.parse("May 23, 2019"));

let someDate = new Date("May 23, 2019");

计算代码运行时间

// 起始时间
let start = Date.now();
// 调用函数
doSomething();
// 结束时间
let stop = Date.now(),
result = stop - start;

2. RegExp正则表达式

let expression = /pattern/flags;

其中,pattern是模式,flags是标记
表示匹配模式的标记。

  • g:全局模式,表示查找字符串的全部内容,而不是找到第一个匹配的内容就结束。(global)
  • i:不区分大小写,表示在查找匹配时忽略 pattern 和字符串的大小写。 (ignoreCase)
  • m:多行模式,表示查找到一行文本末尾时会继续查找。(multiline)
  • 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;

正则表达式也可以使用 RegExp 构造函数来创建,它接收两个参数:模式字符串和(可选的)标记字符串。任何使用字面量定义的正则表达式也可以通过构造函数来创建,比如:

// 匹配第一个"bat"或"cat",忽略大小写
let pattern1 = /[bc]at/i;
// 跟 pattern1 一样,只不过是用构造函数创建的
let pattern2 = new RegExp("[bc]at", "i");

在这里插入图片描述
RegExp 实例属性
在这里插入图片描述

let pattern1 = /\[bc\]at/i;
console.log(pattern1.global); // false
console.log(pattern1.ignoreCase); // true

RegExp 实例方法
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); // 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"

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
}

模式局限
在这里插入图片描述

3. 原始值包装类型

为了方便操作原始值,ECMAScript 提供了 3 种特殊的引用类型:Boolean、Number 和 String。
引用类型与原始值包装类型的主要区别在于对象的生命周期。在通过 new 实例化引用类型后,得到的实例会在离开作用域时被销毁,而自动创建的原始值包装对象则只存在于访问它的那行代码执行期间。这意味着不能在运行时给原始值添加属性和方法。

let s1 = "some text";
s1.color = "red";
console.log(s1.color); // undefined(不具有可变性)

PS:一些疑惑
引用类型不是指对象吗?为什么ECMAScript 提供了 3 种特殊的引用类型:Boolean、Number 和 String。这些不是原始值吗?
在 JavaScript 中,引用类型通常指的是对象、数组、函数等复杂数据类型,这些类型的值在内存中是以引用的形式存在的。这意味着变量存储的是对实际对象的引用,而不是对象本身。引用类型具有属性和方法,可以通过这些属性和方法对对象进行操作。
至于 ECMAScript 提供的特殊的引用类型 Boolean、Number 和 String,它们实际上属于原始值(primitive values)而不是引用类型。这是因为在 JavaScript 中,布尔值、数字和字符串都被视为原始值,它们不是对象,没有属性和方法,也不是通过引用来访问的。
然而,尽管 Boolean、Number 和 String 是原始值, 但在特定情况下,它们可以被看作是对象。这是因为 JavaScript 在访问原始值时会暂时创建一个对应的包装对象,这些包装对象具有对象的特性,可以调用一些操作符。 例如:

let str = "Hello";
console.log(str.length); // 5 这里实际上创建了一个对应的 String 对象来获取字符串的长度

虽然这些包装对象具有对象的特性,但它们并不是真正意义上的引用类型,因为它们是临时创建的,而且并不具备引用类型的其他特性(比如可变性)。
因此,尽管 ECMAScript 提供了 Boolean、Number 和 String 作为特殊的引用类型,但它们实际上属于原始值,而不是引用类型。

(1)Boolean

Boolean 是对应布尔值的引用类型。要创建一个 Boolean 对象,就使用 Boolean 构造函数并传入 true 或 false。

let booleanObject = new Boolean(true);

所有对象在布尔表达式中都会自动转换为 true,因此 falseObject 在这个表达式里实际上表示一个 true 值。

let falseObject = new Boolean(false);
let result = falseObject && true;
console.log(result); // true
let falseValue = false;
result = falseValue && true;
console.log(result); // false
console.log(typeof falseObject); // object
console.log(typeof falseValue); // boolean
console.log(falseObject instanceof Boolean); // true
console.log(falseValue instanceof Boolean); // false

(2)Number

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

let numberObject = new Number(10);
  • 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()。为了以正确的小数位精确表示数值,这 3 个方法都会向上或向下舍入。

isInteger()方法与安全整数,用于辨别一个数值是否保存为整数。有时候,小数位的 0 可能会让人误以为数值是一个浮点值

console.log(Number.isInteger(1)); // true
console.log(Number.isInteger(1.00)); // true
console.log(Number.isInteger(1.01)); // false

(3)String

String 是对应字符串的引用类型。要创建一个 String 对象,使用 String 构造函数并传入一个数值,如下例所示:

let stringObject = new String("hello world");
console.log(stringObject.length); // "11"

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

let stringValue = "hello world";
console.log(stringValue.length); // "11"

charAt()方法返回给定索引位置的字符,由传给方法的整数参数指定。

let message = "abcde";
console.log(message.charAt(2)); // "c"

字符串操作方法:

  • concat(),用于将一个或多个字符串拼接成一个新字符串
let stringValue = "hello ";
let result = stringValue.concat("world");
console.log(result); // "hello world"
console.log(stringValue); // "hello"
  • 3 个从字符串中提取子字符串的方法:slice()、substr()和 substring()

第一个参数表示子字符串开始的位置,第二个参数表示子字符串结束的位置 (左闭右开)。对 slice()和 substring()而言,第二个参数是提取结束的位置(即该位置之前的字符会被提取出来)。**对 substr()而言,第二个参数表示返回的子字符串数量。**任何情况下,省略第二个参数都意味着提取到字符串末尾。与 concat()方法一样,slice()、substr()和 substring()也不会修改调用它们的字符串,而只会返回提取到的原始新字符串值。

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 个方法的行为又有不同。比如,slice()方法将所有负值参数都当成字符串长度加上负参数值。
而 substr()方法将第一个负参数值当成字符串长度加上该值,将第二个负参数值转换为 0。
substring()方法会将所有负参数值都转换为 0。

let stringValue = "hello world";
console.log(stringValue.slice(-3)); // "rld"
// substring()方法会将所有负参数值都转换为 0
console.log(stringValue.substring(-3)); // "hello world"
console.log(stringValue.substr(-3)); // "rld"
console.log(stringValue.slice(3, -4)); // "lo w"
console.log(stringValue.substring(3, -4)); // "hel"
console.log(stringValue.substr(3, -4)); // "" (empty string)

字符串位置方法:
indexOf()和 lastIndexOf()。这两个方法从字符串中搜索传入的字符串,并返回位置(如果没找到,则返回-1)。两者的区别在于,indexOf()方法从字符串开头开始查找子字符串,而 lastIndexOf()方法从字符串末尾开始查找子字符串。

// 序号从0开始
let stringValue = "hello world";
console.log(stringValue.indexOf("o")); // 4
console.log(stringValue.lastIndexOf("o")); // 7

这两个方法都可以接收可选的第二个参数,表示开始搜索的位置。这意味着,indexOf()会从这个参数指定的位置开始向字符串末尾搜索,忽略该位置之前的字符;lastIndexOf()则会从这个参数指定的位置开始向字符串开头搜索,忽略该位置之后直到字符串末尾的字符。

let stringValue = "hello world";
console.log(stringValue.indexOf("o", 6)); // 7
console.log(stringValue.lastIndexOf("o", 6)); // 4

像这样使用第二个参数并循环调 indexOf()或 lastIndexOf(),就可以在字符串中找到所有的目标子字符串

let stringValue = "Lorem ipsum dolor sit amet, consectetur adipisicing elit";
let positions = new Array();
let pos = stringValue.indexOf("e");

while(pos > -1) {
	positions.push(pos);
	pos = stringValue.indexOf("e", pos + 1);
}
console.log(positions); // [3,24,32,35,52]

字符串包含方法:
3 个用于判断字符串中是否包含另一个字符串的方法:startsWith()、endsWith()和 includes()。它们的区别在于,startsWith()检查开始于索引 0 的匹配项,endsWith()检查开始于索引(string.length - substring.length)的匹配项,而 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

startsWith()和 includes()方法接收可选的第二个参数,表示开始搜索的位置。如果传入第二个参数,则意味着这两个方法会从指定位置向着字符串末尾搜索,忽略该位置之前的所有字符。
endsWith()方法接收可选的第二个参数,表示应该当作字符串末尾的位置。如果不提供这个参数,那么默认就是字符串长度。如果提供这个参数,那么就好像字符串只有那么多字符一样

let message = "foobarbaz";
console.log(message.startsWith("foo")); // true
console.log(message.startsWith("foo", 1)); // false
console.log(message.includes("bar")); // true
console.log(message.includes("bar", 4)); // false

console.log(message.endsWith("bar")); // false
console.log(message.endsWith("bar", 6)); // true

trim()方法
这个方法会创建字符串的一个副本,删除前、后所有空格符,再返回结果。由于 trim()返回的是字符串的副本,因此原始字符串不受影响,即原本的前、后空格符都会保留。另外,trimLeft()和 trimRight()方法分别用于从字符串开始和末尾清理空格符。

let stringValue = " hello world ";
let trimmedStringValue = stringValue.trim();
console.log(stringValue); // " hello world "
console.log(trimmedStringValue); // "hello world"

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

padStart()和 padEnd()方法
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"

字符串迭代与解构
在 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"]

字符串大小写转换
toLocaleLowerCase()和 toLocaleUpperCase()方法旨在基于特定地区实现。在很多地区,地区特定的方法与通用的方法是一样的。但在少数语言中(如土耳其语),Unicode 大小写转换需应用特殊规则,要使用地区特定的方法才能实现正确转换。

let stringValue = "hello world";
console.log(stringValue.toLocaleUpperCase()); // "HELLO WORLD"
console.log(stringValue.toUpperCase()); // "HELLO WORLD"
console.log(stringValue.toLocaleLowerCase()); // "hello world"
console.log(stringValue.toLowerCase()); // "hello world"

字符串模式匹配方法
match()方法

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()方法
这个方法返回模式第一个匹配的位置索引,如果没找到则返回-1。

let text = "cat, bat, sat, fat";
let pos = text.search(/at/);
console.log(pos); // 1

字符串替换 replace()方法:两个参数,第一个参数是字符串,第二个参数可以是字符串,可以是函数

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"

在这里插入图片描述

拆分字符串 split()
作为分隔符的参数可以是字符串,也可以是 RegExp 对象。(字符串分隔符不会被这个方法当成正则表达式。)还可以传入第二个参数,即数组大小,确保返回的数组不会超过指定大小。

let colorText = "red,blue,green,yellow";
let colors1 = colorText.split(","); // ["red", "blue", "green", "yellow"]
let colors2 = colorText.split(",", 2); // ["red", "blue"]
let colors3 = colorText.split(/[^,]+/); // ["", ",", ",", ",", ""]

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

4. 单例内置对象

(1)Global

在全局作用域中定义的变量和函数都会变成 Global 对象的属性 。

  • URL 编码方法
    encodeURI() 和 encodeURIComponent() 方法用于编码统一资源标识符(URI),以便传给浏览器。有效的 URI 不能包含某些字符,比如空格。使用 URI 编码方法来编码 URI 可以让浏览器能够理解它们,同时又以特殊的 UTF-8 编码替换掉所有无效字符。
  • eval() 方法
    这个方法就是一个完整的 ECMAScript 解释器,它接收一个参数,即一个要执行的ECMAScript(JavaScript)字符串。
eval("console.log('hi')");
// 上面这行代码的功能与下一行等价:
console.log("hi");
let msg = "hello world";
eval("console.log(msg)"); // "hello world"

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

eval("function sayHi() { console.log('hi'); }");
sayHi();

通过 eval()定义的任何变量和函数都不会被提升,这是因为在解析代码的时候,它们是被包含在一个字符串中的。它们只是在 eval()执行的时候才会被创建。

eval("let msg = 'hello world';");
console.log(msg); // Reference Error: msg is not defined
  • Global 对象属性
    在这里插入图片描述
    在这里插入图片描述
  • 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;
}();

(2)Math

  • Math 对象属性
    在这里插入图片描述
  • 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); // 8
  • 舍入方法
    Math.ceil()方法始终向上舍入为最接近的整数。
    Math.floor()方法始终向下舍入为最接近的整数。
    Math.round()方法执行四舍五入。
    Math.fround()方法返回数值最接近的单精度(32 位)浮点值表示。
console.log(Math.ceil(25.1)); // 26
console.log(Math.round(25.5)); // 26
console.log(Math.fround(0.4)); // 0.4000000059604645
console.log(Math.fround(0.5)); // 0.5
console.log(Math.fround(25.9)); // 25.899999618530273
console.log(Math.floor(25.9)); // 25
  • random()方法
    Math.random()方法返回一个 0~1 范围内的随机数,其中包含 0 但不包含 1。
    可以基于如下公式使用 Math.random()从一组整数中随机选择一个数:
number = Math.floor(Math.random() * total_number_of_choices + first_possible_value)
// 从 1~10 范围内随机选择一个数
let num = Math.floor(Math.random() * 10 + 1);
// 选择一个 2~10 范围内的值
let num = Math.floor(Math.random() * 9 + 2);
  • 其他方法
    在这里插入图片描述
  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值