zoukankan      html  css  js  c++  java
  • js中面向对象 small

    js中没有定义类,但却有function与对象,结合类的思想,可以通过他们来模仿类。
    类有封装、继承的特点,那么先从这两点开始来看js中如何实现的。

    封装

    大家都知道如何创建一个对象:
    var obj=new Object();

    obj.name="myobject";

    obj.sayName=function(){

    return this.name;

    };
    这有一个问题:当要创建大量这种对象时,会出现大批的重复代码。可以通过工厂模式来解决:
    function createPerson(name){

    var o =new Object();

    o.name=name;

    o.sayName=function(){

    return this.name;

    };

    return o;

    }
    调用:var person1=createPerson("xiaoqi");
    这个虽然可以解决以上的问题,但无法识别对象类型。
    构造函数模式:
    function Person(name){

    this.name=name;

    this.sayName=function(){

    return this.name;

    };

    }
    调用:
    var person1=new Person("xiaoqi"); 
    var person2=new Person("xiaosan"); 
    缺点:每创建一个实例都要创建一个方法,内存中会出现重复的相同功能的重复方法,无必要的浪费了内存
    解决方法:
    function Person(name){

    this.name=name;

    this.sayName=sayName;


    function sayName(){

    return this.name;

    }
    调用:
    var person1=new Person("xiaoqi"); 
    var person2=new Person("xiaosan"); 
     这样person1与person2的sayName属性保存的只是sayName的内存地址,而不用重新创建新的方法。
    缺点:
    1.定义很多方法,就要定义很多全局函数
    2.函数全局作用域名不副实,全局作用域中定义的函数实际上只能被某个对象调用
    3.没有丝毫封装性可言 
    要解决上面的问题引进了原型模式:
    function Person(){}
    Person.prototype.name="myName";
    Person.prototype. sayName=function(){

    return this.name;

    };
    结构图如:

    Person.prototype指向的就是Person的原型对象,实例person1与person2也都有一个指针指向此原型对象。

    简单原型:
    function Person(){}
    Person.prototype={

    name:"myName",

    sayName:function(){

    return this.name;

    }

    };
    问题:这时的Person.prototype的constructor不再指向Person,指向了Object。因为Person.prototype指向一个Object对象。
    如:
    var person=new Person();
    person instanceof Person  //true
    person instanceof Object  //true
    person.constructor==Person  //false
    person.constructor==Object  //true
    可通过重指constructor来修正
    Person.prototype={
    constructor:Person,
    name:"myName",
    sayName:function(){
    return this.name;
    }
    };
    此时:
    person.constructor==Person  //true
    person.constructor==Object  //false
    有人说Person也是一种Object为什么会出现这种结果呢。 这个在后面继承中会再做解释。
     原型动态性:
    修改原型中的属性,可立即在实例中得到体现,如:
    var person=new Person();  //此时person中没有方法sayHi
    Person.prototype.sayHi=function(){

    alert("Hi");

    }; 
    person.sayHi();  //Hi
    虽然实例化时person没有sayHi方法,但实例化后通过prototype添加了sayHi方法,原来的实例也一样可以访问此方法,并不是只有后面的实例才能访问。这就是原型的主要功能与优点。
    但重写了prototype就会不同了:
    function Person(){}
    var person=new Person();
    Person.prototype={

    constructor:Person,

    name:"myName",

    sayName:function(){

    return this.name;

    }
    };
    person.sayName(); //error 访问不到sayName方法 
    解释如图:
     
    这里person实例重写之后的原型仍然指向原来的指针,原来没有sayName方法,重写后仍然也就没有了。
    原型缺点:原型对于方法避免了重复定义方法,节省了内存空间,很是适用。但对于属性就不合适了,尤其是引用类型的属性。请看:
    functon Person(){}
    Person.prototype={

    constructor:Person,

    friends:["s","c"]

    }; 
    var person1=new Person(); 
    var person2=new Person(); 
    person1.friends.push("v");
    person1.friends  //"s,c,v"
    person2.friends //"s,c,v"
    这就是问题所在了,修改了一个实例的属性,所有实例的这个属性都被修改了,而我们一般想要实例的属性归它自己所有,与其它的不同。方法共享,与其它的相同。
    这样就产生了最为常用的模式:组合使用构造函数与原型模式:
    function Peson(name){

    this.name=name;

    this.job=["s","c"];

    }
    Person.prototype={

    constructor:Person,

    sayName:function(){

    return this.name;

    }
    };

    这个模式有三人优点:
    1.自己独有的属性
    2.公用的方法,节省内存
    3.支持传递参数
    结构图如:

    至此,js模仿类的封装已经完结,这个模式是最为常用的一个模式。 这里的Person是构造函数,Person prototype是Person的原型,
    person是Person的实例。这里要注意一点,构造函数也是一个实例,是Function的实例,继承自Object,所以Person也是一个对象,它也有自己的属性,如prototype是构造函数时自动生成的,同时也可以自定义属性、方法,如:default属性。

    继承

    后台继承分为接口继承与实现继承两种,我们在这里只介绍实现继承。
    实现的基本思路是借用构造函数的prototype指针指向,如果把这个指向另一人构造函数的实例会怎么样呢。如:
    function SuperType(){

    this.property=true;

    }
    SuperType.prototype.getSuperValue=function(){

    return this.property;

    };
    function SubType(){

    this.subproperty=false;

    }
    SubType.prototype=new SuperType();
    SubType.prototype.getSubValue=function(){

    return this.subproperty;

    };

    调用:
    var instance=new SubType();
    结构图如:

    SubType的原型为SuperType的一个实例即SubType prototype,则SubType prototype指向SuperType的原型即SuperType prototype。如此类推下去就形成一个链式结构,我们称之为原型链。
    借用原型链SubType就继承到了SuperType的所有属性与方法。
    注意SuperType Prototype本身就是个对象,它也是继承了Object的实例。故:

    问题:
    1.超类型属性为引用类型时,所有实例都共享此属性
    2.无法向超类型的构造函数传参数
    解决以上问题借用构造函数:
    function SuperType(name){

    this.name=name;

    this.colors=["s","c"];

    }
    SuperType.prototype.getSuperValue=function(){};
    function SubType=function(name){

    SuperType.call(this,name);

    this.age=29;

    }
    解决了上面的两个问题,但却无法访问超类型的原型中的方法。
    组合继承:
    function SuperType(name){

    this.name=name;

    this.colors=["s","c"];

    }
    SuperType.prototype.getSuperValue=function(){

    };
    function SubType(name,age){

    SuperType.call(this,name);    //第二次调用

    this.age=age;

    }
    SubType.prototype=new SuperType();  //第一次调用
    SubType.prototype.sayAge=function(){

    return this.age;

    };
    这样即可以拥有自己的属性friends,又可以有相同的共享方法,还可以识别类型(instanceof,isPrototypeOf)
    但SuperType的属性有存在两组:

    寄生式组合继承:
    function SuperType(name){

    this.name=name;

    this.colors=["s","c"];

    }
    SuperType.prototype.getSuperValue=function(){

    };
    function SubType(name,age){

    SuperType.call(this,name);

    this.age=age;

    }
    //
    function F(){}
    F.prototype=SuperType.prototype;
    SubType.prototype=new F();
    SubType.prototype.contructor=SubType;
    //
    SubType.prototype.sayAge=function(){

    return this.age;

    };
    此方式为最常用的继承方式。

    另SubType,SuperType等构造函数定义也是一种实例化,是Function的实例。再加上prototype(原型对象)是一个对象,也是Object的一个实例。故最终图为:


    至此,js中模仿类的封装与继承就介绍完毕了。 
    补充一张网上的object关系图
  • 相关阅读:
    【Linux 读书笔记】Linux文件的硬连接和符号连接
    Shell参数
    Shellcase语句的例子
    Shellselect
    Shell小程序一个
    SHELL起步
    接昨天的 while
    Shell循环控制
    Shellwhile循环的例子
    Shellfor语句
  • 原文地址:https://www.cnblogs.com/qd4world/p/2984119.html
Copyright © 2011-2022 走看看