引自:阮一峰的博客
Javascript面向对象编程(一):封装
Javascript面向对象编程(二):构造函数的继承
Javascript面向对象编程(三):非构造函数的继承
JavaScript 对象封装
一、构造函数
function Cat(name,color){
this.name = name;
this.color = color;
}
每个实例特有的属性与方法放在构造函数内,在创建实例时同时创建
1. constructor属性
指向它们的构造函数。
alert(cat1.constructor == Cat); //true
alert(cat2.constructor == Cat); //true
2. instanceof运算符
验证原型对象与实例对象之间的关系。
alert(cat1 instanceof Cat); //true
alert(cat2 instanceof Cat); //true
二、prototype
实例所共有的属性和方法都指向同一内存地址:prototype对象,节省内存,提高效率
Cat.prototype.type = " 猫科";
Cat.prototype.eat = function(){alert('吃老鼠');
Prototype模式的验证方法
1. isPrototypeOf()
用来判断,某个proptotype对象和某个实例之间的关系。
alert(Cat.prototype.isPrototypeOf(cat1)); //true
alert(Cat.prototype.isPrototypeOf(cat2)); //true
2. hasOwnProperty()
用来判断某一个属性到底是本地属性,还是继承自prototype对象的属性。
alert(cat1.hasOwnProperty("name")); // true
alert(cat1.hasOwnProperty("type")); // false
3. in运算符
可以用来判断,某个实例是否含有某个属性,不管是不是本地属性。
alert("name" in cat1); // true
alert("type" in cat1); // true
还可以用来遍历某个对象的所有属性。
for(var prop in cat1) { alert("cat1["+prop+"]="+cat1[prop]); }
三、创建实例
var cat1 = new Cat("小白","黄色");
javascript 对象构造函数的继承
一、 构造函数绑定
第一种方法也是最简单的方法,使用call
或apply
方法,将父对象的构造函数绑定在子对象上,即在子对象构造函数中加一行:
function Cat(name,color){
Animal.apply(this, arguments);
this.name = name;
this.color = color;
}
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); // 动物
二、 prototype模式
直接继承父类的prototype对象,但是,直接将子类prototype指向父类的prototype对象,会导致父类prototype对象的constructor属性被改掉,所以利用空对象作为中介
由于Animal对象中,不变的属性都可以直接写入Animal.prototype。所以,我们也可以让Cat()跳过 Animal(),直接继承Animal.prototype。
现在,我们先将Animal对象改写:
function Animal(){ }
Animal.prototype.species = "动物";
var F = function(){};
F.prototype = Animal.prototype;
Cat.prototype = new F();
Cat.prototype.constructor = Cat;
F是空对象,所以几乎不占内存。
封装成一个函数,便于使用。
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); // 动物
函数体最后一行
Child.uber = Parent.prototype;
意思是为子对象设一个uber属性,这个属性直接指向父对象的prototype属性。这等于在子对象上打开一条通道,可以直接调用父对象的方法。这一行放在这里,只是为了实现继承的完备性,纯属备用性质。
三、 拷贝继承
应当分为浅拷贝和深拷贝
纯粹采用"拷贝"方法实现继承。把父对象的所有属性和方法,拷贝进子对象
function extend2(Child, Parent) {
var p = Parent.prototype;
var c = Child.prototype;
for (var i in p) {
c[i] = p[i];
}
c.uber = p;
}
使用的时候,这样写:
extend2(Cat, Animal);
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); // 动物
javascript 对象非构造函数的继承
一、object()方法
json格式的发明人Douglas Crockford,提出了一个object()函数,可以做到这一点。
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
把子对象的prototype属性,指向父对象,从而使得子对象与父对象连在一起。
使用的时候,第一步先在父对象的基础上,生成子对象:
var Doctor = object(Chinese);
然后,再加上子对象本身的属性:
Doctor.career = '医生';
这时,子对象已经继承了父对象的属性了。
二、浅拷贝
把父对象的属性,全部拷贝给子对象,也能实现继承。
下面这个函数,就是在做拷贝:
function extendCopy(p) {
var c = {};
for (var i in p) {
c[i] = p[i];
}
c.uber = p;
return c;
}
使用的时候,这样写:
var Doctor = extendCopy(Chinese);
Doctor.career = '医生';
alert(Doctor.nation); // 中国
但是,这样的拷贝有一个问题。那就是,如果父对象的属性等于数组或另一个对象,那么实际上,子对象获得的只是一个内存地址,而不是真正拷贝,因此存在父对象被篡改的可能。
所以,extendCopy()只是拷贝基本类型的数据,我们把这种拷贝叫做"浅拷贝"。
三、深拷贝
所谓"深拷贝",就是能够实现真正意义上的数组和对象的拷贝。它的实现并不难,只要递归调用"浅拷贝"就行了。
function deepCopy(p, c) {
var c = c || {};
for (var i in p) {
if (typeof p[i] === 'object') {
c[i] = (p[i].constructor === Array) ? [] : {};
deepCopy(p[i], c[i]);
} else {
c[i] = p[i];
}
}
return c;
}
使用的时候这样写:
var Doctor = deepCopy(Chinese);