一、初识
1.1 基础
1.1.1 语言速成课
1.1.1.1 变量
变量是存储值的容器。首先用let关键字声明一个变量,后面跟着你给变量的名字
变量命名区分大小写
分号在JavaScript中是用来分隔语句的,但是如果语句后面有一个换行符(或者在{block}中只有一个语句),分号可以省略。
1.1.1.2 注释
多行注释:/*comments*/
单行注释:/comments/
1.1.1.3 运算符
运算符是一种基于两个值(或变量)产生结果的数学符号
1.1.1.4 条件语句
条件是用于测试表达式是否返回真值的代码结构
1.1.1.5 函数
函数是对希望重用的功能进行打包的一种方式
1.1.1.6 事件
网站上真正的交互性需要事件处理程序。这些代码结构监听浏览器中的活动,并在响应中运行代码
1.2 第一步
1.2.1 什么是javaScript
JavaScript是一种脚本或编程语言,它允许你在网页上实现复杂的功能
JavaScript是一种轻量级的解释性编程语言
1.2.2 第一次使用js
1.2.3 故障排除
1.2.4 变量
1.2.4.1 变量声明
声明变量let myName;
在JavaScript中,所有代码指令都应该以分号(;)结束——你的代码可能在单行中正常工作,但当你一起编写多行代码时可能就不行了
不要混淆存在但没有定义值的变量和根本不存在的变量。不存在意味着没有盒子,没有定义值意味着有一个盒子,但是里面没有值
1.2.4.2 初始化变量
初始化变量myName = "Chris";
也可以声明和初始化同时进行let myDog = "Rover";
1.2.4.3 关于var的说明
var会变量提升,let不会提升
myVar = 9;
console.log(myVar);
var myVar;//使用var正常工作
let myVar;//使用let报错(推荐方式)
var可以重复声明,let不会
var myVar;//可以重复声明
var myVar;//使用var正常工作let myVar;//不可重复声明
let myVar;//使用let报错(推荐方式)
1.2.4.4 常量
常量声明时必须初始化
初始化后不能赋新值
虽然常量不能更改,但是如果常量类型是对象的话,对象的内容是可以更改的。
使用const还是let:声明变量时对其进行初始化,并且以后不需要对其重新赋值,则将其设置为常量
1.2.5 数值和操作符
**操作符为指数操作符
JavaScript中的运算符优先级和学校数学课上教的一样——乘和除总是先做,然后是加和减(计算总是从左到右计算)。
==和!=
测试值是否相同,但不测试值的数据类型是否相同。===和!==
是严格版本,测试值及其数据类型是否相等。严格的版本往往会导致更少的错误,因此我们建议您使用它们
1.2.6 字符串
在JavaScript中,您可以选择单引号(‘)、双引号(")或反引号(’)来包装字符串
使用反引号声明的字符串是一种特殊类型的字符串,称为模板字面量。在大多数情况下,模板字面值就像普通的字符串,但是它们有一些特殊的属性:
- 你可以在其中嵌入JavaScript
- 可以在多行上声明模板字面量
在模板字面量中,你可以将JavaScript变量或表达式包装在${}中,结果将包含在字符串中
const name = "Chris";
const greeting = `Hello, ${name}`;
console.log(greeting); // "Hello, Chris"
${}只能用于模板字面量,不能用于普通字符串。您可以使用+操作符连接普通字符串
你可以在模板字面量中包含JavaScript表达式,也可以只是变量,结果将包含在结果中
const output = `I like the song ${song}. I gave it a score of ${(score / highestScore) * 100}%.`;
模板字面量尊重源代码中的换行符,所以你可以像这样编写跨多行的字符串
const newline = `One day you finally knew
what you had to do, and began,`;
1.2.7 有用的字符串方法
1.2.8数组
数组itmes执行相同操作使用map()
function double(number) {return number * 2;
}
const numbers = [5, 2, 7, 6];
const doubled = numbers.map(double);
console.log(doubled); // [ 10, 4, 14, 12 ]
数组itmes过滤使用filter()
function isLong(city) {return city.length > 8;
}
const cities = ["London", "Liverpool", "Totnes", "Edinburgh"];
const longer = cities.filter(isLong);
console.log(longer); // [ "Liverpool", "Edinburgh" ]
1.3 构建块
1.3.1 条件
1.3.2 循环
1.3.3 函数
与函数声明不同,函数表达式不会被提升。
1.3.4 自定义函数
1.3.5 函数返回值
1.3.6 事件介绍
1.3.6.1 阻止事件默认行为
e.preventDefault();
1.3.6.2 阻止事件冒泡
event.stopPropagation();
currentTarget
和 target
是 JavaScript 事件对象的两个属性,它们的区别在于它们分别引用的元素。
event.target
:这个属性返回的是触发事件的元素,也就是真正的事件源头。例如,如果你在一个按钮上点击,那么event.target
就会返回那个按钮元素。event.currentTarget
:这个属性返回的是事件侦听器绑定的元素。例如,如果你在一个 div 元素上绑定了点击事件,然后在这个 div 的一个按钮上点击,那么event.target
会返回按钮元素,但是event.currentTarget
会返回 div 元素。
在大多数情况下,event.target
和 event.currentTarget
指向的是同一个元素,但是在事件冒泡或者事件捕获的情况下,它们可能会指向不同的元素。例如,如果你在父元素上绑定了一个事件监听器,然后在它的一个子元素上触发了那个事件,这个时候 event.target
就会返回子元素,而 event.currentTarget
会返回父元素。
开启捕获:addEventListener的第三个参数{ capture: true }
。捕获和冒泡的顺序相反
为什么要同时捕获和冒泡呢?在糟糕的过去,浏览器的交叉兼容性远不如现在,Netscape只使用事件捕获,Internet Explorer只使用事件冒泡。当W3C决定尝试标准化行为并达成共识时,他们最终采用了包含这两者的系统,
1.3.6.3 事件委托
将子元素的单击事件委托给父元素。
父元素绑定事件,使用事件对象的target属性,来获得单击的目标子元素
1.3.7 图片库
1.4 对象介绍
1.4.1 对象基础
对象字面量
const person = {name: ["Bob", "Smith"],age: 32,bio() {//bio: function ()简写console.log(`${this.name[0]} ${this.name[1]} is ${this.age} years old.`);},
};
对象访问,点表示法通常比括号表示法更受欢迎,因为它更简洁,更容易阅读。然而,在某些情况下,你必须使用方括号。例如,如果对象属性名称保存在变量中,则不能使用点表示法访问该值,但可以使用括号表示法访问该值。
例如:
const person = {name: ["Bob", "Smith"],age: 32,
};function logProperty(propertyName) {console.log(person[propertyName]);//此处不能使用点
}logProperty("name");
// ["Bob", "Smith"]
logProperty("age");
// 32
1.4.2 对象原型
1.4.2.1 介绍
javaScript中的每个对象都有一个内置属性,称为原型。原型本身就是一个对象,所以原型会有自己的原型,形成所谓的原型链。当我们到达一个自己的原型为null的原型时,这个链条就结束了
原型的原型一般不叫prototype,它的名字不标准,但实际上所有浏览器都使用__proto__
,访问对象原型的标准方法是Object . getPrototypeof(对象)方法。
当你试图访问对象的属性时:如果在对象本身中找不到该属性,则在原型中搜索该属性。如果仍然找不到属性,则搜索原型的原型,以此类推,直到找到属性或到达链的末端,在这种情况下返回undefined。
Object.prototype原型,是Object的原型,他的值是null,它是所有原型的末端
1.4.2.2 设置原型的方法
设置原型的方法:这里我们将介绍两种:object .create()和构造函数。
//
const personPrototype = {greet() {console.log("hello!");},
};const carl = Object.create(personPrototype);//使用object.create()创建一个以personPrototype为原型的新对象
carl.greet(); // hello!
构造函数:
const personPrototype = {greet() {console.log(`hello, my name is ${this.name}!`);},
};function Person(name) {this.name = name;
}Object.assign(Person.prototype, personPrototype);
// or
// Person.prototype.greet = personPrototype.greet;
你可以使用静态object . hasown(对象,属性名)方法检查一个属性是否为自己的属性
1.4.3 面向对象编程
多态:子类实现父类的方法就叫多态
封装:对象的属性只能被自己的方法访问,对外只暴露公共接口
1.4.4 类
1.4.4.1 类和构造函数
这里描述的功能并不是一种组合对象的新方式:在底层,它们仍然使用原型。它们只是一种使建立原型链更容易的方法。
class Person {name;constructor(name) {//如果你不需要做任何特殊的初始化,你可以省略构造函数,它会为你生成一个默认构造函数this.name = name;}introduceSelf() {console.log(`Hi! I'm ${this.name}`);}
}const giles = new Person("Giles");
1.4.4.2 继承
class Professor extends Person {teaches;constructor(name, teaches) {super(name);//使用super()调用父类构造函数this.teaches = teaches;}introduceSelf() {console.log(`My name is ${this.name}, and I will be your ${this.teaches} professor.`,);}grade(paper) {const grade = Math.floor(Math.random() * (5 - 1) + 1);console.log(grade);}
}
1.4.4.3 封装
私有数据属性:必须在类声明中声明,其名称以#开头。
私有方法:它们的名称以#开头,并且只能由对象自己的方法调用:
1.4.5 使用JSON
- parse():接受JSON字符串作为参数,并返回相应的JavaScript对象
- stringify():接受一个对象作为参数,并返回等效的JSON字符串。
1.4.6 对象构建练习
requestAnimationFrame()函数:
该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。回调函数执行次数通常是每秒 60 次.
若你想在浏览器下次重绘之前继续更新下一帧动画,那么回调函数自身必须再次调用 requestAnimationFrame()
。requestAnimationFrame()
是一次性的。
function loop() {requestAnimationFrame(loop);console.log('执行循环');
}
loop();
1.4.7 添加功能到我们的弹跳球演示
二、指南
2.1 语法和类型
2.1.1 基础
如果语句单独写在一行中,则不需要在语句后面加上分号。但是,如果一行中需要多个语句,则它们必须用分号分隔
2.1.2 注释
注释不能嵌套
/*这是/*dfdf*/注释*//*这是/*dfdf*\/注释*/
2.1.3 声明
2.1.3.1 变量声明
- var,声明一个变量,可选地将其初始化为一个值。
- let,声明一个块作用域的局部变量,可选地将其初始化为一个值。
- const,声明一个块作用域的只读命名常量。
const { bar } = foo
,这将创建一个名为bar的变量,并将对应于对象foo中同名键的值赋给它
变量应该在使用之前声明。JavaScript过去允许对未声明的变量赋值,这会创建一个未声明的全局变量。这是严格模式下的错误,应该完全避免。
如果在没有初始化式的情况下声明变量,则为其赋值undefined
const声明总是需要一个初始化值,因为它们禁止在声明之后进行任何类型的赋值
2.1.3.2 变量作用域
- 全局作用域,在脚本模式下运行的所有代码的默认作用域
- 模块作用域,以模块模式运行的代码的作用域
- 函数作用域,用函数创建的作用域。
- 块作用域,用一对花括号(一个块)创建的作用域。(使用let或const声明的变量)
用var创建的变量不是块作用域,而只是块所在函数的局部作用域(或全局作用域)。
2.1.3.3 变量的提升
var声明的变量是提升的,这意味着您可以在其作用域中的任何地方引用该变量,即使尚未到达其声明。
var声明被“提升”到它的函数或全局作用域的顶部
//注意是,声明被提升,而赋值没有被提升
console.log(x === undefined); // true
var x = 3;
//等价于
var x;
console.log(x === undefined); // true
x = 3;
2.1.3.4 全局变量
全局变量实际上是全局对象window的属性
在不同的JavaScript运行环境中,全局对象被命名为不同的名称。在浏览器环境中是window
,在Node.js环境中是global
,在Web Worker中是self
。然而,这种情况在JavaScript中引入globalThis
后得到了统一,无论在哪种环境下,globalThis
都被定义为全局对象。
2.1.3.5 常量
对象常量的属性是可以更改的
数组常量的数组元素可以更改的
2.1.4 数据结构和类型
2.1.4.1 数据类型
七种基本数据类型:
- Boolean,true和false
- null,表示空值的特殊关键字
- undefined,值没有被定义的顶级属性
- Number,整型或浮点数值
- BigInt,任意精度的整型
- String,字符序列,表示文本值
- Symbol,实例是唯一的且不可变的
一种非基本类型
- Object,
console.log(typeof null); // "object"
,这被认为是一个错误,但是一个无法修复的错误,因为它会破坏太多的脚本
2.1.4.2 数据类型转换
JavaScript是一种动态类型语言。这意味着在声明变量时不必指定它的数据类型。这还意味着在脚本执行期间,数据类型会根据需要自动转换
//不会导致错误
let answer = 42;
answer = "Thanks for all the fish!";
2.1.4.3 数值和‘+’操作符
z = "37" + 7; // "377"
"37" - 7; // 30
"37" * 7; // 259
2.1.4.4 转换字符串为数值
console.log(parseInt('123'));// 123 (默认基本进制是10)
console.log(parseInt('123', 10));// 123 (明确指定进制)
console.log(parseInt(' 123 '));// 123 (空格被忽略)
console.log(parseInt('077'));// 77 (前置0被忽略)
console.log(parseInt('1.9'));// 1 (小数后被忽略)
console.log(parseInt('ff', 16));// 255 (小写16进制)
console.log(parseInt('0xFF', 16));// 255 (前缀位0x的大写16进制)
console.log(parseInt('xyz'));// NaN (输入不能转换为整数)console.log(parseFloat('1.23'));// 1.23
console.log(parseFloat('1.23aaa'));// 1.23(+"1.1") + (+"1.1"); // 2.2
2.1.5 字面量
2.1.5.1 数组字面量
const coffees = ["French Roast", "Colombian", "Kona"];//当使用Array.prototype.map等数组遍历方法时。映射时,空槽被跳过。但是,访问索引的fish[1]仍然返回undefined
const fish = ["Lion", , "Angel"];//['Lion', empty, 'Angel']const myList = ["home", , "school"];//length=3
const myList = [, "home", , "school"];//length=4
const myList = ["home", , "school", ,];//length=4,最后逗号忽略//但是,在编写自己的代码时,应该显式地将缺失的元素声明为未定义,或者至少插入注释以突出其缺失。这样做可以提高代码的清晰度和可维护性
const myList = ["home", /* empty */, "school", /* empty */, ];
2.1.5.2 布尔字面量
//布尔类型有两个字面量值:true和false
2.1.5.3 数值字面量
0, 117, 123456789123456789n //十进制
015, 0001, 0o777777777777n //八进制
0x1123, 0x00111, 0x123456789ABCDEFn //十六进制
0b11, 0b0011, 0b11101001010101010101n //二进制
//整数字面值后面的n后缀表示BigInt字面值//浮点字面量[digits].[digits][(E|e)[(+|-)]digits]
3.1415926
.123456789
3.1E+12
.1e-23
2.1.5.4 对象字面量
const car = { myCar: 字符串, getCar: 函数, special: 变量 };//对象属性名可以是任意字符串,包括空字符串。如何属性名不是合法的js修饰符和数字,它必须被加引号。并且调用时只能使用方括号
const unusualPropertyNames = {'': 'An empty string','!': 'Bang!'
}
console.log(unusualPropertyNames.''); // SyntaxError: Unexpected string
console.log(unusualPropertyNames.!); // SyntaxError: Unexpected token !
console.log(unusualPropertyNames[""]); // An empty string
console.log(unusualPropertyNames["!"]); // Bang!//增强对象字面量
const obj = {// __proto__设置原型__proto__: theProtoObj,// 简写'handler: handler'handler,// 方法toString() {// 调用父级return "d " + super.toString();},// 计算 (动态) 属性名["prop_" + (() => 42)()]: 42,
};
2.1.5.5 正则表达式字面量
//是用斜杠括起来的模板
const re = /ab+c/;
2.1.5.6 字符串字面量
除非特别需要使用string对象,否则应该使用字符串字面值
//字符串字面值是用双引号(")或单引号(')括起来的零个或多个字符
'foo'
"bar"//模板字符串
`In JavaScript '\n' is a line-feed.`
const name = 'Lev', time = 'today';
`Hello ${name}, how are you ${time}?`//插值语法
var s = "say"
console.log(`你是说${s}`)//你是说say
console.log(`你是说%o`,s)//你是说'say'//字符串多行显示使用反斜杠写在行末尾
2.2 控制流和错误处理
2.2.1 块语句
最基本的语句是块语句,用于对语句进行分组。该块由一对花括号分隔
{statement1;statement2;// …statementN;
}
块语句通常与控制流语句(if, for, while)一起使用。
{ x++; }
是块语句
注意:var声明的变量不是块作用域,而是包含函数或脚本的作用域,设置它们的效果在块本身之外持续存在。
2.2.2 条件语句
JavaScript支持两个条件语句:if…Else和switch
2.2.2.1 if语句
如下值被认定为false:
- false
- undefined
- null
- 0
- NaN
- 空字符串
const b = new Boolean(false);
if (b) {// b是对象,所以一直为true
}
if (b == true) {// b的值是否等于true,不等于
}
2.2.2.2 switch语句
switch语句允许程序对表达式求值,并尝试将表达式的值与case标号匹配。如果找到匹配项,则程序执行相关语句
2.2.3 异常处理语句
你可以使用throw语句抛出异常,并使用try…catch语句处理异常
2.2.3.1 异常类型
- ECMAScript exceptions
- DOMException
2.2.3.2 throw语句
throw "Error2"; // String type
throw 42; // Number type
throw true; // Boolean type
throw {toString() {return "I'm an object!";},
};
2.2.3.3 try…catch 语句
try {throw new Error("my")
} catch (error) {console.error(error)
}finally{console.log('始终需要执行的代码')
}
finally块中若有返回值,这个值将是整个try…catch…finally(放在函数里)的返回值,不管try和catch中是否有返回值,该返回值也会覆盖catch中抛出的异常
function f() {try {throw "bogus";} catch (e) {return true;} finally {return false; // 函数返回false}
}
2.2.3.4 利用错误对象
根据错误的类型,您可以使用name和message属性来获得更精细的消息。
message通常提供比将错误对象转换为字符串得到的消息更简洁的消息
2.3 循环和迭代
循环中,单个语句可以部使用大括号。多个语句必须使用大括号
2.3.1 for 语句
for (initialization; condition; afterthought)statement
2.3.2 do…while 语句
//先做某一件事,然后再判断
dostatement
while (condition);
2.3.3 while 语句
//先判断,条件满足,再做某事
while (condition)statement
2.3.4 labeled 语句
提供了一个带有标识符的语句,使您可以在程序的其他地方引用它
使用break和continue时,用于指定跳出的循环是哪个
label:statement
2.3.5 break 语句
当您使用不带标签的break时,它会立即终止最内层的while、do-while、for或switch语句,并将控制转移到下面的语句
当您使用带有标签的break时,它将终止指定的带标签语句。
break跳出整个循环
break;
break label;
2.3.6 continue 语句
跳出本次循环
continue;
continue label;
2.3.7 for…in 语句
迭代属性名,包括自定义属性
for (variable in object)statement
语句除了返回数值索引外,还将返回用户定义属性的名称,因此用传统for循环迭代数组更合适
2.3.8 for…of 语句
迭代属性值,不包括自定义属性
for (variable of object)statement
//for...in和for...of的对比
const arr = ["山河","百川","溪流"]
arr.name = "傻子"console.log(arr)for (const at in arr) {console.log(at)//0 1 2 name}for (const at of arr) {console.log(at)//山河 百川 溪流 }
解构:
const obj = { foo: 1, bar: 2 };for (const [key, val] of Object.entries(obj)) {console.log(key, val);
}
// "foo" 1
// "bar" 2
2.4 函数
2.4.1 定义函数
2.4.1.1 函数声明
function square(number) {return number * number;
}
函数参数传递一个值时,在函数内部对值进行更改时,对函数外部不会影响
函数参数传递一个对象时,在函数内部修改对象时,在函数外部对象会受影响
函数参数传递一个数组时,在函数内部修改数组时,在函数外部数组会受影响
2.4.1.2 函数表达式
将函数作为参数传递时,会方便很多
//它是匿名的,也可以有名字
const square = function (number) {return number * number;
};console.log(square(4)); // 16
可以根据条件创建函数,将函数写在if语句中
还可以使用Function构造函数,由字符串创建函数
2.4.2 调用函数
函数被调用时必须在作用域中,但是函数声明可以被提升(出现在代码中调用的下面)
函数提升只适用于函数声明,而不适用于函数表达式
事实证明,函数本身就是对象——反过来,这些对象也有方法。call()和apply()方法可用于实现此目标。
2.4.3 函数作用域
在函数内部定义的变量不能从函数外部的任何地方访问,因为变量只在函数的作用域中定义。但是,函数可以访问它自己所在作用域中的变量和函数
在全局作用域中定义的函数可以访问全局作用域中定义的所有变量。在另一个函数中定义的函数也可以访问其父函数中定义的所有变量
2.4.4 作用域与函数堆栈
2.4.4.1 递归
函数有三种方式调用自己:
- 函数名
- arguments.callee
- 引用函数的作用域内变量
const foo = function bar() {//如下三种方式都可以调用自身,是等价的。bar();arguments.callee();foo();
};
2.4.4.2 嵌套函数和闭包
一个函数可以嵌套在另一个函数中,内部函数对于它的外部函数是私有的。
内部函数,只能在外部函数的作用域中访问
内部函数形成一个闭包(内部函数可以使用外部函数的参数和变量,外部函数不能使用内部函数的参数和变量)
function outside(x) {function inside(y) {return x + y;}return inside;
}const fnInside = outside(3); //
console.log(fnInside(5)); // 8
console.log(outside(3)(5)); // 8
2.4.4.3 变量的保存
当inside返回时,x是如何保存的?
闭包必须保留它引用的所有作用域中的参数和变量,因为每一次调用传递不同的参数,每次调用outside新的闭包会被创建,直到返回的inside不再可访问,内存才被释放
2.4.4.4 多嵌套函数
因此,闭包可以包含多个作用域;它们递归地包含包含它的函数的作用域。这被称为作用域链
2.4.4.5 命名冲突
function outside() {const x = 5;function inside(x) {return x * 2;}return inside;
}console.log(outside()(10)); // 20 (instead of 10)
2.4.5 闭包
js允许函数嵌套,内部函数可以访问外部函数中定义的所有变量和函数(以及外部函数可以访问的所有其他变量和函数)
但是,外部函数不能访问内部函数中定义的变量和函数。这为内部函数的变量提供了一种封装
因此,如果内部函数设法在外部函数的生命周期之外存活(return 内部函数),那么在外部函数中定义的变量和函数将比外部函数执行的持续时间更长。当内部函数以某种方式可用于外部函数之外的任何作用域时,就创建了闭包
2.4.6 使用arguments对象
函数的参数保存在一个类似数组的对象中arguments,例如第一个参数arguments[0],等等
如果您事先不知道将向函数传递多少参数,这通常是有用的
注意:arguments变量是“类数组”,但不是数组。它类似于数组,因为它具有编号索引和长度属性。但是,它不具备所有的数组操作方法。
2.4.7 函数参数
2.4.7.1 默认参数
在JavaScript中,函数的参数默认为undefined
function multiply(a, b = 1) {return a * b;//b如果不传递值,默认为1
}
2.4.7.2 剩余参数
function test(...args){console.log(arguments)console.log(args)
}
test(1,4,3,5)
2.4.8 箭头函数
箭头函数并且没有自己的this、arguments、super或new.target。箭头函数总是匿名的
箭头函数this指向的问题
function Person() {this.age = 0; //此this指向Person的实例setInterval(function growUp() {this.age++; //此this指向window}, 1000);
}//解决this指向问题
function Person() {const self = this;self.age = 0;setInterval(function growUp() {self.age++;}, 1000);
}//使用箭头函数解决this指向问题
function Person() {this.age = 0; // 此this指向Person的实例setInterval(() => {this.age++; // 此this指向Person的实例}, 1000);
}const p = new Person();
2.5 表达式和操作符
2.5.1 赋值运算符
赋值操作符根据右操作数的值将值赋给左操作数
2.5.1.1 属性赋值
const obj = {};obj.x = 3;
console.log(obj.x); // Prints 3.
console.log(obj); // Prints { x: 3 }.const key = "y";
obj[key] = 5;
console.log(obj[key]); // Prints 5.
console.log(obj); // Prints { x: 3, y: 5 }.//在严格模式下,下面的代码抛出异常,因为不能给基本类型变量分配属性。
const val = 0;
val.x = 3;console.log(val.x); // Prints undefined.
console.log(val); // Prints 0.
2.5.1.2 解构
通过解构,你可以使用一条语句将多个值提取到不同的变量中:
const foo = ["one", "two", "three"];
const [one, two, three] = foo;
2.5.1.3 链式赋值语句
尽量避免使用链式赋值语句
//如下实际只声明了变量z
const z = y = x = f();
2.5.2 比较运算符
在大多数情况下,如果两个操作数不是同一类型,JavaScript会尝试将它们转换为合适的类型进行比较
这种行为通常会导致操作数的数值比较。比较中类型转换的唯一例外涉及===和!==操作符,它们执行严格的相等和不相等比较。在检查相等性之前,这些操作符不会尝试将操作数转换为兼容类型
2.5.3 算术运算符
算术运算符将数值(字面量或变量)作为其操作数,并返回单个数值
2.5.4 按位运算符
位运算符将其操作数视为32位(0和1)的集合,而不是十进制、十六进制或八进制数
2.5.5 逻辑运算符
逻辑运算符通常用于布尔(逻辑)值;如果是,则返回一个布尔值。
然而,&&和||操作符实际上返回的是指定操作数之一的值,因此,如果这些操作符与非布尔值一起使用,它们可能返回非布尔值
//逻辑与,第一个为true,则返回第二个值;第一个为false,直接返回第一个值
const a5 = "Cat" && "Dog"; // t && t returns Dog
const a6 = false && "Cat"; // f && t returns false
const a7 = "Cat" && false; // t && f returns false//逻辑或,第一个为true,则返回第一个值;第一个为false,直接返回第二个值
const o5 = "Cat" || "Dog"; // t || t returns Cat
const o6 = false || "Cat"; // f || t returns Cat
const o7 = "Cat" || false; // t || f returns Cat
2.5.6 长整型数字运算符
在表示大整数时,bigint比number具有更高的精度,但不能表示小数
bigint和数字不能相互替换——你不能在计算中混合使用它们
你可以比较bigint和数字。a = 1n > 2;
2.5.7 字符串运算符
除了可用于字符串值的比较操作符之外,连接操作符(+)还将两个字符串值连接在一起,返回另一个字符串,该字符串是两个操作数字符串的并集。
2.5.8 三元运算符
条件操作符是唯一接受三个操作数的JavaScript操作符。根据条件,运算符可以有两个值之一
condition ? val1 : val2
2.5.9 逗号运算符
逗号操作符(,)计算它的两个操作数,并返回最后一个操作数的值。这个操作符主要在for循环中使用,允许在每次循环中更新多个变量
const x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const a = [x, x, x, x, x];
//下面的代码使用逗号操作符一次更新两个变量
for (let i = 0, j = 9; i <= j; i++, j--) {// ^console.log(`a[${i}][${j}]= ${a[i][j]}`);
}
2.5.10 一元运算符
//删除对象的属性,如果成功返回true,否则返回false
delete object.property;
delete object[propertyKey];
delete objectName[index];
typeof操作符返回一个字符串,该字符串指示未求值操作数的类型
2.5.11 关系运算符
关系运算符比较其操作数,并根据比较是否为真返回布尔值。
//如果指定的属性在指定的对象中,则in操作符返回true。
//其中propNameOrNumber是表示属性名称或数组索引的字符串、数字或符号表达式,objectName是对象的名称
propNameOrNumber in objectName//如果指定的对象属于指定的对象类型,则instanceof操作符返回true
objectName instanceof objectType
2.5.12 基本表达式
this(代表对象本身)
new(创建一个对象)
super(调用父类的构造函数)
//this
<script>
function validate(obj, lowval, hival) {if (obj.value < lowval || obj.value > hival) {console.log("Invalid Value!");}
}
</script>
<p>Enter a number between 18 and 99:</p>
<input type="text" name="age" size="3" onChange="validate(this, 18, 99);" />
2.6 数值和日期
2.6.1 数值
//八进制
076//当0后面的数字小于8时,被解释为八进制,否则解释为10进制,这种严格模式下报错
0o76//正规语法使用该方式//二进制
0b10000000000000000000000000000000 // 2147483648
0b01111111100000000000000000000000 // 2139095040//十六进制
0x123456789ABCDEF
2.6.2 数值对象
Number有一些常量可以使用
let n = 1234
let n1 = 0.001234
//指数表示
console.log (n.toExponential(3)); //1.234e+3
console.log (n1.toExponential(3)); //1.234e-3
//保留小数位数
console.log (n.toFixed(2));//1234.00
console.log (n1.toFixed(2));//0.00
//精度位数
console.log (n.toPrecision(3));//1.23e+3
console.log (n1.toPrecision(3));//0.00123
2.6.3 Math对象
注意,所有的数学三角方法都以弧度为参数
//Math.floor(),向下取整,返回小于或等于参数的最大整数
console.log(Math.floor(5.95));// Expected output: 5
console.log(Math.floor(-5.05));// Expected output: -6//Math.ceil(),向上取整,返回大于或等于参数的最小整数
console.log(Math.ceil(0.95));// Expected output: 1
console.log(Math.ceil(-7.004));// Expected output: -7//Math.round(),四舍五入,返回最接近的整数
console.log(Math.round(5.95), Math.round(5.5), Math.round(5.05));// Expected output: 6 6 5
console.log(Math.round(-5.05), Math.round(-5.5), Math.round(-5.95));// Expected output: -5 -5 -6//Math.fround(),返回最接近的32位单精度浮点数表示形式
console.log(Math.fround(5.5));// Expected output: 5.5
console.log(Math.fround(5.05));// Expected output: 5.050000190734863
console.log(Math.fround(5));// Expected output: 5
console.log(Math.fround(-5.05));// Expected output: -5.050000190734863//Math.trunc(),直接删除小数部分,通过删除任何小数位数返回数字的整数部分
console.log(Math.trunc(0.123));// Expected output: 0
2.6.4 BigInts
2.6.5 日期对象
JavaScript没有日期数据类型。但是,您可以使用Date对象及其方法在应用程序中处理日期和时间
2.6.5.1 创建一个日期对象
const dateObjectName = new Date([parameters]);
不带new关键字调用Date将返回一个表示当前日期和时间的字符串
参数解释:
- 不带参数,表示创建一个当下的日期和时间,例new Date()
- 字符串YYYY-MM-DDTHH:mm:ss.sssZ,例new Date(“1995-12-25”),如果省略小时、分钟或秒,则该值将设置为零
- 年月日设置,例new Date(1995, 11, 25)
- 年月日时分秒设置,例new Date(1995, 11, 25, 9, 30, 0)
2.6.5.2 日期对象的方法
方法分为几大类:
- ”set“类,设置日期和时间
- ”get“类,获取日期和时间
- ”to“类,返回字符串日期和时间
- ”set“类,设置日期和时间
- ”parse “和UTC类,解析日期时间
有一个getDay方法返回星期几,但是没有相应的setDay方法,因为星期几是自动设置的
各种值的取值范围:
- 秒和分钟,0-59
- 小时,0-23
- 星期,0-6
- 日期,1-31
- 月,0-11
- 年,自1990开始
//parse方法用于将日期字符串中的值分配给现有的date对象
const ipoDate = new Date();
ipoDate.setTime(Date.parse("Aug 9, 1995"));
2.7 文本格式化
2.7.1 字符串对象
String对象是字符串原语数据类型的包装器
2.7.1.1 对象方法
方法 | 解释 |
---|---|
charAt(index) | 返回一个指定索引处的字符。超出索引范围,返回一个空字符串。 |
charCodeAt(index) | 0 ~ 65535之间的整数。表示指定索引处字符的UTF-16码单位值。超出索引范围,返回NaN。 |
codePointAt(index) | 一个非负整数,当unicode值为两个16位代码单位时使用。超出索引范围,返回undefined。 |
indexOf(searchString[, position]) | 查找到的searchString第一次出现的索引,如果没有找到则为-1。大于或等于position(默认0)位置 |
lastIndexOf(searchString[, position]) | 返回指定子字符串最后出现的索引 |
startsWith() | 确定该字符串是否以指定字符串的字符开头,并根据需要返回true或false |
endsWith() | 确定字符串是否以该字符串的字符结束,并根据需要返回true或false。 |
includes() | 执行区分大小写的搜索,以确定给定字符串是否可以在此字符串中找到,并根据情况返回true或false |
concat(str1, str2, /* …, */ strN) | 将字符串参数连接到此字符串并返回一个新字符串,参数可以是多个字符串 |
split(separator[, limit]) | 接受一个模式,并通过搜索该模式将该字符串划分为有序的子字符串列表,将这些子字符串放入数组,并返回该数组 |
slice(indexStart[, indexEnd]) | 提取该字符串的一部分,并将其作为新字符串返回,而不修改原始字符串。可接收负参数 |
substring(indexStart[, indexEnd]) | 返回从开始索引到不包括结束索引的字符串部分,如果没有提供结束索引,则返回到字符串的末尾。自动将较小的参数作为开始索引,较大的参数作为结束索引 |
match(regexp) | 检索与正则表达式匹配该字符串的结果,返回数组 |
matchAll(regexp) | 返回一个迭代器,其中包含与正则表达式匹配的字符串的所有结果,包括捕获组 |
replace(pattern, replacement) | 返回一个新字符串,其中包含一个、一些或所有匹配的模式,该模式被替换 |
replaceAll(pattern, replacement) | 返回一个新字符串,其中模式的所有匹配都被替换。模式可以是字符串或RegExp,替换可以是要为每次匹配调用的字符串或函数。原始字符串保持不变。 |
search(regexp) | 执行正则表达式与该字符串之间的匹配搜索,返回字符串中第一个匹配的索引 |
toLowerCase() | 返回转换为小写的字符串。 |
toUpperCase() | 返回转换为大写的字符串。 |
normalize(form) | 返回该字符串的Unicode规范化形式。form的值"NFC" , "NFD" , "NFKC" , or "NFKD" |
repeat(count) | 构造并返回一个新字符串,其中包含该字符串的指定数量的连接在一起的副本 |
trim() | 从字符串的两端删除空白,并返回一个新字符串,而不修改原始字符串。 |
2.7.1.2 多行模板字面量
console.log(`string text line 1
string text line 2`);
// "string text line 1
// string text line 2"const five = 5;
const ten = 10;
console.log(`Fifteen is ${five + ten} and not ${2 * five + ten}.`);
// "Fifteen is 15 and not 20."
2.7.2 国际化
2.7.2.1 时间和日期格式化
Intl.DateTimeFormat对象用于格式化日期和时间
2.7.2.2 数值格式化
Intl.NumberFormat对象格式化数值
2.7.2.3 整理字符串
Intl.Collator对于比较和排序字符串有用
2.8 正则表达式
2.8.1 创建一个正则表达式
两种方式创建:
- 使用字面量
const re = /ab+c/;
- 调用RegExp对象的构造函数
const re = new RegExp("ab+c");
当您知道正则表达式模式将发生变化,或者您不知道该模式并从其他源(例如用户输入)获取该模式时,请使用构造函数
2.8.2 写一个正则表达式模板
2.8.2.1 断言类
断言包括边界,它指示行和单词的开始和结束,以及以某种方式指示可能匹配的其他模式(包括向前查找、向后查找和条件表达式)。
-
边界类断言
单词边界,它表示的是单词字符(字母、数字或下划线)和非单词字符之间的位置,或者单词字符和字符串的开始或结束之间的位置
字符串的开始和结束被认为是非单词
字符 解释 ^ 匹配输入的开头 $ 匹配输入的结尾 \b 匹配单词边界 \B 匹配非单词边界。例如两个字母之间或两个空格之间 -
其他类断言
字符 解释 x(?=y) 只有当"x"后面跟"y"时才匹配"x" x(?!y) 只有当“x”后面没有“y”时才匹配“x” (?<=y)x 仅当“x”前面有“y”时匹配“x” (?<!y)x 仅当“x”前面没有“y”时匹配“x”。
2.8.3.2 字符类
区分不同类型的字符
字符 | 解释 |
---|---|
[xyz] [a-c] | 匹配任何一个包含的字符。连字符指定一个字符范围,如连字符作为方括号内的第一个或最后一个字符为匹配字符 |
[^xyz] [^a-c] | 它匹配没有括在方括号中的任何内容。连字符指定一个字符范围,连字符在^后面的第一个字符或方括号内的最后一个字符,则将其作为文字连字符作为普通字符包含在字符类中 |
. | 匹配除行终止符以外的任何单个字符。在字符类中,点失去了它的特殊含义,而与文字点匹配 |
\d | 匹配任何数字,等价于[0-9] |
\D | 匹配任何非数字,等价于[^0-9] |
\w | 匹配字母数字下划线,等价于[A-Za-z0-9_] |
\W | 匹配非字母数字下划线,等价于[^A-Za-z0-9_] |
\s | 匹配单个空白字符,包括空格、制表符、表单换行、换行和其他Unicode空格 |
\S | 匹配非单个空白字符 |
\t | 匹配水平制表符 |
\r | 匹配回车符。 |
\n | 匹配换行 |
\v | 匹配垂直制表符 |
\f | 表单换行 |
\0 | 匹配一个NUL字符。后面不要跟另一个数字 |
\xhh | 用代码hh(两个十六进制数字)匹配字符 |
\uhhhh | 匹配一个UTF-16编码单元,值为hhhh(四个十六进制数字)。 |
\u{hhhh} \u{hhhhh} | (仅当设置了u标志时。)用Unicode值U+hhhh或U+hhhh(十六进制数字)匹配字符 |
\ | 指示后面的字符应该被特殊处理,或者“转义” |
`x | y` |
2.8.4.3 组和反向引用类
组将多个模式作为一个整体进行分组,在使用正则表达式模式对字符串进行匹配时,捕获组提供了额外的子匹配信息。
反向引用引用在同一正则表达式中先前捕获的组
字符 | 解释 |
---|---|
(x) | 匹配x并记住匹配项,使用exec函数时,括号内会单独匹配一次 |
(?<Name>x) | 匹配“x”并将其存储在返回的匹配项的groups属性中,名称为< name > |
(?:x) | 匹配“x”,但不记得匹配 |
\n | 反向引用括号中的组,例如,/apple(,)\sorange\1/。匹配"apple, orange, cherry, peach"中的"apple, orange,"。 |
\k<Name> | 与指定的Named捕获组匹配的最后一个子字符串的反向引用。类似于\n |
2.8.5.4 数量类
指示要匹配的字符或表达式的数量
字符 | 解释 |
---|---|
x* | 匹配零次或多次。>=0 |
x+ | 匹配一次或多次。>=1 |
x? | 匹配零次或一次。=0或=1 |
x{n} | 精确匹配x出现了n次的 |
x{n,} | 匹配x至少出现了n的 |
x{n,m} | 匹配至少出现n次,最多出现m次 |
x*? x+? x?? x{n}? x{n,}? x{n,m}? | 默认为贪婪模式,加上?后为非贪婪模式 |
2.8.5.5 高级搜索标志
const re = /pattern/flags;
标志 | 描述 |
---|---|
d | 为子字符串匹配生成索引 |
j | 全局搜索 |
i | 不区分大小写的搜索 |
m | 允许^和$在换行符旁边匹配 |
s | 允许.匹配换行符 |
u | 将模式视为Unicode码点序列 |
v | 升级到具有更多Unicode功能的u模式 |
y | 执行从目标字符串的当前位置开始匹配的“粘性”搜索。 |
2.8.3 使用正则表达式
方法 | 描述 |
---|---|
exec() | 执行对字符串中的匹配项的搜索。它返回一个信息数组,如果不匹配则返回null。 |
test() | 测试字符串中的匹配项。它返回真或假 |
match() | 返回一个包含所有匹配项(包括捕获组)的数组,如果没有找到匹配项,则返回null。 |
matchAll() | 返回一个包含所有匹配项(包括捕获组)的迭代器。 |
search() | 测试字符串中的匹配项。它返回匹配项的索引,如果搜索失败则返回-1。 |
replace() | 执行对字符串中的匹配项的搜索,并用替换子字符串替换匹配的子字符串 |
replaceAll() | 对字符串中的所有匹配项执行搜索,并用替换子字符串替换匹配的子字符串。 |
split() | 使用正则表达式或固定字符串将字符串分解为子字符串数组。 |
当您想知道是否在字符串中找到匹配时,请使用test()或search()方法
要获得更多信息(但执行速度较慢),请使用exec()或match()方法
2.8.4 工具
RegExr,一个学习、构建和测试正则表达式的在线工具
Regex tester,一个在线正则表达式构建器/调试器
Regex interactive tutorial,一个在线互动教程,手册,和广场
Regex visualizer,一个在线可视化正则表达式测试器
2.9 索引集合类
2.9.1 创建一个数组
//创建一个有元素的数组
const arr1 = new Array(element0, element1, /* …, */ elementN);
const arr2 = Array(element0, element1, /* …, */ elementN);
const arr3 = [element0, element1, /* …, */ elementN];
//创建一个有长度的空数组
const arr1 = new Array(arrayLength);
const arr2 = Array(arrayLength);
const arr3 = [];
arr3.length = arrayLength;const wisenArray = Array.of(9.3);
2.9.2 引用数组元素
2.9.3 填充一个数组
2.9.3.1 理解length
在实现层,JavaScript的数组实际上将其元素存储为标准对象属性,使用数组索引作为属性名
长度属性是特殊的。它的值总是一个正整数,大于最后一个元素的索引(如果存在)
你也可以给length属性赋值,写入小于存储项数的值将截断数组。写0将把它清空
2.9.3.2 遍历数组
for循环和forEach
请注意,在定义数组时省略的数组元素在使用forEach迭代时不会列出,但在将undefined手动赋值给该元素时才会列出
const sparseArray = ["first", "second", , "fourth"];//不会列出
const nonsparseArray = ["first", "second", undefined, "fourth"];//会列出
不建议使用for…in循环,这样会列出普通元素和所有可枚举属性
2.9.3.3 数组的方法
方法 | 说明 | 示例,let myArray = [“1”, “2”, “3”]; |
---|---|---|
concat() | 连接两个或多个数组,并返回新数组 | const newArray = myArray.concat(“a”, “b”, “c”,[1,2]); |
join() | 将一个数组的所有元素连接成一个字符串 | const list = myArray.join(" - "); |
push() | 将一个或多个元素添加到数组的末尾,并返回数组的结果长度 | myArray.push(“3”); |
pop() | 从数组中删除最后一个元素并返回该元素 | const last = myArray.pop(); |
shift() | 从数组中删除第一个元素并返回该元素。 | const first = myArray.shift(); |
unshift() | 将一个或多个元素添加到数组的前面,并返回数组的新长度 | myArray.unshift(“4”, “5”); |
slice() | 提取数组的一部分并返回一个新数组。 | myArray.slice(1, 2); |
at() | at()方法返回数组中指定索引处的元素,如果索引超出范围则返回undefined。它特别用于从数组末尾访问元素的负索引 | myArray.at(-2); |
splice() | 从数组中删除元素并(可选地)替换它们。它返回从数组中删除的项 | myArray.splice(1, 3, “a”, “b”, “c”, “d”);(1开始移除3个元素,并用后面参数填充) |
reverse() | 将数组中的元素调换位置:第一个数组元素变成最后一个,最后一个元素变成第一个。它返回对数组的引用 | myArray.reverse(); |
flat() | 返回一个新数组,其中所有子数组元素递归地连接到其中,直至指定的深度 | let myArray = [1, 2, [3, 4]]; myArray = myArray.flat(); //[1, 2, 3, 4] |
sort() | 对数组中的元素进行排序,并返回对该数组的引用。可以带回调函数,自定义比较方式 | myArray.sort(); |
indexOf() | 返回第一个匹配项的索引 | myArray.indexOf(“1”) |
lastIndexOf() | 方法的工作方式与indexOf类似,但从末尾开始并向后搜索 | myArray.lastIndexOf(“1”) |
forEach() | 对每个数组项执行回调并返回undefined。 | myArray.forEach((element) => { console.log(element); }); |
map() | 返回对每个数组项执行回调的返回值的新数组。 | const a2 = a1.map((item) => item.toUpperCase()); |
flatMap() | 在map()之后运行深度为1的flat()。 | |
filter() | 返回一个新数组,其中包含回调返回true的项目 | const a2 = a1.filter((item) => typeof item === “number”); |
find() | 返回回调返回true的第一个项 | const i = a1.find((item) => typeof item === “number”); |
findLast() | 返回回调返回true的最后一项。 | |
findIndex() | 返回回调返回true的第一个项的索引。 | |
findLastIndex() | 返回回调返回true的最后一项的索引。 | |
every() | 如果回调函数对数组中的每个项都返回true,则every()方法返回true | |
some() | 如果回调函数对数组中至少一项返回true,则some()方法返回true。 | |
reduce() | 应用回调callback(accumulator, currentValue, currentIndex, array)。此回调的返回值,会再次赋值给accumulator | |
reduceRight() | 工作方式与reduce()类似,但从最后一个元素开始 |
2.9.4 数组变换
您可以在数组和其他数据结构之间来回转换
let students = [{name:'张三',sex:'男'},{name:'王五',sex:'男'},{name:'小三',sex:'女'},{name:'李六',sex:'男'},{name:'韩其',sex:'男'},{name:'小美',sex:'女'},{name:'小帅',sex:'男'},
]//按照性别分组
const result = Object.groupBy(students, ({ sex }) => sex);
console.log (result);
//按照符合条件的分组
const result2 = Object.groupBy(students, ({ name }) => name.startsWith('小'));
console.log (result2);
2.9.5 稀疏数组
数组可以包含“空槽”,这与填充值为undefined的槽不同
2.9.6 多维数组
数组可以嵌套,这意味着一个数组可以包含另一个数组作为元素。使用JavaScript数组的这个特性,可以创建多维数组
2.9.7 使用数组存储其他属性
数组也可以像对象一样用于存储相关信息
const arr = [1, 2, 3];
arr.property = "value";
console.log(arr.property); // "value"
2.9.8 类数组对象
NodeList或函数体中的arguments对象,表面上看起来和行为都像数组,但并不共享它们的所有方法
数组方法不能在类数组对象上直接调用
但是你可以使用Function.prototype.call()间接调用它们
function printArguments() {Array.prototype.forEach.call(arguments, (item) => {console.log(item);});
}
2.10 键集合
2.10.1 Maps
2.10.1.1 Map对象
Map对象是一个简单的键/值映射,可以按插入顺序迭代其元素
const sayings = new Map();
sayings.set("dog", "woof");
sayings.set("cat", "meow");
sayings.set("elephant", "toot");
sayings.size; // 3
sayings.get("dog"); // woof
sayings.get("fox"); // undefined
sayings.has("bird"); // false
sayings.delete("dog");
sayings.has("dog"); // falsefor (const [key, value] of sayings) {console.log(`${key} goes ${value}`);
}
// "cat goes meow"
// "elephant goes toot"sayings.clear();
sayings.size; // 0
2.10.1.2 对象和Map比较
- 对象的key可以是strings和symbols,而map可以是任何值
- 您可以轻松地获得Map的大小,而您必须手动跟踪对象的大小
- map的迭代按照元素的插入顺序进行
- 一个对象有一个原型,所以在映射中有默认的键
let keyObj={}
// 虽然看起来你可以使用数字作为对象的键,但实际上这些键都被转换为了字符串
const obj ={true:"34",1:"456","2":"ww",keyObj:2
}
for (let key of Object.keys(obj)) {console.log(key,typeof key);
}
console.log (obj);
// 1类型是 string
// 2类型是 string
// true类型是 string
// keyObj类型是 stringconst map = new Map();
map.set(true,"34")
map.set(1,"456")
map.set("2","ww")
map.set(keyObj,2)
for (let key of map.keys()) {console.log(key,typeof key);
}
// true 'boolean'
// 1 'number'
// 2 string
// {} 'object'
以下三个技巧可以帮助你决定是使用Map还是Object:
- 当键在运行时都是未知的,并且所有键有相同的类型,所有值有相同的类型时,使用map
- 如果需要将原始值存储为键,请使用 Map,因为对象会将每个键都视为字符串
- 当对单个元素进行操作的逻辑存在时,使用Object
2.10.1.3 WeakMap对象
WeakMap是键/值对的集合,其键必须是对象或未注册的符号,具有任意JavaScript类型的值,并且不会创建对其键的强引用
在WeakMap中作为键存在的对象不会阻止该对象被垃圾收集。一旦作为键使用的对象被收集,它在任何WeakMap中的对应值也成为垃圾收集的候选者——只要它们没有在其他地方被强引用。
惟一可以用作WeakMap键的基本类型是symbol
WeakMap不允许观察其键的活动,这就是为什么它不允许枚举的原因。因此,没有方法可以获取WeakMap中的键列表
WeakMap对象的一个用例是存储对象的私有数据,或者隐藏实现细节
2.10.2 Sets
2.10.2.1 Set对象
集合对象是唯一值的集合。您可以按插入顺序迭代它的元素。Set中的值只能出现一次;这是唯一的集合的集合
const mySet = new Set();
mySet.add(1);
mySet.add("some text");
mySet.add("foo");mySet.has(1); // true
mySet.delete("foo");
mySet.size; // 2for (const item of mySet) {console.log(item);
}
// 1
// "some text"
2.10.2.2 Array和Set之间的转换
使用Array.from,将一个集合转换成数组
Set对象存储唯一的值—因此在转换时将删除任何来自Array的重复元素!
const set = new Set()
set.add(1)
set.add(3)
set.add(4)
set.add(3)const arr = Array.from(set)
console.log (arr);//[1, 3, 4]
2.10.2.3 Array 和 Set 的比较
- 通过
arr.splice(arr.indexOf(val), 1)
删除数组元素非常慢 - 集合可以通过值,直接删除元素,而数组则需要使用splice基于元素索引
- 在数组的indexOf中找不到值NaN
- Set 对象存储唯一值。你不需要手动跟踪重复项
2.10.1.3 WeakSet对象
弱集对象是可垃圾回收值的集合,包括对象和未注册的符号。WeakSet中的值只能出现一次。它在WeakSet集合中是唯一的
WeakSet和Set的不同:
- 与集合相反,弱集只是对象或符号的集合,而不是任何类型的任意值的集合
- WeakSet是弱的:对集合中对象的引用是弱保存的。如果对WeakSet中存储的对象没有其他引用,则可以对它们进行垃圾收集。这也意味着集合中没有存储当前对象的列表
- WeakSet不可枚举
WeakSet对象的用例是有限的。它们不会泄漏内存,因此使用DOM元素作为键并标记它们以进行跟踪是安全的
2.11 使用对象
对象是属性的集合,属性是名称(或键)和值之间的关联。属性的值可以是一个函数,在这种情况下,属性被称为方法。
2.11.1 创建新对象
2.11.1.1 使用初始化器
对象初始化器也称为对象字面量
const obj = {property1: value1, // property name may be an identifier2: value2, // or a number"property n": value3, // or a string
};
使用初始化器创建的对象称为普通对象,因为它们是Object的实例,而不是任何其他对象类型。一些对象类型有特殊的初始化语法——例如,数组初始化和正则表达式字面量
2.11.1.2 使用构造函数
function Person(name, age, sex) {this.name = name;this.age = age;this.sex = sex;
}
const rand = new Person("Rand McKinnon", 33, "M");
您还可以使用class语法而不是function语法来定义构造函数
2.11.1.3 使用Object.create()方法
它允许您为想要创建的对象选择原型对象,而不必定义构造函数。
// Animal properties and method encapsulation
const Animal = {type: "Invertebrates", // Default value of propertiesdisplayType() {// Method which will display type of Animalconsole.log(this.type);},
};// Create new animal type called animal1
const animal1 = Object.create(Animal);
2.11.2 对象和属性
JavaScript对象具有与之关联的属性。对象属性基本上与变量相同,除了它们与对象相关联,而不是与作用域相关联。对象的属性定义了对象的特征
2.11.2.1 访问属性
str = "myString";
myObj[str] = "This key is in variable str";console.log(myObj.str); // undefinedconsole.log(myObj[str]); // 'This key is in variable str'
console.log(myObj.myString); // 'This key is in variable str'
2.11.2.2 枚举属性
- for…in ,此方法遍历对象的所有可枚举字符串属性及其原型链。(自己可枚举属性+原型属性)
- Object.keys(),返回一个数组,只枚举自己中的属性,而不是原型链中的。(自己可枚举属性)
- Object.getOwnPropertyNames(),返回一个数组,只枚举自己中的属性,而不管是否可枚举。(自己可枚举属性+自己不可枚举属性)
2.11.2.3 删除属性
可以使用delete操作符删除非继承属
delete myobj.a;
2.11.3 继承
所有对象都至少继承一个其他对象。被继承的对象被称为原型,继承的属性可以在构造函数的原型对象中找到
你可以使用prototype属性为通过某个构造函数创建的所有对象添加属性
Car.prototype.color = "red";
console.log(car1.color); // "red"
2.11.4 定义方法
方法的定义方式与普通函数的定义方式相同,不同之处在于它们必须被赋值为对象的属性
objectName.methodName = functionName;const myObj = {myMethod: function (params) {// do something},// this works too!myOtherMethod(params) {// do something else},
};
2.11.5 定义getter和setter
getter是一个与属性相关联的函数,用于获取特定属性的值。setter是与属性相关联的函数,用于设置特定属性的值。它们一起可以间接地表示属性的值。
定义时以关键字get或set作为前缀。getter方法不需要参数,而setter方法只需要一个参数(要设置的新值)
const myObj = {a: 7,get b() {return this.a + 1;},set c(x) {this.a = x / 2;},
};console.log(myObj.a); // 7
console.log(myObj.b); // 8, returned from the get b() method
myObj.c = 50; // Calls the set c(x) method
console.log(myObj.a); // 25
也可以使用object . defineproperties()方法在对象创建后随时将getter和setter添加到对象中:
const myObj = { a: 0 };Object.defineProperties(myObj, {b: {get() {return this.a + 1;},},c: {set(x) {this.a = x / 2;},},
});myObj.c = 10; // Runs the setter, which assigns 10 / 2 (5) to the 'a' property
console.log(myObj.b); // Runs the getter, which yields a + 1 or 6
2.11.6 比较对象
对象是一种引用类型。两个不同的对象永远不会相等,即使它们具有相同的属性。只有将同一对象引用与自身进行比较才会产生true
2.12 使用类
在JavaScript中,类主要是对现有原型继承机制的抽象——所有模式都可以转换为基于原型的继承。
类本身也是普通的JavaScript值,并且有自己的原型链。实际上,大多数普通JavaScript函数都可以用作构造函数—使用new操作符和构造函数来创建新对象。
2.12.1 类概述
类的基本
- 类创建对象使用new操作符
- 每一个对象都有一些通过类添加的属性(数据,方法)
- 类存储一些自己的属性(数据,方法),被用于和实例交互
类的三大关键特征:
- 构造函数
- 实例方法和实例字段
- 静态方法和静态字段
2.12.2 声明类
基本声明结构:
class MyClass {// 构造函数constructor() {// 构造函数体}// 实例字段myField = "foo";// 实例方法myMethod() {// 方法体}// 静态字段static myStaticField = "bar";// 静态方法static myStaticMethod() {// 方法体}// 静态代码块static {// 静态初始化代码}// 私有属性#myPrivateField = "bar";
}
与函数声明不同,类声明不会被提升(或者,在某些解释中,会被提升,但有暂时死区限制),这意味着在声明类之前不能使用它。
类声明还可以使用表达式:
const MyClass = class {// Class body...
};
2.12.3 构造函数
也许类最重要的工作是充当对象的“工厂”。在类中,实例创建是由构造函数完成的。
class Color {constructor(r, g, b) {// Assign the RGB values as a property of `this`.this.values = [r, g, b];}
}
建议不要从构造函数返回任何值——因为如果返回一个非原始值,它将成为new表达式的值,而this的值将被删除
2.12.4 实例方法
class Color {constructor(r, g, b) {this.values = [r, g, b];}getRed() {return this.values[0];}
}const red = new Color(255, 0, 0);
console.log(red.getRed()); // 255
2.12.5 私有字段
在面向对象编程中有一种哲学叫做“封装”。这意味着您不应该访问对象的底层实现,而应该使用抽象良好的方法与之交互
私有字段是以#(散列符号)为前缀的标识符。哈希是字段名称的组成部分,这意味着私有属性永远不会与公共属性发生名称冲突
为了在类的任何地方引用私有字段,必须在类体中声明它(不能动态地创建私有属性)。除此之外,私有字段几乎等同于普通属性
注意:在Chrome控制台中运行的代码可以在类的外面访问私有属性。这是一个仅针对devtools的JavaScript语法限制的放松
在对属性进行赋值时,往往需要在函数中进行合法性验证,才允许赋值。但是如果属性不是私有属性,则外部可以直接访问属性,更改其值,而绕过合法检查
类中访问不存在的私有属性会抛出错误,而不是像普通属性那样返回未定义
方法、getter和setter也可以是私有的。当你有一些复杂的事情需要类在内部完成,但不允许代码的其他部分调用时,它们是有用的。
2.12.6 访问器字段
//看起来好像对象有一个名为red的属性——但实际上,实例中不存在这样的属性!只有两个方法,但它们都带有get和set前缀,这使得它们可以像属性一样被操作
//如果一个字段只有getter而没有setter,那么它实际上是只读的
class Color {constructor(r, g, b) {this.values = [r, g, b];}get red() {return this.values[0];}set red(value) {this.values[0] = value;}
}const red = new Color(255, 0, 0);
red.red = 0;
console.log(red.red); // 0
2.12.7 公有字段
字段通常被设计成独立于构造函数的参数。
公共字段几乎等同于为this分配一个属性
class MyClass {luckyNumber = Math.random();
}
console.log(new MyClass().luckyNumber); // 0.5
console.log(new MyClass().luckyNumber); // 0.3//和上面等价
class MyClass {constructor() {this.luckyNumber = Math.random();}
}
2.12.8 静态属性
静态属性是在类本身上定义的一组类特性,而不是在类的单个实例上定义。这些功能包括:
- 静态方法
- 静态字段
- 静态getter和setter
静态属性与实例属性非常相似,不同之处在于:
- 静态的前缀都是static
- 静态的不能由实例访问
还有一个称为静态初始化块的特殊构造,它是在类首次加载时运行的代码块。
静态初始化块几乎等同于在声明类之后立即执行一些代码。唯一的区别是它们可以访问静态私有属性
2.12.9 扩展和继承
一个对象可以“借用”另一个对象的大部分行为,同时用自己的逻辑重写或增强某些部分
派生类可以访问父类的所有公共属性。在JavaScript中,派生类是用extends子句声明的,该子句指出它是从哪个类派生的。
//我们调用super(r, g, b)。在访问this之前调用super()是语言要求
//super()调用调用父类的构造函数来初始化this -这里它大致相当于this = new Color(r, g, b)
//您可以在super()之前有代码,但您不能在super()之前访问this
class ColorWithAlpha extends Color {#alpha;constructor(r, g, b, a) {super(r, g, b);this.#alpha = a;}get alpha() {return this.#alpha;}set alpha(value) {if (value < 0 || value > 1) {throw new RangeError("Alpha value must be between 0 and 1");}this.#alpha = value;}
}
派生类也可以覆盖父类的方法。例如,所有类都隐式继承Object类,该类定义了一些基本方法,如toString()。然而,基本的toString()方法是出了名的无用,因为它在大多数情况下打印[object object]
静态方法也会相互继承,因此您也可以覆盖或增强它们
派生类不能访问父类的私有字段——这是JavaScript私有字段“硬私有”的另一个关键方面。私有字段的作用域限定在类主体本身,不授予对任何外部代码的访问权限
2.13 使用promises
Promise 是一个对象,代表了一个异步操作的最终完成或者失败
2.13.1 链式调用
//果你的错误处理代码对所有步骤都是相同的,你可以把它附加到链的末尾:
doSomething().then((result) => doSomethingElse(result)).then((newResult) => doThirdThing(newResult)).then((finalResult) => {console.log(`Got the final result: ${finalResult}`);}).catch(failureCallback);
2.13.2 错误处理
现在,所有的错误都由链末尾的catch()方法处理,如果不使用async/await,几乎不需要使用try/catch
如果一个 Promise 的拒绝事件没有被任何处理程序处理,它会冒泡到调用堆栈的顶部,主机需要将其显示出来。在网页上,每当一个 Promise 被拒绝,全局范围内(通常是窗口,或者如果在 web worker 中使用,它是 Worker 或其他基于 worker 的接口)会发送两个事件之一。这两个事件是:
- unhandledrejection,当Promise 被拒绝,但没有可用的拒绝处理程序时发送
- rejectionhandled,当处理程序附加到已被拒绝的Promise 时发送,该Promise 已导致unhandledrejection事件.
nodejs和浏览器有些许差异,注意事件名的大小写
process.on("unhandledRejection", (reason, promise) => {// Add code here to examine the "promise" and "reason" values
});
2.13.3 组合
有四种组合工具可用于并发运行异步操作:
四个都接受Promise的可迭代对象作为输入,并返回一个Promise
-
Promise.all()
当所有输入的promise都实现时(包括传递空可迭代对象时),返回的promise就会实现,并带有一个实现后数组
当输入的任何一个承诺被拒绝时,它就会拒绝,这是第一个拒绝原因。
总结:都成功时才成功,任何一个失败都会失败
-
Promise.allSettled()
当所有输入的promise都完成时(包括传递空可迭代对象时),这个返回的promise就会完成,并使用一个对象数组来描述每个promise的结果
总结:总是成功
-
Promise.any()
只要其中一个成功,返回成功,并携带第一个成功返回值
总结:所有失败,才失败,返回拒绝原因的数组
-
Promise.race()
这个返回的承诺与第一个承诺的最终状态一致。
总结:是在任何一个 Promise 完成(无论成功还是失败)时就完成。
我们可以同时开始操作,然后像这样等待它们全部完成:
Promise.all([func1(), func2(), func3()]).then(([result1, result2, result3]) => {// use result1, result2 and result3
});
2.13.4 旧回调API创建promises
在理想情况下,所有异步函数都已经返回promises。不幸的是,一些api仍然期望以旧的方式传递成功和/或失败回调。最明显的例子是setTimeout()函数
//混合旧式的回调和承诺是有问题的。如果saySomething()失败或包含编程错误,则不会捕获它。这是setTimeout的内在设计
setTimeout(() => saySomething("10 seconds passed"), 10 * 1000);
//index.js:1 Uncaught ReferenceError: saySomething is not defined
幸运的是,我们可以将setTimeout包装在一个promise中。最佳实践是在尽可能低的级别包装接受回调的函数,然后不再直接调用它们
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));wait(10 * 1000).then(() => saySomething("10 seconds")).catch(failureCallback);
2.13.5 时间
Promise回调作为微任务处理,而setTimeout()回调作为任务队列处理
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));wait(0).then(() => console.log(4));
Promise.resolve().then(() => console.log(2)).then(() => console.log(3));
console.log(1); // 1, 2, 3, 4
2.14 类型数组
JavaScript类型数组是类似于数组的对象,它提供了在内存缓冲区中读写原始二进制数据的机制。
它们为开发人员提供了操作二进制数据的熟悉接口。这在与平台特性交互时非常有用,例如音频和视频操作、使用WebSockets访问原始数据等等。
在类型化数组上调用array . isarray()将返回false。此外,并不是所有普通数组可用的方法都被类型化数组支持(例如push和pop)
JavaScript的类型化数组将实现分为缓冲区和视图
为了访问缓冲区中的内存,你需要使用视图
2.14.1 缓冲区
缓冲区是表示数据块的对象;它本身没有格式,也不提供访问其内容的机制
缓冲区有两种类型:
- ArrayBuffer
- SharedArrayBuffer
缓冲区支持以下操作:
- 分配,一旦新的缓冲区被创建,新的内存区域被分配,并初始化为0
- 复制,使用slice()方法,您可以有效地复制内存的一部分,而无需创建视图来手动复制每个字节
- 转移,使用transfer()和transferToFixedLength()方法,您可以将内存范围的所有权转移到一个新的缓冲区对象。SharedArrayBuffer不能被转移
- 调整大小,使用resize()方法,您可以调整内存区域的大小,在maxByteLength限制范围内,SharedArrayBuffer只能增长,不能收缩。
2.14.2 视图
视图提供了一个上下文——也就是数据类型,起始偏移量,和元素数量。
目前有两种主要的视图:
- 类型化数组视图,提供实用程序方法,允许您方便地转换二进制数据
- ,层次更低,允许对数据的访问方式进行粒度控制
视图有以下属性:
- buffer,视图引用的底层缓冲区
- byteOffset,视图从其缓冲区开始的偏移量,以字节为单位。
- byteLength,视图的长度,以字节为单位。
2.14.2.1 类型数组视图
Type | Value Range | Size in bytes | Web IDL type |
---|---|---|---|
Int8Array | -128 to 127 | 1 | byte |
Uint8Array | 0 to 255 | 1 | octet |
Uint8ClampedArray | 0 to 255 | 1 | octet |
Int16Array | -32768 to 32767 | 2 | short |
Uint16Array | 0 to 65535 | 2 | unsigned short |
Int32Array | -2147483648 to 2147483647 | 4 | long |
Uint32Array | 0 to 4294967295 | 4 | unsigned long |
Float32Array | -3.4e38 to 3.4e38 | 4 | unrestricted float |
Float64Array | -1.8e308 to 1.8e308 | 8 | unrestricted double |
BigInt64Array | -263 to 263 - 1 | 8 | bigint |
BigUint64Array | 0 to 264 - 1 | 8 | bigint |
所有类型化数组视图都有相同的方法和属性,由TypedArray类定义
类型化数组原则上是固定长度的,因此不能使用可能改变数组长度的数组方法。这些包括 pop
, push
, shift
, splice
, 和 unshift
此外,flat是不可用的,因为没有嵌套的类型数组,相关的方法包括concat和flatMap没有很好的用例,所以不可用。toSpliced也不可用
除以上外其他数组方法在array和TypedArray之间共享
TypedArray有额外的set和subarray方法,可以优化处理查看同一缓冲区的多个类型数组:
-
set(),方法允许使用来自另一个数组或类型化数组的数据同时设置多个类型化数组索引
-
subarray(),方法创建一个新的类型化数组视图,该视图引用与原始类型化数组相同的缓冲区,但范围更窄
与常规数组类似,您可以使用括号表示法访问类型化数组元素
-
越界索引访问总是返回undefined,而不实际访问对象上的属性
-
任何写越界属性的尝试都没有效果:它不会抛出错误,但也不会改变缓冲区或类型化数组
-
类型化数组索引似乎是可配置和可写的,但任何更改其属性的尝试都将失败
const typeArrayView8 = new Uint8Array([1,2,3])
console.log (typeArrayView8[2]); // log 3
2.14.2.2 DataView
DataView是一个低级接口,它提供了一个getter/setter API,用于向缓冲区读取和写入任意数据。
DataView不需要对齐;多字节读写可以在任何指定的偏移量开始
const dataView8 = new DataView(new ArrayBuffer(3));
dataView8.setInt8(0, 1, true);
dataView8.setInt8(1, 2, true);
dataView8.setInt8(2, 3, true);
console.log (dataView8.getInt8(2));//log 3
类型数组视图(TypedArray)和DataView都是操作ArrayBuffer的接口,但它们之间存在一些差异和不同的使用场景。
-
相较于 TypedArray,DataView 对于 ArrayBuffer 的操作更加灵活。我们可以发现在 TypedArray 中操作二进制 ArrayBuffer 时每个元素占用的字节大小是固定的,要么每个元素占用8位、16位或者32位。例如,如果你创建一个Int32Array类型数组视图来操作一个ArrayBuffer,那么每个元素都会占用32位(4个字节)。
-
而 DataView 对于 ArrayBuffer 的操作就显得更加灵活了,我们可以通过 DataView 从 ArrayBuffer 中读取或写入各种不同大小的元素。例如,你可以使用DataView的getUint8方法读取一个8位的无符号整数,或者使用setFloat32方法写入一个32位的浮点数。
-
所以,如果你需要在同一个ArrayBuffer中存储不同类型或不同大小的元素,那么DataView可能是更好的选择。如果你只需要存储同一种类型的元素,那么类型数组视图可能更简单、更直观。
2.14.3 类型化数组的api
- FileReader.prototype.readAsArrayBuffer(),方法开始读取指定Blob或File的内容
- fetch(),请求体可以是类型化数组或ArrayBuffer
- ImageData.data,是Uint8ClampedArray,表示一个一维数组,包含RGBA顺序的数据,整数值在0到255之间
2.14.4 示例
//开辟一块内存空间,空间大小两个字节
const buffer = new ArrayBuffer(2);//创建一个8位的视图
const int8View = new Int8Array(buffer);//给内存单元赋值
int8View[0] = 1;
int8View[1] = 8;console.log(int8View);//结果内存中存储为(十六进制) 01 08//创建一个16位的视图
const int16View = new Int16Array(buffer);console.log(int16View);//由于是16位视图。要取两个字节,但是计算时需要高低字节对调 08 01 = 2049
读成字符文本:
const buffer = new ArrayBuffer(8);
const uint8 = new Uint8Array(buffer);
// Data manually written here, but pretend it was already in the buffer
uint8.set([228, 189, 160, 229, 165, 189]);
const text = new TextDecoder().decode(uint8);
console.log(text); // "你好"
下面的代码使用String.fromCharCode()方法读取UTF-16文本:
const buffer = new ArrayBuffer(8);
const uint16 = new Uint16Array(buffer);
// Data manually written here, but pretend it was already in the buffer
uint16.set([0x4f60, 0x597d]);
const text = String.fromCharCode(...uint16);
console.log(text); // "你好"
转换为一般数组:
const typedArray = new Uint8Array([1, 2, 3, 4]);
const normalArray = Array.from(typedArray);//以及扩展语法:
const typedArray = new Uint8Array([1, 2, 3, 4]);
const normalArray = [...typedArray];
2.15 迭代器和生成器
迭代器和生成器带来了迭代的概念,并且提供一个方法去自定义for…of循环的行为
2.15.1 迭代器
在JavaScript中,迭代器是一个对象,它定义了一个序列,并可能在序列结束时返回一个值
function simpleRangeIterator(start, end, step = 1) {//首先函数要返回一个对象return {[Symbol.iterator]() {// 返回一个符合迭代器协议的对象return {// next() 方法,迭代器协议的一部next() {let result;if (start <= end) {result = { value: start, done: false };start+=2;return result;}return { value: "count", done: true };//done为true时,遍历结束},};},};
}const it = simpleRangeIterator(1, 20);
for (const iterator of it) {console.log(iterator);
}
2.15.2 生成器函数
生成器语法function*
该函数可以根据需要调用多次,每次都返回一个新的Generator。每个生成器只能迭代一次
使用生成器函数:
function* makeRangeIterator(start = 0, end = Infinity, step = 1) {console.log ("----------");let iterationCount = 0;for (let i = start; i < end; i += step) {iterationCount++;yield i;}return iterationCount;
}//生成器函数最初并不执行它们的代码(makeRangeIterator中)
const iter = makeRangeIterator(1, 10, 2);//它返回一种特殊类型的迭代器,称为Generatorlet result = iter.next();//调用生成器的next方法,生成器中的代码(makeRangeIterator中)开始执行,直到遇到yield就停止
while (!result.done) {console.log(result.value); // 1 3 5 7 9result = iter.next();
}console.log("Iterated over sequence of size:", result.value);
2.15.3 可迭代的
为了可迭代,对象必须实现@@iterator方法,这意味着该对象必须有一个属性Symbol.iterator
2.15.4 高级生成器
next()方法还接受一个值,该值可用于修改生成器的内部状态。传递给next()的值将被yield接收
2.16 元编程
Proxy和Reflect对象允许您拦截和定义基本语言操作的自定义行为(例如,属性查找、赋值、枚举、函数调用等)。在这两个对象的帮助下,您可以在JavaScript的元级别进行编程
2.16.1 代理
允许您拦截某些操作并实现自定义行为
//被代理对象
const target = {a:1
}//处理函数
const handler = {get(target, prop) {return prop in target ? target[prop] : "属性未定义";//代理后更改了属性不存在的行为},
};//设置一个代理
const p = new Proxy(target, handler);//意思就是p把target代理了,具有target的一切属性,但是在执行对象一些行为时,可以用处理函数捕捉,并处理后返回
console.log(p.a, p.b); //代理后: 1, "属性未定义"
console.log (target.a,target.b);//未代理: 1, "undefined"
术语:
- 处理器,包含陷阱的占位符对象
- 陷阱,提供属性访问的方法(这类似于操作系统中的陷阱概念),getter和setter等都是陷阱
- 目标,代理虚拟化的对象
- 不变性,当实现自定义操作时,必须保持某些基本操作的语义不变,这些不变的语义称为不变量。如果一个代理(Proxy)的处理程序(handler)违反了这些不变量,就会抛出一个
TypeError
2.16.2 处理器和陷阱
//被代理对象
const target = {a: 1,b: 2,
};/*** target:目标对象。* prop:要设置或获取到的属性名。* value:被设置的值。* receiver:最初被调用的对象。通常是代理本身。* prototype:需要设置的原型对象* fun:目标对象(被调用的函数)。* thisArg:被调用时的this值* argumentsList:被调用时的参数列表,是一个类数组对象* constructFun:目标对象(被调用的构造函数)* args:一个类数组对象,包含了调用构造函数时传递的参数。* */
//处理函数
const handler = {getPrototypeOf(target) {/*** 获取对象的原型* * 拦截以下操作:* Object.getPrototypeOf()* Reflect.getPrototypeOf()* __proto__* Object.prototype.isPrototypeOf()* instanceof*/return Object.getPrototypeOf(target);},setPrototypeOf(target, prototype) {/*** 设置对象的原型* * 拦截以下操作:* Object.setPrototypeOf()* Reflect.setPrototypeOf()*/return Object.setPrototypeOf(target, prototype);},isExtensible(target) {/*** 判断对象是否是可扩展的,可扩展就可所以添加新属性* * 拦截以下操作:* Object.isExtensible()* Reflect.isExtensible()*/return Reflect.isExtensible(target);},preventExtensions(target) {/*** 阻止对象可扩展* * 拦截以下操作:* Object.preventExtensions()* Reflect.preventExtensions()*/return Reflect.preventExtensions(target);},getOwnPropertyDescriptor(target, prop) {/*** 返回指定对象上一个自有属性对应的属性描述符* * 拦截以下操作:* Object.getOwnPropertyDescriptor()* Reflect.getOwnPropertyDescriptor()*/return Object.getOwnPropertyDescriptor(target, prop);},defineProperty(target, prop, descriptor) {/*** 直接在一个对象上定义新属性,或修改对象上的现有属性,并返回这个对象* * 拦截以下操作:* Object.defineProperty()* Reflect.defineProperty()*/return Object.defineProperty(target, prop, descriptor);},has(target, prop) {/*** 检查代理对象(Proxy)的属性是否存在* * 拦截以下操作:* foo in proxy* foo in Object.create(proxy)* Reflect.has()*/return Reflect.has(target, prop);},get(target, prop, receiver) {/*** 获得属性值* * 拦截以下操作:* proxy[foo]* proxy.bar* Object.create(proxy)[foo]* Reflect.get()*/return target[prop];},set(target, prop, value, receiver) {/*** 修改属性值* * 拦截以下操作:* proxy[foo] = bar* proxy.foo = bar* Object.create(proxy)[foo] = bar* Reflect.set()*/return (target[prop] = value);},deleteProperty(target, prop) {/*** 获取对象的原型* * 拦截以下操作:* delete proxy[foo]* delete proxy.foo* Reflect.deleteProperty()*/if (prop in target) {delete target[prop];console.log(`property removed: ${prop}`);}},ownKeys(target) {/*** 获取属性键的列表,包括不可枚举属性和Symbol类型的属性* * 拦截以下操作:* Object.getOwnPropertyNames()* Object.getOwnPropertySymbols()* Object.keys()* Reflect.ownKeys()*/return Reflect.ownKeys(target);},apply: function (fun, thisArg, argumentsList) {/*** 代理函数时,用于拦截函数的调用* * 拦截以下操作:* proxy(..args)* Function.prototype.apply()* Function.prototype.call()* Reflect.apply()*/return fun(argumentsList[0], argumentsList[1]) * 10;},construct(constructFun, args) {/*** 用于拦截构造函数的调用操作,当使用new操作符调用代理作为构造函数时,construct陷阱会被触发* * 拦截以下操作:* new proxy(...args)* Reflect.construct()*/return new constructFun(...args);},
};//设置一个代理
const p = new Proxy(target, handler);
2.16.3 可撤销的代理
Proxy.revocable()
可以创建可撤销的代理,这意味着可以使用revoke()函数撤销代理并关闭代理
//被代理对象
const target = {a: 1,b: 2,
};//处理函数
const handler = {get(target, prop, receiver) {return `属性值:${prop}=${target[prop]}`;},};const revocable = Proxy.revocable(target, handler)
const p1 = revocable.proxyconsole.log (p1.a);//属性值:a=1
revocable.revoke();
console.log (p1.a);//TypeError: Cannot perform 'get' on a proxy that has been revoked
2.16.4 反射
Reflect是一个内置对象,为可拦截的JavaScript操作提供方法
Reflect不是一个函数对象,使用时需要调用其静态方法
//一个更好的apply()函数
function fun(x){console.log (x,'------');
}
fun(1);//1 '------'
Function.prototype.apply.call(fun, undefined, [2]);//2 '------'
Reflect.apply(fun, undefined, [3]);//3 '------'
检查属性定义是否成功
Reflect
的方法与其它方式执行同样的操作相比,返回结果更为一致且可预测。
例如,Reflect.defineProperty()
在失败时返回false
,而Object.defineProperty()
在失败时抛出一个错误。
2.17 模块
2.17.1 基本文件结构
index.html
main.js
modules/canvas.jssquare.js
使用mjs后缀优势:
- 这有利于清晰,也就是说,它清楚地表明哪些文件是模块,哪些是常规JavaScript
- 它确保你的模块文件被运行时(如Node.js)和构建工具(如Babel)解析为模块
但是还是使用js后缀:
- 大多数服务器已经为.js文件设置了正确的MIME类型,但还没有为.mjs文件设置
- 有些工具可能永远不支持.mjs
<script type="module">
属性被用来标记所指向的是一个模块
2.17.2 导出模块属性
要访问模块特性,首先要做的就是导出它们。这是使用export语句完成的
使用它最简单的方法是将它放在你想要导出模块的任何项的前面
export const name = "square";
export function draw() {}
你可以导出函数。var,let,const变量和类。它们需要被放在顶层,如不能导出函数内部的函数
也可以整体导出,在模块文件的末尾使用一个export语句export { name, draw, reportArea, reportPerimeter };
2.17.3 导入模块属性
导入import { name, draw, reportArea, reportPerimeter } from "./modules/square.js";
导入的值是导出的特性的只读视图。与const变量类似,您不能重新分配已导入的变量,但您仍然可以修改对象值的属性
2.17.4 使用导入映射
导入映射是一个JSON对象,它允许开发人员在导入JavaScript模块时控制浏览器如何解析模块说明符
使用导入映射必须放在任何
目录结构:
/modules/a-old.jsa.js
/othermodules/app.js
index.html
main.js重点总结:
1、/test/othermodules/的写法有讲究不能少目录
2、scopes不是scope,注意别少s
3、映射文件 "a1": "./modules/a.js"
4、映射文件夹"a1/": "./modules/"
<script type="importmap">{"imports": {"a1": "./modules/a.js"},"scopes": {"/test/othermodules/": {"a1": "./modules/a-old.js"}}}
</script>
<!-- main里导入了a1 引用的是a.js-->
<script type="module" src="/test/main.js"></script>
<!-- app同样导入了a1,但是由于scopes的作用,引用的是a-old.js -->
<script type="module" src="/test/othermodules/app.js"></script>
2.17.5 在HTML中应用模块
<script type="module" src="main.js"></script>
注意type的设置
您只能在模块中使用import和export语句,而不能在常规脚本中使用。
2.17.6 模块和标准脚本的区别
-
在本地开发,直接打开(即使用file:// URL)带有模块的html,由于JavaScript模块的安全要求,你会遇到CORS错误。您需要通过服务器进行测试
报错:Access to script at 'file:///C:/js/main.js' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, isolated-app, chrome-extension, chrome, https, chrome-untrusted.
-
模块自动使用严格模式
-
在加载模块脚本时,不需要使用defer属性
意思就是模块的引入,可以在元素之前引用,因为自动defer
-
模块只执行一次,即使它们在多个
2.17.7 默认导出
默认导出的作用:
- 使模块提供一个默认的函数更容易
- 有助于CommonJS和AMD之间的交互。
需要把一个函数默认导出使用如下方式:
- 在模块文件的结尾使用,
export default functionName;
注意这里不需要花括号 - 或者在匿名函数前使用,
export default function (ctx) {}
默认函数的导入不需要花括号:
import functionName from "./modules/square.js";
注意这里不需要花括号
//module.js
export default function(){console.log ("默认导出函数执行");
}
//main.js
import fun from "./modules.js";
fun();
每个模块只允许一个默认导出
如果module没有默认导出函数,main中的fun不加花括号就报错
默认导出不能使用常规的导入方式,不需要加花括号,不需要重命名
2.17.8 重命名导入和导出
在导入和导出的语句中,可以使用as后跟新名字,去改变名字
导出重命名,导入使用新名字:
// inside module.js
export { function1 as newFunctionName, function2 as anotherNewFunctionName };// inside main.js
import { newFunctionName, anotherNewFunctionName } from "./modules/module.js";
导出原名称,导入重新命名:
// inside module.js
export { function1, function2 };// inside main.js
import {function1 as newFunctionName,function2 as anotherNewFunctionName,
} from "./modules/module.js";
2.17.9 创建模块对象
上面的方法可以工作,但它有点混乱和冗长。一个更好的解决方案是在模块对象中导入所有模块的特性
创建对象Module:import * as Module from "./modules/module.js";
这会获取Module .js内部可用的所有导出,并使它们作为对象Module的成员可用,从而有效地为其提供自己的命名空间
使用创建的对象:Module.function1();
2.17.10 模块中类的导出
正如我们前面所暗示的,您还可以导出和导入类;这是避免代码冲突的另一种选择,如果您已经以面向对象的风格编写了模块代码,则特别有用
模块中的类:
class Square {constructor(ctx, listId, length, x, y, color) {// …}draw() {// …}
}
export { Square };
导入类:import { Square } from "./modules/square.js";
使用类:
const square1 = new Square(myCanvas.ctx, myCanvas.listId, 50, 50, 100, "blue");
square1.draw();
2.17.12 聚合模块
其实就是,把多个js模块的导出,写在一个新的js模块中,使用的时候,使用新的js模块就可以,相当于中转,把多个模块聚合到一个模块引用。
示例:
//module1.js
export function fun1(){console.log ("fun1导出函数执行");
}
//module2.js
export function fun2(){console.log ("fun2导出函数执行");
}
//aggregate.js 聚合模块
export {fun1} from "./module1.js";
export {fun2} from "./module2.js";//main.js 这样可以使用一个对象module,访问多个模块的函数
import * as module from "./aggregate .js";
module.fun1();
module.fun2();//html中导入main.js就可以使用
2.17.11 动态模块加载
JavaScript模块最近新增的功能是动态模块加载。这允许您仅在需要时动态加载模块,而不必预先加载所有内容
允许您将import()作为函数调用,并将路径传递给函数。它返回一个Promise,它用一个模块对象来实现(参见创建模块对象),让你可以访问该对象的导出
import("./modules/myModule.js").then((module) => {// Do something with the module.
});
动态导入的另一个优点是它们总是可用的,即使在脚本环境中也是如此
因此,如果你的HTML中有一个现有的
<script>import("./modules/square.js").then((module) => {// Do something with the module.});// Other code that operates on the global scope and is not// ready to be refactored into modules yet.var btn = document.querySelector(".square");
</script>
2.17.12 顶层等待
await只能在默认导出中使用
使用如下方式导出同步的感觉
export default await colors;
2.17.13 导入声明被提升
在这种情况下,这意味着导入的值甚至在声明它们的地方之前就可以在模块的代码中使用,并且导入的模块的副作用在模块的其余代码开始运行之前就产生了。
尽管如此,将所有导入放在代码的顶部被认为是一种良好的实践,这使得分析依赖关系变得更加容易
2.17.14 循环导入
// -- a.js --
import { b } from "./b.js";// -- b.js --
import { a } from "./a.js";// Cycle:
// a.js ───> b.js
// ^ │
// └─────────┘
循环导入在异步时,是有可能成功的,但在同步的情况下,肯定报错
通常应该避免在项目中进行循环导入,因为这会使代码更容易出错。一些常见的循环消除技术是:
- 将两个模块合并为一个。
- 将共享代码移动到第三个模块中
- 将一些代码从一个模块移动到另一个模块。
2.17.15 创建“同构”模块
如果模块引用像window这样的全局变量,它可以在浏览器中运行,但会在Node.js服务器中抛出错误,因为那里没有window可用。
类似地,如果代码需要访问process才能正常工作,那么它只能在Node.js中使用。
三种方式实现代码同构:
- 把你的模块分成“核心”和“绑定”。对于“核心”,专注于纯JavaScript逻辑,如计算哈希,不需要任何DOM、网络、文件系统访问和公开实用程序函数。对于“绑定”部分,您可以读取和写入全局上下文
- 在使用特定全局变量之前检测它是否存在。例如,如果测试typeof window === “undefined”,您就知道您可能处于Node.js环境中,不应该读取DOM
- globalThis变量是一个全局对象,在任何环境中都可用,如果您希望在模块中读取或创建全局变量,它将非常有用
2.17.16 模块排错
-
“The server responded with a non-JavaScript MIME type”.
.mjs文件需要加载一个MIME类型的text/javascript(或其他javascript兼容的MIME类型,但推荐text/javascript)
-
CORS错误
如果您尝试在本地加载HTML文件(即使用file:// URL),由于JavaScript模块的安全要求,您将遇到CORS错误。您需要通过服务器进行测试
-
因为.mjs是一个非标准的文件扩展名,所以一些操作系统可能无法识别它,或者试图用其他东西替换它(替换为.mjs.js,然后隐藏扩展名,无法发现)
三、中级知识
3.1 客户端js框架
JavaScript框架是现代前端web开发的重要组成部分,为开发人员提供了久经考验的工具,用于构建可伸缩的交互式web应用程序。许多现代公司使用框架作为其工具的标准部分,因此许多前端开发工作现在都需要框架经验
问题:
- 我为什么要使用框架?它们为我解决了什么问题?
- 在选择框架时,我应该问哪些问题?我甚至需要使用框架吗?
- 框架有哪些特性?它们一般是如何工作的,框架对这些特性的实现又有何不同?
- 它们与“普通的”JavaScript或HTML有什么关系?
3.1.1 入门指南
3.1.1.1 介绍客户端框架
有哪些框架:
- Ember,2011年12月发布,较老的框架,比React和Vue等拥有更少的用户,其稳定性、社区支持和一些聪明的编码原则,它仍然享有相当大的人气
- Angular,2016年9月14日正式发布。Angular是一个开源的web应用框架,由Google的Angular团队以及一个由个人和公司组成的社区领导
- Vue,2014年发布了Vue。和AngularJS一样,Vue用自己的代码扩展HTML。除此之外,它主要依赖于现代的、标准的JavaScript。
- React,2013年发布了React。从技术上讲,React本身并不是一个框架;它是一个渲染UI组件的库
框架为什么存在:
在软件开发中,这种底层数据被称为state。
每次我们改变应用程序的state时,我们都需要更新UI来匹配
JavaScript框架的创建是为了让更新UI工作变得更容易——它们的存在是为了提供更好的开发人员体验。它们不会给JavaScript带来全新的功能;它们让你更容易地访问JavaScript的功能
静态站点生成器: Astro, Eleventy, Hugo, Jekyll, and Gatsby.
3.1.1.2 框架的主要特性
DSLs(Domain-Specific Language,领域特定语言)是为解决特定领域问题而设计的计算机程序语言
- JSX,代表JavaScript和XML,是JavaScript的扩展。它将类似html的语法引入JavaScript环境,单花括号
- Handlebars,Handlebars代码类似于HTML,但它可以选择从其他地方拉入数据,双花括号
- TypeScript,TypeScript是JavaScript的超集,也就是说它扩展了JavaScript。允许开发人员在代码上强制执行严格性
3.1.2 React教程
3.1.2.1 开始使用React
使用vite来构建应用
为了使用Vite,你需要安装Node.js。从Vite 5.0开始,至少需要Node版本18或更高版本,尽可能运行最新的长期支持(LTS)版本是个好主意
-
设置第一个app
- cd到需要的目录
- 根据模板创建项目
npm create vite@latest moz-todo-react -- --template react
- 进入目录安装包
cd moz-todo-react && npm install
- 运行一个本地服务器
npm run dev -- --open --port 3000
-
项目文件结构
moz-todo-react ├── README.md ├── index.html #Vite将代码注入到该文件中,以便浏览器可以运行它。在我们的教程中,您不需要编辑该文件,但是您应该更改该文件中<title>元素中的文本 ├── node_modules ├── package-lock.json ├── package.json ├── public #公共目录包含静态文件,这些文件将直接提供给浏览器,而不需要由Vite的构建工具进行处理 │ └── vite.svg ├── src #应用程序的源代码所在 │ ├── App.css │ ├── App.jsx │ ├── assets #包含您在浏览器中看到的React徽标 │ │ └── react.svg │ ├── index.css │ └── main.jsx └── vite.config.js
-
自定义dev脚本
不必在每次运行npm run dev – --open --port 3000时都传递–open和–port标志
更改文件
package.json
中的"scripts"
对象中的属性"dev": "vite"
更改为"dev": "vite --open --port 3000"
这样,每次运行npm run dev时,你的应用都会在浏览器中打开http://localhost:3000
-
App.jsx文件
- import部分,导入相关模块,引用css文件
- App()部分,使用大驼峰命名。小驼峰会报错
- export部分,使我们的App()函数对其他模块可用
-
main.jsx文件
- 这个文件是我们应用程序的入口点,组件在这里使用
3.1.3 Vue教程
3.1.3.1 开始使用Vue
-
安装
- 开发脚本
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
- 生产脚本
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
- 但是在复杂的项目,建议使用npm vue包
- 开发脚本
-
初始化一个新项目
- cd 到需要创建项目的上级目录
- 使用
npm create vue@latest
,创建项目,根据提示选择 - cd 到项目目录
- 安装相应的包
npm install
- 启动项目
npm run dev
-
项目文件结构
package.json #该文件包含项目的依赖项列表,以及一些元数据和eslint配置 yarn.lock #如果您选择yarn作为包管理器,这个文件将生成一个包含项目所需的所有依赖项和子依赖项的列表 jsconfig.json #这是一个用于Visual Studio Code的配置文件,它为你的项目结构提供了VS Code的上下文,并帮助自动完成 vite.config.js #这是用于在本地机器上构建和服务项目的Vite开发服务器的配置文件。Vite服务器监视源文件的更改,并可以在您进行更改时热重新加载项目 public #此目录包含在构建期间发布的静态资产 └── favicon.ico #这是你的应用程序的图标 index.html #Vue应用程序从这个HTML页面运行 src #该目录包含Vue应用程序的核心 ├── main.js #这是入口点。这个文件初始化Vue应用,表示应用程序应该附加到index.html文件中的哪个HTML元素。通常是您注册全局组件或附加Vue库的地方 ├── App.vue #这是Vue应用程序中的顶层组件 ├── components #该目录存放组件 └── assets #该目录用于存储CSS和图像等静态资产
如果您选择路由器,您还将有一个views目录
-
App.vue文件
- template部分,包含组件的所有标记结构和显示逻辑
- script部分,包含组件的所有非显示逻辑
- style部分,是为组件编写CSS的地方
3.1.3.2 创建第一个Vue组件
-
创建一个组件
- 创建组件,创建一个vue文件,里面的template标签里写上html
- 导入组件,在APP.vue中script标签中用import导入组件
- 注册组件,在APP.vue中script标签中导出对象中添加components属性。里面写上导入的组件名
- 使用组件,根据导入的组件名的驼峰形式,使用时标签名示例(组件名AaBbCc,则标签名为aa-bb-cc)
-
注册props,数据对象
//组件中 export default {//prop,使用{{}}绑定相应数据props: {label: { required: true, type: String },done: { default: false, type: Boolean },},//数据对象,使用 :属性名=”id“data() {return {isDone: this.done,id: "todo-" + nanoid(),};}, };
3.2 客户端web APIs
3.2.1 介绍web APIs
3.2.1.1 什么是APIs
应用程序编程接口(api)是编程语言中可用的结构,允许开发人员更容易地创建复杂的功能。
名词解释:
- JavaScript ,一种内置在浏览器中的高级脚本语言,允许您在网页/应用程序上实现功能
- 浏览器APIs,构建到浏览器中,它位于JavaScript语言之上,允许您更轻松地实现功能
- 第三方APIs,构建到第三方平台(例如Disqus, Facebook),允许您在自己的网页中使用这些平台的一些功能(例如,在网页上显示您的Disqus评论)。
- JavaScript 库,通常是一个或多个包含自定义函数的JavaScript文件
- JavaScript 框架,
库和框架的区别:
库和框架之间的关键区别在于“控制反转”。当从库中调用方法时,开发人员处于控制地位。对于框架,控制是颠倒的:框架调用开发人员的代码。
3.2.2 操作文档APIs
3.2.3 从服务器获取数据APIs
3.2.4 第三方APIs
3.2.5 画图APIs
3.2.6 视频和音频APIs
3.2.7 客户端存储APIs
3.3 语言概述
3.4 js数据结构
3.5 相等、比较和相同
3.6 属性的可枚举性和所有权
3.7 闭包
四、高级知识
4.1 继承和原型链
someObject.[[Prototype]]
指示对象的原型,为内部属性,不能直接访问
等价于访问器__proto__
(为了统一,避免使用这种形式)
实际访问使用Object.getPrototypeOf()
和Object.setPrototypeOf()
函数
函数的
Object.getPrototypeOf(new Box()) === Box.prototype
类是构造函数之上的语法糖,意味着还可以使用Box.prototype
来改变实例的行为
使用函数实现多个原型链:
function Base() {}
function Derived() {}
// Set the `[[Prototype]]` of `Derived.prototype`
// to `Base.prototype`
Object.setPrototypeOf(Derived.prototype, Base.prototype);const obj = new Derived();
// obj ---> Derived.prototype ---> Base.prototype ---> Object.prototype ---> null
等价于使用类实现:
class Base {}
class Derived extends Base {}const obj = new Derived();
// obj ---> Derived.prototype ---> Base.prototype ---> Object.prototype ---> null
一个函数在js中默认有 一个prototype属性,但是箭头函数没有默认的
function doSomething() {}
console.log(doSomething.prototype);
const doSomethingFromArrowFunction = () => {};
console.log(doSomethingFromArrowFunction.prototype);//undefined
4.2 内存管理
4.3 事件循环
五、内置对象
5.1 值属性
这些全局属性返回一个简单的值。它们没有属性或方法
- globalThis,是一个跨环境的全局对象引用,在浏览器端就时window
- Infinity,Infinity全局属性是一个表示无穷大的数值
- NaN,全局属性是一个表示非数字的值
- undefined,全局属性表示原始值undefined
5.2 函数属性
这些全局函数(全局调用的函数,而不是在对象上调用的函数)直接将结果返回给调用者。
- eval(),表示把字符串当做JavaScript代码执行,并返回其完成值。源代码被解析为脚本。
- isFinite(),确定一个值是否为有限值,如果需要,首先将值转换为数字
- isNaN(),确定一个值是否是NaN,如果需要,首先将值转换为数字
- parseFloat(),解析字符串参数并返回一个浮点数
- parseInt(),解析字符串参数并返回指定进制的整数。
- decodeURI(),由encodeURI()或类似例程创建的统一资源标识符(URI)进行解码
- decodeURIComponent(),对以前由encodeURIComponent()或类似例程创建的统一资源标识符(URI)组件进行解码
- encodeURI(),通过用表示字符的UTF-8编码的一个、两个、三个或四个转义序列替换特定字符的每个实例来编码URI(对于由两个代理字符组成的字符,只有四个转义序列)。与encodeURIComponent()相比,该函数编码的字符更少,保留了URI语法的一部分。
- encodeURIComponent(),与encodeURI()相比,这个函数编码更多的字符,包括那些属于URI语法一部分的字符。
5.3 基本对象
这些对象表示基本的语言结构
5.3.1 Object
表示JavaScript的一种数据类型。它用于存储各种键集合和更复杂的实体。
对象可以使用Object()构造函数或对象初始化器/字面量语法创建
5.3.1.1 静态方法
-
Object.assign()
object. assign()静态方法将所有可枚举的自身属性从一个或多个源对象复制到目标对象。它返回修改后的目标对象
如果目标对象中的属性具有相同的键,则目标对象中的属性将被源中的属性覆盖。后来的源的属性会覆盖以前的
object. assign()方法只将可枚举和自己的属性从源对象复制到目标对象
原型链上的属性和不可枚举属性不能被复制
//克隆对象(浅克隆) const copy = Object.assign({}, obj);//如果源值是对对象的引用,则它只复制该引用值 //深度克隆 const obj4 = structuredClone(obj3); //合并对象 const obj = Object.assign(o1, o2, o3);//当对象中有相同属性名时,则会覆盖 //原始类型将被包装成对象 const obj = Object.assign({}, "abc");//字符串’abc‘将被包装成对象{0:’a‘,1:’b‘,2:’c‘}
-
Object.create()
创建一个新对象,使用一个现有对象作为新创建对象的原型
语法:
Object.create(proto) Object.create(proto, propertiesObject)
-
Object.defineProperties()
直接在对象上定义新的或修改现有的属性,并返回该对象
语法:
Object.defineProperties(obj, props)
-
Object.defineProperty()
直接在对象上定义新属性,或修改对象上的现有属性,并返回该对象
语法:
Object.defineProperty(obj, prop, descriptor)
-
Object.entries()
返回给定对象自己的可枚举字符串键属性键值对的数组
语法:
Object.entries(obj)
例如:{a:2,b:3}返回[[”a“,2],[“b”,3]]
-
Object.freeze()
冻结一个对象,冻结对象可以防止扩展,并使现有属性不可写和不可配置
-
Object.fromEntries()
将键值对列表转换为对象。和Object.entries()功能相反
-
Object.getOwnPropertyDescriptor()
返回一个对象,该对象描述给定对象(即直接出现在对象上而不在对象的原型链中)上的特定属性的配置
语法:
Object.getOwnPropertyDescriptor(obj, prop)
-
Object.getOwnPropertyDescriptors()
返回给定对象的所有自己的属性描述符
语法:
Object.getOwnPropertyDescriptors(obj)
-
Object.getOwnPropertyNames()
返回直接在给定对象中找到的所有属性(包括非枚举属性,除了那些使用Symbol的属性)的数组
语法:
Object.getOwnPropertyNames(obj)
-
Object.getOwnPropertySymbols()
返回直接在给定对象上找到的所有Symbol属性的数组
语法:
Object.getOwnPropertySymbols(obj)
-
Object.getPrototypeOf()
返回指定对象的原型(即内部[[prototype]]属性的值)
语法:
Object.getPrototypeOf(obj)
-
Object.groupBy()
根据所提供的回调函数返回的字符串值对给定可迭代对象的元素进行分组
语法:
Object.groupBy(items, callbackFn)
返回对象和原始可迭代对象中的元素是相同的(不是深度拷贝)。更改元素的内部结构将反映在原始可迭代对象和返回对象中
-
Object.hasOwn()
如果指定的对象具有指定的属性作为自己的属性,则object . hasown()静态方法返回true。如果属性是继承的,或者不存在,则该方法返回false。
语法:
Object.hasOwn(obj, prop)
-
Object.is()
确定两个值是否相同
语法:
Object.is(value1, value2)
-
Object.isExtensible()
确定对象是否可扩展(是否可以向其添加新属性)
语法:
Object.isExtensible(obj)
-
Object.isFrozen()
确定对象是否被冻结。
语法:
Object.isFrozen(obj)
-
Object.isSealed()
确定对象是否被密封。
如果一个对象是不可扩展的,并且它的所有属性都是不可配置的,因此是不可移动的(但不一定是不可写的),那么这个对象就是密封的。
-
Object.keys()
返回一个由给定对象自己的可枚举字符串键属性名组成的数组
-
Object.preventExtensions()
防止新属性被添加到对象中(也就是说,防止将来对对象进行扩展)。它还可以防止对象的原型被重新分配
语法:
Object.preventExtensions(obj)
-
Object.seal()
密封对象
封闭对象可以防止扩展,并使现有属性不可配置。密封对象具有一组固定的属性:不能添加新属性,不能删除现有属性,不能更改其枚举性和可配置性,不能重新分配其原型。
只要现有属性的值是可写的,它们仍然可以被更改
-
Object.setPrototypeOf()
指定对象的原型(即内部的[[prototype]]属性)设置为另一个对象或null
语法:
Object.setPrototypeOf(obj, prototype)
-
Object.values()
返回给定对象自己的可枚举字符串键属性值的数组
5.3.1.2 实例方法
-
Object.prototype.hasOwnProperty()
返回一个布尔值,指示该对象是否具有指定的属性作为其自己的属性(而不是继承它)
在支持Object.hasOwn()的浏览器中,建议使用它,而不是hasOwnProperty()
-
Object.prototype.isPrototypeOf()
检查这个对象是否存在于另一个对象的原型链中
-
Object.prototype.propertyIsEnumerable()
返回一个布尔值,指示指定的属性是否是该对象的可枚举自身属性。
-
Object.prototype.toLocaleString()
返回一个表示该对象的字符串。此方法将被派生对象覆盖,以实现特定于区域设置的目的
这个方法常用于格式化时间、日期、数值等
-
Object.prototype.toString()
返回一个表示该对象的字符串。此方法将被用于自定义类型强制转换逻辑的派生对象覆盖
-
Object.prototype.valueOf()
将this值转换为对象。此方法将被用于自定义类型转换逻辑的派生对象覆盖。
对于基本类型的包装可以使用valueOf还原为基本类型
对象在进行算数运算时,会自动调用valueOf
5.3.1.3 实例属性
Object.prototype.constructor
5.3.2 Function
函数提供方法。在JavaScript中,每个函数实际上都是一个Function对象
5.3.2.1 构造方法
new Function(functionBody)
new Function(arg1, functionBody)
5.3.2.2 实例方法
-
Function.prototype.apply()
使用给定的this值和作为数组(或类似数组的对象)提供的参数调用此函数
语法:
apply(thisArg, argsArray)
示例:
function fun(x,y){//fun.apply(obj1,[2,4])//this为obj1,数组中的参数传递给函数console.log (this.e + x + y); }const obj1 = {e:123} const obj2 = {e:1234}fun.apply(obj1,[2,4]) fun.apply(obj2,[5,9])
-
Function.prototype.bind()
创建一个新函数,当调用该函数时,将this关键字设置为提供的值,并在调用新函数时提供的任何参数之前提供给定的参数序列
语法:
bind(thisArg, arg1, arg2)
示例:
function fun(x,y){console.log (this.e+x+y); } const obj1 = {e:1} const fun2 = fun.bind(obj1,2,4)//返回函数,不会立即执行 fun2();
-
Function.prototype.call()
使用给定的this值和单独提供的参数调用此函数
语法:
call(thisArg, arg1, arg2)
示例:
function fun(x,y){console.log (this.e+x+y); } const obj1 = {e:1} fun.call(obj1,2,4)//和bind一样,只不过函数直接执行了
-
Function.prototype.toString()
返回一个表示此函数源代码的字符串
-
Function.prototype[@@hasInstance]()
确定一个对象是否是构造函数的实例
@@hasInstance等价于Symbol.hasInstance
function fun(x,y){console.log (x+y); } const ob = new fun(2,3) const s = fun[Symbol.hasInstance](ob)//等价于ob instanceof fun console.log (s);//true
5.3.2.3 实例属性
-
length
指示函数期望的参数数量。
-
name
表示创建时指定的函数名,对于匿名创建的函数,它可以是anonymous或"(一个空字符串)"
-
prototype
当函数作为带有new操作符的构造函数使用时,使用Function实例的prototype data属性。它将成为新对象的原型
5.3.3 Boolean
布尔对象表示一个真值:true或false。
若要将非布尔值转换为布尔值,请使用boolean作为函数或使用双NOT操作符。不要对new使用Boolean()构造函数。
因为,new出来的不管是true还是false,布尔对象在条件语句中就是true
const bad = new Boolean(2<1)console.log (bad);//Boolean {false}
if(bad){console.log ('---------');//实际会输出
}
布尔强制转换:
- undefined转换为
false
. - null转换为
false
. 0
,-0
, 和NaN
转换为false
; 其他数值转换为true
.0n
转换为false
; 其他BigInts转换为true
.""
转换为false
; 其他字符串转换为true
.- Symbols转换为
true
. - 所有对象转换为
true
.
5.3.4 Symbol
新增的基本数据类型
Symbol通常用于向对象添加唯一属性键,这些键不会与任何其他代码可能添加到对象的键发生冲突,并且隐藏于其他代码通常用于访问对象的任何机制之外。
每次Symbol.for(“key”)调用都会为给定的"key"值返回相同的Symbol
当Symbol.for(“key”)被调用时,如果在全局Symbol注册表中可以找到具有给定键的Symbol,则返回该Symbol。否则,将创建一个新的Symbol,将其添加到给定键下的全局Symbol注册表中,并返回。
方法Symbol.for(tokenString)接受一个字符串键并从注册表返回一个符号值
而Symbol. keyfor(symbolValue)接受一个符号值并返回与之对应的字符串键。每一个都是另一个的倒数
5.4 错误对象
错误对象是基本对象的一种特殊类型。它们包括基本的错误类型,以及一些专门的错误类型
错误的类型:
-
AggregateError
当需要将多个错误包装在一个错误中时,AggregateError对象表示错误
try {throw new AggregateError([new Error("some error"),new Error("some error222")], "Hello"); } catch (e) {console.log(e instanceof AggregateError); // trueconsole.log(e.message); // "Hello"console.log(e.name); // "AggregateError"console.log(e.errors); // [ Error: "some error" ] }
-
EvalError
表示关于全局eval()函数的错误
try {throw new EvalError("Hello"); } catch (e) {console.log(e instanceof EvalError); // trueconsole.log(e.message); // "Hello"console.log(e.name); // "EvalError"console.log(e.stack); // Stack of the error }
-
RangeError
当一个值不在允许的值的集合或范围中时出现错误
-
ReferenceError
表示在引用当前作用域中不存在(或尚未初始化)的变量时发生错误。
-
SyntaxError
表示在尝试解释语法上无效的代码时发生错误。当JavaScript引擎在解析代码时遇到不符合语言语法的令牌或令牌顺序时就会抛出
-
TypeError
表示无法执行操作时的错误,通常(但不限于)当值不是预期类型时
-
URIError
当以错误的方式使用全局URI处理函数时
5.5 数值和日期
这些是表示数字、日期和数学计算的基本对象。
5.5.1 Number
5.5.1.1 静态属性
-
Number.EPSILON
它定义了 JavaScript 中能表示的最小正数,也就是说,它是 JavaScript 可以区分的两个浮点数之间的最小差值
浮点数永远都不要使用
===
比较大小在进行浮点数比较时,
Number.EPSILON
可用来设定一个误差范围,以避免因浮点数精度问题导致的比较不准确。 -
Number.MAX_SAFE_INTEGER
表示JavaScript中的最大安全整数
-
Number.MAX_VALUE
表示JavaScript中可表示的最大数值
-
Number.MIN_SAFE_INTEGER
表示JavaScript中的最小安全整数
-
Number.MIN_VALUE
表示JavaScript中可表示的最小数值
-
Number.NaN
表示不是一个数值
-
Number.NEGATIVE_INFINITY
表示负无穷大值
-
Number.POSITIVE_INFINITY
表示正无穷大的值
5.5.1.2 静态方法
-
Number.isFinite()
它检查给定的值是否是一个数字,并且这个数字既不是正无穷大,也不是负无穷大,也不是NaN。
-
Number.isInteger()
确定传递的值是否是整数。
-
Number.isNaN()
定传递的值是否是数值NaN
-
Number.isSafeInteger()
定所提供的值是否是一个安全整数
-
Number.parseFloat()
解析一个参数并返回一个浮点数。如果不能从参数中解析出一个数字,则返回NaN
-
Number.parseInt()
解析一个参数并返回一个整数数。如果不能从参数中解析出一个数字,则返回NaN
5.5.1.3 实例方法
-
Number.prototype.toExponential()
返回一个以指数表示法表示该数字的字符串
-
Number.prototype.toFixed()
用于将数字格式化为指定小数位数的字符串,会进行四舍五入
关注的是小数的位数
-
Number.prototype.toLocaleString()
-
Number.prototype.toPrecision()
返回一个表示该数字的指定精度的字符串
关注的是整体的位数
-
Number.prototype.toString()
-
Number.prototype.valueOf()
5.5.2 BigInt
是一个大数,通过在整数字面值的末尾添加n,或通过调用BigInt()函数(不带new操作符)并给它一个整数值或字符串值来创建
5.5.2.1 静态方法
-
BigInt.asIntN()
将一个BigInt值压缩为一个带符号整数值,并返回该值
-
BigInt.asUintN()
将一个BigInt值压缩为一个无符号整型值,并返回该值
5.5.2.2 实例方法
- BigInt.prototype.toLocaleString()
- BigInt.prototype.toString()
- BigInt.prototype.valueOf()
5.5.3 Math
5.5.4 Date
5.6 文本处理
这些对象表示字符串并支持对它们进行操作。
5.6.1 String
5.6.1.1 静态方法
-
String.fromCharCode()
返回一个由指定的UTF-16编码单元序列创建的字符串。
参数为0到65535之间的数字(0xFFFF),表示UTF-16编码单元
-
String.fromCodePoint()
返回一个由指定代码点序列创建的字符串。
参数为一个介于0和0x10FFFF(包括)之间的整数,表示Unicode码点
-
String.raw()
用于获取一个模板字符串的原始字符串形式。
5.6.1.2 实例方法
-
String.prototype[@@iterator]()
它返回一个字符串迭代器对象,该对象以单个字符串的形式生成字符串值的Unicode码点
const iterator = "这是一个字符串"[Symbol.iterator](); while(!(temp = iterator.next()).done){console.log (temp.value); }
-
String.prototype.at()
接受一个整数值,并返回一个由位于指定偏移量的单个UTF-16代码单元组成的新String。这个方法允许正整数和负整数。
负整数从最后一个字符串字符开始倒数。
超出索引返回"undefined"
-
String.prototype.charAt()
接受一个正整数值,并返回一个由位于指定偏移量的单个UTF-16代码单元组成的新String。这个方法允许正整数
超出索引返回""
-
String.prototype.charCodeAt()
字符串值的charCodeAt()方法返回一个介于0到65535之间的整数,表示给定索引处的UTF-16代码单元
-
String.prototype.codePointAt()
返回一个非负整数,即从给定索引开始的字符的Unicode码点值
-
String.prototype.concat()
将字符串参数连接到此字符串并返回一个新字符串
console.log(str2.concat(', ', str1));
-
String.prototype.endsWith()
确定字符串是否以该字符串的字符结束,并根据需要返回true或false
endsWith(searchString, endPosition)
-
String.prototype.includes()
执行区分大小写的搜索,以确定给定字符串是否可以在此字符串中找到,并根据情况返回true或false
-
String.prototype.indexOf()
搜索此字符串并返回指定子字符串第一次出现的索引
-
String.prototype.isWellFormed()
返回一个布尔值,指示该字符串是否包含任何lone surrogates
字符在0xD800-0xDFFF范围的为lone surrogates
-
String.prototype.lastIndexOf()
搜索该字符串并返回指定子字符串最后出现的索引。它接受一个可选的起始位置,并返回指定子字符串在小于或等于指定数字的索引处的最后一次出现
-
String.prototype.localeCompare()
比较两个字符串的前后
-
String.prototype.match()
正则表达式匹配,返回匹配结果为数组
-
String.prototype.matchAll()
返回一个迭代器,其中包含与正则表达式匹配的字符串的所有结果,包括捕获组。
-
String.prototype.normalize()
返回该字符串的Unicode规范化形式。
//有些字符,可以有两个表示形式,第一种直接单个字符,第二种为n+~两个字符来表示 const string1 = "\u00F1"; // ñ const string2 = "\u006E\u0303"; // ñconsole.log(string1 === string2); // false console.log(string1.length); // 1 console.log(string2.length); // 2string1 = string1.normalize("NFD");//在这种形式中,组合字符序列被拆分为基本字符和单独的组合重音符号 string2 = string2.normalize("NFD"); console.log(string1 === string2); // true console.log(string1.length); // 2 console.log(string2.length); // 2string1 = string1.normalize("NFC");//在这种形式中,组合字符序列被最大限度地转换为单个字符 string2 = string2.normalize("NFC"); console.log(string1 === string2); // true console.log(string1.length); // 1 console.log(string2.length); // 1
-
String.prototype.padEnd()
在末尾,使用给定的字符串填充字符串到给定的长度
语法:
padEnd(填充到的总长度, 填充字符)
-
String.prototype.padStart()
在开始,使用给定的字符串填充字符串到给定的长度
语法:
padEnd(填充到的总长度, 填充字符)
-
String.prototype.repeat()
构造并返回一个新字符串,其中包含该字符串的指定数量的连接在一起的副本
-
String.prototype.replace()
字符串替换,使用replacement替换pattern
pattern:字符串或正则
replacement:字符串或函数
语法:
replace(pattern, replacement)
-
String.prototype.replaceAll()
可以替换多个
-
String.prototype.search()
执行正则表达式与该字符串之间的匹配搜索,返回字符串中第一个匹配的索引。
-
String.prototype.slice()
提取该字符串的一部分,并将其作为新字符串返回,而不修改原始字符串。
-
String.prototype.split()
接受一个模式,并通过搜索该模式将该字符串划分为有序的子字符串列表,将这些子字符串放入数组,并返回该数组。
-
String.prototype.startsWith()
定该字符串是否以指定字符串的字符开头,并根据需要返回true或false。
-
String.prototype.substring()
返回从开始索引到不包括结束索引的字符串部分,如果没有提供结束索引,则返回到字符串的末尾。
-
String.prototype.toLocaleLowerCase()
返回根据任何特定于语言环境的大小写映射转换为小写的字符串。
-
String.prototype.toLocaleUpperCase()
根据任何特定于语言环境的大小写映射,返回转换为大写的字符串。
-
String.prototype.toLowerCase()
返回转换为小写的字符串。
-
String.prototype.toString()
返回字符串对象的,字符串值
-
String.prototype.toUpperCase()
返回转换为大写的字符串。
-
String.prototype.toWellFormed()
回一个字符串,其中该字符串的所有单独代理都被Unicode替换字符U+FFFD替换。
-
String.prototype.trim()
从字符串的两端删除空白,并返回一个新字符串,而不修改原始字符串。
-
String.prototype.trimEnd()
字符串末尾删除空白,并返回一个新字符串,而不修改原始字符串
-
String.prototype.trimStart()
字符串值的trimStart()方法从字符串的开头删除空白,并返回一个新字符串,而不修改原始字符串
-
String.prototype.valueOf()
返回这个字符串值。
对象类型转换为基本类型
5.6.1.3 实例属性
-
String: length
字符串值的length data属性以UTF-16编码单位表示字符串的长度
5.6.2 RegExp
正则表达式创建方式:
const re = /ab+c/i; // literal notation
const re = new RegExp("ab+c", "i"); // constructor with string pattern as first argument
const re = new RegExp(/ab+c/, "i"); // constructor with regular expression literal as first argument
在使用正则表达式之前,必须对它们进行编译
正则表达式的flag就是上面代码的i位置处的字符,也可能是其他字符
5.6.2.1 实例属性
-
RegExp.prototype.dotAll
返回该正则表达式是否使用s标志。(s标志:允许.匹配换行符。)
-
RegExp.prototype.flags
返回这个正则表达式的标志。
-
RegExp.prototype.global
返回是否将g标志用于此正则表达式。(g标志:全局搜索)
-
RegExp.prototype.hasIndices
返回d标志是否与此正则表达式一起使用。(d标志:为子字符串匹配生成索引)
-
RegExp.prototype.ignoreCase
返回i标志是否与此正则表达式一起使用。(i标志:不区分大小写的搜索)
-
RegExp: lastIndex
指定开始下一次匹配的索引。
-
RegExp.prototype.multiline
返回m标志是否用于此正则表达式。(m标志:允许^和$在换行符旁边匹配。)
-
RegExp.prototype.source
返回一个字符串,其中包含正则表达式的源文本,没有两边的两个正斜杠或任何标志。
-
RegExp.prototype.sticky
返回该正则表达式是否使用了y标志。
-
RegExp.prototype.unicode(y标志:执行从目标字符串的当前位置开始匹配的“粘性”搜索)
返回该正则表达式是否使用u标志。(u标志:将模式视为Unicode码点序列)
-
RegExp.prototype.unicodeSets
回该正则表达式是否使用v标志。(v标志:升级到具有更多Unicode功能的u模式。)
5.6.2.2 实例方法
-
RegExp.prototype.exec()
使用此正则表达式执行搜索,以查找指定字符串中的匹配项,并返回结果数组或null。
返回一个数组并更新正则表达式对象的lastIndex属性。返回的数组将匹配的文本作为第一项,然后为匹配文本的每个捕获组提供一项,并且还有其他项
-
RegExp.prototype.test()
使用此正则表达式执行搜索,以查找正则表达式与指定字符串之间的匹配。如果有匹配,返回true;否则错误。
-
RegExp.prototype.toString()
返回一个表示该正则表达式的字符串
5.7 索引集合
这些对象表示按索引值排序的数据集合。这包括(类型化)数组和类数组结构。
5.7.1 Array
5.7.1.1 静态方法
-
Array.from()
从一个可迭代对象或类数组对象创建一个新的、浅复制的Array实例。
console.log(Array.from([1, 2, 3], function(element,index){console.log (element,index);return element }));
array. from()从不创建稀疏数组。如果arrayLike对象缺少一些索引属性,则它们在新数组中变为未定义。
-
Array.fromAsync()
从异步可迭代对象、可迭代对象或类数组对象创建一个新的、浅复制的Array实例。
-
Array.isArray()
确定传递的值是否是Array。
-
Array.of()
从可变数量的参数创建一个新的Array实例,而不考虑参数的数量或类型。
5.7.1.2 实例方法
-
Array.prototype.at()
接受一个整数值并返回该索引处的项,允许正整数和负整数。负整数从数组中的最后一项开始计数
语法:
at(index)
-
Array.prototype.concat()
用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
语法:
concat(value1,..., value2)
-
Array.prototype.copyWithin()
将此数组的一部分浅层复制到同一数组中的另一个位置,并返回该数组,而不修改其长度。
语法:
copyWithin(target, start, end)
//将start到end位置的值,复制到target位置 -
Array.prototype.entries()
返回一个新的数组迭代器对象,其中包含数组中每个索引的键/值对。
语法:
entries()
-
Array.prototype.every(fun)
测试数组中的所有元素是否通过所提供的函数实现的测试。它返回一个布尔值
通俗,将数组种的每一项,传递到函数中,返回值全是true时,every()返回true
语法:
every(callbackFn)
,every(callbackFn, thisArg)
-
Array.prototype.fill()
将数组中索引范围内的所有元素更改为静态值。它返回修改后的数组。
通俗就是填充数组
语法:
fill(value)
,fill(value, start)
,fill(value, start, end)
-
Array.prototype.filter()
创建给定数组部分的浅拷贝,过滤后只保留给定数组中通过所提供函数实现的测试的元素。
通俗,就是根据回调函数过滤数组元素,返回新数组
语法:
filter(callbackFn)
,filter(callbackFn, thisArg)
回调函数三个参数:
- element,每个元素
- index,元素索引
- array,原始数组
-
Array.prototype.find()
返回所提供的数组中满足所提供的测试函数的第一个元素。如果没有值满足测试函数,则返回undefined。
注意只是找到第一个满足回调函数的元素,就停止了
语法:
find(callbackFn)
,find(callbackFn, thisArg)
-
Array.prototype.findIndex()
返回满足所提供的测试函数的数组中第一个元素的索引。如果没有元素满足测试函数,则返回-1。
和find类似,只是这个返回索引
-
Array.prototype.findLast()
以相反的顺序迭代数组,并返回满足所提供的测试函数的第一个元素的值。如果没有元素满足测试函数,则返回undefined。
和find类似,只是从末尾开始查找
-
Array.prototype.findLastIndex()
以相反的顺序迭代数组,并返回满足所提供的测试函数的第一个元素的索引。如果没有元素满足测试函数,则返回-1。
和find类似,只是这个从末尾开始返回索引
-
Array.prototype.flat()
创建一个新数组,其中所有子数组元素递归地连接到其中,直至指定的深度。
通俗,就是把一个数组种的子数组展开,展开的深度,由参数决定
语法:
flat(depth)
-
Array.prototype.flatMap()
返回一个新数组,该数组通过对数组的每个元素应用给定的回调函数形成,然后将结果平面化一级。
相当于应用map之后,再flat
-
Array.prototype.forEach()
对每个数组元素执行一次所提供的函数。
语法:
forEach(callbackFn)
,forEach(callbackFn, thisArg)
-
Array.prototype.includes()
确定数组是否在其条目中包含某个值,并根据需要返回true或false。
-
Array.prototype.indexOf()
返回在数组中可以找到给定元素的第一个索引,如果该元素不存在则返回-1。
-
Array.prototype.join()
通过连接该数组中的所有元素(用逗号或指定的分隔符字符串分隔)来创建并返回一个新字符串。如果数组只有一个项,则返回该项而不使用分隔符
-
Array.prototype.keys()
返回一个新的数组迭代器对象,其中包含数组中每个索引的键。
-
Array.prototype.lastIndexOf()
返回在数组中可以找到给定元素的最后一个索引,如果不存在则返回-1。从fromIndex开始,向后搜索数组。
-
Array.prototype.map()
创建一个新数组,其中填充对调用数组中的每个元素调用所提供函数的结果。
-
Array.prototype.pop()
数组中删除最后一个元素并返回该元素。这个方法改变数组的长度。
-
Array.prototype.push()
将指定的元素添加到数组的末尾,并返回数组的新长度
-
Array.prototype.reduce()
按顺序对数组的每个元素执行用户提供的“reducer”回调函数,传递对前一个元素计算的返回值。在数组的所有元素上运行reducer的最终结果是一个单一的值。
做数组元素的累加比较合适
-
Array.prototype.reduceRight()
对累加器和数组的每个值(从右到左)应用一个函数,以将其减少为单个值。
-
Array.prototype.reverse()
将数组就地反转并返回对同一数组的引用
-
Array.prototype.shift()
从数组中移除第一个元素并返回被移除的元素。这个方法改变数组的长度。
-
Array.prototype.slice()
将数组的一部分的浅拷贝返回到从开始到结束(不包括结束)选择的新数组对象中,其中start和end表示该数组中项的索引。原数组不能被修改
-
Array.prototype.some()
测试数组中是否至少有一个元素通过所提供的函数实现的测试。如果在数组中找到所提供函数为其返回true的元素,则返回true;否则返回false。它不会修改数组。
some和every的区别,some要求只有一个满足才返回true。every要求全部满足才返回true
-
Array.prototype.sort()
对数组的元素进行排序,并返回对已排序的同一数组的引用
-
Array.prototype.splice()
通过删除或替换现有元素和/或添加新元素来更改数组的内容
语法:
splice(start, deleteCount, item1, item2)
对原数组进行了更改,返回切下来的数组
-
Array.prototype.toLocaleString()
返回一个表示数组元素的字符串
-
Array.prototype.toReversed()
是reverse()方法的复制副本。它返回一个元素顺序颠倒的新数组。
返回新数组,不改变元素组
reverse()会改变原数组
-
Array.prototype.toSorted()
是sort()方法的复制版本。它返回一个元素按升序排序的新数组。
-
Array.prototype.toSpliced()
是splice()方法的复制版本。它返回一个新数组,其中在给定索引处删除和/或替换了一些元素
-
Array.prototype.toString()
返回一个表示指定数组及其元素的字符串。
-
Array.prototype.unshift()
将指定的元素添加到数组的开头,并返回数组的新长度。
-
Array.prototype.values()
返回一个新的数组迭代器对象,该对象迭代数组中每个项的值。
-
Array.prototype.with()
使用括号符号改变给定索引值的复制版本。它返回一个新数组,其中给定索引处的元素被给定值替换
5.7.2 TypedArray
typearray对象描述了底层二进制数据缓冲区的类似数组的视图
5.7.2.1 静态方法
-
TypedArray.from()
从一个类数组或可迭代对象创建一个新的类型化数组
-
TypedArray.of()
根据可变数量的参数创建一个新的类型化数组
5.7.2.2 实例属性
-
TypedArray.prototype.buffer
返回该类型数组所引用的ArrayBuffer或SharedArrayBuffer。
-
TypedArray.prototype.byteLength
返回该类型数组的长度(以字节为单位)。
-
TypedArray.prototype.byteOffset
返回该类型数组从其ArrayBuffer或SharedArrayBuffer开始的偏移量(以字节为单位)。
-
TypedArray.prototype.length
返回这个类型化数组的长度(以元素为单位)。
5.7.5.3 构造语法
new Uint8Array()
new Uint8Array(length)
new Uint8Array(typedArray)
new Uint8Array(object)new Uint8Array(buffer)
new Uint8Array(buffer, byteOffset)
new Uint8Array(buffer, byteOffset, length)
Type | Value Range | Size in bytes | 备注 |
---|---|---|---|
Int8Array | -128 to 127 | 1 | |
Uint8Array | 0 to 255 | 1 | 当元素超出255时,溢出 |
Uint8ClampedArray | 0 to 255 | 1 | 当元素超出255时,只显示255 |
Int16Array | -32768 to 32767 | 2 | |
Uint16Array | 0 to 65535 | 2 | |
Int32Array | -2147483648 to 2147483647 | 4 | |
Uint32Array | 0 to 4294967295 | 4 | |
Float16Array | -65504 to 65504 | 2 | |
Float32Array | -3.4e38 to 3.4e38 | 4 | |
Float64Array | -1.8e308 to 1.8e308 | 8 | |
BigInt64Array | -263 to 263 - 1 | 8 | |
BigUint64Array | 0 to 264 - 1 | 8 |
5.8 键集合
这些对象表示使用键的集合。可迭代集合(Map和Set)包含易于按插入顺序迭代的元素。
5.8.1 Map
5.8.1.1 静态方法
-
Map.groupBy()
使用提供的回调函数返回的值对给定可迭代对象的元素进行分组。最后返回的Map使用来自test函数的唯一值作为键,可用于获取每个组中的元素数组。
该方法主要用于对与对象相关联的元素进行分组,特别是当该对象可能随时间变化时。如果对象是不变的,则可以使用字符串表示它,并使用object . groupby()对元素进行分组。
5.8.1.2 实例方法
-
Map.prototype[@@iterator]()
返回可迭代对象
const iterator1 = map1[Symbol.iterator]();
-
Map.prototype.clear()
从该映射中删除所有元素。
-
Map.prototype.delete()
按键从该映射中删除指定的元素。
-
Map.prototype.entries()
返回一个新的映射迭代器对象,该对象按插入顺序包含此映射中每个元素的[key, value]对。
-
Map.prototype.forEach()
对该映射中的每个键/值对按插入顺序执行一次所提供的函数。
-
Map.prototype.get()
从这个映射返回指定的元素。
如果与提供的键相关联的值是一个对象,那么您将获得对该对象的引用,对该对象所做的任何更改都将有效地在Map对象中修改它。
-
Map.prototype.has()
返回一个布尔值,指示具有指定键的元素是否存在于此映射中
-
Map.prototype.keys()
返回一个新的映射迭代器对象,该对象按插入顺序包含此映射中每个元素的键。
-
Map.prototype.set()
使用指定的键和值在该映射中添加或更新一个条目。
-
Map.prototype.values()
返回一个新的映射迭代器对象,该对象按插入顺序包含该映射中每个元素的值。
5.8.1.3 实例属性
-
Map.prototype.size
返回该映射中元素的数量。
5.8.2 Set
5.8.2.1 实例方法
-
Set.prototype[@@iterator]()
它返回一个集合迭代器对象,该对象按插入顺序生成该集合的值。
-
Set.prototype.add()
将一个具有指定值的新元素插入到这个集合中,如果这个集合中没有具有相同值的元素
-
Set.prototype.clear()
从这个集合中删除所有元素。
-
Set.prototype.delete()
从这个集合中删除指定的值,如果它在集合中。
-
Set.prototype.difference()
接受一个集合,并返回一个新集合,其中包含该集合中的元素,但不在给定集合中。
A.difference(B),在A里,但不在B里
-
Set.prototype.entries()
返回一个新的Set迭代器对象,该对象包含该集合中每个元素的[value, value]数组,按插入顺序排列
对于Set对象,没有像Map对象那样的键。但是,为了使API类似于Map对象,每个条目在这里的键和值都具有相同的值,
因此返回一个数组[value, value]。
-
Set.prototype.forEach()
按插入顺序对该集合中的每个值执行一次所提供的函数
-
Set.prototype.has()
返回一个布尔值,指示具有指定值的元素是否存在于此集合中
-
Set.prototype.intersection()
接受一个集合,并返回一个包含该集合和给定集合元素的新集合。
A.intersection(B),在A里也在B里
-
Set.prototype.isDisjointFrom()
接受一个集合,并返回一个布尔值,指示该集合是否没有与给定集合相同的元素。
是否有交集
-
Set.prototype.isSubsetOf()
接受一个集合,并返回一个布尔值,表示该集合的所有元素是否都在给定集合中。
A.isSubsetOf(B),A是否是B的子集
-
Set.prototype.isSupersetOf()
接受一个集合,并返回一个布尔值,表示给定集合的所有元素是否都在这个集合中。
A.isSupersetOf(B),A是否是B的父集
-
Set.prototype.keys()
是values()方法的别名。
-
Set.prototype.symmetricDifference()
接受一个集合并返回一个新集合,其中的元素要么在这个集合中,要么在给定的集合中,但不同时在这两个集合中。
A.symmetricDifference(B),在A不在B,在B不在A
-
Set.prototype.union()
接受一个集合并返回一个新集合,其中包含在该集合和给定集合中的一个或两个元素。
A.union(B),A和B合并
-
Set.prototype.values()
返回一个新的Set迭代器对象,该对象按插入顺序包含该集合中每个元素的值。
5.8.2.2 实例属性
-
Set.prototype.size
返回这个集合中(唯一的)元素的个数
5.8.3 WeakMap
Map
和 WeakMap
都是 JavaScript 中用于存储键值对的数据结构,但它们之间存在一些重要的差异。
- 键的类型:
在Map
中,键可以是任何类型,包括对象、函数、基本类型等。而在WeakMap
中,键必须是对象。 - 引用关系:
在Map
中,一旦设置了键值对,即使你删除了对键的所有引用,这个键值对也会在Map
中保留。但在WeakMap
中,如果你删除了对键的所有引用,这个键值对会被自动从WeakMap
中删除。这是因为WeakMap
对键持有的是弱引用。 - 遍历和大小:
Map
有.size
属性和.keys()
,.values()
,.entries()
等方法,你可以使用这些方法获取Map
的大小和遍历Map
。但WeakMap
没有这些属性和方法,你不能获取WeakMap
的大小或遍历WeakMap
。 - 使用场景:
由于WeakMap
的这些特性,它通常用于在不影响垃圾回收的情况下向对象添加额外的信息。而Map
更常见,它可以用于存储各种类型的键值对,并且可以方便地遍历它们。
总的来说,虽然 Map
和 WeakMap
在某些方面相似,但由于它们的这些差异,它们在 JavaScript 中的应用场景是不同的。
5.8.4 WeakSet
Set
和 WeakSet
都是 JavaScript 中用于存储唯一值的数据结构,但它们有一些重要的区别:
- 元素类型:
在Set
中,你可以添加任何类型的元素,包括原始类型(如数字和字符串)和对象类型。而在WeakSet
中,你只能添加对象。 - 对元素的引用:
Set
对其元素持有的是强引用,这意味着只要Set
存在,它的元素就不会被垃圾回收机制回收。而WeakSet
对其元素持有的是弱引用,如果没有其他地方引用WeakSet
中的对象,这些对象就会被垃圾回收机制回收。 - 遍历和大小:
Set
有.size
属性和.values()
,.keys()
,.entries()
等方法,可以用来获取Set
的大小和遍历Set
。但WeakSet
没有这些属性和方法,你不能获取WeakSet
的大小或遍历WeakSet
。 - 使用场景:
由于WeakSet
的特性,它通常用于在不影响垃圾回收的情况下向对象添加一些额外的信息。而Set
更多的是用来存储和操作一组不重复的元素。
总之,Set
和 WeakSet
都有各自的特点和使用场景,需要根据具体需求来选择使用哪一个。
5.9 结构化数据
这些对象表示结构化数据缓冲区和使用JavaScript对象表示法(JSON)编码的数据,并与之交互。
5.9.1 ArrayBuffer
ArrayBuffer对象用于表示一个通用的原始二进制数据缓冲区
它是一个字节数组,在其他语言中通常称为“字节数组”。你不能直接操作ArrayBuffer的内容;相反,您可以创建一个类型化数组对象或以特定格式表示缓冲区的DataView对象,并使用它来读写缓冲区的内容。
5.9.1.1 静态方法
-
ArrayBuffer.isView()
确定传递的值是否是ArrayBuffer视图中的一个,例如类型化数组对象或DataView。
5.9.1.2 实例方法
-
ArrayBuffer.prototype.resize()
将ArrayBuffer的大小调整到指定的大小(以字节为单位)。
-
ArrayBuffer.prototype.slice()
返回一个新的ArrayBuffer,它的内容是这个ArrayBuffer的字节拷贝,从开始,包括,到结束,不包括。如果start或end为负值,则指向数组末尾的索引,而不是从头开始的索引
-
ArrayBuffer.prototype.transfer()
创建一个新的ArrayBuffer,其字节内容与这个缓冲区相同,然后分离这个缓冲区。
-
ArrayBuffer.prototype.transferToFixedLength()
创建一个新的不可调整大小的ArrayBuffer,其字节内容与此缓冲区相同,然后分离此缓冲区。
5.9.1.3 实例属性
-
ArrayBuffer.prototype.byteLength
返回该数组缓冲区的长度(以字节为单位)。
-
ArrayBuffer.prototype.detached
返回一个布尔值,表示该缓冲区是否已被分离(传输)。
-
ArrayBuffer.prototype.maxByteLength
属性返回该数组缓冲区可以调整到的最大长度(以字节为单位)。
-
ArrayBuffer.prototype.resizable
返回该数组缓冲区是否可以调整大小。
创建时,设置了 maxByteLength比byteLength长,才可以调整缓冲区
5.9.2 SharedArrayBuffer
用来表示一个通用的原始二进制数据缓冲区,类似于ArrayBuffer对象,但在某种程度上,它们可以用来在共享内存上创建视图
SharedArrayBuffer不是一个可转移对象,不像ArrayBuffer是可转移的
5.9.2.1 实例方法
-
SharedArrayBuffer.prototype.grow()
将SharedArrayBuffer增加到指定的大小(以字节为单位)。
-
SharedArrayBuffer.prototype.slice()
返回一个新的SharedArrayBuffer,它的内容是这个SharedArrayBuffer的字节拷贝,从开始,包括,到结束,不包括。如果start或end为负值,则指向数组末尾的索引,而不是从头开始的索引。
5.9.2.3 实例属性
-
SharedArrayBuffer.prototype.byteLength
返回这个SharedArrayBuffer的长度(以字节为单位)。
-
SharedArrayBuffer.prototype.growable
返回这个SharedArrayBuffer是否可以增长。
-
SharedArrayBuffer.prototype.maxByteLength
返回这个SharedArrayBuffer可以增长到的最大长度(以字节为单位)。
5.9.3 DataView
提供了一个低级接口,用于读写二进制ArrayBuffer中的多个数字类型,而不必关心平台的端序(高字节或低字节在前的问题)
new DataView(buffer, byteOffset, byteLength)
在JavaScript中,DataView
和TypedArray
都是用来操作二进制数据的接口,它们各有特点,适用于不同的场景。
DataView
:提供了一个更灵活的接口来读取和写入多种数字类型,可以自由设定字节序。如果你需要处理不同类型的数据,或者需要在大端和小端字节序之间进行切换,DataView
可能是更好的选择。TypedArray
:提供了一种在ArrayBuffer对象上读写特定数据类型的机制。它比DataView更快,因为数据始终保持相同的字节序,并且可以直接作为普通数组使用。如果你在处理大量的同一类型数据,或者不需要关心字节序,那么TypedArray
可能更适合。
所以,选择使用DataView还是TypedArray,主要取决于你的具体需求。
//内存开辟一段空间
const buffer = new ArrayBuffer(8);// 根据该空间创建一个DataView
const view1 = new DataView(buffer);
// 根据该空间创建一个DataView,该DataView从buffer的第二个字节开始往后4个字节的区域
const view2 = new DataView(buffer, 2, 4);
view1.setInt8(2, 42); // 把42放在该段空间的第二个位置console.log(view1);
console.log(view2);
5.9.4 Atomics
它是一种用于执行原子操作的静态方法集合,常常用在多线程编程中,以确保多个线程对共享内存区进行操作时的数据完整性。
5.9.4.1 静态方法
-
Atomics.add()
在数组中的给定位置加上给定值,并返回该位置的旧值。这个原子操作保证在修改后的值被写回之前不会发生其他写操作。
语法:
Atomics.add(typedArray, index, value)
-
Atomics.and()
对数组中给定位置的给定值进行位与运算,并返回该位置的旧值。这个原子操作保证在修改后的值被写回之前不会发生其他写操作。
语法:
Atomics.and(typedArray, index, value)
-
Atomics.compareExchange()
在数组中给定位置交换给定的替换值,如果给定的期望值等于旧值。它返回该位置的旧值,无论它是否等于期望值。这个原子操作保证在修改后的值被写回之前不会发生其他写操作
语法:
Atomics.compareExchange(typedArray, index, expectedValue, replacementValue)
-
Atomics.exchange()
交换数组中给定位置的给定值,并返回该位置的旧值。这个原子操作保证在读取旧值和写入新值之间不会发生其他写操作。
语法:
Atomics.exchange(typedArray, index, value)
-
Atomics.isLockFree()
用于确定Atomics方法在应用于具有给定元素字节大小的类型化数组时是使用锁还是原子硬件操作。如果给定的大小不是整型typearray类型的BYTES_PER_ELEMENT属性之一,则返回false。
-
Atomics.load()
返回数组中给定位置的值。
语法:
Atomics.load(typedArray, index)
-
Atomics.notify()
通知在等待队列中休眠的一些代理。
语法:
Atomics.notify(typedArray, index, count)
-
Atomics.or()
使用数组中给定位置上的给定值计算位或,并返回该位置上的旧值。这个原子操作保证在修改后的值被写回之前不会发生其他写操作。
语法:
Atomics.or(typedArray, index, value)
-
Atomics.store()
将给定值存储在数组中的给定位置并返回该值。
语法:
Atomics.store(typedArray, index, value)
-
Atomics.sub()
减去数组中给定位置的给定值,并返回该位置的旧值。这个原子操作保证在修改后的值被写回之前不会发生其他写操作。
语法:
Atomics.sub(typedArray, index, value)
-
Atomics.wait()
验证共享内存位置是否仍然包含给定值,如果是,则休眠,等待唤醒通知或超时。它返回一个字符串,要么是“ok”,要么是“not-equal”,要么是“timeout”。
-
Atomics.waitAsync()
在共享内存位置异步等待并返回一个Promise
与Atomics.wait()不同,waitAsync是非阻塞的,并且可以在主线程上使用。
-
Atomics.xor()
使用数组中给定位置上的给定值计算位异或,并返回该位置上的旧值。这个原子操作保证在修改后的值被写回之前不会发生其他写操作。
5.9.5 JSON
5.9.5.1 静态方法
-
JSON.parse()
解析JSON字符串,构造由字符串描述的JavaScript值或对象。可以提供一个可选的reviver函数,用于在返回结果对象之前对其执行转换。
语法:
JSON.parse(text)
,JSON.parse(text, reviver)
-
JSON.stringify()
将JavaScript值转换为JSON字符串,如果指定了替换函数,则可选地替换值;如果指定了替换数组,则可选地只包含指定的属性。
JSON.stringify(value) JSON.stringify(value, replacer) JSON.stringify(value, replacer, space)
5.10 管理内存
这些对象与垃圾收集机制交互。
5.10.1 WeakRef
WeakRef对象允许您保留对另一个对象的弱引用,而不会阻止该对象被垃圾收集
new WeakRef(target)
WeakRef实例的deref()方法返回该WeakRef的目标值,如果目标值已被垃圾收集,则未定义。
5.10.2 FinalizationRegistry
允许您在值被垃圾收集时请求回调。
let obj = {}//创建一个对象new FinalizationRegistry(callbackFn)
const registry = new FinalizationRegistry(callbackFn)//注册一个需要监听的目标对象obj
// register(target, heldValue)
// register(target, heldValue, unregisterToken)
// unregisterToken必须时对象,一般使用源对象就可以,不指定的话,无法注销
const sb = Symbol.for('13')
registry.register(obj,"我是回调传递的值heldValue",obj);
registry.unregister(obj)//此语句解绑,不监听垃圾回收
function callbackFn(heldValue){console.log (heldValue);//我是回调传递的值heldValueconsole.log ("垃圾回收了");
}// obj指向另一个对象,则手动内存回收时,会回收掉{}对象
obj = {b:"hui"}
5.11 控制抽象对象
控制抽象可以帮助构建代码,特别是异步代码(例如,不使用深度嵌套的回调)。
5.11.1 Iterator
它是所有其他迭代器类的超类
const arr = [1,2,3]
//创建一个迭代器,从现有的可迭代对象或迭代器对象创建一个Iterator实例
const i = Iterator.from(arr)
//创建一个迭代器
const i1 = arr[Symbol.iterator]()
console.log (i.next());
console.log (i1.next());
5.11.2 AsyncIterator
5.11.3 Promise
Promise对象表示异步操作的最终完成(或失败)及其结果值。
Promise的三种状态:
- pending: 初始状态,既没完成也不拒绝。
- fulfilled: 表示操作成功完成
- rejected: 意味着操作失败。
5.11.3.1 构造函数
new Promise(executor)
executor:这是一个函数,该函数由构造函数执行。它接收两个参数(resolveFunc和rejectFunc)
当通过new调用Promise构造函数时,它返回一个Promise对象
当函数resolveFunc或rejectFunc被调用时,promise对象将被解决。
注意,如果你调用resolveFunc或rejectFunc并传递另一个Promise对象作为参数,它可以说是“resolved”,但仍然不是“settled”。
Promise()构造函数允许将基于回调的API转换为基于Promise的API。
以下是典型流程的总结:
- 当构造函数生成新的Promise对象时,它也会生成一对对应的resolveFunc和rejectFunc函数
- 执行器通常包装一些异步操作,回调是在执行器代码中定义的,因此它可以访问resolveFunc和rejectFunc
- 使用resolveFunc和rejectFunc函数作为参数,同步调用执行器(在构造Promise时)。
- 异步任务的最终完成是通过resolveFunc或rejectFunc引起的作用与promise实例通信的。
- 如果先调用resolveFunc,传递的值将被解析。pending 态( thenable被传递),fulfilled态(非thenable被传递),rejected 态(无效的值传递)
- 如果先调用rejectFunc,承诺立即被拒绝。
- 如果执行器抛出错误退出,则承诺被拒绝。但是,如果其中一个解析函数已经被调用(因此承诺已经被解析),则该错误将被忽略。
- 一旦promise确定,它(异步)调用通过then()、catch()或finally()关联的任何其他处理程序
5.11.3.2 静态方法
-
Promise.all()
接受Promise的可迭代对象作为输入,并返回一个Promise
当所有输入的promise都实现时(包括传递空可迭代对象时),返回的promise就会实现,并带有一个兑现值数组。当输入的任何一个承诺被拒绝时,它就会拒绝,这是第一个拒绝原因。
该对象在所有给定的 Promise 都已经完成(无论是完成还是被拒绝)后才会解析。返回的 Promise 的解析值是一个数组,每个元素都是一个对象,表示对应的 Promise 的状态和值或拒绝原因。
-
Promise.allSettled()
接受Promise的可迭代对象作为输入,并返回一个Promise
当所有输入的promise都完成时(包括传递空可迭代对象时),这个返回的promise就会完成,并使用一个对象数组来描述每个promise的结果。
该对象在所有给定的 Promise 都已经完成时才会解析。但是,如果任何一个 Promise 被拒绝,
Promise.all()
会立即拒绝,拒绝的原因是第一个被拒绝的 Promise 的拒绝原因。const promise1 = new Promise((resolve,reject)=>{resolve('成功'+1) }); const promise2 = new Promise((resolve,reject)=>{reject('失败'+2) }); const promise3 = new Promise((resolve,reject)=>{resolve('成功'+3) });Promise.all([promise1,promise2,promise3]).then(value =>{console.log (value); }).catch(e =>{console.log ('错误'+e);})//错误失败2Promise.allSettled([promise1,promise2,promise3]).then(value =>{console.log (value); }).catch(e =>{console.log ('错误'+e);}) // [ // {"status": "fulfilled","value": "成功1"}, // {"status": "rejected","reason": "失败2"}, // {"status": "fulfilled","value": "成功3"} // ]
-
Promise.any()
接受Promise的可迭代对象作为输入,并返回一个Promise
当输入的任何一个承诺实现时,返回的承诺就会实现,并带有第一个实现值。当所有输入的承诺都被拒绝时(包括传递空可迭代对象时),它将拒绝,并使用包含拒绝原因数组的AggregateError。
只有在所有 promises 都被拒绝时才会拒绝,并且会返回一个 AggregateError,表示所有 promises 的失败。
-
Promise.race()
方法接受Promise的可迭代对象作为输入,并返回一个Promise。
这个返回的承诺与第一个承诺的最终状态一致。
相当于所有promise竞争,第一个就是全局结果,一旦有一个 promise 被拒绝,就会立即拒绝,而不管其他 promises 的状态
-
Promise.reject()
返回一个被给定原因拒绝的Promise对象。
-
Promise.resolve()
将给定的值“解析”为一个Promise。
如果值是一个promise,则返回该promise;如果该值是一个then(), Promise.resolve()将调用then()方法,并使用它准备好的两个回调函数;否则,返回的承诺将用该值来完成
-
Promise.withResolvers()
返回一个对象,其中包含一个新的Promise对象和两个解析或拒绝它的函数,对应于传递给Promise()构造函数执行器的两个参数
5.11.3.3 实例方法
-
Promise.prototype.catch()
在Promise被拒绝时调用的函数
-
Promise.prototype.finally()
调度一个函数,当Promise被解决(完成或拒绝)时调用
这样可以避免在promise的then()和catch()处理程序中重复代码
-
Promise.prototype.then()
最多有两个参数:Promise的完成和拒绝情况的回调函数
语法:
then(onFulfilled)
,then(onFulfilled, onRejected)
当一个Promise被拒绝时,它的
.catch()
方法将被调用。.catch()
方法是专门用来处理Promise被拒绝的情况的。如果你在.then()
方法中提供了两个函数参数,第二个函数可以用来处理Promise被拒绝的情况,但通常我们在.then()
中只处理Promise被成功解决的情况,将处理拒绝的逻辑放在.catch()
中。这样的代码更清晰,更易于理解。
5.11.4 GeneratorFunction
为生成器函数提供了方法。在JavaScript中,每个生成器函数实际上都是一个GeneratorFunction对象
注意,GeneratorFunction不是一个全局对象。可通过以下代码获得:
const GeneratorFunction = function* () {}.constructor;
语法:
new GeneratorFunction(functionBody)
new GeneratorFunction(arg1, functionBody)
new GeneratorFunction(arg1, arg2, functionBody)
new GeneratorFunction(arg1, arg2, /* …, */ argN, functionBody)GeneratorFunction(functionBody)
GeneratorFunction(arg1, functionBody)
GeneratorFunction(arg1, arg2, functionBody)
GeneratorFunction(arg1, arg2, /* …, */ argN, functionBody)
示例:
const GeneratorFunction = function* () {}.constructor;const foo = new GeneratorFunction(`yield 'a';yield 'b';yield 'c';
`);let str = '';
for (const val of foo()) {str = str + val;
}console.log(str);
5.11.5 AsyncGeneratorFunction
为异步生成器函数提供了方法。在JavaScript中,每个异步生成器函数实际上都是一个AsyncGeneratorFunction对象
注意AsyncGeneratorFunction不是一个全局对象。它可以通过计算下面的代码得到:
const AsyncGeneratorFunction = async function* () {}.constructor;
语法:
new AsyncGeneratorFunction(functionBody)
new AsyncGeneratorFunction(arg1, functionBody)
new AsyncGeneratorFunction(arg1, arg2, functionBody)
new AsyncGeneratorFunction(arg1, arg2, /* …, */ argN, functionBody)AsyncGeneratorFunction(functionBody)
AsyncGeneratorFunction(arg1, functionBody)
AsyncGeneratorFunction(arg1, arg2, functionBody)
AsyncGeneratorFunction(arg1, arg2, /* …, */ argN, functionBody)
5.11.6 Generator
Generator对象由生成器函数返回,它同时符合可迭代协议和迭代器协议
//生成器函数,和GeneratorFunction的区别是,这个是静态创建函数,GeneratorFunction是动态创建函数
function* generator() {yield 1;yield 2;yield 3;
}//生成器对象
const gen = generator(); // "Generator { }"console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3
5.11.6.1 实例方法
-
Generator.prototype.next()
返回一个具有done和value两个属性的对象。还可以为next方法提供参数,以便向生成器发送值
该值将作为yield表达式的结果赋值。例如,在variable = yield表达式中,传递给.next()函数的值将被分配给variable
-
Generator.prototype.return()
作用就像在当前挂起的位置将return语句插入到生成器的主体中,这将完成生成器并允许生成器在与try…finally块。
-
Generator.prototype.throw()
作用就像在生成器的当前挂起位置插入了一条throw语句,它通知生成器有错误条件,并允许它处理错误,或者执行清理和关闭自己。
5.11.7 AsyncGenerator
由async生成器函数返回,它同时符合async可迭代协议和async迭代器协议。
异步生成器方法总是产生Promise对象
5.11.8 AsyncFunction
为异步函数提供了方法。在JavaScript中,每个async函数实际上都是一个AsyncFunction对象。
注意AsyncFunction不是一个全局对象。可通过以下代码获得:
const AsyncFunction = async function () {}.constructor;
语法:
new AsyncFunction(functionBody)
new AsyncFunction(arg1, functionBody)
new AsyncFunction(arg1, arg2, functionBody)
new AsyncFunction(arg1, arg2, /* …, */ argN, functionBody)AsyncFunction(functionBody)
AsyncFunction(arg1, functionBody)
AsyncFunction(arg1, arg2, functionBody)
AsyncFunction(arg1, arg2, /* …, */ argN, functionBody)
5.12 反射
5.12.1 Reflect
Reflect的所有属性和方法都是静态的(就像Math对象一样)。
Reflect 主要用于以函数式的方式替代一些对象操作
Reflect 代替基本操作有什么优点?举个例子,如果我们想要删除对象的属性,通常我们会使用 delete 操作符,如 delete obj.prop。但是,delete 操作符有一些特殊的行为,比如当你尝试删除一个不存在的属性或一个不可配置的属性时,它不会抛出错误,而是静默失败。
反观 Reflect 对象,我们可以使用 Reflect.deleteProperty(obj, 'prop') 来达到同样的效果。与 delete 操作符不同,如果删除失败,Reflect 的方法会返回 false,让我们能更明确地知道操作是否成功。
所以,使用 Reflect 对象的方法,我们可以不必记住每个操作的具体语法和特殊行为,只需要记住 Reflect 的一致且直观的 API。这就是这句话的含义。
5.12.1.1 静态方法
-
Reflect.apply()
使用指定的参数调用目标函数。
语法:
Reflect.apply(target, thisArgument, argumentsList)
-
Reflect.construct()
方法类似于new操作符,但作为一个函数。它相当于调用new target(…args)。它还允许指定不同的new。目标的价值。
语法:
Reflect.construct(target, argumentsList)
,Reflect.construct(target, argumentsList, newTarget)
相当于new构造函数并传递参数
-
Reflect.defineProperty()
类似于Object.defineProperty(),但返回一个布尔值。
语法:
Reflect.defineProperty(target, propertyKey, attributes)
-
Reflect.deleteProperty()
方法类似于删除操作符,但作为一个函数。它从对象中删除属性。
语法:
Reflect.deleteProperty(target, propertyKey)
-
Reflect.get()
方法类似于属性访问器语法,但作为一个函数
语法:
Reflect.get(target, propertyKey)
,Reflect.get(target, propertyKey, receiver)
-
Reflect.getOwnPropertyDescriptor()
类似于Object.getOwnPropertyDescriptor()。如果给定属性存在于对象上,则返回该属性的属性描述符,否则未定义。
语法:
Reflect.getOwnPropertyDescriptor(target, propertyKey)
-
Reflect.getPrototypeOf()
方法类似于Object.getPrototypeOf()。它返回指定对象的原型。
语法:
Reflect.getPrototypeOf(target)
-
Reflect.has()
方法类似于in操作符,但作为一个函数。
语法:
Reflect.has(target, propertyKey)
-
Reflect.isExtensible()
方法类似于Object.isExtensible()。它确定对象是否可扩展(是否可以向其添加新属性)
语法:
Reflect.isExtensible(target)
-
Reflect.ownKeys()
方法返回目标对象自己的属性键数组。
语法:
Reflect.ownKeys(target)
-
Reflect.preventExtensions()
方法类似于Object.preventExtensions()。它防止新属性被添加到对象中(即,防止将来对对象进行扩展)。
语法:
Reflect.preventExtensions(target)
-
Reflect.set()
方法类似于属性访问器和赋值语法,但作为一个函数。
语法:
Reflect.set(target, propertyKey, value)
,Reflect.set(target, propertyKey, value, receiver)
-
Reflect.setPrototypeOf()
方法类似于Object.setPrototypeOf(),但返回一个布尔值。它设置指定对象的原型(即内部的[[prototype]]属性)。
语法:
Reflect.setPrototypeOf(target, prototype)
5.12.2 Proxy
使您能够为另一个对象创建代理,该代理可以拦截和重新定义该对象的基本操作。
Proxy 则用于在对象操作上添加自定义行为。
//目标对象
const target = {notProxied: "original value",proxied: "original value",
};
//处理函数
const handler = {get(target, prop, receiver) {if (prop === "proxied") {return "replaced value";}return Reflect.get(...arguments);},
};
//创建代理
const proxy = new Proxy(target, handler);console.log(proxy.notProxied); // "original value"
console.log(proxy.proxied); // "replaced value"
Proxy.revocable()静态方法创建一个可撤销的Proxy对象
const revocable = Proxy.revocable({},{get(target, name) {return `[[${name}]]`;},},
);
const proxy = revocable.proxy;
console.log(proxy.foo); // "[[foo]]"revocable.revoke();console.log(proxy.foo); // TypeError is thrown
proxy.foo = 1; // TypeError again
delete proxy.foo; // still TypeError
typeof proxy; // "object", typeof doesn't trigger any trap
5.13 国际化
六、函数
6.1 概述
js中有四种类型的函数:
- 正则函数:可以返回任何东西;总是在调用后运行到完成
- 生成器函数:返回一个生成器对象,可以使用yield操作符暂停和恢复
- 异步函数:返回一个Promise,可以使用await操作符暂停和恢复
- 异步生成器函数:返回一个异步生成器对象,yield和await操作符可以使用
每一类函数,都有三种定义方法:
- 声明
- 表达式
- 构造器
所有语法的不同:
- 构造器、表达式和声明创建了完整的函数对象,可以用new构造,但是不能构造箭头函数和方法,异步函数、生成器函数和异步生成器函数都是不可构造的。
- function 声明的函数被提升。其他语法不能提升,并且在定义后才能访问值
- 箭头函数和Function()构造器创建一个匿名函数,意味着它们不能递归调用自己。递归调用箭头函数的一种方法是将其赋值给一个变量
- 箭头函数语法不能访问arguments或this。
- Function()构造函数不能访问任何局部变量——它只能访问全局作用域。
- Function()构造函数导致运行时编译,并且通常比其他语法慢
立即执行函数采用分组或void:
//分组
(function () {console.log("FOO!");
})();
//void
void function () {console.log("FOO!");
}();
有三种特殊的参数语法:
- 如果没有传递值或未定义,默认参数允许使用默认值初始化形式参数。
- rest形参允许将无限数量的参数表示为数组。
- 解构允许将数组中的元素或对象中的属性拆分成不同的变量。
function myFunc({ a, b }, c = 1, ...rest) {// You can access the values of a, b, c, and rest here
}
6.2 箭头函数表达式
箭头函数表达式是传统函数表达式的紧凑替代品,在使用上有一些语义上的差异和故意的限制:
- 箭头函数对this、arguments或super没有自己的绑定,因此不应该用作方法
- 箭头函数不能用作构造函数。用new调用它们会抛出TypeError
- 箭头函数不能在其函数体中使用yield,也不能作为生成器函数创建。
只有当函数只有一个简单的参数时,括号才可以省略。如果它有多个参数、没有参数、默认参数、解构参数或rest参数,则需要在参数列表周围加上括号。
只有当函数直接返回表达式时,才可以省略大括号。
箭头函数总是未命名的。如果箭头函数需要调用自身,则使用命名函数表达式代替。您还可以将箭头函数分配给一个变量,这样它就有了一个名称
直接返回对象字面量:
const func = () => { foo: 1 };//这种报错
const func = () => ({ foo: 1 });//使用括号包裹
箭头函数不能应用与方法中,因为箭头函数没有自己的this
const obj = {i: 10,b: () => console.log(this.i, this),//不应这么使用c() {console.log(this.i, this);},
};
call()、apply()和bind()方法在箭头函数上调用时也没有用,因为箭头函数是根据箭头函数定义的范围来建立this的,而this值不会根据函数的调用方式而改变。
鉴于箭头函数的优先级:
callback = callback || () => {};
callback = callback || (() => {});//加上括号,避免callback ||()被解析为箭头函数的参数列表
示例:
const obj = {num: 100,
};globalThis.num = 42;//正常函数,this指向调用的对象为obj
const add1 = function (a, b, c) {return this.num + a + b + c;
};
//使用箭头函数,this指向globalThis
const add2 = (a, b, c) => this.num + a + b + c;console.log(add1.call(obj, 1, 2, 3)); // 106console.log(add2.call(obj, 1, 2, 3)); // 48
也许使用箭头函数的最大好处是使用setTimeout()和EventTarget.prototype.addEventListener()这样的方法,它们通常需要某种闭包、call()、apply()或bind()来确保函数在适当的范围内执行。
addEventListener():
const obj1 = {count: 10,add(){const btn = document.querySelector('button');btn.addEventListener('click',function(){// 传统函数this指向windowconsole.log (++this.count);})}
}const obj2 = {count: 10,add(){const btn = document.querySelector('button');// 箭头函数this指向objbtn.addEventListener('click',()=>console.log (++this.count))}
}obj1.add();//NaN
obj2.add();//11
setTimeout():
const obj = {count: 10,doSomethingLater() {setTimeout(function () {// 传统函数this指向windowthis.count++;console.log(this.count);}, 300);},
};obj.doSomethingLater(); // logs "NaN"const obj = {count: 10,doSomethingLater() {// 箭头函数this指向objsetTimeout(() => {this.count++;console.log(this.count);}, 300);},
};
obj.doSomethingLater(); // logs "11"
6.3 默认参数
语法:
function fnName(param1 = defaultValue1, /* …, */ paramN = defaultValueN) {// …
}
6.4 get
get语法将对象属性绑定到一个函数,该函数将在查找该属性时被调用。它也可以在类上使用
语法:
{ get prop() { /* … */ } }
{ get [expression]() { /* … */ } }//还可以使用计算属性名称的表达式绑定到给定的函数
getter必须完全没有参数。
//计算属性名
const expr = function(){return "foo"
};const obj = {get [expr()]() {return "bar";},
};console.log(obj.foo); // "bar"
6.5 方法定义
方法定义是在对象初始化器中定义函数属性的较短语法。它也可以在类上使用
语法:
({property(parameters) {},*generator(parameters) {},async property(parameters) {},async *generator(parameters) {},// with computed keys[expression](parameters) {},*[expression](parameters) {},async [expression](parameters) {},async *[expression](parameters) {},
})
6.6 剩余参数
剩余参数语法允许函数以数组的形式接受不确定数量的参数,从而提供了一种在JavaScript中表示可变函数的方法
语法:
function f(a, b, ...theArgs) {// …
}
rest参数不计入函数的length属性
rest和arguments的区别:
- arguments不是真正的数组,rest是一个数组的实例
- arguments有一个被废弃的callee属性
- 非严格模式使用基本参数时,arguments和形参更新是同步的。rest从来不同步更新
- rest绑定所有参数,但不包括…rest之前的参数,arguments包含所有参数,包含…rest
function fun(a,f,...rests){console.log (Array.isArray(arguments));//falseconsole.log (Array.isArray(rests));//true
}fun(1,3,4,5)//简单参数,arguments更新a也更新
function fun2(a){arguments[0] = 9console.log (a);//9
}
fun2(2)//非简单参数(形参包含默认参数,rest参数,结构参数等),arguments更新a不更新
function fun3(a,b=1){arguments[0] = 9console.log (a);//2
}
fun3(2)
6.7 set
set语法将对象属性绑定到要在尝试设置该属性时调用的函数。它也可以在class上使用。
语法:
{ set prop(val) { /* … */ } }
{ set [expression](val) { /* … */ } }
setter必须只有一个参数
6.8 Arguments对象
Arguments是一个可以在函数内部访问的类数组对象,其中包含传递给该函数的实参的值。
类数组转化为数组:
const args = Array.prototype.slice.call(arguments);
// or
const args = Array.from(arguments);
// or
const args = [...arguments];
生成html结构:
function list(type) {let html = `<${type}l><li>`;const args = Array.prototype.slice.call(arguments, 1);html += args.join("</li><li>");html += `</li></${type}l>`; // end listreturn html;
}
七、类
7.1 概述
类是创建对象的模板。它们用处理数据的代码封装数据。JS中的类是建立在原型之上的,但也有一些类特有的语法和语义。
7.2 构造函数
构造函数方法是类的特殊方法,用于创建和初始化该类的对象实例
这里有一些额外的语法限制:
- 称为构造函数的类方法不能是getter、setter、async或生成器。
- 一个类不能有多个构造函数方法
如果您不提供自己的构造函数,那么将为您提供默认构造函数:
- 如果你的类是基类,默认构造函数为空:
- 如果你的类是一个派生类,默认构造函数调用父构造函数,传递提供的任何参数
构造函数方法可以有一个返回值。基类可以从其构造函数返回任何值,而派生类必须返回对象或未定义值,否则将抛出TypeError。
如果父类构造函数返回一个对象,该对象将被用作this值,派生类的类字段将在此值上定义。这个技巧被称为“返回覆盖”,它允许在不相关的对象上定义派生类的字段(包括私有字段)。
禁止将异步方法、生成器方法、访问器和类字段称为构造函数。私有名称不能称为#constructor。任何名为constructor的成员都必须是普通方法
7.3 继承
extends关键字可用于自定义类和内置对象的子类化
任何可以用new调用并具有prototype属性的构造函数都可以作为父类的候选对象。这两个条件必须同时成立——例如,绑定函数和代理可以被构造,但它们没有原型属性,因此它们不能被子类化
7.4 私有属性
私有属性通过使用#前缀创建,不能在类之外合法引用。
访问私有属性的唯一方法是通过点表示法,并且只能在定义私有属性的类中这样做
这里有一些额外的语法限制:
- 在类中声明的所有私有标识符必须是唯一的。名称空间在静态属性和实例属性之间共享。唯一的例外是两个声明定义了getter-setter对。
- 私有标识符不能是#constructor
在Chrome控制台中运行的代码可以在类之外访问私有属性。这是一个仅针对devtools的JavaScript语法限制的放松
7.5 公有类字段
这里有一些额外的语法限制:
- 静态属性(字段或方法)的名称不能是prototype
- 类字段(静态或实例)的名称不能是构造函数
7.6 静态
static关键字定义了类的静态方法或字段,或者静态初始化块(有关此用法的更多信息,请参阅链接)。静态属性不能在类的实例上直接访问。相反,它们是在类本身上访问的。
7.7 静态初始化块
静态初始化块在类中声明。它包含要在类初始化期间求值的语句。这允许比静态属性更灵活的初始化逻辑,例如使用try…从单个值捕获或设置多个字段。初始化是在当前类声明的上下文中执行的,可以访问私有状态,这允许类与在同一作用域中声明的其他类或函数共享其私有属性的信息