- 为什么JavaScript有对象概念,但是却没有类的概念?
- 为什么JavaScript对象可以自由添加属性,而其他语言不行?
一、什么是面向对象?
对象是一切事物的总称,它不是计算机领域凭空制造出来的概念,它是顺着人类的思维模式产生的一种抽象,所以面向对象编程也被认为是更接近人类思维模式的一种编程方式。
对象这一概念在人类的幼儿期形成,这远远早于我们编程逻辑中常用的值、过程等概念。
在幼年期,我们总是先认识到某一个苹果能吃(这里的某一个苹果就是一个对象),继而认识到所有的苹果都可以吃(这里的所有苹果,就是一个类),再到后来我们才能意识到三个苹果和三个梨之间的联系,进而产生数字“3”(值)的概念。
在《面向对象分析与设计》这本书中,Grady Booch 替我们做了总结,他认为,从人类的认知角度来说,对象应该是下列事物之一:
- 一个可以触摸或者可以看见的东西;
- 人的智力可以理解的东西;
- 可以指导思考或行动(进行想象或施加动作)的东西。
在不同的编程语言中,设计者利用不同的语言特性抽象的描述对象,最终成功的使用“类”的方式来描述对象,即产生了C++、Java等流行的编程语言。
ES6之前JavaScript程序员试图将JavaScript变得更像是基于类的编程,所以才产生了Dojo、prototypeJS。
二、JavaScript对象的特征
- 对象具有唯一标识性:即使完全相同的两个对象,也并非同一个对象。
- 对象有状态:对象具有状态,同一对象可能处于不同状态之下。
- 对象具有行为:即对象的状态,可能因为它的行为产生变迁。
1、唯一标识性
var o1={a:1};
var o2={a:1};
consoel.log(o1==o2);//false
复制代码
o1和o2初看时是两个一模一样的对象,但是他们打印出的却是false。
2、状态+行为
状态+行为=属性。
var o = {
d:1,
f(){
console.log(this.d)
}
}
复制代码
其中 o 是对象,d 是一个属性,而函数 f 也是一个属性,尽管写法不太相同,但是对 JavaScript 来说,d 和 f 就是两个普通属性。
所以,总结一句话来看,在 JavaScript 中,对象的状态和行为其实都被抽象为了属性。如果你用过 Java,一定不要觉得奇怪,尽管设计思路有一定差别,但是二者都很好地表现了对象的基本特征:标识性、状态和行为。
在对象基本特征的基础上,JavaScript对象具有高度的动态性,这是因为 JavaScript 赋予了使用者在运行时为对象添改状态和行为的能力。
var o={a:1};
o.b=2;
console.log(o);{a:1,b:2}
复制代码
为了提高抽象能力,JavaScript 的属性被设计成比别的语言更加复杂的形式,它提供了数据属性和访问器属性(getter/setter)两类。
三、JavaScript对象的两类属性
对 JavaScript 来说,属性并非只是简单的名称和值,JavaScript 用一组特征(attribute)来描述属性(property)。
1、数据属性
- value:就是属性的值。
- writable:决定属性能否被赋值。
- enumerable:决定 for in 能否枚举该属性。
- configurable:决定该属性能否被删除或者改变特征值。
2、访问器(setter、getter)属性
- getter:函数或 undefined,在取属性值时被调用。
- setter:函数或 undefined,在设置属性值时被调用。
- enumerable:决定 for in 能否枚举该属性。
- configurable:决定该属性能否被删除或者改变特征值。
Object.getOwnPropertyDescriptor(o,'a')
获得
{
value: 1,
writable: true, //默认true
enumerable: true, //默认true
configurable: true//默认true
}
复制代码
Object.defineProperty(o,'a',{value:6,writable: false, enumerable: false, configurable: true});
o.a=11;
console.log(o.a)//6
Object.getOwnPropertyDescriptor(o,'a')//{value:6,writable: false, enumerable: false, configurable: true}
复制代码
先用Object.defineProperty定义属性,改变了value、writable和 enumerable,用Object.getOwnPropertyDescriptor查看的时候给a赋值a的值不会发生变化。
在创建对象时,也可以使用get和set关键字来创建访问器属性:
var h={ get a(){ return 1}};
console.log(h.a)//1
复制代码
访问器属性跟数据属性不同,每次访问属性都会执行 getter 或者 setter 函数。这里我们的 getter 函数返回了 1,所以 h.a 每次都得到 1。
JavaScript 对象的运行时是一个“属性的集合”,属性以字符串或者 Symbol 为 key,以数据属性特征值或者访问器属性特征值为 value。
为什么会有“JavaScript 不是面向对象” ?
这是由于 JavaScript 的对象设计跟目前主流基于类的面向对象差异非常大。
对于我而言面向对象是一个很广泛的定义,我也感觉我从未理解过面向对象,只是单从字面上解释就是,基于对象的对象以及一切的延伸。