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 ...
随机推荐
- luoguP1739 表达式括号匹配 x
P1739 表达式括号匹配 题目描述 假设一个表达式有英文字母(小写).运算符(+,—,*,/)和左右小(圆)括号构成,以“@”作为表达式的结束符.请编写一个程序检查表达式中的左右圆括号是否匹配,若匹 ...
- 数位dp入门(内容一样,新版格式)
顾名思义,数位dp,是一种用来计数的dp,就是把一个数字拆成一个一个数位去统计 如果现在给你一道题,需要你求在区间[l,r]内满足条件的解的个数,我们很容易想到去暴力枚举,但要是数据范围太大这种办法就 ...
- 卸载brew
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/uninst ...
- Python3中tornado高并发框架
1.单线程tornado.web:基础web框架模块tornado.ioloop:核心IO循环模块,高效的基础.封装了:1.asyncio 协程,异步处理2. epoll模型:水平触发(状态改变就询问 ...
- Spring Boot教程(三十七)整合MyBatis
Spring中整合MyBatis就不多说了,最近大量使用Spring Boot,因此整理一下Spring Boot中整合MyBatis的步骤.搜了一下Spring Boot整合MyBatis的文章,方 ...
- Codeforces 979 D. Kuro and GCD and XOR and SUM(异或和,01字典树)
Codeforces 979 D. Kuro and GCD and XOR and SUM 题目大意:有两种操作:①给一个数v,加入数组a中②给出三个数x,k,s:从当前数组a中找出一个数u满足 u ...
- linux命令---vi编辑器快速定位行数
linux命令—vi编辑器快速定位行数.删除当前行.和删除当前行后面的全部内容 1.vi 编辑器如何快速定位到第N行 命令方式下 :n http://bbs.chinaunix.net/thread- ...
- 库&插件&框架&工具
nodejs 入门 nodejs 入门教程,大家可以在 github 上提交错误 2016 年最好用的表单验证库 SMValidator.js 前端表单验证工具分享 浅谈前端线上部署与运维 说到前端部 ...
- flask 第八篇 实例化flask时的参数配置
Flask 是一个非常灵活且短小精干的web框架 , 那么灵活性从什么地方体现呢? 有一个神奇的东西叫 Flask配置 , 这个东西怎么用呢? 它能给我们带来怎么样的方便呢? 首先展示一下: from ...
- 阿里云服务器yum源更新问题
阿里云官网也给出了yum卸载重装以及修改源为阿里云内网的文档.步骤这里就不说了,可点击下面的链接进行参考 https://help.aliyun.com/knowledge_detail/670864 ...