概念:
面向对象程序设计(英语:Object-oriented programming,缩写:OOP)是种具有对象概念的程序编程典范,同时也是一种程序开发的抽象方针。它可能包含数据、属性、代码与方法。对象则指的是类的实例。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性,对象里的程序可以访问及经常修改对象相关连的数据。通常,OOP被理解为一种将程序分解为封装数据及相关操作的模块而进行的编程方式。有别于其它编程方式,OOP中的与某数据类型相关的一系列操作都被有机地封装到该数据类型当中,而非散放于其外,因而OOP中的数据类型不仅有着状态,还有着相关的行为。
特征:
封装:
具备封装性(Encapsulation)的面向对象编程隐藏了某一方法的具体运行步骤,取而代之的是通过消息传递机制发送消息给它。封装是通过限制只有特定类的对象可以访问这一特定类的成员,而它们通常利用接口实现消息的传入传出。
继承:
在某种情况下,一个类会有“子类”。子类比原本的类(称为父类)要更加具体化。
多态:
是指由继承而产生的相关的不同的类,其对象对同一消息会做出不同的响应。
历史:
起源
对象作为编程实体最早是于1960年代由Simula 67语言引入思维。Simula这一语言是奥利-约翰·达尔(挪威人,面向对象之父)和克利斯登·奈加特设计的。(据说,他们是为了模拟船只而设计的这种语言,并且对不同船只间属性的相互影响感兴趣。他们将不同的船只归纳为不同的类,而每一个对象,基于它的类,可以定义它自己的属性和行为。)这种办法是分析式程序的最早概念体现。在分析式程序中,我们将真实世界的对象映射到抽象的对象,这叫做“模拟”。Simula不仅引入了“类”的概念,还应用了实例这一思想——这可能是这些概念的最早应用。
发展
Smalltalk的创建者深受Simula 67的主要思想影响,但Smalltalk中的对象是完全动态的——它们可以被创建、修改并销毁,这与Simula中的静态对象有所区别。此外,Smalltalk还引入了继承性的思想,它因此一举超越了不可创建实例的程序设计模型和不具备继承性的Simula。
面向对象程序设计在80年代成为了一种主导思想,这主要应归功于C++——C语言的扩充版。在图形用户界面(GUI)日渐崛起的情况下,面向对象程序设计很好地适应了潮流。GUI和面向对象程序设计的紧密关联在Mac OS X中可见一斑。Mac OS X是由Objective-C语言写成的,这一语言是一个仿Smalltalk的C语言扩充版。面向对象程序设计的思想也使事件处理式的程序设计更加广泛被应用(虽然这一概念并非仅存在于面向对象程序设计)。
Question:
- 面向对象的出现解决了什么痛点?有什么弊端?
- 如何看待JavaScript的面向对象?
answer:
优点:主要是解决了重复性代码和代码可读性
- 代码开发模块化,更易维护和修改;
- 代码复用;
- 增强代码的可靠性和灵活性;
- 增强代码的可理解性;
缺点:
- 如果没有好的规划,容易写出结构不合理各级不协调的系统代码
- 只能针对业务进行划分
- 初学者不易接受
看待JavaScript的面向对象
我们从追溯法得知面向对象是先基于类的思想,进而实现其继承性。而JavaScript没有类的概念,是通过原型来实现面向对象,从设计模式的角度讲,原型模式是用于创建对象的一种模式,如果我们想要创建一个对象,一种方法是先指定它的类型,然后通过类来创建这个对象。原型模式选择了另外一种方式,我们不再关心对象的具体类型,而是找到一个对象,然后通过克隆来创建一个一模一样的对象。而克隆出来的这个对象会记住他的原型,由谁克隆而来,同时也会共享原型的属性和方法。
// 父类
function Father() {
this.id = 1;
this.scopeName = 'mac';
}
let son = new Father();// 子类 生成一个实例,即生成一个新对象,这个this指向当前新生成的对象
console.log(son.id);// 1
// 弊端:每个方法都要在实例上重新创建
// 原型式创建对象
function Father() {
}
// prototype为实例的公共资源池,任何实例都可访问
Father.prototype.name = 'father';
Father.prototype.getName = function () {
return this.name;
}
let son1 = new Father();
let son2 = new Father();
console.log(son1.getName());// father
console.log(son2.getName());// father
// 弊端:全部实例共享prototype属性,一个发生改变全部都会改变
// 组合模式 构造函数+原型模式
function Father() {
this.name = 'father';
}
Father.prototype = {
constructor: Father,
getName: function() {
return this.name;
}
}
let son = new Father();
console.log(son.getName());
上面代码介绍了三种最常见的js创建对象的方法,由此演变出了三种继承方式,这也说明了JavaScript是一门很灵活的语言。
// 原型链继承
function Father() {
this.id = 1;
}
Father.prototype.name = 'father';
function Mother() {
}
Mother.prototype = new Father();
Mother.prototype.getName = function () {
return this.name;
}
let son = new Mother();
console.log(son.getName());// father
// 上述Mother的函数继承了Father函数,其实现主要是通过重写prototype
// 弊端:1.共享属性的问题,修改其中一个属性其余引用时都会发生改变 2.不能向实例传递参数
// 借用构造函数
function Father(name) {
this.id = 1;
this.name = name;
}
function Mother() {
Father.call(this, 'hello world');
}
console.log(new Mother().id);// 1
console.log(new Mother().name)// hello world
// 弊端:每个实例都具有各自的属性备份,没有达到函数复用的要求
// 组合继承
function Father(name) {
this.id = 1;
this.name = name;
}
Father.prototype.getName = function () {
return this.name;
}
function Mother() {
Father.call(this, 'hello world');
}
Mother.prototype = new Father();
Mother.prototype.constructor = Mother;
console.log(new Mother().id);// 1
console.log(new Mother().getName());// hello world
// 最理想的继承范式 寄生组合式继承
function Father(name) {
this.id = 1;
this.name = name;
}
Father.prototype.getName = function () {
return this.name;
}
function Mother() {
Father.call(this, 'hello world');
this.age = 30;
}
function inheritPrototype(Mother, Father){
var pro = Object(Father, Mother);
pro.constructor = Mother;
Mother.prototype = pro;
}
inheritPrototype(Mother, Father);
Mother.prototype.getAge = function() {
return this.age;
}
// 避免在Mother的prototype上创建不必要的属性,同时原型链保持不变,因此还支持instanceof及isPrototypeOf
总结:
个人认为面向对象就是将一个个的业务功能拆分成一个个对象,通过抽象业务功能,来对创建的对象进行功能函数的补充,对于重复性较高的业务,则抽离出公共部分,通过继承和多态,将业务的不同之处创建一个个子类进行不同的包装,实现不同的功能。JavaScript没有类的概念,通过以上几种方式来创建对象,主要通过原型链来实现继承,原型链的构建是通过将一个类型的实例赋值给另一个构造函数的原型实现的。
面向对象的相关知识:
ECMA-262把对象定义为:无需属性的集合,其属性可以包含基本值、对象或者函数,每个对象都是基于一个引用类型创建的。
引用类型:引用类型的赋值其实是对象保存在栈区地址指针的赋值,因此两个变量指向同一个对象,任何的操作都会相互影响。
引用类型与基本类型的区别:
(1)基本类型的值是一经确定就不可变的
(2)基本类型的比较是值的比较(==和===的区别)
(3)基本类型的变量是存放在栈区的(栈区指内存里的栈内存)
(4)引用类型的值是可变的
(5)引用类型的值是同时保存在栈内存和堆内存中的对象
(6)引用类型的比较是引用的比较
JavaScript中的对象有两种属性:数据属性
和访问器属性
。
数据属性:有以下四个值,通过Object.defineProperty方法修改默认值
Configurable:表能否通过delete关键字删除属性并重新编辑该属性,默认true
Enumerable:表能否通过for-in遍历并返还属性,默认true
Writable:表能否编辑属性,默认true
Value:从这个位置读取值,默认值undefined
Object.defineProperty(objectName, attribute, {
writable: false
})
注:通过defineProperty将configurable改为false,将不能通过该方法对对象进行操作,调用该方法时如不指定,则Configurable,Enumerable,Writable默认值变为false
访问器属性:必须使用get和set函数,有以下四个值,通过Object.defineProperty方法修改默认值
Configurable:表能否通过delete关键字删除属性并重新编辑该属性,默认true
Enumerable:表能否通过for-in遍历并返还属性,默认true
Get:读取属性调用函数,默认undefined
Set:写入属性调用函数,默认值undefined
// vue双向绑定原理
Object.defineProperty(objectName, attribute, {
get: function() {
return this.attribute;
},
set: function(newVal) {
this.attribute = newVal;
}
})
注:通过defineProperty将configurable改为false,将不能通过该方法对对象进行操作
以上两种属性通过Object.getOwnPropertyDescriptor方法访问
let descriptor = Object.getOwnPropertyDescriptor(object, attribute);
descriptor.configurable;// false
明确构造函数
,实例
,constructor
,prototype
及_proto_
的概念。
构造函数:首字母大写的函数就是构造函数,无特殊意义,只是用来做和普通函数的区别。
实例:使用new操作符调用构造函数进行创建(new的过程中发生了什么)。
constructor:创建的实例中包含的属性,指向创建其的构造函数,通俗理解就是基因,用于证明你是你爸的儿子。
注:使用instanceof证明,son instanceof Father // true son instanceof Object // true
prototype:共享资源,所以函数都具备这个属性,指向Function对象,通俗理解就是你爸的钱,你和你弟弟都可以用。
注:
使用isPrototypeOf证明,Father.prototype.isPrototypeOf(son) // true
使用Object.getPrototypeOf证明,Object.getPrototypeOf(son) == Father.prototype // true
_proto_:每个对象都有的属性,指向父级的prototype
hasOwnProperty():检测一个属性是存在于实例中还是原型中
注:
son.hasOwnProperty('name') true 存在实例中 false 存在原型中
for。。。in返还所有能够通过对象访问的,可枚举的属性,其中既包括实例中的属性,也包括原型中的属性,原型中定义的不可枚举(Enumerable:false)的属性也会返还
Object.keys() 返还对象上所有可枚举的属性数组
Object.getOwnPropertyNames 返还实例所有属性,无论是否可枚举
new的过程发生了什么?
- 创建一个对象
- 将构造函数的作用域赋给新对象(更改this指针指向该对象)
- 执行构造函数中的代码(添加属性)
- 返回新对象
参考:
维基百科。「面向对象程序设计」,http://zh.wikipedia.org/wiki/...
JavaScript高级程序设计(第三版)
再谈JavaScript面向对象思想及继承,https://segmentfault.com/a/11...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。