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的值了
 
 
 
 
 
 

猜你喜欢

转载自www.cnblogs.com/nilinmin/p/9317192.html