ES5类 原型  原型链 继承

  • JavaScript中,原型是相对于构造函数(类)的叫法(或者说概念),原型链是相对于构造函数(类)的实例对象的叫法。
  • 对于JavaScript对象,如果在对象自身上找不到该属性,那么就会向上沿着原型链继续查找该属性

创建一个ES5类

在ES5中,类由函数名首字母大写的函数,通过关键字new创建。

类的构造函数就是函数自身

ES5类的原型对象prototype是自身构造函数,该类的实例化对象的原型链对象__proto__也是该构造函数,这二者指向同一个引用

类的prototype属性与对象的原型链__proto__属性:

因此,给类的原型对象prototype设置属性和方法,相当于给类的实例化对象的原型链对象设置属性和方法,因为二者是同一引用。

如果要访问这些prototype上的属性,那么可以在实例化对象的原型链上找到它们。

ES5类与继承

以上,如果把一个类Box的原型对象设置为另一个类的实例化对象F,那么Box实例化对象的原型链就是该F对象。

F对象的原型链拥有F类原型的属性和方法(同一个引用),而Box类的实例化对象的原型链__proto__是F对象,也拥有了F类prototype的属性和方法,并且这些属性可以沿着原型链被访问到

以上理论上完成了继承,但是存在缺陷。因为Ball.prototype = new F() ,会把Ball.prototype原有的属性和方法覆盖掉,这显然不是我们想要的,也不符合继承的特性。

继承的实现

1 组合式继承

/*
组合式继承:
1 冒充:
子类构造函数调用外面父类构造函数,并绑定父类构造函数的this为子类的实例
这样子类的实例掉用父类的构造函数为自己设置了相应属性
2 子类原型对象设置为父类对象:
其实就是将子类对象的原型链设置为父类对象
以上:
1冒充完成了子类对象自身属性的设置
2完成了子类对象原型链的设置
缺陷:
父类构造函数执行两次,1,2各一次
2会造成子类原型对象原有的属性和方法被覆盖、重置
*/
function Box(_r) {
//设置实例化对象的属性
this.r = _r;
console.log("constructor run");
}
Box.a = 3;
Box.run = function () {
console.log(Box.a);
}
//设置类的原型属性,设置实例化对象的原型链属性
Box.prototype = {
b: 10,
play: function () { }
}
// 设置类的原型对象的构造函数为Box
Object.defineProperty(Box.prototype, "constructor", {
value: Box
}); function Ball(_r) {
//冒充
Box.call(this, _r);
}
Ball.prototype.c = 20;
//设置子类对象的原型链为父类原型对象,会覆盖原有的方法和属性,如c
Ball.prototype = new Box(3);
var ball = new Ball(100);
console.log(ball);

运行结果:

2 原型继承

 /* 

             原型继承:
1 设置中间类F,用F的原型对象接收父类的原型对象
2 不调用父类构造,解决了组合继承调用了两次父类构造的弊病
总结:
只完成了原型的继承,没有调用父类的构造函数来设置子类对象的自身属性
方法是用一个类F(){},设置F的原型对象为父的原型对象,设置子类原型为F类实例化对象
F解决了实例化子类对象父类构造函数执行了两次的问题(new Box()变成了new F(),不调用父类构造)
问题:
因为一次也不调用父类构造,使得对象实例化时无法传参
依然覆盖了子类原有原型属性,继承时,属性相同,应当保留子类属性,覆盖父类属性
*/ function Box(_r) {
//设置Box类实例化对象的属性
this.r = _r;
console.log("constructor run");
}
Box.a = 3;
Box.run = function () {
console.log(Box.a);
}
//设置类的原型属性,设置实例化对象的原型链属性
Box.prototype = {
b: 10,
play: function () { }
}
Object.defineProperty(Box.prototype, "constructor", {
value: Box
});
function F() { }
F.prototype = Box.prototype;
function Ball() { }
Ball.prototype = new F();
console.log(Ball.prototype);
// Ball.prototype =Object.create(F.prototype);//IE9以上可以这样写
console.log(new Ball());

运行结果:

3 寄生式继承

  1. 组合继承存在调用两次父类构造的问题
  2. 原型继承存在不能实例化对象不能传参的问题
  3. 组合继承和原型继承都存在子类原有原型属性被覆盖的问题
  4. 因此推荐使用寄生式继承
 /*
寄生式继承:
1 解决子类原型对象属性被覆盖的问题:
设置F.property接收父类原型属性,将子类原型属性复制到F.property
再将F.peoperty设置给子类原型完成覆盖
2 解决调用父类构造问题:
将父类当成属性设置到子类的原型上,子类实例对象就可以在原型链上找到父类构造函数,
子类可以通过this调用该方法实现实例化,不通过调用外面父类的构造函数也可以完成传参
这样即实现了功能,又避免了组合继承调用父类构造次数过多和原型继承实例化无法通过构造传参的问题
*/
function Box(_r) {
//设置Box类实例化对象的属性
this.r = _r;
console.log("constructor run");
}
Box.a = 3;
Box.run = function () {
console.log(Box.a);
}
Box.prototype = {
b: 10,
play: function () { }
} function Ball(_r) {
this.supClass.apply(this, arguments);
}
Ball.prototype.walk = function () {
console.log("walk");
}
extend(Ball, Box);
Ball.prototype.play = function () {
this.supClass.prototype.play.apply(this.arguments);
console.log("finish method");
}
var b = new Ball(10);//print constructor run
b.play(); // print finish method
console.log(b); function extend(subClass, supClass) {
function F() { }
//将子类原型属性覆盖掉父类原型相同的属性,再设置给子类
F.prototype = supClass.prototype;
//复制原来原型下的内容
if (Object.assign) {
//Objec.asssign不兼容IE
Object.assign(F.prototype, subClass.prototype);
} else {
if (Object.getOwnPropertyNames) {
var names = Object.getOwnPropertyNames(subClass.prototype);
for (var i = 0; i < names.length; i++) {
Object.defineProperty(F.prototype, name[i], Object.getOwnPropertyDescriptor(names[i]));
}
}
else {
for (var prop in subClass.prototype) {
F.prototype[prop] = subClass.prototype[prop];
}
}
}
//完成属性覆盖
subClass.prototype = new F();
//将子类构造设置为自身,不调用外面父类的构造
subClass.prototype.constructor = subClass;
//父类设置到子类原型上去以供this调用
subClass.prototype.supClass = supClass;
if (supClass.prototype.constructor === Object) {
supClass.prototype.constructor = supClass;
}
}

执行结果:

JavaScript ES5类 原型 原型链 组合、原型、寄生式继承的更多相关文章

  1. JavaScript继承基础讲解,原型链、借用构造函数、混合模式、原型式继承、寄生式继承、寄生组合式继承

    说好的讲解JavaScript继承,可是迟迟到现在讲解.废话不多说,直接进入正题. 既然你想了解继承,证明你对JavaScript面向对象已经有一定的了解,如还有什么不理解的可以参考<面向对象J ...

  2. ES5 寄生式继承

    3 寄生式继承 组合继承存在调用两次父类构造的问题 原型继承存在不能实例化对象不能传参的问题 组合继承和原型继承都存在子类原有原型属性被覆盖的问题 因此推荐使用寄生式继承 /* 寄生式继承: 1 解决 ...

  3. Javascript继承5:如虎添翼----寄生式继承

    /* * 寄生式继承 * 其实就是对原型继承的第二次封装,在封装过程中对继承的对象进行了扩展. * 也存在原型继承的缺点!! * 这种思想的作用也是为了寄生组合式继承模式的实现. */ //声明基对象 ...

  4. JavaScript之面向对象学九(原型式继承和寄生式继承)

    一.原型式继承 该继承模式是由道格拉斯*克罗克福德在2006年提出的实现继承的方法. 模式的基本思路:借助原型可以基于已有的对象创建新的对象,同时还不必因此创建自定义类型. 代码如下: functio ...

  5. JavaScript ----------------- 寄生式继承

    寄生式继承 寄生式继承是于原型式继承紧密相关的一种思路.寄生式基础的思路与寄生构造函数和工厂模式类似,既创建一个仅用于封装继承过程的函数,该函数内部以某种方式来增强对象,最后再像真地是它做了所有工作一 ...

  6. JavaScript各种继承方式(五):寄生式继承(parasitic)

    一 原理 与原型式继承完全相同,只是对父类的实例(也当作子类的实例使用)进行了增强. function create(obj){ let mango = Object.create(obj); man ...

  7. JS学习笔记——JavaScript继承的6种方法(原型链、借用构造函数、组合、原型式、寄生式、寄生组合式)

    JavaScript继承的6种方法 1,原型链继承 2,借用构造函数继承 3,组合继承(原型+借用构造) 4,原型式继承 5,寄生式继承 6,寄生组合式继承 1.原型链继承. <script t ...

  8. 一篇文章理解JS继承——原型链/构造函数/组合/原型式/寄生式/寄生组合/Class extends

    说实在话,以前我只需要知道"寄生组合继承"是最好的,有个祖传代码模版用就行.最近因为一些事情,几个星期以来一直心心念念想整理出来.本文以<JavaScript高级程序设计&g ...

  9. 谈JS中的作用域链与原型链(1)

    学习前端也有一段时间了,觉得自己可以与大家分享一些我当初遇到疑惑的东西,希望能给对此问题有疑惑的朋友带来一点帮助. 先来普及一下JS的概念(不要嫌我啰嗦,可能一些朋友开始学习JS是跟着视频和写好的代码 ...

随机推荐

  1. python多线程返回值的实现与处理

    题目: # 编写一个python程序,创建两个子线程,分别到下面的网址获取文本内容# http://mirrors.163.com/centos/6/isos/x86_64/README.txt# h ...

  2. Linux 基本命令简单学习

    平常工作中需要使用到的一些Linux基本命令,简单记录: 通过订单号查看日志:   cat /---/---/xxxx20190908.log | grep C52918588112261 -C 5 ...

  3. ansible笔记(12):变量(一)

    1.定义变量规范 先说说怎样定义变量,变量名应该由字母.数字.下划线组成,变量名需要以字母开头,ansible内置的关键字不能作为变量名. 2.定义变量,使用关键字:vars 定义变量用vars,调用 ...

  4. SSM-整合简单配置

    首先说明Spring和Mybatis的版本: Spring:3.2.4 Mybatis:3.3.0 使用了C3P0连接池和Log4J日志,下面是jar包总览: 然后是项目目录总览: 为了能够让项目跑一 ...

  5. python collections 模块 常用集合

    1.nametulpe # tuple 可以表示不变集合 列如坐标 point = (1,1) # 缺点是 只可以通过下标方式访问 #namedtuple是一个函数,它用来创建一个自定义的tuple对 ...

  6. 算法导论2-4 O(nlgn)时间复杂度求逆序对

    def mergesort(nums,le,ri): if le>ri-2: return 0 mi=le+(ri-le)//2 a=mergesort(nums,le,mi) b=merges ...

  7. dfs题型二(迷宫问题)

    取自:<王道论坛计算机考研机试指南>6.5节 例 6.7 Temple of the bone(九度 OJ 1461)时间限制:1 秒 内存限制:32 兆 特殊判题:否题目描述:The d ...

  8. 题解【2.23考试T1】div

    1. div[题目描述] 这是一道传统题,源代码的文件名为 div.cpp/c/pas. 给定一棵树,你要判断是否存在一条边,使得割掉这条边后,这棵树被分成了点数相等的两部分,并且如果存在,请你找到这 ...

  9. 安装 Navicat for MySQL

    安装 Navicat for MySQL 下载地址:https://www.pcsoft.com.cn/soft/20832.html

  10. 【C语言】输入一个字符串,并对字符串中的偶数位置的字符按从小到大的顺序排序,奇数位置的字符不动,输出排序后的结果

    #include <stdio.h> #include<string.h> int main() { ]; int i,j,k,len; gets_s(a); len= str ...