JavaScript面向对象的实现

本文是看了网上的文章后,自己总结了一些感觉适合自己的,有些地方的说法可能不太规范,甚至是错误的,希望各位看官多多包涵并予以指正,谢谢各位 

原文链接:http://www.ruanyifeng.com/blog/it/javascript/

这里只考虑构造函数模式。

每一个对象(object)指向一个存储空间,同时这个空间还有一个prototype属性指向其原型,prototype的最顶层是__proto__:Object。(我是这么理解的,错了勿怪

使用构造函数模拟类

所谓"构造函数",其实就是一个普通函数,但是内部使用了this属性。对构造函数使用new运算符,就能生成实例,并且this属性会绑定在实例对象上。

下面是一个构造函数

function Cat(name,color){
       this.name=name;
    this.color=color;
       this.eat = function(){alert("吃老鼠");};
}

生成实例对象

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

cat1 指向一个存储空间cat1,里面有name 和color以及eat 三个属性

JavaScript面向对象的实现

不变、共享属性的实现

上面的构造函数,每生成一个实例,都会指向一个独立的存储空间。

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("大毛","黄色");

这时所有实例的type属性和eat()方法,其实都是同一个内存地址,指向prototype对象

此时cat1中如下所示:(此处是在Chrome控制台测试输出的结果)

Cat {name"大毛"color"黄色"type"猫科动物"eat: function}

Ø color"黄色"

Ø name"大毛"

Ø __proto__: Cat

n constructor: function Cat(name,color){

n eat: function (){alert("吃老鼠")}

n type"猫科动物"

n __proto__: Object

注意:

cat1的prototype指向的对象中,还有一个prototype。这是因为cat1的prototype指向的是Cat的prototype ,Cat的prototype指向的对象中又有自己的prototype。 

JavaScript面向对象的实现

type和eat都在__proto__:Cat指向的另一个对象(Cat.prototype)中,并不在cat1中。

调用type, eat

cat1.type或Cat.type,不可以cat1.prototype.type

注意:

cat1.type这种方式不可以为type赋值

cat1.type = "黄色的猫";这句代码会在cat1空间中新生成一个属性type,prototype中的type不受影响;Cat.type = "黄色的猫";会改变prototype中的type。

构造函数的继承

有一个构造函数Animal

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

Animal.prototype.class = "Animal";



现在要使Cat继承自Animal

方法1  构造函数绑定

使用callapply方法,将父对象的构造函数绑定在子对象上

function Cat(name,color){
    Animal.apply(this, arguments);
    this.name = name;
    this.color = color;
}
var cat1 = new Cat("Kitty", "yellow");

这样Cat可以继承Animal的species,但不可以继承prototype中的class

方法2  prototype模式

使Catprototype对象,指向一个Animal的实例

function Cat(name,color){
    this.name = name;
    this.color = color;
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;

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

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

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

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

var cat1 = new Cat();

JavaScript面向对象的实现

Cat.prototype指向了Animal的一个实例,对Cat.prototype的修改不会影响到Animal.prototype,包含了species(这是我们不希望的,species应该在Cat1中),Cat.prototype.constructor本来是指向Animal.prototype.constructor的,调用执行Cat.prototype.constructor = Cat后,重新赋值。(从控制台的结果来看,Cat.prototype中本没有constructor,执行赋值语句后新生成了constructor)

简而言之,Cat.prototype现在指向Animal的一个实例,可以调用Animal.prototype中的属性,但本身并没有这些属性。

方法3  直接继承prototype

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

Cat.prototypeAnimal.prototype现在指向了同一个对象,那么任何对Cat.prototype的修改,都会反映到Animal.prototype这是我们不希望的。

同时无法继承species属性

方法4  利用空对象作为中介

var F = function(){};
F.prototype = Animal.prototype;
Cat.prototype = new F();
Cat.prototype.constructor = Cat;

F是空对象,所以几乎不占内存。

同方法2Animalspecies属性会被包含在Cat.prototype中,而不是Cat的实例中

方法5  拷贝继承

var p = Parent.prototype;
var c = Child.prototype;

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

为子对象设一个uber属性,这个属性直接指向父对象的prototype属性。这等于在子对象上打开一条通道,可以直接调用父对象的方法。这一行放在这里,只是为了实现继承的完备性,纯属备用性质。

这样,修改Child.prototype时不会影响Parent.prototype,但当Parent.prototype中的属性是Object时,修改Child.prototype也会影响到Parent.prototype

同样这种方法也无法实现可变属性的继承。

综合方法

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

function F(){}
F.prototype = Animal.prototype;
Cat.prototype = new F();
Cat.prototype.constructor = Cat;

这样既继承了可变属性,又继承了prototype中的不变属性,且没有多余属性

不足:

Cat.prototype指向F的实例,F.prototype指向Animal.prototype,它们在内存的存储

如下所示:

JavaScript面向对象的实现

初始时,由于F是空函数,Cat.prototype中没有任何属性,执行Cat.prototype.constructor = Cat;后,新生成了属性constructor。

未对Cat.prototype.constructor赋值前,其实调用的是Animal.prototype中constructor。

你可能感兴趣的:(JavaScript,继承,面向对象,构造函数法)