在Java和C#中,你可以简单的理解class是一个模子,对象就是被这个模子压出来的一批一批月饼。压个啥样,就得是个啥样,不能随便动,动一动就坏了。
而在Javascript中,没有模子,月饼被换成了面团,你可以捏成自己想要的样子。对象属性,方法不合适,可以修改。没有的方法,可以自己创建,发挥的自由度很大。

继承是指一个对象直接使用另一对象的属性和方法。

许多面向对象语言都支持两种继承方式:接口继承实现继承
接口继承:只继承方法签名;
实现继承:继承实际的方法。
Javascript是弱类型语言,大小写敏感。它不是原生支持继承,而是通过prototype去模拟的,所以是基于对象,不是面向对象。在ECMAScript中,无法实现接口继承,只支持实现继承,而实现继承主要是依靠原型链。每个函数都有call,apply方法,都有length,arguments,caller等属性。为什么每个函数都有?这肯定是"继承"的。函数由Function函数创建,因此继承Function.prototype中的方法。
ES5继承是先创建子类的实例对象this,再向this对象中添加父类的方法;
ES6继承是先创造父类的实例对象this,再用子类的构造函数修改this。

ECMAScript继承主要分两大类:引用对象继承实例对象继承
一、引用对象继承
子引用类型继承父引用类型,然后通过子引用类型生成的实例对象,具有父引用类型的特性。

// 定义一个父类,公用
function Parent(name,age){
// 属性
this.name = name || 'Parent';
this.age = age || 18;
// 实例方法
this.getAge = function(){
console.log('我的年龄是:'+ this.age);
}
}
// 原型方法
Parent.prototype.getName = function() {
console.log('我的名字是:'+ this.name);
};

1、原型链继承

function Child(){
}
Child.prototype = new Parent();//借助JavaScript中的委托机制,将父类的实例作为子类的原型。
Child.prototype.constructor = Child;
Child.prototype.name = 'child1'; //test
var child1 = new Child();
console.log(child1.name);
console.log(child1.getAge());
console.log(child1.getName());
console.log(child1 instanceof Parent); //true
console.log(child1 instanceof Child); //true

【特点】
A、父类新增原型方法/原型属性,子类都能访问到;
B、简单,易于实现;
C、非常纯粹的继承关系,实例是子类的实例,也是父类的实例。
【缺点】
A、来自原型对象的引用属性是所有实例共享的;
B、创建子类实例时,无法向父类构造函数传参;
C、要想为子类新增属性和方法,必须要在new Parent()后执行;
D、无法实现多继承。

2、构造继承(伪造对象,经典继承,对象冒充继承)

function Child(name){
Parent.call(this);//在子类内部调用父类,通过call,apply改变对象的this指向来继承。
//Parent.apply(this, arguments);
this.name = name || 'Camille';
} //test
var child1 = new Child();
console.log(child1.name);
console.log(child1.getAge());
console.log(child1 instanceof Parent); // false
console.log(child1 instanceof Child); // true

【特点】
A、避免了引用类型的属性被所有实例共享;
B、可以在Child中向Parent传参;
C、可以实现多继承,call多个父类对象。
【缺点】
A、实例并不是父类的实例,只是子类的实例;
B、只能继承父类的实例属性和方法,不能继承原型属性/方法;
C、无法实现函数复用,每个子类都有父类实例函数的副本,影响性能。

3、组合继承,次优(类式继承)

function Child(name,age){
Parent.call(this);//通过调用父类构造函数,继承父类的实例属性并保留传参的优点。
this.name = name || 'Camille';
this.age = age;
}
Child.prototype = new Parent();//通过将父类实例作为子类原型,实现函数复用及对原型属性和方法的继承。 Child.prototype.constructor = Child; //test
var child1 = new Child('HouYi',20);
console.log(child1.name);
console.log(child1.getAge());
console.log(child1 instanceof Parent); // true
console.log(child1 instanceof Child); // true

【特点】
A、可以继承实例属性/方法,也可以继承原型属性/方法;
B、既是子类的实例,也是父类的实例;
C、可传参;
D、函数可复用。
【缺点】
A、调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)。

4、寄生继承

function createObj(o){
var F = function(){};
F.prototype = o;
return new F();
}
function getObj(obj){
var cobj = createObj(obj);//创建新对象,通过寄生方式,砍掉父类的实例属性
cobj.setSex = function(){//增强对象
console.log('新对象的性别设置函数');
};
return cobj;//指定对象
} //test
var psl = new Parent();
var child1 = getObj(psl);
console.log(child1.name);
console.log(child1.setSex());
console.log(child1 instanceof Parent); // true

【特点】
A、给原生继承穿了个马甲,更像继承了;
B、createObj函数不是必须的,新对象是如何创建的并不重要,用createObj生的,new出来的,字面量现做的,都可以。
【缺点】
A、无法实现函数复用(没用到原型,当然不行)。

5、寄生组合继承,最优

function createObj(o){
var F = function(){};
F.prototype = o;
return new F();
}
function Child(name,age){
Parent.call(this);
this.name = name || 'Camille';
this.age = age;
}
var nobj = createObj(Parent.prototype);//通过寄生方式,砍掉父类的实例属性,创建对象
nobj.constructor = Child;//增强对象
Child.prototype = nobj;//指定对象 //test
var child1 = new Child('HouYi',27);
console.log(child1.name);
console.log(child1.getAge());
console.log(child1 instanceof Parent); // true
console.log(child1 instanceof Child); // true

【特点】
A、完美,在调用两次父类的构造的时候,不会初始化两次实例方法/属性;
B、对实例属性和原型属性分别进行了继承。
【缺点】
A、实现复杂。

二、实例对象继承
继承得到的对象都具有父实例对象的所有属性和方法,其实就是指对象的复制和克隆。
1、原型继承

// 定义一个父类,公用
function Parent(name,age){
// 属性
this.name = name || 'Parent';
this.age = age || 18;
// 实例方法
this.getAge = function(){
console.log('我的年龄是:'+ this.age);
}
}
// 原型方法
Parent.prototype.getName = function() {
console.log('我的名字是:'+ this.name);
};
/**
* 创建一个继承父类原型的实例对象,这也是ES5中Object.create()的简单实现。
* 在object()函数内部, 先创建一个临时性的构造函数;
* 然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例。
*/
function createObj(o){
var F = function(){};
F.prototype = o;
return new F();
} //test
var psl = new Parent();
var child1 = createObj(psl);//这是重点
child1.name = 'Camille';
console.log(child1.name);
console.log(child1.getAge());
console.log(child1 instanceof Parent); // true

ECMAScript5通过新增Object.create()方法规范化了原型式继承,这个方法接收两个参数:一个用作新对象原型的对象和一个作为新对象定义额外属性的对象。

var child1 = Object.create(psl);
console.log(child1.name);
console.log(child1.getAge());
console.log(child1 instanceof Parent); // true

【特点】
A、复制父实例对象;
【缺点】
A、原型引用属性会被所有实例共享;
B、无法实现代码复用(新对象是现取的,属性是现添的,都没用函数封装,怎么复用)。

2、拷贝继承

3、借用
有时可能恰好仅需要父对象其中的一两个方法,而不需要父对象的其他方法。这种情况下,可以使用call()和apply()来实现继承,区别就是传参的不同。
A、比如,借用Array中的slice方法

Array.prototype.slice.call();
[].prototype.slice.call();

B、比如,借用对象的方法

//如果getAge的this指向childObj
var parentObj = {
name:'Parent',
getAge:function(age){
console.log('我的年龄是:'+ age);
}
}
var childObj = {
name:'camille'
}
parentObj.getAge.call(childObj, '18'); //我的年龄是:18
//如果getAge的this指向全局对象
var parentObj = {
name:'Parent',
getAge:function(age){
console.log('我的年龄是:'+ age);
}
}
var pgetage = parentObj.getAge;
pgetage("27");//我的年龄是:27 var childObj = {
name:'Camille'
}
function bind(o,m){
return function(){
return m.apply(o,arguments);
};
}
var cgetage = bind(childObj,parentObj.getAge);
cgetage("30");//我的年龄是:30

4、绑定
ES5将bind()添加到Function.prototype,使得bind()像call()apply()一样易用。

// bind的内部实现
if(typeof Function.prototype.bind === 'undefined'){
Function.prototype.bind = function(thisArg){
var _this = this,
slice = Array.prototype.slice,
args = slice.call(arguments, 1);
return function(){
return _this.apply(thisArg, args.concat(slice.call(arguments)));
}
}
}
var parentObj = {
name:'Parent',
getAge:function(age){
console.log('我的年龄是:'+ age);
}
}
var childObj = {
name:'Camille'
}
var cgetage = parentObj.getAge.bind(childObj, '32');
cgetage();//我的年龄是:32

三、ES6中的继承

使用class,extends,super关键字来实现类继承。

//父类 Parentc
class Parentc {
// 父类的构造方法
constructor(name, age) {
this.name = name;
this.age = age;
// 共享变量
this.LEGS_NUM = 2;
}
// 父类的getName方法
getName() {
console.log(`我的名字是:${this.name}`);
} // 父类的getAge方法
getAge() {
console.log('我的年龄是:' + this.age);
} }
//子类 Childc
class Childc extends Parentc {
constructor(name, age, city) {
// 调用父类的构造方法
super(name, age);
this.city = city;
} // 覆盖父类的getName方法
getName() {
console.log(`我的名字是:${this.name},来自${this.city}`);
}
} // 实例化一个Childc的实例
let aperson = new Childc('Camille', 18, '西安');
aperson.getName(); // 我的名字是:Camille,来自西安
aperson.getAge(); // 我的年龄是18
console.log(aperson.LEGS_NUM); //
console.log(aperson instanceof Childc); //true
console.log(aperson instanceof Parentc); //true

Javascript实现对象的继承的更多相关文章

  1. 关于JavaScript中对象的继承实现的学习总结

    一.原型链 JavaScript 中原型链是实现继承的主要方法.其主要的思想是利用原型让一个引用类型继承另一个引用类型的属性和方法.实现原型链有一种基本模式,其代码如下. function Super ...

  2. Javascript之对象的继承

    继承是面向对象语言一个非常重要的部分.许多OOP语言都支持接口继承和实现继承两种方式.接口继承:继承方法签名:实现继承:继承实际的方法.在ECMAScript中函数是没有签名的,所以也就无法实现接口继 ...

  3. JavaScript教程——对象的继承

    面向对象编程很重要的一个方面,就是对象的继承.A 对象通过继承 B 对象,就能直接拥有 B 对象的所有属性和方法.这对于代码的复用是非常有用的. 大部分面向对象的编程语言,都是通过“类”(class) ...

  4. javascript中对象函数继承的概念

    什么是函数对象?这个对象既是通常意义上的对象,又可以加上括号直接执行的函数. 产生函数对象的方式有两种:1.通过function关键字产生:var fn = function(){};2.实例化Fun ...

  5. javascript实现对象的继承的方式

    在JavaScript将原型链作为实现继承的主要方法.基本原理是利用原型让一个subType引用superType的属性和方法 推荐链接 http://www.jb51.net/article/204 ...

  6. javascript中的对象之间继承关系

    相信每个学习过其他语言的同学再去学习JavaScript时就会感觉到诸多的不适应,这真是一个颠覆我们以前的编程思想的一门语言,先不要说它的各种数据类型以及表达式的不同了,最让我们头疼,恐怕就是面向对象 ...

  7. #JavaScript对象与继承

    JavaScript对象与继承 JavaScript是我在C语言之后接触的第二门编程语言,大一暑假的时候在图书馆找了一本中国人写的JavaScript程序设计来看.那个时候在编程方面几乎还是小白,再加 ...

  8. Javascript学习6 - 类、对象、继承

    原文:Javascript学习6 - 类.对象.继承 Javasciprt并不像C++一样支持真正的类,也不是用class关键字来定义类.Javascript定义类也是使用function关键字来完成 ...

  9. JavaScript 面向对象编程(三):非构造函数对象的继承

    JavaScript 面向对象编程(三):非构造函数对象的继承 一.什么是"非构造函数"的继承? 比如,现在有一个对象,叫做"中国人". var Chinese ...

随机推荐

  1. swift中闭包的学习。

    在swift中的闭包等同于OC中的block,它的用途就是在于可以包装一段代码在必要的时候进行调用. 闭包定义:  {(类型列表) -> 返回值 in // 多条swift语句 // 执行代码 ...

  2. CentOS和RedHat Linux的区别

    RHEL 在发行的时候,有两种方式.一种是二进制的发行方式,另外一种是源代码的发行方式. 无论是哪一种发行方式,你都可以免费获得(例如从网上下载),并再次发布.但如果你使用了他们的在线升级(包括补丁) ...

  3. WPF开发中的多线程的问题

    今天帮助同事做了一个WPF版的多线程demo,分享给大家. 要实现的问题就是非主线程thread1 去后台不停的取新数据,当有新数据的时候就会展示到前台. 我给他做的demo实现一个按秒的计数器,随着 ...

  4. ubuntu系统下Python虚拟环境的安装和使用

    ubuntu系统下Python虚拟环境的安装和使用        前言:进行python项目开发的时候,由于不同的项目需要使用不同的资源包和相关的配置,因此创建多个python虚拟环境,在虚拟环境下开 ...

  5. python安装虚拟环境pipenv

    python里如果多个多个项目同时引用包,就会涉及到包版本的问题,包不同版本管理的问题可以用虚拟环境来管理, 创建虚拟环境,这里是用官方推荐的pipenv来创建 先用pip命令行安装pipenv pi ...

  6. Tronado

    Tornado 是 FriendFeed 使用的可扩展的非阻塞式 web 服务器及其相关工具的开源版本.这个 Web 框架看起来有些像web.py 或者 Google 的 webapp,不过为了能有效 ...

  7. PHP实现字符串转义和还原

    首先大家可以简单了解下什么是转义字符?有什么用? 转义字符是一种特殊的字符常量.转义字符以反斜线"\"开头,后跟一个或几个字符.转义字符具有特定的含义,不同于字符原有的意义,故称“ ...

  8. Codeforces 519D A and B and Interesting Substrings(二维map+前缀和)

    题目链接:http://codeforces.com/problemset/problem/519/D 题目大意:给你一串字符串s仅由小写字母组成,并且对于'a'~'z'都给了一个值.求子串t满足t的 ...

  9. cf220b

    不知道为什么线段树区间更新专题里有这题.. 可以用莫队解,也可以直接开数组解 /* n个询问,m个元素 O(m*m):记录每个元素出现次数,筛掉出现次数小于数值的数 */ #include<io ...

  10. hdu2586 lca倍增法

    倍增法加了边的权值,bfs的时候顺便把每个点深度求出来即可 #include<iostream> #include<cstring> #include<cstdio> ...