JavaScript面向对象笔记

From 犀牛书

继承

JavaScript中的所有数据类型都可以归结为对象,因此为了将所有对象联系起来,就需要继承机制。 首先JavaScript通过 new ,从原型对象生成实例对象。但是,JavaScript(一下简称JS)又没有引入“类”,因此JS的 new 调用的不是类,而是“构造函数”

比如像这样,构建一个猫对象的原型:

Javascript
function Cat(name){
	this.name=name;
}

对此构造函数使用new生成实例:

Javascript
var catA=new Cat('小黑');
alert(catA.name);//小黑

然而,通过this创建的属性会被实例对象独立继承拷贝,相对独立,并非共有,也会浪费内存。因此JS还提供了prototype模式,用来创建共有属性。每个构造函数都有一个prototype属性,指向一个对象该对象的所有属性和方法,都会被构造函数的实例继承。

这样一来,实例对象一旦创建,会自动引用prototype对象的属性和方法。共享属性和方法放在prototype对象中,是引用的;不共享的属性和方法放在构造函数内部,是本地的:

Javascript
Cat.prototype.type='猫科';
Cat.prototype.eat=function(){alert("吃鱼");};

生成实例则是这样:

Javascript
alert(catA.type);//猫科
catA.eat();//吃鱼

如果再实例话一个对象,起共有属性和方法均指向prototype对象,因此提高了运行效率:

Javascript
var catB=new Cat('小白');
alert(catA.eat==catB.eat);//True

还有一些验证继承模式的方法:

1.实例会自动含有一个constructor属性,指向它的构造函数:

Javascript
alert(catA.constructor==Cat);//True

2.通过instanceof运算符可以验证原型对象实例对象的关系:

Javascript
alert(catA instanceof Cat);//True

3.通过isPrototypeOf()判断原型对象实例对象的关系:

Javascript
alert(Cat.prototype.isPrototypeOf(catA));//True

4.通过hasOwnProperty()判断某一属性是来自本地还是继承引用:

Javascript
alert(catA.hasOwnProperty("name"));//True
alert(catA.hasOwnProperty("type"));//False

5.通过in运算符判断某个实例是否含有某个属性(无论本地还是引用):

Javascript
alert("name" in catA);//True
alert("type" in catA);//True

有关构造函数的继承

构造函数的继承,其实就是生成一个继承了多个对象实例

像下面这样,有两个构造函数,需要实现Cat继承Animal

Javascript
function Cat(name,color){
	this.name=name;
	this.color=color;
}
function Animal(){
	this.type="动物";
}

有以下方法:

1.构造函数绑定。使用callapply把父对象构造函数绑定在自对象上:

Javascript
function Cat(name,color){
	Animal.apply(this,arguments);
	this.name=name;
	this.color=color;
}
var catA=new Cat("小黑","黑色");
alert(catA.type);//动物

2.prototype模式。把子对象的prototype属性指向父对象的实例

Javascript
Cat.prototype = new Animal();

这样prototype对象原来的值被完全删除,包括指向其构造函数的constructor属性,并赋予了一个新的值。

因此需要重新规定其构造函数的指向:

Javascript
Cat.prototype.constructor=Cat;//

这样之后:

Javascript
var catA=new Cat("小黑","黑色");
alert(catA.type);//动物

3.直接继承父对象的prototype。可以让Cat跳过 Animal,直接继承Animal.prototype。 先设置父对象的prototype

Javascript
function Animal(){ }
Animal.prototype.type = "动物";

再把Cat的prototype指向Animal的prototype,当然因为修改了prototype,构造函数也要改:

Javascript
Cat.prototype = Animal.prototype;
Cat.prototype.constructor = Cat;

这样做的结果是不错,但是因为把父对象和子对象的原型关联了,任何对Cat.prototype的修改都会反映到Animal.prototype,其实Animal.prototype对象的constructor属性也已经被修改成Cat了。

因此最好的方法是利用空对象作为中介:

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

现将空对象prototype指向父对象prototype,再把子对象prototype属性指向空对象的实例,这其实是上面两种方法的结合。

4.prototype模式的封装函数,这是更有可用性的方法:

Javascript
function extend(Child, Parent) {
	var F = function(){};
	F.prototype = Parent.prototype;
	Child.prototype = new F();
	Child.prototype.constructor = Child;
}

调用的时候像这样:

Javascript
extend(Cat,Animal);
var catA = new Cat("小黑","黑色");
alert(catA.type); // 动物

5.纯粹拷贝方法,把父对象的所有属性和方法拷贝进子对象:

Javascript
function extend(Child, Parent) {
	var p = Parent.prototype;
	var c = Child.prototype;
	for (var i in p) {
		c[i] = p[i];
	}
	c.uber = p;//这是为了方便子对象直接调用父对象的prototype属性
}

有关非构造函数的继承

所谓非构造函数,就是普通对象,像下面这样,有两个普通对象,需要实现Cat继承Animal

Javascript
var Cat={
	name:"猫"
}
var Animal={
	type:"动物"
}

有以下方法:

1.使用object()函数。object()函数就是把子对象prototype属性指向父对象,使子对象与父对象连在一起:

Javascript
function object(o) {
	function F() {}
	F.prototype = o;
	return new F();
}

因此先在父对象的基础上生成子对象:

Javascript
var Cat = object(Animal);

子对象随后继承了父对象的属性:

Javascript
alert(Cat.type); //动物

2.浅拷贝。把父对象的属性全部拷贝给子对象,实现继承:

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

调用的时候像这样:

Javascript
var Cat = extendCopy(Animal);
alert(Cat.type); // 动物

浅拷贝存在一个问题: 如果父对象的属性等于数组或另一个对象,那么子对象获得的只是一个内存地址引用,而不是真正拷贝,因此存在父对象可能被篡改。

3.深拷贝。用来完成包括数组和对象的拷贝。

Javascript
function deepCopy(p,c){
	var c||{};
	for(var i in p){
		if(typeof p[i]==='object'){//判断父对象的属性是否为对象
			c[i]=(p[i].contructor===Array)?[]:{};//或者数组
			deepCopy(p[i],c[i]);//递归调用,拷贝数组对象内部属性
		}
		else{
			c[i]=p[i];
		}
	}
	return c;
}

调用的时候依然这样:

Javascript
var Cat = deepCopy(Animal);
alert(Cat.type); // 动物

先到这里~~