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 相关文章推荐
学习ExtJS fit布局使用说明
Oct 08 Javascript
Jquery网页出现的乱码问题的三种解决方法
Jun 30 Javascript
javascript unicode与GBK2312(中文)编码转换方法
Nov 14 Javascript
get(0).tagName获得作用标签示例代码
Oct 08 Javascript
jquery幻灯片插件bxslider样式改进实例
Oct 15 Javascript
详解AngularJS中的依赖注入机制
Jun 17 Javascript
javascript 应用小技巧方法汇总
Jul 05 Javascript
深入理解JavaScript中Ajax
Aug 02 Javascript
jquery无法为动态生成的元素添加点击事件的解决方法(推荐)
Dec 26 Javascript
ES6数组的扩展详解
Apr 25 Javascript
详解angularjs跨页面传参遇到的一些问题
Nov 01 Javascript
html-webpack-plugin修改页面的title的方法
Jun 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
一个域名查询的程序
2006/10/09 PHP
php checkbox复选框值的获取与checkbox默认值输出方法
2010/05/15 PHP
php版微信公众平台接口参数调试实现判断用户行为的方法
2016/09/23 PHP
PHP+redis实现的购物车单例类示例
2019/02/02 PHP
在javascript将NodeList作为Array数组处理的方法
2010/07/09 Javascript
如何使用jQuery来处理图片坏链具体实现步骤
2013/05/02 Javascript
javascript实现获取cookie过期时间的变通方法
2014/08/14 Javascript
基于JavaScript实现瀑布流布局(二)
2016/01/26 Javascript
vue-next/runtime-core 源码阅读指南详解
2019/10/25 Javascript
Vue路由管理器Vue-router的使用方法详解
2020/02/05 Javascript
python实现将元祖转换成数组的方法
2015/05/04 Python
详解Python中用于计算指数的exp()方法
2015/05/14 Python
Python中Random和Math模块学习笔记
2015/05/18 Python
pymssql数据库操作MSSQL2005实例分析
2015/05/25 Python
Windows下实现Python2和Python3两个版共存的方法
2015/06/12 Python
Pandas过滤dataframe中包含特定字符串的数据方法
2018/11/07 Python
Python编写通讯录通过数据库存储实现模糊查询功能
2019/07/18 Python
在python Numpy中求向量和矩阵的范数实例
2019/08/26 Python
python实现发送带附件的邮件代码分享
2020/09/22 Python
Python爬虫+Tkinter制作一个翻译软件的示例
2021/02/20 Python
CSS3中的@keyframes关键帧动画的选择器绑定
2016/06/13 HTML / CSS
THE OUTNET美国官网:国际设计师品牌折扣网站
2017/03/07 全球购物
friso美素佳儿官方海外旗舰店:荷兰原产原罐
2017/07/03 全球购物
英国蜡烛、蜡烛配件和家居香氛购买网站:Yankee Candle
2018/12/12 全球购物
C#里面如何判断一个Object是否是某种类型(如Boolean)?
2016/02/10 面试题
小区门卫管理制度
2014/01/29 职场文书
群众路线剖析材料
2014/02/02 职场文书
中国好声音华少广告词
2014/03/17 职场文书
贯彻落实“八项规定”思想汇报
2014/09/13 职场文书
班主任经验交流材料
2014/12/16 职场文书
物业保洁员岗位职责
2015/02/13 职场文书
2015年乡镇民政工作总结
2015/05/13 职场文书
幼儿园开学报名通知
2015/07/16 职场文书
如何解决goland,idea全局搜索快捷键失效问题
2022/04/03 Golang
《遗弃》开发商删推文要跑路?官方回应:还在开发
2022/04/03 其他游戏
python读取mat文件生成h5文件的实现
2022/07/15 Python