JavaScript进阶--ES6新特性
2022/3/18 22:27:56
本文主要是介绍JavaScript进阶--ES6新特性,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
目录
let声明变量:
const声明变量:
解构赋值:
模板字符串:
对象简化写法:
箭头函数:
参数默认值:
rest参数:
扩展运算符:
Symbol数据类型:
迭代器(iterator):
生成器:
Promise:
Set:
Map:
第一次写那么多,脑壳疼
let声明变量:
- 变量不能重复声明,有效防止变量污染
- 作用域为块级作用域,只在代码块中有效,如
if (true) { var a = 10; let b = 20; } console.log(a); console.log(b);
-
不支持变量提升,即不允许在变量声明前使用变量(用var变量提升的值为undefined)
-
不影响作用域链,通俗说就是块级作用域1中的块级作用域2可以访问块级作用域中的let变量。即函数内仍可以使用全局的let变量。
const声明变量:
- 一定要赋初始值
- 一般来说命名使用大写字母
- const常量的值不可修改
- 作用于块级作用域,与let一样
- 允许对const数组、对象里面的元素进行修改(常量不可修改的本质为指向的地址不可改变)
解构赋值:
- 数组的解构:将数组元素拆分赋值给不同的变量
const T = [1, 2, 5, 4]; let [a, b, c, d] = T;
此时a,b,c,d变量分别为1,2,3,4。数量不匹配时超出的部分为undefined。
-
对象的解构:将对象元素拆分赋值给不同的变量
const data = { name: "baciii", age: 20, say: function() { console.log('say'); } }; let { name, age, say } = data;
与数组解构的区别,此时被赋值的变量名必须与对象的元素名一致,顺序可以颠倒。
模板字符串:
声明方式:`str`(双反引号)
特性:1.双反括号内可以出现换行(回车换行)
let str1 = `123 123 123`;
2.可以进行变量拼接
let name='Baciii'; let p=`Iam${name}`;
对象简化写法:
- 允许直接在大括号内写入已声明的变量和函数作为对象的属性和方法
let name = "baciii"; let age = 20; var data = { name, age, }
2. 方法声明简化,不需要写function直接声明
var data = { say() { console.log('say'); } }
箭头函数:
语法:
let fn=(a,b,c)=>{xxx}
特性:
- 箭头函数的this始终是静态的,this始终指向函数声明是所在的作用域,并且不能被call()、bind()等改变this指向。
- 箭头函数不能作为构造函数实例化对象,原因是this的指向不会改变。
- 不能使用arguments()属性获取函数传递的实参。
- 缩写形式:当形参有且只有一个时,可以省略括号;当函数体只有一句时,可以省略花括号,且函数的返回值为该语句的执行结果。
let pow=n =>n*n;
参数默认值:
允许在声明函数时给参数赋默认值,若不传递参数时则使用该默认值。
function fn(a=10,b=20,c=30){ console.log(a,b,c); }
rest参数:
获取函数的实参,代替arguments。返回值为一个数组。
语法:...args(三个点)
function fn(a, b, ...args) { console.log(args);//[3,4,5,6] console.log(arguments);//{1,2,3,4,5,6} } fn(1, 2, 3, 4, 5, 6);
args返回的是一个数组,arguments返回的是一个对象。rest参数必须放在形参的最后一个。
扩展运算符:
语法:...(伪)数组名
作用:将数组拆分为以逗号分割每个元素的序列,可以直接当作参数传递。
应用场景:将伪数组转化为真正的数组,使其可以使用数组的各种方法、数组的克隆等。
const d=document.querySelectorAll('div'); const divs=[...d]; const div_copy=[...divs];
Symbol数据类型:
Symbol表示一个独一无二的值,是一个类似字符串的数据类型。
即使是通过同一个字符串声明的Symbol()也不相同,而通过Symbol.for()用同一个字符串声明的Symbol相同,且地址相同(即为同一个变量)。可以将Symbol理解为一个唯一的不会重复的字符串,用其声明变量不会出现命名冲突的问题。
let s1 = Symbol(); let s2 = Symbol('abc'); let s3 = Symbol('abc'); console.log(s2 === s3);//false let s4 = Symbol.for('qwe'); let s5 = Symbol.for('qwe'); console.log(s4 === s5);//true
可用于给对象添加理论上一定不会命名重复的元素。
let test1 = Symbol('t1'); let o = { [Symbol('test')]:function(){} } o[test1] = function() { console.log(111); } o[test1]();
对象中的Symbol元素不会被for...in、forEach循环遍历,但是可以通过Object.getOwnPropertySymbols()方法获取当前对象所有用作属性名的Symbol值,该方法返回一个数组,可以借助数组元素访问到对象的Symbol值元素。也可以通过Symbol.keyFor()方法返回某Symbol对象的参数值
let name=Symbol('name'); console.log(Symbol.keyFor(name));//name
Symbol()的参数只是作为一个标识所用,并不会特定创建某一特殊对象,所以不写参数也是一样的(个人理解,仅供参考)。
Symbol还有很多内置函数,此处不一一赘述(主要是我也还没搞明白,下次再说叭)
迭代器(iterator):
迭代器是一个接口,为不同的数据结构提供一种遍历操作的机制。
原生支持iterator接口的数据类型有:Array、Arguments、Set、Map、String、TypedArray、NodeList。
for of迭代与for in迭代的区别:for in迭代返回的是每个元素的键名key,for of迭代返回的是每个元素的键值value。
const arr = ['a', 'b', 'c', 'd']; for (let i in arr) { console.log(i); } for (let i of arr) { console.log(i); }
iterator的工作原理:
- 创建一个指针对象,指向要迭代的数据结构的起始位置,该对象由调用tar[Symbol.iterator]方法获得。
- 调用该指针对象的next方法,使其指向数据结构的第一个成员,并返回一个对象。该对象有value属性、done属性,value属性的值为当前被指向元素的属性值,done属性表示迭代是否完成。
- 接下来不断调用指针对象的next方法,直到访问到最后一个对象。
- 当访问到最后一个对象后的下一次调用next,返回一个value值为undefined、done值为true的对象,表示当前迭代已完成。
我们可以根据工作原理给原生不支持iterator迭代的数据类型添加该方法:
[Symbol.iterator]: function() { let index = 0; return { next: function() { if (index < data.member.length) { const d = { value: data.member[index], done: false } index++; return d; } else { return { value: undefined, done: true } } } } }
简要说明:
给要添加iterator的数据类型添加[Symbol.iterator]方法,首先声明一个变量记录迭代的次数,用于后期判断是否迭代完成。 该方法返回一个对象,该对象有一个next方法。
next方法会在迭代未结束时返回当前迭代元素的值value和是否完成迭代判断属性done。迭代完成后会返回value:undefined,done:true的一个对象。
个人觉得与链表的实现差不多,这个操作之后就可以对该对象使用for of方法。
生成器:
生成器是一种特殊的函数,可以用于异步编程
基本语法:function * fn(){}
生成器函数内的语句不会直接执行,通过调试可以发现,生成器函数内部有一个next()方法,我们可以通过next方法执行函数内部的语句。我们可以通过若干个yield语句将函数内部分为若干个+1部分,每次调用next()方法就按顺序执行一部分。
function* fn() { console.log(123); let a = yield '123'; console.log(a); console.log(456); let b = yield '456'; console.log(b); console.log(789); } let f = fn(); f.next(); f.next('aaa'); f.next('bbb');
每次调用next()方法,该方法的返回值为执行部分的下一个yield语句后面的内容,如这里第一个next()方法的返回值为123.
我们还可以给next()方法传参数,该参数会成为当前执行部分的上一个yield语句的返回值。(这里很绕,不要混淆两者),所以此处代码第一个yield,即a的值为aaa。
所以我们可以利用其分步执行的性质进行异步编程,其核心是避免回调地狱的问题产生。
function one() { setTimeout(() => { console.log(1); i.next(); }, 1000) } function two() { setTimeout(() => { console.log(2); i.next(); }, 2000) } function three() { setTimeout(() => { console.log(3); i.next(); }, 3000) } function* fun() { yield one(); yield two(); yield three(); } let i = fun(); i.next();
简要解释:
此处的三个yield语句分别为三个函数,当第一次调用next()方法时,执行第一个定时器函数,当执行完毕后自动调用next()方法执行下一个yield语句的函数(因为next()方法的返回值就是那个函数),以此类推。 (虽然只有两个玩意,但是还是很绕qaq)
Promise:
异步编程的新解决方案
语法:const p=new Promise(function(resolve,reject){});
Promise对象有三种状态:初始化、成功、失败。当调用第一个参数时,即resolve(),表示状态为成功,反之reject(),表示状态为失败。
Promise.then()方法:该方法的参数为两个函数,Promise对象为成功时执行第一个函数,失败时执行第二个函数。
Promise读取文件操作:当读取成功时调用then参数1,失败时调用then参数2
const { log } = require('console'); const fs = require('fs'); // fs.readFile('./promise.md', (err, data) => { // //err 错误对象 失败时为null data 读取结果 // if (err) throw err; // //data是一个buffer // console.log(data.toString()); // }); //使用promise封装 const p = new Promise(function(resolve, reject) { fs.readFile('./promise.md', (err, data) => { if (err) reject(err); resolve(data); }) }); p.then(function(value) { console.log(value.toString()); }, function(reason) { console.log("读取失败"); });
Promise封装Ajax:当http状态码正常时调用then参数1,反之调用then参数2
const p = new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('GET', 'https://api.apiopen.top/getJoke'); xhr.send(); xhr.onreadystatechange = function() { if (xhr.readyState === 4) { if (xhr.status >= 200 && xhr.status < 300) { resolve(xhr.response) } else { reject(xhr.status) } } } }); p.then(function(value) { console.log(value); }, function(reason) { console.log(reason); })
then()方法:
then()方法的返回值为一个Promise对象,then()方法的回调函数的返回结果决定。
若then()方法的回调函数返回的是一个非Promise对象,then()方法返回的Promise对象的状态为成功,且promisevalue为该返回值 不return默认return undefined 同样状态为成功
若then()方法的回调函数返回的是一个Promise对象,则then()方法返回的Promise对象由其决定,且promisevalue为Promise函数调用resolve或reject时的参数。
由于then()方法返回的是一个Promise对象,所以同样地,新产生的Promise对象也有then()方法,所以一致链式调用then()方法,所以可以根据该特点进行异步编程,与生成器函数类似。
如利用Promise读取多个文件的操作:
const { log } = require('console'); const fs = require('fs'); // fs.readFile('./promise.md', (err, data1) => { // fs.readFile('./promise1.md', (err, data2) => { // fs.readFile('./promise2.md', (err, data3) => { // let data = data1 + data2 + data3; // console.log(data); // }) // }) // }); const p = new Promise((resolve, reject) => { fs.readFile('./promise.md', (err, data) => { resolve(data); }) }); p.then(function(value) { console.log(value.toString()); return new Promise((resolve, reject) => { fs.readFile('./promise1.md', (err, data) => { resolve([value, data]); }) }) }).then(function(value) { console.log(value); return new Promise((resolve, reject) => { fs.readFile('./promise2.md', (err, data) => { value.push(data); resolve(value); }) }) }).then(function(value) { console.log(value.toString()); })
简要说明:
第二次之后的读取文件都在then()方法的回调函数中进行,这样可以利用promisevalue访问到上一次读取到的文件的信息,并进行操作。其中一个步骤出错后面不再执行。(个人感觉也跟链表有点像)
Set:
一种新的数据结构,类似于数组,但成员的值是唯一的,加入成员时会自动去重。
基本操作:size 返回元素个数 add 添加元素 delete 删除元素 has 判断是否存在该元素 clear 清空操作
该数据结构可以使用for of遍历,基本运用场景:与扩展运算符交互紧密,可以将其与数组进行转换。
let arr = [1, 2, 3, 4, 5, 4, 3, 2, 1]; //数组去重 // let result = new Set(arr); //此时result不是一个数组 是一个集合 // let r = [...result] //将result转化为一个数组 // console.log(result); // console.log(r); //求交集 let arr2 = [4, 5, 6, 5, 6]; let result = [...new Set(arr)].filter(item => { return new Set(arr2).has(item); }); console.log(result); //求并集 let arr3 = [1, 2, 3, 4, 5, 4, 3, 2, 1]; let arr4 = [4, 5, 6, 5, 4]; let r = [...new Set([...arr3, ...arr4])]; console.log(r); //求差集 let diff = [...new Set(arr)].filter(item => !(new Set(arr2).has(item))); console.log(diff);
Map:
一种以键值对形式存储的数据结构,键可以是各种数据类型,可以使用for of遍历。该数据结构的返回值的每一项为一个数组,数组的一个成员为键名key
let m = new Map(); m.set('name', 'baciii'); m.set('say', function() { console.log('say'); })
第二个成员为键值value。
class的get()、set()方法:
当我们读取、更改类内的属性时,相应的get()、set()方法就会被自动调用,我们可以通过重写该方法在读取或修改某属性时进行操作。
class Phone{ get price(){ let newVal=''; return newVal; } set price(newVal){ console.log('newVal is'+newVal); } }
get()方法的返回值为当前属性的读取值,set()方法必须有参数,该参数为当前属性的新值。
第一次写那么多,脑壳疼
最后一句,新冠赶紧死,我想回学校!
这篇关于JavaScript进阶--ES6新特性的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-06-26结对编程到底难不难?答案在这里
- 2024-06-19《2023版Java工程师》课程升级公告
- 2024-06-15matplotlib作图不显示3D图,怎么办?
- 2024-06-1503-Loki 日志监控
- 2024-06-1504-让LLM理解知识 -Prompt
- 2024-06-05做软件测试需要懂代码吗?
- 2024-06-0514-ShardingSphere的分布式主键实现
- 2024-06-03为什么以及如何要进行架构设计权衡?
- 2024-05-31全网首发第二弹!软考2024年5月《软件设计师》真题+解析+答案!(11-20题)
- 2024-05-31全网首发!软考2024年5月《软件设计师》真题+解析+答案!(21-30题)