ES6查漏补缺
1. let 和const 命令
区别于var
-
不存在变量提升
-
let 是块级作用域,只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响
const命令同样适用 -
let不允许在相同作用域内,重复声明同一个变量
重点: let实际上为 JavaScript 新增了块级作用域,即外层代码块不受内层代码块的影响
- 块级作用域内部,优先使用函数表达式
- const声明一个只读的常量。一旦声明,常量的值就不能改变;const一旦声明变量,就必须立即初始化,不能留到以后赋值,只声明不赋值,就会报错。
ES5 只有两种声明变量的方法:var命令和function命令。
ES6 除了添加let和const命令,另外两种声明变量的方法:import命令和class命令。所以,ES6 一共有 6 种声明变量的方法。
- var命令和function命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。也就是说,从 ES6 开始,全局变量将逐步与顶层对象的属性脱钩。
2. 数组的解构赋值
从数组和对象中提取值,对变量进行赋值,这被称为解构
- 只要等号两边的模式相同,左边的变量就会被赋予对应的值
- 不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组
- 解构赋值允许指定默认值
对象的解构赋值
- 对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值
- 解构后重命名 XXX:XX
- 对象的解构也可以指定默认值
- 如果要将一个已经声明的变量用于解构赋值,必须非常小心
上面代码将整个解构赋值语句,放在一个圆括号里面,就可以正确执行
字符串的解构赋值
-
字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象
-
类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值
变量的解构赋值用途
-
交换变量的值
-
从函数返回多个值
-
函数参数的定义,解构赋值可以方便地将一组参数与变量名对应起来
3.模板字符串
`xxx ${变量} xxxx`
4. 字符串新增的方法
- includes():返回布尔值,表示是否找到了参数字符串。
- startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
- endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
这三个方法都支持第二个参数,表示开始搜索的位置
使用第二个参数n时,endsWith的行为与其他两个方法有所不同。它针对前n个字符,而其他两个方法针对从第n个位置直到字符串结束。
- repeat方法返回一个新字符串,表示将原字符串重复n次
- replaceAll()方法,可以一次性替换所有匹配,返回一个新字符串,不会改变原字符串
5. 数值的扩展
-
ES6 提供了二进制和八进制数值的新的写法,分别用前缀0b(或0B)和0o(或0O)表示
如果要将0b和0o前缀的字符串数值转为十进制,要使用Number方法 -
数值分隔符
数值使用下划线(_)作为分隔符,可以每三位添加一个分隔符,也可以每一位、每两位、每四位添加一个
不能放在数值的最前面(leading)或最后面(trailing)。
不能两个或两个以上的分隔符连在一起。
小数点的前后不能有分隔符。
科学计数法里面,表示指数的e或E前后不能有分隔符。
-
Number.isFinite()用来检查一个数值是否为有限的(finite),即不是Infinity
-
Number.isNaN()用来检查一个值是否为NaN
-
ES6 将全局方法parseInt()和parseFloat(),移植到Number对象上面,行为完全保持不变
-
ES6 在 Math 对象上新增了 17 个与数学相关的方法。所有这些方法都是静态方法,只能在 Math 对象上调用
Math.trunc方法用于去除一个数的小数部分,返回整数部分;对于非数值,Math.trunc内部使用Number方法将其先转为数值;对于空值和无法截取整数的值,返回NaN
Math.sign方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值
参数为正数,返回+1;
参数为负数,返回-1;
参数为 0,返回0;
参数为-0,返回-0;
其他值,返回NaN。
- ES2020 引入了一种新的数据类型 BigInt(大整数),来解决这个问题,这是 ECMAScript 的第八种数据类型。BigInt 只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。
6. 函数的扩展
- ES6 允许为函数的参数设置默认值,即直接写在参数定义的后
注意点:
参数变量是默认声明的,所以不能用let或const再次声明,会报错
使用参数默认值时,函数不能有同名参数
有默认值的参数,写在最后
- 函数的 length 属性
指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length属性将失真
如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了
- 作用域
一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。等到初始化结束,这个作用域就会消失。这种语法行为,在不设置参数默认值时,是不会出现的。
上面代码中,参数y的默认值等于变量x。调用函数f时,参数形成一个单独的作用域。在这个作用域里面,默认值变量x指向第一个参数x,而不是全局变量x,所以输出是2。
- 利用参数默认值,可以指定某一个参数不得省略
上面代码的foo函数,如果调用的时候没有参数,就会调用默认值throwIfMissing函数,从而抛出一个错误
另外,可以将参数默认值设为undefined,表明这个参数是可以省略的。
- rest 参数
ES6 引入 rest 参数(形式为…变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
注意,rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错
- ES6 允许使用“箭头”(=>)定义函数
(1)箭头函数没有自己的this对象(详见下文)。
(2)不可以当作构造函数,也就是说,不可以对箭头函数使用new命令,否则会抛出一个错误。
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
- try catch语句
如果 try 块中的任何代码发生了错误,就会立即退出代码执行过程,然后接着执行 catch 块。此时,catch 块会接收到一个包含错误信息的对象。即使你不想使用这个错误对象,也要给它起个名字。这个对象中包含一个保存着错误消息的 message 属性和一个保存错误类型的 name 属性。
try{
// 可能会导致错误的代码
} catch(error){
// 在错误发生时怎么处理
}
//虽然在 try-catch 语句中是可选的,但 finally 子句一经使用,
其代码无论如何都会执行
function testFinally(){
try {
return 2;
} catch (error){
return 1;
} finally {
return 0;
}
}
//调用这个函数只能返回 0。
//如果把 finally 子句拿掉,这个函数将返回 2
7. 数组的扩展
- 扩展运算符
扩展运算符(spread)是三个点(…)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列
(1)复制数组
(2)合并数组
两种方法都是浅拷贝,使用的时候需要注意(3)与解构赋值结合,用于生成数组
(4)字符串,扩展运算符还可以将字符串转为真正的数组
- Array.from()
用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。
字符串和 Set 结构都具有 Iterator 接口,因此可以被Array.from转为真正的数组
如果参数是一个真正的数组,Array.from会返回一个一模一样的新数组
Array.from还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组
- entries(),keys() 和 values()
可以用for…of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历
- flat()
数组的成员有时还是数组,Array.prototype.flat()用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响
flat()方法的参数写成一个整数,表示想要拉平的层数,默认为1;
如果原数组有空位,flat()方法会跳过空位
- 数组的空位
数组的空位指的是,数组的某一个位置没有任何值,比如Array()构造函数返回的数组都是空位
上面代码中,Array(3)返回一个具有 3 个空位的数组,空位是没有任何值
ES6 则是明确将空位转为undefined
8. 对象的扩展
- 1.属性的简洁表示法
ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法
- 2.属性名表达式
JavaScript 定义对象的属性,有两种方法
ES6 允许字面量定义对象时,用方法二(表达式)作为对象的属性名,即把表达式放在方括号内
注意: 属性名表达式与简洁表示法,不能同时使用,会报错
- 3.属性的遍历
ES6 一共有 5 种方法可以遍历对象的属性
(1)for…in
for…in循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。
(2)Object.keys(obj)
Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。
(3)Object.getOwnPropertyNames(obj)
Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。
(4)Object.getOwnPropertySymbols(obj)
Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有 Symbol 属性的键名
(5)Reflect.ownKeys(obj)
Reflect.ownKeys返回一个数组,包含对象自身的(不含继承的)所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。
9. 运算符的扩展
- 1.指数运算符 (**)
这个运算符的一个特点是右结合,而不是常见的左结合。多个指数运算符连用时,是从最右边开始计算的。
指数运算符可以与等号结合,形成一个新的赋值运算符(**=)
- 2.Null 判断运算符
判断运算符。它的行为类似||,但是只有运算符左侧的值为null或undefined时,才会返回右侧的值
如果多个逻辑运算符一起使用,必须用括号表明优先级,否则会报错
必须加入表明优先级的括号
10.Promise 对象
- 1.Promise 的含义
Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果;从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。
Promise对象有以下两个特点
(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
Promise也有一些缺点。首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)
- 2.基本用法
ES6 规定,Promise对象是一个构造函数,用来生成Promise实例
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数
resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。这两个函数都是可选的,不一定要提供。它们都接受Promise对象传出的值作为参数。
Promise 新建后就会立即执行
上面代码中,Promise 新建后立即执行,所以首先输出的是Promise。然后,then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出
resolve函数的参数除了正常的值以外,还可能是另一个 Promise 实例
上面代码中,p1和p2都是 Promise 的实例,但是p2的resolve方法将p1作为参数,即一个异步操作的结果是返回另一个异步操作。
注意,这时p1的状态就会传递给p2,也就是说,p1的状态决定了p2的状态。如果p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是resolved或者rejected,那么p2的回调函数将会立刻执行
注意,调用resolve或reject并不会终结 Promise 的参数函数的执行
上面代码中,调用resolve(1)以后,后面的console.log(2)还是会执行,并且会首先打印出来。这是因为立即 resolved 的 Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。
一般来说,调用resolve或reject以后,Promise 的使命就完成了,后继操作应该放到then方法里面,而不应该直接写在resolve或reject的后面。
- 3.then()链式调用
then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法
上面的代码使用then方法,依次指定了两个回调函数。第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数
采用链式的then方法,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用
上面代码中,第一个then方法指定的回调函数,返回的是另一个Promise对象。这时,第二个then方法指定的回调函数,就会等待这个新的Promise对象状态发生变化。如果变为resolved,就调用第一个回调函数,如果状态变为rejected,就调用第二个回调函数
- 4.catch()
.catch()方法效果等同于.then(null, rejection)或.then(undefined, rejection),用于指定发生错误时的回调函数
处理getJSON函数和then函数产生的错误
Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获
- 5.finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作
上面代码中,不管promise最后的状态,在执行完then或catch指定的回调函数以后,都会执行finally方法指定的回调函数
//
finally方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是fulfilled还是rejected。这表明,finally方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。
- 6.Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例
上面代码中,Promise.all()方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例
//
p的状态由p1、p2、p3决定,分成两种情况
//
(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
//(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数
11.async 函数
- 1.基本用法
async函数返回一个 Promise 对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句
上面代码指定 50 毫秒以后,再输出hello world,等待异步
- 2.async 函数有多种使用形式
- 3.async函数返回一个 Promise 对象;async函数内部return语句返回的值,会成为then方法回调函数的参数
console.log(f())得到是一个promise对象,但是.then()中的参数是return返回的值
//
只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数
- 4.await命令
正常情况下,await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值
await命令后面是一个thenable对象(即定义了then方法的对象),那么await会将其等同于 Promise 对象,await会将其视为Promise处理
await命令后面的 Promise 对象如果变为reject状态,则reject的参数会被catch方法的回调函数接收到
任何一个await语句后面的 Promise 对象变为reject状态,那么整个async函数都会中断执行
有时,我们希望即使前一个异步操作失败,也不要中断后面的异步操作。这时可以将第一个await放在try…catch结构里面,这样不管这个异步操作是否成功,第二个await都会执行
另一种方法是await后面的 Promise 对象再跟一个catch方法,处理前面可能出现的错误
- 5.使用注意
await命令后面的Promise对象,运行结果可能是rejected,所以最好把await命令放在try…catch代码块中
第二点,多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发
getFoo和getBar是两个独立的异步操作(即互不依赖);上面两种写法,getFoo和getBar都是同时触发,这样就会缩短程序的执行时间
12.Class 的基本语法
- 1.类的由来
JavaScript 语言中,生成实例对象的传统方法是通过构造函数
ES6通过class关键字,可以定义类
constructor()方法,这就是构造方法,而this关键字则代表实例对象
Point类除了构造方法,还定义了一个toString()方法。注意,定义toString()方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。另外,方法与方法之间不需要逗号分隔,加了会报错
*类的数据类型就是函数,类本身就指向构造函数
类的所有方法都定义在类的prototype属性上面
上面代码中,constructor()、toString()、toValue()这三个方法,其实都是定义在Point.prototype上面
由于类的方法都定义在prototype对象上面,所以类的新方法可以添加在prototype对象上面。Object.assign()方法可以很方便地一次向类添加多个方法
类的内部所有定义的方法,都是不可枚举的
- 2.constructor()方法
constructor()方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法
- 3.类的实例
生成类的实例,使用new命令
与 ES5 一样,实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)
上面代码中,x和y都是实例对象point自身的属性(因为定义在this对象上),所以hasOwnProperty()方法返回true,而toString()是原型对象的属性(因为定义在Point类上),所以hasOwnProperty()方法返回false。这些都与 ES5 的行为保持一致
与 ES5 一样,类的所有实例共享一个原型对象
p1和p2都是Point的实例,它们的原型都是Point.prototype,所以__proto__属性是相等的;这也意味着,可以通过实例的__proto__属性为“类”添加方法
但不推荐使用
- 4.属性表达式
类的属性名,可以采用表达式
- 5.Class 表达式
与函数一样,类也可以使用表达式的形式定义。
上面代码使用表达式定义了一个类。需要注意的是,这个类的名字是Me,但是Me只在 Class 的内部可用,指代当前类。在 Class 外部,这个类只能用MyClass引用。
Me只在 Class 内部有定义;如果类的内部没用到的话,可以省略Me
- 6.注意点
(1)不存在提升
(2)name 属性
name属性总是返回紧跟在class关键字后面的类名
(3)this 的指向
类的方法内部如果含有this,它默认指向类的实例。但是,必须非常小心,一旦单独使用该方法,很可能报错
上面代码中,printName方法中的this,默认指向Logger类的实例。但是,如果将这个方法提取出来单独使用,this会指向该方法运行时所在的环境(由于 class 内部是严格模式,所以 this 实际指向的是undefined),从而导致找不到print方法而报错
- 7.静态方法
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”
注意,如果静态方法包含this关键字,这个this指的是类,而不是实例
上面代码中,静态方法bar调用了this.baz,这里的this指的是Foo类,而不是Foo的实例,等同于调用Foo.baz。另外,从这个例子还可以看出,静态方法可以与非静态方法重名
//
父类的静态方法,可以被子类继承
父类Foo有一个静态方法,子类Bar可以调用这个方法
//
静态方法也是可以从super对象上调用的
- 8.实例属性的新写法
实例属性除了定义在constructor()方法里面的this上面,也可以定义在类的最顶层
这种新写法的好处是,所有实例对象自身的属性都定义在类的头部,看上去比较整齐,一眼就能看出这个类有哪些实例属性
静态属性
静态属性指的是 Class 本身的属性;而不是定义在实例对象(this)上的属性
ES6 明确规定,Class 内部只有静态方法,没有静态属性;常用上述方法
现在有一个提案提供了类的静态属性,写法是在实例属性的前面,加上static关键字。
- 9.私有方法和私有属性
私有方法和私有属性,是只能在类的内部访问的方法和属性,外部不能访问
将私有方法移出类,因为类内部的所有方法都是对外可见的
上面代码中,foo是公开方法,内部调用了bar.call(this, baz)。这使得bar()实际上成为了当前类的私有方法
私有属性的提案
为class加了私有属性。方法是在属性名之前,使用#表示
13.Class 的继承
- 1.extends关键字
Class 可以通过extends关键字实现继承,让子类继承父类的属性和方法
Point是父类,ColorPoint是子类,它通过extends关键字,继承了Point类的所有属性和方法
上面示例中,constructor()方法和toString()方法内部,都出现了super关键字。super在这里表示父类的构造函数,用来新建一个父类的实例对象
ES6 规定,子类必须在constructor()方法中调用super(),否则就会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,添加子类自己的实例属性和方法。如果不调用super()方法,子类就得不到自己的this对象
上面代码中,ColorPoint继承了父类Point,但是它的构造函数没有调用super(),导致新建实例时报错
//
ES5 的继承机制,是先创造一个独立的子类的实例对象,然后再将父类的方法添加到这个对象上面,即“实例在前,继承在后”。
//
ES6 的继承机制,则是先将父类的属性和方法,加到一个空的对象上面,然后再将该对象作为子类的实例,即“继承在前,实例在后”。
注意,这意味着新建子类实例时,父类的构造函数必定会先运行一次。
上面示例中,子类 Bar 新建实例时,会输出1和2。原因就是子类构造函数调用super()时,会执行一次父类构造函数
除了私有属性,父类的所有属性和方法,都会被子类继承,其中包括静态方法
- 2.Object.getPrototypeOf()
Object.getPrototypeOf()方法可以用来从子类上获取父类
因此,可以使用这个方法判断,一个类是否继承了另一个类
- 3.super 关键字
第一种情况,super作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数。
注意,super虽然代表了父类A的构造函数,但是返回的是子类B的实例,即super内部的this指的是B的实例,因此super()在这里相当于A.prototype.constructor.call(this)
作为函数时,super()只能用在子类的构造函数之中,用在其他地方就会报错。
上面代码中,super()用在B类的m方法之中,就会造成语法错误
第二种情况,super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类
上面代码中,子类B当中的super.p(),就是将super当作一个对象使用。这时,super在普通方法之中,指向A.prototype,所以super.p()就相当于A.prototype.p()
//
注意,由于super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的
p是父类A实例的属性,super.p就引用不到它
//
如果属性定义在父类的原型对象上,super就可以取到
上面代码中,属性x是定义在A.prototype上面的,所以super.x可以取到它的值。
ES6 规定,在子类普通方法中通过super调用父类的方法时,方法内部的this指向当前的子类实例。
super在静态方法之中指向父类,在普通方法之中指向父类的原型对象
//
另外,在子类的静态方法中通过super调用父类的方法时,方法内部的this指向当前的子类,而不是子类的实例
上面代码中,静态方法B.m里面,super.print指向父类的静态方法。这个方法里面的this指向的是B,而不是B的实例
- 4.类的 prototype 属性和__proto__属性
(1)子类的__proto__属性,表示构造函数的继承,总是指向父类。
(2)子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性
实例的 proto 属性
子类实例的__proto__属性的__proto__属性,指向父类实例的__proto__属性
p2继承了p1;子类的原型的原型,是父类的原型
因此,通过子类实例的__proto__.__proto__属性,可以修改父类实例的行为
原型链
13. Module 的语法
- 1.ES6 的模块自动采用严格模式
- 2.export 命令
一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。
如果你希望外部能够读取模块内部的某个变量;就必须使用模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。
- 3.基本写法
export 暴露
上面代码使用as关键字,重命名变量n为m
export语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值
export命令可以出现在模块的任何位置,只要处于模块顶层就可以,习惯放末尾
使用export命令定义了模块的对外接口以后,其他 JS 文件就可以通过import命令加载这个模块
上面代码的import命令,用于加载profile.js文件,并从中输入变量。import命令接受一对大括号,里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块(profile.js)对外接口的名称相同。
//
如果想为输入的变量重新取一个名字,import命令要使用as关键字,将输入的变量重命名
export default
使用export default时,对应的import语句不需要使用大括号
- 4.import
import命令会被 JavaScript 引擎静态分析,先于模块内的其他语句执行;import和export命令只能在模块的顶层,不能在代码块之中;
缺点:导致无法在运行时加载模块
//
ES2020提案 引入import()函数,支持动态加载模块
import()返回一个 Promise 对象
(1)按需加载。
import()方法放在click事件的监听函数之中,只有用户点击了按钮,才会加载这个模块
(2)条件加载
(3)动态的模块路径