目录
[1. 原型链机制](#1. 原型链机制)
[2. 深浅拷贝](#2. 深浅拷贝)
[3. 原型继承](#3. 原型继承)
[4. 复习要点速查表](#4. 复习要点速查表)
本文介绍了JavaScript中的原型链机制、深浅拷贝及原型继承。原型链通过原型对象实现继承,形成链式结构,查找属性时遵循自底向上规则。深浅拷贝的区别在于是否完全独立复制对象:浅拷贝仅复制第一层,深拷贝则完全独立。原型继承可通过原型链实现,但存在子类共享父类实例的问题,推荐使用构造函数组合继承解决。文章还提供了复习要点速查表和记忆口诀,帮助理解这些核心概念。
1. 原型链机制
1.1 原型链概念
基于原型对象的继承使得不同构造函数的原型对象关联在一起,形成链状结构
function Star() {}
function Object() {}
// 原型链关系:
// Star实例 → Star.prototype → Object.prototype → null
const ldh = new Star();
// 验证:
console.log(ldh.__proto__ === Star.prototype); // true
console.log(Star.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null
1.2 原型链查找规则
-
自身属性:先在对象自身查找
-
原型对象 :没找到则查找
__proto__
指向的原型对象 -
Object原型:还没找到则继续查找原型对象的原型
-
终点null:直到找到Object.prototype的原型(null)为止
function Star() {
this.name = '刘德华';
}Star.prototype.sing = function() {
console.log('唱歌');
};Object.prototype.dance = function() {
console.log('跳舞');
};const ldh = new Star();
// 查找过程:
ldh.name; // 1. 自身属性 → "刘德华"
ldh.sing(); // 2. Star原型 → "唱歌"
ldh.dance(); // 3. Object原型 → "跳舞"
ldh.toString(); // 3. Object原型 → "[object Object]"
ldh.xxx; // 4. 最终未找到 → undefined
1.3 instanceof 运算符
检测构造函数的
prototype
是否出现在实例对象的原型链上
console.log(ldh instanceof Star); // true
console.log(ldh instanceof Object); // true
console.log(ldh instanceof Array); // false
2. 深浅拷贝
2.1 直接赋值的问题
直接赋值拷贝的是地址,新旧对象相互影响
const pink = {
name: 'pink老师',
age: 18
};
const red = pink; // 直接赋值(拷贝地址)
red.name = 'red老师';
console.log(pink.name); // "red老师"(原对象被修改)
console.log(red.name); // "red老师"
2.2 浅拷贝
只拷贝对象的第一层属性(基本类型拷贝值,引用类型拷贝地址)
// 浅拷贝方法
const obj = { a: 1, b: { c: 2 } };
const shallowCopy = { ...obj }; // 展开运算符
// 或
const shallowCopy2 = Object.assign({}, obj);
// ✅ 第一层属性互不影响
shallowCopy.a = 10;
console.log(obj.a); // 1(未受影响)
// ❌ 深层属性仍然共享地址
shallowCopy.b.c = 20;
console.log(obj.b.c); // 20(受影响)
2.3 深拷贝
完全拷贝整个对象,新旧对象完全独立
方法1:JSON方法(最简单)
const obj = {
name: 'pink',
hobby: ['篮球', '足球'],
family: { baby: '小pink' }
};
const deepCopy = JSON.parse(JSON.stringify(obj));
// 修改拷贝对象
deepCopy.family.baby = '老pink';
console.log(obj.family.baby); // "小pink"(不受影响)
console.log(deepCopy.family.baby); // "老pink"
方法2:递归实现
function deepClone(obj) {
// 基本类型直接返回
if (typeof obj !== 'object' || obj === null) return obj;
// 初始化结果对象
const result = Array.isArray(obj) ? [] : {};
for (let key in obj) {
// 确保是自身属性(非继承属性)
if (obj.hasOwnProperty(key)) {
// 递归复制
result[key] = deepClone(obj[key]);
}
}
return result;
}
// 使用
const obj = { a: 1, b: { c: 2 } };
const cloned = deepClone(obj);
cloned.b.c = 20;
console.log(obj.b.c); // 2(不受影响)
方法3:使用lodash库
// 先引入lodash库
import _ from 'lodash';
const obj = { a: 1, b: { c: 2 } };
const deepCopy = _.cloneDeep(obj);
deepCopy.b.c = 20;
console.log(obj.b.c); // 2(不受影响)
3. 原型继承
3.1 基本实现
通过原型链实现对象间的继承关系
// 人类(父类)
function People() {
this.head = 1;
this.eyes = 2;
}
People.prototype.say = function() {
console.log('说话');
};
// 男人(子类)
function Man() {
this.gender = '男';
}
// 继承:将Man的原型指向People的实例
Man.prototype = new People();
Man.prototype.constructor = Man; // 修复constructor
const zs = new Man();
console.log(zs.head); // 1(继承自People)
zs.say(); // "说话"(继承自People原型)
3.2 问题与解决方案
问题:多个子类共享同一个父类实例
function Woman() {}
Woman.prototype = new People(); // 同样指向People的实例
// 修改一个子类会影响所有子类
Woman.prototype.age = 18;
const ls = new Man();
console.log(ls.age); // 18(不应被影响)
解决方案:使用构造函数组合继承
function People(head, eyes) {
this.head = head;
this.eyes = eyes;
}
function Man() {
People.call(this, 1, 2); // 调用父类构造函数
this.gender = '男';
}
// 设置原型链
Man.prototype = Object.create(People.prototype);
Man.prototype.constructor = Man;
4. 复习要点速查表
原型链核心要点
概念 | 说明 | 示例 |
---|---|---|
原型链 | 对象间通过原型链接的链式结构 | obj → 构造函数.prototype → Object.prototype → null |
查找规则 | 自底向上查找属性/方法 | 先自身 → 原型 → 原型的原型... |
instanceof | 检测构造函数的prototype是否在原型链上 | obj instanceof Constructor |
深浅拷贝对比
方式 | 特点 | 影响 |
---|---|---|
直接赋值 | 拷贝地址 | 新旧对象完全共享 |
浅拷贝 | 只拷贝第一层 | 深层属性仍共享 |
深拷贝 | 完全独立拷贝 | 新旧对象互不影响 |
深拷贝方法对比
方法 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
JSON方法 | 简单快速 | 1. 不能处理函数/undefined 2. 不能处理循环引用 | 普通JSON对象 |
递归实现 | 可定制性强 | 1. 需处理循环引用 2. 需考虑各种数据类型 | 复杂对象 |
lodash.cloneDeep | 功能完善 | 需引入外部库 | 生产环境推荐 |
原型继承要点
-
原型链继承 :
Child.prototype = new Parent()
-
问题:多个子类共享同一个父类实例
-
解决方案:组合继承(构造函数 + 原型链)
记忆口诀 :
"原型链,链链相扣;查找规则,层层递进"
"深浅拷贝,层级不同;深拷贝独立,浅拷贝共享"
"原型继承,组合为佳;构造函数初始化,原型链连方法"