一,字符串的扩展
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.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
:是存放当前迭代对象值的变量,该变量能用 const
、let
、var
关键字来声明。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); // trueconsole.log(-0 === +0); // trueconsole.log(NaN == NaN); // falseconsole.log(NaN === NaN); // falseconsole.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 之前,对象是创建键值对数据结构的主要方式,但对象在使用上有一些局限性。
在对象中的键名只能是字符串、数字或者 Symbol。对象不可以直接使用 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
是一个回调函数,其函数包含三个参数:
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
方法的使用过程,功能说明:
Promise
对象,在返回过程中,执行一个延时操作,定义一个参数 n ,如果参数 n 大于 0 ,则返回该数据,否则,则返回 “ 不能小于 0“ 的字符信息。调用 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
方法的使用过程,功能说明:
Promise
对象,在返回过程中,执行一个延时 3 秒的操作,请求成功后,则返回一个”请求成功“ 的字样。再定义一个超时请求的函数,返回一个 Promise
对象,在返回过程中,执行一个延时 5 秒的操作,如果超过 5 秒,则返回一个”请求超时“ 的字样。调用 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、实现模板字符串解析