面向对象还是基于对象?
历史问题
不同的编程语言中,设计者利用各种不同的语言特性来抽象描述对象,最为成功的流派是使用“类”的方式来描述对象,这诞生了诸如 C++、Java 等流行的编程语言。
而 JavaScript 早年却选择了一个更为冷门的方式:原型。然而很不幸,因为一些公司政治原因,JavaScript 推出之时受管理层之命被要求模仿 Java,所以,JavaScript 创始人 Brendan Eich 在“原型运行时”的基础上引入了 new、this 等语言特性,使之“看起来更像 Java”。
JavaScript对象的特征
- 对象具有唯一标识性:即使完全相同的两个对象,也并非同一个对象。
- 对象有状态:对象具有状态,同一对象可能处于不同状态之下。
- 对象具有行为:即对象的状态,可能因为它的行为产生变迁。
在 JavaScript 中,将状态和行为统一抽象为“属性”。JavaScript中对象具有高度的动态性,这是因为 JavaScript 赋予了使用者在运行时为对象添改状态和行为的能力。
JavaScript对象的两类属性
对 JavaScript 来说,属性并非只是简单的名称和值,JavaScript 用一组特征(attribute)来描述属性(property)。
- 数据属性
- value: 属性的值
- writable: 决定属性都否被赋值
- enumerable: 决定for in 能否枚举该属性
- configurable: 决定该属性能否被删除或者改变特征值
- 访问器(getter/setter)属性
- getter: 函数或undefined,在取属性值时被调用
- setter: 函数或undefined,在设置属性值时被调用
- enumerable: 决定for in 能否枚举该属性
- configurable: 决定该属性能否被删除或者改变特征值
我们通常用于定义属性的代码会产生数据属性,其中的 writable、enumerable、configurable 都默认为 true。我们可以使用内置函数 Object.getOwnPropertyDescripter 来查看:
var o = { a: 1 };
o.b = 2;
//a 和 b 皆为数据属性
Object.getOwnPropertyDescriptor(o,"a") // {value: 1, writable: true, enumerable: true, configurable: true}
Object.getOwnPropertyDescriptor(o,"b") // {value: 2, writable: true, enumerable: true, configurable: true}
改变属性特征或者定义访问器属性,可以使用Object.defineProperty
var o = { a: 1 };
Object.defineProperty(o, "b", {value: 2, writable: false, enumerable: false, configurable: true});
//a 和 b 都是数据属性,但特征值变化了
Object.getOwnPropertyDescriptor(o,"a"); // {value: 1, writable: true, enumerable: true, configurable: true}
Object.getOwnPropertyDescriptor(o,"b"); // {value: 2, writable: false, enumerable: false, configurable: true}
o.b = 3;
console.log(o.b); // 2
什么是原型
在javaScript之前,原型系统就更多与高动态性语言配合,并且多数基于原型的语言提倡运行时的原型修改,这应该是 Brendan 选择原型系统很重要的理由。
javaScript原型
- 如果所有对象都有私有字段 [[prototype]],就是对象的原型;
- 读一个属性,如果对象本身没有,则会继续访问对象的原型,直到原型为空或者找到为止。
new 运算接受一个构造器和一组调用参数,实际上做了几件事:
- 以构造器的 prototype 属性(注意与私有字段 [[prototype]] 的区分)为原型,创建新对象;
- 将 this 和调用参数传给构造器,执行;
- 如果构造器返回的是对象,则返回,否则返回第一步创建的对象。
ES6中的类
基本写法
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
// Getter
get area() {
return this.calcArea();
}
// Method
calcArea() {
return this.height * this.width;
}
}
继承能力
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a noise.');
}
}
class Dog extends Animal {
constructor(name) {
super(name); // call the super class constructor and pass in the name parameter
}
speak() {
console.log(this.name + ' barks.');
}
}
let d = new Dog('Mitzie');
d.speak(); // Mitzie barks.
网友评论