zoukankan      html  css  js  c++  java
  • javascript 之 继承与闭包

    继承:
    Call:
     var lipi={
           name:"李皮",
           play:function(foo){
               console.log(this.name+"正在玩"+foo);
           }
        }
        lipi.play("和泥")
       
        liujiaming想拿到lipi里面的play方法
        var liujiaming = {
           name:"刘嘉明"
        }
       lipi.play.call(liujiaming,"回旋踢");
        call第一个参数改变this的指向,第二个参数是play这个方法要传递的值,可以多个,用逗号隔开,比如,lipi.play.call(liujiaming,"回旋踢",3,4);但是如果方法里面没传两个形参play:function(foo,a,b)的话3,4是不会输出,也不会报错,但是如果形参传了实参没传就是undefined
     
    案例1
    var aLi = document.getElementById("list").getElementsByTagName("li");
    console.log(aLi instanceof Array)  //fasle,用这种方法获取的数组是伪数组
     
    var aLi = document.getElementById("list").getElementsByTagName("li");
    var arr = [].slice.call(aLi) //true,借用真数组arr的方法使aLi变为真数组
    console.log(arr instanceof Array)
     
    案例2
    function fn(){}
    var a = new fn();
    console.log(typeof a)//object typeof只能判断基本数据类型,当判断复杂数据类型是typeof只会判断为object
     
    1 function fn(){}
    console.log(Object.prototype.toString.call(fn))//[object Function]
     
    2 var obj ={};
    console.log(Object.prototype.toString.call(obj))//[object object]
    后面为数据类型,使用这个方法就可以判断复杂数据类型了
    老师笔记:
    Object.prototype.toString.call():检测一个复杂数据类型是一个什么样的类型
    instanceof:判断一个对象是不是另一个对象创建出来的
    typeof:只能判断基本数据类型  引用数据类型统计返回OBject
     
     
    apply:(同样具有call的用法,但它的第二个参数可以传数组)
    Math.max(取出最大值)这个方法没办法放数组
    所以。。。
    var arr = [10,20,30,40]
    var arr1 = [];
    console.log(Math.max.apply(arr1,arr));//40
    先创建arr1再让this指向arr1跟直接传 [ ]是一样的,但是var这样占用内存空间
    因此:
    var arr = [10,20,30,40]
    console.log(Math.max.apply([],arr));//40
    max是Math的方法,数组通过apply跟Math借,第一个依然是改变this的指向,所以传this指向一个数组,第二个传判断哪个数组的最大值
     
    ---------------------------------------------------------------------------------------------
     
    1、属性继承:
     
    但是lipi并没有继承到Person里面的属性
     
    使用call跟Person借属性,第一个参数改变this指向,指向Man,后面传要借的属性
     
    这时候call已经成功继承到Person的属性和方法了
    但是一般为了节省内存空间,都会把方法放在prototype上面
     
    但是放了之后却继承不到方法了,所以call只能继承属性,不能继承方法,想继承的话又会耗费内存,所以一般只用call和apply来做属性继承
    笔记:call和apply一般情况下我们都用来做属性继承
     
    --------------------------------------------------------------------------------------------- 
     
    prototype__proto__constructor:原型链
    笔记(背会)
    prototype:每一个函数里面都有一个prototype ,这个属性叫做原型,这个原型指向一个对象 我们把这个对象叫做原型对象
    prototype原型对象里面有2个东西
    1、constructor:构造器--作用指向创建自己的那个构造函数
    2、__proto__:1 每一个对象里面都会有一个__proto__这个属性
                 2 __proto__指向了一个对象 这个对象就是原型对象
                 3 实例化对象可以直接访问__proto__里面的一些方法
    原型链:由__proto__组成的链条就叫做原型链
     
    ---------------------------------------------------------------------------------------------
    实例化的过程就是创建对象的过程,new的过程就是创建对象的过程
    方法在_proto_里面
    //结果为true
    ---------------------------------------------------------------------------------------------
     
    本来访问实例化对象访问__proto__里面的方法需要console.log(p1.__proto__.eat)
    但却直接console.log(p1.eat)就可以直接访问,所以说  实例化对象可以直接访问__proto__里面的一些方法
    ---------------------------------------------------------------------------------------------
     
    ---------------------------------------------------------------------------------------------
     
    原型链:
    可以通过 console.log(p1.__proto__.__proto__.toString)
     访问父级Person的方法,p1里没有tostring这个方法,是使用链条访问到父级的方法(p1的父级是Person,Person的父级是object)
    ---------------------------------------------------------------------------------------------
     
    2、原型继承(极度简单,但也极度不推荐使用,因为会污染父级)
    方法work要加在原型上面
    属性都通过call继承到了,但方法却只继承到了work一个,因为他本来就放在Man上面,但是它也要继承到person的方法
    这时候所有属性和方法就都继承到了
    ---------------------------------------------------------------------------------------------
     
    输出父级
    发现Man的work方法也加到了父级上面,这就是污染父级
    --------------------------------------------------------------------------------------------- 
     
    3、原型拷贝 (不能直接继承父级以上的东西,只能一层层嵌套)
    那既然会污染父级,我们就把Man.prototype = Person.prototype;改为下面的拷贝继承
     
     
     
    复制父级的方法,这样就父级的方法就不会发生变化,输出p1和父级看一下
     
     
    子级继承到父级Person的方法了,父级的方法也没有被子级污染到,貌似看起来没什么问题.
    但是不能继承父级以上的东西(也就是Person的以上就不能继承了)
    ---------------------------------------------------------------------------------------------
     
     
     
    依然保留Man拷贝Person的循环,再加上shengwu这个方法,也加一个拷贝的循环,一层层嵌套,依然输出p1和person
     
    这时候Person继承到了父级shengwu的increase的方法,P1继承也到了父级的父级的方法
    但这样需要多层嵌套,一层一层的获取,是原型拷贝的唯一缺点
     
    --------------------------------------------------------------------------------------------- 
     
    4 、原型链继承 (多了无用的属性,并且constructor构造器没了,原型对象的指向也发生改变)
    原型链:由__proto__组成的链条就叫做原型链
    因为prototype里面的东西跟实例化对象里面的__proto__的东西是一样的,
    所以我们让子级Man的prototype等于父级的实例化里面的东西(p1.eat=p1.___proto___.eat,所以直接省略___proto___就可以访问了)
    因为是原型链继承 ,所以是形成链条一样的,但这时候确实是访问到了父级的方法,但是多了无用的属性,并且constructor构造器没了,原型对象的指向也发生改变
     
    console.log( [Person] )这时候父级也没有被污染
    原型链继承的缺点:丢失构造器,多了一些无用的属性,原型对象的指向也发生改变
     
    ---------------------------------------------------------------------------------------------
     
    原型链链条
    object再上面就是null,所以说null是原型链的顶端
     
    --------------------------------------------------------------------------------------------- 
     
    5 、混合继承(完美继承方式)
    person.prototype里面的东西本身就跟new Person 的__proto__里面的东西一样,所以跟上面的原型链继承差不多,只不过自己加个constructor构造器上去
    父级并没有被污染
    constructor构造器有了,父级的方法也继承到了,所以说混合继承是完美的继承方式
     
    --------------------------------------------------------------------------------------------- 
     
    6 、寄生继承 (没有混合继承的方法代码少)
     
    所以又要再给它加上构造器
     
    这时候就有了,所以这种方法也是好,但是没有混合继承的方法代码少,所有混合继承的方式最好
     
     
    总结:
    <script>
          (模板例子)
          function Person(name,age,sex){
                this.name = name;
                this.age = age;
                this.sex = sex;
          }
          Person.prototype.eat = function(){}
          Person.prototype.sleep = function(){}
          
          function Man(y,name,age,sex){
                this.y = y;
                Person.call(this,name,age,sex)
          }     
          -----------------------------------
          //继承语句
          Man.prototype = {
                constructor:Man,
                __proto__:Person.prototype
          }
          -----------------------------------
          Man.prototype.work = function(){} //写在继承语句下面,不然添加不到Man上
          
          var p1 = new Man();
          console.log(p1);
          console.log([Person])
          
     
          -----------------------------
         继承主要语句:
          //混合继承(完美继承方式)
          Man.prototype = {
                constructor:Man,  //添加少了的构造器
                __proto__:Person.prototype  
          }
          //寄生继承
          function fn(){}; //寄生壳
          fn.prototype = Person.prototype;
          Man.prototype = new fn();
          
          //原型拷贝(不能拷贝父级的父级)
          for(var key in Person.prototype){
                Man.prototype[key] = Person.prototype[key];
          }
          //原型链继承(多了无用东西,少了构造器)
          Man.prototype = new Person();
          
          //原型继承(污染父级,不推荐)
          Man.prototype = Person.prototype;
     
          //属性继承(只能继承属性)
          Person.call(this,name,age,sex)
          Person.apply(this,name,age,sex)
          
    </script>
     
     
     
     闭包:
     
    闭包:闭包就是能够读取其他函数内部的变量的函数
     
    全局变量:任何范围之内都能访问
    局部变量:只能在当前作用域内访问
     
    好处:1 可以让局部的变量在全局进行访问
            2 保留i的值
    坏处:1 占用内存空间
            2 在IE浏览器下可能会造成内存溢出
            3 所以不要滥用闭包,如果用完记得销毁
        
     
    通过return接受值
    正常情况下每次调用后都会被销毁,但在闭包里面就不会
     
    垃圾回收机制:
    当函数运行完毕以后如果内部的变量或者函数没有在全局挂载的话,
    那么这个函数就会被销毁掉
    如果第二次执行这个函数的时候里面的代码就会重新执行(所以就是因为这样才每次调用都会销毁重置让a=10,所以外面输出的结果都是11)
    a已经被return出去变成全局的了,所以在外面调用两次都会进行++ //11 //12
     
    面试题:
    同上,也就是把局部的变量挂载到全局上面,所以全局可以访问到进行累加
    挂载在全局后如果不销毁的话会一直在里面进行++,不会销毁重置,所以值才能在全局一直累加,会占用内存,所以不要滥用闭包,如果用完记得销毁
    让b=null;就是销毁了,再调用就是没有了
    ---------------------------------------------------------------------------------------------
     
    这时候i一下子就等于4了
    放在立即执行函数里面,就形成闭包,这样子就可以保留i的值了
     
     
     
     
     
     
  • 相关阅读:
    java中源代码和lib库中有包名和类名都相同的类(转)
    Python 入门之基本数据类型
    Python 学习经历分享
    String 与不可变对象
    String 的常用操作
    Java 中的国际化
    接口和工厂设计模式
    抽象类和模板设计模式
    Java中的访问控制权限
    Java 中类的初始化过程
  • 原文地址:https://www.cnblogs.com/nilinmin/p/9317192.html
Copyright © 2011-2022 走看看