防抖:在多次调用时,只触发最近一次调用,类似于——回城,一旦重新点击,重新开始
节流:调用一次后,就不能再次调用除非冷却时间过去——类似技能冷却
debounce
//debounce
function debounce(fn, times) {
let timer;
return function () {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
timer = null
fn.apply(this, ...arguments)
}, times)
}
}throttle
function throttle(fn) {
let flag = null; // 通过闭包保存一个标记
return function () {
if (flag) return; // 当定时器没有执行的时候标记永远是null
flag = setTimeout(() => {
fn.apply(this, arguments);
// 最后在setTimeout执行完毕后再把标记设置为null(关键)
// 表示可以执行下一次循环了。
flag = null;
}, 500);
};
}首先我们需要知道,a标签干了什么,a标签实现了跳转,而且是本页还是新开页面跳转,此外还有a标签上面的样式改变
document.querySelector('.a').addEventListener('click',()=>{
window.location.href = 'https://www.baidu.com/'
window.open('_blank')
})a标签的href就是location.href跳转的目标
window.open()即打开方式,即a的target,然后在修改部分样式即可达到
//通过直接修改源数组
a.splice(2,1)
//拷贝数组
a.slice(0,2).concat(a.slice(3))
// 方法三 delete数组中的元素 再把这个元素给剔除掉
delete arr[2]
arr.join(" ").replaceAll(/\s{1,2}/g," ").split(" ")不考虑循环引用
1.使用解构
const obj = {
a: 1,
b: {
c:2
}
}
const objCopy = { ...obj };
objCopy.b.c = 3;
console.log(obj); // { a: 1, b: { c: 3 } }
console.log(objCopy); // { a: 1, b: { c: 3 } }2.使用Object.assign
const obj = {
a: 1,
b: {
c:2
}
}
const obj1 = Object.assign({}, obj);
obj1.b.c = 4;
console.log(obj); // { a: 1, b: { c: 4 } }
console.log(obj1); // { a: 1, b: { c: 4 } }3.使用遍历
const obj2 = {}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
obj2[key] = obj[key];
}
}1.手写深拷贝函数
//深拷贝
function deepCopy(obj) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
const newObj = Array.isArray(obj)? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = deepCopy(obj[key]);
}
}
return newObj;
}
//测试
const obj = {
a: 1,
b: 2,
c: {
d:[1,2,3]
}
}
const newObj = deepCopy(obj)
console.log(newObj === obj)
newObj.c.d.push(4)
console.log('obj', obj)
console.log('newObj', newObj)2.使用JSON
const newObj = JSON.parse(JSON.stringify(obj))如果出现循环引用,这两种方法都不行
//处理循环引用问题
const obj2 = {
a:1
}
const obj1 = {
a: 1,
b:obj2
}
obj2.b = obj1使用map进行存储,各个key然后可以实现拷贝
const deepCopyOptimization = (obj) => {
const cache = new WeakMap();
const copy = (obj) => {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
if (cache.has(obj)) {
return cache.get(obj);
}
const newObj = Array.isArray(obj)? [] : {};
cache.set(obj, newObj);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = copy(obj[key]);
}
}
return newObj;
}
return copy(obj);
}
const newObj2 = deepCopyOptimization(obj2)
const newObj1 = deepCopyOptimization(obj1)
console.log(newObj1)
console.log(newObj2)
console.log(newObj2.b === newObj2)首先我们应该知道,bind,call,apply分别做了什么
bind,在函数上调用,将其this指向传入的对象,并且返回一个函数
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error: this is not a function');
}
if (context === undefined || context === null) {
context = window;
} else {
context = Object(context);
}
const self = this;
const args = [...arguments].slice(1)
return function () {
return self.apply(context, args.concat([...arguments]));
}
}call,在函数上调用,将其this指向传入的对象,第二个参数是正常传参,并且返回一个执行结果
Function.prototype.myCall = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error: this is not a function');
}
if (context === undefined || context === null) {
context = window;
} else {
context = Object(context)
}
const args = [...arguments].slice(1);
const sy = Symbol('fn')
context[sy] = this;
const result = context[sy](...args);
delete context[sy];
return result;
}apply在函数上调用,将其this指向传入的对象,第二个参数是数组,并且返回一个执行结果
Function.prototype.myApply = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error: this is not a function');
}
if (context === undefined || context === null) {
context = window;
} else {
context = Object(context)
}
//因为是数组直接取第二位就行了
const args = [...arguments][1];
const sy = Symbol('fn')
context[sy] = this;
//这里要改变一下,因为传入的参数是一个数组需要结构一下
const result = arguments.length > 1? context[sy](...args) : context[sy]();
delete context[sy];
return result;
}这里需要知道原型链相关的知识
原型继承——即将子类的prototype指向为一个父类的实例
缺点:父类的引用类型,会被所有子类共享
//原型链继承
function Person() {
this.shared = ['a', 'b', 'c']
}
function Student() {
}
Student.prototype = new Person()
let p = new Student("Mary", 25);
p.shared.push('d')
let s = new Student("John", 20, 3);
console.log('s.shared', s.shared) //s.shared [ 'a', 'b', 'c', 'd' ]
console.log('p.shared', p.shared) // p.shared [ 'a', 'b', 'c', 'd' ]2.构造函数继承
使用父类的构造函数来增强子类实例,等同于复制父类的实例给子类(不使用原型)
//构造函数继承
function Father(){
this.colors = ['red', 'blue', 'green'];
this.sayGoodbye = () =>{
console.log('goodbye')
}
}
Father.prototype.sayHello = function(){
console.log('hello')
}
function Son() {
Father.call(this)
}
let s = new Son()
let f = new Son()
s.colors.push('yellow')
console.log('s.colors', s.colors) //s.colors [ 'red', 'blue', 'green', 'yellow' ]
console.log('f.colors', f.colors) //f.colors [ 'red', 'blue', 'green' ]
s.sayGoodbye() //goodbye
s.sayHello()//报错,不存在这个东东
console.log(s.sayGoodbye === f.sayGoodbye) // false核心代码是Father.call(this),创建子类实例时调用Father构造函数,于是Son的每个实例都会将SuperType中的属性复制一份。
但是,只能继承父类的实例属性和方法,原型上的不可得到,而且每个子类都有父类的实例函数副本,没有实现复用,浪费性能
组合继承
结合了上面两者的优点,使用原型链进行原型继承,借用构造函数实现实例属性的继承
//组合继承
function Parent(name) {
this.name = name
this.colors = ['red', 'blue', 'green']
}
Parent.prototype.sayHello = function () {
console.log('hello')
}
function Child(name, age) {
Parent.call(this, name)
this.age = age
}
Child.prototype = new Parent()
Child.prototype.constructor = Child
let c = new Child('Tom', 20)
let b = new Child('Jerry', 25)
c.colors.push('yellow')
console.log('c', c) // c Child { name: 'Tom',colors: [ 'red', 'blue', 'green', 'yellow' ], age: 20}
console.log('b', b) // b Child { name: 'Jerry', colors: [ 'red', 'blue', 'green' ], age: 25 }
console.log(c.sayHello === b.sayHello) // true
console.log('child',Child.prototype)缺点:会给Child.prototype添加两个属性“name”和“colors”
而且会给Child的实例上添加name和colors属性

寄生继承
核心:在原型式继承的基础上,增强对象,返回构造函数
function createAnother(original){
var clone = object(original); // 通过调用 object() 函数创建一个新对象
clone.sayHi = function(){ // 以某种方式来增强对象
alert("hi");
};
return clone; // 返回这个对象
}
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); //"hi"缺点:原型链继承多个实例的引用类型属性指向相同,存在篡改的可能。无法传递参数
组合寄生继承
最成熟的方法,也是当前库所有 的方法
//寄生组合继承
function inheritPrototype(child, parent) {
var prototype = Object.create(parent.prototype);// 创建对象,创建父类原型的一个副本
prototype.constructor = child; // 增强对象,弥补因重写原型而失去的默认的constructor 属性
child.prototype = prototype; // 指定对象,将新创建的对象赋值给子类的原型
}
function Parent(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
function Child(name, age) {
Parent.call(this, name); // 调用父类的构造函数,并传入参数
this.age = age;
}
inheritPrototype(Child, Parent); // 继承父类原型
Parent.prototype.sayName = function () {
console.log(this.name);
}
var child1 = new Child("John", 25);
var child2 = new Child("Mary", 30);
child1.colors.push("yellow");
console.log(child1)
console.log(child2)只有子类实例上有方法,而父类没有

class Person {
constructor(name) {
this.name = name;
}
sayHello() {
console.log(`Hello, my name is ${this.name}`);
}
}
class Child extends Person {
constructor(name, age) {
super(name)
this.age = age;
}
}
const child1 = new Child('John', 10);
child1.sayHello(); // Output: Hello, my name is John
console.log(child1); //首先,我们需要知道new操作符,干了什么。
new操作符,创建了一个对象,然后将属性附加在对象上
function myNew(constructor, ...args) {
const obj = Object.create(constructor.prototype);
const result = constructor.call(obj, ...args)
return typeof result === 'object' ? result : obj
}try catch只能拦截这个块里面的
可以给window对象添加一个unhandledrejection方法
// 使用 unhandledrejection 来拦截全局错误 (这个是对的)
window.addEventListener("unhandledrejection", (event) => {
event && event.preventDefault();
console.log("event", event);
});(async function () {
console.log('start sleep');
await sleep(3000)
console.log('end sleep')
function sleep(ms) {
return new Promise(res => {
setTimeout(() => {
res()
}, ms)
})
}
})()通过await异步阻塞
通过while循环完全阻塞进程
(function () {
console.log('start sleep');
sleep(3000)
console.log('end sleep')
function sleep(ms) {
const start = Date.now()
while (Date.now() - start < ms) {
// continue;
}
}
})()function add(x) {
let sum = x;
let temp = function (y) {
sum += y;
return temp
}
temp.toString = function () {
return sum;
}
return temp;
}
alert(add(1)(2)(1)(2)); //3这里利用了alert参数为string 的方法,如果不是string会调用toString()
比如[5,[[4,3],2],1]换为(5-((4-3)-2)-1)
1.转化为字符串然后进行遍历替换
//比如`[5,[[4,3],2],1]`换为`(5-((4-3)-2)-1)`
const arr = [5, [[4, 3], 2], 1]
console.log(JSON.stringify(arr).split(''))
const fn = JSON.stringify(arr).split('').map(item => {
if (item === ',') return '-'
else if (item === '[') return '('
else if (item === ']') return ')'
else return item
}).join('')
let res = new Function(`return ${fn}`)()
console.log(res) // 5reduce的递归调用
const convert = (arr) => {
return arr.reduce((pre, cur) => {
const first = Array.isArray(pre) ? convert(pre) : pre
const second = Array.isArray(cur) ? convert(cur) : cur
return first - second
})
}
console.log(convert(arr)) // 5手写flat
const flat = (arr, deep = 1) => {
const res = []
for (let a of arr) {
if (Array.isArray(a) && deep > 0) {
res.push(...flat(a,deep-1))
} else {
res.push(a)
}
}
return res
}还有巧计,只求最大的话
arr.toString().split(',').map(Number)等同于
arr.flat(Infinity)function convert(arr,parentId){
const filter = arr.filter((item)=>{
return parentId === undefined? item.parent === -1 : item.parent === parentId
})
return filter.map(item=>{
item.childNode = convert(arr,item.id)
return item
})
}