let:在全局或者块中都只能独立声明一次,作用域是块级,有暂时性死区(也就是不会变量提升)/*不会影响作用域链*/
如果通过 var 遍历循环绑定的点击事件会导致变量覆盖,所以批量声明点击事件的时候用 let 作为循环体的循环变量
const:只能声明一次且有初始值,不能被修改值,如引用类型(数组、对象)的话在不修改内存地址执行的情况下可以修改值(不整个修改的前提可以通过索引或者属性名修改) 一般全大写命名,用来声明数组和对象比较稳妥,防止误操作
// 连续解构赋值 + 重命名,下面两条语句等价,前者中不能单独访问 k(连续解构) 以及 v(重命名) 这两个变量,访问了会报错
const {k:{v:keyWord}} = this ---> const {v:keyWord} = this.k
基本定义: ` ${ 变量 } ` // 可随意换行以及拼接串
/* this 指向很关键,箭头函数的 this 是指向声明时所在作用域下的 this,和作用域牵扯
箭头函数适用于与 this 无关的回调,如定时器,数组的方法回调
箭头函数不适用于与 this 有关的回到,如事件绑定时的回调,对象的方法(不适用不代表不能用) */
window.name = 'sgg'
const school = {
name: 'ggs',
getName2: () => {
console.log(this.name) // 该this就会被指向window,因为声明在全局作用域中
}
}
school.getName2()
7、函数参数默认值
// 默认值:函数的默认值一般都是靠后赋值(潜规则),否则实参传入可能导致参数值错乱,可搭配解构赋值适用
function fn(a, b, c = 10) { function fn({a, b, c = 10}) {
console.log(a, b, c); console.log(a, b, c);
} }
fn(1, 5) fn({a:1,b:2})
// 必须放在最后接收,否则报错,接收整合成一个数组,可以用数组上的 API 操作数据
function fn(a, b, ...args) {
console.log(a, b, ...args)
}
fn(1, 5, 1, 1, 1, 1)
// ... 扩展运算符能将 数组 转换为逗号分隔的 参数序列(也就是将数组分隔开),数组的合并、数组的克隆、伪数组转真数组
const boys = ['易烊千玺','王源','王俊凯']
function fn() {
console.log(arguments);
}
fn(...boys) // 等同于 fn('易烊千玺','王源','王俊凯'),如果不用扩展运算符收到的将是一整个 数组
// 在对象中用扩展运算符:如果是只有单独一层的话是深拷贝,修改两个对象的值互不影响(地址不一样),如果是有多层结构,只有第一层是深拷贝,里面的数据仍然是浅拷贝(原对象修改,另一对象中的值跟着改变),如果是直接赋值的话是浅拷贝,只是赋值了个地址值过去而已
// Symbol 类型数据并不能做 运算 以及 比较
// 1、创建 Symbol 类型,类型为 symbol,symbol 好比身份证,参数好比姓名,两个是不相等的
let s = Symbol('张三')
// 2、创建 Symbol 类型第二种方式,通过这种方式创建的 symbol 类型,参数相等便相等
let s1 = Symbol.for('张三')
let s2 = Symbol.for('张三')
// 第一种创建方式:动态传值的方法
let game = { name: '俄罗斯方块' }
let methods = { up: Symbol('up') }
game[methods.up] = function () {
console.log('添加成功');
}
console.log(game);
// 第二种添加 Symbol 方法,因为是动态值要用 [] 包裹
let youxi = {
name: '狼人杀',
[Symbol('say')]: function () {
console.log('成功发言');
}
}
console.log(youxi);
const arr = [1,2,3]
arr[Symbol.isConcatSpreadable] = false // 这样这个数组就不能作为 被 拼接 的数组
Iterator 接口主要供 for…of 消费,原生具备 iterator 接口的数据(可用 for of 遍历)【Array、Arguments、Set、Map、String、TypedArray、NodeList】
// 迭代器工作原理:先给指定数据创建一个迭代器对象,迭代器对象有next方法取数据,需要自定义遍历数据的时候,要想到迭代器
const xiyou = [1,2,3]
let iterator = xiyou[Symbol.iterator]()
console.log(iterator.next()) // 返 { value:1, done:false } 表示数据没取完,取完value为undefined done值为true
// 自定义遍历数据
const banji = {
name: '1',
stus: [1, 2, 3, 4, 5],
[Symbol.iterator]() {
// 索引变量控制是否遍历
let index = 0
// let _this = this // 如果不是箭头函数要将this指向指回,否则会指向return对象
return {
next: () => {
if (index < this.stus.length) {
const result = { value: this.stus[index], done: false }
// 下标自增
index++
return result
} else { return { value: undefined, done: true } }
}
}
}
}
for (const v of banji) { console.log(v) }
// 生成器就是一个特殊的函数,在关键字和函数名中间加一个 * ,调用后返回一个迭代器对象,有next方法
// 生成器是可以传入实参的
function* gen(args) { // * 写在左右中间都可以
console.log(args)
let y = yield '一只小白兔'
yield '两只小白兔'
yield '三只小白兔'
}
let iterator = gen('111')
console.log(iterator.next()) // 输出:111 以及遍历的第一个yield {value:'一只小白兔',done:false}
// next方法是可以传入实参的,从第二个next方法传入的实参将会成为第一个 yield 的返回值,以此类推
console.log(iterator.next('123'))
console.log(iterator.next())
console.log(iterator.next())
案例:这样写出来的生成器就 不需要套用一堆定时器,就不会有 回调地域 的问题
function one() {
setTimeout(() => {
console.log('111')
iterator.next()
}, 1000)
}
function two() {
setTimeout(() => {
console.log('222')
iterator.next()
}, 2000)
}
function* gen() {
yield one()
yield two()
}
// 调用生成器函数,生成一个迭代器对象,调用next方法,调用后会调用第一个定时器,第一个定时器执行完调用第二个定时器
let iterator = gen()
iterator.next()
// 在 node 中读取文件时操作,用 promise 的方式就不需要全部套在一起读取
const fs = require('fs')
const p = new Promise((resolve, reject) => {
fs.readFile('./t1.txt', (err, data) => {
resolve(data)
})
})
p.then(value => {
return new Promise((resolve, reject) => {
fs.readFile('./t2.txt', (err, data) => {
resolve([value, data])
})
})
}).then(value => {
return new Promise((resolve, reject) => {
fs.readFile('./t3.txt', (err, data) => {
// 将拿到的数据压入前面已经获取的数据
value.push(data)
resolve(value)
})
})
}).then(value => {
console.log(value.toString());
})
在
关键问题2
中,可以得知,当promise状态改变时,他的链式调用都会生效,那如果我们有这个一个实际需求:我们有5个then(),但其中有条件判断,如当我符合或者不符合第三个then条件时,要直接中断链式调用,不再走下面的then,该如何?(1) 当使用 promise 的 then 链式调用时, 在中间中断, 不再调用后面的回调函数
(2) 办法: 在回调函数中返回一个
pending
状态的promise 对象
<script> let p = new Promise((resolve, reject) => {setTimeout(() => { resolve('OK');}, 1000);}); p.then(value => {return new Promise(() => {});}) // 返回一个pending状态的promise对象,有且只有这一个方式 .then(value => { console.log(222);}) .then(value => { console.log(333);}) .catch(reason => {console.warn(reason);}); </script>
(1) 简单表达: 由 then() 指定的回调函数执行的结果决定
(2) 详细表达:
① 如果抛出异常, 新 promise 变为 rejected(失败), value(值) 为抛出的异常
② 如果返回的是非 promise 的任意值, 新 promise 变为 resolved(成功), value(值) 为返回的值
③ 如果返回的是另一个新 promise, 此 promise 的结果就会成为新 promise 的结果,能接着被 then 作为值接收(状态为成功,值为返回值)
// Set 集合的创建 增、删、查、清除
const s = new Set([1, 2, 3, 1, 1, 2])
console.log(s.size);
s.add(2)
s.delete(2)
console.log(s.has(2))
s.clear()
// Map 集合的创建 增、删、获取、清除
// 相当于对象的升级版,对象只能用 串 来做键,map 能用对象来做键,值是 二维数组
const m = new Map()
console.log(m.size);
m.set('a',1)
const key = { school: 'gdmec' }
m.set(key,[1,2,3])
m.delete('a')
console.log(m.get('a'))
m.clear()
// map 也实现了 iterator 接口:可以被 for...of 循环遍历
for( const iterator of m ){
console.log( iterator ) // 遍历出来的每项都是 键值对
}
// 1、声明父类
class Father {
constructor(name) { this.name = name }
}
// 继承父类(通过 extends 后在构造器中通过 super 来继承想要继承的属性)
class Son extends Father {
static n = '123'
// 2、constructor 定义构造函数初始化(不是必须要有)
constructor(name) {
super(name)
}
cal() { console.log(this.name + '触发') }
// 通过 get set 可以对数据的一个读取和设置
get price() {
console.log('读取');
}
set price(newvalue) {
console.log('新值', newvalue);
}
}
const p = new Son('牛马先生')
console.log( Son.n ) // 只有这样才能访问到类上的静态属性
p.cal()
Number.EPSILON 是 js 表示的最小精度
Number.isFinite( 123 ) // 检测一个数是否为有限数,返布尔值
Number.isNaN() // 检测一个数值是否为 NaN
Number.parseInt() Number.parseFloat() // 转整数和浮点数,遇到英文自动截断
Number.isInteger() // 判断一个数是否为整数
Math.trunc() // 将数字的小数部分抹掉
Math.sign() // 判断一个数是正数(1) 负数(-1) 还是零(0)
Object.is( n1, n2 ) // 和全等 === 的区别在于:is 对于 NaN 的判断是返true,两个值得完完全全相等
Object.assign( o1, o2 ) // 对象合并,同属性后者覆盖前者
Object.setPrototypeOf(obj,props) Object.getPrototypeOf(xxx) // 给指定对象原型设置属性和获取指定对象的原型属性
index.html
<script type="module">
// 通用方式:表示将另一模块中所有导出的代码都封存在变量m1中(三种暴露都可用)
import * as m1 from './module.js'
// 通用方式:分布引入,不过默认暴露的写法有所不同
import { xxx } from './module.js'
// 因为default是关键字,所以不能用来作为引入时的变量名
import { default as xxx } from './module.js'
// 只针对默认暴露可用
import xxx from './module.js'
script>
<script>
module.js
// 1、分布暴露(一个一个暴露)
export let school = 'gdmec'
// 2、默认暴露(定义好所有变量和方法。可以定义在default的对象里,键值对写法)
let school = 'gdmec'
export default { school }
export default {
school: 'gdmec'
}
// 统一暴露(定义好所有变量和方法,统一暴露出去)
let school = 'gdmec'
export { school }
script>
// 注意,super关键字表示原型对象时,只能用在对象的方法之中,用在其他地方都会报错。
const proto = {
foo: 'hello',
};
const obj = {
foo: 'world',
find() {
return super.foo; // 只能用在对象的方法之中
},
};
Object.setPrototypeOf(obj, proto);
console.log(obj.find()); // 输出:hello,super指向当前对象的原型对象
Array.prototype.includes:方法用来检测数组中是否包含某个元素,返回布尔值,arr.includes( xxx )
用来实现幂运算,等效于 Math.pwo( n1, n2)
async 修饰在函数前,会得到返回一个 promise 对象,promise 对象的结果由 async 函数执行的返回值决定
await 修饰在带有async的函数中,修饰的右侧表达式一般为promise对象,接收完返回promise的返回值,若失败,要用try...catch捕获处理
经过第一个await修饰之后后面的代码会变成异步
Object.keys( obj1 ) // 获取对象所有的键【es5】
Object.values( obj1 ) // 获取对象所有的值【es6】
Object.entries( obj1 ) // 获取对象的键值整合成一个个数组项,可搭配map,方便创建map
Object.fromEntries([
['name','gdmec'] // 上着是将对象转换为键值对的map数组,fromEntries 是将二维数组,转换为键值对的对象
])
const m = new Map(Object.entries( obj1 )) // 因为map就是由键值对组成的数组
Object.getOwnPropertyDescriptors( obj1 ) // 获取对象的描述对象,如是否可枚举,是否可修改...下边可见
Object.create(null,{
name:{
value:'gdmec',
writable:true
}
})
题外话:
Object.entries( obj ) 和 arr.entries( ) 的区别Object.entries( obj1 ) // 获取对象的键值整合成一个个数组项,二维数组
// 方法返回一个新的Array Iterator对象,该对象包含数组中每个索引的键/值对(可以直接通过for...of来遍历这个迭代对象)
// 或者通过返回的迭代对象上的next方法取对应的键值,一次取一个,直到取完
const a = arr.entries()
a.next()
方法会将一个深层次的数组进行递归遍历,而层次取决于参数,默认是 1 ,Infinity 表示展开任意深度的嵌套数组,将所有嵌套的数组值合并成一个数组返回(默认还会移除空项)
[1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]].flat(Infinity); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// 首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。与 map 连着深度值为 1 的 flat 几乎相同,但 flatMap 通常在合并成一种方法的效率稍微高一些。
flatMap、近似 map 方法
const arr = [1,2,3]
const result = arr.flatMap( item => [item * 10] )
console.log(result) // [ 10,20,30 ] 将本来是多维(多少层都无所谓)数组的数组给运算完展开成一维数组
let s = Symbol('gdmec')
console.log( s.description ) // 输出:gdmec
// 类属性默认情况下是公有的,可以使用 哈希前缀 # 的方式来定义私有类属性,这一隐秘封装的类特性由 JavaScript 自身强制执行。
class Person {
name
#age
constructor(name, age) {
this.name = name
this.#age = age
}
}
const p = new Person('gdmec', 18)
console.log(p.name, p.#age) // 会报错,并不能访问私有属性,但是可以在类内部通过方法访问修改,在外部调用方法就能访问到
前者永远返回true,后者只有全部为true才返回true
// 该方法可以传入多个promise对象,永远返回true,里面存放着一个数组:promise对象的状态和返回值都在其中,与all不同的是all必须全部都为成功才返回,有一个错就返回错误的promise对象
const result = Promise.allSettled([ p1,p2 ])
// 1、通过多个await方式发送多个请求,效率偏低:await之后的代码都会变为异步代码,后一个请求需在前一个请求成功返回后才发送
// const result1 = await reqCategory(pCategoryId);
// const result2 = await reqCategory(categoryId);
// 2、一次性发送多个请求,而不是等前面发完请求返回后才发后面的请求,只有都成功了true,才正常处理
const results = await Promise.all([reqCategory(pCategoryId), reqCategory(categoryId)]);
const result = 字符串.matchAll( 正则 ) // 返回一个迭代器对象:可以用for...of循环
function main(config){
const dbHost = config?.db?.host
console.log(dbHost)
}
main() // 最终输出 undefined,如果没有可选链操作符会报错
import('./module.js').then((module) => {
console.log(module)
})
BigInt是一种内置对象,它提供了一种方法来表示大于 2^53 - 1
的整数。这原本是 Javascript 中可以用 [Number
] 表示的最大数字。BigInt 可以表示任意大的整数。
Number.MAX_SAFE_INTEGER 该常量表示在 js 中最大的安全数(2的53次方-1),Math.pow(2, 53) -1,2 ** 53 - 1
Number.MAX_VALUE 属性表示在 js 里所能表示的最大数值。
// 两种定义方式
let n = 123n;
let b = BigInt(123);
console.log( typeof n ) // 输出 bigint
console.log( BigInt( Number.MAX_SAFE_INTEGER ) + BigInt(1) ) // 输出 9007199254740992n
/*
jsonp解决ajax跨域的原理
1). jsonp只能解决GET类型的ajax请求跨域问题
2). jsonp请求不是ajax请求, 而是一般的get请求
3). 基本原理
浏览器端:
动态生成