zoukankan      html  css  js  c++  java
  • javascript面向对象程序设计

    面向对象语言有一个标志,就是都有类的概念,通过类可以创建任意多个具有相同属性和方法的对象。但是在javascript中是没有类这个概念的,所以它与其他基于类的面向对象语言也存在差异。在讲javascript的面向对象程序设计之前,我们还是先来温习一下javascript的一些基础知识。相当多的人都在项目中用javascript,但也有很多的人与我之前一样,敲了相当长时间的js代码,却不知道javascript中有哪些类型。言归正传,咱们从数据类型开始吧。

    一、数据类型

    Javascript中有五种简单数据类型(也称为基本数据类型):Undefined、Null、Boolean、Number和String;一种复杂数据类型:Object。

    —Undefined类型只有一个值,即特殊的undefined。每个未被初始化的变量的默认值都为undefined。
    var message;
    alert(message);      // "undefined"
    —
    Null类型也只有一个值,这个特殊的值是null。实际上undefined是派生自null的,但是不完全相等undefined。
    var code=null;
    alter(typeof code);   //Object
    alter(null==undefined);  //true
    alter(null===undefined); //false
    —
    Boolean 类型有两个值:true和false,它与其他类型的转换可以参考下面的表。
    数据类型 转换为true 转换为false
    Boolean true false
    String 任何非空字符串 ""(空字符)
    Number 任何非0数字 0和NaN
    Object 任何对象 null
    Undefined N/A(不适用) undefined

    Number类型的包括5e-324到1.79e+308之间的所有浮点数和NaN(非数值)。javascript中没有整型和浮点型,Number类型用于表示所有数值。

    NaN值有两个特点:

    1.任何涉及NaN的操作都会返回NaN;

    2.NaN与任何值都不相等,包括NaN。

    复制代码
    alter(NaN==NaN);  //false
    alert(isNaN(NaN)); //true alter(isNaN(10)); // false alter(isNaN("10")); // false alter(isNaN("hello")); //true alter(isNaN(true)); // false
    复制代码

    从上面的代码中可以看出,只要能转换成数值型的都不是NaN。

    Number的转换规则:

    1.如果是数值,则直接返回

    2.如果是Boolean,则true=1,false=0

    3.如果是null,则返回0

    4.如果是undefined,则返回NaN

    5.如果是字符串,当字符串是数值,则转为数值返回,忽略前导0;当字符串是十六进制的字符串,转为十进制数返回;当字符串是""(空值)时,返回0;当字符串为其他值时,为NaN

    6.如果是Object,则先调用对象本身的.valueOf(),然后依据前面的规则进行转换,如果得到的值是NaN,再调用对象的.toString()再依据前面的规则转换。

    说到数值转换,javascript中的两个方法顺带提一下,parseInt()和parseFloat(),除了一个转换整数,一个可以转换小数以外,他们还存在一些差别的:

    1.parseInt()可以转换二进制、八进制、十进制、十六进制的数值,而parseFloat只能转换十进制的数

    2.parseFloat()始终都会忽略前导0

    String类型,除了null和undefined,任何对象都有一个.toString()方法,String类型的转换就是直接返回.toString()

    Object类型就是一组数据和函数的集合,Object类型是所有它的实例的基础,我们可以使用new关键词来创建它的实例。下面两种方式都是OK的。

    var 0=new Object;   //true
    var o=new Object();  //true

    Object类型可以直接添加属性和方法,无需定义。但是我们不能给任何基本类型的值添加属性。

    var person=new Object();
    person.name="Sherry";
    alert(person.name);   //"Sherry"
    var age=30;
    age.name="meco";
    alter(age.name);   //undefined

    Object的每一个实例都具备下面的属性和方法:

    1.constructor——保存着用于创建当前对象的函数。

    2.hasOwnProperty(propertyName)——用于检查给定的属性在当前的对象实例中是否存在。

    3.isPropertyOf(object)——用于检查传入的对象是不是另外一个对象的原型。

    4.propertyIsEnumerable(propertyName)用于检查给定的属性是否能够用for-in来枚举。

    5.toString()——返回对象的字符串

    6.valueOf()——返回对象的字符串、数值或者布尔值

    我们常用的Object类型有:Array、Date、RegExp、Function等。另外javascript种还内置了两个对象:Global和Math ,这两个对象已经实例化了,我们不需要显示的实例化这两个对象。

     二、操作符

    javascript中的大部分操作符都与其他语言的保持一致,下面说一组与其他语言不一致的操作符:相等和不等、全等和不全等。

    这两组操作符的主要区别在于:

    —1.相等和不等——先转换,再比较
    2.—全等和不全等——仅比较,不转换
    alert(“55”==55);  //true
    alert(“55”===55);  //false
    alert(“55”!=55); //false
    alert(“55”!==55); //true

    三、函数

    javascript的函数有以下的一些特点:

    —1.无须指定函数的返回值,可以在任何时候返回任何值。
    —2.函数的参数是以一个数组形式传递的(包括0个或多个值),所以函数可以传递任意数量的参数。
    —3.没有传递值的命名参数被初始化为undefined。
    —4.可以通过arguments对象来访问这些参数。 arguments对象与数组类似,通过下标访问。
    5.—函数中没有签名的概念,所以不支持重载。

    因为可以不用签名,那么javascript中还存在一种特殊的函数:匿名函数。

    function(obj1,obj2){
        ......
    }

    使用匿名函数一般有一下三种形式:

    1.函数作为参数传入另外一个函数

    2.将函数作为一个值赋值给某一个声明的变量

    3.一个函数中返回一个函数

    function demo(propertyName){
            return function(object1,object2){
                return object1[propertyName]>object2[propertyName]?object1[propertyName]: object2[propertyName];
            };
    }

     另一个值得注意的函数是:递归函数。

    递归函数在调用自身的时候,使用arguments.callee比使用函数名直接调用要更加保险。如下代码所示:

    function Sum(num){
       if(num<=1)
          return 0;
       return arguments.callee(num-2) + arguments.callee(num-1);  
    }

    假如使用函数名调用的话,可能会引起函数错误。如下所示:

    var fun=Sum;
    Sum=null;
    fun(10);  //函数报错

    虽然这种几率很小,但我们还是要尽量避免。

     四、闭包

    说完了函数,顺便说一下闭包。如何在javascript中将声明的变量私有化?——闭包可以帮我们做到。还是看代码:

    复制代码
    function demo(){
       var name="";
       
       function getName(){
          alert(name);
       }; 
    }
    复制代码

    闭包与匿名函数有点相似,只要在一个函数内构建了其他函数,就构成了闭包。

    要访问闭包内的函数,需要构建特权方法,还是以上面的代码为例:

    function demo(){
       var name="";
       //特权方法
       this.getName=function(){
          alert(name);
       }; 

    过度使用闭包会造成内存泄漏,所以闭包不能滥用,变量使用完后尽量销毁。

     

     五、Javascript面向对象的程序设计

    面向对象语言的一个标志,那就是它们都有类的概念,通过类可以创建多个具有相同属性和方法的对象。javascript中没有类,所以与其他基于类的面向对象的语言有所不同,javascript中的对象实际上就是一堆无序属性的集合。首先看一下,在javascript中我们如何创建一个具有多个属性的对象。

    复制代码
    var person=new Object();
       person.Name=””;
       person.Age=””;
       person.Sex=””;
    
       person.sayName=function(){
          alert(this.Name);
       };
    
       person.sayName();//调用
    复制代码

    创建自定义对象最简单的方式就是先创建一个Object实例,然后直接给这个实例添加属性和方法。OK,对象是创建出来了,但是使用同一个接口创建多个对象,会产生大量的重复代码。为了解决这个问题,人们创建一个广为人知的模式。

    a.工厂模式

    复制代码
    function createPerson (name,age,sex){
       var o=new Object();
       o.Name=name;
       o.Age=age;
       o.Sex=sex;
    
       o.sayName=function(){
          alert(this.Name);
       };
    
       return o;
    }
    
    //创建对象
    var person1=createPerson(“Nick”,27,”男”);
    var person2=createPerson(“Lili”,30,”女”);
    
    //对象调用
    person1.sayName();
    person2.sayName();
    复制代码

    虽然工厂模式解决了创建多个相似对象的问题,但是无法解决对象识别的问题,即无法识别创建的对象类型。于是,另一种新的模式出现了。

    b.构造函数模式

    复制代码
    function Person (name,age,sex){
       this.Name=name;
       this.Age=age;
       this.Sex=sex;
    
       this.sayName=function(){
          alert(this.Name);
       };
    }
    //创建对象
    var person1=new Person (“Nick”,27,”男”);
    var person2=new Person (“Lili”,30,”女”); 
    //对象调用
    person1.sayName();
    person2.sayName();
    复制代码

    构造函数与其他的函数唯一的区别在于调用方式的不同。

    复制代码
    //当做构造函数使用
    var person=new Person(“Grey”,”27”,”男”);
    person.sayName();
     
    
    //作为普通函数调用
    Person(“Grey”,”27”,”男”);
    window.sayName();
     
    
    //再另外一个对象的作用域中调用
    Var o=new Object();
    Person.call(o, “Grey”,”27”,”男”);
    o.sayName();
    复制代码

     使用构造函数,最主要的问题就是:每个方法都要在每个实例上重新创建一遍,即:在使用new Person()创建person对象的时候,sayName()方法被创建了两遍。

    下面的两种写法在逻辑上是等价的。

    this.sayName=function(){
      alert(this.Name);
    }
    
    this.sayName=new function(“alert(this.name)”);

    OK,继续上面的话题,既然重复创建了方法,那么我们把方法转移到函数外部,就能解决这个问题了。代码如下:

    复制代码
    function Person (name,age,sex){
       this.Name=name;
       this.Age=age;
       this.Sex=sex;
    
       this.sayName= sayName;
    }
    
    function sayName(){
      alert(this.Name);
    }
     
    //实例方法
    var person1=new Person (“Nick”,27,”男”);
    var person2=new Person (“Lili”,30,”女”);
     
    //方法调用
    person1.sayName();
    person2.sayName();
    复制代码

    最然这样解决了创建一个实例的时候一个方法被重复创建的问题,但是全局函数sayName只能对某个对象调用,而且当对象有很多方法时就要定义多个全局函数。这个问题,我们可以通过原型模型来解决。

     

    c.原型模型

    我们还是先看代码:

    复制代码
    function Person(){}
    
    Person.prototype.Name=”Nick”;
    Person.prototype.Age=27;
    Person.prototype.Sex=”男”;
    
    Person.prototype.sayName=function(){
       alert(this.Name);
    };
    
    var person1=new Person ();
    var person2=new Person ();
    
    person1.Name=”Grag”;
    person1.sayName();   //”Grag”
    person2.sayName();   //”Nick”
    
    alert(person1.sayName==person2.sayName);   //true
    
    delete person1.Name;
    
    person1.sayName();   //” Nick”
    复制代码

    原型模型我们使用了prototype(原型)属性,每一个创建的函数对象都有prototype属性,这样我们不必在函数内部定义对象,可以直接将对象添加到原型对象中。现在构造函数只是为我们创建了一个对象,但是新对象的属性和方法,是跟所有的实例共享的。

    为了减少不必要的代码和代码的整洁,我们可以对原型进行封装。

    复制代码
    function Person(){}
    
    Person.prototype={
      Name:”Nick”,
      Age=27,
      Sex=”男”;
    
      sayName=function(){
        alert(this.Name);
      };
    };
    
    var person=new Person ();
    
    person.sayName();   //”Nick”
    复制代码

    可以随时为原型添加属性和方法,但是不能重写整个原型对象。

    复制代码
    function Person(){} 
    
    var person=new Person();
    
    person.prototype.sayHi=function(){
       alert(“hello word”);
    };
    
    person.sayHi(); //hello word
     
    
    function Person(){};
    
    var person=new Person();
    
    person.prototype={
      name:”Nick”,
      sayHi:function(){
          alert("hello word");
       }
    };
    
    person.sayHi();  //error
    复制代码

    同样的,原型对象也存在缺点:

    1. 所有实例在默认情况下都取得相同的属性值

    2. 原型中所有的属性是被很多实例共享的,所以当属性是引用对象的时候,其中一个实例修改属性值时就可能会导致其他实例的属性值被改变。

    另外,顺便提一下,利用prototype我们可以实现对象的继承。

    复制代码
    function SuperFunction(){
       this.property="Super";
    }
    
    SuperFunction.prototype.getSuperValue=function(){
       return this. property;
    };
    
    function SubFunction(){
       this. subproperty="Sub";
    };
    
    SubFunction.prototype=new SuperFunction();// SubFunction继承了SuperFunction
    
    //给SubFunction添加新方法
    SubFunction.prototype.getSubValue=function(){
       return this. subproperty;
    };
    
    //重写超类的方法
    SubFunction.prototype.getSuperValue=function(){
       return "Override";
    };
    
    var instance=new SubFunction();
    instance. getSuperValue();   //"Override"
    instance. getSubValue();   //"Sub"
    复制代码

    利用prototype实现继承,也存在一些问题:

    1.如果有方法或者属性包含引用类型的值,那么当值发生变化的时候,所有引用对象的值都会变化;

    2.创建子类的实例,不能向超类型传递参数

    OK,对于上面的问题,我们可以借用构造函数实现继承,代码如下:

    复制代码
    function SuperFunction(name){
      this.name=name;
    }
    
    function SubFunction(name)
    {
       //继承SuperFunction,同时传递参数
       SuperFunction.call(this,name);
    }
    
    var instance=new SubFunction("Nick");
    alert(instance.name);   //"Nick"
    复制代码

    仅仅是借用构造函数,就产生了和构造函数一样的问题:函数无法复用。所以我们还要再进行改进,继续看代码:

    复制代码
    function SuperFunction(name){
      this.name=name;
    }
    
    SuperFunction.prototype.sayName=function(){
       alert(this.name);  
    }
    
    function SubFunction(name){
      SuperFunction.call(this,name);//继承属性
    }
    
    SubFunction.prototype=new SuperFunction();//继承方法
    
    var instance=new SubFunction("Nick");
    instance.sayName();  //"Nick"
    复制代码

    d.组合使用构造函数和原型模式

    因为原型模式的缺陷,所以在Javascript中,我们很少使用单一的原型模式,创建自定义类型最为常见的方式就是组合使用构造函数和原型模式,这样每个实力都可以共享方法,又可以创建实例的时候有一份相同属性和方法的副本,而且最大限度的节省了内存的开销。代码如下:

    复制代码
    function Person(name,age,sex){
      this.Name=name;
      this.Age=age;
      this.sex=sex;
      this.Friends=[“Shely”,”Court”];
    }
    
    Person.prototype={
       sayFriends=function(){
         alert(this. Friends);
      };
    };
    //实例化对象
    var person1=new Person(“Nick”,27,”男”);
    var person2=new Person(“Alen”,27,”女”);
    //函数调用
    person1.Friends.push(“Van”);
    person1. sayFriends();  //“Shely”,”Court”, “Van”
    person2. sayFriends();  //“Shely”,”Court”
    复制代码
  • 相关阅读:
    PHP pcntl
    Linux 远程登录命令telnet
    git .gitignore不生效
    使用 GoLand 启动 运行 Go 项目
    Go语言: 万物皆异步
    MYSQL 单表一对多查询,将多条记录合并成一条记录
    详解PHP中instanceof关键字及instanceof关键字有什么作用
    all_user_func()详解
    python的反射
    python 的魔术方法
  • 原文地址:https://www.cnblogs.com/jiangdd/p/3012857.html
Copyright © 2011-2022 走看看