Coding
1. Array
去重
// filter去重
function unique(arr){
return arr.filter((item,index,array) => {
//return array.lastIndexOf(item) === index // [3, 1, 2, 4]
return array.indexOf(item) === index
})
}
let arr = [1,2,3,1,2,4]
unique(arr) // [1, 2, 3, 4]
// reduce去重
function Uniq(arr = []) {
return arr.reduce((prev, next) => prev.includes(next) ? prev : [...prev, next], [])
}
let arr = [1,2,3,1,2,4]
Uniq(arr) // [1, 2, 3, 4]
set 去重
let unique = arr => Array.from(new Set(arr))
let arr = [1,2,3,1,2,4]
unique(arr) // [1, 2, 3, 4]
let unique = arr => [...new Set(arr)]
let arr = [1,2,3,1,2,4]
unique(arr) // [1, 2, 3, 4]
找到数组中重复的元素
function repeat(arr) {
let res = arr.filter((item,index,array) =>{
// return array.indexOf(item) !== index // [1,2]
return array.lastIndexOf(item) !== index
})
return res
}
let arr = [1,2,3,1,2,4]
repeat(arr) //[1,2]
数组扁平
function flatten(arr) {
return [].concat(...arr.map(item =>{
return Array.isArray(item) ? flatten(item) : item
})
)
}
let arr = [[1, 2], 3, [[[4], 5]]]
flatten(arr) // [1, 2, 3, 4, 5]
// Array.prototype.flat(depth = 1)
let arr = [[1, 2], 3, [4], 5]
let res =arr.flat() // [1, 2, 3, 4, 5]
// reduce
function Flat(arr = []){
return arr.reduce((prev, next) => prev.concat(Array.isArray(next) ? Flat(next) : next),[] )
}
const arr = [0, 1, [2, 3], [4, 5, [6, 7]], [8, [9, 10, [11, 12]]]]
Flat(arr) // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
类数组转换为数组
let arrayLike = {
length: 3,
0: 'a',
1: 'b',
2: 'c',
3: 'd',
4: 'e',
}
let arr1 = Array.from(arrayLike) // ['a', 'b', 'c']
let arr2 = Array.prototype.slice.call(arrayLike) // ['a', 'b', 'c']
2. 浅拷贝
clone
- 如果属性是基本类型,拷贝的就是基本类型的值
- 如果属性是引用类型,拷贝的就是内存地址,所以如果其中一个对象改变了这个地址,就会影响到另一个对象
function clone(obj) {
let target
for(key in obj){
target[key] = obj[key]
}
return target
}
Object.assign()
Object.assign方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象
let obj1 = {person:{name:"Tom",age:20},job:"teacher"}
let obj2 = Object.assign({},obj1)
函数库lodash的_.clone方法
let _ = require("lodash")
let obj1 = {person:{name:"Tom",age:20},job:"teacher"}
let obj2 = _.clone(obj1)
展开运算符 ...
let obj1 = {person:{name:"Tom",age:20},job:"teacher"}
let obj2 = {... obj1}
Array.prototype.concat()
let arr1 = [1,2,3]
let arr2 = [4,5,6]
let arr3 = arr1.concat(arr2)
Array.prototype.slice()
let arr1 = [1,2,3]
let arr2 = arr1.slice()
3. 深拷贝
deepClone
- 深拷贝是将一个对象从内存中完整的拷贝一份出来
- 从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象
function deepClone(obj){
let target
if(typeof obj !== Object){
throw new Error("obj must be an Object!")
}
if(typeof obj === Object){
for(key in obj){
target[key] = deepClone(obj[key])
}
return target
}else{
return obj
}
}
JSON.parse(JSON.stringify())
- 这种方法虽然可以实现数组或对象深拷贝,但不能处理正则和函数
- 如果使用这种方法正则变为空对象,函数变为null
let data1 = [1,{name: "John", age: 20},/\d$/,function(){}]
let data2 = JSON.parse(JSON.stringify(data1))
console.log(data2)
// 0: 1
// 1: {name: 'John', age: 20}
// 2: {} // 正则变为空对象
// 3: null // 函数变为null
// length: 4
函数库lodash的_.cloneDeep方法
let _ = require("lodash")
let obj1 = {person:{name:"Tom",age:20},job:"teacher"}
let obj2 = _.cloneDeep(obj1)
jQuery.extend()方法
var $ = require('jquery')
var obj1 = {person:{name:"Tom",age:20},job:"teacher"}
var obj2 = $.extend(true, {}, obj1)
4. 防抖
防抖
防抖(debounce
):在事件被触发n
秒后再执行回调,如果再这n秒内事件又被触发,则重新计算,最终只执行一次
function debounce(fn,delay){
let timer
return function(){
if(timer) && clearTimeout(timer)
timer = setTimeout(()=>{
fn.apply(this,arguments)
},delay)
}
}
let onInput = debounce(e=>{
console.log(e.target.value)
},1000)
document.getElementByTagName('input').addEventListener('input',onInput)
5. 节流
节流
节流(throttle
):在规定的时间间隔内不会触发,只有大于时间间隔才会触发,把频繁触发变为少量触发
function throttle(fn, delay) {
let timer
let lastTime = Date.now()
return function () {
let currentTime = Date.now()
if (timer) clearTimeout(timer)
if (currentTime - lastTime > delay) {
fn.apply(this, arguments)
lastTime = currentTime
} else {
timer = setTimeout(() => {
fn.apply(this, arguments)
}, delay)
}
}
}
let mouseMove = throttle(e => {
console.log(e.pageX, e.pageY)
}, 1000)
document.querySelector("#div").addEventListener("mousemove", mouseMove)
6. 继承
原型链继承
- 将子类的原型对象指向父类的实例
- 继承了父类的模板,又继承了父类的原型对象
- 如果要给子类的原型上新增属性和方法,就必须放在
Student.prototype = new Person()
这样的语句后面 - 创建子类时,无法向父类构造函数传参
function Person(name,age) {
this.name = name
this.age = age
}
Person.prototype.run = function() {
console.log(`${this.name}-${this.age}`)
}
function Student(sex) {
this.sex = sex
}
Student.prototype = new Person('张三',"20")
Student.prototype.constructor = Student
Student.prototype.run = function() {
console.log(`学生信息: ${this.name}-${this.age}-${this.sex}`)
}
var s1 = new Student('男')
s1.run() // 学生信息: 张三-20-男
构造函数继承
- 在子类构造函数内部使用
call
或apply
来调用父类构造函数- 解决了原型链继承中子类实例共享父类引用对象的问题,实现多继承
- 创建子类实例时,可以向父类传递参数
- 构造继承只能继承父类的实例属性和方法,不能继承父类原型的属性和方法
- 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
function Person (name,age) {
this.name = name
this.age = age
}
Person.prototype.run = function () {
console.log(`${this.name}-${this.age}`)
}
function Student (sex) {
this.sex = sex
Person.call(this,"张三",20)
}
Student.prototype.run = function () {
console.log(`学生信息: ${this.name}-${this.age}-${this.sex}`)
}
var s1 = new Student('男')
s1.run() // 学生信息: 张三-20-男
组合继承
- 原型链继承+构造继承
- 可以继承父类实例属性和方法,也能够继承父类原型属性和方法
- 弥补了原型链继承中引用属性共享的问题
- 可传参,可复用
- 使用组合继承时,父类构造函数会被调用两次
- 并且生成了两个实例,子类实例中的属性和方法会覆盖子类原型(父类实例)上的属性和方法,所以增加了不必要的内存
function Person (name, age) {
this.name = name
this.age = age
}
Person.prototype.run = function(){
console.log(`${this.name}-${this.age}`)
}
function Student (sex) {
Person.call(this, '张三',20)
this.sex = sex
}
Student.prototype = new Person()
Student.prototype.constructor = Student
Student.prototype.run = function(){
console.log(`学生信息: ${this.name}-${this.age}-${this.sex}`)
}
var s1 = new Student('男')
s1.run() // 学生信息: 张三-20-男
寄生式继承
- 寄生组合继承算是
ES6
之前一种比较完美的继承方式- 只调用了一次父类构造函数,只创建了一份父类属性
- 子类可以用到父类原型链上的属性和方法
function Person (name,age) {
this.name = name
this.age = age
}
Person.prototype.run = function () {
console.log(`${this.name}-${this.age}`)
}
function Student (sex) {
this.sex = sex
Person.call(this,'张三',20)
}
// 与组合继承的区别
Student.prototype = Object.create(Person.prototype)
Student.prototype.constructor = Student
Student.prototype.run = function(){
console.log(`学生信息: ${this.name}-${this.age}-${this.sex}`)
}
var s1 = new Student('男')
s1.run() // 学生信息: 张三-20-男
ES6继承
- 主要是依赖
extends
关键字来实现继承,且继承的效果类似于寄生组合继承**
class Person {
constructor (name,age) {
this.name = name
this.age = age
}
run () {
console.log(`学生信息: ${this.name}-${this.age}`)
}
}
class Student extends Person {
constructor (name,age,sex) {
super(name,age)
this.sex = sex
}
run(){
console.log(`学生信息: ${this.name}-${this.age}-${this.sex}`)
}
}
var s1 = new Student('张三',20,'男')
s1.run() // 学生信息: 张三-20-男
7. new
new
- 创建一个空对象,作为即将要返回的那个对象实例
- 将这个对象的
__proto__
指向构造函数的prototype
- 执行构造函数并将this绑定到新创建的对象上
- 判断构造函数的返回值类型,
- 如果是引用类型,就返回这个引用类型的对象,
- 否则就返回创建的那个对象
function myNew(context){
let obj = new Object()
obj.__proto__ = Object.prototype
let res = context.apply(obj,[...args].slice(1))
return typeof res === "object" ? res : obj
}
8. 懒加载
懒加载
- 把图片真正的URL放在另一个属性
data-src
中 - 然后遍历所有的图片是否到达可视区域
- 到达可视区域后把
data-src
值赋给src
let imgList = document.querySelectorAll("img")
function lazyLoad() {
// 获取屏幕可视窗口高度
let clientHeight = document.documentElement.clientHeight
// 获取滚动条滚动的距离
let scrollTop = document.documentElement.scrollTop
for(let i = 0; i < imgList.length; i++){
if(imgList[i].offsetTop < clientHeight + scrollTop){
if(imgList[i].getAttribute("src") == default.jpg){
imgList[i].src = imgList[i].getAttribute("data-src")
}
}
}
}
通过IntersectionObserver实现
IntersectionObserver
接口(从属于Intersection Observer API
)提供了一种异步观察目标元素与其祖先元素或顶级文档视口(viewport
)交叉状态的方法- 其祖先元素或视口被称为根(
root
)
const observer = new IntersectionObserver(entries => {
entries.forEach(item => {
// 如果 intersectionRatio 为 0,则目标在视野外
if (item.intersectionRatio <= 0) return
const { target } = item
// 将元素的 data-src 属性设
target.src = target.dataset.src
// 停止观察该元素
image.onload = () =>{
observer.unobserve(target)
}
})
})
// 监听页面中所有 img 标签
document.querySelectorAll('img').forEach(img => {
observer.observe(img)
})
- Vue实现图片懒加载
<script setup lang="ts">
import { ref, reactive, Directive } from 'vue'
// https://cn.vitejs.dev/guide/features.html
// Vite 支持使用特殊的 import.meta.glob 函数从文件系统导入多个模块
const images: Record<string, { default: string }> = import.meta.glob(
'../assets/images/*.*',
{ eager: true }
)
const arr = Object.values(images).map((v) => v.default)
const vLazy: Directive<HTMLImageElement, string> = async (el, banding) => {
const url = await import('../assets/1.png')
el.src = url.default
const observe = new IntersectionObserver((entries) => {
entries.forEach((item) => {
if (item.intersectionRatio > 0) {
setTimeout(() => {
el.src = banding.value
}, 1000)
observe.unobserve(el)
}
})
})
observe.observe(el)
}
</script>
<template>
<div>
<img v-for="item in arr" v-lazy="item" width="360" height="500" />
</div>
</template>
9. instanceof
instanceof
instanceof
运算符用于检测构造函数的prototype
属性是否出现在某个实例对象的原型链上
function myInstanceOf(a,b) = {
if(typeof a !== 'object' || a === "null") return false
if(typeof b !== "function" ) {
throw new Error("b must be function")
let proto = a.__proto__
let prototype = b.prototype
while (true) {
if(proto === null) return false
if(proto === prototype) return true
proto = proto.__proto__
}
}
}
10. call,apply,bind
call
- 可以改变函数指向
- 第一个参数是要改变指向的对象
- 之后的参数形式是
arg1
,arg2
... 的形式
Function.prototype.myCall = function (context,...args){
// 为null和undefined的this值会自动指向全局对象
if(context === undefined || context === null){
context = windows
}else{
// 值为原始值(数字,字符串,布尔值)的this会指向该原始值的实例对象
context = Object(context)
}
// 用 Symbol 是防止跟上下文的原属性冲突
const fn = Symbol()
context[fn] = this
let result = context[fn](...args)
//把函数删除掉,为了还原外部obj对象,把添加到他上边的属性再删除掉
delete context[fn]
return result
}
let year = 2021
obj = {
year: 2022
}
const getDate = function (month,day) {
return `${this.year}-${month}-${day}`
}
console.log(getDate.call(obj,7,9)) // 2022-7-9
console.log(getDate.myCall(obj,7,9)) // 2022-7-9
apply
- 可以改变函数指向
- 第一个参数与
call
相同,为函数内部this
指向 - 函数的参数,则以数组的形式传递,作为
apply
第二参数
Function.prototype.myApply = function (context,args){
if(context === undefined || context === null){
context = windows
}else{
context = Object(context)
}
const fn = Symbol()
context[fn] = this
let result = context[fn](...args)
delete context[fn]
return result
}
let year = 2021
obj = {
year: 2022
}
const getDate = function (month,day) {
return `${this.year}-${month}-${day}`
}
console.log(getDate.apply(obj, [7,9])) // 2022-7-9
console.log(getDate.myApply(obj, [7,9])) // 2022-7-9
bind
- 第一个参数为要绑定的
this
对象 - 可以传递多个参数,参数规则类似
call
, - 后面的参数传递给要执行函数作为参数
bind
返回的是一个函数,所以需要在后面加上()去执行返回的函数,此()里面也可以加参数
Function.prototype.myBind = function (context,...args1) {
if(context === undefined || context === null){
context = windows
}else{
context = Object(context)
}
return (...args2)=>{
const fn = Symbol()
context[fn] = this
let result = context[fn](...args1,...args2)
delete context[fn]
return result
}
}
let year = 2021
obj = {
year: 2022
}
const getDate = function (month,day) {
return `${this.year}-${month}-${day}`
}
console.log(getDate.bind(obj)(7,9)) // 2022-7-9
console.log(getDate.myBind(obj)(7,9)) // 2022-7-9
11. promise
promise
Promise
存在三个状态(state
)pending
、fulfilled
、rejected
,pending
为初始态,并可以转化为fulfilled
和rejected
,- 成功时,不可转为其他状态,且必须有一个不可改变的值(
value
) - 失败时,不可转为其他状态,且必须有一个不可改变的原因(
reason
) - 若是
executor
函数报错 直接执行reject
promise
// promise
function Promise(executor){
this.promiseState = 'pending'
this.promiseResult = 'null'
const self = this
function resolve(data){
if(self.promiseState === 'pending'){
self.promiseState ='fulfilled'
self.promiseResult = data
}
}
function reject(reason){
if(self.promiseState === 'pending'){
self.promiseState ='rejected'
self.promiseResult = reason
}
}
try{
executor(resolve,reject)
}
catch(e){
reject(e)
}
}
promise.then
// then 第一种:只考虑了同步任务
class Promise{
......
then(onResolved, onRejected) {
if(this.promiseState === "fulfilled"){
onResolved(this.promiseResult)
}
if(this.promiseState === "rejected"){
onRejected(this.promiseResult)
}
}
}
// then 第二种:
//当promise去调用then方法时,当前的promise一直处于pending状态,
//我们需要先将成功和失败的回调分别存放起来,触发resolve或reject,依次调用成功或失败的回调
class Promise{
constructor(executor){
this.promiseResult = "PENDING"
this.promiseResult = null
this.reason = null
this.onResolvedCallbacks = [] // 存放成功的回调
this.onRejectedCallbacks = [] // 存放失败的回调
let resolve = data => {
if(this.promiseState === "PENDING"){
this.promiseState = "FULFILLED"
this.promiseResult = data
this.onResolvedCallbacks.forEach(fn =>{fn()}) // 依次将对应的函数执行
}
}
let reject = reason => {
if(this.promiseState === "PENDING"){
this.promiseState = "REJECTED"
this.reason = reason
this.onRejectedCallbacks.forEach(fn =>{fn()}) // 依次将对应的函数执行
}
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
then(onResolved, onRejected) {
if(this.promiseState === "fulfilled"){
onResolved(this.promiseResult)
}else if(this.promiseState === "rejected"){
onRejected(this.promiseResult)
}else{
// 如果promise的状态是 PENDING,需要将 onResolved 和 onRejected函数存放起来
this.onResolvedCallbacks.push(() => {
onResolved(this.value)
})
this.onRejectedCallbacks.push(() => {
onRejected(this.reason)
})
}
}
}
promise.resolve
// resolve方法用来生成一个直接处于FULFILLED状态的Promise
// 对于传入的数据需要判断是不是promise,如果是则正常流程走
// 如果不是表示则返回的是一个成功的promise
Promise.resolve = function(value){
return new Promise((resolve,reject) => {
if(value instanceOf Promise){
value.then((value)=>{
resolve(value)
},err=>{
reject(err)
})
}else{
resolve(value)
}
})
}
promise.reject
// reject方法用来生成一个直接处于REJECTED状态的Promise
Promise.reject = function(reason){
return new Promise((resolve,reject) => {
reject(reason)
})
}
promise.catch
// catch方法用来捕获promise的异常,就相当于一个没有成功的then
Promise.prototype.catch = function(onRejected){
return this.then(null,onRejected)
}
promise.all
// all方法多个异步并发获取最终的结果
// 如果有一个失败则失败,全部成功则成功
Promise.all = function(promiseAll){
return new Promise((resolve,reject) => {
let arr = []
let count = 0
if(promiseAll.length === 0){
resolve([])
}else{
for(let i = 0; i < promiseAll.length; i++) {
promiseAll[i].then((value) =>{
arr[i] = value
count += 1
if(count === promiseAll.length){
resolve(arr)
}
})
.catch(error =>{
reject(error)
})
}
}
})
}
promise.finally
// 不管是resolve还是reject都会调用finally方法
Promise.prototype.finally = function(callback){
return this.then((value) => {
callback()
return value
}),
(err) => {
callback()
throw err
}
}
promise.race
// race用来处理多个请求,采用最快的(谁先完成用谁的)
Promise.race = function(promiseRace){
return new Promise((resolve, reject) => {
if(promiseRace.length === 0){
resolve([])
}else{
for(let i = 0; i < promiseRace.length; i++) {
promiseRace[i].then((value) => {
resolve(value)
})
}.catch((e) => {
reject(e)
})
}
})
}
promiseLike
/**
* @description: 判断一个值是否是Promise Like
* @return {*}
*/
function isPromiseLike(value) {
return (
value !== null &&
(typeof value === 'object' || typeof value === 'function') &&
typeof value.then === 'function'
)
}
12. 可拖拽的div
js实现一个可以拖拽的div
document.body.style.margin = 0
document.body.style.padding = 0
let newDiv = document.createElement("div")
newDiv.style.position = "absolute"
newDiv.style.width = "100px"
newDiv.style.height = "100px"
newDiv.style.border = "2px red solid"
document.body.appendChild(newDiv)
let dragging = false
let position = []
newDiv.addEventListener("mousedown", e => {
dragging = true
position = [e.pageX, e.pageY]
// console.log(e.pageX, e.pageY, "===>初始位置")
})
document.addEventListener("mousemove", e => {
if (dragging) {
let x = e.pageX
let y = e.pageY
// console.log(e.pageX, e.pageY, "===>移动位置")
let deltaX = x - position[0]
let deltaY = y - position[1]
// console.log(deltaX, deltaY, "===>位移距离")
let left = parseInt(newDiv.style.left || 0)
let top = parseInt(newDiv.style.top || 0)
// console.log(left, top, "===>left,top")
newDiv.style.left = left + deltaX + "px"
newDiv.style.top = top + deltaY + "px"
// 判断不能移出屏幕左侧
if (left < 0) {
newDiv.style.left = 0
}
// 判断不能移出屏幕上方
if (top < 0) {
newDiv.style.top = 0
}
position = [x, y]
}
})
document.addEventListener("mouseup", e => {
dragging = false
})
13. 排序
冒泡排序
- 比较相邻的元素,如果第一个比第二个大,就交换它们两个
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数
- 针对所有的元素重复以上的步骤,除了最后一个
- 重复上述步骤,直到没有任何一堆数字需要比较
function bubbleSort(arr) {
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
}
}
}
return arr
}
let arr = [5,3,7,9,6,1,8,4,2]
bubbleSort(arr) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
bubbleSort 动图演示
选择排序
- 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
- 从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾
- 重复第二步,直到所有元素均排序完毕
function selectSort(arr){
let minIndex
for(let i = 0; i < arr.length - 1; i++){
minIndex = i
for(let j = i + 1; j < arr.length; j++){
if(arr[j] < arr[minIndex]){
minIndex = j
}
}
[arr[i], arr[minIndex]] = [arr[minIndex], arr[i]]
}
return arr
}
let arr = [5,3,7,9,6,1,8,4,2]
selectSort(arr) //[1, 2, 3, 4, 5, 6, 7, 8, 9]
selectSort 动图演示
快速排序
- 从数列中挑出一个元素,称为"基准"(
pivot
) - 重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任何一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(
partition
)操作 - 递归地(
recursively
)把小于基准值元素的子数列和大于基准值元素的子数列排序
function quickSort(arr) {
let target = arr[0]
let left = []
let right = []
if(arr.length <= 1) return arr
for(let i = 1; i < arr.length; i++) {
if(arr[i] < target){
left.push(arr[i])
}else{
right.push(arr[i])
}
}
return quickSort(left).concat([target],quickSort(right))
}
let arr = [5,3,7,9,6,1,8,4,2]
quickSort(arr) //[1, 2, 3, 4, 5, 6, 7, 8, 9]
quickSort 动图演示
插入排序
- 把待排序的数组分成已排序和未排序两部分,初始的时候把第一个元素认为是已排好序的
- 从第二个元素开始,在已排好序的子数组中寻找到该元素合适的位置并插入该位置(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面)
- 重复上述过程直到最后一个元素被插入有序子数组中
function insertionSort(arr) {
var len = arr.length
var preIndex, current
for (var i = 1; i < len; i++) {
preIndex = i - 1
current = arr[i]
while(preIndex >= 0 && arr[preIndex] > current) {
arr[preIndex+1] = arr[preIndex];
preIndex--
}
arr[preIndex+1] = current
}
return arr
}
let arr = [5,3,7,9,6,1,8,4,2]
insertionSort(arr) //[1, 2, 3, 4, 5, 6, 7, 8, 9]
insertionSort 动图演示
14. Symbol.iterator
Object.values
const obj = {a:1,b:2,c:3}
const values = Object.values(obj)
for(let value of values){
console.log(value) // 1 2 3
}
Object.entries
const obj = {a:1,b:2,c:3}
const entries = Object.entries(obj)
for(let [key,value] of entries){
console.log(value) // 1 2 3
}
Symbol.iterator
const obj={
a:1,
b:2,
c:3,
[Symbol.iterator](){
let index = 0
return{
next(){
if(index < Object.values(obj).length){
return{
value:Object.values(obj)[index++],
done:false
}
}else{
return {
value:undefined,
done:true
}
}
}
}
}
}
for(let value of obj){
console.log(value) // 1 2 3
}
Example
// 使左右俩边相等
[a,b] = {a:1,b:2}
Object.prototype[Symbol.iterator] = function(){
return Object.values(this)[Symbol.iterator]()
}
15. 获取对象的key
for...in 遍历
const obj = {a:1,b:2,c:3}
for(let key in obj) {
console.log(key) // a b c
}
Object.keys
const obj = {a:1,b:2,c:3}
const keys = Object.keys(obj)
console.log(keys) // ['a', 'b', 'c']
Object.getOwnPropertyNames
const obj = {a:1,b:2,c:3}
const keys = Object.getOwnPropertyNames(obj)
console.log(keys) // ['a', 'b', 'c']
Object.getOwnPropertySymbols
let d:symbol= Symbol(1)
const obj = {a:1,b:2,c:3,[d]:1}
const keys = Object.getOwnPropertySymbols(obj)
console.log(keys) // [Symbol(1)]
Reflect.ownKeys
let d:symbol= Symbol(1)
const obj = {a:1,b:2,c:3,[d]:1}
const keys = Reflect.ownKeys(obj)
console.log(keys) // ["a", "b", "c", Symbol(1)]
16. forEach,map,filter,reduce
forEach
- 针对每一个元素执行提供的函数
forEach
方法不会返回执行结果,而是undefined
forEach
对原数组进行修改- 不能中止或跳出
forEach
循环(return false
或者break
)
let arr = [1, 2, 3, 4, 5]
arr.forEach((item, index) => {
return (arr[index] = item * 2)
})
console.log(arr) // [2, 4, 6, 8, 10]
map
- 创建一个新的数组,其中每一个元素由调用数组中的每一个元素执行提供的函数得来
map
方法会得到一个新的数组并返回
let arr = [1, 2, 3, 4, 5]
let doubled = arr.map(item => {
return item * 2
})
console.log(arr) // [1, 2, 3, 4, 5]
console.log(doubled) // [2, 4, 6, 8, 10]
filter
filter
方法创建一个新数组,其包含通过所提供函数实现的测试的所有元素filter
不会改变原数组,它返回过滤后的新数组
let arr = [1, 2, 3, 4, 5]
let filter = arr.filter(item => {
return item >= 3
})
console.log(arr) // [1, 2, 3, 4, 5]
console.log(filter) // [3, 4, 5]
reduce
对数组中的每个元素执行一个自定义的累计器,将其结果汇总为单个返回值
形式:
Array.prototype.reduce
(callback
,initialValue
)callback
:回调函数(必选)- 回调函数的参数:
previousValue
:累计器完成计算的返回值(必选)currentValue
:当前元素(必选)currentIndex
:当前元素的索引(可选)Array
:当前元素所属的数组对象(可选)
- 回调函数的参数:
initValue
:初始值(可选)- 如果调用
reduce
() 时提供了initialValue
,previousValue
取值则为initialValue
,currentValue
则取数组中的第一个值 - 如果没有提供
initialValue
,那么previousValue
取数组中的第一个值,currentValue
取数组中的第二个值
- 如果调用
累加累乘:
javascriptconst arr = [1, 2, 3, 4, 5] const a = arr.reduce((pre, cur) => pre + cur) // 15 // 等同于 const b = arr.reduce((pre, cur) => pre + cur, 0) // 15
javascriptconst Accumulation = (...args) =>{ return args.reduce((pre,cur) => pre + cur,0) } Accumulation(1, 2, 3, 4, 5) // 15
javascriptconst Multiplication = (...args) =>{ return args.reduce((pre,cur) => pre * cur,1) } Accumulation(1, 2, 3, 4, 5) // 120
权重求和:
javascriptconst scores = [ { score: 90, subject: "Chinese", weight: 0.5 }, { score: 95, subject: "Math", weight: 0.3 }, { score: 85, subject: "English", weight: 0.2 } ] const res = scores.reduce((pre, cur) => pre + cur.score * cur.weight, 0) // 90.5
代替reverse:
javascriptconst reverse = (arr = []) =>{ return arr.reduceRight((pre,cur) => (pre.push(cur),pre),[]) } reverse([1, 2, 3, 4, 5]) // [5, 4, 3, 2, 1]
代替 map 和 filter:
javascriptconst arr = [1, 2, 3, 4, 5] // 代替map:[2, 4, 6, 8, 10] const a = arr.map(item => item * 2) const b = arr.reduce((pre, cur) => [...pre, cur * 2], []) // 代替filter:[4, 5] const c = arr.filter(item => item > 3) const d = arr.reduce((pre, cur) => cur > 3 ? [...pre, cur] : pre, []) // 代替map和filter:[4, 6, 8 ,10] const e = arr.map(item => item * 2).filter(item => item > 2) const f = arr.reduce((pre, cur) => cur * 2 > 2 ? [...pre, cur * 2] : t, [])
数组最大最小值:
javascript// 最大值 function Max(arr = []){ return arr.reduce((pre, cur) => pre > cur ? pre : cur) } // 最小值 function Min(arr = []){ return arr.reduce((pre, cur) => pre < cur ? pre : cur) } let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9] Max(arr) // 9 Min(arr) // 1
字符串反转:
javascript// reduceRight function ReverseStr(str){ return str.split("").reduceRight((pre, cur) => pre + cur) } ReverseStr("Rng牛逼") // '逼牛gnR' function ReverseStr2(str){ return str.split("").reverse().join("") } ReverseStr2("Rng牛逼") // '逼牛gnR'
17. 发布订阅
18 代码雨
// 当页面加载完成时执行
onload = () => {
// 获取 canvas 元素
let canvas = document.querySelector('canvas') as HTMLCanvasElement
// 获取 2D 上下文对象
let ctx = canvas.getContext('2d') as CanvasRenderingContext2D
// 设置 canvas 的宽度和高度为屏幕可用宽度和高度
canvas.width = screen.availWidth
canvas.height = screen.availHeight
// 定义要显示的字符数组
let str: string[] = 'HelloWorld0101010'.split('')
// 计算列数,并创建数组用于存储每列的位置
let arr = Array(Math.ceil(canvas.width / 10)).fill(0)
// 定义绘制代码雨的函数
let rain = () => {
// 绘制半透明背景,以实现模糊效果
ctx.fillStyle = 'rgba(0,0,0,.05)'
ctx.fillRect(0, 0, canvas.width, canvas.height)
// 设置绘制字符的颜色
ctx.fillStyle = '#0f0'
// 遍历每一列
arr.forEach((item, index) => {
// 随机选择一个字符,并在当前列的位置绘制
ctx.fillText(
str[Math.floor(Math.random() * str.length)],
index * 10,
item + 10
)
// 更新当前列的位置
arr[index] =
item > canvas.height || item > 10000 * Math.random() ? 0 : item + 10
})
}
// 使用 setInterval 方法每隔 40 毫秒调用一次绘制函数,以实现动画效果
setInterval(rain, 40)
}