ES6
1.Class的基本语法
1.1 简介
1.1.1 类的由来
传统的生成实例对象的方法是通过构造函数。
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';
};
var p = new Point(1, 2);
但是这种写法与其他面向对象的编程语言写法不太一样,所以ES6提供了更接近传统语言的写法,引入了类。类的大部分功能,ES5都可以做到,只是用class的写法让对象原型的写法更加清晰,更像传统语言的写法。上述代码可以改写为:
class Point {
constructor(x, y) {//构造函数
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
类的所有方法都定义在类的原型上。
class Point {
constructor() {
// ...
}
toString() {
// ...
}
toValue() {
// ...
}
}
// 等同于
Point.prototype = {
constructor() {},
toString() {},
toValue() {},
};
需要注意的是constructor()属性是指向类本身的,这一点和ES5一致。而且类内部定义的所有方法都是不可枚举的,但是ES5的写法中,方法是可枚举的。
1.1.2 constructor方法
类的默认方法,生成实例对象时自动调用。
1.1.3 类的实例
通过new来创建一个新的类对象,需要注意的是,和ES5一样,实例的属性并不是定义在this对象上,而是定义在class中。
1.1.4 getter和setter
可以用get和set关键字,来拦截某个属性的存值函数和取值函数。
1.1.5 属性表达式
let methodName = 'getArea';
class Square {
constructor(length) {
// ...
}
[methodName]() {
// ...
}
}
1.1.6 Class表达式
const MyClass = class Me {
getClassName() {
return Me.name;
}
};
1.1.7 注意
- 类和模块的内部默认严格模式,不需要使用use strict指定运行模式。
- 不存在变量提升。
- 有name属性。(好像没什么用)
- 可以在类内部定义Generator方法。
- 类的方法内部如果存在this,就默认指向类的实例。(少用,容易报错)
1.2 静态方法
类相当于一个实例的原型,所有类中定义的方法都会被实例继承。如果不希望实例继承某个方法,就可以在方法前加上static,只能通过类来调用,而不能用实例来调用。
1.3 实例属性的新写法
原本只能定义在constructor()方法内部的this上面,先在也可以直接定义到类的最顶层。
1.4 静态属性
只能通过类来访问。
1.5 私有方法和私有属性
如何实现私有化?(只能在类的内部访问的方法,外部不能访问)
- 定义私有方法时在变量命名前加上下划线,但是这种方法并不保险。
- 将私有方法移出类。(类内部的方法都是对外可见的)
- 利用Symbol值的唯一性,将私有方法命名为一个Smybol值。
私有属性的提案:在属性名前加上#。
in运算符:
原本可以用try...catch结构来判断是否存在某个私有属性。后面引入in来判断私有属性。
class A {
use(obj) {
if (#foo in obj) {
// 私有属性 #foo 存在
} else {
// 私有属性 #foo 不存在
}
}
}
1.6 静态块
ES2022引入,用来初始化静态属性。
class C {
static x = ...;
static y;
static z;
static {
try {
const obj = doSomethingWith(this.x);
this.y = obj.y;
this.z = obj.z;
}
catch {
this.y = ...;
this.z = ...;
}
}
}
1.7 new.target属性
用来确定构造函数时怎么调用的,如果不是通过new或者Reflect.cobnnstruct()调用的,就会返回undefined。
Class内部调用new.target时,会返回当前的Class,子类继承父类时,new.target会返回子类。函数外部调用new.target就会报错。
2.Class的继承
2.1 简介
通过extends关键字实现继承。子类必须在构造函数中调用super方法来调用父类的构造函数建造父类的this对象。
ES5中的继承,本质是创造子类的实例对象this,然后再将父类的方法添加到this上面。而ES6设计是将父类实例对象的属性和方法,加到this上面,再用子类的构造函数修改this,与ES5相反。
父类的静态方法也会被子类继承。
2.2 Object.getPrototypeOf()
通过此方法从子类获取父类。
Object.getPrototypeOf(ColorPoint) === Point
// true
2.3 super关键字
super当函数调用时,代表父类的构造函数,ES6要求,子类的构造函数必须执行一次super函数。(哪怕子类不定义构造函数,默认的构造函数也会调用super)
super作为对象是,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
2.4 类的prototype属性和__proto__属性
- 子类的__proto__属性,表示构造函数的继承,总是指向父类。
- 子类的prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。
实例的__proto__属性: 子类实例的__proto__属性的__proto__属性,指向父类实例的__proto__属性。意思就是子类的原型的原型是父类的原型。
2.5 原生构造函数的继承
以前原生构造函数是无法继承的。Boolean(),Number(),String(),Array(),Date(),Function(),RegExp(),Error(),Object()。继承原生构造函数后,类的行为与原生构造函数完全不一致。
ES6允许继承原生构造函数定义子类,ES6先新建父类的实例对象this,然后再用子类的构造函数修饰this,使服了爹所有行为都可以继承。
注意: 继承Object的子类有一个行为差异,无法通过super向父类Object传参。
2.6 Mixin模式的实现
多个对象合成一个新的对象,新的对象由各个组成成员的接口。
3.Module的语法
3.1 概述
为了更加静态化,让编译时就能确定模块的依赖关系,以及输入和输出的变量,ES6模块应运而生。ES6模块不是对象,而是通过export命令显示指定输出的代码,再通过import命令输入。
优点:
- 是编译时加载,所有可以静态分析,引入宏、类型检测。
- 不在需要UMP模块格式。
- 将来浏览器的新API能用模块格式提供,不再必须做成全局变量或者navigator对的属性。
- 不再需要对象作为命名空间。
3.2 严格模式
自动使用严格模式。
严格模式:
- 变量必须声明后再使用
- 函数的参数不能有同名属性,否则报错
- 不能使用with语句
- 不能对只读属性赋值,否则报错
- 不能使用前缀 0 表示八进制数,否则报错
- 不能删除不可删除的属性,否则报错
- 不能删除变量delete prop,会报错,只能删除属性delete global[prop]
- eval不会在它的外层作用域引入变量
- eval和arguments不能被重新赋值
- arguments不会自动反映函数参数的变化
- 不能使用arguments.callee
- 不能使用arguments.caller
- 禁止this指向全局对象
- 不能使用fn.caller和fn.arguments获取函数调用的堆栈
- 增加了保留字(比如protected、static和interface)
3.3 export 命令
用于规定模块的对外接口。可以导出对象、函数和类,但是必须与模块内部的变量建立一一对应的关系。
3.4 import 命令
用于输入其他模块提供的功能。
重命名:output as newName
不允许在加载模块的脚本中改写接口,但是可以改对象的属性,但是这样其他脚本里引用这个接口里的对象时,是被改写后的值了,所以不推荐改导入的接口。
3.5 模块的整体加载
写法:import * from xxx
3.6 export default 命令
默认导出,这样就可以在脚本中给导出的模块命名。
3.7 export 与 import 的复合写法
export { foo, bar } from 'my_module';
// 可以简单理解为
import { foo, bar } from 'my_module';
export { foo, bar };
3.8 模块的继承
在导出时,先export * from 父模块。但是这样会忽略父模块的default方法。
3.9 跨模块常量
用来写跨模块的常量,因为const是块级作用域,这里可以用export const PI = 3.14来定义常量。
如果需要的常量比较多,就建一个专门的constants目录,将写有很多常量的文件放在此目录下。
3.10 import()
简介: import语句实在编译时处理的,所以import放在if里面无意义,是静态加载的。但ES2020提案中引入了import()函数,支持动态加载模块。import(加载的模块的位置)。
使用场合:
-
按需加载:在需要的时候及在某个模块。
-
条件加载:放在if代码块中,根据不同情况加载。
-
动态的模块路径:和then一起用,根据参数加载不同的模块。
import(f()) .then(...);
注意点:
用import()加载后,这个模块会作为一个对象,当作then方法的参数,所以可以使用对象解构赋值的语法获取输出的接口。
4.Module的加载实现
4.1 浏览器加载
传统方法:
HTML用
defer要等到整个页面在内存中正常渲染结束(DOM 结构完全生成,以及其他脚本执行完成),才会执行;async一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。、defer是“渲染完再执行”,async是“下载完就执行”。另外,如果有多个defer脚本,会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺序的。
加载规则:
浏览器加载模块是,也是用
async属性也可以用于模块加载中,只要加载完成,渲染引擎就会中断渲染立即执行。执行完成后再次渲染。
对于外部的模块脚本的注意点:
- 代码是在模块作用域之中运行,而不是在全局作用域运行。模块内部的顶层变量,外部不可见。
- 模块脚本自动采用严格模式,不管有没有声明use strict。
- 模块之中,可以使用import命令加载其他模块(.js后缀不可省略,需要提供绝对 URL 或相对 URL),也可以使用export命令输出对外接口。
- 模块之中,顶层的this关键字返回undefined,而不是指向window。也就是说,在模块顶层使用this关键字,是无意义的。
- 同一个模块如果加载多次,将只执行一次。
4.2 ES6 模块与 CommonJS 模块的差异
- CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
- CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
- CommonJS 模块的require()是同步加载模块,ES6 模块的import命令是异步加载,有一个独立的模块依赖的解析阶段。
4.3 Node.js 的模块加载方法
Js的两种模块,ES6和CommoJs模块。前者使用import和export,后者用require()和module.exports,后者是Node.js专用,与ES6模块不兼容。
ES6要求模块必须用.mjs后缀文件名,Node.js遇到.mjs文件就会认为他是ES6模块,默认启用严格模式。
package.json的main字段:
用来指定模块加载的入口文件。如果一个模块的后缀不是.mjs,格式又是ES6模块,就要在package.json里将type改成module。
package.json的exports字段(优先级高于main):
-
指定脚本或者子目录的别名。
// ./node_modules/es-module-package/package.json { "exports": { "./features/": "./src/features/" } } import feature from 'es-module-package/features/x.js'; // 加载 ./node_modules/es-module-package/src/features/x.js -
main的别名。
{ "exports": { ".": "./main.js" } } // 等同于 { "exports": "./main.js" }
- 条件加载。
{
"type": "module",
"exports": {
".": {
"require": "./main.cjs",
"default": "./main.js"
}
}
}
- CommonJS模块加载ES6模块。
(async () => {
await import('./my-app.mjs');
})();
-
ES6模块加载CommonJS。
import packageMain from 'commonjs-package';
-
同时支持两种格式的模块。
import cjsModule from '../index.js'; export const foo = cjsModule.foo; //方法二 "exports":{ "require": "./index.js", "import": "./esm/wrapper.js" } -
Node.js的内置模块
// 整体加载 import EventEmitter from 'events'; const e = new EventEmitter(); // 加载指定的输出项 import { readFile } from 'fs'; readFile('./foo.txt', (err, source) => { if (err) { console.error(err); } else { console.log(source); } });
- 加载路径:ES6必须给出脚本的完整路径,而且不能省略后缀名。
- 内部变量:ES6内,顶层的this指向undefined,CommonJS指向当前模块。
4.4 循环加载
耦合度过高,但无避免。
// a.js
var b = require('b');
// b.js
var a = require('a');
CommonJS模块的加载原理: 每一个模块都是一个脚本文件,require命令第一次加载该脚本就会执行整个脚本,然后在内存生成一个对象。用到这个模块的时候就会去缓存里取值。
CommonJS模块的循环加载: 由于CommonJS是加载时执行,所以循环加载时,只输出已执行的部分,未执行的部分不会输出。
ES6模块的循环加载: 因为ES6是动态引用。可以把可能会用到的参数改写成函数形式(不是函数表达式),因为函数具有提升作用,这样就不会报错了。
5.编程风格
5.1 块级作用域
- let代替var。
- let和const之间,优先用const。
5.2 字符串
一律用单引号或反引号,不要用双引号。动态字符串使用反引号。
5.3 解构赋值
优先使用解构赋值的情况:
- 用数组成员对变量赋值。
- 函数的参数是对象的成员。
- 函数返回多个值。
5.4 对象
- 单行定义的对象,最后一个成员不以逗号结尾。多行定义的对象,最后一个成员以逗号结尾。
- 对象尽量静态化,不要随便添加新的属性。
- 对象的属性名如果是动态的,创造对象的时候可以使用属性表达式定义。
5.5 数组
- 使用扩展运算符拷贝数组。
- 使用Array.from方法,将类似数组的对象转为数组。
5.6 函数、
- 立即执行的函数可以写成箭头函数。
- 匿名函数作参数时,尽量用箭头函数代替。
- 简单的、单行的、不会复用的函数,用箭头函数代替。
- 不要在函数体内使用arguments变量,使用rest运算符(...)代替。因为rest可以提供一个真的数组。
- 使用默认值语法设置函数参数的默认值。
5.7 Map 结构
只有模拟现实世界的实体对象才会使用Object,如果只是需要简单映射,Map结构就足够了。
5.8 Class
- 在需要prototype的操作时,使用Class。
- 使用extends实现继承,这样更简单,不会破坏instanceof运算。
5.9 模块
- 用import取代require()
- 用export取代module.exports
- 如果模块只有一个输出值,就使用export default,如果有多个值,尽量不要使用export default。
- 如果模块默认输出一个函数,函数名的首字母应该小写。
- 如果模块默认输出一个对象,对象名的首字母应该大写。
5.10 ESLint 的使用
6.读懂ECMAScript规格
6.1 术语
**抽象操作:**引擎内部的一些方法,外部不能调用。
**Record和field:**键值对的数据结构就是Record,每一组键值对称为field。
**[[Notation]]:**用[[Value]]、[[Writable]]等写法来指代field的键名。例如Obj就是一个Record,有一个Prototype属性,ES6中一般都用obj.[[Prototype]]这种写法啦替代obj.Prototype。所以使用[[Notation]]这种写法的属性,都是对象的内部属性。所有的JavaScript函数都有一个内部属性[[call]]用来允许该函数。
**Completion.Record:**每个语句都会返回一个Completion Record表示运行结果,每个Completion Record都有一个[[Type]]属性,表示运行结果的类型。
normal:运行正常。return、throw、break、continue都是abrupt completion,只用关注[[Type]]为throw的情况,就是运行出错了。其他三个都只出现在特定的个场景,不用考虑。
6.2 抽象操作的标准流程
- Let result be AbstractOp()
- ReturnIfAbrupt(result)
- return result
先调用抽象操作得到result,如果时abrupt completion就直接返回,如果没有返回就是normal completion,将result的值设为resultCompletionRecord.[[value]],然后返回result。
6.3 相等运算符
x==y是如何实现的?
- 如果x不是正常值,中断执行。
- 如果y不是正常值,中断执行。
- 如果Type(x)与Type(y)相同,执行严格相同运算x===y。
- 如果x是null,y是undefined,返回true。
- 如果x是undefined,y是null,返回true。
- 如果Type(x)是数值,Type(y)是字符串,返回x == ToNumber(y)的结果。
- 如果Type(x)是字符串,Type(y)是数值,返回ToNumber(x) == y的结果。
- 如果Type(x)是布尔值,返回ToNumber(x) == y的结果。
- 如果Type(y)是布尔值,返回x == ToNumber(y)的结果。
- 如果Type(x)是字符串或数值或Symbol值,Type(y)是对象,返回x == ToPrimitive(y)的结果。
- 如果Type(x)是对象,Type(y)是字符串或数值或Symbol值,返回ToPrimitive(x) == y的结果。
- 返回false。
6.4 数组的空位
数组的空位会反映在length属性,但是这个位置的值是未定义,如果读取,结果就是undefined。所以in运算符、数组的hasOwnProperty方法、Object.keys方法都取不到空位额属性名。
6.5 数组的map方法
为什么数组的map方法会跳过空位?
- 得到当前数组的this对象。
- 如果报错就返回。
- 求出当前数组的length属性。
- 如果报错就返回。
- 如果map方法的参数callbackfn不可执行,就报错。
- 如果map方法的参数之中,指定了this,就让T等于该参数,否则T为undefined。
- 生成一个新的数组A,跟当前数组的length属性保持一致。
- 如果报错就返回。
- 设定K等于0。
- 只要k小于当前数组的length属性,就重复以下步骤:
- 设定Pk等于ToString(k),即将k转为字符串。
- 设定kPresent等于HasProperty(O, Pk),即求当前数组有没有指定属性
- 如果报错就返回
- 如果kPresent等于true,则进行下面步骤
- 设定kValue等于Get(O, Pk),取出当前数组的指定属性
- 如果报错就返回
- 设定mappedValue等于Call(callbackfn, T, «kValue, k, O»),即执行回调函数
- 如果报错就返回
- 设定status等于CreateDataPropertyOrThrow (A, Pk, mappedValue),即将回调函数的值放入A数组的指定位置
- 如果报错就返回
- k增加
- 返回A
上述算法中,当处理一个全是空位的数组时,在10.2步,KPresent会报错,因为空位对应的属性名对于数组来说是不存在的,因此就会返回,就没有后面的操作了。
7.异步遍历器
7.1 同步遍历器的问题
Iterator接口遍历的next方法必须是同步的,如果要用来处理异步操作可以把next()方法的返回值的value属性变为一个Thunk函数或者Promise对象,然后等待返回真正的值,但是done属性还是同步产生的。
7.2 异步遍历的接口
调用遍历器的next方法,返回一个Promise对象。然后用then方法指定这个Promise对象的状态变为resolve以后的回调函数。这个回调函数的参数就是一个具有value和done两个属性的对象,和同步遍历器一样。
异步遍历器的接口部署在Symbol.asyncIterator属性上。如果一个对象的Symbol.asyncIterator有值,就应该对它进行异步遍历。
**注意:**异步遍历器的next方法可以连续调用,不用等到上一步的Promise对象resolve以后再调用。此时next方法会累积起来,可以把next方法放在promise.all方法里面或者一次性调用所有的next方法,然后await最后一步操作。
7.3 for await...of
用于遍历异步的Iterator接口。
async function f() {
for await (const x of createAsyncIterable(['a', 'b'])) {
console.log(x);
}
}
// a
// b
部署了asyncIterable操作的异步接口可以直接放入这个循环。
let body = '';
async function f() {
for await(const data of req) body += data;
const parsed = JSON.parse(body);
console.log('got', parsed);
}
如果Promise对象被reject,for await...of就会报错,要用try...catch捕捉。此循环也可遍历同步遍历器。
7.4 异步Generator函数
异步Generator就是返回一个异步遍历器对象。语法上就是async函数和Generator函数结合。异步遍历器的设计目的之一,**就是Generator函数在处理同步操作和异步操作时,可以用同一套接口。**此函数也可以和for await...of一起用。
此时函数有四种函数形式:普通函数,async函数,Generator函数和异步Generator函数。
异步Generator函数也可以通过next方法的参数,接受外部传入的数据。
7.5 yield*函数
async function* gen1() {
yield 'a';
yield 'b';
return 2;
}
async function* gen2() {
// result 最终会等于 2
const result = yield* gen1();
}
8.ArrayBuffer
ArrayBuffer对象、TypedArray视图和DataView视图是JavaScript操作二进制数据的一个接口。
二进制数组由三类对象组成。
(1)ArrayBuffer对象:代表内存之中的一段二进制数据,可以通过“视图”进行操作。“视图”部署了数组接口,这意味着,可以用数组的方法操作内存。
(2)TypedArray视图:共包括 9 种类型的视图,比如Uint8Array(无符号 8 位整数)数组视图, Int16Array(16 位整数)数组视图, Float32Array(32 位浮点数)数组视图等等。
(3)DataView视图:可以自定义复合格式的视图,比如第一个字节是 Uint8(无符号 8 位整数)、第二、三个字节是 Int16(16 位整数)、第四个字节开始是 Float32(32 位浮点数)等等,此外还可以自定义字节序。
8.1 ArrayBuffer 对象
该对象代表二进制数据的一段内存,不能直接读写,只能通过视图来读写。DataView视图的创建需要提供ArrayBuffer对象实例作为参数,另一种TypedArray视图不是一个构造函数,而是一组构造函数,代表不同的数据格式。
几个属性和方法:
**ArrayBuffer.prototype.byteLength:**ArrayBuffer实例的byteLength属性,返回所分配的内存区域的字节长度。
**ArrayBuffer.prototype.slice() :**允许将内存区域的一部分,拷贝生成一个新的ArrayBuffer对象。
**ArrayBuffer.isView():**有一个静态方法isView,返回一个布尔值,表示参数是否为ArrayBuffer的视图实例。
8.2 TypedArray 视图
一共有9种类型的视图:Int8Array、UintArray、Uint8ClampedArray、Int16Array、Uint16Array、Int32Array、Uint32Array、Float32Array、Float64Array。
四种构造函数:TypedArray(buffer, byteOffset=0,length?)、TypedArray(length)、TypedArray(typedArray)、TypedArray(arrayLikeObject)。
普通数组的操作方法和属性,对TypedArray数组完全适用。
**BYTES_PER_ELEMENT属性:**每一种视图的构造函数都有这个属性,来表示着中国数据类型占据的字节数。
**ArrayBuffer与字符串的相互转换:**使用原生的TextEncoder和TextDecoder方法。
**溢出:**TypedArray数组的溢出处理规则就是抛弃溢出的位,然后按照视图类型进行解释。、
**TypedArray.prototype.buffer:**TypedArray实例的buffer属性,返回整段内存区域对应的ArrayBuffer对象。
**TypedArray.prototype.byteLength, TypedArray.prototype.byteOffset:**前者返回TypedArray数组占据的内存长度,单位为字节。后者返回TypedArray数组从底层ArrayBuffer对象的哪个字节开始。
**TypedArray.prototype.lentgh:**表示TypedArray数组含有多少个成员。
**TypedArray.prototype.set():**用于复制数组。
**TypedArray.prototype.subarray():**对于TypedArray数组的一部分再建立一个新的视图。
**TypedArray.prototype.slice():**返回一个指定位置的新的TypedArray实例。
**TypedArray.of():**将参数转为TypedArray实例。
**TypedArray.from:**接受一个可遍历的数据结构作为参数,返回一个基于这个结构的TypedArray实例。
8.3 复合视图
该视图的构造函数可以指定起始位置和长度,在同一段内存中可以依次存放不同类型的数据,这就是“复合视图”。
8.4 DataView 视图
用来操作一段包括多种类型的数据。
8.5 二进制数组的应用
**AJAX:**传统上,服务器通过AJAX操作只能返回文本数据,responseType默认位text,因为XMLHttpRequest第二版XHR2允许服务器返回二进制数据,如果明确知道返回的二进制数据类型可以把返沪类型设为arraybuffer,不知道就设为blob。
**Canvas:**该元素输出的二进制像素数据,就是TypedArray数组。
**WebSocket:**通过ArrayBuffer,发送或接受二进制数据。
**Fetch API:**取回的数据就是ArrayBuffer对象。
**File API:**如果知道一个文件的二进制数据类型,也可以将这个文件读取为ArrayBuffer对象。
8.6 SharedArrayBuffer
Js是单线程的,但是Web worker是多线程的,所以ES2017引入了SharedArrayBuffer,允许Worker线程与主线程共享一块内存。
8.7 Atomics 对象
用来防止两个线程同时修改某个地址,保证所有共享内存的操作都是原子性的,并且可以再所有线程内同步。
(1)Atomics.store(),Atomics.load():前者用向共享内存写入数据,后者用来从向共享内存读出数据。保证了读写操作的原子性。
(2)Atomics.exchange():写入数据的第二种方法,此方法是返回被替换的值,上面的方法是返回写入的值。
(3)Atomics.wait(),Atomics.notify():用于等待通知,相当于锁内存。
Atomics.wait(sharedArray,index,value,timeout):参数分别是共享内存的视图数组,试图数据的位置,该位置的预期值,表示休眠的时间(默认Infinity,整数,只能被Atomics.notify()唤醒)。
Atomics.notify(sharedArray,index,count):参数分别是共享内存的视图数组,视图数据的位置,需要唤醒的Worker线程的数( 默认Infinity)。
(4)运算方法:
Atomics.add(sharedArray, index, value)
Atomics.add用于将value加到sharedArray[index],返回sharedArray[index]旧的值。
Atomics.sub(sharedArray, index, value)
Atomics.sub用于将value从sharedArray[index]减去,返回sharedArray[index]旧的值。
Atomics.and(sharedArray, index, value)
Atomics.and用于将value与sharedArray[index]进行位运算and,放入sharedArray[index],并返回旧的值。
Atomics.or(sharedArray, index, value)
Atomics.or用于将value与sharedArray[index]进行位运算or,放入sharedArray[index],并返回旧的值。
Atomics.xor(sharedArray, index, value)
Atomic.xor用于将vaule与sharedArray[index]进行位运算xor,放入sharedArray[index],并返回旧的值。
(5)其他方法:
Atomics.compareExchange(sharedArray, index, oldval, newval):如果sharedArray[index]等于oldval,就写入newval,返回oldval。
Atomics.isLockFree(size):返回一个布尔值,表示Atomics对象是否可以处理某个size的内存锁定。如果返回false,应用程序就需要自己来实现锁定。
9.最新提案
9.1 do 表达式
使块级作用域变为表达式,就可以返回值了。封装什么就返回什么。
9.2 throw 表达式
允许throw用于表达式。
9.3 函数的部分执行
绑定参数返回一个新函数。
注意点:
- 函数的部分执行使基于原函数的,如果原函数发生变化,部分执行生成的新函数夜壶立即反映这种变化。
- 如果预先提供的那个值是一个表达式,那这个表达式不会在定义时求值,而是再每次调用时求值。
- 如果新函数的参数多于占位符的数量,那么多余的参数将被忽略。
- ...只会被采集一次,如果函数的部分执行使用了多个...,那么每个...的值都将相同。
9.4 管道运算符
将前一个操作的值传给后一个操作。
x|>f 等同于 f(x)
9.5 Math.signbit()
用来判断一个值的正负。
参数为NaN,返回false;为-0,返回true;为负值,返回true;其他情况都是false。
9.6 双冒号运算符
“函数绑定”运算符,用来取代call、apply、bind调用。
foo::bar 等同于 bar.bind(foo)
9.7 Realm API
提供沙箱功能,允许隔离代码,防止被隔离的代码拿到全局对象。以前常用作为沙箱。
9.8 #!命令
放在脚本的第一行,用来指定脚本的执行器。
9.9 import.meta
- import.meta.url:返回当前模块的URL路径。
- import.meta.scriptElement:返回加载模块的那个元素
9.10 JSON 模块
目前只能使用fetch()加载JSON模块,现在允许用Import直接加载。
10.Decorator(用@来表示
10.1 类的装饰
装饰器可以用来装饰整个类。
**注意:**装饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,装饰器能在编译阶段运行代码。也就是说,装饰器本质就是编译时执行的函数。
10.2 方法的装饰
装饰器还可以装饰类的属性。
10.3 为什么装饰器不能用于函数?
因为存在函数提升。
10.4 core-decorators.js
- @autobind:使得方法中的this对象绑定原始对象。
- @readonly:使属性或方法不可写。
- @override:检查子类的方法,是否正确覆盖了父类的同名方法。
- @deprecate:显示一条警告,表示该方法将被废除。
- @suppressWarnings:一致deprecated装饰器而导致的console.warn()调用。
10.5 使用装饰器实现自动发布事件
使对象的方法被调用时,自动发出一个事件。
10.6 Mixin
混入,在一个对象之中混入另一个对象的方法。
10.7 Trait
与mixin相似,可以防止同名方法的冲突、排除混入某些方法、为混入的方法起别名等等。