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采用完全独立于语言的 ...
- django 百度分页算法
效果如下: 脚本: 1. 脚本结构 2.pagination.py from django.utils.safestring import mark_safe class Page: ''' curr ...
- Python——变量的作用域
原创声明:本文系博主原创文章,转载及引用请注明出处. 1. 在编程语言中,变量都有一定的作用域,用来限定其生命周期,且不同类型的变量作用域不同. 在Python中解释器引用变量的顺序(优先级)为:当前 ...
- Python:面向对象编程3 定制类(有更新)
Python:面向对象编程3 定制类(有更新) ⚠️本文主要内容为对Data model相关知识点的提取学习记录.(内容来自文档和部分网页教程案例) ⚠️:这个连接指向<流畅的python&g ...
- idea的maven项目运行出错_java.io.FileNotFoundException: class path resource [spring/sprint-tx.xml] cannot be opened because it does not exist
前提:idea maven ssm 错误信息如下: 严重: Exception sending context initialized event to listener instance of ...
- VMware 虚拟机下载与安装
虚拟机下载 VMware官网地址:https://www.vmware.com/ 进行官网后,点击左边的下载图标,然后 根据操作系统选择合适的产品,在这里以Windows系统为例,点击转至下载,如下图 ...
- 多规格商品SKU 组件封装
前言 做电商项目呢,离不开多规格商品,SKU 也是弄了许久才搞出来,主要是多层级的联动关系,用ID和库存来判断是否是按钮禁止状态 下面就放下代码: 以封装的小程序为例: WXML: <view ...
- HDU-2594-Simpsons' Hidden Talents(kmp, 扩展kmp)
链接: https://vjudge.net/problem/HDU-2594#author=0 题意: 求S1的前缀和S2的后缀的<最大>匹配 思路: kmp方法: 将s1, s2首尾连 ...
- 35. ClustrixDB 减少device1大小
ClustrixDB中的device1文件用于所有数据库数据.撤消日志.临时表.binlog和ClustrixDB系统对象.ClustrixDB确保device1文件在集群的所有节点上大小相同.一旦得 ...
- flask框架(一):初入
1.装饰器回顾 # -*- coding: utf-8 -*- # @Author : Felix Wang # @time : 2018/7/3 17:10 import functools &qu ...