js第四周总结

110 阅读8分钟

animate.css文件,动画库

使用:搜索animate.css得到网址:www.animate.net.cn
animate.style
下载并引入,然后挑选动画,把class名放到那个元素上,并设置上animation-duration:执行时长;根据不同的动画,设置不同的初始效果;

Object:对象

  1. Array/String/RegExp/Date..对象具有属性和方法,都是预定义好的,现在我们可以自定义对象,js是基于原型的面向对象语言;
  • 面向对象三大特点:封装,继承,多态;
  • 开发过程:开始-过程-结束,其实一致开发方式都是面向过程;面向对象:对象(属性和方法),js中万物皆对象;
  • 何时使用面向对象:以后任何操作都要封装在一个对象之中,但是新手不太推荐,难度太大;
  • 为何使用面向对象:现实生活中所有的数据都必须包含在一个事物之中才有意义; 2.封装/创建/定义:封装自定义对象(3种)
  1. 直接量方式:
			var obj={
				"属性名":属性值,
				...,
				"方法名":function(){操作},//可以简化为箭头函数
				...
			}

强调:属性名和方法名的""可以不加,但是之后学习一个数据格式JSON,必须在键上加上""
2. 访问对象的属性和方法

  • obj.属性名;===obj["属性名"];
  • obj.方法名(); === obj"方法名";
  • 建议使用.去访问对象的属性和方法,更简单
  • js中一切都是对象,除了undefined和null,一切对象的底层都是hash数组
  1. 访问到不存在的属性,返回undefined;
  2. 可以随时随地的添加新属性和新方法;
  3. 如果希望遍历出对象所有的东西,必须使用for in,obj[i]才能拿到,但是不要使用,会出问题;
  4. 如果希望在对象的方法里,使用对象自己的属性,写出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、自定义构造函数的方式:

  1. 创建自定义构造函数:
                               function 类名(name,age,hobby){
					this.name=name;
					this.age=age;
					this.hobby=hobby;
				}

千万不要在里面创建方法,每个对象都会创建出一个相同的方法,浪费内存 2. 调用构造函数创建对象:

var obj=new 类名(实参,...);

面向对象优点:

  1. 所有属性和方法都在一个对象中,更符合现实,更具有意义;
  2. 每个功能特地分开写-便于以后维护;
  3. 铁索连舟,一个方法触发多个方法联动;

缺点:对于新手不友好,尤其是this指向问题;

继承

  • 继承:父对象的成员(属性和方法),子对象可以直接使用
  • 为什么要继承:代码重用!提高代码的复用性,节约了内存空间!提升了网站的性能!
  • 何时继承:只要多个子对象公用的属性和【方法】,都要集中定义在父对象之中

1、如何找到原型对象(父对象):保存了一类子对象共有属性和共有方法

  • 对象名.proto; //必须先有一个对象
  • 构造函数名.prototype;//构造函数名几乎人人都有,除了Math和Window,new 构造函数名();//Array、String、Date、RegExp...

2、面试题:两链一包:作用域链和【原型链】和闭包

  • 每个对象都有一个属性:proto,可以一层一层的找到每个人的父亲,形成的链式结构,就称之为叫做原型链
  • 可以找到父对象的成员(属性和方法),作用:找共有属性和共有方法
  • 最顶层的是Object的原型,上面放着一个我们眼熟的方法toString,怪不得人人都可以使用toString,JS万物皆对象

3、有了原型对象,可以设置共有属性和共有方法

  • 原型对象.属性名=属性值;
  • 原型对象.方法名=function(){}

面向对象

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,说明不是数组
  1. 判断x是不是由Array这个构造函数创建的
                         x instanceof Array;
			结果为true,说明是数组,结果为false,说明不是数组
  1. Array.isArray(x);
    • ES5新增的方法,只有数组可以这么使用
  • 结果为true,说明是数组,结果为false,说明不是数组
  1. 输出【对象的字符串】形式,在Object的原型上保存着最原始的toString方法, 原始的toString输出形式:[object 构造函数名]
    多态:子对象觉得父对象的成员不好用,就在本地定义了同名函数,覆盖了父对象的成员,不严格定义:同一个方法,不同的人使用,效果不同,有多种形态
    固定套路:
Object.prototype.toString.call(x)==="[object Array]"
  1. 实现自定义继承:
    1、两个对象之间设置继承:子对象._ proto_=父对象

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

class关键字:简化面向对象(封装、继承、多态)

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

Function:闭包

作用域:两种

  1. 全局:随处可用,可以反复使用,缺点:容易被污染
  2. 函数:只能在函数调用时内部可用,不会被污染,缺点:一次性的,是会自动释放的

函数的执行原理:

  1. 程序加载时:
    创建执行环境栈(ECS):保存函数调用顺序的数组,首先压入全局执行环境(全局EC),全局EC引用着全局对象window,window中保存着我们全局变量

  2. 定义函数时:
    创建函数对象:封装代码段,在函数对象之中有一个scope(作用域)属性:记录着函数来自己的作用域是哪里,全局函数的scope都是window

  3. 调用前:

  • 在执行环境栈(ECS)压入新的EC(函数的EC)
  • 创建出活动对象(AO):保存着本次函数调用时用到的局部变量
  • 在函数的EC中有一个scope chain(作用域链)属性引用着AO
  • AO有一个parent属性是函数的scope引用着的对象
  1. 调用时:
    正是因为有前面三步,才来带变量的使用规则:优先使用自己的,自己没有找全局,全局没有就报错
  2. 调用完:
    函数的EC会出栈,没人引用AO,AO自动释放,局部变量也就释放了
  • 闭包:希望保护一个可以反复使用的局部变量的一种语法结构,其实还是一个函数,只是写法比较特殊;
  • 何时使用:
  1. 两个函数进行嵌套
  2. 外层函数创建出受保护的变量
  3. 外层函数return出内层函数
  4. 内层函数再操作受保护的变量

强调

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

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

三个事件需要防抖节流-共同点:触发的速度飞快;

  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. 原型链:查找变量,带来了变量的使用规则:优先使用自己的,自己没有找全局,全局没有就报错
    作用:查找属性和方法,哪怕自己没有也会顺着原型链向上找,怪不得人人都能用toString(),因为他在最顶层
  3. 闭包:希望保护一个可以【反复使用的局部变量】的一种词法结构,其实还是一个函数,只是写法比较特殊
    作用:专门用于防抖节流