1. 数组去重
let arr=[1,2,3,4,4,3,4,6,1,5]
//方法一:new Set()
function unique(arr){
return [...new Set(arr)]
//return Array.from(new Set(arr)) new Set()方法返回的是类数组,使用Array.from转换为数组
}
//方法二:filter+indexOf
function unique(arr){
let result = arr.filter(function(item,index,arr){
return arr.indexOf(item) !== index
})
return result
}
//方法三:for+includes
function unique(arr){
let newArr=[]
for(let i=0;i<arr.length;i++){
if(!newArr.includes(arr[i])){
newArr.push(arr[i])
}
}
return newArr
}
//方法四:sort
function unique(arr){
let newArr=[]
arr.sort()
for(let i=0;i<arr.length;i++){
if(arr[i]!==arr[i+1]){
newArr.push(arr[i])
}
}
return newArr
}
2. 数组扁平化
- 方法一:Array.flat()
优点:代码简单,使用内置方法,性能较好
缺点:不支持ES2019的环境
let arr=[1,2,3,[4,5,[6,[7]]]]
let _arr=arr.flat(Infinity) //Infinity展开任意深度的嵌套数组
- 方法二:for循环递归
优点:代码简单,方便易懂
缺点:存在性能问题,对于非常大的数组和嵌套很深的数组,递归可能导致堆栈溢出
let arr=[1,2,3,[4,5,[6,[7]]]]
function flatten(arr){
let flatArr=[]
arr.forEach((item)=>{
if(Array.isArray(item)){
flatArr=flatArr.concat(flatten(item))
}else{
flatArr.push(item)
}
})
return flatArr
}
- 方法三:toString和split
优点:适用于简单的扁平化需求
缺点:对于包含字符串和对象元素的数组,可能会出现不符合预期的结果
function flatten(arr){
return arr.toString().split(',').map(item=>+item)
}
- 方法四:使用正则表达式+JSON
优点:适用于简单的扁平化需求
缺点:对于包含字符串和对象元素的数组,可能会出现不符合预期的结果
function flatten(arr){
return JSON.parse('['+JSON.stringify(arr).replace(/\[|\]/g,'')+']')
}
- 方法五:扩展运算符和递归
优点:结合了扩展运算符和递归的优点
缺点:存在性能问题,对于非常大的数组和嵌套很深的数组,递归可能导致堆栈溢出
function flatten(arr){
return [].concat(...arr.map(item=>Array.isArray(item)?flatten(item):item))
}
3. 发布订阅
class eventBus{
constructor(){
this.events={}
}
on(eventName,callback){
if(!this.events[eventName]){
this.events[eventName]=[]
}
this.events[eventName].push(callback)
}
}
4. 深浅拷贝
- 浅拷贝:拷贝基础数据类型时,不受任何影响,拷贝引用数据类型时,拷贝的是堆内存中的地址指针,所以修改拷贝对象,原对象也会被修改
- 深拷贝:深拷贝就是在堆内存中开辟新的内存区域,赋值一份新的值,修改拷贝对象时原对象不会被影响
- 浅拷贝的常用方法
- 赋值
- Object.assign(target,origin)
- ES6扩展运算符
let person = {
name: ' tom ',
appearance: {
tall: 180,
skin: 'yellow',
weight: 76
}
}
//赋值
let p1 = person
p1.tall = 192
console.log(p1.tall) //192
//Object.assign() 当属性值是一个引用类型时,是浅拷贝
let p1 = Object.assign({}, person)
p1.appearance.tall = 192
console.log(person.appearance.tall) //192
//扩展运算符
let p1={...person}
p1.appearance.tall = 192
console.log(person.appearance.tall) //192
- 深拷贝的常用方法
- JSON.parse(JSON.stringify(obj))
- JOSN.stringify方法无法处理一些特殊的对象,如RegExp,Error等,序列化的结果为空对象
- 属性值为
日期对象,序列化的值为字符串- 属性值为
函数,Symbol或undefined,序列化时会将函数,Symbol和undefined丢失- 属性值为
NaN,infinity或-infinity,序列化结果为null- JOSN.stringify()只能序列化对象的可枚举属性,如果属性值为构造函数的实例,序列化后将丢弃对象的constructor属性
function Person(name, age) {
this.name = name
this.age = age
}
let p1 = new Person('lili', 28)
let data = {
data1: new Date('2023-10-24'),
data2: new RegExp('\\w+'),
data3: undefined,
data4: function () {
console.log('Hello world')
},
data5: NaN,
data6: Infinity,
data7: Symbol('123'),
data8: p1
}
let jsonData = JSON.parse(JSON.stringify(data))
jsonData.data8.constructor == Person //false
jsonData.data8.constructor == Object //true
console.log(jsonData)
- 手写深拷贝
function deepClone(obj){
if(typeof obj !== 'object' || obj === null){
return obj
}
let newObj=Array.isArray(obj)?[]:{}
for(let key in obj){
if(obj.hasOwnProperty(key)){
newObj[key]=typeof obj[key] === 'object'?deepClone(obj[key]):obj[key]
}
}
return newObj
}
5. 字符串模板
function template(strings, ...values) {
let result = strings[0];
for (let i = 0; i < values.length; i++) {
result += values[i] + strings[i + 1];
}
return result;
}
// 使用示例
const name = 'Alice';
const age = 30;
const greeting = template`Hello, my name is ${name} and I am ${age} years old.`;
console.log(greeting); // 输出: Hello, my name is Alice and I am 30 years old.
6. 函数防抖
触发高频事件,
N秒后只会执行一次,N秒内再次触发,将会重新计算(将多次执行变成最后一次执行)
function debounce(fn, delay) {
let timer = null
return function () {
let _args = arguments
if (timer) {
clearTimeout(timer)
timer = null
}
timer = setTimeout(() => {
fn.apply(this, _args)
}, delay)
}
}
function test(text) {
alert(`Hello World ${text}`)
}
let testScrollFn = debounce(test, 500)
window.onscroll = function () {
testScrollFn('lili')
}
7. 函数节流
触发高频事件,
N秒内只会执行一次
//方法一:时间戳来实现
function throttle(fn,wait){
let previous=0
return function(){
let now=+new Date()
let args=arguments
if(now-previous>wait){
fn.apply(this,args)
previous=now
}
}
}
//方法二:定时器来实现
function throttle(fn,wait){
let timer=null
return function(){
if(timer){
return
}
let context=this
let args=arguments
timer=setTimeout(()=>{
fn.apply(context,args)
timer=null
},wait)
}
}
let testThrottle = throttle(test, 500)
function test(text) {
alert(`Hello World ${text}`)
}
window.onscroll = function () {
testThrottle('lili')
}
8. AJAX
- 优缺点
优点:可以无需刷新页面与服务器端进行通信,根据用户事件来更新部分页面内容
缺点:没有浏览历史,无法回退,存在跨域问题 - 发送Ajax请求的五个步骤
创建异步对象,即XMLHttpRequest对象- 使用
open方法设置请求参数open(method,url,async)(method:方法;url:请求url;async:是否异步) 发送请求:send()注册onreadystatechange事件,状态改变时就会调用- 服务器端响应,获取返回的数据
function ajaxFn({method,url,data}){
return new Promise((resolve,reject)=>{
const xhr=XMLHttpRequest?new XMLHttpRequest():new ActiveXObject('Microsoft.XMLHTTP');
xhr.send(method,url,)
})
}
9. new关键字
1. 创建一个新对象
2. 继承原型,将新对象的__proto__属性指向构造函数的prototype属性
3. 将构造函数的this,指向新的对象
4. 执行构造函数中的代码,为这个新的对象添加属性和方法
5. 如果构造函数返回非空对象,则返回该对象,否则返回新对象
function mynew(func,...args){
//创建一个新对象
let obj={}
//将新对象的__proto__属性指向构造函数的prototype属性
Object.setPrototypeOf(obj,func.prototype)
//将构造函数的this指向新对象,并执行构造函数中的代码
let result=func.apply(obj,args)
//如果构造函数返回的为非空对象,则返回该对象,否则返回新对象
return result instanceof Object ? result:obj
}
//测试一下
function Person(name,age){
this.name=name
this.age=age
}
Person.prototype.getInfo=function(){
console.log(`姓名:${this.name};年龄:${this.age}`)
}
let p1 = mynew(Person,'lili',28) //{name: 'lili', age: 28}
10. instanceof
function Person(){}
let p1=new Person()
p1.__proto__==Person.prototype //true
//方法一:
function instanceOfFn(left,right){
if(typeof left!==null&&(typeof left==='object'||typeof left==='function')){
if(left.__proto__==right.prototype){
return true
}else{
if(left.__proto__===null){
return false
}
return instanceOfFn(left.__proto__.__proto__,right)
}
}else{
return false
}
}
//方法二:
function instanceofFn2(left, right) {
if (left !=== null && (typeof left === 'object' || typeof left === 'function')) {
let proto = Object.getPrototypeOf(left)
while (true) {
if (proto == null) return false
if (proto == right.prototype) return true
proto = Object.getPrototypeOf(proto)
}
} else {
return false
}
}
11. Object.create
Object.create()方法用于创建一个新对象,使用现有的对象作为新对象的原型
Object.createFn=function(obj){
if(typeof obj !== 'object' && typeof obj !== 'function'){
throw new TypeError('Object prototype may only be an Object or null')
}
function Func(){}
Func.prototype=obj
Func.prototype.constructor=F
return new Func()
}
const person={getInfo:function(){console.log(`My name is ${this.name},My age is ${this.age}`)}}
let
12. 继承
- 原型链继承
问题1:原型中包含的引用类型属性将被所有实例共享
问题2:子类在实例化的时候,无法给父类的构造函数传参
function Person(name,age){
this.name=name
this.age=age
this.des={nationality:'China',skinColor:'Yellow'}
}
Person.prototype.getBaseInfo=function(){
return `姓名:${this.name},年龄:${this.age},身高:${this.tall},语言:${this.des.language}`
}
function Child(tall){
this.tall=tall
}
Child.prototype=new Person()
let child1=new Child('lili',28)
child1.des.language='Chinese'
child1.getBaseInfo() //'姓名:undefined,年龄:undefined,身高:lili,语言:Chinese'
- 借用构造函数实现继承
优点:解决了引用类型共享和构造函数传参
问题1:由于方法定义在子类构造函数中,所以每次创建子类实例时,都会调用一次父类方法 问题2:只能继承父类的实例属性和方法,不能继承原型属性和方法
function Person(name,age){
this.name=name
this.age=age
this.des={nationality:'China',skinColor:'Yellow'}
}
Person.prototype.getBaseInfo=function(){
return `姓名:${this.name},年龄:${this.age},身高:${this.tall},语言:${this.des.language}`
}
function Child(name,age,tall){
this.tall=tall
Person.call(this,name,age)
}
let child1=new Child('lili',28,'168cm')
child1.des.language='Chinese'
let child2=new Child('tom',30,'180cm')
child2.getBaseInfo() //Uncaught TypeError: child2.getBaseInfo is not a function
- 组合继承
优点:集合了原型链和借用构造函数的优点
问题:调用了两次父类构造函数,生成了两份实例(第一次:new Person();第二次:Person.call(this,name,age))
实现思路:使用原型链继承原型链上的方法,使用借用构造函数继承实例属性,把方法定义在原型上得以重用,又可以让每个实例都有自己的属性
function Person(name,age){
this.name=name
this.age=age
this.des={nationality:'China',skinColor:'Yellow'}
}
Person.prototype.getBaseInfo=function(){
return `姓名:${this.name},年龄:${this.age},身高:${this.tall},语言:${this.des.language}`
}
function Child(name,age,tall){
this.tall=tall
Person.call(this,name,age)
}
Child.prototype=new Person()
Child.prototype.constructor=Child
let child1=new Child('lili',28,'168cm')
child1.des.language='Chinese'
let child2=new Child('tom',30,'180cm')
child2.getBaseInfo() //'姓名:tom,年龄:30,身高:180cm,语言:undefined'
- 寄生组合继承
优点:解决了调用了两次父类构造函数,生成了两份实例的问题
function Person(name,age){
this.name=name
this.age=age
this.des={nationality:'China',skinColor:'Yellow'}
}
Person.prototype.getBaseInfo=function(){
return `姓名:${this.name},年龄:${this.age},身高:${this.tall},语言:${this.des.language}`
}
function Child(name,age,tall){
this.tall=tall
Person.call(this,name,age)
}
Child.prototype=Object.create(Person.prototype) //核心语法
Child.prototype.constructor=Child
let child1=new Child('lili',28,'168cm')
child1.des.language='Chinese'
let child2=new Child('tom',30,'180cm')
child2.getBaseInfo() //'姓名:tom,年龄:30,身高:180cm,语言:undefined'
- Class继承
class Person{
constructor(name,age){
this.name=name
this.age=age
this.des={nationality:'China',skinColor:'Yellow'}
}
getBaseInfo(){
return `姓名:${this.name},年龄:${this.age},身高:${this.tall},语言:${this.des.language}`
}
}
class Child extends Person{
constructor(name,age,tall){
super(name,age)
this.tall=tall
}
}
let child1=new Child('lili',28,'168cm')
child1.des.language='Chinese'
let child2=new Child('tom',30,'180cm')
child2.getBaseInfo() //'姓名:tom,年龄:30,身高:180cm,语言:undefined'