什么是面向对象?
概念起源
面向对象程序设计的雏形,早在1960年的 Simula 语言中即可发现,过后的程序设计畛域正面临着一种危机:在软硬件环境逐步简单的状况下,软件如何失去良好的保护?面向对象程序设计在某种程度上通过强调可重复性解决了这一问题。--引自维基百科
面向对象(OOP)
面向对象编程是以形象形式创立基于事实世界模型的一种编程模式;在此种模式下将对象作为领有明确职责的根本单元,对象自身能够接管音讯,解决数据以及发送数据给其余对象;OOP 有以下几点个性:
Inheritance 继承
- 子类能够继承父类的特色(属性和办法)
Encapsulation 封装
- 是一种把数据和相干的办法绑定在一起应用的办法
Abstraction 形象
- 联合简单的继承,属性,办法的对象可能模仿事实的模型
Polymorphism 多态
- 不同类能够定义雷同的属性和办法
基于原型和基于类
基于原型的编程提倡咱们去关注一系列对象实例的行为,而后才去关怀如何将这些对象,划分到最近的应用形式类似的原型对象,而不是将它们分成类
- 基于原型的面向对象零碎通过复制的形式来创立新对象一些语言的实现中,还容许复制一个空对象,这实际上就是创立一个全新的对象
原型零碎的“复制操作”有两种实现思路:
- 并不真的去复制一个原型对象,而是使得新对象持有一个原型的援用
- 切实地复制对象,从此两个对象再无关联
基于类的编程提倡应用一个关注分类和类之间关系开发模型
- 在这类语言中,总是先有类,再从类去实例化一个对象
- 类与类之间又可能会造成继承、组合等关系
- 类又往往与语言的类型零碎整合,造成肯定编译时的能力
基于原型和基于类的区别
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) // falseconst 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 correctlyconsole.log(student instanceof Person) // trueconsole.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面向对象简介
- 对象模型的细节
- 多继承