对象Object
设置对象属性
let obj = {
a: 1
};
obj.b = 2;
Object.defineProperty(obj, 'c', {
value: 3,
// ... 其他描述
});
Object.defineProperties(obj, {
'd': {
value: 4,
// ... 其他描述
},
'e': {
value: 5,
// ... 其他描述
}
});
console.log(obj); // {a: 1, b: 2, c: 3, d: 4, e: 5}
类&原型
基于类的面向对象语言(Java C++) 是构建在两个不同实体之上:类和实例。
类:定义某一对象集合的全部特征(属性、方法)。
实例:一个类的实例化,拥有与类完全一致的全部特征,不多也不少。
基于原型的语言JavaScript,它不存在这种区别(类和实例),它只有对象。
新对象都有一个作为模板的原型对象,可以从中获取原始的属性。
任何对象又可以作为另一个对象的原型对象,允许后者共享前者的属性。
任何对象都可以指定自己的属性,在创建时或创建后。
类定义、添加、删除
在基于类的语言中,需要专门方式定义类,在定义类时允许定义一个构造器用于创建该类的实例。在构造器方法中,指定实例的属性的初始值,并做一些其他操作。最终通过new操作符创建类的实例。
一旦定义了类,无法对类的属性进行更改。
JavaScript 大体上与之类似,但并没有专门的类定义方式,同样通过定义一个构造函数创建一个有着初始值的属性和方法的对象。任何JavaScript函数都可以被用作构造函数。你也可以使用 new 操作符来创建一个新对象。
在 JavaScript 中,允许运行时添加或者移除任何对象的属性。如果您为一个对象中添加了一个属性,而这个对象又作为其它对象的原型,则以该对象作为原型的所有其它对象也将获得该属性。
子类和继承
基于类的语言 假设 Employee 类只有 name 和 dept 属性。而 Manager 只有 reports 属性。并且指定Manager为Employee的子类。这时,创建 Manager 类的实例,将具有所有三个属性:name,dept和reports。
JavaScript 通过构造器函数和原型对象相关联的方式实现继承。
- 首先定义 Employee 的构造函数,函数返回一个包含 name 和 dept 属性的对象。
- 然后定义 Manager 的构造函数,在该函数内调用 Employee 的构造函数,并将this指向Manager的构造函数。
- Manager 构造函数中定义属性reports。
- 将一个获得了Employee.prototype(Employee构造函数原型)的新对象赋予 Manager 构造函数,作为Manager构造函数的原型。
- 之后再创建Manager实例时,该实例会从Employee对象继承name、dept属性。
function Employee () {
this.name = "";
this.dept = "general";
}
function Manager() {
Employee.call(this)
this.reports = [];
}
// 以Employee原型创建一个新对象作为Manager的原型
Manager.prototype = Object.create(Employee.prototype);
// 也可以将一个Employee实例作为Manager的原型
// Manager.prototype = new Employee;
let jane = new Manager;
// 如果不需要向构造器中传入参数,可以省略new Manager()后面的括号
// new 操作符创建了一个新的对象,并将其 __proto__ 属性设置为 Manager.prototype。
// 构造函数初始化的函数会显式的展示在实例对象jane中
console.log(jane); // Manager {name: "", dept: "general", reports: Array(0)}
// 在原型上后期添加的属性,则隐式则展示在原型链中,可在__proto__中逐级寻找到
Employee.prototype.age = 30;
console.log(jane); // Manager {name: "", dept: "general", reports: Array(0)}
console.log(jane.age); // 30
属性的继承
本地值和继承值
在访问一个对象的属性时,JavaScript 将执行下面的步骤:
- 检查对象自身是否存在。如果存在,返回值。
- 如果本地值不存在,检查原型链(通过 __proto__ 属性)。
- 如果原型链中的某个对象具有指定属性,则返回值。
- 如果这样的属性不存在,则对象没有该属性,返回 undefined。
function Employee () {
this.name = "";
this.dept = "general";
}
function Manager() {
Employee.call(this)
this.reports = [];
}
Manager.prototype = new Employee;
function SalesPerson() {
this.quota = 100;
}
SalesPerson.prototype = new Employee;
let jack = new Manager; // jack将具有本地属性name和depy
let jane = new SalesPerson; // jane只能在原型链中找到name和depy, 从jane的 __proto__中获得
继承值:
创建Employee的任意实例时,实例拥有一个本地属性name
Manager.prototype SalesPerson.prototype 都有一个本地属性name
修改Employee.prototype.name不会令jack jane的name变更
jane.name 查询到SalesPerson.prototype.name存在即返回
// 接上面的代码
Employee.prototype.name = 'Unknown';
console.log(jane.name); // ''
如果想在运行时修改一个对象的属性值并且希望该值被所有该对象的后代所继承,您就不能在该对象的构造器函数中定义该属性。而应该将该属性添加到该对象所关联的原型中。
function Employee () {
// this.name = ""; // 不在构造函数中定义name
this.dept = "general";
}
Employee.prototype.name = 'Unknown'; // 这行也可以省略
function Manager() {
Employee.call(this)
this.reports = [];
}
Manager.prototype = new Employee;
function SalesPerson() {
this.quota = 100;
}
SalesPerson.prototype = new Employee;
let jack = new Manager;
let jane = new SalesPerson;
Employee.prototype.name = 'Unknown';
console.log(jack.name); // Unknown
console.log(jane.name); // Unknown
没有多重继承
因为每个对象只有一个原型与之关联,JavaScript 无法动态地从多个原型链中继承。
可以在构造函数中调用多个其他构造器函数,创建本地值,实现多重继承的假象。
但同上述所说,因为有了本地值,修改原型的属性(继承值),已创建的实例不会继承变更。
判断实例的关系
JavaScript 的属性查找机制 首先在对象本地属性中查找,如果没有找到,将在对象的特殊属性 __proto__ 中查找。这个过程是递归的;被称为“在原型链中查找”。
对象在创建时,会定义**__proto__** 属性,属性设置为构造函数的 prototype 属性的值。
所以表达式 new Foo() 将创建一个对象,其 __proto__ === Foo.prototype
因而修改 Foo.prototype 的属性,将改变所有通过new Foo()创建的对象的属性的查找。
每个对象都有一个 __proto__ 对象属性(除了 Object);
每个函数都有一个 prototype 对象属性。
因此,通过“原型继承”,对象与其它对象之间形成关系。通过比较对象的 __proto__ 属性和函数的 prototype 属性可以检测对象的继承关系。
JavaScript 提供了便捷方法:instanceof 操作符可以用来将一个对象和一个函数做检测,如果对象继承自函数的原型,则该操作符返回真。例如:
function Employee () {
this.dept = "general";
}
function Manager() {
this.reports = [];
}
Manager.prototype = new Employee;
let jack = new Manager;
console.log(jack instanceof Employee); // true
console.log(jack instanceof Manager); // true