JavaScript非构造函数继承:原型链、浅拷贝与深拷贝详解

作者:木棉时间:2026-10-26 13:02:33浏览:555

本文介绍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库采用的正是这种基于深拷贝的继承方法。


(完)

作者最新文章