JS 面向对象编程

首先申明,本文参考的是阮一峰老师的博客,因为写的真是太好了,为了加深记忆,理解,所以自己手写一份,链接是http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_encapsulation.html,本文主要抽取了里面的构造函数部分。

构造函数模式

所谓的构造函数是什么呢?其实就是一个普通函数,函数名首字母一般大写,但是内部使用了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

存在的问题

为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(); // 吃老鼠

表面看起来没有什么问题,但是其实,每次生成实例,所有的属性和方法都会重新生成一份,这样就造成了浪费内存。

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

解决方案:

就是在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

构造函数继承的五种方法

现有一个动物的构造函数

  function Animal(){

    this.species = "动物";

  }
    Animal.prototype.eat=function(){
       console.log('我是动物')
    }

还有一个猫的构造函数

  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); // 动物
    
    cat1.eat();//报错

缺点就是,无法继承父级的原型方法。

prototype模式

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

  Cat.prototype = new Animal();

  Cat.prototype.constructor = Cat;

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

  alert(cat1.species); // 动物

这里对第二行做出讲解,原来,任何一个prototype对象都有一个constructor属性,指向它的构造函数。如果没有"Cat.prototype = new Animal();"这一行,Cat.prototype.constructor是指向Cat的;加了这一行以后,Cat.prototype.constructor指向Animal。

Cat.prototype.constructor == Animal//true

更重要的是每个实例也有一个constructor属性,默认调用prototype.constructor。如果没有第二行,那么:

cat1.constructor == Cat //false

这就导致了继承链错乱,明明cat1是cat的实例化对象,怎么变成Animal了呢,因此我们必须手动纠正,这就是第二行的意义所在。

这里问题是,如果属性存在引用类型,那将将是共享的。

直接继承prototype

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

现在,我们先将Animal对象改写:

  function Animal(){ }

  Animal.prototype.species = "动物";

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

  Cat.prototype = Animal.prototype;

  Cat.prototype.constructor = Cat;

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

  alert(cat1.species); // 动物

但是缺点是Cat.prototype和Animal.prototype现在指向了同一个对象,那么任何对Cat.prototype的修改,都会反映到Animal.prototype。

利用空对象作为中介

这个方法的存在就是为了解决“直接继承prototype”的缺点的

  function extend(Child, Parent) {

    var F = function(){};

    F.prototype = Parent.prototype;

    Child.prototype = new F();

    Child.prototype.constructor = Child;

    Child.uber = Parent.prototype;

  }

函数最后一行,意思是为子对象设一个uber属性,这个属性直接指向父对象的prototype属性。(uber是一个德语词,意思是"向上"、"上一层"。)这等于在子对象上打开一条通道,可以直接调用父对象的方法。这一行放在这里,只是为了实现继承的完备性,纯属备用性质。

拷贝继承

  function extend2(Child, Parent) {

    var p = Parent.prototype;

    var c = Child.prototype;

    for (var i in p) {

      c[i] = p[i];

      }

    c.uber = p;

  }

非构造继承

Object

     var china={
         nation:'中国'
     }
     var object=function(o){
         var fn=function(){}
         fn.prototype=o;
         return new fn();
     }
     var doctor=object(china);
     console.log(doctor.nation)//中国

浅拷贝

  function extendCopy(p) {

    var c = {};

    for (var i in p) { 
      c[i] = p[i];
    }

    c.uber = p;

    return c;
  }

缺点大家都知道吧,引用类型数据无法拷贝

深拷贝

  var clone= function (obj) {
      var a;
      if (obj instanceof Array) {
         a = [];
         for (var i = 0; i < obj.length; i++) {
             a.push(this.clone(obj[i]))
         }
      } else if (obj instanceof Function) {
         return eval('(' + obj.toString() + ')')
      } else if (obj instanceof Object) {
         a = {};
         for (var i in obj) {
             a[i] = this.clone(obj[i])
         }
      } else {
         return obj;
      }
      return a;
  }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值