JS入门到精通详解(16)
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
闭包全是面试题
闭包的概念及应用
什么是闭包
简单来说闭包就是函数嵌套函数目的是为了访问一个函数中的局部变量让这个局部变量可以全局访问即使这个变量所在的函数被释放之后。
为什么不使用全局变量
造成全局污染
闭包原理
是利用了浏览器的垃圾回收机制当被销毁的内容被垃圾回收机制清除的时候发现这个内容正在被另一个函数所使用则这个内容将永远不被回收长驻内存所以会造成内存泄露。
闭包的优缺点
优点可以让一个局部变量在全局访问
缺点由于闭包的变量是始终保存在内存中内存消耗会变大导致网页性能问题。在IE浏览器中可能还会导致内存泄漏等问题故在退出函数之前将所有变量删除。
如何解决内存泄露的问题
将使用完毕的变量赋值为null
闭包的应用
模拟私有变量
const User = (function(){
//定义私有变量_password
let _password = null;
class User{
constructor(username,password){
//初始化私有变量_password
_password = password
this.username = username;
}
login(){
console.log(this.username,_password);
}
}
return User;
})();
let user = new User('小明',123);
console.log(user.username); //小明
console.log(user.password); //undefined
console.log(user._password); //undefined
user.login(); //小明 123
防抖
在浏览器的各种事件中有一些容易频繁触发的事件比如scroll、resize、鼠标事件比如 mousemove、mouseover、键盘事件keyup、keydown 等。频繁触发回调导致大量的计算会引发页面抖动甚至卡顿影响浏览器性能。防抖和节流就是控制事件触发的频率的两种手段。
防抖的中心思想是在某段时间内不管你触发了多少次回调我都只执行最后一次。
// fn是我们需要包装的事件回调, delay是每次推迟执行的等待时间
function debounce(fn, delay) {
// 定时器
let timer = null;
// 将debounce处理结果当作函数返回
return function () {
// 保留调用时的this上下文
let context = this
// 保留调用时传入的参数
let args = arguments
// 每次事件被触发时都去清除之前的旧定时器
if(timer) {
clearTimeout(timer)
}
// 设立新定时器
timer = setTimeout(function () {
fn.apply(context, args)
}, delay)
}
}
// 用debounce来包装scroll的回调
window.onscroll = debounce(() => {
console.log('触发了滚动事件');
},1000)
节流
节流的中心思想是在某段时间内不管你触发了多少次回调我都只认第一次并在计时结束时给予响应也就是隔一段时间执行一次。
// fn是我们需要包装的事件回调, interval是时间间隔的阈值
function throttle(fn, interval) {
// last为上一次触发回调的时间
let last = 0;
// 将throttle处理结果当作函数返回
return function () {
// 保留调用时的this上下文
let context = this
// 保留调用时传入的参数
let args = arguments
// 记录本次触发回调的时间
let now = new Date()
// 判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值
if (now - last >= interval) {
// 如果时间间隔大于我们设定的时间间隔阈值则执行回调
last = now;
fn.apply(context, args);
}
}
}
// 用throttle来包装scroll的回调
window.onscroll = throttle(() => {
console.log('触发了滚动事件')
},1000);
柯理化函数
函数柯里化就是可以将一个接受多个参数的函数分解成多个接收单个参数的函数的技术直到接收的参数满足了原来所需的数量后才执行原函数
//求和
const sumFn = (...args) => {
return args.reduce((a,b) =>{
return a + b;
})
};
//排序
const sortFn = (...args) => {
return args.sort((a,b) => a - b);
}
let currying = function(fn){
const args = []; //接收所有传递的参数
return function result(...rest){ //剩下的参数
if(rest.length === 0){
return fn(...args);
}else{
args.push(...rest);
return result;
}
}
}
console.log(currying(sumFn)(1)(2)(3)()); //6
console.log(currying(sumFn)(1,2)(3,4)(5)()); //15
console.log(currying(sumFn)(1)(2,3,4,5)(6)()); //21
console.log(currying(sortFn)(1)(3)(2)(6,4)(5)());
继承
借用构造函数继承
构造继承特点
子类实例共享父类引用属性的问题
创建子类实例时可以向父类传递参数
可以实现多继承call 或 apply 多个父类对象
构造继承缺点
实例并不是父类的实例只是子类的实例
只能继承父类的实例属性和方法不能继承原型属性和方法
无法实现函数复用每个子类都有父类实例函数的副本影响性能
原型继承
原型的特点共享一个空间
原型及原型链
所有的函数都有一个属性prototype,这个属性可以找到该函数后面对应的原型对象。
所有的对象都有一个属性__proto__,通过这个属性可以找到该对象后面对应的原型对象原型对象通过__proto__这个属性可以找到当前原型对象的父级原型对象一直找到顶级原型Object.prototype,再找则找到null,这个过程称为原型链
所有原型对象都有一个属性constructor,通过这个属性可以找到当前原型对象对应的构造函数。
特点
非常纯粹的继承关系实例是子类的实例也 是父类的实例
父类新增原型方法/原型属性子类都能访问到
简单易于实现
缺点
要想为子类新增原型属性和方法必须要在new 父类这样的语句之后执行。要想为子类新增实例属性和方法必须要在构造函数中添加
无法实现多继承
来自原型对象的所有属性被所有实例共享
组合继承
特点
可以继承实例属性和方法也可以继承原型属性和方法
既是子类的实例也是父类的实例
不存在引用属性的共享问题
可传参
函数可复用
缺点
调用了两次父类构造函数生成了两份实例
ES6 的继承语法
class 子类名 extends 父类名{
constructor([参数]){
//调用父类的构造函数
super([参数]);
}
}