1. let 关键字
// let 定义的变量名不能重复,否则报错; 也不存在变量提升
// 块级作用域
let name = "JS"
※:let 的操作相当于在一个代码块内
var divs = document.getElementsByClassName("div");
for(let i = 0; i < divs.length; i++){
// 这里用let 就不会出现用var修饰的那种报错的情况
divs[i].onclick = function (){}
}
2. const 关键字
// const 用来声明常量, 值不能修改, 必须赋初始值; 块级作用域。
// 对常量数组的元素进行修改不会报错, 不算做对常量的修改。
const name = "JS"
3. Symbol 关键字:
用来声明一个唯一值,Symbol是JS 的第七种类型的数据,其特点是不能参与任何计算,定义的对象也不能通过for...in进行操作,只能使用Reflect.ownKeys进行遍历。
// 声明Symbol变量
1. let s1 = Symbol() // 返回一个唯一值
2. let s2 = Symbol.for("A"); // 这样可以创建两个值一样的Symbol变量
let s1 = Symbol()
let s2 = Symbol()
let s3 = Symbol("A")
let s4 = Symbol("A")
s1 === s2 // false
s3 === s4 // true
Symbol在创建对象中 不能直接作为属性名称,因为是一个动态的值,想要使用需要加上大括号。
let person = {
[Symbol()]: function (){}
}
Symbol可以方便我们对一个对象进行属性添加的时候无需考虑是否已经存在当前方法。
let person = {
up: function (){}
}
let param = {
up: Symbol()
}
person[param.up] = function (){}
Symbol的内置属性:能控制在特定场景下的一些表现。
// 1. Symbol.isConcatSpreadable
let arr1 = ["A","B","C"]
let arr2 = ["1","2","3"]
arr2[Symbol.isConcatSpreadable] = false // 设置链接时数组能否展开
// 2. Symbol.hasInstance
class Person{
static [Symbol.hasInstance](params){
console.log("开始做老北京核酸检测")
}
}
var person = new Person();
console.log(person instanceof Person)
4. BigInt类型:为了解决超过当超过 Number.MAX_SAFE_INTEGER 的数据。
// 声明一个bigInt类型 在后面加上n即可
let r = 123n;
// 类型转换
let r = BigInt(123);
// 运算
let r = 123n + 456n;
let r = 123n - 456n;
let r = 123n * 456n;
let r = 123n / 456n;
5. 变量解析:有点像Python的变量拆包
// 解析数组
const teams = ["A","B","C"];
let [a, b, c] = teams; // 数组会按照索引解析,注意两边类型一致
// 解析对象
const person = {
name: "xxx",
age: 12,
say: function (){
console.log("======")
}
}
let {name, age, say} = person; // 对象会按照和属性名相同的变量进行解析,不能用一个不存在的属性名作为变量进行接收。
6. 迭代器:在ES6中添加了for...of 循环遍历,和Java的加强for循环一样
// 使用for...of循环
let words = ["A", "B", "C"]
for(let word of words){
console.log(word)
}
// 通过Symbol.iterator 创建一个迭代器
var iterator = arrs[Symbol.iterator]();
iterior.next()
其实for...of 实际上去调用的对象的 iterator的实现方法,也就是说所有能进行遍历的都是实现iterator的方法。
var obj = {
arrs: ["A", "B", "C"],
[Symbol.iterator](){ // 实现iterator方法
let index = 0;
let _this = this // 由于返回的是一个对象需要提前保留this对象
return {
next: function (){
if(index < arrs.length){
return {value: _this.arrs[index++], done: false}
}
// 只有返回done为true的时候循环才会终止,否则会一直循环
return {value: undefined, done: true}
}
}
}
}
7. 生成器和yield关键字
function * Generator(){} // 定义一个生成器函数,需要加 *
let iter = Generator();
iter.next();
一般yield会和生成器一同使用,yield的作用类似return,每次调用next方法的时候都会从上一个yield处开始执行。
function * Generator(){
let a = yield "aaaa";
yield a;
}
let iter1 = Generator();
iter1.next(); // 第一次执行到第一个yield时候返回
iter1.next("cc"); // 从第一个yield处开始执行 并将 值传递给a
8. 模板字符串:通过用反引号进行声明,字符串内可以包含换行符,还可以进行变量拼接
let str = `php`
let php = `${str}是最好的语言` // 进行变量的拼接
9. 简化声明对象的操作
var name = "JS"
let man = {say(){}, name} // 声明对象的时候无需再写名称,属性名称默认是变量名
10. 箭头函数
let fn = (params) => {
// 通过 '=>' 声明的函数就是箭头函数
// 箭头函数的 this值是静态的,始终是指向声明时所在作用域this的值
// 箭头函数不能作为构造函数,并且不能使用arguments变量
// 当形参有且只有一个的时候可以省略小括号
}
rest参数:将多余的参数全部打包到一个数组中,和arguments很像。也可以进行拆解参数。
// 打包
function func1(...t1){}
func1("A","B","C")
// 拆包
function func2(a,b,c){}
func1(...["A", "B", "C"])
// 可以方便的将dom元素转化为数组
var divList = document.querySelectorAll("div");
const divs = [...divList]
console.log(divList instanceof Array) // false
console.log(divs instanceof Array) // true
11. Set:ES6新增了一个集合类型Set,和Java的一样。
var set = new Set(); // 声明一个集合
set.add(1); // 添加元素
set.delete(2); // 删除元素
set.clear(); // 清空
12. Map:ES6新增了一个集合类型Map,和Java的一样。
var map = new Map(); // 声明一个Map
map.set("a", 12); // 往map中添加元素
map.set("a"); // 根据key获取元素
map.has("b"); // 是否有key为 'b' 的数据
map.clear(); // 清空
13. 绝对全局对象:无论环境始终指向全局对象,例如在浏览器中始终指向window对象。
console.log(globalThis)
14. 链式判空操作:省去了三运算符的空间
// 如果A为空则直接返回undefined,否则继续调用A.b
A?.b?.c?.d
15. 私有属性:以往通过变量以 "_" 开头的规范约定为私有变量,现在真正实现了私有变量,虽然还是能看见,但是无法进行调用。
class Person{
#name = "xxx";
}
16. Object 新方法:
const p1 = {
name: "xxx",
age: 12
}
const p2 = {name: "xbx"}
// 判断两个值是否相等,特殊点:判断两个NAN的时候会返回true,用'==='判断返回的是false。
Object.is(12,22);
var p = Object.assign(p1, p2); // 合并两个对象,后面的会覆盖前面
Object.setPrototypeOf(p3,p4); // 将p4设置为p3的原型对象,和平常直接赋值是一样的
Object.getPrototypeOf(p3); // 获取某个对象的原型对象
Object.getOwnPropertyDescriptors(p1) // 获取对象的描述信息
// 接收一个map类型或一个二维数组,返回一个对象的格式
Object.fromEntries([["xx","yy"],["ll", "qq"]]);
arr.flat(2) // 将数组扁平化 传最深的层数
arr.flatMap(() => {}) // 先map 然后再flat
// 为现有的对象添加属性, 可以定义属性的值和属性,除了下面的配置还能传get set
Object.defineProperty(son, "score", {
value: "male",
enumerable: true, // 属性是不参与遍历的
writable: true, // 是否可以被直接修改
configurable: true, // 属性是否可以被删除
})
// 如已设置 set 或 get, 就不能设置 writable 和 value 中的任何一个了.
Object.defineProperty(son, "score", {
get() {
return str // 调用的时候触发
},
set(v) {
修改值的时候触发
}
})
17. Promise:异步操作
// 创建一个promise对象,需要传入一个函数
const promise = new Promise(function (resolve, reject){
// 当调用resolve的时候 会触发then的第一个回调函数
// 当调用reject的时候 会触发then的第一个回调函数
resolve("aaa")
});
promise.then(function(value){
console.log("成功回调我");
},function(reason){
console.log("失败回调我");
});
为了避免多次回调出现层层嵌套的情况,then也是可以链式调用的。
var p = promise.then(value =>{
// 返回一个promise对象以供后续的调用
return new Promise(function (resolve, reject){
resolve(value + "bbb")
})
}).then(value =>{
// 这里的 value 是上个then返回的promise对象传递的值
console.log(value)
});
// 快速得到一个成功的对象
var resplve = Promise.resolve();
// 快速得到一个失败的对象
var resplve = Promise.reject();
多个promise对象同时调用
var p1 = new Promise(value =>{}, reason => {})
var p2 = new Promise(value =>{}, reason => {})
// 接收多个promise对象, 如果有一个返回成功,整体的返回值就是成功。
var promise = Promise.allSettled([p1, p2]);
// 接收多个promise对象, 如果有一个返回失败,整体的返回值就是失败。
var promise = Promise.all([p1, p2]);
// 接收多个promise对象, 由第一个完成的promise对象的结果决定
var promise = Promise.race([p1, p2]);
18 async 和 await 关键字
通过async修饰的函数一般返回一个promise对象。
async function func(){
return new Promise(function (resolve, reject){
resolve("YYYY")
})
}
await 必须出现在async修饰的函数中,标识当执行到await的时候需要等待返回,不会继续向下执行,直到有返回。
async function func(){
let a1 = await p1;
}
19. class关键字
class 用来标识一个类,实际上这是一个语法糖,本质上效果和es5 的一样,只是起到一个简化的作用。
class Person{
static age = 10; // static修饰的都是静态方法/属性 只能通过类调用
constructor() {} // 定义构造方法
say(){} // 添加方法 实际上也是会添加到原型对象中。
}
类和类之间继承的关键字是extends,这里和Java一样。
class Man extends Person{
constructor() {
super();
}
// 直接重新父类的方法
say(){
// super().say() 这样是不允许的 不能通过super直接调用同名方法
}
}
20. get 和 set: 这里其实就是监听变量的获取和修改时候的行为。
class Man extends Person{
// 每当获取属性的时候会自动触发,方法名是具体的变量名
get name(){
return "xxx" // 返回不能是自己,因为又会去调用get方法 一直递归
}
// 每当属性被修改的时候就会触发
set name(newName){}
}
21. JS 模块化
<script type="module"></script> // 模块化需要将type设置为module
<script src="../js/index.js" type="module"></script> // 可以将某个js设置为入口js
22. 引入和暴露:
export 规定模块对外暴露的接口。
1. 分别暴露的形式: 每个需要暴露的变量都加上export
export let name = "由内到外";
export function getReal(){}
2. 统一暴露: 将需要暴露的变量或方法统一写到大括号内
export {name, getReal}
3. 默认暴露: 需要加上default关键字,一般以对象的形式暴露
export default {}
import 用于引入其他模块的功能。
1. 引用指定变量的形式
import {name, getReal} from "../js/index.js";
2. 当有名称冲突的时候 通过as关键字起别名
import * as m1 from "../js/index.js";
import {name, getReal as real} from "../js/index.js";
通过import函数进行导入
// import函数返回一个promise对象,module代表暴露出的对象
import("../js/index.js").then(module =>{});
23. Proxy 代理对象
它是一种拦截器,拦截外界对该对象的访问,都必须先通过这层拦截。比Object.defineProperty 更好用。
let person = {name: "WTW", bFriend: "GH"}
// 创建代理对象,需要两个参数,一个是对象,一个是拦截配置
let p = new Proxy(person, {
// 拦截获取方法:target 源对象,p 属性名
get(target, p) {
return Reflect.get(target, p)
},
// 拦截修改/新增方法:target 源对象,p 属性名,value 值
set(target, p, value) {
Reflect.set(target, p, value)
},
// 拦截删除方法:target 源对象,p 属性名
deleteProperty(target, p) {
return delete Reflect.get(target, p)
}
})
JavaScript的反射机制 Reflect,和Java的反射很像,不愧是小JS。
let person = {name: "WTW", bFriend: "GH"}
Reflect.get(person, 'name') // 获取对象的属性
Reflect.set(person, 'name', 'GH') // 设置对象的属性
Reflect.deleteProperty(person, 'name') // 删除对象的属性
※:Reflect.defineProperty 要比 Object.defineProperty 更好用,因为在重复定义属性时后者会直接抛出异常,前者会通过返回值来表示是否创建成功。
const result = Reflect.defineProperty(person, 'name', {
get() {
return 'xxx'
}
})