本文介绍JavaScript中不使用构造函数实现继承的三种方式。首先通过Douglas Crockford提出的object()函数利用原型链实现继承;其次探讨使用extendCopy进行浅拷贝,指出其处理数组或对象时存在引用篡改风险;最后讲解通过递归调用实现深拷贝,确保子对象修改不影响父对象,这也是jQuery早期版本的实现原理。
非构造函数继承概述
本节为最后一部分,重点讲解如何在不使用构造函数的情况下实现对象继承。
一、什么是"非构造函数"的继承?
假设我们定义了一个名为“中国人”的对象。
具体代码实现如下:
var Chinese = {
nation:'中国'
}; 另外,再定义一个名为“医生”的对象。
其代码结构如下:
var Doctor ={
career:'医生'
} 现在的问题是,如何让“医生”对象继承“中国人”对象的属性?即如何生成一个兼具两者特征的“中国医生”对象?
需要注意的是,上述两个均为普通对象而非构造函数,因此无法直接使用构造函数模式来实现继承。
二、object()方法
利用object()函数实现原型链继承
JSON格式发明人Douglas Crockford提出了一种名为object()的辅助函数,专门用于解决此类问题。
该函数的实现代码如下:
function object(o) {
function F() {}
F.prototype = o;
return new F();
} object()函数的核心逻辑仅有一步:将子对象的prototype属性指向父对象,从而建立子对象与父对象之间的原型链连接。
具体使用时,首先在父对象基础上生成子对象:
var Doctor = object(Chinese);
随后,为子对象添加其特有的属性:
Doctor.career = '医生';
此时,子对象已具备继承自父对象的属性。
alert(Doctor.nation); //输出:中国
三、浅拷贝
浅拷贝及其局限性
除了利用prototype原型链外,还有另一种思路:将父对象的所有属性直接拷贝到子对象中,同样可实现继承效果。
以下函数用于执行这种拷贝操作:
具体代码为:
function extendCopy(p) {
var c = {};
for (var i in p) {
c[i] = p[i];
}
c.uber = p;
return c;
} 调用方式如下:
代码示例:
var Doctor = extendCopy(Chinese);
Doctor.career = '医生';
alert(Doctor.nation); // 输出:中国 然而,这种拷贝方式存在缺陷。若父对象的属性值为数组或另一个对象,子对象获得的仅是内存地址引用而非真实拷贝,这可能导致父对象数据被意外篡改。
例如,现在给Chinese对象添加一个名为“出生地”的属性,其值为数组。
Chinese.birthPlaces = ['北京','上海','香港'];
通过extendCopy()函数,Doctor对象继承了Chinese对象的属性。
var Doctor = extendCopy(Chinese);
接着,我们为Doctor对象的“出生地”属性追加一个城市:
Doctor.birthPlaces.push('厦门');
此时发生了什么?Chinese对象的“出生地”属性也被修改了!
alert(Doctor.birthPlaces); //输出:北京, 上海, 香港, 厦门
alert(Chinese.birthPlaces); //输出:北京, 上海, 香港, 厦门
由此可见,extendCopy()仅能拷贝基本类型数据,这种拷贝方式被称为“浅拷贝”。这也是早期jQuery库实现继承所采用的方法。
四、深拷贝
深拷贝的实现与应用
所谓“深拷贝”,是指能够真正对数组和对象进行完整拷贝的技术。其实现并不复杂,只需在浅拷贝的基础上递归调用即可。
具体实现代码如下:
function deepCopy(p, c) {
var c = c || {};
for (var i in p) {
if (typeof p[i] === 'object') {
c[i] = (p[i].constructor === Array) ? [] : {};
deepCopy(p[i], c[i]);
} else {
c[i] = p[i];
}
}
return c;
} 使用时写法如下:
var Doctor = deepCopy(Chinese);
现在,给父对象添加一个属性,其值为数组。随后在子对象上修改该属性:
相关代码如下:
Chinese.birthPlaces = ['北京','上海','香港'];
Doctor.birthPlaces.push('厦门'); 此时,父对象将不再受到影响。
验证代码如下:
alert(Doctor.birthPlaces); //输出:北京, 上海, 香港, 厦门
alert(Chinese.birthPlaces); //输出:北京, 上海, 香港 目前,jQuery库采用的正是这种基于深拷贝的继承方法。
(完)