闭包和继承

1.闭包

问题演示:闭包的作用

控制台是否有输出

    <script>
        function foo(){
            var a = 10;
            function bar(){
                a++;
                console.log(a);
            }
            bar();
        }
        foo();//11
        foo();//11
    </script>

//闭包:在一个函数外部能够访问函数内部局部变量的函数,我们把这个函数称为闭包(函数),把变量称为闭包变量

//作用域链 : 逐层向上级作用域查找时形成一个链条

        function foo(){
            var a =10;
            function bar(){
                a++;
                console.log(a);
            }
            return bar;
        }
       // foo();//没有输出
        var baz = foo();
        console.log(baz);//得到函数
        baz();//11
        baz();//12

1.2闭包的经典应用

点击对应的li输出相应索引

<body>
    <ul>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
    </ul>
    <script>
        //请使用闭包实现 点击li输出对应的索引
      let aList = document.querySelectorAll("li");
      for(let i =0;i<aList.length;i++){
          aList[i].onclick =()=>{
            console.log(i);
          };
      }

    </script>
    
</body>

使用var变量

 for(var i=0;i<aList.length;i++){
        aList[i].onclick = function(){
            console.log(i);
            //i是全局变量输出全是5
        };
    }

使用闭包实现

 for(var i=0;i<aList.length;i++){
        //立即执行的匿名函数
        aList[i].onclick = (function(i){
            return function(){
                console.log(i);//这个i是个局部变量,访问的是外层函数的形参i
            };
        })(i);
    }

1.3闭包的好处

//减少全局变量的定义,避免污染环境,避免冲突

  var a=10;
        let b=20;
        var a=30;
        let b =30;//报错

//使用匿名函数立即执行

let obj =  (function(){
        var a = 10;
        let b = 20;
        return{
            fn1(){
                a++;
                console.log(a);
            },
            fn2(){
                b++;
                console.log(b);
            }
        }
    })();
    obj.fn1();//11
  obj.fn1();//12

1.4闭包的缺点和好处

  //内存泄露:一块内存空间既不能被使用也不能释放(回收)

    //垃圾回收机制  引用计数  标记清除

  //内存泄露:一块内存空间既不能被使用也不能释放(回收)
        //垃圾回收机制  引用计数  标记清除
        let obj1 = {a:1,b:2};//引用次数为1,不能被清除
        obj =null;//引用次数为0
        //形成循环清不掉
        let obj2 =obj1;
        obj1.prop1 =obj2;
        obj2.prop2 =obj1;

1.5闭包面试题

//第一道

       function fun(n,o){
            console.log(o);//对应的是第二个实参的值,如果没有传第二个实参,undefined;
            //返回一个对象 对象里有一个方法
            return{
                fun:function(m){
                    return fun(m,n);//fun对应全局函数fun,m对应的fn方法里的形参吗, n对应的是全局函数fun里的形参
                },
            };
        }
   var a =fun(0);// undefined   a对应的fun这个函数的返回值,是一个对象,这个对象里有一个方法fun
    a.fun(1);//fun(1,0)  0
    a.fun(2);//fun(2,0)  0
    a.fun(3);//fun(3,0)  0

//第二道

  var b = fun(0).fun(1).fun(2).fun(3)//undefined 0 1 2
    var c= fun(0).fun(1);
    c.fun(2);//undefined 0
    c.fun(3);//1 1

2.继承

2.1继承解释

        //继承:一个类的实例能够访问另外一个类里定义的属性和方法

        //子类和父类的说法

        function Parents(){
        }
        function Children(name,age){
            this.name = name;
            this.age = age;
            this.sayHello = function(){};
        }
        let xixi = new Children("xixi",20);

2.2call或apply继承

 function Person(name, age) {
        this.name = name;
        this.age = age;
        this.sayHello = function () {
          console.log(this.name);
        };
      }
      function Male(name, age) {
        Person.call(this, name, age); //把person里面的代码拿过来执行,并且改变this指向,让其指向Male的实例,相当于指定在Male里写this.name = name……
        this.gendor = "男";
      }
      function Female(name, age) {
        Person.apply(this, [name, age]);
        this.gendor = "女";
      }
      let ych = new Male("yinchuangjie", 20);
      ych.sayHello();
      let sss = new Female("shishanshan", 20);
      sss.sayHello();

2.3原型继承

 //原型对象:每一个函数内部有个属性叫prototype,prototype里的属性和方法能被实例所访问,我们把这个属性称为原型(原型对象)

 //每一个实例对象里有一个内部属性__proto__,这个属性指的是prototype

    function Person(){}
    console.log(Person.prototype);
    console.log(Person.prototype.constructor === Person);//true
    let p = new Person();
    console.log(p.__proto__ === Person.prototype);//true

//如何实现原型继承

 //如果让一个类型的原型对象等于另外一个类型的实例,就可以实现继承

    function Person(){}
    Person.prototype.name = "zhangmeng";
    Person.prototype.age = 20;
    Person.prototype.sayHello = function(){
        console.log(this.name);
    }

//可以让Male的原型对象等于Person的实例

    function Male(){}
    Male.prototype = new Person();//实现了原型继承  为什么?
    let male = new Male();
    male.sayHello();
    console.log(Male.prototype.__proto__ === Person.prototype);//ture
    console.log(male.__proto__ === Male.prototype);//true

    //访问一个实例的属性和方法有一个查找顺序

    //首先,看当前实例自身有没有这些属性和方法,如果自身有,直接访问

 //其次,看当前实例__proto__指向的原型对象上的__proto__对应的那个原型对象上有没有如果有的话,直接访问

    //以此类推,最终会查找到Object.prototype,如果有,直接访问,没有的话,得到undefined

    //沿着__proto__逐层查找形成的一个链式结构,称为原型链

    console.log(male.__proto__ === Male.prototype);//true
    console.log(Male.prototype);
    console.log(Male.prototype.__proto__ === Person.prototype);//ture
    console.log(male.__proto__.__proto__ === Peoson.prototype);//true

//原型链

//自身有属性访问自身(C的实例属性)

如果没有取原型上找 (C的一个原型属性)

    function A() {}
    A.prototype.prop = "A的一个原型属性";
    function B() {}
    B.prototype.prop = "B的一个原型属性";
    function C() {
        this.prop = "C的实例属性"
    }
    C.prototype.prop = "C的一个原型属性";

    let c = new C();
    console.log(c.prop);

//先C的实例,没有C的原型属性

//继承后 C实->C原 -> B -> A

    function A() {}
    A.prototype.prop = "A的一个原型属性";
    function B() {}
    // B.prototype.prop = "B的一个原型属性";
    function C() {
        // this.prop = "C的实例属性"
    }
    // C.prototype.prop = "C的一个原型属性";
    B.prototype = new A();
    C.prototype = new B();
    let c = new C();
    console.log(c.prop);
    function A() {}
    A.prototype.prop = "A的一个原型属性";
    function B() {
        this.prop = "B的一个实例属性"
    }
 
    function C() {
       
    }
   
    C.prototype = new B();
    console.log(C.prototype);
    let c = new C();
    console.log(c.prop);

//原型链顶端

    function A() {}
    A.prototype.prop = "A的一个原型属性";
    function B() {}
    // B.prototype.prop = "B的一个原型属性";
  
    function C() {
        this.prop = "C的实例属性"
    }
    C.prototype.prop = "C的一个原型属性";

    B.prototype = new A();//B继承了A
    C.prototype = new B();//C继承了B  

    let c = new C();
   
    console.log(c.__proto__ === C.prototype);//true
    console.log(c.__proto__.__proto__ === B.prototype);//true
    console.log(c.__proto__.__proto__.__proto__ === A.prototype);//true
    console.log(c.__proto__.__proto__.__proto__.__proto__ ===Object.prototype);
    console.log(Object.prototype,Object.prototype.__proto__);//Object.prototype是任意原型链的顶端

2.4组合继承

通过call方法实现属性继承,通过原型继承和循环实现方法继承

      //组合创建
      function Person(name, age) {
        this.name = name;
        this.age = age;
      }
      Person.prototype.sayHello = function () {
        console.log(this.name);
      };

      function Male(name, age) {
        Person.call(this, name, age); //已经实现了属性的继承
      }
      //只需要考虑原型方法的继承
      //Male.prototype = Person.prototype; //这种写法有一个问题,地址指向一致
      //目的 是把父类原型对象上的方法给到子类的原型对象,并且地址还不能一样

      for (let i in Person.prototype) {
        Male.prototype[i] = Person.prototype[i];
      }

      Male.prototype.sayHi = function () {
        console.log("hi");
      };

      let male = new Male("彭震", 20);
      male.sayHello();

      let person = new Person("高真爱", 20); //父类访问子类里的方法,有逻辑上错误
      person.sayHi();

2.5寄生式组合创建

    <script>
      //Object里面提供的一种新的方法
      //let obj1 = Object.create(obj); 用来创建一个对象,创建出来的对象有一个特点 obj1.__proto__== obj

      function Person(name, age) {
        this.name = name;
        this.age = age;
      }
      Person.prototype.sayHello = function () {
        console.log(this.name);
      };

      function Male(name, age) {
        Person.call(this, name, age); //已经实现了属性的继承
      }

      //实现原型的继承
      Male.prototype = Object.create(Person.prototype);
      //修正
      Male.prototype.constructor = Male;
      //console.log(Male.prototype.__proto__ === Person.prototype);

      let male = new Male("王晓林", 20);

      male.sayHello();
      //反向查找构造函数 通过实例找到对应的构造函数
      console.log(male.__proto__ === Male.prototype);
      console.log(male.__proto__.constructor);

2.6extends继承

      class Person {
        constructor(name, age) {
          this.name = name;
          this.age = age;
        }
        sayHello() {
          console.log(this.name);
        }
      }
      //class Male extends Person {} //继承实现
      //需求 子类里有自己的属性和方法
      class Male extends Person {
        constructor(name, age) {
          //super 创建this并改变this指向,执行父类构造函数里的代码
          super(name, age);
          this.gender = "男";
        }
        sayHi() {
          console.log("hi " + this.name);
        }
      }

      let male = new Male("wangxiaolin", 20);
      male.sayHello();
      male.sayHi();

2.7面试题

1.每一个函数都有一个原型对象(prototype)(true)

2.每一个构造函数都有一个原型对象(prototype)(true)

3.每一个对象都有一个原型对象(prototype)(false)

4.每一个函数都有一个内部属性__proto__(ture)

5.每一个构造函数都有一个内部属性__proto__(ture)

6.每一个对象都有一个内部属性(true)

        function foo(){
            console.log(this);
            //写在这实例可以访问到
            // this.getName = function(){
            //     console.log(1);
            // }
        }
        //添加一个属性  new 跟实例没有半毛钱关系
        foo.getName = function(){
            console.log(2);
        };
        //给prototye.getName添加一个属性
        foo.prototype.getName = function(){
            console.log(3);
        }
        //实例 访问这个方法首先在构造函数上找 没有找原型对象
        new foo().getName();//3  

//需求 : 给任意数组添加两个方法 getMin() getMax() ,当数组调用这两个方法时,能得到数组中的最小值和最大值

      let arr = [11, 22, 10, 77];
      //console.log(arr.__proto__);
      Array.prototype.getMin = function () {
        return Math.min.apply(null, this);
      };
      Array.prototype.getMax = function () {
        return Math.max.apply(null, this);
      };
      console.log(arr.getMin(), arr.getMax());

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值