蓝桥杯——web知识点(es6)

一,字符串的扩展

1.模板字符串 ``

多行字符串的处理

模板字面量有个特点,定义在反撇号中的字符串,其中的空格、缩紧、换行都会被保留。

<script>
      let str = `Hello,
        ECMAScript 6`;
      console.log(str);
    </script>

字符串占位符 ${}

let str = `LanQiao Courses`;
let message = `I like ${str}.`;
console.log(message);

2.字符串新增方法

判断指定字符串是否存在

let str = "LanQiao Courses";
console.log("str 字符串中是否存在 Java:" + str.includes("Java"));
console.log("str 字符串的首部是否存在字符 Lan:" + str.startsWith("Lan"));
console.log("str 字符串的尾部是否存在字符 Course:" + str.endsWith("Course"));

重复字符串

repeat(n) 方法用于返回一个重复 n 次原字符串的新字符串,其参数 n 为整数,如果设置 n 为小数,会自动转换为整数 

替换字符串

二、数组的扩展

1.创建数组

Array.from()方法是基于原来的对象创建的一个新数组。

Array.from() 方法可以将以下两类对象转为数组。

  • 类似数组的对象(array-like-object)。
  • 可遍历的对象(iterable-object)。
  • 其基本使用格式为:

    Array.from(待转换的对象);
let arrLike = {
  0: "🍎",
  1: "🍐",
  2: "🍊",
  3: "🍇",
  length: 4,
};
var arr = Array.from(arrLike);
console.log("arr:" + arr);

2、数组实例的方法

find() 方法是用于从数组中寻找一个符合指定条件的值,该方法返回的是第一个符合条件的元素,如果没找到,则返回 undefined

其语法格式为:

array.find(callback(value, index, arr), thisValue);
  • 参数说明如下:
  • callback 是数组中每个元素执行的回调函数。
  • value 是当前元素的值,它是一个必须参数。
  • index 是数组元素的下标,它是一个可选参数。
  • arr 是被 find() 方法操作的数组,它是一个可选参数。
  • thisValue 是执行回调时用作 this 的对象,它是一个可选参数。

findIndex() 方法返回数组中第一个符合指定条件的元素的索引下标值,如果整个数组没有符合条件的元素,则返回 -1

其语法格式为:

array.findIndex(callback(value, index, arr), thisArg);

参数说明如下:

  • callback 是数组中每个元素都会执行的回调函数。
  • value 是当前元素的值,它是一个必须参数。
  • index 是数组元素的下标,它是一个必须参数。
  • arr 是被 findIndex() 方法操作的数组,它是一个必须参数。
  • thisArg 是执行回调时用作 this 的对象,它是一个可选参数

fill() 方法是用指定的值来填充原始数组的元素 

其使用格式为:

array.fill(value, start, end);

其参数说明如下:

  • value 是用来填充数组的值,它是一个必须参数。
  • start 是被填充数组的索引起始值,它是一个可选参数。
  • end 是被填充数组的索引结束值,它是一个可选参数。

注意:如果不指定 start 和 end 参数,该方法会默认填充整个数组的值

entries()keys()values() 是 ES6 中三种数组的遍历方法,三个方法返回的都是 Array Iterator 对象。但三者之间肯定不是完全相同的,接下来我们一一学习。

entries() 方法以键/值对的形式返回数组的 [index,value],也就是索引和值。其语法格式为:

array.entries();
let arr = ["🐱", "🐶", "🐰", "🐍", "🐦", "🐟"];
let result = arr.entries();
console.log(result);

 

for...of 就摆脱了计数器、退出条件等烦恼,它是通过迭代对象的值来循环的。它能迭代的数据结构很多,数组、字符串、列表等。

for...of 的语法格式如下所示:

for (variable of iterable) {
}

参数说明如下:

  • variable:是存放当前迭代对象值的变量,该变量能用 constletvar 关键字来声明。
  • iterable:是可迭代对象。

3.扩展运算符(...)是 ES6 的新语法,它可以将可迭代对象的参数在语法层面上进行展开。

使用扩展运算符可以起到将数组展开的作用。

其语法格式为:

// 在数组中的使用
let VariableName = [...value];
let animals = ["兔子🐰", "猫咪🐱"];
let zoo = [...animals, "老虎🐯", "乌龟🐢", "鱼🐟"];
console.log(zoo);

在控制台可以看到如下输出

图片描述

扩展运算符号问世后,成为了程序员的宠儿,但在 ES2018 版本前的它有一个缺点就是只能用在数组和参数上。于是在 ES2018 中又将扩展运算符引入了对象。

在对象上,我们主要有以下三种操作:

  • 可以使用扩展运算符将一个对象的全部属性插入到另一个对象中,来创建一个新的对象。
  • 可以使用扩展运算符给对象添加属性。
  • 可以使用扩展运算符合并两个新对象。

 三、函数的扩展

rest 参数又称为剩余参数,用于获取函数的多余参数。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中

rest 参数和扩展运算符在写法上一样,都是三点(...),但是两者的使用上是截然不同的。

扩展运算符就像是 rest 参数的逆运算,主要用于以下几个方面:

  • 改变函数的调用。
  • 数组构造。
  • 数组解构。

rest 参数语法格式为:

// 剩余参数必须是函数的最后一个参数
myfunction(parameters, ...rest);

箭头函数,顾名思义,就是用箭头(=>)来表示函数。箭头函数和普通函数都是用来定义函数的,但两者在语法构成上非常不同

箭头前面 () 里放着函数的参数,箭头后面 {} 里放着函数内部执行的表达式。

箭头函数除了代码简洁以外,它还解决了匿名函数的 this 指向问题。

箭头函数的基本用法。

(param1,param2,...,paramN) => {expression}

this 的指向

四、对象的扩展 

对象字面量就是使用 {} 去定义对象。

对象的扩展运算符遍历参数对象中的所有属性,并将其拷贝到新对象中

对象的新增的方法

Object.is

在 ES6 之前,如果我们要判断两个值是否相等,可以用 == 或者 ===,但是这两种判断方式都存在一些缺点。我们来看看下面的代码:

console.log(-0 == +0); // true
console.log(-0 === +0); // true

console.log(NaN == NaN); // false
console.log(NaN === NaN); // false

console.log(7 == "7"); // true

Object.assign

在 obj1 和 obj3 中有相同名称的属性名 food,从输出结果可以看到 obj1 中的 food 属性被覆盖了,这一点也需要同学们注意哦,被合并的对象中出现同名属性,后面的对象会覆盖前面的对象中的属性值。

还有一点需要大家注意一下哦~

这就是合并方式是一种浅拷贝,也就是说如果被合并对象中的属性发生变化,合并后的对象不会继承修改的属性

五、类的扩展

1、类的声明

ES6 的 class 关键字只是一个语法糖,其底层还是函数和原型只是给它们披上了一件衣服而已。

class MyClass {
  // constructor 方法是类的默认方法
  constructor(num) {
    this.num = num;
    this.enginesActive = false;
  }
  // 相当于 MyClass.prototype.startEngines
  startEngines() {
    console.log("staring...");
    this.enginesActive = true;
  }
}
const myclass = new MyClass(1);
myclass.startEngines();

2、类表达式

类和函数都有两种存在形式:

  • 声明形式(例如:function、class 关键字声明)。
  • 表达式形式(例如:const A = class{})。

前面我们已经见过声明形式了,接下来我们看看表达式形式是如何使用的。

// ES6 语法
let DogType = class {
  constructor(name) {
    this.name = name;
  }
  sayName() {
    console.log(`大家好!我是一只小${this.name}。`);
  }
};
let dog = new DogType("柯基");
dog.sayName();
console.log(dog instanceof DogType);
console.log(dog instanceof Object);

 在上面代码中 constructor(name){} 等价于 DogType 的构造函数,sayName(){} 等价于 DogType.prototype.sayName = function(){}

3、类的继承

在 ES6 中为我们提供了 extends 关键字来实现继承。

其使用格式如下:

class child_class_name extends parent_class_name {}

上面代码的意思是 child_class_name 类继承了 parent_class_name 类。

class Animal {
  constructor(name, age, speed) {
    this.name = name;
    this.age = age;
    this.speed = speed;
  }
  run() {
    console.log(`${this.age}岁的${this.name}酷跑了 ${this.speed} 公里。`);
  }
  stop() {
    console.log(`${this.name}停止了奔跑。`);
  }
}
class Dog extends Animal {} // Dog 类继承了 Animal 类
// 实例化 Dog 类
let dog = new Dog("闷墩儿", "一", 5);
// 调用 Animal 类中的 run() 方法和 stop() 方法
dog.run();
dog.stop();
  • 在上面代码中,使用 class Dog extends Animal {} 定义了一个 Dog 类继承了 Animal 类的属性和方法。
  • 使用 let dog = new Dog('闷墩儿','一',5) 实例化 Dog 对象。
  • 使用 Dog 类的实例对象 dog 去调用 run 和 stop 方法,程序会先在 Dog 类的原型 Dog.prototype 中查找两个方法是否存在,找不到的情况下会去其父类原型 Animal.prototype 中查找,以此类推,层层向上最终找到并调用执行

4、super 

ES6 为我们提供了超级函数 super 我们的继承变得完整且具备可扩展性。

它的使用格式有两种:

  • 使用 super.method(...) 来调用父方法。
  • 使用 super(...) 调用父构造函数

六、Set 和 Map 

 Set 是 ES6 提供的一种新的数据结构,其结构与数组类似,但与数组不同的是 Set 里面不允许存放相同的元素,也就是说 Set 中的每个值都是独一无二的。

创建不带参数的 Set

创建带参数的 Set

Set 相关的方法

delete() 方法

我们已经知道了使用 add() 方法可以往 Set 中添加元素。我们还可以对 Set 进行删除等操作。

在 Set 中使用 delete() 方法来移除指定元素。其使用格式为:

Set.delete(element);

has() 方法来检查某个元素是否存在于 Set 中 

删除 Set 中的所有数据,可以使用 clear() 方法
let dogs = new Set(["柯基", "博美", "秋田犬", "藏獒"]);
dogs.delete("秋田犬");
console.log(dogs);
console.log(dogs.has("柯基"));
console.log(dogs.has("秋田犬"));
dogs.clear();
console.log(dogs);

Set 的遍历 

我们使用 forEach() 方法可以遍历 Set 中的元素。

其使用格式为:

Set.prototype.forEach(callback[,thisArg])

参数说明如下:

  • callback 是 Set 中每个元素要执行的回调函数。
  • thisArg 是回调函数执行过程中的 this 值。

WeakSet

下面给大家说一说,Set 与 WeakSet 的区别:

  • WeakSet 的成员只能是对象且都是弱引用。

    在 WeakSet 中,add() 方法中不能传入非对象参数,若传入会报错。

  • 在 WeakSet 中,给 has() 和 delete() 方法传入非对象参数,虽然不会报错,但是会返回 false。

  • WeakSet 对象没有 size 属性,不能被遍历。

    由于 WeakSet 里面存储的都是弱引用,内部有多少个成员,取决于垃圾回收机制有没有运行。运行前后很可能成员个数是不一样的,而垃圾回收机制何时运行是不可预测的,因此 ES6 规定 WeakSet 不可遍历。

2、Map 对象

对象,我们不陌生吧!在 ES6 之前,对象是创建键值对数据结构的主要方式,但对象在使用上有一些局限性。

  1. 在对象中的键名只能是字符串、数字或者 Symbol。
  2. 对象不可以直接使用 forEach 方法遍历。

而 Map 的出现就解决了上述问题。

Map 是 ES6 中一种存储许多键值对的有序列表,其键值对可以是任意数据类型。Map 是有序的,它会按照键值插入的顺序来排列。

(1)创建 Map 对象。

其语法格式为:

let map = new Map([iterable]);

(2)set 方法添加数据

使用 set() 方法可以向对象中添加数据。其使用格式为:

map.set(key:value);

在 index5.html 文件中增加以下代码:

bookstore.set([1, 2, 3], "书籍");
bookstore.set(false, "日用品");
bookstore.set(3, "化妆品");
console.log(bookstore);

(3)get 方法从集合中获取数据

我们要获取集合中的数据,使用 get() 方法即可。

其使用格式为:

map.get(key);

我们来获取一下 bookstore 集合中的数据吧~

console.log(bookstore.get(false));

(4)其他常用方法

除了上方提到的 set() 和 get() 方法,在 Map 中,还有下面三种方法比较常用。

  • has() 用来判断指定键名对应的数据是否存在于当前集合中。
  • delete() 用来删除指定键名的数据。
  • clear() 用来清空集合中的数据。
let bookstore = new Map();
bookstore.set("《活着》", "余华");
bookstore.set("《平凡的世界》", "路遥");
bookstore.set("《三体》", "刘慈欣");
bookstore.set("《猫和老鼠》", "电影");
console.log(`《活着》是否存在:${bookstore.has("《活着》")}`);
bookstore.delete("《猫和老鼠》");
console.log(`《猫和老鼠》是否存在:${bookstore.has("《猫和老鼠》")}`);
bookstore.clear();
console.log(bookstore);

(5)Map 的遍历 

与对象不同,Map 可以使用 forEach() 方法来遍历数据值。

在讲 forEach() 方法之前,先给大家说一说如何在创建 Map 时就为其赋初始值。在前面的例子中,我们都是创建了一个空的 Map,然后使用 set() 方法往里面添加的值。现在我们来创建并初始化一个带数据的 Map。

语法格式如下:

let map = new Map([[key1,value1],[key2,value2]...]);

Map 还有一个 forEach() 方法,与数组的 forEach() 方法类似,可以实现对 Map 实例的遍历。

map.forEach(callback(value, key, ownerMap));

callback 是一个回调函数,其函数包含三个参数:

  • value:本次循环所读取的元素的数据。
  • key:本次循环所读取的元素的键名。
  • ownerMap:Map 集合本身
let userName = new Map([
  [1, "小红"],
  [2, "小蓝"],
  [3, "小白"],
]);
userName.forEach(function (value, key, ownerMap) {
  console.log(`userName 中的值有: ${value}`);
  console.log(`userName 中的键有:${key}`);
  console.log(ownerMap);
});

 

3、总结

Set 对象:

Set 与数组之间的区别:

  • Set 是一个可以存储数据的对象,你可以在其中添加或者删除数据,并循环访问 Set。但是 Set 中没有索引,也不能存放重复的值,数组与之相反。

Set 与 WeakSet 的区别:

  • Set 是强引用,创建的对象不能被垃圾回收,WeakSet 是弱引用,创建的对象可以被垃圾回收。

Map 对象:

  • Map 可以创建任意数据类型的键值对,打破了对象键名类型限制的局限性。
  • 我们可以使用 forEach() 方法来遍历 Map,而对象不能。
  • 我们可以使用 set()get()has()delete()clear() 等方法来操作 Map。

七、异步编程

1、Promise 对象基础应用

(1)、地狱式回调

在日常开发中,往往会遇到这样的需求:通过接口 1 的返回值,去获取接口 2 的数据,然后,再通过接口 2 的返回值,获取接口 3 的数据。即每次请求接口数据时,都需要使用上一次的返回值。为了实现这个需求,通常会使用回调函数来完成,即把函数作为参数进行层层嵌套。

(2)定义 Promise 对象

为了解决这种地狱式的回调,可以使用 Promise 对象,且代码更优雅,由于 Promise 对象是一个构造函数,因此,必须通过实例化来生成,它的定义格式如下代码:

let p = new Promise(function (resolve, reject) {
  // 此处做一个异步的事情
});

在定义格式的代码中,需要说明的几个问题:

  • 在实例化中,参数为函数,函数中又有两个用于回调的函数。
  • 两个回调函数中,resolve 为异步执行成功时的回调,其参数可以传递执行的结果。
  • reject 为异步执行失败时的回调,其参数可以传递失败的错误信息。

 (3)Promise 对象的 then 方法

Promise 对象实例化后,可以调用 then 方法获取两个回调函数中的传参值,该方法接收两个回调函数作为参数,第一个参数是必选参数,表示异步成功后执行的 resolve 回调函数,第二个参数是可选参数,表示异步失败后执行的 reject 回调函数,它的调用格式如下:

回调函数带参数的格式如下:

p.then(
  function (v) {},
  function (e) {}
);

其中参数 v 值表示 resolve 回调函数中的参数值,e 值表示 reject 回调函数中的参数值,如下列代码所示:

let n = 6;
let p2 = new Promise(function (resolve, reject) {
  setTimeout(function () {
    if (n > 5) {
      resolve(n);
    } else {
      reject("必须大于5");
    }
  });
});
p2.then(
  function (v) {
    console.log(v);
  },
  function (e) {
    console.log(e);
  }
);
// 执行代码后,由于 n 值大于 5 ,因此,在控制台中输出数字 6 。

此外,一个 then 方法被执行后,如果仍然返回一个 Promise 对象,则可以继续再执行 then 方法,形成链式写法效果,代码如下所示:

p1.then(function (v) {
  return p1;
}).then(function (v) {
  return p1;
});

(4)解决地狱式回调

在学习完 Promise 对象的定义和 then 方法调用后,接下来,我们使用 Promise 来实现开头提到的需求,从而解决由此引起的回调地狱问题,实现过程如下: 

var outnum = function (order) {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log(order);
      resolve();
    }, 1000);
  });
};
outnum("1")
  .then(function () {
    return outnum("2");
  })
  .then(function () {
    return outnum("3");
  })
  .then(function () {
    console.log("0");
  });

2、Promise 对象中的方法 

 (1)Promise.all 方法

日常开发过程中,往往会遇到这种问题,当首次加载某个页面时,由于数据庞大需要分别同时发送多个异步请求向服务器获取数据,最终所有数据返回之后做下一步操作(如“隐藏页面的加载 loading 动画”)。由于很难捕获这些异步请求全部成功的时机,导致这个需求实现起来相当困难。难道就没有解决办法了吗?🤔 这时使用 Promise.all 方法就可以解决这种问题。

使用格式

Promise.all 方法中的参数是一个数组,数组中的每个元素是实例化后的 Promise 对象,格式如下代码:

Promise.all([p1,p2,p3,...]).then(res=>{
  // 所有请求成功后的操作步骤
},error=>{
  // 某一个请求失败后的操作步骤
});

上述代码中,p1、p2、p3 都是实例化后的 Promise 对象,并且该方法可以通过链式写法直接调用 Promise.all 中的 then 方法,当全部的实例化对象都执行成功后,进入 then 方法的第一个执行成功的回调函数中,函数参数是每个任务执行成功后的结果,以数组形式保存

通过 Promise.all 方法可以并列完成多个异步的请求,只有当全部请求成功后,才进入 then 方法中的成功回调函数中,否则,进入失败的回调函数中,因此,当首次加载页面时,可以将各种的异步请求放入 Promise.all 方法中,如果全部完成,则在 then 方法中的成功回调函数中执行下步操作,否则,直接进入失败回调函数中。

实战应用

下列通过一个实战来演示 Promise.all 方法的使用过程,功能说明:

  1. 定义一个函数 p1,返回一个 Promise 对象,在返回过程中,执行一个延时操作,定义一个参数 n ,如果参数 n 大于 0 ,则返回该数据,否则,则返回 “ 不能小于 0“ 的字符信息。
  2. 调用 Promise.all 方法,使用不同的参数,调用三次 p1 函数,当全部执行成功或有一个执行失败后,分别查看控制台的输出信息。
function p1(n) {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      if (n > 0) {
        resolve(n);
      } else {
        reject("不能小于0");
      }
    }, 1000);
  });
}

//先传入三个执行成功的任务
Promise.all([p1(5), p1(6), p1(7)]).then(
  function (v) {
    console.log(v);
  },
  function (e) {
    console.log(e);
  }
);

//传入一个执行失败的任务,
Promise.all([p1(5), p1(-2), p1(7)]).then(
  function (v) {
    console.log(v);
  },
  function (e) {
    console.log(e);
  }
);

(2)Promise.race 方法 

 与 Promise.all 方法不同,Promise.race 方法是多个 Promise 实例化对象在比赛, 执行最快的那个任务的结果,将返回给 then 方法中的对应回调函数中,通过这种方式,可以检测页面中某个请求是否超时,并输出相关的提示信息。

使用格式

与 Promise.all 方法一样,Promise.race 中的参数也是一个数组,每个元素也是实例化后的 Promise 对象,格式如下代码:

Promise.race([p1,p2,p3,...]).then(
    function(v){
      //获取最快任务成功时的返回值
  },
  function(){
      //获取最快任务失败时的返回值
  }
)

实战应用

下列通过一个实战来演示 Promise.race 方法的使用过程,功能说明:

  1. 定义一个模拟异步请求的函数,返回一个 Promise 对象,在返回过程中,执行一个延时 3 秒的操作,请求成功后,则返回一个”请求成功“ 的字样。
  2. 再定义一个超时请求的函数,返回一个 Promise 对象,在返回过程中,执行一个延时 5 秒的操作,如果超过 5 秒,则返回一个”请求超时“ 的字样。
  3. 调用 Promise.race 方法,添加这 2 个 Promise 对象,当请求大于 5 秒和小于 5 秒时,分别查看控制台的输出信息。
function timeOut() {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      reject("请求超时");
    }, 5000);
  });
}

先再义一个延时小于 5 秒的任务,在新建页面中的 script 元素中添加如下代码:

function loadData() {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      resolve("请求成功");
    }, 3000);
  });
}

接下来调用 Promise.race() 方法,获取并在显示执行最快任务的返回内容,在新建页面中的 script 元素中添加如下代码:

Promise.race([loadData(), timeOut()]).then(
  function (d) {
    console.log(d);
  },
  function (e) {
    console.log(e);
  }
);

上述代码执行后的效果如下图所示:

图片描述

由于 loadData 函数的延时时间小于请求超时的延时时间,因此,该任务执行最快,所以在控制台显示 ”请求成功“ 的信息。

如果将 loadData 函数的延时时间修改为 6 秒,即将 loadData 函数的代码修改为如下代码:

function loadData() {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      resolve("请求成功");
    }, 6000);
  });
}

其他代码不变,页面执行后的效果如下图所示:

图片描述

由于 timeOut 函数的延时时间小于请求超时的延时时间,因此,该任务执行最快,所以在控制台显示 ”请求超时“ 的信息。

3、async 关键字和 await 关键字

(1)async 关键字

async 英文单词的意思是异步,虽然它是 ES7 中新增加的一个关键字,但它的本质是一种语法糖写法(语法糖是一种简化后的代码写化,它能方便程序员的代码开发),async 通常写在一个函数的前面,表示这是一个异步请求的函数,将返回一个 Promise 对象,并可以通过 then 方法取到函数中的返回值,下面通过一个简单示例来说明它的使用。

async function fn() {
  return "12345";
}
fn().then((val) => {
  console.log(val);
});

在上述代码中,定义一个名称为 fn 的函数,但由于在函数前添加了关键字 async ,使这个函数将返回一个 Promise 对象,因此,函数执行后,可以直接调用 then 方法;同时,fn 函数中的返回值,就是 then 方法中,执行成功回调函数时的参数值,因此,执行上述代码后,将在页面的控制台输出 “12345” 字符

虽然 async 关键字简化了我们之前实现异步请求中返回 Promise 实例对象的那一步,直接返回了一个 Promise 对象,但是仍然需要在 then 方法中处理异步获取到的数据。有没有什么办法可以继续优化呢?比如省去 then 方法的调用,让异步操作写起来更像同步操作那么简洁明了?答案就是—— await ,接下来我们来介绍下它的用法。

(2)await 关键字

await 可以理解为 async wait 的简写,表示等待异步执行完成,await 必须在 async 定义的函数中,不能单独使用,await 后可以返回任意的表达式,如果是正常内容,则直接执行,如果是异步请求,必须等待请求完成后,才会执行下面的代码。

// 函数 p 返回的是一个 Promise 对象,在对象中,延时 2 秒,执行成功回调函数,相当于模拟一次异步请求
function p(v) {
  return new Promise(function (resolve) {
    setTimeout(function () {
      // 在 p 函数执行时,将函数的实参值 v ,作为执行成功回调函数的返回值。
      resolve(v);
    }, 2000);
  });
}

// 一个用于正常输出内容的函数
function log() {
  console.log("2.正在操作");
}

async function fn() {
  console.log("1.开始");
  await log();
  let p1 = await p("3.异步请求");
  console.log(p1);
  console.log("4.结束");
}
fn();

基于 await 的特性,可以将异步请求的代码变成同步请求时的书写格式,代码会更加优雅,特别是处理多层需要嵌套传参时,使用 await 的方式,代码会更少,更易于阅读,如下列需求。

需求分析

需要发送三次异步请求,第一次请求,成功后获取返回 1,并将该值作为参数并加 2,发送第二次请求,成功后获取返回值,并将该值作为参数并加 3,发送第三次请求,成功后输出全部的返回值,如果三次请求都成功了,则在控制台输出 “登录成功!”的字样。

实现代码
// 函数 p 返回的是一个 Promise 对象,在对象中,延时 2 秒,执行成功回调函数,相当于模拟一次异步请求
function p(v) {
  return new Promise(function (resolve) {
    setTimeout(function () {
      // 在 p 函数执行时,将函数的实参值 v ,作为执行成功回调函数的返回值。
      resolve(v);
    }, 2000);
  });
}
async function fn() {
  let p1 = await p("1");
  let p2 = await p(p1 + "2");
  let p3 = await p(p2 + "3");
  console.log(p3);
  console.log("登录成功!");
}
fn();

八、模块化

1、export

模块化开发项目是目前的主流趋势,它是将复杂的功能分解成各自独立的子模块,所有子模块按照一种方式进行组合,最终完成复杂功能的过程,它的优势是各模块是独立工作的,更利于复用和维护,同时更有利于节略资源,提高效率。

基于模块化开发的趋势,在前端项目开发时,各个功能都是独立开发的,如登录模块,注册模块和公用模块等,它们都是一个个单独的文件,如果登录模块想要访问公用模块中的某个方法,则需要公用模块开放这个方法,并制定访问的标准,而这时就需要使用 ES6 中新添加的关键字 export 了,

关键字 export 可以将一个模块中的方法、变量和其他功能从模块中输出,允许其他需要的模块按指定的标准进行访问,没有使用关键字 export 输出的模块内容,是封闭的,其它模块无法访问到它,下面介绍关键字 export 几种输出的方式 。

//1.在上述代码中,由于变量 name 和 age 之前都使用了输出关键字 export ,
//因此,它们都可以被其他模块访问,
//由于变量 work 之前没有添加关键字 export ,所以,其他的模块无法访问到这个变量。
export let name = "小蓝同学";
export let age = 18;
let work = "一直在写代码呢!";
//2.合并成一个对象,并使用关键字 export 一次性输出
let name = "小蓝同学";
let age = 18;
let work = "一直在写代码呢!";
export { name, age };
//3.输出方法
function say() {
  console.log("我的名字叫小蓝");
}
function act() {
  console.log("我的工作是写代码");
}
export function log() {
  console.log("说点什么。。。");
}
export { say, act };

2、import

与关键字 export 相对应,import 的功能是输入已经使用关键字 export 输出的内容,它们是对应关系, export 负责输出,而 import 则用于接受输出的内容,即负责输入

关键字 import 在输入模块中加载输出模块的变量时,可以使用大括号包裹全部变量名,各个变量之间使用逗号分割,再通过 from 指定输出模块的路径,这个路径可以是绝对的,也可以是相对的,代码格式如下:

import { 变量1,变量2,变量3,...} from 输出模块位置

在上述格式代码中,大括号中的变量 1,变量 2 也可以通过关键字 as 取一个别名,格式如下:

import { 变量1 as a1,变量2 as fn,变量3,...} from 输出模块位置

取了别名之后,在输入模块中,只能使用这个别名,而不能再使用原先的名称,否则,将会出现变量未定义的错误信息。

let strSex = "女";
let strName = "小蓝";
let strLike = "就喜欢写点代码";

function log(v) {
  console.log(v);
}

export { strSex, strName, strLike, log };



<script type="module">
      import { strName, strSex, strLike, log } from "./outfile.js";
      console.log(strName + "," + strSex + "," + strLike);
      log("写代码,是一种情怀");
 </script>
  • 由于在页面的 script 元素中,需要执行关键字 import ,因此,必须将 script 元素的 type 属性值设置为 module ,表示代码中允许使用模块输入和输出的关键字,进行模块化代码开发。
  • 虽然使用 import 输入的是 log 函数名称,但它本质上是一个与输出模块相关联的真实函数,因此,在输入的页面中,可以直接像普通函数一样去使用这个输入的函数。

3、案例——数据排序的模块开发 

需求分析

使用模块化开发的方式,分解各个功能模块,实现一个数字型数组的正反排序效果。

实现代码

首先,打开我们的线上环境,新建一个 data.js 文件,作为项目中的数据模块,在文件中添加如下代码:

let _data = [12, 18, 16, 20];
export { _data as data };

在数据模块代码中,定义一个名称为 _data 的数组,并使用关键字 export 将该数组输出,用于逻辑模块的使用;接下来, 新建一个 logic.js 文件,作为项项目中的逻辑模块,在文件中添加如下代码:

import { data } from "./data.js";
function sort(blnS) {
  if (blnS)
    return data.sort(function (a, b) {
      return a - b;
    });
  else
    return data.sort(function (a, b) {
      return b - a;
    });
}
export { sort };

在逻辑模块代码中,先使用 import 输入数据模块中的数组,再定义一个 sort 函数,对加载的数组进行逻辑操作,通过布尔值 blnS 控制排序的方向,最后,再使用关键字 export 将该函数输出,用于视图模块的使用。

最后,新建一个 index2.html 文件,作为项目中的视图模块,在页面中使用快捷键方式生成模版,在 body 元素中添加 script 元素,并在 script 元素中添加逻辑代码,页面完整的代码如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script type="module">
      import { sort } from "./logic.js";
      console.log("升序: " + sort(true));
      console.log("降序: " + sort(false));
    </script>
  </body>
</html>

新建的 index2.html 页面在浏览器中执行的效果如下图所示:

图片描述

在页面 index2.html 中,先使用 import 输入逻辑模块中的 sort 函数,再直接在控制台中执行函数,完成数据排序的效果输出。

 九、Proxy

1、什么是 Proxy

Proxy 可以对目标对象的读取、函数调用等操作进行拦截,然后通过对象的代理对象进行操作。也可以理解为在外界与对象之间建立了一道门,外界要访问该对象必须先打开这道门,如果想要获得打开该门的钥匙,就要遵守一个访问“条约”,允许对来访人员进行改造(提供一种机制:可以对外界的访问进行过滤和改写)。

用 Proxy 创建代理需要传入两个参数:目标对象(target)和处理程序(handler)。语法格式如下:

var proxy = new Proxy(target, handler);

参数说明如下:

  • target:要拦截的目标对象。
  • handler:制定拦截行为的对象。
let target = {};
let proxy = new Proxy(target, {});
proxy.name = "闷墩儿";
console.log(proxy.name);
console.log(target.name);

target.name = "憨憨";
console.log(proxy.name);
console.log(target.name);

2、Proxy 的实例方法

get(target, propKey, receiver)

ES6 中为我们提供了 get 方法,在访问对象之前检验一下是否存在你要访问的属性,该方法接受三个参数,具体说明如下:

  • target:被读取属性的目标对象。
  • propKey:要读取的属性键值。
  • receiver:操作发生的对象。
let dog = {
  name: "闷墩儿",
};
var proxy = new Proxy(dog, {
  get(target, propKey) {
    // 遍历目标对象的属性键值
    if (propKey in target) {
      return target[propKey]; // 返回相应的属性值
    } else {
      throw new ReferenceError(propKey + " 属性不存在");
    }
  },
});
console.log("访问 dog 对象中的 name 属性值为:" + proxy.name);
console.log("访问不存在的 age 属性:" + proxy.age);

set(target, propKey, value, receiver)

如果要创建一个只接受数字作为属性值的对象,那么在创建属性时,必须判断该值是否是数字,若不是数字应该报错。我们使用 set 方法就可以实现这个需求。

set 方法接受四个参数,具体说明如下:

  • target:用于接收属性的目标对象。
  • propKey:要写入的属性键值。
  • value:要写入的属性值。
  • receiver:操作发生的对象。
let validator = {
  set(target, propKey, value) {
    if (propKey === "age") {
      // 判断 age 属性值是否时数字
      if (!Number.isInteger(value)) {
        throw new TypeError("狗狗的年龄只能是整型哦!");
      }
    }
    target[propKey] = value;
    return true;
  },
};

let dog = new Proxy({}, validator);
console.log((dog.age = "22"));

has(target, propKey)

has 方法接收两个参数,具体说明如下:

  • target:读取属性的目标对象。
  • propKey:要检查的属性键值。

新建一个 index3.html 文件,在文件中写入以下内容:

let dog = {
  name: "闷墩儿",
  age: 2,
};
let handler = {
  has(target, propKey) {
    if (propKey == "age" && target[propKey] < 5) {
      console.log(`${target.name}的年龄小于 5 岁哦!`);
      return true;
    }
  },
};
let proxy = new Proxy(dog, handler);

console.log("age" in proxy);

在控制台可以看到以下输出:

图片描述

ownKeys(target)

ownKeys 方法用于拦截对象自身属性的读取操作,具体可以拦截以下四种操作:

  • Object.getOwnPropertyNames()
  • Object.getOwnPropertySymbols()
  • Object.keys()
  • for...in

下面我们举一个拦截 for...in 的例子吧~

新建一个 index4.html 文件,在文件中写入以下内容:

let dog = {
  name: "闷墩儿",
  age: 2,
  food: "狗罐头",
};
const proxy = new Proxy(dog, {
  ownKeys() {
    return ["name", "color"];
  },
});

for (let key in proxy) {
  console.log(key); // 输出 name
}

在控制台的输出如下:

图片描述

从上图我们可以看到只输出了 name 属性,这是因为在 dog 对象中不包含 color 属性。

3、总结

十、练习

1、生成数组

2、数组去重

3、实现模板字符串解析

  • 11
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值