一.闭包
什么是闭包:
闭包就是可以访问外层函数作用域中变量的函数.
闭包的特点:
1.会将闭包所访问的变量永远保留在计算机内存中, 可能会产生内存泄漏.
2.闭包可以实现变量的私有化.
内存泄漏:闭包中的变量会存储在计算机内存中,且不释放,会导致计算机内存搁置,这个就叫内存泄漏。
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<script>
//获取所有button标签
var buttons = document.querySelectorAll('button');
//循环三次, 分别给三个按钮绑定点击事件
for (var i = 0; i < buttons.length; i++) {
buttons[i].onclick = (function (i){
return function(){
//这个内层函数就是闭包函数, 可以访问外层函数的变量( 这个变量会永远保留在计算机内存中 )
console.log(i);
}
})(i)
}
</script>
二.事件防抖
事件抖动:一般情况下在网页中, 当同一个事件被频繁多次触发, 就会产生抖动. 最终出现页面频繁弹框, 频繁显示/隐藏等等. 可以借助闭包实现 事件防抖 , 最终可以实现同一事件连续多次触发结束后只处理一次(最后一次).
<input type="text" oninput="newhandleInput()">
//事件防抖:先创建一个函数,其内部打印要输出的数据
function handleInput() {
console.log('我要防抖');
}
//构建一个闭包函数,实现防抖的目的:为防止一直点击而使得服务器一直处于访问状态导致崩溃,
//我们需要让它在一段事件内无论点击多少次,想要的数据只执行一次
//定义一个防抖函数debounce
function debounce(hand, time) {
//设置外部函数的参数:要实现事件防抖,需要用到定时器,所以在外层函数中创建一个定时器,将它的值设置为空
var timerid = null
//返回一个内部函数
return function () {
//先进行条件判断,如果定时器存在,我们就销毁定时器,不存在则创建一个定时器
//当函数执行时,定时器为空,创建一个定时器,当再次点击时进行判断,定时器存在,将他销毁,然后再判断,再销毁,
//注意这儿不能写else,不然它只会销毁不会创建
//循环往复,使定时器中的内容在所设置的时间内只执行一遍
//此时定时器的值为空,我们建立一个定时器,放入我们想执行的函数,即调用handinput函数
if (timerid) {
clearTimeout(timerid)
}
timerid = setTimeout(() => {
hand()
}, time)
}
}
//将debounce函数调用,并将它的值保存在一个新函数里
var newhandleInput = debounce(handleInput, 1000)
事件节流:
如果原来是1s内连续执行 100次, 现在做了事件节流, 每隔0.1s执行一次, 1s内执行10次
事件节流, 将短时间内同一事件连续多次执行, 变为每隔一段时间执行一次.
<input type="text" oninput="newhandleInput()">
//创建一个函数,内部为我们想要执行的代码
function handleInput(){
console.log('我要节流');
}
//创建一个节流函数throttle
function throttle(hand,time){
//写外部函数的参数创建一个空的定时器
var timerid=null
//闭包函数
return function(){
//先进行条件判断,当定时器不存在时 ,执行要输出的语句,执行完立马销毁
//并将其设置为空,因为是在定时器所限定的时间内,所以在定时器时间到的这段时间
//会反复进行判断,当定时结束,才会执行一次
if(!timerid){
timerid=setTimeout(()=>{
hand()
//执行完立即销毁定时器
clearTimeout(timerid)
//将定时器置为空
timerid=null
},time)
}
}
}
var newhandleInput=throttle(handleInput,100)
柯里化函数:
// function add(a,b,c){
// console.log(a+b+c);
// }
function add(a){
return function(b){
return function(c){
console.log(a+b+c);
}
}
}
//函数的普通调用方式
//add(10,20,30)
//柯里化函数
add(10)(20)(30)
三.继承
构造函数 实现面向对象的写法
class 实现面向对象的写法
构造函数 或 class的写法 目的都是为了完成已有代码的封装(面向对象程序的 第一特性).
继承(面向对象程序的 第二特性)
实现继承的方法一: 构造函数+call
function Father(){
this.height = 199;
this.color = 'yellow';
this.money = 9999999;
this.run = function(){
console.log('running');
}
}
function Son(){
//构造函数继承( 构造函数+call )
Father.call(this);
//继承了Father的所有属性和方法
}
var s = new Son();
s.run();
//s.run() 之所以能调用成功,是因为 Son这个构造函数继承了Father这个构造函数的所有属性和方法
var s=new Son()
console.log(s.height);
通过更改Son的原型对象, 更改为Father的实例对象, 实现了通过原型链继承Father的属性。
function Father(){
this.height = 199;
this.color = 'yellow';
this.money = 9999999;
this.run = function(){
console.log('running');
}
}
function Son(){}
Son.prototype = new Father();
var s1 = new Son();
var s2 = new Son();
var s3 = new Son();
s.run();
//s.run() 之所以能调用成功,是因为 Son这个构造函数继承了Father这个构造函数的所有属性和方法
console.log(s.height,s.color,s.money);
console.log(s1,s2,s3);
组合继承: 同时使用构造函数继承( 继承数据属性 ),原型链继承( 继承方法 )
function Father(){
this.height = 199;
this.color = 'yellow';
this.money = 9999999;
this.run = function(){
console.log('running');
}
}
//构造函数继承
function Son(){
Father.call(this);
}
//将方法保存到原型对象身上, 使用Son构造函数创建的所有实例对象 都可以访问到该方法, 但是该方法只保存了一份(在原型对象身上)
//原型链继承
Son.prototype.run=function(){
run()
}
// Son.prototype.run = function(){ console.log('running'); }
var s1 = new Son()
var s2 = new Son()
var s3 = new Son()
console.log(s1,s2,s3,s1.run);
class继承, 通过extends 关键词实现继承
class Father {
constructor(){
this.height = 199;
this.color = 'yellow';
this.money = 9999999;
}
run(){
console.log('running');
}
}
//class继承, 通过extends 关键词实现继承
class Son extends Father {}
var s = new Son()
console.log(s.height,s.run);