第四周

48 阅读5分钟

1、object:对象(js是基于原型的面向对象语言)

1.1.封装自定义对象

1.1.1.直接量方式

var obj={
    "属性名":属性值,
    ...,
    "方法名":function(){操作},//可以简化为箭头函数
    ...
}
复制代码

强调:

1、属性名和方法名的""可以不加

2、访问对象的属性和方法: obj.属性名; === obj["属性名"]; obj.方法名(); === obj"方法名";

3、js中一切都是对象,除了undefined和null,一切对象的底层都是hash数组

4、访问到不存在的属性,返回undefined

5、可以随时随地的添加新属性和新方法

obj.属性名=新值;
obj.方法名=function(){};
复制代码

6、如果我希望遍历出对象所有的东西,必须使用for in,obj[i]才能拿到

7、如果你希望在对象的方法里,使用对象自己的属性,写为this.属性名

1.1.2.预定义构造函数方式

var obj=new Object();//空对象
//需要自己后续慢慢添加属性和方法
obj.属性名=新值;
obj.方法名=function(){};
复制代码

总结:以上两个都有一个缺陷:一次只能创建一个对象,适合创建单个元素的时候(第一种方法),第二种方法完全是垃圾,如果你要批量创建多个对象,那么我推荐第三种方法

1.1.3.自定义构造函数方式(2步)

1、创建自定义构造函数

function 类名(name,age,hobby){
    this.name=name;
    this.age=age;
    this.hobby=hobby;
}
复制代码

千万不要在里面创建方法,每个对象都会创建出一个相同的方法,浪费内存 - 学习继承后可以解决

2、调用构造函数创建对象

var obj=new 类名(实参,...);
复制代码

优点:

1、所有的属性和方法都保存在一个对象之中 - 更符合现实更有意义

2、每个功能特地分开写 - 便于以后维护

3、铁锁链舟 - 一个方法触发多个方法联动

缺点:

对新手不友好,尤其是this的指向问题

1.2.继承

1、父对象的成员(属性和方法),子对象可以直接使用

2、为什么要继承:代码重用!提高代码的复用性,节约了内存空间!提升了网站的性能!

3、何时继承:只要多个子对象公用的属性和【方法】,都要集中定义在父对象之中

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

对象名.__proto__; //必须先有一个对象
构造函数名.prototype;//构造函数名几乎人人都有,除了Math和Windownew 构造函数名();//Array、String、Date、RegExp...
复制代码

5、两链一包:作用域链和【原型链】和闭包

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

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

原型对象.属性名=属性值;
原型对象.方法名=function(){}
复制代码

7、判断是自有还是共有

1、判断自有:obj.hasOwnProperty("属性名");
    如果结果为true,说明是自有属性,如果结果为false,有两种可能,说明可能是共有,也可能是没有
2、判断共有:
    if(obj.hasOwnProperty("属性名")==false&&"属性名" in obj){//in关键字,会自动查找整条原型链上的属性,找到了结果为true,找不到结果为false
        共有				
    }else{
        没有
    }

完整公式:
    if(obj.hasOwnProperty("属性名")){
        自有
    }else{
        if("属性名" in obj){
        共有
        }else{
        没有
        }
    }
复制代码

8、修改和删除:自有和共有

自有:

修改:obj.属性名=新属性值;
删除:delete obj.属性名;
复制代码

共有:

修改:原型对象.属性名=新属性值;//千万不要觉得,自己能拿到,就能直接修改,这样很危险,并没有修改原型的东西,而是在本地添加了一个同名属性
删除:delete 原型对象.属性名;//如果你对着本地直接删除,那么此操作直接无效
复制代码

9、如何为老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;
    }
}
复制代码

10、如何判断x是不是一个数组:4种方法:千万别用typeof(),只能检查原始类型,不能检查引用类型,如果检查引用类型得到的结果都是一个object

1、判断x是否继承自Array.prototypeArray.prototype.isPrototypeOf(x);
    结果为true,说明是数组,结果为false,说明不是数组
	
2、判断x是不是由Array这个构造函数创建的
    x instanceof Array;
    结果为true,说明是数组,结果为false,说明不是数组

3Array.isArray(x); - ES5新增的方法,只有数组可以这么使用
    结果为true,说明是数组,结果为false,说明不是数组

4、输出【对象的字符串】形式
    在Object的原型上保存着最原始的toString方法
    原始的toString输出形式:[object 构造函数名]
    
多态:子对象觉得父对象的成员不好用,就在本地定义了同名函数,覆盖了父对象的成员,不严格定义:同一个方法,不同的人使用,效果不同,有多种形态

固定套路:
    Object.prototype.toString.call(x)==="[object Array]"
复制代码

11、实现自定义继承:

1、两个对象之间设置继承
    子对象.__proto__=父对象

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

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

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

1.3.Function:闭包

1、作用域:2种

全局:随处可用,可以反复使用,缺点:容易被污染

函数:只能在函数调用时内部可用,不会被污染,缺点:一次性的,是会自动释放的
复制代码

2、函数的执行原理

程序加载时
    创建执行环境栈(ECS):保存函数调用顺序的数组
    首先压入全局执行环境(全局EC)
    全局EC引用着全局对象window
    window中保存着我们全局变量
    
定义函数时
    创建函数对象:封装代码段
    在函数对象之中有一个scope(作用域)属性:记录着函数来自己的作用域是哪里
    全局函数的scope都是window
    
调用前
    在执行环境栈(ECS)压入新的EC(函数的EC)
    创建出活动对象(AO):保存着本次函数调用时用到的局部变量
    在函数的EC中有一个scope chain(作用域链)属性引用着AO
    AO有一个parent属性是函数的scope引用着的对象
    
调用时
    正是因为有前面三步,才来带变量的使用规则:优先使用自己的,自己没有找全局,全局没有就报错
    
调用完
    函数的EC会出栈,没人引用AO,AO自动释放,局部变量也就释放了
复制代码

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

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

如何使用:
    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、原型链:每个对象都有一个属性叫做.__proto__,可以一层一层的找到每个对象的原型对象,最顶层的就是Object的原型,形成的一条链式结构,我们就称之为叫做原型链
    作用:查找属性和方法,哪怕自己没有也会顺着原型链向上找,怪不得人人都能用toString(),因为他在最顶层
    
    3、闭包:希望保护一个可以【反复使用的局部变量】的一种词法结构,其实还是一个函数,只是写法比较特殊
    作用:专门用于防抖节流

作者:nnnnna
链接:juejin.cn/post/722020…
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。