zoukankan      html  css  js  c++  java
  • javascript OOP(上)(八)

    一、OOP的概念和继承

    1、OOP概念

     面向对象程序设计(Object-oriented programming,OOP)是一种程序设计范型,同时也是一种程序开发的方法。对象指的是类的实例。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性。

    OOP特点:继承、封装、多态和抽象。

    2、基于原型的继承

     

    function Foo(){
        this.y=2;
    }
    
    /*每个函数对象有一个对象属性prototype,这个prototype是个对象,Fooj就是Foo.prototype*/
    
    Foo.prototype.x=1;
    var obj3=new Foo();
    /*创建一个Foo的实例obj3,
    使用new调用的时候,Foo作为构造器来使用,并且this指向一个对象,而这个对象的原型会指向构造器的prototype属性,也就是Foo.prototype*/
    
    console.log(obj3.y);  //2  对象上的
    console.log(obj3.x);   //1 原型上的

    3、prototype属性与原型

    用函数声明创建一个空函数function Foo(){}的时候,函数就会有一个prototype对象属性。

    prototype对象属性默认会有2个属性,一个是constructor,会指向它本身Foo;另外一个是_proto_,一般的对象都会指向Object.prototype。

    x:1是通过赋值语句Foo.prototype.x=1;增加的。

    Foo.prototype的作用:

    当使用new Foo去构造Foo的实例的时候,这个prototype属性会用作new出来的对象(obj1,obj2,obj3...)的原型(_proto_)。

    prototype:是函数对象上面预设的对象属性。

    _proto_:对象上的原型,通常都是它的构造器的prototype属性。

    4、更复杂的实例:

    Student继承Person

    <script>
    function Person(name,age){//new方法调用,this作为 return值
        this.name=name;
        this.age=age;
    }
    /*Person.prototype添加实例共享的属性和方法*/
    Person.prototype.hi=function(){
        console.log('Hi,my name is'+this.name+"I'm"+this.age+"years old now.");
    }
    
    Person.prototype.LEGS_NUM=2;
    Person.prototype.ARMS_NUM=2;
    Person.prototype.walk=function(){
        console.log(this.name+" is wlking...");
    }
    /*初始化Student类*/
    function Student(name,age,className) {
        Person.call(this,name,age);//先调用父类初始化
        this.className=className;
    }
    
    /*继承*/
    Student.prototype=Object.create(Person.prototype);
    Student.prototype.constructor=Student;
    /*覆盖基类的hi方法*/
    Student.prototype.hi=function(){
        console.log('Hi,my name is '+this.name+",I'm "+this.age+" years old now,and from "+this.className+'.');
    }
    
    Student.prototype.learn=function(subject){
        console.log(this.name+' is learning '+subject+' at '+this.className+'.');
    }
    //test
    var bosn=new Student('Bosn',27,'Class 3,Grade 2');
    console.log(bosn.hi());//Hi,my name is Bosn,I'm 27 years old now,and from Class 3,Grade 2.
    console.log(bosn.LEGS_NUM);//2
    bosn.walk();
    //Bosn is wlking...
    bosn.learn('math');
    //Bosn is learning math at Class 3,Grade 2.
    </script>

    二、原型链

    如下:

    从后往前:

    bosn构造:通过 new Student()创建了bosn实例,bosn实例的原型_proto_指向构造器的prototype属性,也就是Student.prototyep。Studnet.prototype上面有hi()方法和learn()方法。

    Studnet.prototype构造:通过Object.create(Person.prototype)来构造。Student.prototype是个空对象,这个空对象随后添加了hi()方法和learn()方法,它的原型_proto_指向了Person.prototype。

    Person.prototype:直接定义了一个Person函数,在Person.ptototype上添加了共享的属性和方法,这个Person.prototype就是一个内置的普通对象。它本身也会有原型就是Object.prototype。

    坑1:

    并不是所有的对象,最终原型链上都有Object.prototype。

    通过Object.create(null)创建的对象原型链上没有Object.prototype,也就没有Object.prototype上的方法。

    坑2:

    并不是所有的函数对象都会有prototype属性。

    ES5的bind()函数用来修改函数在运行时的this。

    bind方法返回的也是一个函数,可以通过typeof判断一下,但是bind()方法返回的函数就没有prototype属性。

    三、prototype属性

    1、prototype相关的修改

    /*给prototype添加属性和方法会影响到已创建或新创建的实例*/
    Student.prototype.x=101;
    console.log(bosn.x);     //101
    
    /*修改prototype并不会影响已经创建的实例,会影响后续创建的实例*/
    Student.prototype={y:2};
    console.log(bosn.y);       //undefined
    console.log(bosn.x);      //101
    
    
    var nunnly=new Student('Nunnly',3,'Class LOL KengB');
    console.log(nunnly.x); //undefined
    console.log(nunnly.y);  //2

    2、内置的构造器的prototype

     内置的函数构造器也有prototype属性,比如Object,Number,Boolean,Function,等等。

    bind,apply,call等等都是从Function的prototype上取到的。

    边际效应:

    修改这些构造器的prototype有时候会带来一些边际效应。

    Object.prototype.x=1;
    var obj={};
    console.log(obj.x); //1
    for(var key in obj){
        console.log('result: '+key);
    }
    //result: x

    边际效应:在prototype上添加属性x,在for in遍历的时候会把x遍历出来。

    解决办法:

    ES5里面有defineProperty来控制对象属性。默认标签都是false,下面只设置writable:true。简洁表示不可枚举,也不可配置。

    Object.defineProperty(Object.prototype,'x',
        {writable:true,value:1});
    var obj={};
    console.log(obj.x); //1
    for(var key in obj){
        console.log('result: '+key);
    }
    //nothing output here

    这样for in的时候就不会有边际效应。所以如果写nodejs,判断一下如果有defineProperty,可以通过这种方式来改写Object.property这样一些内置构造器上的prototype的时候,可以跨过这样一个边际效应。

    很少会用到修改Object.prototype,因为影响范围太广了。 但是有时候为了兼容性可能会通过Object.defineProperty做一些ES5才支持的方法模拟。

    四、实现继承的方式

    见代码。

    <script>
    function Person(){
    }
    function Student(){
    }
    
    Student.prototype=Person.prototype;//1   禁止用,改变Student也会修改Person
    
    Student.prototype=new Person();//2     
    /*可以实现,问题:比如构造函数有一个name和age,new Person()的时候name和age传什么呢?传任何东西都是很奇怪的。因为Student只是一个类,还没有实例化。
    
    所以这里只是为了继承调用了Person的构造函数,创建了一个实例,在很多情况下也是比较奇怪的。
    */
    Student.prototype=Object.create(Person.prototype);//3
    Student.prototype.constructor=Person;
    
    /*第三种是相对理想的办法*/
    /*
    创建了一个空的对象,并且对象的原型指向了Person.prototype。既保证了继承Person.prototype删的方法,并且Student.prototype又有自己的一个空的对象,Student.prototype的修改又不会影响Person.prototype。
    */
    
    /*Object.create是ES5才支持的方法,在ES5之前可以模拟实现*/
    if(!Object.create){
        Object.create=function(proto){
            function F(){};
            F.prototype=proto;
            return new F;  /*new调用时创建一个对象,并且原型指向F.prototype,即proto*/
        }
    }
    </script>

    本文作者starof,因知识本身在变化,作者也在不断学习成长,文章内容也不定时更新,为避免误导读者,方便追根溯源,请诸位转载注明出处:http://www.cnblogs.com/starof/p/4904929.html有问题欢迎与我讨论,共同进步。 

  • 相关阅读:
    [USACO09Open] Tower of Hay 干草塔
    [HNOI2004]打鼹鼠
    BZOJ1222[HNOI 2001]产品加工
    BZOJ1270[BJWC2008]雷涛的小猫
    NOIP2018出征策
    解析·NOIP·冷门 CLZ最小环
    CCF-NOIP-2018 提高组(复赛) 模拟试题(九)(2018 CSYZ长沙一中)
    [脚本无敌1]图片批量处理(matlab)
    用Matlab解《2013年数据建模比赛》图像碎片拼接题
    火灾检测-fire,fire
  • 原文地址:https://www.cnblogs.com/starof/p/6408923.html
Copyright © 2011-2022 走看看