本文主要介绍的是ES6以及更新的JavaScript脚本语言标准规范的使用。主要是对相关知识点的自我认知和理解,如有不对的地方,望指出并探讨
通过let关键字声明变量
let dzw = "大张伟";
let关键字声明的变量的特点:
var关键字声明的变量特点
var关键字
for (var i = 1; i <= 10; i++) {
console.log(i, "outer"); // 1 - 10
setTimeout(function () {
console.log(i, "inner"); // 10 个 11
}, i * 500);
}
分析:
var i = 1,首先是不会形成局部作用域的,而且会将i变量的声明提升至for循环体的外面,这里会挂载到window对象下,同步逻辑代码for循环执行完后 ,再执行 setTimeout异步代码,此时在setTimeout异步里面,存在变量i,但没有声明,所以会向父级作用域链查找,找到了window.i = 10++; 所以会打印10个11
使用IIFE,立即执行函数解决这个问题
for (var i = 1; i <= 10; i++) {
setTimeout((function (j) {
return function() {
console.log(j);
}
})(i), i * 500);
}
分析:
这里会把每次循环的变量i,传到setTimeout的回调函数里,使用闭包的方式,进行变量的保存,当然会存在闭包的缺点,比如变量无法销毁,导致内存的泄露
使用let关键字解决这个问题
for (let i = 1; i <= 10; i++) {
console.log(i, "outer"); // 1 - 10
setTimeout(function () {
console.log(i, "inner"); // 1-10
}, i * 500);
}
分析:
let i = 1; 不存在变量声明提升,而且会形成局部作用域{},将当前循环的变量保存到当前的作用域下;每次迭代都形成一个具有新的初始化let i的局部作用域,且每个let i 互不影响。之后的每一个初始化的i值,都是上一次迭代的i的结果,因此在同步for代码执行完后,再执行setTimeout,会在当前局部作用域下,找到之前保存的初始化的i变量值。 因为变量i的持久保存(遇到每个作用域的 } ,let销毁,作用域销毁),所以let会自动形成闭包
通过const关键字声明常量
const DZW = "大张伟";
const定义的常量的特点:
锁栈不锁堆的扩展:
使用Object.freeze(obj); 可以锁住对象(锁堆)
const DZW = {
age : 18
};
Object.freeze(DZW);
DZW.age = 35; // DZW 这个对象被冻结,其内部属性更改失败
console.log(DAW.age) // 18
… 扩展运算符将数组转化为 逗号分隔的实参参数序列: test(…arr)
let xing = ["大"];
let ming = ["张伟"];
let dzw = [...xing, ...ming]; // ["大","张伟"]
let dzw = ["大","张伟"];
let _dzw = [...dzw];
console.log(_dzw);
function add() {
// 类数组 Array.isArray(arguments)
console.log(arguments, Array.isArray(arguments)); // [Arguments] { '0': 1, '1': 2, '2': 3 } false
let _argu = [...arguments];
console.log(_argu, Array.isArray(_argu)); // [ 1, 2, 3 ] true
}
add(1, 2, 3);
var arr = [1,3,5,7,2,9,8];
// var max = Math.max(1, 3, 5, 7, 2, 9, 8);
// var max = Math.max.apply(Math,arr); //apply所接收的参数是一个数组
var max = Math.max(...arr);
let arr = ['篮球', 'music'];
let [ball, other] = arr; // ball = '篮球' other = 'music'
let zhw = {
name: 'zhw'
age: 18,
say(){
console.log('hello')
}
}
let { say } = zhw;
say() // 'hello’
模板字符串使用 `` 反引号
特性:
const test = (a, b) => {
return a + b
}
const sum = test(1, 2)
console.log(sum) // 3
特性:
使用场景:箭头函数适合与this无关的回调,比如
不适用场景:箭头函数不适合与this有关的回调,比如
ES6引入rest参数, 用于获取函数的实参,用来代替 arguments
function test() {
// arguments收集到的实参是一个包裹了1, 2, 3, 4, 5的类数组,本质是一个对象
console.log(arguments)
}
test(1, 2, 3, 4, 5)
可以通过以下方法将类数组arguments 转化为数组
function test(...args) {
// 收集的实参args是一个数组
console.log(args);
}
注意:rest参数必须放在所有形参的最后, 否则会报语法错误
function test(a, b, ...args) {
console.log(a); // 1
console.log(b); // 2
// 收集的实参args是一个数组
console.log(args); // [3, 4, 5]
}
test(1, 2, 3, 4, 5)
function test(a, b, {...args}) {
console.log(a); // 1
console.log(b); // 2
// 收集的实参args是一个数组
console.log(args); // [3, 4, 5]
}
test(1, 2, {name: 'zhw'})
一种新的基本数据类型
let s = Symbol();
// let s2 = Symbol('当前symbol的描述信息')
let s2 = Symbol('desc')
let s3 = Symbol('desc')
console.log(s2 === s3) // false
// 对象方法创建symbol类型
let s4 = Symbol.for('desc')
let s5 = Symbol.for('desc')
console.log(s4 === s5) // true
用来解决命名冲突的问题,
给对象添加属性和方法, 避免别人覆盖属性
痛点:
// 假设这是别人写的对象, 我们并不知道对象里具体有哪些属性
let zhw = {
name: '呵呵'
}
// 需要给对象zhw添加name属性
zhw.name = xxx // 这样给对象添加属性是不安全的
解决: 使用Symbol()定义需要添加的属性
let needAddProps = {
name: Symbol()
}
// 这样使用不会破坏原对象的数据结构
zhw[needAddProps.name] = xxx
其他方法向对象添加Symbol类型的属性
let zhw = {
name: '呵呵',
[Symbol('say')]: function() {
console.log('hello')
}
}
Symbol有自己的一些内置属性, 这些属性都是用来控制 对象在特定情况下的表现, 例如
Sysbol.isConcatSpreadable 控制数组是否可以展开let arr = [1, 2, 3]
let arr2 = [4, 5, 6]
console.log(arr.concat(arr2)) // [1, 2, 3, 4, 5, 6]
// 禁止arr2数组展开
arr2[Sysbol.isConcatSpreadable] = false
console.log(arr.concat(arr2)) // [1, 2, 3, [4, 5, 6]]
let s = Symbol('zhw')
console.log(s.description);
迭代器(Iterator)是一种接口,为不同的数据结构提供统一的访问机制。
js中,迭代器的本质是 对象属性
注意:只要部署了Iterator接口,就可以使用ES6提供的for…of命令遍历
let zhw = {
name: '呵呵'
hobbies: [
'篮球',
'rap'
]
}
// 报错: zhw对象无iterator
for(let item of zhw) {
}
给对象加入迭代器
let zhw = {
name: '呵呵'
hobbies: [
'篮球',
'rap'
],
[Symbol.iterator]() {
let index = 0
// 返回一个指针对象
return {
next: () => {
if(index < this.hobbies.length) {
const result = {value: this.hobbies[index], done: false)
index++
return result
} else {
return {value: undefined, done: true}
}
}
}
}
}
生成器本质是一个函数。 生成器的返回结果其实是一个迭代器对象,因为具有next方法
// * 位置可左可右可中间
function * gen() {
console.log('hello')
}
let iterator = gen();
// 迭代器函数的调用语句,执行生成器函数体的语句
iterator.next() // 返回结果是yield对应的值
yield 生成器函数代码体的分隔符n个yield,将代码体分割成n + 1块
function * gen() {
console.log(111)
yield '分隔符1';
console.log(222)
yield '分隔符2';
console.log(333)
yield '分隔符3';
console.log(444)
}
for (const iterator of gen()) {
console.log(iterator);
console.log('======')
}

function * gen(arg) {
console.log(arg)
}
let iterator = gen('生成器函数的参数')
iterator.next() // 执行了 console.log(arg)
第n个yield的返回值,保存着第n + 1个的next方法参数
function * gen() {
console.log('代码块1')
let one = yield 111;
console.log(one, 'zhw');
console.log('代码块2')
}
let iterator = gen()
iterator.next()
iterator.next('第2个next的参数')
next传多个参数 用数组: Generator
.next(…args: [] | [unknown]): IteratorResult
function * gen() {
console.log('代码块1')
let one = yield 111;
console.log(one);
console.log('代码块2')
}
let iterator = gen()
iterator.next()
iterator.next(['第2个next的参数', 'zhw'])

例如: 1s后打印111,2秒后打印222, 3秒后打印333
setTimeout(function(){
console.log(111)
setTimeout(function(){
console.log(222)
setTimeout(function(){
console.log(333)
}, 3000)
}, 2000)
}, 1000)
缺点:多层回调嵌套造成了 地狱回调【一种回调现象】,不便于阅读和维护代码
function one(timer, num) {
setTimeout(() => {
console.log(num);
// 第二次调用next 可以通过第一个yield获取参数数据
iterator.next('one')
}, timer);
}
function two(timer, num) {
setTimeout(() => {
console.log(num);
// 第三次next 此时会调用: yield three(3000, 333)
// 不过第三次的next参数 是传递给第二个yield的返回值的
iterator.next('two')
}, timer);
}
function three(timer, num) {
setTimeout(() => {
console.log(num);
iterator.next('three')
}, timer);
}
function * gen() {
let resOne = yield one(1000, 111);
console.log(resOne);
let resTwo = yield two(2000, 222);
// 第三次yield的参数
console.log(resTwo);
const resThree = yield three(3000, 333);
console.log(resThree);
}
let iterator = gen()
// 第一次调用next
iterator.next()
Promise本质是一个构造函数。 是ES6引入的异步编程的一种解决方案,主要是用来解决原生js造成的回调地狱现象
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('111')
// reject('err')
}, 500)
})
p.then((res)=>{
console.log(res); // 111
}, (err)=>{
console.log(err); // err
})
p,catch(err=>{
console.log(err); // err
})
p.then(成功的回到, 失败的回调)
p.then()的返回值始终是一个Promise。具体状态(PromiseState),由回调参数的返回值决定,规则如下
let p = new Promise((resolve, reject) => {
setTimeout(() => {
// resolve('111')
reject('err')
}, 500)
})
// p.then()的返回值始终是一个Promise。具体状态(PromiseState),由回调参数的返回值决定,规则如下
// 1、没有return(即是返回undefined) / 返回非Promise的值 状态为:fulfilled
// 2、return返回Promise对象: 状态和返回的Promise状态保持一致
let res = p.then((res)=>{
console.log(res); // 111
// resolve('111') 触发return
}, (err)=>{
console.log(err); // err
// reject('err') 触发return
return new Promise((resolve, reject) => {
resolve('res')
})
})
console.log(res, 'res'); // PromiseState: fulfilled, PromiseResult: 'eee'
需求: 1s后打印111,2秒后打印222, 3秒后打印333
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(111);
}, 1000)
})
p.then((res)=>{ // 这个res是p对象的Promise的执行结果
console.log(res);
return new Promise((resolve, reject) => {
setTimeout(()=>{
resolve(222)
}, 2000)
})
}).then(res=>{ // 这个res是上一个回调参数的 promise执行结果
console.log(res);
return new Promise((resolve, reject) => {
setTimeout(()=>{
resolve(333)
}, 3000)
})
}).then(res => { // 这个res是上一个回调参数的 promise执行结果
console.log(res);
})
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('111')
}, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('222')
}, 2000)
})
// PromiseState: 始终是fulfilled
// PromiseResult[]:
// [{status: 'fulfilled', reason: '111'} , {status: 'rejected', reason: '111'}]
const res = Promise.allSettled([p1, p2])
console.log(res);
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('111')
}, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('222')
}, 2000)
})
// PromiseState: fulfilled
// PromiseResult[]:
// ['111', '222']
const res = Promise.all([p1, p2])
console.log(res);
async 和 await 结合可以让异步代码 像同步一样执行
async function fn() {
// 1. 返回 非 Promise对象时, fn()的返回状态为: fulfilled
// return 'zhw'
// 2. 抛出一个错误时, fn()的返回状态为: rejected
// throw new Error('error')
// 3. 返回一个Promise对象时, fn()的返回状态 由 return 的 Promise状态决定
return new Promise((resolve, reject) => {
// resolve('data')
reject('error')
})
}
// res为一个Promise对象, 所以可以使用then()
let res = fn()
console.log(res);
1s后打印111,2秒后打印222, 3秒后打印333
function one() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(111);
}, 1000)
})
}
function two() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(222);
}, 2000)
})
}
function three() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(333);
}, 3000)
})
}
async function test() {
let _one = await one();
console.log(_one)
let _two = await two();
console.log(_two)
let _three = await three();
console.log(_three)
}
test()
集合:ES6提供的一种数据集合。类似于数组,但不是数组。本质是一个对象【typeof new Set === ‘object’】。成员都是唯一的。Set实现了迭代器接口,所以可以使用扩展运算符…和for…of
let arr = [1, 2, 3, 4, 2, 1]
let s = new Set(arr)
console.log(typeof s); // 'object'
console.log(s.size); // Set对象个数
// let s2 = s.add(5)
// console.log(s === s2); // true: Set(5) {1, 2, 3, 4, 5}
// let flag = s.delete(1)
// console.log(s); // Set(5) {2, 3, 4}
console.log(s.has(1));
s.clear()
console.log(s);
let arr = [1, 2, 3, 4, 2, 1]
let s = new Set(arr)
for (const item of s) {
console.log(item); // 1, 2, 3, 4
}
let arr = [1, 2, 3, 4, 2, 1]
let s = new Set(arr) // 类数组 Set自带去重
let sArr = [...s] // 类数组转数组
console.log(sArr);
ES6提供的新的数据结构。Map数据结构是一种用于存储键值对的数据结构,是一种升级版的对象,即对象的key不限于字符串,key可以是各种类型,Map中的键是唯一的。实现了迭代器接口,可以用扩展运算符…和for…of进行遍历
let obj = {name: 'zhw'}
let m = new Map()
m.set('name', 'zhw')
m.set('age', 18) // 被覆盖
m.set('age', 22) // Map的键是唯一的 下面会覆盖上面的
m.set('say', () => {console.log('i say');})
m.set(obj, 'zhw?')
// console.log(m); // 0: {"name" => "zhw"}
// for (const item of m) {
// // 每一项是一个数组[key, value]
// console.log(item); // ['name', 'zhw']
// }
let arr = [...m]
console.log(arr); // [Array(2), Array(2), Array(2)] ['name', 'zhw']
console.log(m.get('name'));
console.log(m.get(obj));
console.log(m.has('age'))
// m.clear()
console.log(m.clear()) // undefined
// 二维数组 转对象
const result = Object.fromEntries([
['name', 'zhw'],
['age', 18]
])
console.log(result);
const m = new Map()
m.set('name', 'zhw')
// Map就是一个二维数组
const result2 = Object.fromEntries(m)
console.log(result2);
这里的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法
function Person(name, age) {
// new Person() 就会执行这里的代码体
this.name = name || 'zhw';
this.age = age;
// console.log(this);
}
Person.prototype.say = function() {
console.log('i say');
}
// 构造函数的静态属性
Person.p = 'Peron对象上的属性p' // Peron对象上的属性:会挂载到实例化对象的原型上
Person.prototype.test = 'Person原型上的属性test' // Person原型上的属性, 会呗实例化对象直接访问到
let zhw = new Person('zhw', 22)
// zhw.__proto__.constructor === Person
// console.log(zhw.__proto__.constructor === Person); // true
console.log(zhw.test);
console.log(zhw.__proto__ === Person.prototype); // true
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
// 类似原生的Person.p
// 静态属性属于类/或者构造函数, 不属于实例对象
static p = 'Peron对象上的静态属性p'
// 类的非静态属性
test = 'Person原型上的属性test' // 和构造方法里面的属性同级
// Person.prototype.say的简化
// 这块必须是ES6的简介方法,不能出现function关键字的完整写法
say() {
console.log('i say');
}
}
let zhw = new Person('zhw', 19)
// zhw.say()
// console.log(zhw, 'zhw');
// 访问类的静态属性
// console.log(zhw.__proto__.constructor === Person); // true
// console.log(Person.p);
console.log(zhw.test);
// 父构造方法
// 手机
function Phone(color, price) {
this.color = color;
this.price = price;
}
Phone.prototype.call = function() {
console.log('打电话');
}
// 子构造方法
// 智能手机
function SmartPhone(color, price, size) {
// this指向SmartPhone
Phone.call(this, color, price); // 类似super()
this.size = size
}
// 设置子构造函数的原型
// SmartPhone的原型指向了父构造函数 无参构造实例对象
SmartPhone.prototype = new Phone();
// 还原SmartPhone的构造函数指向
SmartPhone.prototype.constructor = SmartPhone;
// 声明子构造函数的方法
SmartPhone.prototype.playGame = function() {
console.log('玩游戏');
}
const HuaWei = new SmartPhone('黑色', 3999, '15px')
console.log(HuaWei, 'xx');
// 父类
// 手机
class Phone {
constructor(color, price) {
this.color = color;
this.price = price;
}
call() {
console.log('打电话');
}
}
// 子类智能手机
class SmartPhone extends Phone{
constructor(color, price,size) {
// 调用父类的构造函数
super(color, price);
this.size = size;
}
playGame() {
console.log('打游戏');
}
}
let huaWei = new SmartPhone('黑色', 1000, '大得很');
console.log(huaWei);
class Person {
// 公有属性
name;
// 私有属性
#sex;
constructor(name, sex) {
this.name = name;
this.#sex = sex;
}
// 只有在类里面才可以访问私有属性
}
const zhw = {
name: 'zhw',
age: 18,
hobbies: ['篮球', 'rap'],
other: {
desc: '待定'
},
}
// 获取对象所有的key
// console.log(Object.keys(zhw)); // ['name', 'age', 'hobbies', 'other']
// 获取对象所有的值
// console.log(Object.values(zhw)); // ['zhw', 18, ['篮球', 'rap'], {desc: '待定'}]
// 将对象的键值用二维数组包裹起来
// [
// ['name', 'zhw']
// ]
// 将对象转Map数据结构
// let m = new Map(Object.entries(zhw))
// console.log(m.get('name'));
// Object.getOwnPropertyDescriptors(): 获取对象属性的描述对象
// 自定义创建对象
// zhw将挂载在other_zhw的原型上
const other_zhw = Object.create(zhw, {
// 属性名: 描述对象
errorDesc: {
value: "另一个我。 错了",
writable: true,
configurable: true,
enumerable: true
}
})
// 不会获取原型上zhw对象的属性描述对象
// 仅仅打印errorDesc属性的描述对象
console.log(Object.getOwnPropertyDescriptors(other_zhw))
// 将n维数组 转化为 n - 1维数组
// const arr = [1, 2, 3, [4, 5]]
// console.log(arr.flat()); // [1, 2, 3, 4, 5]
// const arr = [1, 2, 3, [4, 5, [6, 7]]]
// console.log(arr.flat()); // [1, 2, 3, 4, 5, [6, 7]]
const arr = [1, 2, 3, [4, 5, [6, 7]]]
// 参数: 扁平化的深度 默认是1
console.log(arr.flat(2)); // [1, 2, 3, 4, 5, 6, 7]
// 需求:一维数组的每个元素 * 10
const arr = [1, 2, 3, 4]
// 结果是二维是数组
// const res = arr.map(item => [item * 10]) // [[10], [20], [30], [40]]
// 此时需要使用flatMap
const res = arr.flatMap(item => [item * 10]) // [10, 20, 30, 40]
console.log(res);
模块化:是指将一个大的程序文件,拆分成许多个小的文件,然后将小文件组合起来
export const a = 1;
export const b = {};
export const c = () => {}
const a = 1;
const b = {};
const c = () => {}
export {
a,
b,
c
}
export default {
const a = 1;
const b = {};
const c = () => {}
}
// m1 文件别名; as: 取别名关键字
import * as m1 from './src/js/m1.js
import {a, b, c} from './src/js/m1.js'
// 当 当前文件import解构的变量重名时, 可以给重名的变量取别名
import {a as m2_a } from './src/js/m2.js'
// 解构赋值的方法 导入默认导出的文件 也是默认导出文件的
import {default as m3 } from './src/js/m3.js'
// m3: 自定义的
import m3 from './src/js/m3.js'
判断数组中, 是否包含某个元素。替代原生的 indexOf
** 指数运算符 替代 原生的 Math.pow(2, 10)
console.log(2 ** 10)
console.log(Math.pow(2, 10))
又一个新的基本类型
let n = 521n;
console.log(n, typeof n); // 521n 'bigint'
let n = 521;
console.log(BigInt(n)); // 521n
// 参数只能是整数
console.log(BigInt(1.2)); // error
let max = Number.MAX_SAFE_INTEGER;
// console.log(max); // 9007199254740991
// console.log(max + 1); // 9007199254740992
// // 再 + 1 大数值未变
// console.log(max + 2); // 9007199254740992
// 希望大数值变化
console.log(BigInt(max)); // 9007199254740991n
console.log(BigInt(max) + BigInt(1)); // 9007199254740992n
// bigint 只能和bigint类型运算
// 所以这里的2 也需要转换为bigint再运算
console.log(BigInt(max) + BigInt(2)); // 9007199254740993n