严格模式:use strict
<script>
"use strict"
</script>
推荐使用const,定时器和循环要用let
数组常见遍历
const newArr = arr.map(item => {
return item * item
})
console.log(newArr) // [81, 25, 36, 64, 5929, 4, 9]
参数:
some
的当前数组。返回值:
const arr = [1,2,3,4,5]
const res = arr.some(item => {
item > 2
})
console.log(res) // true
参数:
every
的当前数组返回值:
const arr = [1,2,3,4,5]
const result = arr.every(item => {
return item < 6
})
console.log(result) // true
参数:
filter
的当前数组返回值:
const arr = [1,2,3,4]
const res = arr.filter(item => {
return item > 2
})
console.log(res) // [3,4]
参数:
callbackFn
reduce
的当前数组initialValue 初始值
返回值:
const arr = [1,2,3,45]
const res = arr.reduce((pre,item) => {
return pre += item
},0)
console.log(res) // 51
const str = 'hello'
const [a,b,c,d,e,f] = str
console.log(a) // h
console.log(f) // undefined
// 结合扩展运算符使用
const str1 = 'helloworld'
const [a,b,..c] = str1
console.log(a) // h
console.log(c) // ['l', 'l', 'o', 'w', 'o', 'r', 'l', 'd']
const [...s] = 'helloworld'
console.log(s) // ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd']
s.reverse().join('')
console.log(s) // ['d', 'l', 'r', 'o', 'w', 'o', 'l', 'l', 'e', 'h']
const arr1 = [1,2,3]
const arr2 = [4,5]
const arr = [...arr1,...arr2]
console.log(arr) // [1,2,3,4,5]
可以实现拷贝数组
const arr = [1,2,3,4]
const newArr = [...arr]
console.log(newArr) // [1,2,3,4]
newArr[0] = 100
console.log(newArr) // [100,2,3,4]
console.log(arr) // [1,2,3,4]
const arr1 = [1,2,[3,4]]
const arr2 = [...arr1]
arr2[2][0] = 99
console.log(arr2) // [1, 2, Array(2)]
console.log(arr1) // [1, 2, Array(2)]
通过键来映射的
解构出来对象中的属性和方法
const obj = {
uname: '张三',
age: 24,
say() {
console.log('会说话')
}
}
// 解构对象的属性和方法
const { uname } = obj
console.log(uname)
const { age: newAge } = obj
console.log(newAge)
const { say } = obj
say()
// 解构整个对象,利用扩展运算符
const o = { ...obj }
console.log(o)
箭头函数传参
let fn = x => {
return x * x
}
console.log(fn(10)) // 100
返回体
let fn = x => x * x
console.log(fn(10)) // 100
function fn(x = 0,y = 0,...args) {
console.log(x) // 1
console.log(args) // [3,6,9]
}
fn(1,2,3,6,9)
const { uname,age = 24 } = { uname: 'zs' }
console.log(uname) // zs
console.log(age) // 24
为了解决对象属性名冲突而诞生的新的原始数据类型
代表独一无二的值
Symbol值通过 Symbol() 函数生成
对象的属性名现在可以有两种类型
Symbol() 里面可以接收一个字符串参数。也就是属性名,用于区分
应用场景:
// 如果添加一个,可以直接写在对象中
const obj = {
// uname : '张三',
[Symbol] : '张三'
}
console.log(obj[Symbol]) // 张三
// 如果多个,就只能在外面声明好,再追加
const age = Symbol('age')
const gender = Symbol('gender')
obj[age] = 24
obj[gender] = '男'
console.log(obj)
console.log(obj[age]) // 24
很少用,了解
不允许出现重复的值,也是一个构造函数,需要new进行使用
接收数组类型的参数,如果不是,会尝试进行转换为数组,转换不成功,则报错
可以进行解构出来
格式:set(size长度) {元素...}
const s = new Set([1, 2, 3])
console.log(s) // set(3) {1,2,3}
console.log(s[0]) // undefined
console.log(...s) // 1,2,3
add() 进行添加
s.add(4,5)
console.log(s) // Set(4) {1, 2, 3, 4}
// 一次只能添加一个,如果想添加多个,可以放到数组或者对象里
s.add([6,7,8])
console.log(s) // Set(5) {1, 2, 3, 4, Array(3)}
s.add({uname: 'zs',age: 24})
console.log(s) // Set(6) {1, 2, 3, 4, Array(3), …}
delete() 进行删除
const s = new Set([1, 2, 3])
// delete删除某个值,返回一个布尔值,删除成功/失败
console.log(s.delete(1)) // true
console.log(s) // Set(2) {2, 3}
has() 是否包含某个值
const s = new Set([1, 2, 3])
// has判断是否有某个值
console.log(s.has(9)) // false
console.log(s.has(4)) // true
clear()
const s = new Set([1, 2, 3])
s.clear()
console.log(s) // Set(0) {size: 0}
keys() 返回键名的一个遍历器
values() 返回值的一个遍历器
Set的键和值是同一个值,所以 keys() 和 values() 返回的结果是一样的
const s = new Set([1,5,9,4])
console.log(s) // Set(4) {1, 5, 9, 4}
// 1. keys() 返回键名的遍历器
console.log(s.keys()) // SetIterator {1, 5, 9, 4}
// 2. values() 返回值的遍历器
console.log(s.values()) // SetIterator {1, 5, 9, 4}
const s = new Set([1,5,9,4])
// 3. entries() 返回键值对映射的遍历器
console.log(s.entries()) // SetIterator {1 => 1, 5 => 5, 9 => 9, 4 => 4}
iterator
接口(只要有iterator接口,就可以使用for…of)
const s = new Set([1,5,9,4])
// 4. for...of 遍历
for (const iterator of s) {
console.log(iterator) // 1 5 9 4
}
const s = new Set([1,5,9,4])
// 5. 用Set自带的forEach循环遍历
s.forEach(item => {
console.log(item) // 1 5 9 4
})
数组去重:
const arr = [1, 2, 6, 6, 6, 9]
let res = new Set(arr)
console.log(res) // Set(4) {1, 2, 6, 9}
// 此时res还是Set数据类型,要把Set转为数组
// 1. Array.from(res)
console.log(Array.from(res)) // [1, 2, 6, 9]
// 2. 扩展运算符
console.log([...res]) // [1, 2, 6, 9]
很少用,了解
也是对对象进行扩展的,对对象进行了升级(原来对象的键只能用字符串,这个的键可以是任何类型)
set() 进行添加
格式: Map(size长度) {元素...}
const m = new Map()
console.log(m) // Map(0) {size: 0}
// 1. set() 添加
// 简单数据类型时,会覆盖,因为存放在栈里
m.set(1,'yi')
m.set(1,'one')
console.log(m) // Map(1) {1 => 'one'}
// 复杂数据类型时,因为内存地址不同,所以会添加两个
m.set([1],'yi')
const arr = [1]
m.set(arr,'one')
console.log(m) // Map(3) {1 => 'one', Array(1) => 'yi', Array(1) => 'one'}
get() 进行获取
const m = new Map()
m.set(1,'one')
// 复杂数据类型时,因为内存地址不同,所以会添加两个
m.set([1],'yi')
const arr = [1]
m.set(arr,'one')
console.log(m) // Map(3) {1 => 'one', Array(1) => 'yi', Array(1) => 'one'}
// 2. get() 获取
console.log(m.get(1)) // one
// 引用类型不能直接用定义的键名获取
console.log(m.get([1])) // undefined
// 要提前给引用类型的数据赋值变量,然后把变量存进去,取的时候也用变量名取
console.log(m.get(arr)) // one
delete() 删除
const m = new Map()
m.set(1,'one')
// 复杂数据类型时,因为内存地址不同,所以会添加两个
m.set([1],'yi')
const arr = [1]
m.set(arr,'one')
// 3. delete() 删除
m.delete(1)
console.log(m) // Map(2) {Array(1) => 'yi', Array(1) => 'one'}
m.delete(arr)
console.log(m) // Map(1) {Array(1) => 'yi'}
clear() 清空
const m = new Map()
console.log(m) // Map(0) {size: 0}
m.set(1,'one')
// 4. clear() 清空
m.clear()
console.log(m) // Map(0) {size: 0}
遍历和Set一样,也可以通过 keys()
values()
entries()
for...of
forEach
数据双向绑定原理
vue2 Object.defindeProperty()
vue3 proxy()
区别:
语法:Object.defindeProperty(要监听的对象名,新增属性名/对象原有的属性名,{配置项})
配置项里面有多个属性:
// vue2 响应式原理是 Object.defindeProperty()
const obj = {
uname: '张三',
age: 23
}
// defineProperty 有3个参数 要监听的对象名 新增属性名/对象有的属性名 {配置项}
Object.defineProperty(obj,'uname',{
// value 劫持属性名后要修改的值
value: '王二小',
// writable 布尔值,是否允许被重写
writable: false,
})
// 因为配置项里不允许再被修改,所以这里修改了也没用
obj.uname = '王五'
console.log(obj) // {uname: '王二小', age: 23}
get() get方法,只要获取对象中的属性/方法,就会调用这个方法。默认undefined
set() set方法,只要对象中的属性/方法修改,就会调用这个方法。默认undefined
注意:get() set() 方法 与 value writable冲突,二选一,不能同时出现
const obj = {
uname: '张三',
age: 23
}
// 定义一个过渡值,就可以解决栈内存溢出的问题
let newName = ''
Object.defineProperty(obj, 'uname', {
// get get方法,只要获取对象中的属性/方法,就会调用这个方法。默认undefined
get() {
// return 'get方法被调用了'
// return this // this 指向 obj
// return this.uname
return newName
},
// set set方法,只要对象中的属性/方法修改,就会调用这个方法。默认undefined
// set() 会有一个默认参数,就是修改后的新值
set(newVal) {
// set() 劫持到修改后的新值newVal以后,要赋值给原先的属性名吧
// this.uname = newVal
// 把newVal赋值给过渡值
newName = newVal
}
})
// 修改uanme 会被set方法劫持到,会触发set()
obj.uname = '王五'
// 查看uname的值,会触发get方法
console.log(obj.uname)
const obj = {
uname: '王五',
age: 23
}
// Proxy() 有两个参数,第一个是要监视的对象名,第二个是配置项(get&set)
const p = new Proxy(obj,{
// 获取属性的时候触发,有3个参数
// target:目标对象 propKey:目标对象的属性 recevier:可选 实例化对象
get: function(target, propKey, recevier) {
// console.log(target)
// console.log(propKey)
return target[propKey]
},
// 设置属性的时候触发,有4个属性,比get多了一个value
// value:设置的新值
set: function(target, propKey, value, recevier) {
target[propKey] = value
}
})
// 原对象调用是不会触发get和set的,因为原对象已经交给了Proxy()进行代理
// console.log(obj.uname) // 王五
// 所以应该使用Proxy()进行调用
p.age = 24
console.log(p.age) // 24
p.uname = '罗翔'
console.log(p.uname) // 罗翔
const p = new Promise((resolve,reject) => {
setTimeout(() => {
// resolve('请求成功')
reject('请求失败')
},2000)
}).then(res => {
console.log(res) // 请求成功
}).catch(err => {
console.log(err) // 请求失败
})
console.log(p)
promise.all()
<body>
<script>
// 模拟几个异步请求
const p1 = new Promise((resolve,reject) => {
setTimeout(() => {
// resolve('调用成功')
reject('调用失败')
},1000)
})
// console.log(p1)
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('调用成功')
}, 2000)
})
// console.log(p2)
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('调用成功')
// reject('调用失败')
}, 3000)
})
// console.log(p3)
// Promise.all() 全部成功回调后,合成成功的回调数组。有一个失败的,就返回失败的
const p = Promise.all([p1,p2,p3])
console.log(p)
// Promise.race() 不管成功失败,返回最先返回的回调状态
const pp = Promise.race([p1, p2, p3])
console.log(pp)
</script>
</body>
面试题:Promise.all()与Promise.race() 的 异同
同:
异:
<body>
<button>get请求</button>
<button class="post">post请求</button>
<script>
// get
document.querySelector('button').addEventListener('click',async function() {
const { data: res } = await axios({
method:'get',
url: 'https://autumnfish.cn/api/joke',
params: {
num: 1
}
})
console.log(res)
})
// post
document.querySelector('.post').addEventListener('click',async function() {
try {
const { data: res } = await axios({
method: 'post',
url: 'https://autumnfish.cn/api/joke',
data: {
num: 1
}
})
console.log(res)
} catch (error) {
console.log(error)
}
})
</script>
</body>
class Father {
// 私有属性
constructor(uname,age) {
this.uname = uname
this.age = age
}
// 共有属性/方法
gender = '男'
say() {
console.log('总是训斥儿子')
}
}
const f = new Father('王五',55)
console.log(f) // Father {gender: '男', uname: '王五', age: 55}
f.say() // 总是训斥儿子
console.log(f.gender) // 男
// 创建一个子类,继承父类,可以使用父类的方法和属性
class Son extends Father {
constructor(uname,age,gender) {
// 子类要有自己的私有属性,必须调用super()
super(uname,age)
this.gender = gender
}
// 子类可以添加自己特有的方法
study() {
console.log('儿子努力学习')
}
// 子类也可以重写父类的方法
say() {
console.log('儿子希望父亲能和蔼一点')
}
}
const s = new Son('张三',24,'男')
console.log(s) // Son {gender: '男', uname: '张三', age: 24}
s.say() // 儿子希望父亲能和蔼一点
s.study() // 儿子努力学习
class Person {
constructor(uname,age) {
this.uname = uname
this.age = age
}
// static 修饰的方法和属性,都不会被继承,只能通过此类调用
static gender = '男'
static say() {
console.log('会说话')
}
}
console.log(Person.gender) // 男
Person.say() // 会说话
class zs extends Person {
constructor(uname,age) {
super(uname, age)
}
}
const z = new zs('张三',24)
console.log(z) // // zs {uname: '张三', age: 24}
Array,String,Map,Set,NodeList,TypeArray,函数的arguments 都内置了iterator
只要有iterator接口,就可以使用 for…of 方法进行循环
其他类型想要使用iterator,则需要进行配置,才能使用for…of
// Array,String,Map,Set,NodeList,TypeArray,函数的arguments 都内置了iterator
// 可以直接使用 for...of 进行循环
// const arr = [1,5,9,10]
// for (const iterator of arr) {
// console.log(iterator)
// }
// const str = 'helloworld'
// for (const iterator of str) {
// console.log(iterator)
// }
// console.log(arr)
// 其他类型想要使用iterator,则需要进行配置,才能使用for...of
const obj = {
uname: '张三',
age: 24,
// 进行iterator配置
[Symbol.iterator]() {
const that = this
// console.log(that) // 此时that就是obj
let index = 0
const data = Object.keys(that) // 返回一个键组成的新数组
// console.log(data)
return {
next() {
if (index < data.length) {
return {
value: data[index++],
done: false
}
}
return { value: undefined, done: true }
}
}
}
}
console.log(obj)
for (const k of obj) {
// 键
console.log(k)
// 值
console.log(obj[k])
}
模块,就是一个独立的js文件
模块导出 export 模块导入import
script标签的type属性,可以定义模块。定义模块后,浏览器会将其识别为一个模块
<script type="module">...</script>
分别导出
.js文件(模块)
export let hero = '维克托'
export function work() {
console.log('发明家');
}
.html文件
统一导出又叫批量导出
.js文件(模块)
const num = 10
function fn() {
console.log('77777')
}
export { num, fn }
.html文件
默认导出
import 导入名 from '...'
导入.js文件(模块)
// 一个模块只能有一个默认导出
export default {
uname: '张三',
age: 23,
say() {
console.log('会普法')
},
}
.html文件
模块引入
import * as 导入名 from '....'
import { 属性名[, 方法名...]} from '...'
import { default as 导入名 } from '...'
import 导入名 from '...'
const uname = '张三'
const age = 23
const obj = {
uname: uname,
// 1. 键名和值名都相同的情况下,可以简写成一个
age,
// 2. 函数简写
// 老版
sing: function() {
console.log('唱')
},
// 简写
dance() {
console.log('跳')
}
}
console.log(obj) // {uname: '张三', age: 23, sing: ƒ, dance: ƒ}
obj.sing() // 唱
obj.dance() // 跳
Uncaught RangeError: Maximum call stack size exceeded
栗子
<body>
<script>
const obj = {
uname: '张三',
age: 23
}
Object.defineProperty(obj, 'uname', {
// get get方法,只要获取对象中的属性/方法,就会调用这个方法。默认undefined
19行 get() {
// return 'get方法被调用了'
// return this // this 指向 obj
22行 return this.uname
},
// set set方法,只要对象中的属性/方法修改,就会调用这个方法。默认undefined
// set() 会有一个默认参数,就是修改后的新值
set(newVal) {
// set() 劫持到修改后的新值newVal以后,要赋值给原先的属性名吧
28行 this.uname = newVal
}
})
// 修改uanme 会被get方法劫持到
obj.uname = '王五'
33行 console.log(obj.uname)
// 为什么22行+28行的this写法会造成栈内存溢出?
// 因为:代码执行到33行时,要查看obj的uname属性,所以会调用19行的get方法
// 而19行的get方法,返回值又是 obj.uname 又是要查看obj的uname属性
// 所以会接着调用19行的get方法,反复调用,然后就陷入了死循环
</script>
</body>