javascript 原型与原型链的理解及实例分析


Posted in Javascript onNovember 23, 2019

本文实例讲述了javascript 原型与原型链的理解。分享给大家供大家参考,具体如下:

javascript中一切皆对象,但是由于没有Class类的概念,所以就无法很好的表达对象与对象之间的关系了。

比如对象A与对象B之间,它们两个是相对独立的个体,互不干扰,对象A修改自身的属性不会影响到对象B。

虽然这很好,但是有一个问题,如果对象A与对象B都有一个方法 run() ,并且代码也一样,那对象A与对象B各自都独立拥有一份 run() 方法的完整代码,这是需要资源去保存的。

一旦我们程序中应用的对象过多,那这种资源消耗会是巨大的。那有没有一种方法可以让对象A与对象B拥有一些公共的属性和方法,让它们之前有某种联系?

我们设想一下,会不会存在一个 common对象(公共对象),common对象上保存着公共的属性和方法,而对象A与对象B里面有一个prototype属性指向这个 common对象,

当然我们调用对象A或对象B的属性和方法时,如果在自身对象中没有找到,就去prototype这个属性指向的对象上面去找。

而common对象本身也有一个prototype属性指向更上一级的common对象,然后一直往上找啊找,直到为null,就停止。

这种不断的从下往上找的这种路径,就像链条一样,我们称它为 原型链,而那个common对象,我们称它为 原型对象。

我们来看一个构造函数

function Base(name) {
  this.name = name;
}
let A = new Base('A');
let B = new Base('B');
//每一个函数都有一个prototype属性,指向该函数的原型对象
console.log(Base.prototype);
//当然原型对象也是一个对象,它也有一个constructor,指向构造函数
console.log(Base.prototype.constructor === Base);
//每一个实例对象的constructor都指向创建它们的构造函数
console.log(A.constructor === Base);
console.log(B.constructor === Base);
//每一个实例对象都有一个__proto__属性,该属性指向构造函数的原型对象
console.log(A.__proto__ === Base.prototype);
console.log(B.__proto__ === Base.prototype);

1、每一个函数都有一个prototype属性,它指向该函数的原型对象。

2、原型对象也是对象,它也有自已的constructor,它指向构造函数Base()。换句话说,其实原型对象也是构造函数Base()的一个实例。只不过比较特殊,用来存放公共属性和方法的。

3、每一个通过构造函数Base()创建的实例对象,都有一个constructor,指向创建它们的构造函数。

4、每一个对象,都有一个 __proto__ 属性,指向构造函数Base()的 原型对象。换句话说,__proto__ 是将 原型 串联起来形成链条的关键。不然对象A与对象B都无法找到原型对象上的公共属性和方法。

function Base(name) {
  this.name = name;
}
//我们在原型对象上添加公共属性
Base.prototype.status = '开始';
//我们在原型对象上添加公共方法
Base.prototype.run = function() {
  console.log(this.name + ' run ...');
};
let A = new Base('A');
let B = new Base('B');
A.run();
B.run();
console.log(A.status);
console.log(B.status);
//修改原型上的属性,则实例对象也会跟着改变
Base.prototype.status = '停止';
console.log(A.status);
console.log(B.status);

通过原型与原型链,让对象与对象之间有了关联关系。

那如何通过原型与原型链,让一个构造函数继承于另一个构造函数?

比如,我们要让构造函数Child 继承于 构造函数Base,只需要让 Child 的 prototype 指向 Base的 原型对象,不就可以了?

function Base(name) {
}
Base.prototype.name = 'Base';
Base.prototype.run = function () {
  console.log(this.name + ' run ...');
};
function Child() {
}
Child.prototype = Base.prototype;
//注意这个时候,Child.prototype对象的constructor属性指向了Base
//这就导致通过构造函数Child创建的实例对象,对象的constructor属性会指向Base,而不是Child,这会导致混乱。
//所以我们重新设置Child.prototype.constructor指向Child
Child.prototype.constructor = Child;
let c = new Child();
console.log(c.name);
c.run();

这样有一个问题,Child.prototype 与 Base.prototype 指向同一个原型对象,任何对 Child.prototype 的修改都会反应到 Base.prototype 上面。

这时,Base.prototype.constructor 指向了 Child,这显然是有问题。

我们只能通过一个中间的空构造函数,来完成原型的指向。

function Base(name) {
}
Base.prototype.name = 'Base';
Base.prototype.run = function () {
  console.log(this.name + ' run ...');
};
function Child() {
}
//创建一个中间的空构造函数
function Mid() {
}
//让该空构造函数的prototype指向Base的原型对象
Mid.prototype = Base.prototype;
//再让Child的prototype指向该空构造函数的一个实例
Child.prototype = new Mid();
//这样,当修改Child.prototype.constructor时,Base.prototype就不会受影响了
Child.prototype.constructor = Child;
let c = new Child();
console.log(c.name);
c.run();
//Base.prototype的constructor仍然指向Base,没有受到影响
console.log(Base.prototype.constructor);

那怎么通过原型与原型链,让你一对象继承于另一个对象呢?

比如,我们要让对象B继承于对象A,无非就是想要拿到对象A的属性和方法,这么一想,那通过把对象B的 __proto__  指向 对象A,不就可以实现了?

let A = {
  name: 'A',
  run() {
    console.log(this.name + ' run ...');
  }
};
console.log(A.name);
A.run();
let B = {};
//让对象B的__proto__指向对象A
B.__proto__ = A;
//当对象B调用run()方法时会在自身上找,如果没找到,则通过__proto__向上找
//由于__proto__指向对象A,所以最终会在对象A中找到run()方法
B.run();
B.__proto__.name = 'B';
console.log(A.name);
console.log(B.name);

这样有一个问题,当修改 B.__proto__.name = 'B'; 时,对象A也会受到影响。

我们可以通过ES5提供的 Object.create() 来解决此问题,Object.create()可以通过指定的 原型对象 创建一个新对象。

let A = {
  name: 'A',
  run() {
    console.log(this.name + ' run ...');
  }
};
console.log(A.name);
A.run();
let B = {};
//通过Object.create()创建一个以对象A为原型对象的新对象
//让对象B的__proto__指向该新对象
//这样再操作B.__proto__中的属性就与对象A无关了。
B.__proto__ = Object.create(A);
B.run();
B.__proto__.name = 'B';
console.log(A.name);
console.log(B.name);

感兴趣的朋友可以使用在线HTML/CSS/JavaScript代码运行工具:http://tools.3water.com/code/HtmlJsRun测试上述代码运行效果。

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
javascript获取函数名称、函数参数、对象属性名称的代码实例
Apr 12 Javascript
判断复选框是否被选中的两种方法
Jun 04 Javascript
谷歌浏览器不支持showModalDialog模态对话框的解决方法
Sep 22 Javascript
jQuery实现预加载图片的方法
Mar 17 Javascript
Jsonp 关键字详解及json和jsonp的区别,ajax和jsonp的区别
Dec 30 Javascript
基于JS如何实现类似QQ好友头像hover时显示资料卡的效果(推荐)
Jun 09 Javascript
js实现上下左右弹框划出效果
Mar 08 Javascript
jQuery实现的手动拖动控制进度条效果示例【测试可用】
Apr 18 jQuery
微信小程序开发之tabbar图标和颜色的实现
Oct 17 Javascript
Element input树型下拉框的实现代码
Dec 21 Javascript
Angular6 发送手机验证码按钮倒计时效果实现方法
Jan 08 Javascript
JavaScript React如何修改默认端口号方法详解
Jul 28 Javascript
jquery 插件重新绑定的处理方法分析
Nov 23 #jQuery
微信小程序实现录音功能
Nov 22 #Javascript
小程序实现按下录音松开识别语音
Nov 22 #Javascript
小程序采集录音并上传到后台
Nov 22 #Javascript
webpack优化之代码分割与公共代码提取详解
Nov 22 #Javascript
小程序实现录音上传功能
Nov 22 #Javascript
vue使用recorder.js实现录音功能
Nov 22 #Javascript
You might like
PHP URL地址获取函数代码(端口等) 推荐
2010/05/15 PHP
PHP中绘制图像的一些函数总结
2014/11/19 PHP
jquery动态加载js三种方法实例
2013/08/03 Javascript
js编写trim()函数及正则表达式的运用
2013/10/24 Javascript
JavaScript动态修改弹出窗口大小的方法
2015/04/06 Javascript
javascript实现tab切换的四种方法
2015/11/05 Javascript
jQuery基于函数重载实现自定义Alert函数样式的方法
2016/07/27 Javascript
pc加载更多功能和移动端下拉刷新加载数据
2016/11/07 Javascript
js实现QQ面板拖拽效果(慕课网DOM事件探秘)(全)
2017/09/19 Javascript
vue项目优化之通过keep-alive数据缓存的方法
2017/12/11 Javascript
js最简单的双向绑定实例讲解
2018/01/02 Javascript
详解如何在webpack中做预渲染降低首屏空白时间
2018/08/22 Javascript
js实现动态增加文件域表单功能
2018/10/22 Javascript
JS多个异步请求 按顺序执行next实现解析
2019/09/16 Javascript
[04:59]DOTA2-DPC中国联赛 正赛 Ehome vs iG 选手采访
2021/03/11 DOTA
Python实现去除代码前行号的方法
2015/03/10 Python
关于pip的安装,更新,卸载模块以及使用方法(详解)
2017/05/19 Python
python针对不定分隔符切割提取字符串的方法
2018/10/26 Python
基于python实现名片管理系统
2018/11/30 Python
python3使用matplotlib绘制条形图
2020/03/25 Python
python 浮点数四舍五入需要注意的地方
2020/08/18 Python
css3的transform造成z-index无效解决方案
2014/12/04 HTML / CSS
html5实现的便签特效(实战分享)
2013/11/29 HTML / CSS
VICHY薇姿英国官网:全球专业敏感肌护肤领先品牌
2017/07/04 全球购物
法制宣传月活动方案
2014/05/11 职场文书
小学毕业典礼演讲稿
2014/09/09 职场文书
党政领导班子群众路线对照检查材料思想汇报
2014/09/27 职场文书
2015学生会文艺部工作总结
2015/04/03 职场文书
导游词之崇武古城
2019/10/07 职场文书
python 如何用map()函数创建多线程任务
2021/04/07 Python
在HTML5 localStorage中存储对象的示例代码
2021/04/21 Javascript
详解JVM系列之内存模型
2021/06/10 Javascript
基于Java的MathML转图片的方法(示例代码)
2021/06/23 Java/Android
redis cluster支持pipeline的实现思路
2021/06/23 Redis
Python 类,对象,数据分类,函数参数传递详解
2021/09/25 Python
Win11如何默认打开软件界面最大化?Win11默认打开软件界面最大化的方法
2022/07/15 数码科技