1 let关键字和const关键字
let关键字
①作用
声明变量,用来代替var。let声明的变量具备变量的基本特征
let 变量名=值
②特点
-
let声明的变量不能重复声明
-
let声明的变量不会变量提升
-
let声明的变量不会再作为顶层全局的属性
-
let声明的变量在全局作用域和局部作用域的基础上新增了块级作用域
块级作用域
产生块级作用域的语法:
// 1. {} 语法
{
语句;
}
// 2. for / while 循环结构
for () {
}
while () {
}
// 3. if / else / switch 分支结构
if () {
} else {
}
switch () {
}
// 4 try catch
try {
} catch ();
const关键字
作用
用于定义常量,常量的特点是值不能修改。
为了与变量区分,一般常量名一般采用全部大写(潜规则)
特点
-
const声明的常量,不能重新赋值也不能重新声明
-
全局变量不会作为顶层全局对象的属性值
-
变量不会提升
-
常量在全局作用域和局部作用域的基础上新增了块级作用域
数组解构赋值
//变量声明赋值
let [a,b,c]=[1,2,3];
//变量赋值
[a,b,c]=[5,6,7];
// 解构赋值可以有默认值
let [d,e,f=300]=[433,45];
//数组解构可以是直接量也可以是变量
let arr=[5,6,4,3];
let[aa,bb,cc]=arr;
注意:
1. 数组解构赋值本质是按照索引进行解构的
等号右边必须是数组或者伪数组(具有索引结构的),否则报错
对象解构赋值
//对象解构赋值,按照属性名进行匹配
let {name:'username',age:'userage'}={name:'anni',age:100};
//得到username='anni',age=100,属性名不是变量
//属性名和变量名相同则可以简写
let {a,b}={a:55,b:'anni'}
//变量名可以有默认值
let {c, d=100}={c:89}
一切皆对象,一切都可以作为对象解构赋值(除了null和undefined)
对象解构赋值不需要按照索引,只要有属性就可以
解构赋值用于传参
解构赋值可以用于变量声明时赋值或者修改赋值;也可以给形参传值
//数组的解构赋值
function giao([a,b]){
console.log(a+b);
}
giao([100,200]);//300
//对象解构赋值
function woligiao({name,age,address}){
console.log(name,age,address);
}
woligiao({name:'anni',age:99,address:'上海'});
解构赋值应用场景
① 交换两个变量的值
let [a,b]=[5,6];
[a,b]=[b,a]
字符串新增特性
模板字符串
① 定义
使用反引号声明的字符串,称为模板字符串
//定义变量
let [msg1,msg2]=['are you ok','i am fine'];
//模板字符串
let html=`
<ul>
<li>${msg1}</li>
<li>${msg2}</li>
<li>${100+200}</li>
</ul>
`
②特性
-
在模板字符串中换行会作为字符串内容
-
模板字符串可以使用
${}
嵌入变量,表达式都可以嵌入到模板字符串中
其他特性与单双引号字符串一致
字符串实例新增方法
ES3方法:
indexOf() 返回参数在字符串中的位置
lastIndexOf()
toUpperCase()
toLowerCase()
slice()
subString()
subStr()
split()
charCodeAt()
ES5方法:
trim() 去除字符串两边的空格,全是空格的字符串会转换为空字符串
ES6+方法:
repeat() 字符串重复
includes() 判断字符串中是否包含某个值,返回布尔值;第二个参数可以指定查找开始的位置
startsWith()判断字符串是否以某个值开头,返回布尔值;第二个参数指定开始查找的位置
endsWith() 判断字符串是否以某个值结束,返回布尔值;第二个参数指定字符串查找的长度
padStart() 将字符串补充到指定的长度,在前面添加,第一个参数指定总长度,第二个参数指定填充内容(ES2017)
padEnd() 将指定字符串补充到指定长度,将填充内容添加到后面,第一个参数是指定总长度,第二个参数指定填充内容(ES2017)
trimStart()删除字符串左边的空格(ES2019)
trimEnd()删除字符串右边的空格(ES2019)
数值新增特性
新增二进制和八进制表示方法
let num1 = 0b101;//二进制表示方法
let num2 = 0o101;//八进制表示方法
Number 构造函数本身新增方法和属性
ES3属性:
Number.MAX_VALUE
Number.MIN_VALUE
ES6属性和方法:
Number.isNaN() 同全局属性isNaN()
Number.isFinit() 同全局函数 isFinite()
Number.parseInt() 同全局函数 parseInt()
Number.parseFloat() 同全局函数 parseFloat()
Number.isInteger() 判断是否是整数,返回布尔值
Number.isSafeInteger()判断是否是安全整数
Number.MAX_SAFE_Integer 返回最大安全整数
Number.MIN_SAFE_Integer 返回最小安全整数
Number.EPSILON 返回JS的最小精度
安全整数:
在 -2^53 到 2^53 之间的整数称之为安全整数, 不存在计算精度问题。超出该范围的数字计算存在精度问题。
Math新增方法
ES3属性方法:
Math.PI
Math.abs()
Math.sqrt()
Math.pow()
Math.round()
Math.floor()
Math.ceil()
Math.random()
Math.max()
Math.min()
ES 6新增方法:
Math.trunc() 截取数字的整数部分,类似于parseInt()
Math.sign() 参数是正数返回1,参数是负数返回-1,参数等于0返回0,参数是NaN返回NaN
Math.hypot() 求所有参数的平方和的平方根
指数运算符
console.log(3**3); //27
新增原始数据类型bigint
如果要进行计算的数字超过安全整数的范围,还想保证精度的话,可以定义成 bigint 类型。
bigint 类型用于对大的整数进行计算。
bitint 类型是原始类型,使用 typeof 判断返回 bigint
。
// 定义 bigint 类型的数据
let n1 = 100n;
console.log(n1);
console.log(typeof n1); // bigint
console.log('');
// bigint 类型数据只能与 bigint 类型数据进行数学计算
console.log(n1 + 40n);
// console.log(n1 * 3); // 报错
console.log('');
// bigint 类型数据可以与number类型比较,自动类型转换
console.log(n1 === 100); // false
console.log(n1 == 100); // true
console.log(n1 > 40); // true
console.log(n1 > 400); // false
console.log('');
// 也有 二进制方式 八进制方式 十六进制方式
let n2 = 0b101n;
let n3 = 0o101n;
let n4 = 0x101n;
console.log(n2, n3, n4);
函数新增特性
函数参数的默认值
ES6允许为函数的参数设置默认值,即直接写在参数的最后面
function fun(name,age=100){
console.log(name,age);
}
fun('姬发');// 姬发 100
fun('姬发',50)//姬发 50
rest参数
ES6引入rest参数(形式为...变量名
),用于获取函数的多余参数,用来代替arguments对象
rest参数搭配的变量是一个数组,将多余的参数放到数组中
//计算所有参数之和
let getSum=(
rest 参数特点:(相比于arguments)
rest参数得到的是纯数组,arguments得到的是伪数组
rest参数获取的是多余的参数,arguments获取的是所有的参数
rest必须写在形参的最后一个,arguments不需要写也直接产生
箭头函数
①语法
//创建有个箭头函数
let fn=(name,age)=>{
console.log(`我叫${name},我今年${age}岁`)
};
//调用函数
fn('gmy','23');
箭头函数简略写法:
省略()的条件:只有一个参数,多个参数或者没有参数都不能省略。
省略{}的条件:函数体内只有一条返回值语句,省略{}和return
②箭头函数的特点
-
箭头函数没有自己的this,会取上层作用域的this;因此箭头函数中的this与调用者无关,只与箭头函数声明的地方有关
-
箭头函数没有arguments但是可以有rest参数
-
箭头函数不可以作为构造函数,new则报错
-
箭头函数不能使用yield,无法创建生成器函数
函数参数尾逗号(ES2017)沙雕行为
函数的最后一个参数后面可以添加尾逗号,不论报错!
不论是函数声明还是函数调用,都可以加尾逗号。
function fn(name, age, ) {
console.log(name, age);
}
fn('安妮', 109);
fn('安妮', 108, );
// ES5 中就已经允许对象的尾逗号
let obj = {
name: '曹操',
age: 100,
};
数组的新特性
①扩展运算符
扩展(spread)运算符是三个点 ...
。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
不但可以拆解纯数组,伪数组也可以被拆解。
function fun(a,b,c){
console.log(a,b,c,a+b+c);
}
var nums=[100,200,300,250];
fun(nums)//输出[100,200,300,250],undefined,undefined,100,200,300,250undefinedundefined
fun(
②扩展运算符的应用场景
1)用于函数传参
//1.函数传参
let arr = [123,54,65,324];
//获取最大值
console.log(Math.max(
2)复制数组
let arr=[789,534,54,234];
var arr2=[
3)合并数组
let arr1=[43,54,56];
let arr2=[24,6,234];
var arr3=[
4)把类数组转换为纯数组
var arr=[
rest 元素
扩展运算符用于数组解构赋值, rest 元素对应的变量获取由剩下的值组成的数组。
rest 必须放在最后,本质与 rest 参数是一致的。
// 数组解构赋值
let [first,
Array构造函数本身新增的方法
ES6新增方法:
Array.from() 把伪数组转换为纯数组
Array.of() 创建新数组,参数会作为数组中的元素,可以设置任意量的参数,基本可以用来代替Array()和new Array()
Array实例新增方法
ES3: length join() slice() push() pop() unshift() shift() concat() splice() reverse() sort() ES5: indexOf() lastIndexOf() forEach() filter() map() some() every() reduce() reduceRight() ES6: find(callback) 返回数组中第一个满足条件的元素,参数是回调函数,回调函数接受元素和索引,回调函数返回true表示条件满足 findIndex(callback) 返回数组中第一个满足条件的参数的索引,参数是回调函数,回调函数接受元素和索引,回调函数返回true表示条件满足 fill(val [,start][,end]) 用于填充数组,把数组中的指定元素替换为val,后两个参数不填则替换全部 entries() 返回由数组的索引和值构成的遍历器对象 keys() 返回由数组的索引组成的遍历器对象 values() 返回右数组的值组成的遍历器对象 includes(val) 判断数组中是否包含指定值,返回布尔值(可以判断NaN) flat(num) 用于拉平数组(把多维数组转换为一维数组),参数可以指定拉多少层,默认是1,可以设置参数为Infinity(拉平任意维数组) filterMap() 先对数组Map处理再进行flat操作,只能拉平一层
对象新特性
对象声明时候的简写
如果属性的值用变量来表示,恰好属性名与变量名一致,可以简写
//定义四个变量 let [name,age,address,grade]=['曹操',32,'许昌','魏国']; //声明函数 function getInfo(){ console.log('getInfo'); } //声明对象 let obj={ name, age, address, getInfo };
声明对象时方法的简写
//定义对象 let user={ name:'司马懿', age:99, aoligei(){ } }
属性名表达式
ES5 中,在使用属性的时候,如果属性名用变量表示或者属性名不符合标识符规范,可以使用[]
语法。
ES6,在创建对象的时候,也可以用 []
来指定属性名,[]
内可以写表达式(变量、直接量、运算表达式),表达式的值会作为属性名。
//声明变量 let prop='address'; //创建对象 let obj={ [prop]:'上海', [1+7]:'丑', 'user-name':'文丑', ['grade']:66 }
super 关键字
-
supper指向的是方法所在的原型对象,this指向的是调用该方法的对象;
-
super只与声明时所在的对象有关系,与哪个调用该方法无关
-
super只能写在声明方法的简写方式中
对象的扩展运算符
扩展运算符如果用于数组,则可以把数组转换为由逗号隔开的参数序列。
扩展运算如果由对象使用,可以把对象转为由逗号隔开的键值对序列。
①对象的扩展运算符的应用场景
-
用于对象的复制
-
用于对象合并
var obj3={...obj1,...obj2};
②对象的扩展运算符用于对象解构赋值
解构赋值时,变量名前面加扩展运算符,会得到一个对象,带扩展运算符的变量要写在最后面
//用于解构赋值 let {gdx,...df}={name:534,gdx:'ok',ok:54,fls:'fsdj'}; console.log(df);// 得到df为{name: 534, ok: 54, fls: "fsdj"}
Object构造函数本身新增的方法
ES5方法: Object.create() 创建一个对象,指定对象的原型 Object.definedProperty() 添加属性,并指定属性的特性 Object.definedProperties() 添加多个属性,并指定属性的特性 Object.getOwnPropertyDescriptor() 获取对象中属性的特性 ES6方法: Object.is(arg1,arg2) 用来判断参数是否相等,返回布尔值,与===的区别是,+0和-0不相等,NaN和NaN相等 Object.assign(target,source1,source2...) 合并对象,把第二个参数开始合并到第一个参数,并返回第一个参数(用于:对象合并,对象复制,对象默认值) Object.keys(obj) 返回对象obj中所有属性名的数组集合 Object.values(obj) 返回obj中所有属性值的数组集合 Object.entries(obj) 返回对象中所有的属性名和属性值组成的二维数组 Object.fromEntries() Object.entries的逆运算 Object.getOwnPropertyDescriptors() 获取对象中所有的特性信息
Object 实例新增的属性
__proto__
属性(前后各两个下划线),用来读取或设置当前对象的原型对象。
__proto__
前后的双下划线,说明它本质上是一个内部属性,而不是一个正式的对外的 API,只是由于浏览器广泛支持,才被加入了 ES6。
Class语法
使用Class定义类
//定义类 给类设置属性和方法 class User{ //给实例设置属性,属性添加到实例本身 name='安妮'; sex='女'; //给实例添加方法,添加到实例的原型 say(){ console.log(`我叫${this.name},我的性别是${this.sex}`); } }
类的语法特性:
-
类的本质还是构造函数,typeof返回function
-
类只能用来实例化不能调用
-
类中智能定义属性和方法,不能直接使用this(方法中才能写this),不能写其他语句
-
类中定义的属性会添加到实例的本身,类中定义的方法会添加到实例的原型上面
类中定义构造器方法
//定义类 给类设置属性和方法 class User{ name='null'; age='null'; address='null'; //定义构造器方法 在实例化的过程中,自动调用 constructor(name,age,address){ //给属性赋值 this.name=name; this.age=age; this.address=address; } //给实例设置方法 say(){ console.log(`我叫${this.name},今年${this.age}岁,住在${this.address}`); } } //实例化 let u1=new User('曹操',99,'许昌 '); let u2=new User('刘备',99,'汕头')
构造器方法在类被实例化的时候,会自动调用;可以接受类名后面()里的参数。
构造器方法中的 this 指向实例。
一般用于实例化的时候为属性赋值。
静态方法
在类中的方法前面加static关键字,就表示该方法不会添加到实例的原型上,而是添加到实例的本身,被称为静态方法
class Foo{ static classMethod(){ return 'hello'; } }
属性名前面添加static,也会创建属性,(类的本身属性)非标准
访问器属性
//创建类 class Person{ //定义属性 firstName=null; lastName=null; //构造器方法 constructor(firstName,lastName){ this.firstName=firstName; this.lastName=lastName; } //访问器属性 读取 get fullName(){ return this.firstName+'·'this.lastName; } //访问器属性 设置 set fullName(val){ if(val.includes('·')){ this.firstName=val.split('·')[0]; this.lastName=val.split('·')[1]; } } }
实现继承
使用关键字extends继承
//定义父类 class Animal{ //设置属性 color:null; weight:null; //吃 eat(){ console.log('我是动物我会吃'); } } //定义子类 class Person extends Animal{ name=null; say(){ console.log(`我叫${this.name}, 我的肤色是${this.color}, 我的重量是${this.weight}`); } }
子类继承了父类之后,子类的实例,不但可以使用自己的类定义的属性和方法,还可以使用父类上定义的属性和方法。
单继承:一个父类可以有多个子类,但是一个子类只能有一个父类。
继承的原理: 子类实例的原型指向父类的一个实例。
方法和属性的重写
子类如果定义了与父类重名的属性和方法,会重写
super关键字
子类如果重写构造器方法,必须在里面用super()调用父类的构造器方法。
子类重写构造器方法只能增量修改
super()只能在子类构造器方法中调用,且必须写在最前面
// 定义子类 class Person extends Animal { // 设置属性 name = null; // 重写父类的构造器方法 constructor(name, color, weight) { // 调用super() 相当于调用父类的构造器方法 super(color, weight); this.name = name; } }
继承系统类(内置的构造函数)
允许子类继承系统内置的类(Array、Function、Object ...),继承内置类的特性。
// 声明类 class MyArray extends Array { // 重写构造器方法 constructor(...args) { super(...args); } }
新增元素数据类型Symbol
1)Symbol 是 JavaScript 新增的一种原始类型数据,typeof 会返回 symbol
。
2)通过调用 Symbol() 可以创建 Symbol 类型的数据,注意, Symbol 只能调用不能实例化。
3)每创建一个 symbol 类型的数据,都是唯一的。其实,对象类型的数据,每创建一个实例也都是唯一了,而 symbol 作为一个原始类型,具有了对象类型的这一个特点。
4)Symbol 类型的数据可以作为对象的属性名。
新增数据类型Set和Map
set
①Set数据类型
ES6新增的数据类型,跟数组类型相似,是值的集合,但是不能重复
值可以是任意类型
②Set构造函数
set函数可以接受一个数组(或者具有iterable接口的其他数据解构)作为参数,用来初始化。
//创建Set数据 const s=new Set([1,3,4,5]); //将set转换为数组 var arr=[...s];
③Set实例的方法/属性
add() 添加一个值 delete() 删除一个值 has() 判断是否包含某个值,返回布尔值 clear() 清空Set所有值 forEach() 遍历 keys() 返回遍历器对象 values() 返回遍历器对象 entries() 返回遍历器对象 size 得到长度(类似于数组的length)
④利用Set结构数组去重
let arr=[5,6,4,5,6]; let newArr=[...new Set(arr)];
WeakSet
①WeakSet数据类型
WeakSet结构与Set类似,也是不重复的值的集合,但是它与Set有两个区别:
1)首先,WeakSet成员只能是对象
2)WeakSet不可遍历
②WeakSet构造函数
作为构造函数,WeakSet可以接受一个数组(或者其他可遍历对象)作为参数。该数组的所有成员,都会自动成为WeakSet的实例对象的成员
作为参数的数组(或者具有 iterable 接口的其他数据结构)的成员只能是对象。
③WeakSet实例的方法
add(val) delete(val) has(val)
Map
①Map类型数据
类似于对象,也是键值对的集合,但是“键”的范围不限于字符串各种类型的值(包括对象)都可以当做键。
②Map构造函数
任何具有iterator接口、且每个成员都是一个双元素的数据结构(数组和可遍历对象)都可以当做Map构造函数的参数
//用二维数组作为参数 let map=new Map([ [1,2], ['ok','fine'], [{name:'anni'},1] ]); //用Set数据结构作为参数 let set=new Set([ ['shit','bol'], [name,'曹操'] ]); let map1=new Map(set);
③Map实例的方法
get(key) 根据key获取值 set(key,val) 根据key设置值 has(key) 判断是否存在指定的key delete(key) 删除对应的key和value clear() 清除所有的键值对 keys() 获取所有的key组成的遍历器对象 values()获取所有的value组成的遍历器对象 entries() 获取所有的键值对组成的遍历器对象 forEach(callback)用于遍历 size属性,获取键值对的数量
WeakMap
① WeakMap 数据类型
WeakMap结构与Map结构类似,也是用于生成键值对的集合,WeakMap 与Map 的区别有两点:
1)WeakMap只接受对象(所有的对象类型,除了原始类型)作为键名,不接受其他类型的值作为键名。
2)不可遍历。
② WeakMap 构造函数
// WeakMap 可以使用 set 方法添加成员 const wm1 = new WeakMap(); const key = {foo: 1}; wm1.set(key, 2); wm1.get(key) // 2 // WeakMap 也可以接受一个数组, // 作为构造函数的参数 const k1 = [1, 2, 3]; const k2 = [4, 5, 6]; const wm2 = new WeakMap([[k1, 'foo'], [k2, 'bar']]); wm2.get(k2) // "bar"
③ WeakMap 实例的方法
set(key, value) get(key) has(key) delete(key)
Iterator遍历器对象和可遍历对象
Iterator遍历器对象
1)iterator(遍历器)是一种接口,为各种不同数据结构提供统一访问机制,任何数据只要部署Iterator接口,就可以完成遍历操作。
2)每个Iterator(遍历器)都有一个next()方法
3)Iterator遍历器内部存在一个指针,指针指向遍历器的第一个数据,调用next(),会取出指针指向的数据,并且指针下移;
4)每一次调用next()方法,都会返回数据结构的当前成员的信息,具体来说,就是返回包含value
和down
两个属性,的对象。其中value属性是当前成员的值,down
属性是一个布尔值表示遍历是否结束;
var set = new Set([100,200,300,400]); var setIterator=set.keys(); let res=null; do{ res=setIterator.next(); if(res.value!==undefined){ console.log(res.value); } }while(!res.done);
iterable可遍历对象
①什么是可遍历对象
1)吧部署了Iterator(遍历器对象)接口的数据结构称之为iterable(可遍历对象)
2)iterator 接口部署在了数据结构的 Symbol.iterator 属性上,换句话说一个对象,只有具有 Symbol.iterator 属性,且该属性指向一个返回 iterator(遍历器对象) 的函数, 该对象就是 iterable(可遍历对象)。
const obj={ [Symbol.iterator]:function(){ return{ next(){ return { value:1, done:true }; } } } }
②原生实现了Iterator接口的类型(可遍历对象)
Array Set Map String Arguments NodeList HTMLCollection
③ 可遍历对象和遍历器对象的区别和关系
iterator(遍历器对象)也是iterable(可遍历对象);但是,iterable(可遍历对象)不一定是iterator(遍历器对象)。
遍历器对象本身也部署了 iterator 接口。
可遍历对象不一定拥有 next 方法.
④ 可遍历对象和类数组(伪数组)
类数组(伪数组): 对象中有索引结构,有length
属性,就是伪数组。
可遍历对象: 部署了 iterator 接口的对象,才是可遍历对象。
调用可遍历对象Iterator接口的场合
调用Iterator时说明必须使用可遍历对象
数组解构赋值 使用数组扩展运算符 Array.from() for of遍历 Set 构造函数的参数 WeakSet 构造函数的参数 Map 构造函数的参数 WeakMap 构造函数的参数 Promise.all Promise.race
for of
for of结构可以用来遍历对象(包括遍历器对象),在遍历可遍历对象的时候自动调用Iterator接口
var arr=['red', 'green', 'blue']; for(let k of arr ){ console.log(k); //red', 'green', 'blue' }
生成器generator
定义
生成器就是能创建遍历器的函数
如何定义
function* gen(){ } typeof gen; //function let iter=gen();
调用生成器函数无论里面有没有return,都会返回一个遍历器对象
yield关键字
1)yield关键字返回一个值,遍历的时候每次得到的就是yield的值
2)调用next(),执行到yield就会停止,下次调用next(),执行到下一个yield停止
调用生成器函数时,函数内部代码不会执行,只有调用next()方法的时候才开始执行内部代码,执行到以yield停止
function* gener(){ yield 值; yield 值; yield 值; yield 值; }
return 可以终止生成器遍历
利用生成器对象部署Iterator接口
//创建obj对象 var obj={ name: '安妮', age: 89, address: '上海', //部署Iterator接口 [Symbol.iterator]:function*(){ for(var k in obj){ yield [i, obj[i]] } } }
模块(module)
定义模块
//module1.js export var firstName='anni'; export function say(){ console.log('hello world'); }
//module2.js var user='典韦'; function skill(){ console.log('奥利给'); } export default{ user, skill }
模块导入
//导入模块一 import {firstName,say} from 'module1.js' //使用导入的变量、函数 console.log(firstName); say();
//导入模块二 import myMo from 'moudule.js' //以对象形式传入 myMo.user; myMo.skill();
新增的运算符 (ES2020)
可选链运算符 ?.
可选链运算符( ?.
)允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。
?.
操作符的功能类似于 .
链式操作符,不同之处在于,在引用为空(null 或者 undefined) 的情况下不会引起错误,该表达式短路返回值是 undefined
。与函数调用一起使用时,如果给定的函数不存在,则返回 undefined
。
当尝试访问可能不存在的对象属性时,可选链操作符将会使表达式更短、更简明。
用于调用对象属性的属性的时候,链式调用
// 使用对象属性的属性,需要现判断对象的属性是否存在 // 如果 obj 中没有 users 属性,会报错!!!!! if (obj.users.age) { } // 使用嵌套方式避免报错 if (obj.usrs) { if (obj.users.age) { } } // 使用 && 避免报错 if (obj.users && obj.users.age) { } // 使用可选链运算 推荐!!!!!!!!! 牛逼!!!!!!!!!!!!!! if (obj.users?.age) { }
// 如果 obj 下没有 getInfo 方法,会报错 obj.getInfo(); // 即使 obj 下没有 getInfo 方法,也不会报错! obj.getInfo?.();
空值合并运算符??
空值合并运算符(??
)是一个逻辑操作符,当左侧的操作数为 null 或者 undefined 时,返回其右侧操作数,否则返回左侧操作数。
与逻辑或运算符符(||
)不同,逻辑或操作符会在左侧操作数为假值时返回右侧操作数。也就是说,如果使用 ||
来为某些变量设置默认值,可能会遇到意料之外的行为。比如为假值(例如,''
或 0
)时。
console.log(null ?? 2000); // 2000 console.log(undefined ?? 2000); // 2000 console.log(false ?? 2000); // false console.log(0 ?? 2000); // 0 console.log('' ?? 2000); // 空字符串 console.log(NaN ?? 2000); // NaN