目录
1. let和const命令
1.1 let
var arr = [];
for(let i=0;i<2;i++){
arr[i] = function(){
console.log(i)
}
}
arr[0]() // 0
arr[1]() // 1
这样输出的值就是我们所期待的了,把var换成let之后,每次循环的时候,变量i的值都会保存在当前的块级作用域内,所以在函数调用的时候,输出的也是当前块级作用域里的保存的值了。
上面代码中,变量i
是let
声明的,当前的i
只在本轮循环有效,所以每一次循环的i
其实都是一个新的变量,所以最后输出的是6
。你可能会问,如果每一轮循环的变量i
都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i
时,就在上一轮循环的基础上进行计算。
1.1.1 暂时性死区(TDZ)
总之,在代码块内,使用let
命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。
var tmp = 123;
if (true) {
// TDZ开始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp; // TDZ结束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
1.2 const
- 声明常量
const A = 'abc'
-
一定要赋初始值
-
一般常量使用大写(潜规则)
-
常量的值不能修改
-
也具有块级作用域
- 对于数组和对象的元素修改,不算作对常量的修改
const team = ['uzi','MXLG','Ming','Letme'];
team.push('Meiko'); //不报错,常量地址没有发生变化
2. 解构赋值
ES6 允许按照一定模式从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。
3. 模板字符串
1.声明
let str = `我也是一个字符串`
console.log(str,typeof str);
2.内容中可以直接出现换行符
let str = `<ul>
<li>RHF</li>
<li>RHF</li>
</ul>`;
3.变量拼接
let lovest = 'RHF';
let out = `${lovest}是最帅的`;
console.log(out) //RHF是最帅的
4. 对象的简化写法
ES6允许在大括号里面,直接写入变量和函数,作为对象的属性和方法,这样的书写更加简洁.
let name = 'aaa';
let change = function(){
console.log('aaa');
}
const school = {
name,
change,
improve(){
consolg.log('bbb');
}
}
5. 箭头函数
ES6允许使用箭头(=>)定义函数
1. this是静态的,this始终指向函数声明时所在作用域下的this的值
function A(){
console.log(this.name)
}
let B = () => {
console.log(this.name);
}
window.name = '尚硅谷';
const school = {
name: 'ATGUIGU'
}
//直接调用
A() //尚硅谷
B() //尚硅谷
//call
A.call(school); //ATGUIGU
B.cal(school); //尚硅谷
2.不能作为构造实例化对象
let A(name,age) => {
this.name=name;
this.age=age;
}
let me = new A('xiao',123);
console.me //error
3.不能使用arguments变量
let fn = () => {
console.log(arguments);
}
fn(1,2,3) //error
4.简写
-
省略小括号,当形参有且只有一个的时候
let add = n => { return n + 1; }
-
省略花括号,当代码体只有一条语句的时候,此时return也必须省略
let add = n => n+1;
6. 函数参数默认值
ES6允许给函数参数赋默认值
1.与解构赋值结合
function A({host='127.0.0.1',username,password,port}){
console.log(host+username+password+port)
}
A({
username:'ran',
password:'123456',
port:3306
})
7. rest参数
ES6引入rest参数,用于获取函数的实参,用来代替arguments
function date(...args){
console.log(args);
}
date('aaa','bbb','ccc');
8. 扩展运算符
对象中的扩展运算符(...)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中
这里有点需要注意的是扩展运算符对对象实例的拷贝属于一种浅拷贝。肯定有人要问什么是浅拷贝?我们知道javascript中有两种数据类型,分别是基础数据类型和引用数据类型。基础数据类型是按值访问的,常见的基础数据类型有Number、String、Boolean、Null、Undefined,这类变量的拷贝的时候会完整的复制一份;引用数据类型比如Array,在拷贝的时候拷贝的是对象的引用,当原对象发生变化的时候,拷贝对象也跟着变化,比如:
let obj1 = { a: 1, b: 2};
let obj2 = { ...obj1, b: '2-edited'};
console.log(obj1); // {a: 1, b: 2}
console.log(obj2); // {a: 1, b: "2-edited"}
上面这个例子扩展运算符拷贝的对象是基础数据类型,因此对obj2
的修改并不会影响obj1
,如果改成这样:
let obj1 = { a: 1, b: 2, c: {nickName: 'd'}};
let obj2 = { ...obj1};
obj2.c.nickName = 'd-edited';
console.log(obj1); // {a: 1, b: 2, c: {nickName: 'd-edited'}}
console.log(obj2); // {a: 1, b: 2, c: {nickName: 'd-edited'}}
这里可以看到,对obj2
的修改影响到了被拷贝对象obj1
,原因上面已经说了,因为obj1
中的对象c
是一个引用数据类型,拷贝的时候拷贝的是对象的引用。
需要注意的一点是:
如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。
9. Symbol
ES6引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是JavaScript语言的第七种数据类型,是一种类似于字符串的数据类型。
Symbol特点:
- Symbol的值是唯一的,用来解决命名冲突的问题
- Symbol值不能与其他数据进行运算
- Symbol定义的对象属性不能使用for…in循环遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名
1.创建
let s = Symbol('aa');
let s2= Symbol('aa');
console.log(s===s2) //false
let s3 = Symbol.for('bb');
let s4 = Symbol.for('bb');
comsole.log(s3===s4) ///true
Symbol.for()
与Symbol()
这两种写法,都会生成新的 Symbol。它们的区别是,前者会被登记在全局环境中供搜索,后者不会。Symbol.for()
不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key
是否已经存在,如果不存在才会新建一个值。比如,如果你调用Symbol.for("cat")
30 次,每次都会返回同一个 Symbol 值,但是调用Symbol("cat")
30 次,会返回 30 个不同的 Symbol 值。
2.不能与其他数据进行运算
let result = s + 100 //error
let result = s > 100 //error
let result = s + s //error
3.内置Symbol值
除了定义自己使用的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。
class Person {
static [Symbol.hasInstance](param){
console.log(param);
console.log("我被用来检测了");
return false;
}
}
let o = {};
console.log(o instanceof Person); //我被用来检测了,false
对象的Symbol.hasInstance
属性,指向一个内部方法。当其他对象使用instanceof
运算符,判断是否为该对象的实例时,会调用这个方法。比如,foo instanceof Foo
在语言内部,实际调用的是Foo[Symbol.hasInstance](foo)
。
应用
1.给对象添加方法方式一
let game = {
name : 'ran'
}
let methods = {
up:Symbol()
down:Symbol()
}
game[methods.up]=function(){
console.log('aaa');
}
game[methods.down]=function(){
console.log('bbb');
}
console.log(game) // name: 'ran',Symbol(),Symbol()
2.给对象添加方法方式二
let youxi = {
name: '狼人杀',
[Symbol('say')]:function(){
console.log('阿萨德')
}
}
console.log(youxi) // name:'狼人杀',Symbol(say)
10. 迭代器(lterator)
迭代器(lterator)是一种接口,可以为各种不同的数据结构提供一种访问机制(访问接口),任何数据结构部署Iterator
接口,就可以完成该数据解构成员的遍历操作(Iterator 接口主要供for...of
使用)
迭代器对象本质上,就是一个指针对象。通过指针对象的next(),用来移动指针。
【迭代器协议】对象必须提供一个next(),执行该方法要么返回迭代中的下一项,要么就引起一个Stopiteration异常,以终止迭代。
next()返回一个对象,表示当前数据成员的信息。这个对象具有value和done两个属性,value属性返回当前位置的成员,done属性是一个布尔值,表示遍历是否结束,即是否还有必要再一次调用next()。对于遍历器对象来说,done: false和value: undefined属性都是可以省略的。
ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性上。
const xiyou=['AA','BB','CC','DD'];
// for(let v of xiyou){
// console.log(v) // 'AA','BB','CC','DD' //for in保存的是键名,for of保存的是键值
// }
let iterator = xiyou[Symbol.iterator]();
console.log(iterator.next()); //{{value:'唐僧',done:false}}
console.log(iterator.next()); //{{value:'孙悟空',done:false}}
应用
const banji = {
name: "终极一班",
//String类型没有配置Iterator,所以没有被for of 遍历到
stus: [
'aa',
'bb',
'cc',
'dd'
],
//Iterator 内部实现
[Symbol.iterator]() {
let index = 0;
let _this = this;
return {
next: () => {
if (index < this.stus.length) {
const result = { value: _this.stus[index], done: false };
//下标自增
index++;
//返回结果
return result;
} else {
return { value: undefined, done: true };
}
}
}
}
}
for (let v of banji) {
console.log(v); // aa bb cc dd
}
11. 生成器(Generator)
生成器函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同,是一种特殊的函数
function * gen (){ //函数名和function中间有一个 *
yield '耳朵'; //yield是函数代码的分隔符
yield '尾巴';
yield '真奇怪';
}
let iterator = gen();
console.log(iteretor.next());
//{value:'耳朵',done:false} next()执行第一段,并且返回yield后面的值
console.log(iteretor.next()); //{value:'尾巴',done:false}
console.log(iteretor.next()); //{value:'真奇怪',done:false}
1.生成器函数接受next()的参数传递
function* gen(args) {
console.log(args);
let one = yield 111;
console.log(one);
let two = yield 222;
console.log(two);
let three = yield 333;
console.log(three);
}
let iterator = gen('AAA');
console.log(iterator.next()); //执行第一个打印
console.log(iterator.next('BBB')); //next中传入的BBB将作为yield 111的返回结果
console.log(iterator.next('CCC')); //next中传入的CCC将作为yield 222的返回结果
console.log(iterator.next('DDD')); //next中传入的DDD将作为yield 333的返回结果
2.用生成器函数的方式解决回调地狱问题
function one(){
setTimeout(()=>{
console.log('111')
iterator.next()
},1000)
}
function two(){
setTimeout(()=>{
console.log('222')
iterator.next();
},2000)
}
function three(){
setTimeout(()=>{
console.log('333')
iterator.next();
},3000)
}
function * gen(){
yield one();
yield two();
yield three();
}
let iterator = gen();
iterator.next();
3.模拟异步获取数据
function one(){
setTimeout(()=>{
let data='用户数据';
iterator.next(data)
},1000)
}
function two(){
setTimeout(()=>{
let data='订单数据';
iterator.next(data)
},2000)
}
function three(){
setTimeout(()=>{
let data='商品数据';
iterator.next(data)
},3000)
}
function * gen(){
let users=yield one();
console.log(users)
let orders=yield two();
console.log(orders)
let goods=yield three();
console.log(goods)
}
let iterator = gen();
iterator.next();
12. Promise
Promise是ES6引入的异步编程的新解决方案。语法上 Promise是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果。
1.基本特性
<script>
const p =new Promise((resolve, reject)=>{
setTimeout(()=>{
let data='数据库数据'
// resolve(data);
reject(data);
})
})
p.then(function (value){ //成功则执行第一个回调函数,失败则执行第二个
console.log(value)
},function (reason){
console.error(reason)
})
</script>
2.Promise.then()方法
then
方法的第一个参数是resolved
状态的回调函数,第二个参数是rejected
状态的回调函数,它们都是可选的。
then
方法返回的是一个新的Promise
实例(注意,不是原来那个Promise
实例)。因此可以采用链式写法,即then
方法后面再调用另一个then
方法。
采用链式的then
,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise
对象(即有异步操作),这时后一个回调函数,就会等待该Promise
对象的状态发生变化,才会被调用。
getJSON("/post/1.json").then(function(post) {
return getJSON(post.commentURL);
}).then(function (comments) {
console.log("resolved: ", comments);
}, function (err){
console.log("rejected: ", err);
});
上面代码中,第一个then
方法指定的回调函数,返回的是另一个Promise
对象。这时,第二个then
方法指定的回调函数,就会等待这个新的Promise
对象状态发生变化。如果变为resolved
,就调用第一个回调函数,如果状态变为rejected
,就调用第二个回调函数。
Promise.catch()方法
//catch()函数只有一个回调函数,意味着如果Promise对象状态为失败就会调用
//catch()方法并且调用回调函数
<script>
const p = new Promise((resolve, reject) => {
setTimeout(()=>{
reject('出错啦')
},1000)
})
p.catch(reason => {
console.log(reason)
})
</script>
13. 集合
13.1 Set
ES6提供了新的数据结构set(集合)。它类似于数组,但成员的值都是唯一的,集合实现了iterator接口,所以可以使用「扩展运算符』和「 for…of…』进行遍历,集合的属性和方法:
- size返回集合的元素个数
- add增加一个新元素,返回当前集合
- delete删除元素,返回boolean值
- has检测集合中是否包含某个元素,返回boolean值
- filter 后面函数返回结果为true时以数组返回其参数
- clear清空数组
-
<script> let s = new Set(); let s2 = new Set(['A','B','C','D']) //元素个数 console.log(s2.size); //添加新的元素 s2.add('E'); //删除元素 s2.delete('A') //检测 console.log(s2.has('C')); //清空 s2.clear() console.log(s2); </script>
应用
-
<script>
let arr = [1,2,3,4,5,4,3,2,1]
//1.数组去重
let result = [...new Set(arr)]
console.log(result);
//2.交集
let arr2=[4,5,6,5,6]
let result2 = [...new Set(arr)].filter(item => new Set(arr2).has(item))
console.log(result2);
//3.并集
let result3=[new Set([...arr,...arr2])]
console.log(result3);
//4.差集
//filter 后面函数返回结果为true时以数组返回其参数
let result4= [...new Set(arr)].filter(item => !(new Set(arr2).has(item)))
console.log(result4);
</script>
13.2 Map
ES6提供了Map数据结构。它类似于对象,也是键值对的集合。但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map也实现了iterator接口,所以可以使用『扩展运算符』和「for…of…』进行遍历。Map的属性和方法。
Map实例set方法的参数成为map对象的一个数组成员
<script>
let m = new Map();
m.set('name','ran');
m.set('age',18);
// 相当于 Map{["name","ran"],["age",18]}
m.set('change',()=>{
console.log('改变!')
})
let key={
school:'atguigu'
}
m.set(key,['成都','西安']);
//size
console.log(m.size);
//删除
m.delete('name');
//获取
console.log(m.get('change'));
// //清空
// m.clear()
//遍历
for(let v of m){
console.log(v);
}
</script>
14. Class
ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6的class可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
<script>
class shouji {
constructor(brand,price) {
this.brand=brand;
this.price=price
}
call(){
console.log('我可以打电话')
}
}
let A = new shouji('1+',1999);
console.log(A); //shouji {brand: "1+", price: 1999}
</script>
实例属性的新写法:实例属性除了定义在constructor()
方法里面的this
上面,也可以定义在类的最顶层。
class IncreasingCounter {
_count = 0;
get value() {
console.log('Getting the current value!');
return this._count;
}
increment() {
this._count++;
}
}
上面代码中,实例属性_count
与取值函数value()
和increment()
方法,处于同一个层级。这时,不需要在实例属性前面加上this
。
14.1 静态属性和静态方法
静态属性指的是 Class 本身的属性,即Class.propName
,而不是定义在实例对象(this
)上的属性。
用法:静态的可以直接通过类名访问,非静态只能通过对象进行访问
- 带静态修饰符的方法只能访问静态属性
- 非静态方法既能访问静态属性也能访问非静态属性
- 非静态方法不能定义静态变量
- 静态方法不能使用this关键字
- 静态方法不能调用非静态方法,反之可以
注:静态属性、静态方法、非静态属性都可以被继承和隐藏,但是不可以被重写,
非静态方法可以被重写和继承
class MyClass {
static myStaticProp = 42;
constructor() {
console.log(MyClass.myStaticProp); // 42
}
}
14.2 私有属性和私有方法
私有属性是在属性名之前,使用#
表示。
class IncreasingCounter {
#count = 0;
get value() {
console.log('Getting the current value!');
return this.#count;
}
increment() {
this.#count++;
}
}
上面代码中,#count
就是私有属性,只能在类的内部使用(this.#count
)。如果在类的外部使用,就会报错。
const counter = new IncreasingCounter();
counter.#count // 报错
counter.#count = 42 // 报错
这种写法不仅可以写私有属性,还可以用来写私有方法。
class Foo {
#a;
#b;
constructor(a, b) {
this.#a = a;
this.#b = b;
}
#sum() {
return this.#a + this.#b;
}
printSum() {
console.log(this.#sum());
}
}
私有属性也可以设置 getter 和 setter 方法。
class Counter {
#xValue = 0;
constructor() {
super();
// ...
}
get #x() { return #xValue; }
set #x(value) {
this.#xValue = value;
}
}
上面代码中,#x
是一个私有属性,它的读写都通过get #x()
和set #x()
来完成。
静态块
静态属性的一个问题是,它的初始化要么写在类的外部,要么写在constructor()
方法里面。
class C {
static x = 234;
static y;
static z;
}
try {
const obj = doSomethingWith(C.x);
C.y = obj.y
C.z = obj.z;
} catch {
C.y = ...;
C.z = ...;
}
上面示例中,静态属性y
和z
的值依赖静态属性x
,它们的初始化写在类的外部(上例的try...catch
代码块)。另一种方法是写到类的constructor()
方法里面。这两种方法都不是很理想,前者是将类的内部逻辑写到了外部,后者则是每次新建实例都会运行一次。
为了解决这个问题,ES2022 引入了静态块(static block),允许在类的内部设置一个代码块,在类生成时运行一次,主要作用是对静态属性进行初始化。
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 = ...;
}
}
}
上面代码中,类的内部有一个 static 代码块,这就是静态块。它的好处是将静态属性y
和z
的初始化逻辑,写入了类的内部,而且只运行一次。
每个类只能有一个静态块,在静态属性声明后运行。静态块的内部不能有return
语句。
静态块内部可以使用类名或this
,指代当前类。
除了静态属性的初始化,静态块还有一个作用,就是将私有属性与类的外部代码分享。
let getX;
export class C {
#x = 1;
static {
getX = obj => obj.#x;
}
}
console.log(getX(new C())); // 1
上面示例中,#x
是类的私有属性,如果类外部的getX()
方法希望获取这个属性,以前是要写在类的constructor()
方法里面,这样的话,每次新建实例都会定义一次getX()
方法。现在可以写在静态块里面,这样的话,只在类生成时定义一次。
14.3 es6 中的继承
class继承
<script>
class Phone{
constructor(brand,price) {
this.brand=brand;
this.price=price;
}
//父类的成员属性
call(){
console.log('我可以打电话')
}
}
class SmartPhone extends Phone{
constructor(brand,price,color,size) {
super(brand,price);
this.color=color;
this.size=size;
}
photo(){
console.log('拍照');
}
playGame(){
console.log('打游戏');
}
// 若子类出现父类同名成员 则会重写覆盖!
//call(){
// console.log('我可以进行视频通话')
//}
}
const xiaomi=new SmartPhone('小米',1999,'黑色','4.7inch')
xiaomi.call();
xiaomi.photo();
xiaomi.playGame();
</script>
14.4 get和set设置
<script>
class Phone{
get price(){
console.log("价格被读取了")
return 'I LOVE YOU'
}
set price(val){
console.log('价格被修改了')
return val;
}
}
//实例化对象
let s = new Phone();
s.price=12
console.log(s.price) //其实是调用price方法
</script>
15.数值的扩展
Number.EPSILON == 2的-52次方
可以用来设置“能够接受的误差范围”。比如,误差范围设为 2 的-50 次方(即Number.EPSILON * Math.pow(2, 2)
),即如果两个浮点数的差小于这个值,我们就认为这两个浮点数相等。
引入一个这么小的量的目的,在于为浮点数计算,设置一个误差范围。我们知道浮点数计算是不精确的。
function withinErrorMargin (left, right) {
return Math.abs(left - right) < Number.EPSILON * Math.pow(2, 2);
}
0.1 + 0.2 === 0.3 // false
withinErrorMargin(0.1 + 0.2, 0.3) // true
1.1 + 1.3 === 2.4 // false
withinErrorMargin(1.1 + 1.3, 2.4) // true
//二进制和八进制
let b = 0b1010; //2进制
let o = 0o777; //8进制
let d = 100; //10进制
let x = 0xff; //16进制
console.log(x) //255
//检测一个数是否为有限数
console.log(Number.isFinite(100)); //true
console.log(Number.isFinite(100/0)); //false
console.log(Number.isFinite(Infinity)); //false
//检测一个数值是否为NaN
console.log(Number.isNaN(123)) //false
//字符串转数值 截取在出现第一个字符之前的数值
console.log(Number.parseInt('5213123love')); //5213123
console.log(Number.parseFloat('5.123123神器')); //5.123123
//判断是否为整数
console.log(Number.isInteger(5)); //true
console.log(Number.isInteger(2.5)); //false
//将小数部分抹除
console.log(Math.trunc(3.45345345345)) //3
//检测一个数到底是正数、负数、还是0
console.log(Math.sign(100)) //1
console.log(Math.sign(0)) //0
console.log(Math.sign(-123)) //-1
安全整数和 Number.isSafeInteger()
JavaScript 能够准确表示的整数范围在-2^53
到2^53
之间(不含两个端点),超过这个范围,无法精确表示这个值
Math.pow(2, 53) // 9007199254740992
9007199254740992 // 9007199254740992
9007199254740993 // 9007199254740992
Math.pow(2, 53) === Math.pow(2, 53) + 1
// true
ES6 引入了Number.MAX_SAFE_INTEGER
和Number.MIN_SAFE_INTEGER
这两个常量,用来表示这个范围的上下限。
Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1
// true
Number.MAX_SAFE_INTEGER === 9007199254740991
// true
Number.MIN_SAFE_INTEGER === -Number.MAX_SAFE_INTEGER
// true
Number.MIN_SAFE_INTEGER === -9007199254740991
// true
而Number.isSafeInteger()
则是用来判断一个整数是否落在 这个范围 之内。
Number.isSafeInteger('a') // false
Number.isSafeInteger(null) // false
Number.isSafeInteger(NaN) // false
Number.isSafeInteger(Infinity) // false
Number.isSafeInteger(-Infinity) // false
Number.isSafeInteger(3) // true
Number.isSafeInteger(1.2) // false
Number.isSafeInteger(9007199254740990) // true
Number.isSafeInteger(9007199254740992) // false
Number.isSafeInteger(Number.MIN_SAFE_INTEGER - 1) // false
Number.isSafeInteger(Number.MIN_SAFE_INTEGER) // true
Number.isSafeInteger(Number.MAX_SAFE_INTEGER) // true
Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1) // false
实际使用这个函数时,需要注意。验证运算结果是否落在安全整数的范围内,不要只验证运算结果,而要同时验证参与运算的每个值。
Number.isSafeInteger(9007199254740993)
// false
Number.isSafeInteger(990)
// true
Number.isSafeInteger(9007199254740993 - 990)
// true
9007199254740993 - 990
// 返回结果 9007199254740002
// 正确答案应该是 9007199254740003
所以,如果只验证运算结果是否为安全整数,很可能得到错误结果。下面的函数可以同时验证两个运算数和运算结果。
function trusty (left, right, result) {
if (
Number.isSafeInteger(left) &&
Number.isSafeInteger(right) &&
Number.isSafeInteger(result)
) {
return result;
}
throw new RangeError('Operation cannot be trusted!');
}
trusty(9007199254740993, 990, 9007199254740993 - 990)
// RangeError: Operation cannot be trusted!
trusty(1, 2, 3)
// 3
16. 对象方法扩展
<script>
//1.Object.is 判断两个值是否完全相等
console.log(Object.is(120,120)) //true
console.log(Object.is(NaN,NaN)) //false
//2.Object.assign 对象的合并
const a = {
name:'ran',
age:12
}
const b = {
pass:'i love you'
}
console.log(Object.assign(a,b)) //{name:'ran',age:'12',pass:'i love you'}
//3.Object.setPrototypeOf 设置原型对象 Object.getPrototypeof
const school = {
name:'尚硅谷'
}
const cities = {
xiaoqu:['北京','上海']
}
Object.setPrototypeOf(school,cities)
console.log(Object.getPrototypeOf(school)) //{xiaoqu: Array(2)}
console.log(school) //{name: "尚硅谷"}
</script>
17.模块化
模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。
模块化的好处:
- 防止命名冲突
- 代码复用
- 高维护性
- 模块化规范产品
ES6之前的模块化规范有:
CommonJS ====> NodeJS、Browserify
AMD ====> requireJS
CMD ====> seaJS
模块功能主要有两个命令构成:export和import
- export命令用于规定模块的对外接口
- import命令用于输入其他模块提供的功能
export let school = '尚硅谷'
export function teach(){
console.log('教技能')
}
<script type="module">
import * as m1 from "./src/js/m1.js";
console.log(m1);
</script
17.2 暴露语法汇总
统一暴露
//统一暴露
let school = '尚硅谷';
function findjob(){
console.log('找工作吧');
}
//export {school,findjob}
默认暴露
//默认暴露
export default {
school:'ATGUIGU',
change:function(){
console.log('我们可以改变你')
}
}
17.3 引入语法汇总
通用导入方式
import * as m1 from "./src/js/m1.js"
import * as m2 from "./src/js/m2.js"
import * as m3 from "./src/js/m3.js"
解构赋值方式
import {school,teach} from "./src/js/m1.js"
import {school as guigu,findJob} from "./src/js/m2.js"
import {default as m3 } from "./src/js/m3.js"
简便形式(只针对默认暴露)
import m3 from "./src/js/m3.js"
17.4 模块化方式2
<script src="./src//js/app.js" type=modeule></script>
ES7
- Array.prototype.includes:用来检测数组中是否包含某个元素,返回布尔类型值
- 在ES7中引入指数操作符**,用来实现幂运算,功能与Math.pow结果相同
<script>
//include
const mingzhu = ['西游记','红楼梦','水浒传','三国演义']
console.log(mingzhu.includes('西游记')) //true
console.log(mingzhu.includes('金瓶梅')) //false
//**
console.log(2**10) // 1024
</script>
ES8
1. async函数
async和await两种语法结合可以让异步代码像同步代码一样
async函数:
- async函数的返回值为promise对象
- async返回的promise对象的结果值由async函数执行的返回值决定
async function fn(){
//1.如果返回的是一个非Promise的对象,则fn()返回的结果就是成功状态的Promise对象,值为返回值
//2.如果返回的是一个Promise对象,则fn()返回的结果与内部Promise对象的结果一致
//3.如果返回的是抛出错误,则fn()返回的就是失败状态的Promise对象
return new Promise((resolve,reject)=>{
resolve('成功的数据');
});
}
const result = fn();
result.then(value=>{
console.log(value) //成功的数据
},reason=>{
console.log(reason)
})
2. await表达式
- await必须放在async函数中
- await右侧的表达式一般为promise对象
- await可以返回的是右侧promise成功的值
- await右侧的promise如果失败了,就会抛出异常,需要通过try…catch捕获处理
<script>
//创建Promise对象
const p = new Promise((resolve, reject) => {
// resolve("成功的值")
reject("失败了")
})
//await 必须放在async函数中
async function main() {
try {
let res = await p;
console.log(res);
} catch (e) {
console.log(e);
}
}
//调用函数
main() //失败了
</script>
3.ES8对象方法扩展
const school = {
name: '尚硅谷',
cities: ['北京', '上海', '深圳'],
xueke: ['前端', 'Java', '大数据', '运维']
};
//获取对象所有的键
console.log(Object.keys(school));
//获取对象所有的值
console.log(Object.values(school));
//entries,用来创建map
console.log(Object.entries(school));
console.log(new Map(Object.entries(school)))
//对象属性的描述对象
const obj = Object.create(null, {
name: {
value: '尚硅谷',
//属性特性
writable: true,
configurable: true,
enumerable: true,
}
})
console.log(Object.getOwnPropertyDescriptors(obj));
const o = {
foo: 123,
get bar() { return 'abc' }
};
console.log(Object.getOwnPropertyDescriptors(o));
ES9
1. 运算扩展符与rest参数
<script>
function connect({host,port,...user}){
console.log(host);
console.log(port);
console.log(user)
}
connect({
host:'127.0.0.1',
port:3306,
username:'root',
password:'root',
type:'master'
}) //127.0.0.1 3306 {username: "root", password: "root", type: "master"}
</script>
<script>
const AA={
username:'ran'
}
const BB={
password:'lyyrhf'
}
const CC={
job:'Java'
}
const D={...AA,...BB,...CC};
console.log(D) //{username: "ran", password: "lyyrhf", job: "Java"}
</script>
ES10
1. 对象扩展方法
<script>
//二维数组
const res = Object.fromEntries([
['name','RHF'],
['cities','成都','武汉']
])
console.log(res) //{name: "RHF", cities: "成都"}
//Map
const m = new Map();
m.set('name','ranhaifeng')
const result = Object.fromEntries(m)
console.log(result); //{name: "ranhaifeng"}
</script>
2. 字符串扩展方法
<script>
//trim
let str= ' asd '
console.log(str) //asd
console.log(str.trimStart()) //asd 清空头空格
console.log(str.trimEnd()) // asd 清空尾空格
</script>
3. flat与flatMap
<script>
const arr = [1,2,3,[4,5,6,[7,8,9]]]
//参数为深度,是一个数字
console.log(arr.flat(2)) //[1,2,3,4,5,6,7,8,9]
const arr2=[1,2,3,4]
const result = arr2.flatmap(item => [item * 10]); //如果map的结果是一个多维数组可以进行flat 是两个操作的结合
</script>
4. Symbol的description
用来获取Symbol的字符串描述
let s = Symbol('尚硅谷');
console.log(s.description) //尚硅谷
5 .可选链操作符 ?.
//相当于一个判断符,如果前面的有,就进入下一层级
function main(config){
const dbHost = config?.db?.host
console.log(dbHost) //192.168.1.100
}
main({
db:{
host:'192.168.1.100',
username:'root'
},
cache:{
host:'192.168.1.200',
username:'admin'
}
})
6. BigInt类型
//大整型
let n = 521n;
console.log(n,typeof(n)) // 521n "bigint"
//函数
let n = 123;
console.log(BigInt(n)) // 123n //不要使用浮点型,只能用int
//大数值运算
let max = Number.MAX_SAFE_INTEGER; // 9007199254740991
console.log(max +1) // 9007199254740992
console.log(max +2) // 9007199254740992 出问题了
console.log(BigInt(max)+BigInt(1)) 9007199254740992n
console.log(BigInt(max)+BigInt(2)) 9007199254740993n
7. 绝对全局对象globalThis
console.log(globalThis) //window //适用于复杂环境下直接操作window