js继承的实现(原型/链、函数伪装)
一、原型继承父类的实例
//父类及其原型属性/方法
function SuperType () {
this.name = ['zc','ls','ww'];
}
SuperType.prototype.getSuperName = function() {
return this.name;
}; //子类及其原型属性/方法
function SubType() {
this.test = ['a','b','c','d'];
}
//子类型的原型指向父类型的实例(即子类的原型复制了父类的构造器以及父类原型属性/方法)
SubType.prototype = new SuperType();①
//为子类原型添加原型拓展属性/方法
SubType.prototype.getSubTest = function() {
return this.test;
} var instance1 = new SubType();
instance1.name.push('yzy');//name属性是原型继承自父类实例
instance1.test.push('e');//test属性是源于子类本身的构造器
console.log(instance1.name,instance1.test) var instance2 = new SubType();
console.log(instance2.name,instance2.test)
控制台输出:
标注:
①注意这里的子类原型指向一个父类的实例(引用传递),那么这块的父类实例就是内存中的一块地址,以后所有的子类实例都会有一个原型属性指向这块地址,并且子类A对这块地址中数据更改也会影响到子类B。
图示:
所以你可以看到,instance1.name是从父类实例来的,这个属性实际存在于这个单例,访问的时候都是引用传递,由于这个单例是共享的,instance1 push了一个数据,那么就算instance2没有任何动作,instance2读的时候数据也会是变化后的数据;
而对于test属性,是子类自身的,所以这个属性值存在于子类实例自身,相互之间互不影响,所以虽然instance1.test push了一个数据,但是instance2访问的时候丝毫不受影响。
缺点:继承自父类实例的原型属性会被所有实例所共享。
二、构造函数伪装(call()、apply())
//父类及其原型属性/方法
function SuperType(name) {
this.name = name;
this.color = ['green','red'];
}
SuperType.prototype.getSuperName = function() {
return this.name;
} //子类及其原型属性/方法
function SubType(name) {
SuperType.call(this, name);①
this.test = ['a','b','c','d'];
}
SubType.prototype.getSubTest = function() {
return this.test;
} var instance1 = new SubType('Jack');
console.log(instance1.name,instance1.getSubTest());
console.log('------------------------');
console.log(instance1.getSuperName())
控制台输出:
标注:
①call()方法实际上就是在当前作用域拷贝了一下函数执行者的构造函数/方法,所以上述call()方法实际上做了如下的事情
//子类及其原型属性/方法
function SubType(name) {
//SuperType.call(this, name);
this.name = name;
this.color = ['green','red']; this.test = ['a','b','c','d'];
}
注意的是,call()函数伪装并不会在当前作用域执行 SuperType 原型下的方法/属性
所以,因为 getSuperName() 是父类原型下的方法,所以call() 方法自然不会复制该方法给 SubType 构造器,因此控制台报错也就是理所当然的咯
缺点:函数伪装不会继承父类原型下的属性/方法。
三、组合继承(函数伪装 + 原型继承)
//父类及其原型属性/方法
function SuperType(name) {
this.name = name;
}
SuperType.prototype.getSuperName = function () {
return this.name;
} // 子类1及其原型属性/方法
function SubType1(name) {
SuperType.call(this, name);
this.test = ['h1', 'h2', 'h3', 'h4'];
}
SubType1.prototype = SuperType.prototype;①
SubType1.prototype.getSubTest = function () {
return this.test;
} // 子类2及其原型属性/方法
function SubType2(name) {
SuperType.call(this, name);
this.age = ;
}
SubType2.prototype = SuperType.prototype;
SubType2.prototype.getSubAge = function () {
return this.age;
} var instance1 = new SubType1('Jack');
var instance2 = new SubType2('Tom');
console.log(instance1,instance2);
控制台输出:
标注:
①这里子类原型继承自父类原型,然后子类为原型添加了原型拓展,这里的原型继承是引用传递,所以添加拓展的操作都是基于同一块内存地址的。
图示:
所以,无论是父类的原型属性还是子类继承的原型(父类原型),实际上都是引用传递,都指向内存中的同一块地址,因此,上述的代码,虽然子类2虽然没有原型方法 getSubTest,但是实际上子类1已经在他们指向的共同内存地址添加了该方法,同理子类1也是。
缺点:子类型的原型属性共享。
四、寄生组合式继承
function object(o) {
function F() { };
F.prototype = o;
return new F();
}
//寄生组合式继承 ①
function inheritPrototype(subType, superType) {
var prototype = object(superType.prototype);
subType.prototype = prototype;
} //父类及其原型属性/方法
function SuperType(name) {
this.name = name;
}
SuperType.prototype.getSuerperName = function () {
return this.name;
} //子类1及其原型属性/方法
function SubType(name) {
SuperType.call(this, name);
this.test = ['h1', 'h2', 'h3', 'h4'];
}
inheritPrototype(SubType, SuperType);
SubType.prototype.getSubTest = function () {
return this.test;
}; //子类2及其原型属性/方法
function SubType2(name) {
SuperType.call(this, name);
this.test2 = ['s1', 's2', 's3', 's4'];
}
inheritPrototype(SubType2, SuperType);
SubType2.prototype.getSubTest2 = function () {
return this.test2;
}; /* 以下为测试代码示例 */
var instance1 = new SubType(['yyy', 'Jack', 'Nick']);
var instance2 = new SubType2(['yyy2', 'Jack2', 'Nick2']);
console.log(instance1,instance2)
控制台输出:
标注:
①我们看这个寄生组合式继承的处理方式,传进来一个子类和父类,子类的原型 = 新对象(新对象的原型 = 父类的原型),所以就是子类原型下的原型 = 父类的原型
这就是我们所看到的上面控制台输出的结果了,父类的原型挂在子类原型下的原型下,这样为各个子类添加原型的时候就不会影响挂在上面的父类原型了。
但是,由于依旧是引用传递,所以这个子类原型下原型(继承自父类的原型)依旧是共享的。
图示:
为达上述目的,我这边直接将父类实例挂在子类原型上,也是可以的:
//寄生组合式继承
function inheritPrototype(subType, superType) {
subType.prototype =new superType();②
} //父类及其原型属性/方法
function SuperType(name) {
if(name){
this.name = name;
}
}
SuperType.prototype.getSuerperName = function () {
return this.name;
} //子类1及其原型属性/方法
function SubType(name) {
SuperType.call(this, name);
this.test = ['h1', 'h2', 'h3', 'h4'];
}
inheritPrototype(SubType, SuperType);
SubType.prototype.getSubTest = function () {
return this.test;
}; //子类2及其原型属性/方法
function SubType2(name) {
SuperType.call(this, name);
this.test2 = ['s1', 's2', 's3', 's4'];
}
inheritPrototype(SubType2, SuperType);
SubType2.prototype.getSubTest2 = function () {
return this.test2;
}; /* 以下为测试代码示例 */
var instance1 = new SubType(['yyy', 'Jack', 'Nick']);
var instance2 = new SubType2(['yyy2', 'Jack2', 'Nick2']);
console.log(instance1,instance2)
标注:
②这里挂载在子类原型下的原型的是一个父类的实例,值得注意的是,实例化一个父类实例是会自动调用父类构造器的,所以会将父类构造器以及父类原型一同挂载到子类原型下的原型下,不妨让我们把上述例子中的父类构造器if判断去掉看看控制台输出结果:
讲到这里你是不是觉得已经结束了???当然~~~没有!
上面说过:这个子类原型下原型(继承自父类的原型)依旧是共享的!
那么我后来做了个实验:
Ⅰ.父类原型属性值是基本数据类型:
//寄生组合式继承
function inheritPrototype(subType, superType) {
subType.prototype =new superType();
} //父类及其原型属性/方法
function SuperType(name) {
if(name){
this.name = name;
}
}
SuperType.prototype.getSuerperName = function () {
return this.name;
}
SuperType.prototype.age =
SuperType.prototype.console = function(){
this.age += ;
console.log(this.age)
};
//子类1及其原型属性/方法
function SubType(name) {
SuperType.call(this, name);
this.test = ['h1', 'h2', 'h3', 'h4'];
}
inheritPrototype(SubType, SuperType);
SubType.prototype.getSubTest = function () {
return this.test;
}; //子类2及其原型属性/方法
function SubType2(name) {
SuperType.call(this, name);
this.test2 = ['s1', 's2', 's3', 's4'];
}
inheritPrototype(SubType2, SuperType);
SubType2.prototype.getSubTest2 = function () {
return this.test2;
}; /* 以下为测试代码示例 */
var instance1 = new SubType(['yyy', 'Jack', 'Nick']);
var instance2 = new SubType2(['yyy2', 'Jack2', 'Nick2']);
instance1.console();
instance1.console();
instance1.console();
instance1.console();
instance1.console();
instance2.console();
控制台输出:
结果说明:父类原型下的age属性没有共享!
Ⅱ.父类原型属性值是非基本数据类型(例如:对象):
//寄生组合式继承
function inheritPrototype(subType, superType) {
subType.prototype =new superType();
} //父类及其原型属性/方法
function SuperType(name) {
if(name){
this.name = name;
}
}
SuperType.prototype.getSuerperName = function () {
return this.name;
}
SuperType.prototype.age = {
age:
}
SuperType.prototype.console = function(){
this.age.age += ;
console.log(this.age.age)
};
//子类1及其原型属性/方法
function SubType(name) {
SuperType.call(this, name);
this.test = ['h1', 'h2', 'h3', 'h4'];
}
inheritPrototype(SubType, SuperType);
SubType.prototype.getSubTest = function () {
return this.test;
}; //子类2及其原型属性/方法
function SubType2(name) {
SuperType.call(this, name);
this.test2 = ['s1', 's2', 's3', 's4'];
}
inheritPrototype(SubType2, SuperType);
SubType2.prototype.getSubTest2 = function () {
return this.test2;
}; /* 以下为测试代码示例 */
var instance1 = new SubType(['yyy', 'Jack', 'Nick']);
var instance2 = new SubType2(['yyy2', 'Jack2', 'Nick2']);
instance1.console();
instance1.console();
instance1.console();
instance1.console();
instance1.console();
instance2.console();
控制台输出:
结果说明:父类原型下的age属性共享!
综上所述:
原型上的基本数据类型属性是值传递(内存地址不共享);
原型上的非基本数据类型属性是引用传递(内存地址共享)。
js继承的实现(原型/链、函数伪装)的更多相关文章
- 【前端知识体系-JS相关】深入理解JavaScript原型(继承)和原型链
1. Javascript继承 1.1 原型链继承 function Parent() { this.name = 'zhangsan'; this.children = ['A', 'B', 'C' ...
- js继承之组合继承(结合原型链继承 和 借用构造函数继承)
在我的前两篇文章中,我们已经介绍了 js 中实现继承的两种模式:原型链继承和借用构造函数继承.这两种模式都存在各自的缺点,所以,我们考虑是否能将这二者结合到一起,从而发挥二者之长.即在继承过程中,既可 ...
- JS高阶---继承模式(原型链继承)
[前言] 之前已经介绍了对象创建的五种模式,下面看下继承模式 本节介绍下<原型链继承> [主体] 验证如下: 关键点: .
- 第20篇 js高级知识---深入原型链
前面把js作用域和词法分析都说了下,今天把原型链说下,写这个文章费了点时间,因为这个东西有点抽象,想用语言表达出来不是很容易,我想写的文章不是简单的是官方的API的copy,而是对自己的知识探索和总结 ...
- js继承中,原型属性的继承探究
最近研究了js的继承,看了幻天芒的文章http://www.cnblogs.com/humin/p/4556820.html#3947420,明白了最好是使用apply或call方法来实现继承. 已知 ...
- 面试题常考&必考之--js中的难点!!!原型链,原型(__proto__),原型对象(prototype)结合例子更易懂
1>首先,我们先将函数对象认识清楚: 补充snow的另一种写法: var snow =function(){}; 2>其次:就是原型对象 每当我们定义一个函数对象的时候,这个对象中就会包含 ...
- JS prototype chaining(原型链)整理中······
初学原型链整理 构造器(constructor).原型(prototype).实例(instance); 每一个构造器都有一个prototype对象,这个prototype对象有一个指针指向该构造器: ...
- 原来JS是这样的 - 原型链
上一篇提到属性描述符 [[Get]] 和 [[Put]] 以及提到了访问描述符 [[Prototype]],看它们的特性就会很容易的让人想到经典的面向对象风格体系中对类操作要做的事情,但带一些 int ...
- 关于JS面向对象中原型和原型链以及他们之间的关系及this的详解
一:原型和原型对象: 1.函数的原型prototype:函数才有prototype,prototype是一个对象,指向了当前构造函数的引用地址. 2.函数的原型对象__proto__:所有对象都有__ ...
- JS高级. 03 混入式继承/原型继承/经典继承、拓展内置对象、原型链、创建函数的方式、arguments、eval、静态成员、实例成员、instanceof/是否在同一个原型链
继承:当前对象没有的属性和方法,别人有,拿来给自己用,就是继承 1 混入式继承 var I={ }; var obj = { name: 'jack', age:18, sayGoodbye : fu ...
随机推荐
- Python中json的简单读写操作
Python中json的简单读写操作 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.它基于ECMAScript的一个子集. JSON采用完全独立于语言的 ...
- 不使用C库函数(Sprintf)将void* 指针转换为十六进制字符串
#include <stdio.h> #include <stdint.h> #include <stdlib.h> void hexDump(void *ptr, ...
- 报表开发神器!DevExpress Reporting v19.1:WinForms平台新功能
行业领先的.NET界面控件DevExpress Reporting全新发布了v19.1版本,本文主要为大家介绍WinForms.ASP.Net Core平台.Visual Studio报表设计器中发布 ...
- Pandas中DataFrame数据合并、连接(concat、merge、join)之concat
一.concat:沿着一条轴,将多个对象堆叠到一起 concat(objs, axis=0, join='outer', join_axes=None, ignore_index=False, key ...
- HDU-4513-完美队形2(Manacher变形)
链接: http://acm.hdu.edu.cn/showproblem.php?pid=4513 题意: 吉哥又想出了一个新的完美队形游戏! 假设有n个人按顺序站在他的面前,他们的身高分别是h[1 ...
- 题解 【POJ1157】LITTLE SHOP OF FLOWERS
先把题目意思说一下: 你有F束花,编号为\(1\)~\(F\)(\(1<=F<=100\)),\(V\)个花瓶,编号为\(1\) ~\(V\)(\(1<=V<=100\)), ...
- vue中点击按钮自动截图并下载图片
点击一个按钮,截取对应区域的界面,才对截取的界面进行裁切并下载 下载 html2canvas npm install html2canvas --save 引用 : import htm ...
- js文件下载
因为公司后端忙得无法给批量下载的api,就我们前端自己把多张图片打包成zip下载,zip打包我们用的是jszip,下面是js实现下载的三种方法: 利用HTML download 属性 var a = ...
- angular打包(一): electron
路由问题: 打包成electron前,需要修改 index.html <base href="/"> 成 <base href="./"> ...
- Codevs 1851 越狱 2008年湖南省队选拔赛
1851 越狱 2008年湖南省队选拔赛 时间限制: 10 s 空间限制: 128000 KB 题目等级 : 大师 Master 题目描述 Description 监狱有连续编号为1-N的N个房间,每 ...