1

一.创建一个对象

起因.................
用Object或对象字面量创建对象会产生大量的重复代码,而且只能通过变量名来区分不同的对象,没有记录对象的类型
例如:

 //Object创建对象
 var student = new Object();
 student.name = 's';
 student.age = "20";
 var student1 = new Object();
 student1.name = "s1";
 student.age = "20";
 //字面量创建对象
 var student = {
   name: 's',
   age: '23'
 }
 var student1 = {
   name: 's',
   age: '23'
 }

1.工厂模式创建对象

工厂模式解决了相似代码重复书写的问题

//好处:js中无法创建类,为了模仿类的创建方式,抽象具体对象的创建过程;
//缺点:工厂模式创建的对象是通过普通函数调用实现的,没有解决标识对象类型的问题,无法区分对象的类型,
  function person(name) {
   var o = new Object();
   o.name = name;
   o.sayName = function() {
     console.log(this.name)
   }
   return o;
 }
 var p1 = person('a');
 var p2 = person('b');

2.构造函数模式创建对象

 function Person(name) {
   this.name = name;
   this.sayName = function() {
     console.log(this.name);
   }
//function() { console.log(this.name);}相当于new Function(){console.log(this.name);}}
 var a = new Person('ww');
 a.sayName();
 console.log(a)

new 一个实例的过程:
(1)先创建一个对象
(2)将构造函数的作用域赋给新对象
(3),执行构造函数中的代码
(4)返回新对象

构造函数模式解决了标识构造函数类型的问题,但是构造函数内部每定义一个函数就会实例化一个对象,可以通过将公用的方法提到全局环境下,通过普通函数调用的方式实现调用函数,避免了重复实例化对象的问题

function Person(name) {
   this.name = name;
   this.sayName = sayName;
 }

 function sayName() {
   console.log(this.name)
 }
 var a = new Person('p1');
 var b = new Person('p2');
 a.sayName();
 b.sayName();

如果Person内部封装较多的函数,会导致定义大量的全局函数,这些函数散乱分布在全局环境中,失去了封装性.为了解决这个问题,可以用原型模式创建对象

3.原型模式

原型模式创建对象,把属性和函数添加到对象的prototype上,实例化一个对象p1,p1可以通过原型链访问到原型链上的对象

 function Person() {}
 Person.prototype.name = "ww";
 Person.prototype.sayName = function() {
   console.log(this.name);
 }
 var p1 = new Person();
 p1.sayName();

(1)原型模式的另一种方式:
clipboard.png
这种方式会导致,Person.prototype上原本指向Person的constructor丢失,可以手动添加constructor属性,如下
clipboard.png

(2)如果先创建实例,再定义Person.prototyp的值会报错,p1还是通过[[prototype]]隐式属性指向Person没修改过的原型,导致找不到sayName方法
clipboard.png

解释如js高级程序设计一书中的例子:
clipboard.png
clipboard.png

(3)原型链模式存在以下问题:(1),prototype上的属性和方法共享,一个对象对prototype属性的修改会影响另一个对象的属性;(2)不能传递参数

clipboard.png

4.组合使用构造函数和原型函数

我们可以把一些共享的属性和方法添加到prototype,再利用构造函数在实例对象上添加不同属性

clipboard.png

二.继承

1.原型链继承

通过将一个构造函数的原型重新赋值(另一个构造函数的实例)实现继承
例子:

function SuperType() {
  this.property = ['p1'];
}
SuperType.prototype.getValue = function() {
  console.log(this.property);
}

function SubType() {
  this.subproperty = ['1'];
}
SubType.prototype.getSubValue = function() {
  console.log("getSubVlue", this.subproperty);
}
SubType.prototype = new SuperType();
var a = new SubType();
console.log("a", a);

结果:
clipboard.png

对于引用类型属性,原型链继承会将该属性作为公共属性,谁都可以对它的值进行修改;对于像name这样的非引用类型,每创建一个实例就会定义一个新额属性,不会和其他实例中的属性共享,如下所示

clipboard.png

2.借用构造函数

为了解决引用类型值共享的问题和原型链继承不能传递参数的缺陷,可以在子类型构造函数的内部调用超类型构造函数。如下:

function SuperType() {
  this.property = ['p1'];
}
function SubType() {
  this.subproperty = ['1'];
  SuperType.call(this);
}
SubType.prototype = new SuperType();
var a = new SubType();
var b = new SubType();
a.property.push('2');
console.log(a.property, b.property);

clipboard.png

利用单纯的借用构造函数解决了引用类型值共享的问题,但是如果大量的函数写着超类中,函数无法复用,所有需要结合原型构造函数.

3.组合构造函数

组合构造函数到的思路是将利用构造函数实例实现对属性的继承,利用原型链来实现对原型对象的属性和函数的继承.

clipboard.png

由于给SubType.prototype直接赋值为SuperType的实例,导致constructor丢失,利用Object.defineProperty找回

clipboard.png

4.原型式继承

思路:基于已有的对象创建新对象,继承一个现成的对象

clipboard.png

clipboard.png
原型式继承存在的问题就和原型模式一样,对于引用型属性有共享的特性

5.寄生式继承

思路:创建一个仅用于封装继承过程的函数

clipboard.png

该继承方式存在的问题是a对象内部的函数不能复用

6.寄生组合继承

组合构造函数也有缺陷,需要调用两次超类构造函数,降低效率

function SuperType(name) {
  this.name = name;
  this.books = ['b1']
}

SuperType.prototype.getName = function() {
  console.log(this.name);
}

function SubType(name, age) {
  this.age = age;
  SuperType.call(this, name); //第二次调用构造函数
}

SubType.prototype = new SuperType(); //第一次调用构造函数
SubType.prototype.constructor = SubType;
SubType.prototype.getSubValue = function() {
  console.log("getSubVlue", this.age);
}

利用寄生式继承来继承超类的原型,利用构造函数继承实例属性

clipboard.png

有不对或者表达不准确的地方欢迎指出!


Delia
75 声望3 粉丝

路漫漫其修远兮,吾将上下而求索。


« 上一篇
flex