JavaScript闭包和继承

什么是闭包:

闭包的概念:闭包就是函数嵌套时,让局部变量变成自由变量的环境,是一种让局部变量进化的方式
(简单来说就是在函数内部书写函数,利用函数调用局部的变量)

闭包的应用场景:
for (var i = 0; i < 10; i++) {
        console.log(i);        //可以访问到每次的i
    }

    必须满足两个条件:
        1.for循环内存在函数
        2.函数内会调用这个变量

    var ali = document.getElementsByTagName("li");
    for(var i=0;i<10;i++){
        ali[i].onclick = function(){
            console.log(i);        //在函数内部就无法访问到外部变量
        }
    }
    
    如何形成闭包
    var ali = document.getElementsByTagName("li");
    for(var i=0;i<10;i++){
        (function(a){
            ali[a].onclick = function(){
                console.log(a);
            }
        })(i)
    }
    一旦内部函数调用外部函数的局部变量,那么这个时候,这个局部变量就会变成内部函数的私有变量


二、当需要给setTimeout的回调函数传参时:
    setTimeout(function (a){
        console.log(a); //两秒后,undefined
    }, 2000);setTimeout(fn(10), 2000);
    function fn(a){
        console.log(a); //没有延迟,直接打印10
    }
    
    使用闭包:

    function fn(a){
        return function(){
            console.log(a); //两秒后,10
        }
    }
    var f = fn(10);
    setTimeout(f, 2000);


闭包的特点:

闭包是把函数内部和函数外部连接起来的桥梁
可以读取函数内部的变量
让这些变量的值,始终保存在内存中,不会在调用结束后被系统回收
避免全局变量命名空间的污染
内存会消耗很大,不能滥用
闭包会在父函数外部,改变父函数内部变量的值

继承

1.构造函数继承

在构造函数中,同样属于两个新创建的函数,也是不相等的
    function Fn(name){
        this.name = name;
        this.show = function(){
            alert(this.name);
        }
    }
    var obj1 = new Fn("AAA");
    var obj2 = new Fn("BBB");
    obj1.show()
    obj2.show()
    
    此时,任何一个new出来的实例上都有了show方法,可以视为最基础的继承。

2.call/apply继承

 function Father(skill){
        this.skill = skill;
        this.show = function(){
            alert("我会"+this.skill);
        }
    }
    function Son(abc){
        //这里的this指向函数Son的实例化对象
        //将Father里面的this改变成指向Son的实例化对象,当相遇将father里面所有的属性和方法都复制到了son身上
        //Father.call(this,abc);//继承结束,call适合固定参数的继承
        //Father.apply(this,arguments);//继承结束,apply适合不定参数的继承
    }
    var f = new Father("绝世木匠”);
    var s = new Son("一般木匠");
    f.show()
    s.show();
    
    优点:
        创建子类实例时,可以向父类的构造器传参;
        
    缺点:
        只能继承构造器中定义的属性和方法,不能继承原型上定义的属性和方法

3.prototype的概念:

原型对象
指针 constructor表示当前函数属于谁,用来指向当前原型所属的函数
原型指针[prototype] proto 相当于一根原型指针,指向当前对象的“父级”。
可以在当前函数的原型上添加方法和各种属性,以供使用
JS中万物皆对象,所有的内容的指针终点都指向Object。
4.原型链继承

方法一:Son.prototype = new Father();
    //创建一个函数Father,用来做原始对象
    function Father(){
        this.skill = "铁匠"
    };
    Father.prototype.show = function(){
        console.log(this.skill)
    }
    //创建一个函数Son,准备继承Father的原型
    function Son(){};
    //将Son点原型,赋值为一个指针,指向Father的原型
    Son.prototype = new Father();

    //此时就可以通过执行new Son()获取到从构造函数Father上继承过来属性和方法
    var s = new Son()
    s.show();        //铁匠

    优点:
        1.可以访问父类原型上的方法和属性
        2.简单方便,易于实现

    缺点:
        1.创建子类实例时,无法向父类的构造器传参

方法二:
    //创建一个函数Father,用来做原始对象
    function Father(){
        this.skill = "铁匠";
    };
    Father.prototype.show = function(){
        console.log("hello world")
    }
    //创建一个函数Son,准备继承Father的原型
    function Son(){};
    //将Son的原型,设置为Father的原型
    //Son.prototype = Father.prototype;
    //但是这种拷贝方式为对象的浅拷贝,一旦后期修改Son原型上的方法,会影响到Father的原型
    //需采用对象的深拷贝方法
    for(var i in Father.prototype){
        Son.prototype[i] = Father.prototype[i];
    }
    Son.prototype.show = function(){
        console.log("这是子类修改之后的show方法")
    }
    var f = new Father()
    f.show()
    var s = new Son()
    s.show();
    
    优点:
        完全将父类原型上的方法和属性拷贝到子类原型

    缺点:
        只能继承原型上的方法和属性,构造函数无法传参和继承

5.混合继承

 特点:
        使用call或apply继承父类的构造器中的内容,使用原型继承,继承父类的原型
    function Father(skill,id){
        this.skill = skill;
        this.id = id;
    }
    Father.prototype.show = function(){
        alert("我是father,这是我的技能"+this.skill);
    }

    function Son(){
        Father.apply(this,arguments);
    }
    //如果不做Son的原型继承Father的原型,此时会报错:son.show is not a function
    for(var i in Father.prototype){
        Son.prototype[i] = Father.prototype[i];
    }
    //因为,如果不让Son的原型等于Father的原型,Son使用apply是继承不到原型上的方法
    Son.prototype.show = function(){
        alert("我是son,这是我的技能"+this.skill);
    }
    var f = new Father("专家级铁匠","father");
    var s = new Son("熟练级铁匠","son");
    f.show();
    s.show();

6.ES6的class继承方式

class Father{
        constructor(){
        }
        show(){
        }
    }

    class Son extends Father{
        constructor(){
            super()
        }
        show(){
        }
    }
原型对象的相关概念

1.对象中的__proto__是什么:
js中万物皆对象,每个数据都会有一个__proto__的属性,这个属性叫隐式原型,一个对象(obj)的隐式原型(proto)指向构造该对象(obj)的构造函数(object())的原型属性(object.prototype),这样做的原因是为了能够保证实例(obj)能够访问到在构造函数(object())的原型属性(object.prototype)中定义的属性和方法。

2.函数中的prototype是什么:
函数(Function)是一个特殊的对象,除了和其他对象一样有上述__proto__属性之外,还有自己特有的属性——原型(prototype),这个属性被描述成指针,他指向一个对象类型的数据,这个对象的用途就是包含所有将来使用该函数构造出来的可被共享的属性和方法(我们把这个对象叫做原型对象)。
原型对象内也有一个属性,叫做constructor,这个属性包含了一个指针,指回原函数(类似于arguments.callee。但是arguments只能在函数内部获得,而函数原型对象内的constructor属性,可以在任何能访问到这个函数的位置使用)。

3.构造函数,原型,实例之间的关系:
1>构造函数Fn身上有属性prototype为原型对象,原型对象内有constructor属性指向当前prototype所在的构造函数Fn
2>在new执行构造函数Fn时,创造了一个实例对象f,实例对象f的__proto__指向构造函数Fn的原型prototype
3>因为实例对象f的__proto__指向构造函数Fn的原型prototype,所以实例对象f可以间接访问到Fn原型prototype的方法
4.查看实例对象f是否有指针指向构造函数Fn的原型:
isPrototypeOf()用于检测两个对象之间似乎否存在这种关系,使用方法如下:
Fn.prototype.isPrototypeOf(f) // 查看 Fn 的 prototype 对象,是否是 f 原型
类似的还有instanceof运算符,使用方法如下:
console.log(f instanceof Fn) // 查看 f 对象是否是构造函数 Fn 的实例
console.log(f instanceof Object)
两种使用,如果是返回ture,如果不是返回false
注意:instanceof运算符右侧为构造函数,并且js中所有原型都来自Object构造函数

猜你喜欢

转载自blog.csdn.net/ZhangXiaoTianZC/article/details/102868542