原型
原型的背景
首先,你应该知道javascript是一门面向对象语言。
是对象,就具有继承性。
继承性,就是子类自动共享父类的数据结构和方法机制。
而prototype 和 __proto__ 的存在就是为了建立这种子类与父类间的联系。
我们将prototype称作原型,将通过__proto__来建立起来的对象与对象的关系称作原型链。
下面,通过创建一个简单对象,来探索原型和原型链到底是什么。
原型与原型链
首先,创建一个最简单的对象
function Foo(){}
var o = new Foo();
ps:这是刚从java转入JavaScript时,最令我费解的一段代码,凭什么一个function就可以用来创建一个对象。下面就是new 关键字的分解动作。。。这个关键字究竟做了什么,能创建一个对象。。
这个创建的过程,可以分解成下面代码
function Foo(){}
// 创建一个基本对象
var o = new Object();
// 创建对象和父类原型之间的链接
o.__proto__ = Foo.prototype;
// 执行构造函数
Foo.call(o);
为了更好的理解这段代码,我们先理解这几个概念
-
什么是构造函数constructor
构造函数就是对象创建时,自动调用的方法
-
prototype
prototype,长这样
{ constructor: f foo() __proto__: Object }
它是一个对象,存储着一类事物的基本信息,所以可以将它称作类。
-
__proto__
__proto__,这个属性用来建立对象和类之间的关系。
有了这些概念,我们来分析创建对象的过程中,究竟做了些什么.
创建一个对象,会做如下三件事。
- 创建一个基本对象
new Object()
- 建立新对象与原型(我把它理解为类)之间的连接
- 执行构造函数
小结:prototype可以理解为类,也就是存储一类事物的基本信息。__proto__可以理解为一条线索,用来建立原型(类)和对象之间的关系。
原型、原型链和继承之间的关系。
继承,需要满足如下三个要求。
- 子类继承父类的成员变量
- 子类继承父类的方法
- 子类继承父类的构造器,如果父类的构造函数带有参数,那么子类中应该显示调用
我们该如何实现继承呢?
// 创建一个构造函数,我认为 a.prototype就是父类对象。
function a(x,y) {
a.prototype.x = x;
a.prototype.y = y
}
// 为父类对象添加一个method
a.prototype.console = function() {
console.log(this.x);
console.log(this.y);
}
//创建子类构造函数
function b(x,y) {
// 子类显示的调用父类构造方法
a.call(this,x,y);
}
// 子类继承父类的成员变量以及父类的方法
b.prototype = Object.create(a.prototype); = b.prototype.constructor = b;
// 创建对象
var c = new b(1,2);
// 这里Object.create 是用来创建一个新的prototype,用来记录新类的信息,并与父类建立联系
Object.create = function() {
//创建一个基本对象
var temp = new Object();
//与父类的的原型建立联系
temp.__proto__ = proto;
//返回新类的原型
return temp;
}
小结:继承关系的实现,做了如下两件事情
- 子类显示的调用父类的构造函数
- 子类通过原型来与父类建立联系,从而能让子类拥有父类的成员变量和方法。
原型就是类,原型链就是来建立子类和父类之间的联系。
原型链的实际表现
先创建一个类
function people() {}
// 为父类对象添加一个method
people.prototype.run = function() {
console.log("I am running");
}
通过类来创建一个对象
var p = new people();
p.run();
// i am running
这里p对象长这样
{
__proto__: Object
}
很显然,这个对象之中并没有run方法。
但是它却能调用run,因为它会通过__proto__(原型链)来寻找类中的方法。
经常有人这么问proto 和prototype有什么区别?
我想看到这里,你应该很明白了。
- prototype 一个存储类信息的对象,只存在function中.(下图中绿块)
- proto 单纯是对象用来指向上级的一链接。(看下图黄线)
那么又有人会问function中__proto__又是什么关系呢?
function 本身是对象,所以当然也有自己原型。function继承于Function.(看下图蓝线)。
下面介绍一下原型继承和类继承的关系。
原型继承和类继
ES5
'use strict';
/**
* Shape class.
*
* @constructor
* @param {String} id - The id.
* @param {Number} x - The x coordinate.
* @param {Number} y - The y coordinate.
*/
function Shape(id, x, y) {
this.id = id;
this.setLocation(x, y);
}
/**
* Set shape location.
*
* @param {Number} - The x coordinate.
* @param {Number} - The y coordinate.
*/
Shape.prototype.setLocation = function(x, y) {
this.x = x;
this.y = y;
};
/**
* Get shape location.
*
* @return {Object}
*/
Shape.prototype.getLocation = function() {
return {
x: this.x,
y: this.y
};
};
/**
* Get shape description.
*
* @return {String}
*/
Shape.prototype.toString = function() {
return 'Shape("' + this.id + '")';
};
/**
* Circle class.
*
* @constructor
* @param {String} id - The id.
* @param {Number} x - The x coordinate.
* @param {Number} y - The y coordinate.
* @param {Number} radius - The radius.
*/
function Circle(id, x, y, radius) {
Shape.call(this, id, x, y);
this.radius = radius;
}
Circle.prototype = Object.create(Shape.prototype);
Circle.prototype.constructor = Circle;
/**
* Get circle description.
*
* @return {String}
*/
Circle.prototype.toString = function() {
return 'Circle > ' + Shape.prototype.toString.call(this);
};
// test the classes
var myCircle = new Circle('mycircleid', 100, 200, 50); // create new instance
console.log(myCircle.toString()); // Circle > Shape("mycircleid")
console.log(myCircle.getLocation()); // { x: 100, y: 200 }
ES6
'use strict';
/**
* Shape class.
*
* @constructor
* @param {String} id - The id.
* @param {Number} x - The x coordinate.
* @param {Number} y - The y coordinate.
*/
class Shape(id, x, y) {
constructor(id, x, y) { // constructor syntactic sugar
this.id = id;
this.setLocation(x, y);
}
/**
* Set shape location.
*
* @param {Number} - The x coordinate.
* @param {Number} - The y coordinate.
*/
setLocation(x, y) { // prototype function
this.x = x;
this.y = y;
}
/**
* Get shape location.
*
* @return {Object}
*/
getLocation() {
return {
x: this.x,
y: this.y
};
}
/**
* Get shape description.
*
* @return {String}
*/
toString() {
return `Shape("${this.id}")`;
}
}
/**
* Circle class.
*
* @constructor
* @param {String} id - The id.
* @param {Number} x - The x coordinate.
* @param {Number} y - The y coordinate.
* @param {Number} radius - The radius.
*/
function Circle extends Shape {
constructor(id, x, y, radius) {
super(id, x, y); // call Shape's constructor via super
this.radius = radius;
}
/**
* Get circle description.
*
* @return {String}
*/
toString() { // override Shape's toString
return `Circle > ${super.toString()}`; // call `super` instead of `this` to access parent
}
}
// test the classes
var myCircle = new Circle('mycircleid', 100, 200, 50); // create new instance
console.log(myCircle.toString()); // Circle > Shape("mycircleid")
console.log(myCircle.getLocation()); // { x: 100, y: 200 }
这段代码,自己体会。。。。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。