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 ...
随机推荐
- Python之hmac模块的使用
hmac模块的作用: 用于验证信息的完整性. 1.hmac消息签名(默认使用MD5加算法) #!/usr/bin/env python # -*- coding: utf-8 -*- import h ...
- Python之threading模块的使用
作用:同一个进程空间并发运行多个操作,专业术语简称为:[多线程] 1.任务函数不带参数多线程 #!/usr/bin/env python # -*- coding: utf-8 -*- import ...
- java+根据多个url批量下载文件
1.基本流程 当我们想要下载网站上的某个资源时,我们会获取一个url,它是服务器定位资源的一个描述,下载的过程有如下几步: (1)客户端发起一个url请求,获取连接对象. (2)服务器解析url,并且 ...
- 「SNOI2017」礼物
题目链接:Click here Solution: 设\(f(x)\)代表第\(x\)个人送的礼物的数量,\(s(x)\)代表\(f(x)\)的前缀和,即: \[ f(x)=s(x-1)+x^k\\ ...
- C语言的预编译,程序员必须懂的知识!【预编译指令】【预编译过程】
由“源代码”到“可执行文件”的过程包括四个步骤:预编译.编译.汇编.链接.所以,首先就应该清楚的首要问题就是:预编译只是对程序的文本起作用,换句话说就是,预编译阶段仅仅对源代码的单词进行变换,而不是对 ...
- Vue_(组件通讯)动态组件结合keep-alive
keep-alive 传送门 <keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们.和 <transition> 相似,<keep-alive ...
- vue项目中主要文件的加载顺序(index.html、main.js、App.vue)
todo: https://www.cnblogs.com/xifengxiaoma/p/9493544.html https://www.cnblogs.com/stella1024/p/10563 ...
- SRGAN 学习心得
一.理论 关于SRGAN的的论文中文翻译网上一大堆,可以直接读网络模型(大概了解),关于loss的理解,然后就能跑代码 loss = mse + 对抗损失 + 感知损失 : https://bl ...
- Python中函数的使用
函数让代码的编写,阅读,测试和修改都变得更容易,提高代码的复用性,python中使用def关键字定义函数 如下代码在python3.7.3的Genay开发工具中编写测试通过. 一.简单函数定义及调用 ...
- php 获取域名
echo 'SERVER_NAME:'.$_SERVER['SERVER_NAME']; //获取当前域名(不含端口号) echo '<p>'; echo 'HTTP_HOST:'. ...