本文是看了网上的文章后,自己总结了一些感觉适合自己的,有些地方的说法可能不太规范,甚至是错误的,希望各位看官多多包涵并予以指正,谢谢各位
原文链接: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规定,每一个构造函数都有一个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。
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
使用call或apply方法,将父对象的构造函数绑定在子对象上
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
使Cat的prototype对象,指向一个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值改为Cat,Cat.prototype.constructor = Cat。
var cat1 = new Cat();
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中的属性,但本身并没有这些属性。
function Cat(name,color){ this.name = name; this.color = color; } Cat.prototype = Animal.prototype; Cat.prototype.constructor = Cat;
Cat.prototype和Animal.prototype现在指向了同一个对象,那么任何对Cat.prototype的修改,都会反映到Animal.prototype。这是我们不希望的。
同时无法继承species属性
var F = function(){}; F.prototype = Animal.prototype; Cat.prototype = new F(); Cat.prototype.constructor = Cat;
F是空对象,所以几乎不占内存。
同方法2,Animal的species属性会被包含在Cat.prototype中,而不是Cat的实例中
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,它们在内存的存储
如下所示:
初始时,由于F是空函数,Cat.prototype中没有任何属性,执行Cat.prototype.constructor = Cat;后,新生成了属性constructor。
未对Cat.prototype.constructor赋值前,其实调用的是Animal.prototype中constructor。