定义原型
原型实际上就是一个普通对象,继承于 Object 类,由 JavaScript 自动创建并依附于每个函数身上,原型在 JavaScript 对象系统中的位置和关系如图所示。
Object 和 Function 是两个不同类型的构造函数,利用运算符 new 可以创建不同类型的实例对象。实例对象、类、Object 和 Function 之间的关系如图所示。
使用点语法,可以通过 function.prototype 访问和操作原型对象。
示例
在下面代码中为函数 P 定义原型。
下面展示一些 内联代码片
。
function P (x) { //构造函数
this.x = x; //声明私有属性,并初始化为参数x
}
P.prototype.x = 1; //添加原型属性x,赋值为1
var p1 = new P (10); //实例化对象,并设置参数为10
P.prototype.x = p1.x; //设置原型属性值为私有属性值
console.log(P.prototype.x); //返回10
访问原型
访问原型对象有 3 种方法,简单说明如下:
obj.proto。
obj.constructor.prototype。
Object.getPrototypeOf(obj)。
其中,obj 表示一个实例对象,constructor 表示构造函数。proto(前后各两个下划线)是一个私有属性,可读可写,与 prototype 属性相同,都可以访问原型对象。Object.getPrototypeOf(obj) 是一个静态函数,参数为示例对象,返回值是参数对象的原型对象。
proto 属性是一个私有属性,存在浏览器兼容性问题,以及缺乏非浏览器环境的支持。使用 obj.constructor.prototype 也存在一定风险,如果 obj 对象的 constructor 属性值被覆盖,则 obj.constructor.prototype 将会失效。因此,比较安全的用法是使用 Object.getPrototypeOf(obj)。
示例
下面代码创建一个空的构造函数,然后实例化,分别使用上述三种方法访问实例对象的原型。
var F = function () {}; //构造函数
var obj = new F(); //实例化
var proto1 = Object.getPrototypeOf(obj); //引用原型
var proto2 = obj.__proto__; //引用原型,注意,IE暂不支持
var proto3 = obj.constructor.prototype; //引用原型
var proto4 = F.prototype; //引用原型
console.log(proto1 === proto2); //true
console.log(proto1 === proto2); //true
console.log(proto1 === proto4); //true
console.log(proto2 === proto3); //true
console.log(proto2 === proto4); //true
console.log(proto3 === proto4); //true
设置原型
设置原型对象有 3 种方法,简单说明如下:
obj.proto=prototypeObj.
Object.setPrototypeOf(obj, prototypeObj)。
Object.create(prototypeObj)。
其中,obj 表示一个实例对象,prototypeObj 表示原型对象。注意,IE 不支持前面两种方法。
示例
下面代码简单演示利用上述三种方法为对象直接量设置原型。
var proto = {name : "prototype"}; //原型对象
var obj1 = {}; //普通对象直接量
obj1.__proto__ = proto; //设置原型
console.log(obj1.name);
var obj2 = {}; //普通对象直接量
Object.setPrototypeOf(obj2, proto); //设置原型
console.log(obj2.name);
var obj3 = Object.create(proto); //创建对象,并设置原型
console.log(obj3.name);
检测原型
使用 isPrototypeOf() 方法可以判断该对象是否为参数对象的原型。isPrototypeOf() 是一个原型方法,可以在每个实例对象上调用。
示例
下面代码简单演示如何检测原型对象。
var F = function () {}; //构造函数
var obj = new F(); //实例化
var proto1 = Object.getPrototypeOf(obj); //引用原型
console.log(proto1.isPrototypeOf(obj)); //true
也可以使用下面代码检测不同类型的实例。
var proto = Object.prototype;
console.log(proto.isPrototypeOf({})); //true
console.log(proto.isPrototypeOf([])); //true
console.log(proto.isPrototypeOf(//)); //true
console.log(proto.isPrototypeOf(function () {})); //true
console.log(proto.isPrototypeOf(null)); //false
原型属性和私有属性
原型属性可以被所有实例访问,而私有属性只能被当前实例访问。
如果给构造函数定义了与原型属性同名的私有属性,则私有属性会覆盖原型属性值。
如果使用 delete 运算符删除私有属性,则原型属性会被访问。
私有属性可以在实例对象中被修改,不同实例对象之间不会相互干扰。
原型属性将会影响所有实例对象,修改任何原型属性值,则该构造函数的所有实例都会看到这种变化,这样就省去了私有属性修改的麻烦。
prototype 属性属于构造函数,所以必须使用构造函数通过点语法来调用 prototype 属性,再通过 prototype 属性来访问原型对象。原型属性于私有属性之间的关系如图所示。
Object 和 Function 都可以定义原型,它们的属性与原型关系如图所示。
应用原型
下面通过几个示例介绍原型在代码中的应用技巧。
示例1
利用原型为对象设置默认值。当原型属性与私有属性同名时,删除私有属性之后,可以访问原型属性,即可以把原型属性值作为初始化默认值。
function p (x) { //构造函数
if (x) { //如果参数存在,则设置属性,该条件是关键
this.x = x; //使用参数初始化私有属性x的值
}
}
p.prototype.x = 0; //利用原型属性,设置私有属性x的默认值
var p1 = new p (); //实例化一个没有带参数的对象
console.log(p1.x); //返回0,即显示私有属性的默认值
var p2 = new p (1); //再次实例化,传递一个新的参数
console.log(p2.x); //返回1,即显示私有属性的初始化值
示例2
利用原型间接实现本地数据备份。把本地对象的数据完全赋值给原型对象,相当于为该对象定义一个副本,也就是备份对象。这样当对象属性被修改时,就可以通过原型对象来恢复本地对象的初始值。
function p (x) { //构造函数
this.x = x;
}
p.prototype.backup = function () { //原型方法,备份本地对象的数据到原型对象中
for (var i in this) {
p.prototype[i] = this[i];
}
}
var p1 = new p (1); //实例化对象
p1.backup; //备份实例对象中的数据
p1.x = 10; //改写本地对象的属性值
console.log(p1.x); //返回10,说明属性值已经被改写
p1 = p.prototype; //恢复备份
console.log(p1.x); //返回1,说明对象的属性值已经被恢复
示例3
利用原型进行批量复制。
function f (x) { //构造函数
this.x = x; //声明私有属性
}
var a = []; //声明数组
for (var i = 0; i < 100; i ++) { //使用for循环结构批量复制构造类f的同一个实例
a[i] = new f (10); //把实例分别存入数组
}