什么是面向对象?
概念起源
面向对象程序设计的雏形,早在 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) // 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 面向对象简介
- 对象模型的细节
- 多继承