乐趣区

关于javascript:JavaScript系列之面向对象

什么是面向对象?

概念起源

面向对象程序设计的雏形,早在 1960 年的 Simula 语言中即可发现,过后的程序设计畛域正面临着一种危机:在软硬件环境逐步简单的状况下,软件如何失去良好的保护?面向对象程序设计在某种程度上通过强调可重复性解决了这一问题。– 引自维基百科

面向对象(OOP)

面向对象编程是以形象形式创立基于事实世界模型的一种编程模式;在此种模式下将对象作为领有明确职责的根本单元,对象自身能够接管音讯,解决数据以及发送数据给其余对象;OOP 有以下几点个性:

  • Inheritance 继承

    • 子类能够继承父类的特色(属性和办法)
  • Encapsulation 封装

    • 是一种把数据和相干的办法绑定在一起应用的办法
  • Abstraction 形象

    • 联合简单的继承,属性,办法的对象可能模仿事实的模型
  • Polymorphism 多态

    • 不同类能够定义雷同的属性和办法

基于原型和基于类

基于原型的编程提倡咱们去关注一系列对象实例的行为,而后才去关怀如何将这些对象,划分到最近的应用形式类似的原型对象,而不是将它们分成类

  • 基于原型的面向对象零碎通过 复制 的形式来创立新对象一些语言的实现中,还容许复制一个空对象,这实际上就是创立一个全新的对象

原型零碎的“复制操作”有两种实现思路:

  1. 并不真的去复制一个原型对象,而是使得新对象持有一个原型的援用
  2. 切实地复制对象,从此两个对象再无关联

基于类的编程提倡应用一个关注分类和类之间关系开发模型

  • 在这类语言中,总是先有类,再从类去实例化一个对象
  • 类与类之间又可能会造成继承、组合等关系
  • 类又往往与语言的类型零碎整合,造成肯定编译时的能力

基于原型和基于类的区别

JavaScript Java
所有对象均为实例 类和实例是不同的事物
无类的定义
通过结构器函数来定义和创立一组对象
有类的定义
通过结构器办法来实例化类
通过 new 操作符创立单个对象 雷同
指定一个对象作为原型并且与构造函数一起构建对象的层级构造 用类定义现存类的子类,从而构建对象的层级构造
遵循原型链继承属性 遵循类链继承属性
结构器函数或原型指定实例的 初始 属性集
容许动静地向单个对象或者整个对象集中增加或移除属性
在类定义时指定所有实例的 所有 属性
无奈在运行时动静增加属性

面向对象语言中都有类的概念,如 Java、C++ 都是基于类的语言,而 JavaScript 是基于原型的语言,在旧的 ES 版本没有类的定义,所以咱们要做一系列模仿类的操作去定义类
ES6 中提供了 class,使咱们不必再去模仿类的操作,然而这个 class 并非真正的类,其实是基于原型继承形式的语法糖

function Person() {} 
// 或
var Person = function(){}
// 或
class Person{}

构造函数

在实例化时构造函数被调用 (也就是对象实例被创立时)
构造函数是对象中的一个办法
在 JavaScript 中函数就能够作为结构器应用,因而不须要特地地定义一个结构器办法,每个申明的函数都能够在实例化后被调用执行(在 ES6 标准中 class 内的 constructor 起到同样的作用)
结构器罕用于给对象的属性赋值或者为调用函数做筹备

function Person(name) {
  this.name = name
  alert('Person instantiated')
}
// 或
class Person{constructor(name){
    this.name = name
    alert(`Person instantiated`)
  }
}

var person = new Person('mxin');

JavaScript 对象

对象在计算机中是一个具备惟一标识的内存地址,参考 Grandy Booch 的《面向对象分析与设计》来看,对象具备下列几个特点:

  • 惟一标识性:即便完全相同的两个对象,也并非同一个对象
  • 状态:对象具备状态,同一对象可能处于不同状态之下
  • 行为:即对象的状态,可能因为它的行为产生变迁
惟一标识性
function Person() {}
var person_1 = new Person();
var person_2 = new Person();
console.log(person_1 == person_2) // false

const obj_1 = {a: 1};
const obj_2 = {a: 1};
console.log(obj_1 == obj_2); // false
状态和行为

别离对应了属性和办法这两个概念,在 JavaScript 被形象为属性,其属性能够为任何类型

const obj = { 
  name: 'mxin', 
  age: 99,
  getInfo() {return `name: ${this.name} , age: ${this.age}`
  }
}

继承

JavaScript 通过将构造函数与原型对象相关联的形式来实现继承,上面将以 ES5 及 ES6 两种形式展现

  • ES5
// 定义 Person 结构器
function Person(firstName) {this.firstName = firstName;}
  
  // 在 Person.prototype 中退出办法
  Person.prototype.walk = function(){console.log("I am walking!");
  };
  Person.prototype.sayHello = function(){console.log("Hello, I'm " + this.firstName);
  };
  
  // 定义 Student 结构器
  function Student(firstName, subject) {// 调用父类结构器, 确保(应用 Function#call)"this" 在调用过程中设置正确
    Person.call(this, firstName);
  
    // 初始化 Student 类特有属性
    this.subject = subject;
  };
  
  // 建设一个由 Person.prototype 继承而来的 Student.prototype 对象.
  // 留神: 常见的谬误是应用 "new Person()" 来建设 Student.prototype.
  // 这样做的谬误之处有很多, 最重要的一点是咱们在实例化时
  // 不能赋予 Person 类任何的 FirstName 参数
  // 调用 Person 的正确地位如下,咱们从 Student 中来调用它
  Student.prototype = Object.create(Person.prototype); // See note below
  
  // 设置 "constructor" 属性指向 Student
  Student.prototype.constructor = Student;
  
  // 更换 "sayHello" 办法
  Student.prototype.sayHello = function(){console.log("Hello, I'm "+ this.firstName +". I'm studying" + this.subject + ".");
  };
  
  // 退出 "sayGoodBye" 办法
  Student.prototype.sayGoodBye = function(){console.log("Goodbye!");
  };
  
  // 测试实例:
  var student = new Student("Janet", "Applied Physics");
  student.sayHello();   // "Hello, I'm Janet. I'm studying Applied Physics."
  student.walk();       // "I am walking!"
  student.sayGoodBye(); // "Goodbye!"
  
  // Check that instanceof works correctly
  console.log(student instanceof Person);  // true 
  console.log(student instanceof Student); // true
  • ES6
class Person {constructor(firstName) {this.firstName = firstName}

  walk() {console.log(`I am walking!`)
  }

  sayHello() {console.log(`Hello, I'm ${this.firstName}`)
  }
}

class Student extends Person {constructor(firstName, subject) {super(firstName)
    this.subject = subject
  }

  sayHello() {console.log(`Hello, I'm ${this.firstName}. I'm studying ${this.subject}.`)
  }

  sayGoodBye() {console.log(`Goodbye!`)
  }
}

// 测试实例:
const student = new Student(`Janet`, `Applied Physics`)
student.sayHello() // "Hello, I'm Janet. I'm studying Applied Physics."
student.walk() // "I am walking!"
student.sayGoodBye() // "Goodbye!"

// Check that instanceof works correctly
console.log(student instanceof Person) // true
console.log(student instanceof Student) // true

封装

下面例子中 Student 类尽管不须要晓得 Person 类的 walk() 办法是如何实现的,然而依然能够应用这个办法,Student 类不须要明确地定义这个办法,除非咱们想扭转它,这就叫做封装

形象

形象是容许模仿工作问题中通用局部的一种机制,这能够通过 继承 组合(JavaScript 让类的实例是其余对象的属性值来实现组合)来实现:

JavaScript Function 类继承自 Object 类(继承)
Function.prototype 的属性是一个 Object 实例(组合)

var foo = function(){};

console.log(foo instanceof Function) // true    
console.log(foo.prototype instanceof Object) // true

多态

就像所有定义在原型属性外部的办法和属性一样,不同的类能够定义具备雷同名称的办法,办法是作用于所在的类中(并且这仅在两个类不是父子关系时成立)

总结

联合下面的常识,能够理解到 JavaScript 一种基于原型设计的面向对象语言,而且 JavaScript 的对象操作灵便,是具备高度动态性的属性汇合
能够深刻理解一下基于原型的设计思路以及 JavaScript 对象模型的细节

参考资料

  • JavaScript 面向对象简介
  • 对象模型的细节
  • 多继承
退出移动版