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

    声明:本文转载自阮一峰的博客,使用请注明

    Javascript是一种基于对象(object-based)的语言,所有东西几乎都是对象。但是,它又不是一种真正的面向对象编程(OOP)语言,因为它的语法中没有class(类)。 如果我们要把"属性"(property)和"方法"(method),封装成一个对象,甚至要从原型对象生成一个实例对象可以有以下几种方法:

    假定我们把猫看成一个对象,它有"名字"和"颜色"两个属性。   

     var Cat = {    

       name : '',   

       color : ''   

     }   

     我们需要根据这个原型对象生成两个实例对象。     

    var cat1 = {}; // 创建一个空对象     

    cat1.name = "大毛";      

    cat1.color = "黄色";   

    var cat2 = {};     

    cat2.name = "二毛";     

    cat2.color = "黑色";   

     这就是最简单的封装了,把两个属性封装在一个对象里面。但是,这样的写法有两个缺点,一是如果多生成几个实例,写起来就非常麻烦;二是实例与原型之间,没有任何办法,可以看出有什么联系。

    我们可以写一个函数,解决代码重复的问题。   

     function Cat(name,color){    

       return {       

        name:name,       

        color:color     

      }  

     }

    然后 生成实例对象,就等于是在调用函数:

    var cat1 = Cat("大毛","黄色");

    var cat2 = Cat("二毛","黑色");

    这种方法的问题依然是,cat1和cat2之间没有内在的联系,不能反映出它们是同一个原型对象的实例。

    为了解决从原型对象生成实例的问题,Javascript提供了一个构造函数(Constructor)模式。所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量。对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上。   

     function Cat(name,color){    

      this.name=name;     

      this.color=color;      

    }     

    var cat1 = new Cat("大毛","黄色");  

     var cat2 = new Cat("二毛","黑色");  

     alert(cat1.name); // 大毛   

    alert(cat1.color); // 黄色     

    cat1和cat2会自动含有一个constructor属性,指向它们的构造函数。   

     alert(cat1.constructor == Cat); //true   

     alert(cat2.constructor == Cat); //true   

    Javascript还提供了一个instanceof运算符,验证原型对象与实例对象之间的关系。     

    alert(cat1 instanceof Cat); //true     

    alert(cat2 instanceof Cat); //true 

    构造函数方法很好用,但是存在一个浪费内存的问题。我们现在为Cat对象添加一个不变的属性"type", 再添加一个方法eat(吃老鼠)。那么,原型对象Cat就变成了下面这样:

    function Cat(name,color){     

       this.name = name;     

       this.color = color;    

       this.type = "猫科动物";    

       this.eat = function(){

        alert("吃老鼠");

       };

    }

    还是采用同样的方法,生成实例:     

    var cat1 = new Cat("大毛","黄色");  

     var cat2 = new Cat ("二毛","黑色");   

    alert(cat1.type); // 猫科动物   

    cat1.eat(); // 吃老鼠

    这样做,有一个很大的弊端。那就是对于每一个实例对象,type属性和eat()方法都是一模一样的内容 每一次生成一个实例,都必须为重复的内容,多占用一些内存。这样既不环保,也缺乏效率。         

    alert(cat1.eat == cat2.eat); //false 

    Javascript规定,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法 都会被构造函数的实例继承。我们可以把那些不变的属性和方法,直接定义在prototype对象上。        

     function Cat(name,color){    

     this.name = name;    

     this.color = color;   

    }   

    Cat.prototype.type = "猫科动物";  

    Cat.prototype.eat = function(){alert("吃老鼠")};

    然后,生成实例。   

     var cat1 = new Cat("大毛","黄色");     

     var cat2 = new Cat("二毛","黑色");   

    alert(cat1.type); // 猫科动物   

    cat1.eat(); // 吃老鼠

    这时所有实例的type属性和eat()方法,其实都是同一个内存地址,指向prototype对象,因此就提高 了运行效率。     

    alert(cat1.eat == cat2.eat); //true

    isPrototypeOf()     这个方法用来判断,某个proptotype对象和某个实例之间的关系。   

    alert(Cat.prototype.isPrototypeOf(cat1));//true     

    alert(Cat.prototype.isPrototypeOf(cat2));//true

    hasOwnProperty()     用来判断某一个属性到底是本地属性,还是继承自prototype对象的属性     

    alert(cat1.hasOwnProperty("name")); // true   

    alert(cat1.hasOwnProperty("type")); // false

    现在有一个"动物"对象的构造函数。   

      function Animal(){     this.species = "动物";   }

    还有一个"猫"对象的构造函数。     

     function Cat(name,color){     this.name = name;     this.color = color;   }

    使"猫"继承"动物

    使用call或apply方法,将父对象的构造函数绑定在子对象上,即在子对象构造函数中加一行   

     function Cat(name,color){     

      Animal.apply(this, arguments);     

      this.name = name;     

      this.color = color;   

     }  

     var cat1 = new Cat("大毛","黄色");  

     alert(cat1.species); // 动物

    如果"猫"的prototype对象,指向一个Animal的实例,那么所有"猫"的实例,就能继承Animal了。     

    Cat.prototype = new Animal();    

    Cat.prototype.constructor = Cat;    

    var cat1 = new Cat("大毛","黄色");     a

    lert(cat1.species); // 动物

    任何一个prototype对象都有一个constructor属性,指向它的构造函数。如果没有"Cat.prototype = new Animal();"这一行,Cat.prototype.constructor是指向Cat的;加了这一行以后,Cat.prototype.constructor指向Animal。     alert(Cat.prototype.constructor == Animal); //true

    更重要的是,每一个实例也有一个constructor属性,默认调用prototype对象的constructor属性。     

    alert(cat1.constructor == Cat.prototype.constructor); // true

    在运行"Cat.prototype = new Animal();"这一行之后,cat1.constructor也指向Animal   

     alert(cat1.constructor == Animal); // true

    这显然会导致继承链的紊乱(cat1明明是用构造函数Cat生成的),因此我们必须手动纠正,将Cat.prototype对象的constructor值改为Cat

    这种方法是对第二种方法的改进。由于Animal对象中,不变的属性都可以直接写入Animal.prototype。所以,我们也可以让Cat()跳过 Animal(),直接继承Animal.prototype     

    function Animal(){ }   

    Animal.prototype.species = "动物";

    将Cat的prototype对象,然后指向Animal的prototype对象,这样就完成了继承。       

     Cat.prototype = Animal.prototype;        

     Cat.prototype.constructor = Cat;        

     var cat1 = new Cat("大毛","黄色");          a

    lert(cat1.species); // 动物 这样做的优点是效率比较高(不用执行和建立Animal的实例了),比较省内存。缺点是 Cat.prototype和Animal.prototype现在指向了同一个对象,那么任何对 Cat.prototype的修改,都会反映到Animal.prototype 第二行实际上把Animal.prototype对象的constructor属性也改掉了!   

     alert(Animal.prototype.constructor); // Cat

    由于"直接继承prototype"存在上述的缺点,所以就有第四种方法,利用一个空对象作为中介。     

    var F = function(){};   

    F.prototype = Animal.prototype;   

    Cat.prototype = new F();  

    Cat.prototype.constructor = Cat;     

    F是空对象,所以几乎不占内存。这时,修改Cat的prototype对象,就不会影响Animal   的prototype对象。     

    alert(Animal.prototype.constructor); // Animal     

    将上面的方法,封装成一个函数,便于使用。

    function extend(Child, Parent) {
       var F = function(){};      

       F.prototype = Parent.prototype;        

       Child.prototype = new F();      

       Child.prototype.constructor = Child;      

       Child.uber = Parent.prototype;   

    }   

     使用的时候,方法如下     

    extend(Cat,Animal);    

    var cat1 = new Cat("大毛","黄色");    

    alert(cat1.species); // 动物       

    这个extend函数,就是YUI库如何实现继承的方法。

    简单说,就是把父对象的所有属性和方法,拷贝进子对象 把Animal的所有不变属性,都放到它的prototype对象上。     

    function Animal(){}    

    Animal.prototype.species = "动物";

    然后,再写一个函数,实现属性拷贝的目的。   

     function extend2(Child, Parent) {     

      var p = Parent.prototype;     

      var c = Child.prototype;     

      for (var i in p) {       

        c[i] = p[i];       

      }     

      c.uber = p;   

    }

    这个函数的作用,就是将父对象的prototype对象中的属性,一一拷贝给Child对象的prototype对象。 使用的时候,这样写:   

     extend2(Cat, Animal);      

    var cat1 = new Cat("大毛","黄色");      

    alert(cat1.species); // 动物

  • 相关阅读:
    209. Minimum Size Subarray Sum
    208. Implement Trie (Prefix Tree)
    207. Course Schedule
    206. Reverse Linked List
    205. Isomorphic Strings
    204. Count Primes
    203. Remove Linked List Elements
    201. Bitwise AND of Numbers Range
    199. Binary Tree Right Side View
    ArcGIS API for JavaScript 4.2学习笔记[8] 2D与3D视图同步
  • 原文地址:https://www.cnblogs.com/qhhw/p/5946069.html
Copyright © 2011-2022 走看看