引自
Javascript面向对象编程(一):封装
Javascript面向对象编程(二):构造函数的继承
Javascript面向对象编程(三):非构造函数的继承

JavaScript 对象封装

一、构造函数

  1. function Cat(name,color){
  2. this.name = name;
  3. this.color = color;
  4. }

每个实例特有的属性与方法放在构造函数内,在创建实例时同时创建

1. constructor 属性

指向它们的构造函数。

  1. alert(cat1.constructor == Cat); //true
  2. alert(cat2.constructor == Cat); //true

2. instanceof 运算符

验证原型对象与实例对象之间的关系。

  1. alert(cat1 instanceof Cat); //true
  2. alert(cat2 instanceof Cat); //true

二、prototype

实例所共有的属性和方法都指向同一内存地址:prototype 对象,节省内存,提高效率

  1. Cat.prototype.type = " 猫科";
  2. Cat.prototype.eat = function(){alert('吃老鼠');

Prototype模式的验证方法

1. isPrototypeOf()

用来判断,某个proptotype对象和某个实例之间的关系。

  1. alert(Cat.prototype.isPrototypeOf(cat1)); //true
  2. alert(Cat.prototype.isPrototypeOf(cat2)); //true

2. hasOwnProperty()

用来判断某一个属性到底是本地属性,还是继承自prototype对象的属性。

  1. alert(cat1.hasOwnProperty("name")); // true
  2. alert(cat1.hasOwnProperty("type")); // false

3. in 运算符

可以用来判断,某个实例是否含有某个属性,不管是不是本地属性。

  1. alert("name" in cat1); // true
  2. alert("type" in cat1); // true

还可以用来遍历某个对象的所有属性。

  1. for(var prop in cat1) {
  2. alert("cat1["+prop+"]="+cat1[prop]);
  3. }

三、创建实例

  1. var cat1 = new Cat("小白","黄色");

JavaScript 对象构造函数的继承

一、 构造函数绑定

第一种方法也是最简单的方法,使用 callapply 方法,将父对象的构造函数绑定在子对象上,即在子对象构造函数中加一行:

  1. function Cat(name,color){
  2. Animal.apply(this, arguments);
  3.  this.name = name;
  4.  this.color = color;
  5. }
  6. var cat1 = new Cat("大毛","黄色");
  7. alert(cat1.species); // 动物

二、 prototype 模式

直接继承父类的 prototype 对象,但是,直接将子类 prototype 指向父类的 prototype 对象,会导致父类 prototype 对象的 constructor 属性被改掉,所以利用空对象作为中介

由于 Animal 对象中,不变的属性都可以直接写入 Animal.prototype。所以,我们也可以让 Cat() 跳过 Animal(),直接继承 Animal.prototype

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

  1.   function Animal(){ }
  2.   Animal.prototype.species = "动物";
  3.   var F = function(){};
  4.   F.prototype = Animal.prototype;
  5.   Cat.prototype = new F();
  6.   Cat.prototype.constructor = Cat;

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

封装成一个函数,便于使用。

  1.   function extend(Child, Parent) {
  2.     var F = function(){};
  3.     F.prototype = Parent.prototype;
  4.     Child.prototype = new F();
  5.     Child.prototype.constructor = Child;
  6.     Child.uber = Parent.prototype;
  7.   }

使用的时候,方法如下

  1. extend(Cat,Animal);
  2. var cat1 = new Cat("大毛","黄色");
  3. alert(cat1.species); // 动物

函数体最后一行

  1. Child.uber = Parent.prototype;

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

三、 拷贝继承

应当分为浅拷贝和深拷贝

纯粹采用”拷贝”方法实现继承。把父对象的所有属性和方法,拷贝进子对象

  1. function extend2(Child, Parent) {
  2. var p = Parent.prototype;
  3. var c = Child.prototype;
  4. for (var i in p) {
  5.    c[i] = p[i];
  6. }
  7. c.uber = p;
  8. }

使用的时候,这样写:

  1. extend2(Cat, Animal);
  2. var cat1 = new Cat("大毛","黄色");
  3. alert(cat1.species); // 动物

JavaScript 对象非构造函数的继承

一、object() 方法

json 格式的发明人 Douglas Crockford,提出了一个 object() 函数,可以做到这一点。

  1. function object(o) {
  2. function F() {};
  3. F.prototype = o;
  4. return new F();
  5. }

把子对象的 prototype 属性,指向父对象,从而使得子对象与父对象连在一起。

使用的时候,第一步先在父对象的基础上,生成子对象:

  1. var Doctor = object(Chinese);

然后,再加上子对象本身的属性:

  1. Doctor.career = '医生';

这时,子对象已经继承了父对象的属性了。

二、浅拷贝

把父对象的属性,全部拷贝给子对象,也能实现继承。

下面这个函数,就是在做拷贝:

  1. function extendCopy(p) {
  2. var c = {};
  3. for (var i in p) {
  4. c[i] = p[i];
  5. }
  6. c.uber = p;
  7. return c;
  8. }

使用的时候,这样写:

  1. var Doctor = extendCopy(Chinese);
  2. Doctor.career = '医生';
  3. alert(Doctor.nation); // 中国

但是,这样的拷贝有一个问题。那就是,如果父对象的属性等于数组或另一个对象,那么实际上,子对象获得的只是一个内存地址,而不是真正拷贝,因此存在父对象被篡改的可能。

所以,extendCopy()只是拷贝基本类型的数据,我们把这种拷贝叫做”浅拷贝”。

三、深拷贝

所谓”深拷贝”,就是能够实现真正意义上的数组和对象的拷贝。它的实现并不难,只要递归调用“浅拷贝”就行了。

  1. function deepCopy(p, c) {
  2. var c = c || {};
  3. for (var i in p) {
  4. if (typeof p[i] === 'object') {
  5. c[i] = (p[i].constructor === Array) ? [] : {};
  6. deepCopy(p[i], c[i]);
  7. }
  8. else {
  9. c[i] = p[i];
  10. }
  11. }
  12. return c;
  13. }

使用的时候这样写:

  1. var Doctor = deepCopy(Chinese);