day1
轮播--后面可以用插件SWIPER
滚动轮播:(难)
找到每一个按钮,小圆点,图片以及计数器
var btns = document.querySelectorAll("button"),
lis = document.querySelectorAll("li"),
car_img = document.querySelectorAll(".carousel_img")[0],
j = 0;
封装的函数
ban();
点击左右的按钮,判断是左边还是右边的按钮
btns.forEach(btn => {
用事件委托
btn.onclick = e => {
var t = e.target;
如果是右边就让点击到最大值的时候 ,计数器设置为0
if(t.innerText == ">") {
animate(1);
} else {
animate(-1);
}
ban()
}
})
遍历每一个小圆点,并传入实参
lis.forEach((li, i) => {
li.onclick = () => {
animate(0,i)
ban(i)
}
})
设置动画,
function animate(num,x){
if(num){
j+=num;
if(j==lis.length){
j=0;
}else if(j==-1){
j=lis.length-1;
}
}else{
j=x;
}
lis.forEach(li=>li.className="");
car_img.style.marginLeft=j*-100+"%";
lis[j].className="active";
}
小圆点设置配合拿到下标
function ban() {
if(j == 0) {
btns[0].setAttribute("disabled", "disabled");
} else {
btns[0].removeAttribute("disabled");
}
if(j == lis.length - 1) {
btns[1].setAttribute("disabled", "disabled");
} else {
btns[1].removeAttribute("disabled");
}
}
无缝轮播:思路
var btns=document.querySelectorAll("button"),
lis=document.querySelectorAll("li"),
car_img=document.querySelector(".carousel_img"),
srcs=["img/1.jpg","img/2.jpg","img/3.jpg"],
设置了初始状态为0
state=0,
j=0;
遍历每一个按钮
btns.forEach(btn=>{
事件委托
btn.onclick=e=>{
if(state==0){
state=1;
console.log(1);
用一个变量保存下事件委托
var t=e.target;
按钮右边情况
if(t.innerText==">"){
j++;
j==lis.length&&(j=0);
var img=new Image();
img.src=srcs[j];
img.className="rimg";
car_img.appendChild(img);
var imgs=document.querySelectorAll("img");
图片加载时候
img.onload=()=>{
右边图片从右边出
imgs[0].style.left="-100%";
imgs[1].style.left="0%";
setTimeout(()=>{
imgs[0].remove();
state=0;
},1000)
}
lis.forEach(li=>li.className="");
lis[j].className="active";
}else{
j--;
j==-1&&(j=lis.length-1);
创建树
var img=new Image();
img.src=srcs[j];
img.className="limg";
car_img.appendChild(img);//追加到末尾
var imgs=document.querySelectorAll("img");
img.onload=()=>{
imgs[0].style.left="100%";
imgs[1].style.left="0%";
setTimeout(()=>{
imgs[0].remove();
state=0;
},1000)
}
lis.forEach(li=>li.className="");
lis[j].className="active";
}
}
}
})
lis.forEach((li,i)=>{
li.onclick=()=>{
if(state==0){
state=1;
j=i;
var Oldli=document.querySelector("li.active");
var Oldj=Oldli.getAttribute("dy");
if(j>Oldj){
var img=new Image();
img.src=srcs[j];
img.className="rimg";
car_img.appendChild(img);
var imgs=document.querySelectorAll("img");
img.onload=()=>{
imgs[0].style.left="-100%";
imgs[1].style.left="0%";
setTimeout(()=>{
imgs[0].remove();
state=0;
},1000)
}
lis.forEach(li=>li.className="");
lis[j].className="active";
}else if(j<Oldj){
var img=new Image();
img.src=srcs[j];
img.className="limg";
car_img.appendChild(img);//追加到末尾
var imgs=document.querySelectorAll("img");
img.onload=()=>{
imgs[0].style.left="100%";
imgs[1].style.left="0%";
setTimeout(()=>{
imgs[0].remove();
state=0;
},1000)
}
lis.forEach(li=>li.className="");
lis[j].className="active";
}
}
}
})
1、animate.css文件是一个动画库:放着很多很多的动画。 为了我们程序员方便,怕我们自己画的丑。 如何使用: 1、打开百度:搜索animate.css 得到网址 www.animate.net.cn/ animate.style/ 2、下载 - 你们不用做这个操作 3、引入此文件 4、挑选你喜欢的动画,把class放到那个元素上 5、并且要记得设置上animation-duration:3s; 执行时长 6、还需要根据不同的动画,设置不同的初始效果,才会更好看
2、滚动轮播:如果你会淡入淡出轮播,其实差不多,布局不同,动画方式不同 详见,02.html
3、无缝轮播: 详见,03.html
4、swiper插件:专门的一个轮播插件,提供了你HTML/CSS/JS,我们只需要复制。 1、打开百度:搜索swiper 2、选你自己喜欢的
5、封装一个运动(动画)函数 运动函数---注意写法
btn.onclick = () => {
// div.style.transition="1s";
move(div, {
"transition": "1s",
"width": "600px",
"height": "600px",
"background": "pink",
"borderRadius": "50%",
"border": "10px solid black",
"transform": "rotateZ(360deg)",
"transition": "1s",
})
}
function move(elem, obj) {
// 对象的底层是哈希数组只能用for In
for (var i in obj) {
elem.style[i] = obj[i];
}
}
day2
1、*****Object:对象 - Array/String/RegExp/Date... 对象具有属性和方法,都是预定义好的,现在我们可以学习自定义对象 - js是基于原型的面向对象语言 面向对象 - 三大特点:封装、继承、多态 1、***开发方式: 面向过程:过程 - 开始->结束,其实我们一致的开发方式都是面向过程:先干什么再干什么最后干什么 面向对象:对象(属性和方法),js有一句话万物皆对象,假设一个人是一个对象的话: 属性:身高、体重、姓名、性别、爱好、智商、情商... 方法:吃饭、睡觉、跑步、拉粑粑、打字、上课...
何时使用面向对象:以后任何操作都要封装在一个对象之中 - 但是新手并不太推荐,难度较大
为什么要面向对象:现实生活中所有的数据都必须包含在一个事物之中才有意义
2、*****封装/创建/定义:封装自定义对象:3种
1、*直接量方式:
var obj={
"属性名":属性值,
...,
"方法名":function(){操作},//可以简化为箭头函数
...
}
强调:
1、其实属性名和方法名的""可以不加 - 暂时建议你加上,以后我们要学习一个数据格式JSON,他必须在键上加上""
2、访问对象的属性和方法:
obj.属性名; === obj["属性名"];
obj.方法名(); === obj["方法名"]();
建议使用.去访问对象的属性和方法,更简单
***js中一切都是对象,除了undefined和null,一切对象的底层都是hash数组
3、访问到不存在的属性,返回undefined
4、可以随时随地的添加新属性和新方法
obj.属性名=新值;
obj.方法名=function(){};
5、如果我希望遍历出对象所有的东西,必须使用for in,obj[i]才能拿到,不要使用.会出问题
6、***如果你希望在对象的方法里,使用对象自己的属性,写为this.属性名
*****难点:this的指向:
1、单个元素绑定事件,this->这个元素
2、多个元素绑定事件,this->当前元素
3、定时器中的this->window
4、箭头函数this->外部对象
5、函数中this->谁在调用此方法,this就是谁
6、构造函数之中this->当前正在创建的对象
2、预定义构造函数方式:
var obj=new Object();//空对象
//需要自己后续慢慢添加属性和方法
obj.属性名=新值;
obj.方法名=function(){};
以上两个都有一个缺陷:一次只能创建一个对象,适合创建单个元素的时候(第一种方法),第二种方法完全是垃圾,如果你要批量创建多个对象,那么我推荐第三种方法
3、自定义构造函数方式:2步
1、创建自定义构造函数
function 类名(name,age,hobby){
this.name=name;
this.age=age;
this.hobby=hobby;
}
//千万不要在里面创建方法,每个对象都会创建出一个相同的方法,浪费内存 - 学习继承后可以解决
2、调用构造函数创建对象
var obj=new 类名(实参,...);
面向对象
优点:
1、逼格高,所有的属性和方法都保存在一个对象之中 - 更符合现实更有意义
2、每个功能特地分开写 - 便于以后维护
3、铁锁链舟 - 一个方法触发多个方法联动
缺点:对新手不友好,尤其是this的指向问题
2、*****继承:父对象的成员(属性和方法),子对象可以直接使用
为什么要继承:代码重用!提高代码的复用性,节约了内存空间!提升了网站的性能!
何时继承:只要多个子对象公用的属性和【方法】,都要集中定义在父对象之中
1、***如何找到原型对象(父对象):保存了一类子对象共有属性和共有方法
1、对象名.__proto__; //必须先有一个对象
2、构造函数名.prototype;//构造函数名几乎人人都有,除了Math和Window,new 构造函数名();//Array、String、Date、RegExp...
2、*面试题:两链一包:作用域链和【原型链】和闭包
每个对象都有一个属性:__proto__,可以一层一层的找到每个人的父亲,形成的链式结构,就称之为叫做原型链
可以找到父对象的成员(属性和方法),作用:找共有属性和共有方法
最顶层的是Object的原型,上面放着一个我们眼熟的方法toString,怪不得人人都可以使用toString
JS万物皆对象
3、有了原型对象,可以设置共有属性和共有方法
1、原型对象.属性名=属性值;
2、原型对象.方法名=function(){}
***继承具有非常非常多的鄙视/面试题,明天再学
1.*****开关门的例题:注意面向对象和封装的写法。注意this的指向。
// ---------------面向对象版本------------
// 创建函数(找到里面的按钮和div
function door(parent) {
找到里面的属性
this.btn = parent.querySelector("button");
this.div = parent.querySelector("div");
}
door.prototype.init = function () {
this.bind();
}
door.prototype.bind = function () {
// 这里输出是door
console.log(this)
// var me = this;
// console.log(me)
// 注意:这里换了箭头函数,在下面一定要用this.btn来作为动画的实参
// 写法1(箭头函数的this指的是外部的)
this.btn.onclick = () => {
// 输出的是按钮
console.log(this);
this.animate(this.btn);
console.log(this)
console.log(this.btn);
// 这里输出的内容是door
// console.log(me)
}
// 写法2
// var me=this;
// this.btn.onclick=function(){
// this.animate(this)
// }
}
door.prototype.animate = function (btn) {
// 带隐式转换
// (在判断条件里面,浏览器会悄悄将颜色的功能转换会自动识别rgba的写法)
if (btn.style.background == "blue") {
this.div.style.display = "block";
btn.style.background = "tomato";
} else {
this.div.style.display = "none";
btn.style.background = "blue";
}
}
var door = new door(sec);
door.init()
// ----------------------------封装版本--------------
// var door = {
// // 跟以前一样做法,找到每一个按钮,div
// "btn": document.getElementsByTagName("button")[0],
// "div": document.getElementsByTagName("div")[0],
// "init": function () {
// // 初始化
// this.bind();
// },
// "bind": function () {
// var me = this;
// me.btn.onclick = function () {
// me.animate(this);
// }
// },
// "animate": function (btn) {
// if (btn.style.background == "rgb(255, 255, 255)") {
// this.div.style.display = "block";
// btn.style.background = "gray";
// } else {
// this.div.style.display = "none";
// btn.style.background = "#fff";
// }
// }
// }
// door.init()
// --------------------------最初版本-----------
// var btn=document.getElementsByTagName("button")[0];
// btn.onclick=function(){
// var div=document.getElementsByTagName("div")[0];
//
// if(this.style.background=="rgb(255, 255, 255)"){
// div.style.display="block";
// this.style.background="gray";
// }else{
// div.style.display="none";
// this.style.background="#fff";
// }
//
//// if(btn.className=="d1"){
//// div.style.display="block";
//// btn.className="d2"
//// }else{
//// div.style.display="none";
//// btn.className="d1"
//// }
//
// //判断文本/内容完成的
//// if(btn.innerHTML=="开"){
//// div.style.display="block";
//// btn.innerHTML="关";
//// }else{
//// div.style.display="none";
//// btn.innerHTML="开";
//// }
// }
选项卡的基础版本,封装版本,面向对象版本 注意:面向对象版本(用的等号)和封装版本(用冒号的写法)
// ------------面向对象-----------------
function carousel(parent) {
this.parent = parent;
this.btns = parent.querySelectorAll("button");
this.lis = parent.querySelectorAll("li");
this.imgs = parent.querySelectorAll("img");
this.j = 0;
this.timer = null;
}
// carousel.prototype.init = function () {
// this.bind();
// this.setInterval();
// };
carousel.prototype.init = function () {
this.bind();
this.setInterval();
}
carousel.prototype.bind = function () {
this.btns.forEach(btn => {
// 用事件委托对其进行绑定事件
btn.onclick = e => {
if (e.target.innerText == ">") {
this.animate(1);
} else {
this.animate(-1);
}
}
})
this.lis.forEach((li, i) => {
li.onclick = e => {
this.animate(0, 1);
}
})
this.parent.onmouseover = () => {
this.clearInterval()
}
this.parent.onmouserout = () => {
this.setInterval();
}
}
carousel.prototype.animate = function (num, i) {
this.clear();
if (num) {
this.j += num
if (this.j == this.lis.length) {
this.j = 0
} else if (this.j == -1) {
this.j = this.lis.length - 1;
}
} else {
this.j = i;
}
this.imgs[this.j].className = "active";
this.lis[this.j].className = "active";
}
carousel.prototype.clear = function (num, i) {
this.lis.forEach((li, i) => {
li.className = "";
this.imgs[i].className = "";
})
}
carousel.prototype.setInterval = function (num, i) {
this.timer = setInterval(() => {
this.animate(1);
}, 1000)
}
carousel.prototype.clearInterval = function (num, i) {
clearInterval(this.timer);
}
var carousel = new carousel(car);
carousel.init()
// ----------------------封装函数-----------
// var carousel = {
// // 找到所有的元素。将原来的等号换为冒号,并且属性名是可以省略的,方便以后学json的时候一起记忆
// "parent": document.querySelector("#car"),
// "btns": document.querySelectorAll("#car button"),
// "lis": document.querySelectorAll("#car li"),
// "j": 0,
// "timer": null,
// "imgs": document.querySelectorAll("#car img"),
// // 初始化,是一打开页面就有的东西
// // 这里不能换位箭头,不然是window
// "init": function () {
// // 对其进行绑定事件
// this.bind();
// },
// // 注意每一个对象用逗号隔开
// "bind": function () {
// // 遍历每一个按钮
// this.btns.forEach(btn => {
// // 用事件委托对其进行绑定事件
// btn.onclick = e => {
// // 目标元素
// // 封装的函数
// if (e.target.innerText == ">") {
// this.animate(1);
// } else {
// this.animate(-1);
// }
// }
// })
// // 小圆点
// this.lis.forEach((li, i) => {
// li.onclick = e => {
// this.animate(0, 1);
// }
// })
// // 鼠标移入移出可以最后来做
// this.parent.onmouseover = () => {
// this.clearInterval()
// }
// this.parent.onmouserout = () => {
// this.setInterval();
// }
// },
// // 因为前面调用了动画,需要创建动画
// animate: function (num, i) {
// // 清空动画
// this.clear();
// // 左右键获得的j和小圆点获得的下标
// if (num) {
// this.j += num
// if (this.j == this.lis.length) {
// this.j = 0
// } else if (this.j == -1) {
// this.j = this.lis.length - 1;
// }
// } else {
// this.j = i;
// }
// this.imgs[this.j].className = "active";
// this.lis[this.j].className = "active";
// },
// // 创建清空的对象
// clear: function () {
// this.lis.forEach((li, i) => {
// li.className = "";
// this.imgs[i].className = "";
// })
// },
// // 定时器也可以最后来做
// setInterval: function () {
// this.timer = setInterval(() => {
// this.animate(1);
// }, 1000)
// },
// clearInterval: function () {
// clearInterval(this.timer);
// }
// }
// // 创建对象记得调用对象
// carousel.init();
// ----------------------最初版本的淡入淡出轮播----------------
淡入淡出轮播
// //找到了2个按钮和3个li和4张图片,以及创建了一个计数器j(记录着我们当前应该是那张图那个li激活)
// var btns = document.getElementsByTagName("button"),
// lis = document.getElementsByTagName("li"), //[li,li,li]
// imgs = document.getElementsByTagName("img"), //[img,img,img,img]
// j = 0; //计数器
// //循环
// for(var i in btns) {
// //得到每个按钮绑定事件
// btns[i].onclick = function() {
// //判断出左右
// if(this.innerText == ">") {//右
// //每次点击计数器j都会+1
// j++;
// //但是如果超过了最大下标,就让他回到最小下标0
// if(j == lis.length) {
// j = 0
// }
// //先清楚掉所有的li和img的class
// for(var i = 0; i < lis.length; i++) {
// lis[i].className = "";
// imgs[i].className = "";
// }
// //通过计数器让对应的图片和li激活
// imgs[j].className = "active";
// lis[j].className = "active";
// } else {//左
// //每次点击计数器j都会-1
// j--;
// //但是如果超过了最小下标,就让他回到最大下标lis.length-1
// if(j == -1) {
// j = lis.length - 1
// }
// //先清楚掉所有的li和img的class
// for(var i = 0; i < lis.length; i++) {
// lis[i].className = "";
// imgs[i].className = "";
// }
// //通过计数器让对应的图片和li激活
// imgs[j].className = "active";
// lis[j].className = "active";
// }
// }
// }
// //循环
// for(var i in lis) {
// //为每一个li绑定点击事件
// lis[i].onclick = function() {
// //li获取计数器j的方式和左右按钮都不一样,是直接获取自定义属性dy,做法跟选项卡的做法是一样的
// j = this.getAttribute("dy"); //"1"
// //先清楚掉所有的li和img的class
// for(var i = 0; i < lis.length; i++) {
// lis[i].className = "";
// imgs[i].className = "";
// }
// //通过计数器让对应的图片和li激活
// imgs[j].className = "active";
// lis[j].className = "active";
// }
// }
//
// timer = setInterval(function() {
// j++;
// if(j == lis.length) {
// j = 0
// }
//
// for(var i = 0; i < lis.length; i++) {
// lis[i].className = "";
// imgs[i].className = "";
// }
//
// imgs[j].className = "active";
// lis[j].className = "active";
// }, 1000)
//
// car.onmouseover = function() {
// clearInterval(timer);
// }
//
// car.onmouseout = function() {
// timer = setInterval(function() {
// j++;
// if(j == lis.length) {
// j = 0
// }
//
// for(var i = 0; i < lis.length; i++) {
// lis[i].className = "";
// imgs[i].className = "";
// }
//
// imgs[j].className = "active";
// lis[j].className = "active";
// }, 1000)
// }
day3
面向对象: 1、*****继承具有很多的面试笔试题: 1、判断是自有还是共有: 1、判断自有:obj.hasOwnProperty("属性名"); 如果结果为true,说明是自有属性,如果结果为false,有两种可能,说明可能是共有,也可能是没有 2、判断共有: if(obj.hasOwnProperty("属性名")==false&&"属性名" in obj){//in关键字,会自动查找整条原型链上的属性,找到了结果为true,找不到结果为false 共有 }else{ 没有 }
完整公式:
if(obj.hasOwnProperty("属性名")){
自有
}else{
if("属性名" in obj){
共有
}else{
没有
}
}
2、修改和删除:自有和共有
自有:
修改:obj.属性名=新属性值;
删除:delete obj.属性名;
共有:
修改:原型对象.属性名=新属性值;//千万不要觉得,自己能拿到,就能直接修改,这样很危险,并没有修改原型的东西,而是在本地添加了一个同名属性
比如:某某某.__proto__=新值;
删除:delete 原型对象.属性名;//如果你对着本地直接删除,那么此操作直接无效
3、*如何为老IE的数组添加indexOf方法:- 这道题不是固定的:可能问如何为一类人创建某个方法
if(Array.prototype.indexOf===undefined){//老IE
Array.prototype.indexOf=function(key,starti){
starti===undefined&&(starti=0);
for(var i=starti;i<this.length;i++){
if(this[i]===key){
return i;
}
}
return -1;
}
}
var arr1=[1,2,3,4,5];
var arr2=[2,4,6,8,10];
4、*如何判断x是不是一个数组:4种方法:千万别用typeof(),只能检查原始类型,不能检查引用类型,如果检查引用类型得到的结果都是一个object。
1、判断x是否继承自Array.prototype:
Array.prototype.isPrototypeOf(x);
结果为true,说明是数组,结果为false,说明不是数组
2、判断x是不是由Array这个构造函数创建的
x instanceof Array;
结果为true,说明是数组,结果为false,说明不是数组
3、Array.isArray(x); - ES5新增的方法,只有数组可以这么使用
结果为true,说明是数组,结果为false,说明不是数组
4、*输出【对象的字符串】形式
在Object的原型上保存着最原始的toString方法
原始的toString输出形式:[object 构造函数名]
***多态:子对象觉得父对象的成员不好用,就在本地定义了同名函数,覆盖了父对象的成员,不严格定义:同一个方法,不同的人使用,效果不同,有多种形态
固定套路:
Object.prototype.toString.call(x)==="[object Array]"
*** 新版本面向对象的例题:注意:有一个class关键字可以省略function,constructor放在它里面的是自有的属性,放在她外面的是共有的属性和方法。 extends表示继承的作,super()。
class flyer{
constructor(name,speed){//放在constructor里面的都是自有属性
this.name=name;
this.speed=speed;
}//放在constructor外面的都是共有方法
fly(){
return `${this.name}正在以时速${this.speed}飞行`;
}
}
//创建了一个飞机类
class plane extends flyer{//extends有两个作用:1、继承自的自有属性全部都不 用再写,只需要写一次super(),2、自动继承到共有方法
constructor(name,speed,rl){//放在constructor里面的都是自有属性
super(name,speed);
this.rl=rl;
}
fly(){
return `${this.name}正在以时速${this.speed}飞行,可以容纳 ${this.rl}人`;
}
}
var j20=new plane("歼20",1000,1);
console.log(j20);
console.log(j20.fly());
var bird=new flyer("钟清翰",10);
console.log(bird);
console.log(bird.fly());
5、实现自定义继承:
1、两个对象之间设置继承
子对象.__proto__=父对象
2、多个对象之间设置继承
构造函数名.prototype=父对象;
时机:应该在开始创建对象之前就设置好继承关系
2、class关键字:简化面向对象(封装、继承、多态) class 类名 extends 老类{ constructor(name,age,hobby,...){//放在constructor里面的都是自有属性 super(name,age); this.hobby=hobby; }//放在constructor外面的都是共有方法 //还会继承到老类所有的API,也可以添加新的 }
3、*****Function:闭包 作用域:2种 1、全局:随处可用,可以反复使用,缺点:容易被污染 2、函数:只能在函数调用时内部可用,不会被污染,缺点:一次性的,是会自动释放的
***函数的执行原理:
1、程序加载时
创建执行环境栈(ECS):保存函数调用顺序的数组
首先压入全局执行环境(全局EC)
全局EC引用着全局对象window
window中保存着我们全局变量
2、定义函数时
创建函数对象:封装代码段
在函数对象之中有一个scope(作用域)属性:记录着函数来自己的作用域是哪里
全局函数的scope都是window
3、调用前
在执行环境栈(ECS)压入新的EC(函数的EC)
创建出活动对象(AO):保存着本次函数调用时用到的局部变量
在函数的EC中有一个scope chain(作用域链)属性引用着AO
AO有一个parent属性是函数的scope引用着的对象
4、调用时:
正是因为有前面三步,才来带变量的使用规则:优先使用自己的,自己没有找全局,全局没有就报错
5、调用完:
函数的EC会出栈,没人引用AO,AO自动释放,局部变量也就释放了
执行原理图片
*****闭包:
希望保护一个可以【反复使用的局部变量】的一种词法结构,其实还是一个函数,只是写法比较特殊
何时使用:希望保护一个可以【反复使用的局部变量】的时候
function factory(){
var i=0;
return function(){
i++;
return i;
}
}
var icbc=factory();
var abc=factory();
如何使用:
1、两个函数进行嵌套
2、外层函数创建出受保护的变量
3、外层函数return出内层函数
4、内层函数再操作受保护的变量
强调:
1、判断是不是闭包:有没有两个函数嵌套,返回了内层函数,内层函数再操作受保护的变量
2、外层函数调用了几次,就创建了几个闭包,受保护的变量就有了几个副本
3、同一次外层函数调用,返回的内层函数,都是在操作同一个受保护的变量
缺点:受保护的变量,永远都不会被释放,使用过多,会导致内存泄漏 - 不可多用
问题:应该在哪里去使用呢?
1、三个事件需要防抖节流 - 共同点:触发的速度飞快
1、elem.onmousemove - 鼠标移动事件
2、input.oninput - 每次输入/改变都会触发
3、onresize - 每次窗口改变大小都会触发
防抖节流的公式:
elem.on事件名(至少三个事件名)=function(){
inner();}
function fdjl(){
var timer=null;
return function(){
if(timer!==null){clearTimeout(timer);timer=null;}
timer=setTimeout(()=>{
操作
},500)
}
}
var inner=fdjl();
总结:
两链一包:
1、作用域链:以函数的EC的scope chain属性为起点,经过AO,逐级引用,形成的一条链式结构,我们就称之为叫做作用域链
作用:查找变量,带来了变量的使用规则:优先使用自己的,自己没有找全局,全局没有就报错
2、原型链:每个对象都有一个属性叫做.__proto__,可以一层一层的找到每个对象的原型对象,最顶层的就是Object的原型,形成的一条链式结构,我们就称之为叫做原型链
作用:查找属性和方法,哪怕自己没有也会顺着原型链向上找,怪不得人人都能用toString(),因为他在最顶层
3、闭包:希望保护一个可以【反复使用的局部变量】的一种词法结构,其实还是一个函数,只是写法比较特殊
作用:专门用于防抖节流