下一篇:继承的概念
在
JavaScript
中对象是⼀个由两个对象组成的概念. 这句话⽐较晦涩, 下⾯通过分析来详细讨论对象和原型对象的概念.
什么是原型
JavaScript
中原型的概念来源于Self
语⾔.Self
中将精简作为设计原则. 设计之初就舍去了类的概念, 只有对象的概念. 因此在JavaScript
中出现了原型继承之说, 即JavaScript
的继承所依附的是原型对象.
在
JavaScript
中, 对象{}
中包含toString
,valueOf
, 和constructor
等⽅法, 实际上就是由原型继承来实现的. 对象继承⾃原型对象Object.property
.
为什么需要原型
解释为何使⽤原型之前, ⾸先看下⾯的案例.
// 创建⼀个构造⽅法
function Person() {
this.sayHello = function() {
console.log("Hello!");
};
}
// 创建两个对象
var p1 = new Person(),
p2 = new Person();
// 调⽤和⽐较
p1.sayHello();
p2.sayHello();
console.log("p1.sayHello === p2.sayHello 的结果是: " +
(p1.sayHello === p2.sayHello));
表达式
p1.sayHello === p2.sayHello
的运⾏结果是false
. 可⻅两个对象的⽅法是两个独⽴的⽅法.
在
JavaScript
中函数是对象, 也就是说sayHello
⽅法也是对象的成员. 与num = 123
,name = "JK"
⼀样,sayHello
也是⼀个变量. 在内存逻辑中:
如图, 在内存中, 对象p1
和对象p2
同时具有独⽴的两个成员sayHello
,都占有内存. ⽽sayHello
作为⽅法是相同的⼀段执⾏逻辑. ⽆论是什么对象,执⾏逻辑都是⼀样的. 因此应该将该逻辑共享. 内存逻辑应该为:
因此, 在JavaScript
中有了原型的概念, 原型⽤来保存那些共享的数据和⽅法.
原型也是对象
在
JavaScript
中, ⼀个对象是由两部分构成的, ⼀个是当前对象, ⼀个是原型对象. 如下⾯代码:
function Person() {
this.name = "蒋坤";
this.sayHello = function() {
console.log("你好, 我是" + this.name);
};
}
var p = new Person();
这段代码中, 定义了⼀个
Person
构造函数, 创建了⼀个Person
类型的对象p
. 它就是当前对象. 构造⽅法是如何设计的, 那么它就具有什么成员.
但是对象
p
还包含其他⽅法, 例如toString
. 这个⽅法就是由原型对象提供.
console.log(p.toString()); // => [object Object]
也就是说⼀个对象由构造⽅法创建的部分, 和原型部分⼀起构成.
原型属性
每⼀个对象都有⼀个原型, 这个原型常常称为"
这个对象的原型属性
", 或"这个对象的原型
".
原型对象
对象的原型实际上是构造函数的⼀个属性. 在定义构造函数的时候就定义了其原型. 如果需要引⽤原型对象使⽤语法:
<构造⽅法>.prototype
在原型对象中定义的所有成员, 都将被其构造⽅法创建出的对象所继承. 也就是说原型对象中有什么, 那么由构造⽅法创建出来的对象就有什么.
设置原型对象
由于原型中所有的成员, 对象中都会有, 因此可以将之前的案例修改成:
// 创建⼀个构造⽅法
function Person() {
}
// 给其原型添加⽅法
Person.prototype.sayHello = function() {
console.log("Hello!");
};
// 创建两个对象
var p1 = new Person(),
p2 = new Person();
// 调⽤和⽐较
p1.sayHello();
p2.sayHello();
console.log("p1.sayHello === p2.sayHello 的结果是: " + (p1.sayHello === p2.sayHello));
此时运⾏, 表达式
p1.sayHello === p2.sayHello
的结果便是true
. 也就是说这两个对象是共享同⼀个⽅法的执⾏代码.
因此, 在编写构造函数创建对象的时候, 应该提供两部分: ⼀个是构造⽅法, ⼀个是原型对象. 将属性部分写在构造⽅法中, 将⽅法添加到原型对象中.
proto
想要获得原型对象, 必须使⽤构造⽅法, 即
<构造⽅法>.prototype
. 但是在代码上下⽂中, 获得指定对象的原型对象就不那么⽅便了.
Mozilla
实现的JavaScript
中引⼊了⼀个__proto__
属性接⼝. 利⽤该属性可以直接访问对象的原型对象.
function Person() {
}
var p = new Person();
console.log(p.__proto__ === Person.prototype); // => true
但是, 不建议在开发中直接使⽤该属性, 虽然
FireFox
,Chrome
和Safari
实现了该属性, 但并不是所有的浏览器.
为了兼容不⽀持
__proto__
属性的浏览器, 可以提供⼀个通⽤⽅法来解决
// 使⽤ __myproto 来表⽰对象的原型对象
if (!({}).__proto__) {
Object.prototype.__myproto = function () {
return this.constructor.prototype;
}
} else {
Object.prototype.__myproto = function() {
return this.__proto__;
}
}
下一篇:继承的概念
小结
- 对象由两部分构成, ⼀个是原型对象, ⼀个是构造⽅法描述的对象.
- 获得原型引⽤使⽤
<构造⽅法>.prototype 或者 <对象>.__proto__.
- 设计构造⽅法, 将属性写在构造⽅法中, 将⽅法添加到原型对象中.
此系列笔记均参考自 蒋坤 笔记