JavaScript设计模式阅读随笔(一)——JavaScript面向对象

JavaScript中的面向对象其实就相当于是将需求抽象成一个对象,这个对象中包含属性和方法,可以用来描述或处理某一类实物,这个对象就叫做类。

在Java中面向对象的三要素是:封装、继承、多态,在ES6以前虽然没有class、extend这样的关键字或方法,中同样可以实现这三要素。

创建一个类

在ES6之前创建一个类通可以用:声明一个函数将其保存在变量中,内部通过对this来添加属性和方法;也可以在类的原型上添加属性和方法。

可以看到当在控制台中创建一个Book的类,Book的实例化对象book包含两个属性 id 和 name,它的隐身原型__proto__中包含一个constructor属性,它又指向了Book这个原型对象。

如果在Book的原型上添加方法或属性:好比 Book。prototype.display = function (){ ... } 每个实例化对象都会通过原型链继承到这个方法,但这并非是她自有的方法,需要通过prototype访问。

如今ES6引入Class可以这么创建类,constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。

1 class Book {
2   constructor(id, name) {
3     this.id = id;
4     this.name = name;
5   }
6 }

封装

利用javascript的函数作用域可以创建类的私有变量和私有方法,通过this创建类的共有属性和共有方法。一下代码是我理解中javascript封装的精髓

 1 var Book = function (id,name) {
 2   // 安全检查 判断执行过程中是否是当前这个对象
 3   // 避免误创建全局对象
 4   if (this instanceof Book) {
 5     this.name = name;
 6   } else {
 7     return new Book(id,name);
 8   }
 9   // 私有属性
10   var num = 1;
11   // 私有方法
12   function checkId() {
13     return num +'个'+ id;
14   }
15   // 公有属性和公有方法
16   this.id = id;
17   this.copy = function () {}
18   // 特权方法
19   this.setName = function (name){}
20   this.getName = function() {
21     // 可以访问公有方法和属性,也可以访问创建对象时的私有方法和属性
22   }
23   // 构造器
24   this.setName = function (name) {
25     // 在对象创建时,使用特权方法初始化实例对象
26   }
27   // 闭包
28   return checkId; 
29 }

继承

继承就是对原有对象的封装,从中创建私有属性、公有属性等,对于每种属性和方法特点都不一样。可以被继承的方法属性无外乎两种,一种是在构造函数中,在对象实例化时复制一遍属性和方法;一种是在类的原型对象中,这类方法和属性可以被所有实例化对象共有。

类式继承:

类式继承可以理解为把父类的实力赋值给子类的原型,通过原型链来实现继承。

1 function SuperClass() {
2     this.books = ['父类book'];
3 }
4 function SubClass() {}
5 Sub.prototype = new SuperClass();
6 
7 var instance1= new SubClass();
8 console.log(instance1.books); //['父类book']

用这种方式新创建的对象不仅可以访问父类原型上的属性和方法,还能访问父类构造函数中赋值的属性和方法,子类的原型还可以访问父类原型上的方法.

不足之处是,如果某一个实例化对象修改了books这个属性,那么所有实例化的对象books值都会被污染。

构造函数继承:

构造函数式继承可以很好的解决类式继承中所暴露的问题。它的精髓在于使用call() 这个函数

function SuperClass(id) {
  this.books = ['super'];
  this.id = id;
}
function SubClass(id) {
  SuperClass.call(this,id);
}
var instance1 = new SubClass(10);
var instance2 = new SubClass(11);

instance1.books.push('sub');
console.log(instance1.books)    // ['super', 'sub']
console.log(instance2.books)    // ['super', 'sub']
console.log(instance1.id)       // 10
console.log(instance2.id)       // 11

可以看出 通过call()这个函数改变函数的作用环境,在子类中调用父类是将子类中的变量拿到父类中执行了一遍。这一类继承不涉及到原型,所以父类原型的方法就不能被子类继承。

组合继承:

 组合继承就是结合了类式继承中原型继承,以及构造函数式继承中在子类构造函数环境中执行一次父类 这两点来继承的

function SuperClass(id) {
  this.books = ['super'];
  this.id = id;
}
SuperClass.prototype.getId = function () {
  console.log(this.id);
}
function SubClass(id,name) {
    // 构造函数中继承父类 id属性
   SuperClass.call(this,id);
   this.name = name;
}
// 类式继承原型继承父类
SubClass.prototype = new SuperClass();
// 新增子类原型方法
SubClass.prototype.getName = function () {
  console.log(this.name);
}
var instance1 = new SubClass(10,'sub1');
var instance2 = new SubClass(11,'sub2');
 
instance1.books.push('sub1-1');
console.log(instance1.books)    // ['super', 'sub1-1']
console.log(instance2.books)    // ['super']
instance1.getName()             // 10
instance1.getTime()             // sub2

 寄生组合继承 :

function inheritObject(o) {
  // 过渡对象
  function F() {}
  // 原型继承
  F.prototype = o;
  // 返回一个原型继承了父类对象的实例
  return new F();
}
function inheritPrototype(subClass, superClass) {
  // 复制一份父类的原型副本保存在变量中
  var p = inheritObject(superClass.prototype);
  //修正因为重写子类原型导致子类constructor属性被修改
  p.constructor = subClass;
  //设置子类的原型
  subClass.prototype = p;
}
function SuperClass(id) {
  this.books = ['super'];
  this.id = id;
}
SuperClass.prototype.getId = function () {
  console.log(this.id);
}
function SubClass(id,name) {
  // 构造函数中继承父类 id属性
  SuperClass.call(this,id);
  this.name = name;
}
inheritPrototype(SubClass, SuperClass)
SubClass.prototype.getName = function () {
  console.log(this.name);
}
var instance1 = new SubClass(12,'sub12');
var instance2 = new SubClass(13,'sub13');

最大的改变就是子类的原型被赋予了父类的原型的一个引用,此时子类想要添加原型方法必须通过prototype添加。

以上是ES6之前,在使用javascript继承时大多采用的方法,那么在ES6当中,我们可以采用关键字 简洁明了的达到继承的目的

 1 class ColorPoint extends Point {
 2   constructor(x, y, color) {
 3     super(x, y); // 调用父类的constructor(x, y)
 4     this.color = color;
 5   }
 6 
 7   toString() {
 8     return this.color + ' ' + super.toString(); // 调用父类的toString()
 9  } 10 }

在子类的构造函数中必须使用 super()来调用父类的构造函数,这是因为子类自己的this对象,必须先通过父类的构造函数构造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。因此只有在调用super以后,子类的this才能正常使用。

多态

多态可以理解为,在调用方法时,更具传递参数的数量、类型不同时,具有多种实现方式。 可以说根据接口不同,呈现不一样的结果。 

对于ES6以前实现继承,多态,封装的原理,主要围绕原型、原型链调用来实现,虽说目前ES6可以很好的实现这些灵魂玩法,包括目前大势的TypeSScript更是把JS 引向了强类型语言的圈子,但万变不离其宗,了解原理,才能更好的运用。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          

猜你喜欢

转载自www.cnblogs.com/realpzy/p/9352486.html