ECMAScript进阶

3 篇文章 0 订阅
2 篇文章 0 订阅

ECMAScript进阶

一、学习目标

  1. 能灵活使用模板字符串
  2. 能熟练使用扩展运算符
  3. 掌握对象扩展的属性和方法
  4. 能灵活使用Set和Map数据结构
  5. 掌握Module的导入导出

二、字符串的扩展

模板字符串

使用反引号 代替普通字符串中的双引号和单引号
可当作普通字符串使用,也可用来定义多行字符串

模板字符串示例:

//多行字符串
document.write(`
   最快的脚步不是跨越,而是继续;<br>
   最慢的步伐不是缓慢,而是徘徊;<br>
   最好的道路不是大道,而是坦荡;<br><hr>   `);
//字符串中嵌入变量
let name = "Bob", time = "today";
document.write(`Hello ${name}, how are you ${time}?`);

字符串的新增方法

方法局部变量
includes(searchString:string):boolean返回布尔值,表示是否找到了参数字符串
startsWith(searchString:string):boolean返回布尔值,表示参数字符串是否在原字符串头部
endsWith(searchString:string):boolean返回布尔值,表示参数字符串是否在原字符串尾部
repeat(count:number):string返回一个新字符串,表示将原字符串重复n次
padStart(length:number,string:string):string用于头部补全
padEnd(length:number,string:string):string用于尾部补全
trimStart():string消除字符串头部的空格
trimEnd():string消除尾部的空格

三、数组的扩展

3.1扩展运算符Array.from

1.用于将类数组对象或者可遍历对象转换成一个真正的数组

2.类数组对象,最基本的要求是具有length属性的对象

Array.from()

Array.from() 转换数组示例1:

let arrayLike = {
	   0: 'tom', 
       1: '65',
       2: '男',
       3: ['jane','john','Mary'],
'length': 4
};

let arr = Array.from(arrayLike)
console.log(arr) //['tom','65','男',['jane','john','Mary']]

注意: 以上示例当去掉length属性,运行代码答案是一个长度为0的空数组

在这里插入图片描述

Array.from() 转换数组示例2:

let arrayLike = {
	'name': 'tom', 
	 'age': '65',
	 'sex': '男',
 'friends': ['jane','john','Mary'],
	length: 4
};

let arr = Array.from(arrayLike)
console.log(arr)//[undefined,undefined,undefined,undefined]

问题: 再次修改代码,具有length属性,但是对象的属性名不再是数字类型,而是其他字符串类型,会发现结果长度是4,但是均为undefined

在这里插入图片描述

结论:

  1. 类数组对象必须具有length属性,用于指定数组的长度
  2. 如果没有length属性,那么转换后的数组是一个空数组
  3. 类数组对象的属性必须为数值型或字符串型的数字
  4. 类数组对象的属性名可以加引号,也可以不加引号

3.2数组的新增方法

数组.entries();
数组.keys();
数组.values();

三个点(...),可将一个数组转为用逗号分隔的序列

扩展运算符的使用示例1:

let arr = [];
//扩展运算符的基本用法
arr.push(...[1,2,3,4,5]);
console.log(arr); //[1,2,3,4,5]
console.log(1, ...[2, 3, 4], 5) //1 2 3 4 5
//可以放置表达式
console.log(...(1 > 0 ? ['a'] : [])); //a

由于扩展运算符可以展开数组,所以不再需要apply方法,将数组转为函数的参数了

扩展运算符的使用示例2:

//空数组,不产生效果
console.log([...[], 1]); //[1]
//用于函数调用
function push(array, ...items) { 
    array.push(...items);
}
function add(x, y) {
    console.log(x + y);
}
const numbers = [4, 38];
add(...numbers) // 42

结论: 扩展运算符与正常的函数参数可以结合使用,后面可以放置表达式,但是如果扩展运算符后面是一个空数组,则不产生任何效果。

通过push函数,将一个数组添加到另一个数组的尾部示例:

// ES5的 写法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
Array.prototype.push.apply(arr1, arr2);
// ES6 的写法
let arr3 = [0, 1, 2];
let arr4 = [3, 4, 5];
arr3.push(...arr4);

扩展运算符-应用

复制数组示例:

//复制数组
// ES5 的写法
const a1 = [1, 2];
const a2 = a1.concat();
console.log(a2); 
// ES6 的写法
const a1 = [1, 2];
const a2 = [...a1];
console.log(a2); 
//或
const [...a2] = a1;
console.log(a2);

合并数组示例:

const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];

// ES5 的合并数组
console.log(arr1.concat(arr2, arr3));
// ES6 的合并数组
console.log([...arr1,...arr2,...arr3]);

与解构赋值结合示例:

//与解构赋值结合使用
const [first, ...rest] = [1, 2, 3, 4, 5];
console.log(first);
console.log(rest);
//扩展运算符位置
const [...butLast, last] = [1, 2, 3, 4, 5];  // 报错
const [first, ...middle, last] = [1, 2, 3, 4, 5];  // 报错

遍历数组-keys()、values()和entries()

  1. keys():对键名的遍历
  2. values():对键值的遍历
  3. entries():对键值对的遍历
for (let index of ['张三', '李四'].keys()) {
    console.log(index); //0,1
}
for (let elem of ['张三', '李四'].values()) {
    console.log(elem); //张三,李四
}
for (let [index, elem] of ['张三', '李四'].entries()) {
    console.log(index, elem); //0 张三,1 李四
}

ES6中表示集合的对象主要有Array、Set、Object、Map(Set和Map后面讲解),在以前遍历他们需要使用2中不同的方法,而现在,ES6提出了Iterator机制,可以给不同的数据结构提供统一的遍历方法,就是for...of。换句话说,只有部署了Iterator的数据才能用for…of循环进行遍历


四、对象的扩展

这里包含两部分内容:
一是,数据结构本身的改变,比如属性的简洁表示法、属性名表达式、方法的 name 属性(了解即可)、扩展运算符(与《数组的扩展》中的扩展运算符使用相似,不过多赘述)等;
二是,对象的新增方法,比如Object.is()、Object.assign()等。

4.1属性的简洁表示法

普通写法示例:

let name = '小明';
let birth = '2022/09/23';
//普通写法
const Person = {
	name: name,
	birth: birth,
	getInfo: function () {
		console.log(`我的名字是:${this.name},我出生时间是:${this.birth}`);
    }
};
Person.getInfo();

简洁写法示例:

<script type="text/javascript">
    //简洁写法
    const Person = {
        name,
        birth,
        getInfo() {
            console.log(`我的名字是:${this.name},我出生时间是:${this.birth}`);
        }
    };
    Person.getInfo();
</script>

结论: ES6 允许在对象之中,直接写变量。这时,属性名为变量名, 属性值为变量的值

4.2属性表达式

JavaScript 定义对象的属性 示例1:

//JavaScript 定义对象的属性
// 方法1:用标识符作为属性名
obj.foo = true;
// 方法2:表达式作为属性名
obj['a' + 'bc'] = 123;
//使用字面量方式定义对象,只能使用标识符定义属性
var obj = {foo: true,abc: 123 };

ES6表达式作为属性名 示例2:

//使用字面量方式定义对象时,ES6允许用表达式作为属性名
let propKey = 'foo';
let obj = {
  [propKey]: true,
  ['a' + 'bc']: 123
};
//表达式还可以用于定义方法名
let obj = {
  ['h' + 'ello']() { return 'hi';}
};
obj.hello()

结论:

JS中定义对象属性的两种方法,其中示例1是直接用标识符作为属性名,方法二是用表达式作为属性名,这时要将表达式放在方括号之内。但是,如果使用字面量方式定义对象(使用大括号),在ES5 中只能使用方法一(标识符)定义属性。

在ES6中,允许字面量定义对象时,用示例2中(表达式)作为对象的属性名,即把表达式放在方括号内,注意,属性名表达式与简洁表示法,不能同时使用,会报错


4.3Object.is()

作用: 用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致

回顾:

  • ES5中用来比较两个值是否相等的运算符
    • 比较运算符(==
    • 严格比较运算符(===
  • 两个不同之处
    • +0不等于-0
    • NaN等于自身

比较运算符(==)—> 缺点:会自动转换数据类型
严格比较运算符(===)—> 缺点:NaN不等于自身,以及+0等于-0

<script type="text/javascript">
    console.log(+0 === -0); //true
    console.log(NaN === NaN); // false
    console.log(Object.is(+0, -0)); // false
    console.log(Object.is(NaN, NaN)); // true
</script>

4.4Object.assign()

概念: 用于对象的合并,将源对象的所有可枚举属性,复制到目标对象

语法:

Object.assign(target, source1, source2);//target:目标对象,source1:源对象

用途:

  1. 为对象添加属性
  2. 为对象添加方法
  3. 克隆对象
  4. 合并多个对象
  5. 为属性指定默认值

1.对象合并示例:

//基本用法
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
//进行对象合并
Object.assign(target, source1, source2);
console.log(target); // {a:1, b:2, c:3}

2.如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性

//如果目标对象与源对象有同名属性,或多个源对象有同名属性
const target1 = { a: 1, b: 1 };
const source3 = { b: 2, c: 2 };
const source4 = { c: 3 };
Object.assign(target1, source3, source4);
console.log(target1); // {a:1, b:2, c:3}

3.如果只有一个参数,Object.assign会直接返回该参数

//如果只有一个参数
const obj = {a: 1};
Object.assign(obj) === obj // true

4.如果源对象参数不是对象,则会先转成对象,然后返回

//如果非对象参数出现在源对象的位置
let obj = {a: 1};
Object.assign(obj, undefined) === obj // true
Object.assign(obj, null) === obj // true

5.非对象参数出现在源对象的位置,这些参数都会转成对象,如果无法转成对象,就会跳过(boolean和number无法转换为对象)。字符串会以数组形式,拷贝入目标对象

//其他类型的值不在首参数
const v1 = 'abc';
const v2 = true;
const v3 = 10;
const obj = Object.assign({}, v1, v2, v3);
console.log(obj)//{0: 'a', 1: 'b', 2: 'c'}

结论:

  1. 如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性
  2. 如果只有一个参数,Object.assign会直接返回该参数
  3. 如果该参数不是对象,则会先转成对象,然后返回
  4. 如果非对象参数出现在源对象的位置,那么处理规则有所不同。首先,这些参数都会转成对象,如果无法转成对象,就会跳过。这意味着,如果undefined和null不在首参数,就不会报错
  5. 如果其他类型的值(即数值、字符串和布尔值)不在首参数,也不会报错。但是,除了字符串会以数组形式,拷贝入目标对象,其他值都不会产生效果
  6. 由于undefined和null无法转成对象,所以如果它们作为参数,就会报错,错误信息:Cannot convert undefined or null to object

对象遍历示例:

var obj = { foo: 'bar', baz: 42 };
console.log(Object.keys(obj));// ["foo", "baz"]
console.log(Object.values(obj));// ["bar", 42]

//如果属性名为数值的属性
const obj1 = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.values(obj1))// ["b", "c", "a"]

//如果Object.values方法的参数是一个字符串
console.log(Object.values('foo'));// ["f", "o", "o"]

//如果Object.values方法的参数是数值和布尔值
console.log(Object.values(42)) // []
console.log(Object.values(true)) // []
console.log(Object.entries(obj))// [ ["foo", "bar"], ["baz", 42]

五、Set数据结构

作用: set数据结构类似于数组,但成员的值都是唯一的,没有重复的值,并利用这一特性可以实现数组、字符串的去重。

5.1Set数据结构-基本用法

单一数组的去重示例:

//例1:单一数组的去重
let set1 = new Set([1, 2, 2, 3, 4, 3, 5])
console.log(set1);//1,2,3,4,5

多数组的合并去重示例:

//例2:多数组的合并去重
let arr1 = [1, 2, 3];
let arr2 = [2, 3, 4, 5];
let set2 = new Set([...arr1, ...arr2])
console.log(set2);//1,2,3,4,5

去除字符串里面的重复字符:

//例3:字符串去重
console.log([...new Set('ababbc')].join(''));//abc

Set函数可以接受一个数组作为参数,用来初始化:

//例4:Set函数接受数组作为参数
const items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
console.log(items.size); // 5

5.2Set数据结构-属性和方法

在这里插入图片描述

Set数据结构属性和方法使用,示例1:

let set1 = new Set();
//向Set中添加元素
set1.add(1);
set1.add(2);
set1.add(3);
console.log(set1);//{1, 2, 3}
//返回Set实例的成员总数
console.log(set1.size);//3
//从Set中删除元素
set1.delete(1);
console.log(set1);//{2, 3}

Set数据结构属性和方法使用,示例2:

//判断某元素是否存在
console.log(set1.has(1));//false
console.log(set1.has(2));//true
//清除所有元素
set1.clear();
console.log(set1);//{};
//Set转数组
let set2 = new Set([4, 5, 6]);
console.log(Array.from(set2)); //[4, 5, 6]

Set数据结构-遍历方法,示例3:

let set = new Set(['red', 'green', 'blue']);
for (let item of set.keys()) {
	console.log(item);
}
for (let item of set.values()) {
	console.log(item);
}
for (let item of set.entries()) {
	console.log(item);
}

六、Map数据结构

Map数据结构-概念

  1. JavaScript中的对象本质上是键值对的集合,但是传统上只能用 字符串 当作键
  2. Map数据结构类似于对象,也是键值对的集合,但是“键”的范围不限于字符串, 各种类型的值 都可以当作键
  3. Object 结构提供了 “字符串—值” 的对应,Map 结构提供了 “值—值” 的对应

6.1Map数据结构-基本用法

Map构造函数接受数组作为参数示例:

//Map 也可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组
const map = new Map([['name', '张三'],['title', 'Author']]);
console.log(map.get('name')); // "张三"
console.log(map.get('title')); // "Author"

Set和Map都可以用来生成新的Map:

//Set和Map都可以用来生成新的Map
const set = new Set([['foo', 1],['bar', 2]]);
const m1 = new Map(set);
console.log(m1.get('foo')); // 1

const m2 = new Map([['baz', 3]]);
const m3 = new Map(m2);
console.log(m3.get('baz')); // 3

对同一个键多次赋值示例:

//如果对同一个键多次赋值,后面的值将覆盖前面的值
const map1 = new Map();
map1.set(1, 'aaa');
map1.set(1, 'bbb');
console.log(map1.get(1)); // "bbb"

读取一个未知的键示例:

//如果读取一个未知的键,则返回undefined
console.log(new Map().get('asfddfs'));// undefined

对同一个对象的引用示例:

//只有对同一个对象的引用,Map 结构才将其视为同一个键
const map2 = new Map();
map2.set(['a'], 555);
console.log(map2.get(['a'])); // undefined

//注意:代码的set和get方法,表面是针对同一个键,但实际上这是两个值,内存地址是不一样的,因此get方法无法读取该键,返回undefined

6.2Map数据结构-属性和方法

在这里插入图片描述

size属性使用示例:

//size属性
const map1 = new Map();
map1.set('foo', true);
map1.set('bar', false);
console.log(map1.size);//2

set方法使用示例:

//set方法
const map2 = new Map();
console.log(map2.size);//0

map2.set('edition', 6);
map2.set(262, 'standard');
map2.set(undefined, 'nah');

console.log(map2.size);//3

get方法使用示例:

//get方法
const map3 = new Map();
const hello = function () {
    console.log('hello');
};
map3.set(hello, 'Hello ES6!');
console.log(map3.get(hello));//Hello ES6!

has方法使用示例:

const map2 = new Map();
map2.set('edition', 6);
map2.set(262, 'standard');
map2.set(undefined, 'nah');
//has方法
console.log(map2.has('edition'));//true
console.log(map2.has('years'));  //false
console.log(map2.has(2));        //false
console.log(map2.has(undefined));//true

6.3Map数据结构-遍历方法

<script type="text/javascript">
    const map = new Map([
        ['F', 'no'],
        ['T', 'yes'],
    ]);
    //keys():输出:F T
    for (let key of map.keys()) {
        console.log(key);
    }
    //values():输出no yes
    for (let value of map.values()) {
        console.log(value);
    }
    //entries():输出F no T yes
    for (let item of map.entries()) {
		console.log(item[0], item[1]);
	}
	//forEach:输出F no T yes
	map.forEach(function(value, key, map) {
		console.log( key, value);
	});
</script>

七、Module

7.1Module的语法-概述

为什么需要Module模块?

JavaScript一直没有模块体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来

ES6模块带来的好处:

  1. 避免变量污染,命名冲突
  2. 提高代码复用率
  3. 提高维护性
  4. 依赖关系的管理

7.2Module的语法-命令

模块功能主要由两个命令构成

  1. export命令 :用于规定模块的对外接口

    ​ 一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望 外部能够读取模块内部的某个变量 ,就必须使用export关键字输出该变量。

  2. import命令 :用于输入其他模块提供的功能

7.2.1export命令

使用export命令可以做到外部能够读取模块内部的某个变量.

export 命令使用的2种方式示例:

// profile.js
//写法一
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
//写法二
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export { firstName, lastName, year };

结论:

profile.js文件:

写法一:保存了用户信息。ES6 将其视为一个模块,里面用export命令对外部输出了三个变量。

写法二:使用大括号指定所要输出的一组变量。它与前一种写法(直接放置在var语句前)是等价的,但是应该优先考虑使用这种写法。因为这样就可以在脚本尾部,一眼看清楚输出了哪些变量。

除此之外,export命令还可以输出函数或类可以使用as关键字重命名,可以出现在模块的任何位置,只要处于模块顶层就可以。

//输出函数或类
export function multiply(x, y) {
  return x * y;
};
//使用as关键字重命名
function v1() { ... }
function v2() { ... }
export {
  v1 as streamV1,
  v2 as streamV2,
  v2 as streamLatestVersion
};

使用export命令定义了模块的对外接口以后,其他 JS 文件就可以通过import命令加载这个模块。


7.2.2import命令

import 基本使用示例:(注意:需要设置<script type="module">

<script type="module">
    //1、基本使用
    import {firstName, lastName, year} from './profile.js';
	
    console.log('姓名:'+firstName + ' ' + lastName);//姓名:Michael Jackson
    console.log('年份:'+year);//年份:1958
</script>

2.使用as关键字,将输入的变量重命名

<script type="module">
	//2、使用as关键字,将输入的变量重命名
	import {lastName as surname} from './profile.js';
	console.log('名:'+surname );//名:Jackson
</script>

3.不允许在加载模块的脚本里面,改写接口

//person.js
function Person() {
}
Person.prototype.name = '';
Person.prototype.showName = function () {
    console.log(this.name)
}
let person = new Person();
export {person};
//3、不允许在加载模块的脚本里面,改写接口
<script type="module">
    import {person} from "./js/person.js";
    person =new Object();//报错:Assignment to constant variable.
    person.name='刘妃平'; //合法操作,因为改写name的属性是允许的
    person.showInfo();
</script>

结论:

(1)代码块的import命令,用于加载profile.js文件,并从中输入变量。import命令接受一对大括号,里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块(profile.js)对外接口的名称相同。

(2)如果想为输入的变量重新取一个名字,import命令要使用as关键字,将输入的变量重命名。

(3)不允许在加载模块的脚本里面,改写接口


7.2.3模块的整体加载

用星号(*)指定一个对象,所有输出值都加载在这个对象上面

circle.js 关键方法calculateArea()/calculatePerimeter()

//计算圆的面积
export function calculateArea(radius) {
    return Math.PI * radius * radius;
}
//计算圆的周长
export function calculatePerimeter(radius) {
    return 2 * Math.PI * radius;
}

逐一加载与整体加载示例:

<script type="module">
    import { calculateArea, calculatePerimeter } from './js/circle.js';
    //加载模块(逐一加载)
    console.log('圆面积:' + calculateArea(4));
    console.log('圆周长:' + calculatePerimeter(14));

    //加载模块(整体加载)
    import * as circle from './js/circle.js';
    console.log('圆面积:' + circle.calculateArea(4));
    console.log('圆周长:' + circle.calculatePerimeter(14));
</script>

代码中,在circle.js文件里面,它输出两个方法calculateAreacalculatePerimeter,然后在html页面加载模块


7.2.4export default命令

export default命令为模块指定默认输出

基本用法示例:

//1、基本用法
// export-default.js
export default function () {
  console.log('foo');
}
// import-default.js
import customName from './export-default';
customName(); // 'foo'

比较默认输出和正常输出

//2、比较默认输出和正常输出
// 第一组
export default function crc32() { // 输出
  // ...
}
import crc32 from 'crc32'; // 输入
// 第二组
export function crc32() { // 输出
  // ...
};
import {crc32} from 'crc32'; // 输入

1.分析export default 命令解决的问题,作用:

从前面的例子可以看出,使用import命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。但是,用户肯定希望快速上手,未必愿意阅读文档,去了解模块有哪些属性和方法。

为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出。

2.每个序号对应的是代码序号对应的说明,即一一对应的

(1)上面代码是一个模块文件export-default.js,它的默认输出是一个函数。import命令,可以用任意名称指向export-default.js输出的方法,这时就不需要知道原模块输出的函数名。需要注意的是,这时import命令后面,不使用大括号

(2)上面代码的两组写法,第一组是使用export default时,对应的import语句不需要使用大括号;第二组是不使用export default时,对应的import语句需要使用大括号。

export default命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,因此export default命令只能使用一次。所以,import命令后面才不用加大括号,因为只可能唯一对应export default命令。

除此之外,它与正常输出一样,也可输出函数和类,不再举例说明。


八、总结

总结思维导图

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

众生云海,一念初见

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值