JavaScript第四周总结

154 阅读7分钟

1、animate.css文件

是一个动画库:放着很多很多的动画。

为了我们程序员方便,怕我们自己画的丑。 如何使用:

  • 1、打开百度:搜索animate.css 得到网址
    www.animate.net.cn/
    animate.style/
  • 2、下载 - 你们不用做这个操作
  • 3、引入此文件
  • 4、挑选你喜欢的动画,把class放到那个元素上
  • 5、并且要记得设置上animation-duration:3s; 执行时长
  • 6、还需要根据不同的动画,设置不同的初始效果,才会更好看

2、滚动轮播:

如果你会淡入淡出轮播,其实差不多,布局不同,动画方式不同

 var divImg=document.querySelector('.div_img'),
        imgs=document.querySelectorAll('.div_img>img'),
        btns=document.querySelectorAll('button'),
        lis=document.querySelectorAll('li');
        var j=0;
        judge(j);
        btns.forEach(btn=>{
            btn.onclick=function(){
                if(this.innerText=='>'){
                    j++;
                    if(j==lis.length){j=0};
                    setImg(j)
                }else{
                    j--;
                    if(j==-1){j=lis.length-1};
                    setImg(j)
                }
                judge(j);
            }
        })
        lis.forEach((li,i)=>{
            li.onclick=function(){
                setImg(i)
                j=i;
                judge(j);
            }
        })

        function setImg(a){
            divImg.style.marginLeft = a * (-100) + '%';
            lis.forEach(li => li.className = '');
            lis[a].className = 'active';
        }

        function judge(k) {
            if (k == 0) {
                btns[0].setAttribute('disabled', 'disabled')
            } else {
                btns[0].removeAttribute('disabled')
            }
            if (k == 2) {
                btns[1].setAttribute('disabled', 'disabled')
            } else {
                btns[1].removeAttribute('disabled');
            }
        }

3、无缝轮播:

var divImg = document.querySelector('.divImg'),
    lis = document.querySelectorAll('li'),
    btns = document.querySelectorAll('button'),
    srcs = ['../img/1.jpg', '../img/2.jpg', '../img/3.jpg'],
    state=0,
    j = 0;
btns.forEach(btn => {
    btn.onclick = () => {
        if (state == 0) {
            state = 1;
            if (btn.innerText == '>') {
                j++;
                j == lis.length && (j = 0);
                createImg(j, -1);
            } else {
                j--;
                j == -1 && (j = lis.length - 1);
                createImg(j, 1);
            }
        }
    }
})
lis.forEach((li,i) => {
    li.onclick = () => {
        console.log(i);
        if (state == 0) {
            state = 1;
            j = i;
            var oldLi = document.querySelector('li.active'),
                oldj = oldLi.getAttribute('tc');
            if (j > oldj) {
                createImg(j, -1);
            } else if (j < oldj) {
                createImg(j, 1);
            }
        }
    }
})

setInterval(() => {
    if (state == 0) {
        j++;
        j == lis.length && (j = 0);
        createImg(j, -1);
    }

},2000)


function createImg(k,a) {
    var img = new Image();
    if (a==-1) {
        img.className = 'rimg';
    } else {
        img.className = 'limg';
    }
    
    img.src = srcs[k];
    divImg.appendChild(img);

    var imgs = document.querySelectorAll('img');
    img.onload = () => {
        imgs[0].style.left = a * 100 + '%';
        imgs[1].style.left = '0%';

        setTimeout(() => {
            imgs[0].remove();
            state = 0;
        }, 1000);
    }
    lis.forEach(li => li.className = '');
    lis[k].className = 'active';
}

4、swiper插件:

专门的一个轮播插件,提供了你HTML/CSS/JS,我们只需要复制。

1、打开百度:搜索swiper

2、选你自己喜欢的

5、封装一个运动(动画)函数

btn.onclick = () => {
    //div.style.transition="1s";
    //div.style.width="600px";
    //div.style.height="600px";
    //div.style.background="green";
    //div.style.borderRadius="50%";
    //div.style.border="20px solid blue";
    //div.style.transform="rotateY(360deg)";

    move(div, {
        "transition": "1s",
        "width": "600px",
        "height": "600px",
        "background": "green",
        "borderRadius": "50%",
        "border": "20px solid blue",
        "transform": "rotateY(360deg)",
    })
}

function move(elem, obj) {
    for (var i in obj) {
        elem.style[i] = obj[i];
    }
}

Object:对象

Array/String/RegExp/Date... 对象具有属性和方法,都是预定义好的,现在我们可以学习自定义对象 - js是基于原型的面向对象语言

面向对象 - 三大特点:封装、继承、多态

1、开发方式:

面向过程:过程 - 开始->结束,其实我们一致的开发方式都是面向过程:先干什么再干什么最后干什么

面向对象:对象(属性和方法),js有一句话万物皆对象

何时使用面向对象:以后任何操作都要封装在一个对象之中 - 但是新手并不太推荐,难度较大

为什么要面向对象:现实生活中所有的数据都必须包含在一个事物之中才有意义

2、封装/创建/定义:

1、封装自定义对象: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、判断是自有还是共有:

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.属性名;

共有:

修改:原型对象.属性名=新属性值;//千万不要觉得,自己能拿到,就能直接修改,这样很危险,并没有修改原型的东西,而是在本地添加了一个同名属性
删除: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]"

5、实现自定义继承:

1、两个对象之间设置继承

子对象.proto=父对象

2、多个对象之间设置继承

构造函数名.prototype=父对象;
时机:应该在开始创建对象之前就设置好继承关系

class关键字:

简化面向对象(封装、继承、多态)

class 类名 extends 老类{
	constructor(name,age,hobby,...){//放在constructor里面的都是自有属性
		super(name,age);
		this.hobby=hobby;
	}//放在constructor外面的都是共有方法
	//还会继承到老类所有的API,也可以添加新的
}

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自动释放,局部变量也就释放了

闭包:

希望保护一个可以【反复使用的局部变量】的一种词法结构,其实还是一个函数,只是写法比较特殊

何时使用:希望保护一个可以【反复使用的局部变量】的时候

如何使用: 1、两个函数进行嵌套 2、外层函数创建出受保护的变量 3、外层函数return出内层函数 4、内层函数再操作受保护的变量

强调:
1、判断是不是闭包:有没有两个函数嵌套,返回了内层函数,内层函数再操作受保护的变量
2、外层函数调用了几次,就创建了几个闭包,受保护的变量就有了几个副本
3、同一次外层函数调用,返回的内层函数,都是在操作同一个受保护的变量

缺点:受保护的变量,永远都不会被释放,使用过多,会导致内存泄漏 - 不可多用

问题:应该在哪里去使用呢? 1、三个事件需要防抖节流 - 共同点:触发的速度飞快
1、elem.onmousemove - 鼠标移动事件
2、input.oninput - 每次输入/改变都会触发
3、onresize - 每次窗口改变大小都会触发

防抖节流的公式:

		function fdjl(){
			var timer=null;
			return function(){
				if(timer!==null){clearTimeout(timer);timer=null;}
				timer=setTimeout(()=>{
					操作
				},500)
			}
		}
		
		var inner=fdjl();

		elem.on事件名=function(){
			inner();
		}

总结:

两链一包:

  • 1、作用域链:
    以函数的EC的scope chain属性为起点,经过AO,逐级引用,形成的一条链式结构,我们就称之为叫做作用域链
    作用:查找变量,带来了变量的使用规则:优先使用自己的,自己没有找全局,全局没有就报错

  • 2、原型链:
    每个对象都有一个属性叫做.proto,可以一层一层的找到每个对象的原型对象,最顶层的就是Object的原型,形成的一条链式结构,我们就称之为叫做原型链
    作用:查找属性和方法,哪怕自己没有也会顺着原型链向上找,怪不得人人都能用toString(),因为他在最顶层

  • 3、闭包: 希望保护一个可以【反复使用的局部变量】的一种词法结构,其实还是一个函数,只是写法比较特殊
    作用:专门用于防抖节流