JavaScript几种继承方式的总结
1、原型链继承
直接将子类型的原型指向父类型的实例,即“子类型.prototype = new 父类型();”,实现方法如下:
//父类构造函数
function father(name) {
this.name = name;
this.colors = ['red'];
}
//父类添加属性
father.prototype.sayName = function() {
console.log(this.name);
}
//子类构造函数(声明)
function son(name,age) {
this.age = age;
}
// 实现继承的关键步骤,子类型的原型指向父类型的实例
son.prototype = new father();
// 在此处给子类型添加方法,一定要在实现继承之后,否则会在将指针指向父类型的实例,那么方法就为空(被父类型给覆盖了)
son.prototype.sayAge = function() {
console.log(this.age);
}
var tmp1 = new son('try',18);
console.log(tmp1.colors); //'red'
tmp1.sayAge();
var tmp2 = new son();
tmp2.colors.unshift('yellow');
console.log(tmp1.colors); //'yellow','red'
以上代码不难看出,有两个问题
- 原型链方式可以实现所有属性方法共享,但无法做到属性、方法独享
- 在创建子类型的实例时,不能向父类型的构造函数中传递参数。实际上是没有办法在不影响所有对象实例的情况下,给父类型的构造函数传递参数
基于此,实践中很少单独使用原型链。
2、借用构造函数
为了解决上面两个问题,出现了这种方法。在子类型的构造函数内调用父类型的构造函数,函数只不过是在特定环境中执行代码的对象,因此可以通过apply()或call()方法执行构造函数
除了能独享属性、方法外还能在子类构造函数中传递参数,但代码无法复用。
function father(name) {
this.name = name;
this.colors = ['red'];
this.test = function() {
console.log('test success');
}
}
father.prototype.sayName = function() {
console.log(this.name);
}
function son(name,age) {
//就只是加了下面这句
father.apply(this);
this.age = age;
}
var tmp1 = new son('try',18);
console.log(tmp1.colors); //'red'
tmp1.sayName(); //error!
tmp1.test(); //OK
var tmp2 = new son();
tmp2.colors.unshift('yellow');
console.log(tmp1.colors); // 'red'
不难发现,这种方法也有一个超大的问题。子类只能调用父类构造函数里的函数(如上面的test),对于其后面定义在其属性里的函数(如上面的sayName)就无法调用了。因此函数复用就无从谈起了。
3、组合继承
顾名思义,组合,就是将上两种方式组合在一起,从而发挥二者之长的一种继承模式。使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现堆实例属性的继承
分别是 son.prototype = new father();
apply(this);
但混合模式也并非没有缺点,在继承方法的时候实际已经继承了父类型的属性,只不过此时对于引用类型属于共享的;
因此在子类型的构造函数内再次调用父类型的构造函数从而继承了父类型的属性而去覆盖了原型中所继承的属性。
即调用了两次父类构造函数
function father(name) {
this.name = name;
this.colors = ['red'];
this.test = function() {
console.log('test success');
}
}
father.prototype.sayName = function() {
console.log(this.name);
}
function son(name,age) {
father.apply(this); //一次调用
this.age = age;
}
son.prototype = new father(); //两次调用
var tmp1 = new son('try',18);
console.log(tmp1.colors); //'red'
tmp1.sayName(); //error!
tmp1.test(); //OK
var tmp2 = new son();
tmp2.colors.unshift('yellow');
console.log(tmp1.colors); // 'red'
4、原型式继承(寄生和这个差不多 不单独介绍了,但寄生有增强对象的过程 即构造函数指向自己)
和原型链唯一的不一样在于,原型式继承 就是不用实例化父类了,直接实例化一个临时副本实现了相同的原型链继承。(即子类的原型指向父类副本的实例从而实现原型共享)。
原型式继承并没有使用严格意义上的构造函数,而是借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。
当然,这种方式,包含引用类型值的属性时钟会共享相应的值。
//原型式继承
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
var father = {
name: 'try',
colors: ['red']
}
//ECMAScript规范了原型式继承
var son1 = Object.create(father);
son1.name = 'rr';
son1.colors.push('yellow');
console.log(son1.colors);
var son2 = Object.create(father);
console.log(son2.colors);
5、寄生组合式继承
完美的继承方式,重点掌握。寄生+借用构造,解决了之前需要调用两个父类构造函数的问题。
所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。
不必为例指定子类型的原型而调用父类型的构造函数,我们所需要的无非是父类型原型的一个副本而已。
本质上,就是使用寄生式继承来的父类型的原型,然后再将结果指定给子类型的原型。
function object(fa) {
//实际上是实行了一次浅复制
function F() {}; //为了得到fa的原型创造出的临时对象
F.prototype = fa; //获得原型
return new F();
}
function inheritPrototype(son, father) {
var prototype = object(father.prototype); //创建对象
prototype.constructor = son; //构造函数指向要创建的 即增强对象
son.prototype = prototype; //原型继承
}
function father(name) {
this.name = name;
this.colors = ['red'];
}
father.prototype.sayName = function() {
console.log(this.name);
}
function son(name,age) {
father.call(this, name);
this.age = age;
}
inheritPrototype(son,father);
var tmp1 = new son('try',19);
tmp1.colors.unshift('yellow');
var tmp2 = new son('tt',10);
console.log(tmp2.colors); //'red'
tmp2.sayName(); //tt
属性继承代码是SuperType.call(this,name);
原型继承代码是inheritPrototype(son,father);
参考《JavaScript高级程序设计第3版》。
JavaScript几种继承方式的总结的更多相关文章
- JavaScript之四种继承方式讲解
在Javascript中,所有开发者定义的类都可以作为基类,但出于安全性考虑,本地类和宿主类不能作为基类,这样可以防止公用访问编译过的浏览器级的代码,因为这些代码可以被用于恶意攻击. 选定基类后,就可 ...
- JavaScript几种继承方式
我们先构建一个Person的构造函数 function Person(name) { this.name=name; } Person.prototype.sayHi=function () { co ...
- JavaScript五种继承方式详解
本文抄袭仅供学习http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html 一. 构造函数绑定 ...
- Javascript的四种继承方式
在Javascript中,所有开发者定义的类都可以作为基类,但出于安全性考虑,本地类和宿主类不能作为基类,这样可以防止公用访问编译过的浏览器级的代码,因为这些代码可以被用于恶意攻击. 选定基类后,就可 ...
- JavaScript的3种继承方式
JavaScript的继承方式有多种,这里列举3种,分别是原型继承.类继承以及混合继承. 1.原型继承 优点:既继承了父类的模板,又继承了父类的原型对象: 缺点:不是子类实例传参,而是需要通过父类实例 ...
- JavaScript 的对象继承方式,有几种写法?
JavaScript 的对象继承方式,有几种写法? 一.对象冒充 其原理如下:构造函数使用 this 关键字给所有属性和方法赋值(即采用类声明的构造函数方式).因为构造函数只是一个函数,所以可使 Pa ...
- 架构师JavaScript 的对象继承方式,有几种程序写法?
架构师JavaScript 的对象继承方式,有几种程序写法? 一.对象冒充 其原理如下:构造函数使用 this 关键字给所有属性和方法赋值(即采用类声明的构造函数方式).因为构造函数只是一个函数, ...
- 【设计模式+原型理解】第三章:javascript五种继承父类方式
[前言] 我们都知道,面向对象(类)的三大特征:封装.继承.多态 继承:子类继承父类的私有属性和公有方法 封装:把相同的代码写在一个函数中 多态: ->重载:JS严格意义上是没有重载,但可以通过 ...
- 都0202年了,你还不知道javascript有几种继承方式?
前言 当面试官问你:你了解js哪些继承方式?es6的class继承是如何实现的?你心中有很清晰的答案吗?如果没有的话,可以通过阅读本文,帮助你更深刻地理解js的所有继承方式. js ...
随机推荐
- SQLite使用(二)
sqlite3_exec虽然好用,但是一般不推荐直接使用. 常用的一组操作是: 关于sqlite3_exec和sqlite3_prepare_v2的使用场景,建议如下: 一个小DEMO: #inclu ...
- 最长公共子序列(Longest common subsequence)
问题描述: 给定两个序列 X=<x1, x2, ..., xm>, Y<y1, y2, ..., yn>,求X和Y长度最长的公共子序列.(子序列中的字符不要求连续) 这道题可以 ...
- ssrf爆破mysql
php ssrf 代码<?php $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $_GET['url']); #curl_setopt($ch ...
- P1750 出栈序列
这好像是普及难度的吧~ 感觉再次被小学生吊打了........ \(\color{Red}{----------------------=|(●'◡'●)|=我是手动的分割线------------- ...
- tp5中提示错误A non well formed numeric value encountered
问题因为自动完成时间导致的 原来我的数据库是这样的 修改成下面这样就好了
- CSS中的间距设置与盒子模型
CSS间距 内补白 外补白 盒子模型 CSS间距 很多时候我们为了美观,需要对内容进行留白设置,这时候就需要设置间距了. 内补白 设置元素的内间距 padding: 检索或设置对象四边的内部边距 pa ...
- Spring Cloud学习 之 Spring Cloud Ribbon(负载均衡器源码分析)
文章目录 AbstractLoadBalancer: BaseLoadBalancer: DynamicServerListLoadBalancer: ServerList: ServerListUp ...
- # C#学习笔记(一)——准备工作
C#学习笔记(一)--准备工作 目录 C#学习笔记(一)--准备工作 1.1 下载安装.NET框架 1.2 创建源代码 1.3 一些基本名称 1.4 简单的命名建议 1.1 下载安装.NET框架 .N ...
- 排序算法整理(Python实现)
目录 1. 冒泡排序 2. 选择排序 3. 插入排序 4. 归并排序 5. 快速排序 1. 冒泡排序 冒泡排序(Bubble Sort)是稳定排序,其基本思想是:遍历待排序列,依次两两比较,如果顺 ...
- .net core HttpClient 使用之掉坑解析(一)
一.前言 在我们开发当中经常需要向特定URL地址发送Http请求操作,在.net core 中对httpClient使用不当会造成灾难性的问题,这篇文章主要来分享.net core中通过IHttpCl ...