Javascript面向对象(七)——类模式

Javascript面向对象(七)——类模式

在面向对象编程里,类是一个为创建对象的可扩展编程代码模板。提供了状态的初始值(成员变量),实现行为(成员函数或方法)。——维基百科

在Javascript中有特定语法结构以及关键字class,但学习之前,我们应该考虑术语“类”是面向对象编程理论。其定义是引用上面内容,且和语言无关。

即使不用class关键字,在Javascript也有几种众所周知的类编程模型。这里我们首先讨论它们。

类结构将在下一章描述,但在Javascript中它是个“语法糖”,我们马上要学习模型中一个的扩展。

函数类模式

根据类的定义,下面代码的构造函数可以视作类:

function User(name) {
  this.sayHi = function() {
    alert(name);
  };
}

let user = new User("John");
user.sayHi(); // John

它遵循定义的所有部分:
- 是创建对象的编程代码模板(使用new调用)
- 提供了状态初始值(name,从参数提供)
- 提供了方法(sayHi)

这被称为函数类模型。局部变量和内嵌函数在User里面,没有赋值给this,对内可见,但是不能被外部代码访问。
所以我们能容易增加内部函数和变量,如calAge():

function User(name, birthday) {

  // only visible from other methods inside User
  function calcAge() {
    new Date().getFullYear() - birthday.getFullYear();
  }

  this.sayHi = function() {
    alert(name + ', age:' + calcAge());
  };
}

let user = new User("John", new Date(2000,0,1));
user.sayHi(); // John

上面代码中,name、birthday和函数calcAge()在内部,是对象私有的,仅仅在对象内部可见。
另外方面,sayHi是外部的,公共方法。外部代码可以访问,如创建的对象user可以访问它。
这种方式能够让外部访问的助手方法隐藏实现细节,仅赋值this的方法成为外部可见的。

工厂类模式

我们能创建类根本不使用new关键字,如下代码:
function User(name, birthday) {
// only visible from other methods inside User
function calcAge() {
new Date().getFullYear() - birthday.getFullYear();
}

  return {
    sayHi() {
      alert(name + ', age:' + calcAge());
    }
  };
}

let user = User("John", new Date(2000,0,1));
user.sayHi(); // John

我们能看到,函数User返回一个对象,带有公共的方法。这种方式唯一好处就是可以省略new关键字,直接写 let user = User(...) ,代替写let user = new User(...),除此以外,几乎和函数类模式完全一样。

基于原型类模式

基于原型类模式是最重要、最好的方式。实践中函数类模式、工厂类模式很少使用。很快我们会说明原因。
这里我们用原型方式重新写上面代码:

function User(name, birthday) {
  this._name = name;
  this._birthday = birthday;
}

User.prototype._calcAge = function() {
  return new Date().getFullYear() - this._birthday.getFullYear();
};

User.prototype.sayHi = function() {
  alert(this._name + ', age:' + this._calcAge());
};

let user = new User("John", new Date(2000,0,1));
user.sayHi(); // John

代码结构解释如下:

  • 构造函数User仅初始化当前对象状态。
  • 方法通过原型增加:User.prototype.
    我们看到,方法在词法上不在function User内部,他们不共享一个词法环境。如果我们声明变量在function User内部,那么他们对方法不可见。
    所以,有一个广泛的共识是:内部属性和方法通常使用“”开头,如_name_calcAge()。技术,这仅是一个约定,外部代码仍然能访问他们,但几乎所有的开发者认同“”的意思,外部代码尽力不触碰下划线开头的属性和方法。

这里是原型模式优于函数类模式之处:

  • 在函数模式中,每个对象有自己所有方法的拷贝,示例中的拷贝是:this.sayHi = function(){...},以及其他构造函数中的方法。
  • 在原型模式中,所有方法在User.prototype中,为所有对象共享,对象仅存储数据。

所以,原型模式是更有效的。

不仅如此,原型允许我们使用有效的方式实现继承。内置Javascript对象都使用原型。也有特定的语法机构提供漂亮的语法方式实现“类”,后续详解。

基于原型的类继承

我们有两个基于原型的类Rabbit:

function Rabbit(name) {
  this.name = name;
}

Rabbit.prototype.jump = function() {
  alert(this.name + ' jumps!');
};

let rabbit = new Rabbit("My rabbit");

和Animal类:

function Animal(name) {
  this.name = name;
}

Animal.prototype.eat = function() {
  alert(this.name + ' eats.');
};

let animal = new Animal("My animal");

现在他们完全独立。

但我们想让Rabbit继承Animal,换句话说,rabbit应该基于animals,能够访问Animal的方法,并可以扩展实现自己的方法。
Javascript语言中原型意味什么?
现在rabbit对象的方法在Rabbit.prototype中,我们想如果访问rabbit对象的某个方法不在Rabbit.prototype中,使用Animal.prototype作为后备。
所以原型链应该是rabbit –> Rabbit.prototype –>Animal.prototype. 如图所示:

代码实现:

// Same Animal as before
function Animal(name) {
  this.name = name;
}

// All animals can eat, right?
Animal.prototype.eat = function() {
  alert(this.name + ' eats.');
};

// Same Rabbit as before
function Rabbit(name) {
  this.name = name;
}

Rabbit.prototype.jump = function() {
  alert(this.name + ' jumps!');
};

// setup the inheritance chain
Rabbit.prototype.__proto__ = Animal.prototype; // (*)

let rabbit = new Rabbit("White Rabbit");
rabbit.eat(); // rabbits can eat too
rabbit.jump();

(*)行设置了原型链,所以rabbit首先在Rabbit.prototype中搜索方法,然后是Animal.prototype,搜到完成,如果Animal.prototype中仍没有搜到,会继续在Object.prototype中搜索,因为Animal.prototype是一般的普通对象,所以继承自Object。
完整图示如下:

总结

属于“类”来自面向对象编程,在Javascript中,通常意味着函数类模式或原型模式。原型模式更强大且高效,所以推荐使用。
根据原型模式规范:
- 方法存储在Class.prototype中。
- 原型继承自其他原型。

下一章,我们讨论class关键字和其结构,是提供了更简洁的方式实现原型模式,并且还有其他优势。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值