# es6 语法
# 1、判断 JavaScript 数据类型
1、typeof()函数
console.log(
typeof 1, //number
typeof "1", //string
typeof true, //boolean
typeof undefined, //undefined
typeof null // object
);
因为 typeof null 返回的是 object,但是 null 这个是 string 类型的,没法用来区分引用数据类型,所以 typeof 一般都是用于判断值类型数据(number,string,boolean,undefined)
2、instanceof 方法
let obj = {};
obj instanceof Object; //true
let arr = [];
arr instanceof Array; //true
let now = new Date();
now instanceof Date; //true
let func = function() {};
func instanceof Function; //true
let str = "string";
let PDstr = str instanceof String; //false
console.log(PDstr);
因为上面例子中 PDstr 返回的是 false,但是这个实例又是 string 类型的,所以无法对值类型进行判断,instanceof 一般用来判断一个变量是否是某个对象的实例,所以对于引用类型我们使用 instanceof 来进行类型判断。
3、Object.prototype.toString.call()
let num1 = 1;
let num2 = new Number(1);
let numB = Object.prototype.toString.call(num1) == "[object Number]"; //true
Object.prototype.toString.call(num2) == "[object Number]"; //true
console.log(numB);
let att = [];
Object.prototype.toString.call(att) == "[object Array]"; //true
let attf = function() {};
let attfFn = Object.prototype.toString.call(attf) == "[object Function]"; //true
console.log(attfFn);
function A() {}
let a = new A();
Object.prototype.toString.call(a) == "[object Object]"; //true
在 javascript 高级程序设计中提供了另一种方法,可以通用的来判断原始数据类型和引用数据类型
4、constructor
console.log("constructor", [].constructor === Array);
console.log({}.constructor === Object);
console.log("string".constructor === String);
console.log((123).constructor === Number);
console.log(true.constructor === Boolean);
属性返回对创建此对象的数组函数的引用,就是返回对象相对应的构造函数。原因:constructor 是 prototype 对象上的属性,指向构造函数。
# 2、隐式类型转换以及转换原则
最常见的隐式类型转换主要是 boolead 转换
let test;
test = null;
if (!test) {
console.log("非空字符串会转换成true,空字符串会转换成false", !test);
}
test = "";
if (!test) {
console.log("''和null都是空字符串", !test);
}
test = 0;
if (!test) {
console.log("非0会转换成true,0会转换成false", !test);
}
test = {};
if (test) {
console.log("任何对象都会转成true", !test);
}
1、String,非空字符串会转换成 true,空字符串会转换成 false
2、Number, 非 0 会转换成 true,0 会转换成 false
3、Object,任何对象都会转成 true
# 3、instanceof 解析
function instance_of(L, R) { // L即stu ; R即Person
const O = R.prototype; // O为Person.prototype
L = L.__proto__; //L为stu._proto_,现在指向的是per实例对象
while (true) { // 执行循环
if (L === null) //不通过
return false;
if (O === L) //判断: Person.prototype === stu._proto_ ?
return true; //此时,stu._proto_ 指向per实例对象,并不满足
L = L.__proto__; //令L= stu._proto_._proto_,执行循环
} //stu._proto_ ._proto_,看图示知:
}
# 4、逻辑运算符(|| 和 &&) 的使用
定义: &&是“与”的意思,||是“或者”的意思
使用方式: a && b:a 和 b 同时为 true 才返回 true, 否则返回 false;a || b:a 或 b 任意一个为 true 就返回 true , 否则返回 false
运算方式: 都表示运算,但是&&运算符第一个表达式不成立的话,后面的表达式不运算,直接返回。而&对所有表达式都得判断。
let judgingType = screeningType.some(item => {
return (
(data.indicator === "" && item.value !== "") ||
(data.indicator === undefined && item.value !== "")
);
});
# 5、强大JavaScript运算符
# 1、多重三元运算(?:)
const size = val =>
Array.isArray(val)
? val.length
: val && typeof val === 'object'
? val.size || val.length || Object.keys(val).length
: typeof val === 'string'
? new Blob([val]).size
: 0;
size([1, 2, 3, 4, 5]); // 5
size('yuan'); // 4
size({ one: 1, two: 2, three: 3 }); // 3
这个的实现非常巧妙,利用 Blob 类文件对象的特性,获取对象的长度。
# 2、 非空运算符(??)
?? 运算符被称为非空运算符。
如果第一个参数不是 null/undefined,将返回第一个参数,否则返回第二个参数
null ?? 5
// 5
3 ?? 4
// 3
注意这里的假值只是 null/undefined
# 3、逻辑空赋值 (??=)
逻辑空赋值运算符 (x ??= y) 仅在 x 是 null 或 undefined时对其赋值。
const a = { duration: 50 };
a.duration ??= 10;
console.log(a.duration);
// expected output: 50
a.speed ??= 25;
console.log(a.speed);
// expected output: 25
??= 可用来初始化缺失的属性
const pages = [
{
title:'主要场景',
path:'/'
},
{
path:'/a'
},
{
path:'/b'
},
]
for (const page of pages){
page.title ??= '默认标题'
}
console.table(pages)
//(index) title path
//0 "主要场景" "/"
//1 "默认标题" "/a"
//2 "默认标题" "/b"
空赋值的短路用法
空值合并运算符从左至右求值
(结果非 null 或 undefined 的表达式) ?? expr 被短路求值为左侧表达式,当左侧证明为既非 null 也非 undefined.
语法短路意味着 expr 部分尚未被求值,因此任何与其求值产生的相关副作用都不会生效(例如,如果 expr 是一个函数调用,则该调用将不会发生)。
逻辑空赋值的语法短路也意味着 x ??= y 等价于:
x ?? (x = y);
而不等价于如下的表达式,因为其一定会发生赋值:
x = x ?? y;
在使用??=时,这里的假值只是 null/undefined
逻辑运算||=
在||=中判断的假值可以是''/null/undefined/0
const a = { duration: 50, title: '' };
a.duration ||= 10;
console.log(a.duration);
// expected output: 50
a.title ||= 'title is empty.';
console.log(a.title);
// expected output: "title is empty"
赋值表达式
a ||= b
//等价于
a = a || (a = b)
a &&= b
//等价于
a = a && (a = b)
a ??= b
//等价于
a = a ?? (a = b)
注意:
a ||= b:当a值不存在时,将b变量赋值给a
a &&= b:当a值存在时,将b变量赋值给a
a ??= b:当a值为null或者undefined时,将b变量赋值给a
# 4、可选链操作符(?.)
通过使用 ?. 操作符取代 . 操作符,JavaScript 会在尝试访问 obj.first.second 之前,先隐式地检查并确定 obj.first 既不是 null 也不是 undefined。如果obj.first 是 null 或者 undefined,表达式将会短路计算直接返回 undefined。
const adventurer = {
name: 'Alice',
cat: {
name: 'Dinah'
}
};
const dogName = adventurer.cat?.name;
console.log(dogName);
// expected output: Dinah
console.log(adventurer.cat?.age);
// expected output: undefined
如果在对象的每一层都需要判断
adventurer?.set?.age
在Map中使用可选链操作符
let myMap = new Map();
myMap.set("foo", {name: "baz", desc: "inga"});
let nameBar = myMap.get("bar")?.name;
console.log(nameBar) // undefined
可选链操作符的短路计算
当在表达式中使用可选链时,如果左操作数是 null 或 undefined,表达式将不会被计算,例如:
let potentiallyNullObj = null;
let x = 0;
let prop = potentiallyNullObj?.[x++];
console.log(x); // x 将不会被递增,依旧输出 0
使用空值合并操作符
空值合并操作符可以在使用可选链时设置一个默认值:
let customer = {
name: "Carl",
details: { age: 82 }
};
let customerCity = customer?.city ?? "何小帅";
console.log(customerCity); // “何小帅”
# 6、Class 类的理解
定义: ES6 新引入的类概念,作为对象的模版,可通过 calss 定义类。
原理: 类本身指向构造函数,所有方法定义在 prototype 上,可看作构造函数的另一种写法(Class === Class.prototype.constructor)
# 方法和关键字
constructor():
构造方法,new 命令生成实例时自动调用 。类必须有 constructor 方法,如果没有显式定义,一个空的 constructor 方法会被默认添加,类必须使用 new 调用。 this 指向实例对象。
class testClass {
constructor() {
this.name = "hyp";
this.id = 100;
}
}
let testData = new testClass();
console.log(testData);
实例属性除了定义在 constructor()方法里面的 this 上面,也可以定义在类的最顶层
class testClass {
name = "hyp";
id = 100;
}
let testData = new testClass();
console.log(testData);
新写法能清楚的看出该类中的实例属性
extends:
继承父类
class testClass {
name = "hyp";
id = 100;
}
new testClass();
class Child extends testClass {} // 继承了testClass父类实例
let child = new Child();
console.log(child);
super:
新建父类的 this,子类使用父类的属性方法时,必须在构造函数中调用 super(),否则得不到父类的 this,
父类静态属性方法可被子类继承,子类继承父类后,可从 super 上调用父类静态属性方法
class testClass {
name = "hyp";
id = 100;
}
new testClass();
class Child extends testClass {
constructor() {
super(); // 相当于Parent.call(this)
this.name = "child";
}
} // 继承了testClass父类实例
let child = new Child();
console.log(child);
static:
定义静态属性方法,不会被实例继承,需通过类来调用。
class testClass {
name = "hyp";
id = 100;
static a() {
return 1;
}
}
class Child extends testClass {
constructor() {
super();
this.name = "child";
}
static b() {
return testClass.a() + super.a();
}
}
console.log(Child.b());
私有方法和私有属性,是只能在类的内部访问的方法和属性,外部不能访问。这是常见需求,有利于代码的封装
class Parent {
// 私有属性最新写法
#name1 = "zs";
#name2 = "ls";
// 私有属性
constructor() {
this.name = "parent";
this.age = "40";
}
static s = 900;
// 静态方法
static b() {
return 2;
}
}
之所以要引入一个新的前缀#表示私有属性,而没有采用 private 关键字,是因为 JavaScript 是一门动态语言,没有类型声明,使用独立的符号似乎是唯一的比较方便可靠的方法,能够准确地区分一种属性是否为私有属性。另外,Ruby 语言使用@表示私有属性,ES6 没有用这个符号而使用#,是因为@已经被留给了 Decorator
get:
取值函数,拦截属性的取值行为
set:
存值函数,拦截属性的存值行为
# 立即执行写法
// 立即执行
let person = new (class {
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
})("张三");
person.sayName();
# Generator 方法
// Generator 方法
class Foo {
constructor(...args) {
this.args = args;
}
*[Symbol.iterator]() {
for (let arg of this.args) {
yield arg;
}
}
}
for (let x of new Foo("hello", "world")) {
console.log(x);
}
# 7、Set 实例的属性和方法
Set 结构的实例有以下属性。
Set.prototype.constructor:构造函数,默认就是Set函数。
Set.prototype.size:返回Set实例的成员总数。
Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。
# 1、四个操作方法。
Set.prototype.add(value):添加某个值,返回 Set 结构本身。
Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。
Set.prototype.clear():清除所有成员,没有返回值。
# 2、四个遍历方法
Set.prototype.keys():返回键名的遍历器
Set.prototype.values():返回键值的遍历器
Set.prototype.entries():返回键值对的遍历器
Set.prototype.forEach():使用回调函数遍历每个成员
let set = new Set(['red', 'green', 'blue']);
for (let item of set.keys()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.values()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.entries()) {
console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
# 8、reduce()方法
arr.reduce(callback(accumulator, currentValue, index, array), initialValue)
参数
callback (执行数组中每个值的函数,包含四个参数)
1、previousValue (上一次调用回调返回的值,或者是提供的初始值(initialValue))
2、currentValue (数组中当前被处理的元素)
3、index (当前元素在数组中的索引)
4、array (调用 reduce 的数组)
initialValue (作为第一次调用 callback 的第一个参数。)
使用
回调函数第一次执行时,accumulator 和currentValue的取值有两种情况:如果调用reduce()时提供了initialValue,accumulator取值为initialValue,currentValue取数组中的第一个值;如果没有提供 initialValue,那么accumulator取数组中的第一个值,currentValue取数组中的第二个值。
注意:如果没有提供initialValue,reduce 会从索引1的地方开始执行 callback 方法,跳过第一个索引。如果提供initialValue,从索引0开始。
const arr = [1, 2, 3, 4];
const sum = arr.reduce(function(prev, cur, index, arr) {
console.log(prev, cur, index);
return prev + cur;
},0) //注意这里设置了初始值
console.log(arr, sum);
打印结果:
0 1 0
1 2 1
3 3 2
6 4 3
[1, 2, 3, 4] 10
具体使用场景 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce#%E8%AF%AD%E6%B3%95
# 9、Promise 对象
1、Promise解决了什么问题?
主要解决的是异步编程的问题。在之前的写法中我们都是采用回调函数+事件去解决异步编程的问题,在实际的业务场景中我们可能会写回调中嵌套回调,出现很多的回调函数,造成“回调地狱”,出现代码的可读性差,不易于理解具体的业务逻辑。
有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。
2、Promise对象的特点?
Promise对象状态不受外界影响,状态一旦改变就不会再变,任何时候都可以得到这个结果。
Promise对象有三种状态:
pending(进行中),resolved(已成功),rejected(已失败)
Promise实例具有的方法:
then() 返回resolved状态的回调函数
catch() 返回rejected状态的回调函数
const fn = new Promise(function(resolve,reject){
let rel = {
code:200,
data:{
name:"何小鹏",
age:18
}
};
if (rel.code === 200) {
resolve(rel.data)
}else{
reject("出错了")
}
})
fn.then((v)=>console.log(v)).catch((e)=>console.log(e));
// {name: '何小鹏', age: 18}
finally() 方法用于指定不管 Promise 对象最后状态如何,都会执行的操作
all() 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
race() 方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。和all()方法相似。
allSettled()Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例