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


Posted in Javascript onFebruary 10, 2020

本文实例讲述了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面向对象入门教程》、《JavaScript查找算法技巧总结》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》及《JavaScript数学运算用法总结》

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

Javascript 相关文章推荐
js中匿名函数的创建与调用方法分析
Dec 19 Javascript
javascript实现rgb颜色转换成16进制格式
Jul 10 Javascript
jquery转盘抽奖功能实现
Nov 13 Javascript
JS 清除字符串数组中,重复元素的实现方法
May 24 Javascript
一道优雅面试题分析js中fn()和return fn()的区别
Jul 05 Javascript
详细谈谈AngularJS的子级作用域问题
Sep 05 Javascript
jQuery+ajax读取并解析XML文件的方法
Sep 09 Javascript
Angularjs自定义指令实现分页插件(DEMO)
Sep 16 Javascript
浅析vue-router原理
Oct 19 Javascript
Vue Element UI + OSS实现上传文件功能
Jul 31 Javascript
详解mpvue实现对苹果X安全区域的适配
Jul 31 Javascript
利用 Chrome Dev Tools 进行页面性能分析的步骤说明(前端性能优化)
Feb 24 Javascript
在 Vue 中使用 JSX 及使用它的原因浅析
Feb 10 #Javascript
node.js使用http模块创建服务器和客户端完整示例
Feb 10 #Javascript
webpack打包优化的几个方法总结
Feb 10 #Javascript
JS+HTML5本地存储Localstorage实现注册登录及验证功能示例
Feb 10 #Javascript
node.js制作一个简单的登录拦截器
Feb 10 #Javascript
使用 Vue-TCB 快速在 Vue 应用中接入云开发的方法
Feb 10 #Javascript
jQuery实现简易QQ聊天框
Feb 10 #jQuery
You might like
第六节 访问属性和方法 [6]
2006/10/09 PHP
php在程序中将网页生成word文档并提供下载的代码
2012/10/09 PHP
解析PHP中ob_start()函数的用法
2013/06/24 PHP
PHP_SELF,SCRIPT_NAME,REQUEST_URI区别
2014/12/24 PHP
ThinkPHP3.2.2实现持久登录(记住我)功能的方法
2016/05/16 PHP
PHP实现自动识别原编码并对字符串进行编码转换的方法
2016/07/13 PHP
php中各种定义变量的方法小结
2017/10/18 PHP
Jquery.LazyLoad.js修正版下载,实现图片延迟加载插件
2011/03/12 Javascript
JS window对象的top、parent、opener含义介绍
2013/12/03 Javascript
jquery插件NProgress.js制作网页加载进度条
2015/06/05 Javascript
confirm确认对话框的实现方法总结
2016/06/17 Javascript
实现隔行换色效果的两种方式【实用】
2016/11/27 Javascript
JS常用加密编码与算法实例总结
2016/12/22 Javascript
jQuery插件echarts实现的单折线图效果示例【附demo源码下载】
2017/03/04 Javascript
详解使用JS如何制作简单的ASCII图与单极图
2017/03/31 Javascript
JavaScript输入框字数实时统计更新
2017/06/17 Javascript
Vue自定义事件(详解)
2017/08/19 Javascript
JS异步函数队列功能实例分析
2017/11/28 Javascript
原生JS实现的简单轮播图功能【适合新手】
2018/08/17 Javascript
python连接sql server乱码的解决方法
2013/01/28 Python
python实现绘制树枝简单示例
2014/07/24 Python
Python处理XML格式数据的方法详解
2017/03/21 Python
python对配置文件.ini进行增删改查操作的方法示例
2017/07/28 Python
Python常见数字运算操作实例小结
2019/03/22 Python
python输出决策树图形的例子
2019/08/09 Python
python3 requests库实现多图片爬取教程
2019/12/18 Python
Django中modelform组件实例用法总结
2020/02/10 Python
Python中三维坐标空间绘制的实现
2020/09/22 Python
Python爬虫中Selenium实现文件上传
2020/12/04 Python
加拿大在线隐形眼镜专家:PerfectLens.ca
2016/11/19 全球购物
平面设计师岗位职责
2014/09/18 职场文书
2015年五一劳动节活动总结
2015/02/09 职场文书
后勤个人工作总结
2015/02/28 职场文书
2016年春节慰问信息大全
2015/11/30 职场文书
如何起草一份正确的合伙创业协议书?
2019/07/04 职场文书
Mysql数据库group by原理详解
2022/07/07 MySQL