JavaScript原型继承和原型链原理详解


Posted in Javascript onFebruary 04, 2020

这篇文章主要介绍了JavaScript原型继承和原型链原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

在讨论原型继承之前,先回顾一下关于创建自定义类型的方式,这里推荐将构造函数和原型模式组合使用,通过构造函数来定义实例自己的属性,再通过原型来定义公共的方法和属性。

这样一来,每个实例都有自己的实例属性副本,又能共享同一个方法,这样的好处就是可以极大的节省内存空间。同时还可以向构造函数传递参数,十分的方便。

这里还要再讲一下两种特色的构造函数模式:

1.寄生构造函数

从形式上来看,这种模式和工厂模式并无区别:

function Person(name, age, job) {
  var o = new Object();
  o.name = name;
  o.age = age;
  o.job = job;
  o.sayName = function() {
    alert(this.name);
  };
  return o;
}
var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName(); //"Nicholas"

都是在内部创建一个Object对象实例,再赋予属性和方法,最后返回,这种构造模式的好处是,对于原生的引用类型创建的实例,例如Array,想为实例创建新的方法或者属性时,因为不建议直接修改原生的Array引用类型的构造函数,所以可以利用寄生构造函数:

function SpecialArray() {
  //创建数组
  var values = new Array();
  //添加值
  values.push.apply(values, arguments);
  //添加方法
  values.toPipedString = function() {
    return this.join("|");
  };
  //返回数组
  return values;
}
var colors = new SpecialArray("red", "blue", "green");
alert(colors.toPipedString()); //"red|blue|green"

通过在内部创建一个Array实例,并添加新的方法,最后将这个实例返回,既没有修改原生的Array构造函数,又成功添加了自定义的方法和属性。

缺点:使用寄生构造函数有一个缺点,那就是返回的实例与构造函数或构造函数原型属性之间没有关系,与在构造函数外创建实例没有区别,也无法通过instanceof来确定对象类型,因此有其他更好选择的时候,不推荐使用该方法。

2.稳妥构造函数

稳妥构造函数与寄生构造函数类似,但是并不使用new和this(某些环境下禁止使用),前面的函数可以改写成这样:

function Person(name, age, job) {
  //创建要返回的对象
  var o = new Object();

  //可以在这里定义私有变量和函数
  //添加方法
  o.sayName = function() {
    alert(name);
  };
  //返回对象
  return o;
}

说完这些,现在来谈谈原型继承和原型链,所谓继承,基本思想是利用原型让一个引用类型继承另一个引用类型的方法和属性。每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。所有引用类型默认都继承了Object,而这个继承也是通过原型链实现的。大家要记住,所有函数的默认原型都是Object 的实例,因此默认原型都会包含一个内部指针,指向Object.prototype。

借用构造函数(即在子类型构造函数的内部调用超类型构造函数)

如果仅仅是借用构造函数,那么也将无法避免构造函数模式存在的问题——方法都在构造函数中定
义,因此函数复用就无从谈起了。而且,在超类型的原型中定义的方法,对子类型而言也是不可见的,结
果所有类型都只能使用构造函数模式。考虑到这些问题,借用构造函数的技术也是很少单独使用的。

组合继承(思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承)

function SuperType(name) {
  this.name = name;
  this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function() {
  alert(this.name);

};
function SubType(name, age) {
  //继承属性
  SuperType.call(this, name);
  this.age = age;
}
//继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function() {
  alert(this.age);
};
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29
var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27

SuperType 构造函数定义了两个属性:name 和colors。SuperType 的原型定义了一个方法sayName()。SubType 构造函数在调用SuperType 构造函数时传入了name 参数,紧接着又定义了它自己的属性age。然后,将SuperType 的实例赋值给SubType 的原型,然后又在该新原型上定义了方法sayAge()。这样一来,就可以让两个不同的SubType 实例既分别拥有自己属性——包括colors 属性,又可以使用相同的方法了。

原型式继承(借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型)

var person = {
  name: "Nicholas",
  friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"

在没有必要兴师动众地创建构造函数,而只想让一个对象与另一个对象保持类似的情况下,原型式
继承是完全可以胜任的。不过别忘了,包含引用类型值的属性始终都会共享相应的值,就像使用原型模
式一样。

寄生式继承(寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象)

function createAnother(original) {
  var clone = object(original); //通过调用函数创建一个新对象
  clone.sayHi = function() { //以某种方式来增强这个对象
    alert("hi");
  };
  return clone; //返回这个对象
}

寄生组合式继承

通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。其背后的基本思路是:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型原型的一个副本而已。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascript引用对象的方法代码
Aug 13 Javascript
ExtJS 2.2.1的grid控件在ie6中的显示问题
May 04 Javascript
三级下拉菜单的js实现代码
May 23 Javascript
JS左右无缝滚动(一般方法+面向对象方法)
Aug 17 Javascript
浅谈JSON中stringify 函数、toJosn函数和parse函数
Jan 26 Javascript
jQuery设置Cookie及删除Cookie实例分析
Apr 15 Javascript
利用VUE框架,实现列表分页功能示例代码
Jan 12 Javascript
js原生代码实现轮播图的实例讲解
Jul 28 Javascript
js 中rewrap-ajax.js插件实例代码
Oct 20 Javascript
Koa项目搭建过程详细记录
Apr 12 Javascript
react的滑动图片验证码组件的示例代码
Feb 27 Javascript
js实现超级玛丽小游戏
Mar 18 Javascript
JavaScript单线程和任务队列原理解析
Feb 04 #Javascript
Vue中this.$nextTick的作用及用法
Feb 04 #Javascript
JS中this的4种绑定规则详解
Feb 04 #Javascript
详解JavaScript中精度失准问题及解决方法
Feb 04 #Javascript
Preload基础使用方法详解
Feb 03 #Javascript
使用PreloadJS加载图片资源的基础方法详解
Feb 03 #Javascript
使用preload预加载页面资源时注意事项
Feb 03 #Javascript
You might like
PHP函数篇详解十进制、二进制、八进制和十六进制转换函数说明
2011/12/05 PHP
PHP 杂谈《重构-改善既有代码的设计》之一 重新组织你的函数
2012/04/09 PHP
PHP使用递归生成文章树
2015/04/21 PHP
php实现的微信红包算法分析(非官方)
2015/09/25 PHP
浅谈PHP中其他类型转化为Bool类型
2016/03/28 PHP
PHP实现基于mysqli的Model基类完整实例
2016/04/08 PHP
JavaScript中textRange对象使用方法小结
2015/03/24 Javascript
JS字符串长度判断,超出进行自动截取的实例(支持中文)
2017/03/06 Javascript
Webpack如何引入bootstrap的方法
2017/06/17 Javascript
微信小程序picker组件简单用法示例【附demo源码下载】
2017/12/05 Javascript
vue 使某个组件不被 keep-alive 缓存的方法
2018/09/21 Javascript
Vue 处理表单input单行文本框的实例代码
2019/05/09 Javascript
微信小程序中悬浮窗功能的实现代码
2019/08/02 Javascript
Node Mongoose用法详解【Mongoose使用、Schema、对象、model文档等】
2020/05/13 Javascript
详解React 条件渲染
2020/07/08 Javascript
Python常用随机数与随机字符串方法实例
2015/04/09 Python
python DataFrame获取行数、列数、索引及第几行第几列的值方法
2018/04/08 Python
python求质数的3种方法
2018/09/28 Python
python用match()函数爬数据方法详解
2019/07/23 Python
关于Python 常用获取元素 Driver 总结
2019/11/24 Python
python 解决flask uwsgi 获取不到全局变量的问题
2019/12/22 Python
CSS3 @keyframes简单动画实现
2018/02/24 HTML / CSS
用HTML5制作数字时钟的教程
2015/05/11 HTML / CSS
浅谈利用缓存来优化HTML5 Canvas程序的性能
2015/05/12 HTML / CSS
YSL Beauty加拿大官方商城:圣罗兰美妆加拿大
2017/05/15 全球购物
网游商务专员求职信
2013/10/15 职场文书
2013年保送生自荐信格式
2013/11/20 职场文书
村抢险救灾方案
2014/05/09 职场文书
市场营销专业自荐书
2014/06/10 职场文书
大学第二课堂活动总结
2014/07/08 职场文书
列车乘务员工作不细心检讨书
2014/10/07 职场文书
银行授权委托书样本
2014/10/13 职场文书
英语辞职信怎么写
2015/02/28 职场文书
百年校庆宣传标语口号
2015/12/26 职场文书
2016年圣诞节活动总结范文
2016/04/01 职场文书
Python Pandas知识点之缺失值处理详解
2021/05/11 Python