js继承的方式及其优缺点
js继承方法
前因:ECMAScript不支持接口继承,只支持实现继承
一、原型链
概念:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针,让这个原型对象(子的原型)等于要继承的引用类型(父)的实例,由于引用类型(父)的实例包含一个指向(父)原型对象的内部指针,以此类推,层层递进,便构成实例与原型的链条,即原型链。
基本思想:利用原型让(子)引用类型继承(父)引用类型的属性和方法
基本模式的代码(参考红宝书):
/*原型链继承*/
//父的引用类型,拥有一个property属性
function SuperType() {
this.property = true;
} //为(父)引用类型的原型对象添加一个getSuperValue方法
//由于原型对象都包含一个指向构造函数的指针(constructor指向其引用类型的指针),
//故其方法可以取到property属性的值
SuperType.prototype.getSuperValue = function () {
return this.property; //返回property的值
} //子的引用类型,拥有一个subproperty属性
function SubType() {
this.subproperty = false;
} //继承父类型,即SuperType,让子引用类型的原型对象指向父类型的实例
SubType.prototype = new SuperType(); //为(子)引用类型的原型对象添加一个getSubValue方法
SubType.prototype.getSubValue = function () {
return this.subproperty; //返回subproperty的值
} //实例示范
var instance = new SubType(); //该对象同时拥有SubType和SuperType里面的所有方法和属性
alert(instance.getSuperValue); //true
/*原型链继承*/
默认原型:所有函数的默认原型都是Object实例,所以都会继承toString()和valueOf()等默认方法
确定原型和实例的关系方法:
如:alert(Object.prototype.isPrototyOf(instance)); //true
注意点:
原型链存在的问题:
二、借用构造函数继承(伪造对象或经典继承)
基本思想:在子类型构造函数的内部调用超类型构造函数(通过使用apply()和call()方法也可以在(将来)新创建的对象上执行构造函数);
代码(参考红宝书):
/*借助构造函数继承*/
// 父类型
function SuperType(){
this.colors = ["red","blue","green"];
} //子类型
function SubType(){
// 继承SuperType
SuperType.call(this);
} /*每执行new SubType()操作,都会在其实例环境下调用一次SuperType构造函数,
都会执行SuperType()函数中所定义的所有对象初始化代码,
因此,SubType的每个实例都会有自己的colors属性的副本*/
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black" var instance2 = new SubType();
alert(instance1.colors); //"red,blue,green" /*借助构造函数继承*/
存在问题:方法都在构造函数中定义,函数复用变得没有意义
补充:通过call()或者appla()调用父类型时,可以进行传参
三、组合继承(伪经典继承,原型链和借用构造函数技术组合)
基本思想:使用原型链实现对原型属性和方法的继承(主要想继承方法),而通过借用构造函数来实现对实例属性的继承(子类型的实例内部存在同名属性,从而对父类型的同名属性进行屏蔽);最后同时避免了原型链会被继承时会共享同一个父类型属性和借用构造函数的函数复用的缺陷
代码(参考红宝书):
/*组合继承*/
// 父类型
function SuperType(name){
this.name = name;
this.colors = ["red","blue","green"];
} //设置父类型的sayName方法
SuperType.prototype.sayName = function () {
alert(this.name);
} // 子类型
function SubType(name,age) {
// 借用构造函数的方法继承"属性",该代码会在新建的实例里面添加和父类型相同的属性
SuperType.call(this,name);
this.age = age;
} // 通过原型链继承方法
SubType.prototype = new SuperType();
SubType.prototype.costructor = SubType; //原型对象指向构造函数的指针(constructor)
SubType.prototype.sayAge = function () {
alert(this.age);
} // 实例
var instance1 = new SubType("Jack",29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Jack"
instance1.sayAge(); // var instance2 = new SubType("Greg",27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg"
instance2.sayAge(); //
/*组合继承*/
缺点:两次调用父类构造函数:(第一次是在创建子类原型的时候,第二次是在子类构造函数内部) ;从而造成子类继承父类的属性,一组在子类实例上,一组在子类原型上(即在子类原型上创建不必要的多余的属性)
基本思想:借助原型可以基于已有的的对象创建新对象,即如在一个函数Object内部,先创建一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型(实际上执行了浅复制),最后返回这个临时类型的新实例。
代码(参考红宝书):
/*原型式继承*/
function object(o){
function F(){}; //创建临时性的构造函数(对象)
F.prototype = o; //浅复制(只将地址地址进行赋值)
return new F();
} // 例子如下:
var person = {
name:"Nicholas",
friends:["Shelby","Court","Van"]
} //新建一个anothorPerson对象(空的),原型指向person
var anothorPerson = object(person);
anothorPerson.name = "Greg"; //直接在anothorPerson里面创建name(基本类型),再进行赋值
/*在anothorPerson查找friends(引用类型),
没有找到则顺着原型向上(即继承的父类型)找,直到找到,否则报undefind*/
anothorPerson.friends.push("Rob"); var yetAnothorPerson = object(person);
yetAnothorPerson.name = "Linda";
yetAnothorPerson.friends.push("Barbie");
// anothorPerson和yetAnothorPerson共享person的friends
alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
/*原型式继承*/
补充:ES5新增的Object.create()方法拥有和object()方法同样的效果,但是Object.create()方法接受两个参数:一个用作新对象原型的对象和(可选的)一个为新对象定义额外属性(会覆盖作新对象原型的对象上的同名属性)的对象
举例子如下:
// Object.create()
var person = {
name: "Nicholas",
friends:["Shelby","Court","Van"]
}; var anothorPerson = Object.create(person,{
name:{
value:"Greg"
}
}); alert(anothorPerson.name); //"Greg"
基本思想:创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象
代码(参考红宝书):
/*寄生式继承*/
function object(o){
function F(){}; //创建临时性的构造函数(对象)
F.prototype = o; //浅复制(只将地址地址进行赋值)
return new F();
} function createAnother(original){
var clone = object(original); //通过调用函数创建一个新对象
clone.sayHi = function () { //以某种方式来增强这个对象
alert("Hi");
};
return clone; //返回这个对象
} var person = {
name: "Nicholas",
friends:["Shelby","Court","Van"]
}; var anothorPerson = createAnother(person);
anothorPerson.sayHi(); //"Hi"
/*寄生式继承*/
缺点:不能做到函数复用导致降低效率
六、寄生组合式继承
概念:通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。
基本思想:没有必要为了指定子类型的原型而调用超类型的构造函数(函数复用),我们需要的是超类型原型的副本。本质:使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。
代码(参考红宝书):
/*寄生组合式继承*/
function object(o){
function F(){}; //创建临时性的构造函数(对象)
F.prototype = o; //浅复制(只将地址地址进行赋值)
return new F();
} function inheritPrototype(subType,superType){
//创建对象,使用寄生式继承来继承超类型的原型
var prototype = object(superType.prototype);
prototype.constructor = subType; //增强对象
subType.prototype = 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;
} inheritPrototype(SubType,SuperType); //使用寄生式继承来继承方法 SubType.prototype.sayAge = function(t) {
alert(this.age);
};
/*寄生组合式继承*/
js继承的方式及其优缺点的更多相关文章
- js继承的方式
深入理解继承的实现方式不仅仅有利于自己去造轮子,封装插件,更有利于我们去阅读一些框架的源码, 以下记录几种常见的继承方式 1. 原型链实现继承 function Father(){ this.name ...
- JS继承 实现方式
JS中继承方式的实现有多种方法,下面是比较推荐的方法,其它继承方式可做了解: function object(o) { function F() {} F.prototype = o; return ...
- js 继承的方式
//定义object的继承方法 Object.extend = function(destination, source) { for(property in source) { destinatio ...
- js面向对象编程(第2版)——js继承多种方式
附带书籍地址: js面向对象编程(第2版)
- 【编程题与分析题】Javascript 之继承的多种实现方式和优缺点总结
[!NOTE] 能熟练掌握每种继承方式的手写实现,并知道该继承实现方式的优缺点. 原型链继承 function Parent() { this.name = 'zhangsan'; this.chil ...
- JS继承的原理、方式和应用
概要: 一.继承的原理 二.继承的几种方式 三.继承的应用场景 什么是继承? 继承:子类可以使用父类的所有功能,并且对这些功能进行扩展.继承的过程,就是从一般到特殊的过程.要了解JS继承必须首先要了解 ...
- js各种继承方式和优缺点的介绍
js各种继承方式和优缺点的介绍 作者: default 参考网址2 写在前面 本文讲解JavaScript各种继承方式和优缺点. 注意: 跟<JavaScript深入之创建对象>一样,更像 ...
- JS实现继承的几种方式以及优缺点(转载)
前言 JS作为面向对象的弱类型语言,继承也是其非常强大的特性之一.那么如何在JS中实现继承呢?让我们拭目以待. JS继承的实现方式 既然要实现继承,那么首先我们得有一个父类,代码如下: // 定义一个 ...
- 三张图搞懂JavaScript的原型对象与原型链 / js继承,各种继承的优缺点(原型链继承,组合继承,寄生组合继承)
摘自:https://www.cnblogs.com/shuiyi/p/5305435.html 对于新人来说,JavaScript的原型是一个很让人头疼的事情,一来prototype容易与__pro ...
随机推荐
- electron-vue [Vue warn]: Failed to resolve directive: decorator
electron-vue引入ant-desigin-vue使用ant自定义指令 v-decorator报销 <a-form-item> <a-input v-decorator=&q ...
- 未AC
Count the Trees Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) ...
- 【原创】LUOGU P1808 单词分类
STL大法好!!! 使用sort()将string排序,map去重并统计即可. 最短代码如下: #include<bits/stdc++.h> using namespace std; s ...
- 32位linux安装chrome浏览器
首先你需要一个安装包,可以在CSDN上搜索google-chrome-stable_current_i386.deb. 然后在终端输入 sudo apt-get install gdebi 然后找到安 ...
- tail命令:显示文件结尾的内容
tail 命令和 head 命令正好相反,它用来查看文件末尾的数据,其基本格式如下:tail [选项] 文件名 选项 含义 -n K 这里的 K 指的是行数,该选项表示输出最后 K 行,在此基础上,如 ...
- TCP连接创建与终止
创建连接:三次握手一句话,”就是客户端发个syn,服务端发个syn+ack,客户端再回个ack“ 终止连接:四次挥手
- luogu4212
P4212 外太空旅行 题目描述 在人类的触角伸向银河系的边缘之际,普通人上太空旅行已经变得稀松平常了.某理科试验班有n个人,现在班主任要从中选出尽量多的人去参加一次太空旅行活动. 可是n名同学并不是 ...
- zookeeper系列(三)zookeeper的使用--开源客户端
作者:leesf 掌控之中,才会成功:掌控之外,注定失败, 原创博客地址:http://www.cnblogs.com/leesf456/ 奇文共欣赏,大家共同学习进步. 一.前言 上一篇博客已 ...
- 【python / mxnet / gluoncv / jupyter notebook】基于mxnet和gluoncv的图像内容识别
程序环境为高性能集群:CPU:Intel Xeon Gold 6140 Processor * 2(共36核心)内存:512GB RAMGPU:Tesla P100-PCIE-16GB * 2 In ...
- koa 项目实战(四)注册接口和调试工具(postman)
1.安装模块 npm install koa-bodyparser --save npm install bcryptjs --save 2.引入模块 根目录/app.js const bodyPar ...