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()等默认方法

确定原型和实例的关系方法:

(1)通过使用instanceof()
如:alert(instance instanceOf Object); //true
(2)通过使用isPrototypeOf()

如:alert(Object.prototype.isPrototyOf(instance)); //true

注意点:

(1)给原型添加方法一定要放在替换原型的语句之后
(2)在通过原型链实现继承时,不能使用对象字面量创建原型方法

原型链存在的问题:

(1)子类型的所有实例都可以共享父类型的属性
(2)子类型的实例无法在不影响所有对象的情况下,给父类型的构造函数传递参数

二、借用构造函数继承(伪造对象或经典继承)

基本思想:在子类型构造函数的内部调用超类型构造函数(通过使用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继承的方式及其优缺点的更多相关文章

  1. js继承的方式

    深入理解继承的实现方式不仅仅有利于自己去造轮子,封装插件,更有利于我们去阅读一些框架的源码, 以下记录几种常见的继承方式 1. 原型链实现继承 function Father(){ this.name ...

  2. JS继承 实现方式

    JS中继承方式的实现有多种方法,下面是比较推荐的方法,其它继承方式可做了解: function object(o) { function F() {} F.prototype = o; return ...

  3. js 继承的方式

    //定义object的继承方法 Object.extend = function(destination, source) { for(property in source) { destinatio ...

  4. js面向对象编程(第2版)——js继承多种方式

    附带书籍地址: js面向对象编程(第2版)

  5. 【编程题与分析题】Javascript 之继承的多种实现方式和优缺点总结

    [!NOTE] 能熟练掌握每种继承方式的手写实现,并知道该继承实现方式的优缺点. 原型链继承 function Parent() { this.name = 'zhangsan'; this.chil ...

  6. JS继承的原理、方式和应用

    概要: 一.继承的原理 二.继承的几种方式 三.继承的应用场景 什么是继承? 继承:子类可以使用父类的所有功能,并且对这些功能进行扩展.继承的过程,就是从一般到特殊的过程.要了解JS继承必须首先要了解 ...

  7. js各种继承方式和优缺点的介绍

    js各种继承方式和优缺点的介绍 作者: default 参考网址2 写在前面 本文讲解JavaScript各种继承方式和优缺点. 注意: 跟<JavaScript深入之创建对象>一样,更像 ...

  8. JS实现继承的几种方式以及优缺点(转载)

    前言 JS作为面向对象的弱类型语言,继承也是其非常强大的特性之一.那么如何在JS中实现继承呢?让我们拭目以待. JS继承的实现方式 既然要实现继承,那么首先我们得有一个父类,代码如下: // 定义一个 ...

  9. 三张图搞懂JavaScript的原型对象与原型链 / js继承,各种继承的优缺点(原型链继承,组合继承,寄生组合继承)

    摘自:https://www.cnblogs.com/shuiyi/p/5305435.html 对于新人来说,JavaScript的原型是一个很让人头疼的事情,一来prototype容易与__pro ...

随机推荐

  1. hdu 6041 I Curse Myself

    题目: 点这里OvO http://acm.hdu.edu.cn/showproblem.php?pid=6041 2017 Multi-University Training Contest - T ...

  2. 一个3D正方体

    一个小例子,3D的正方体 <!DOCTYPE html> <html oncontextmenu=self.event.returnValue=false onselectstart ...

  3. input框输入手机号码分隔显示

    在input框输入手机号码时,自动加入空格按照3,4,4位显示,如: 实现方法如下: <label>手机号码</label><input type="text& ...

  4. [人物存档]【AI少女】【捏脸数据】金发西洋风格

    点击下载(城通网盘):AISChaF_20191103124436239.png

  5. VirtualBox:启动虚拟机后计算机死机

    造冰箱的大熊猫@cnblogs 2018/2/21 故障描述:Ubuntu 16.04升级Linux内核后,在VirtualBox中启动虚拟机发现Ubuntu死机,只能通过长按电源开关硬关机的方式关闭 ...

  6. 洛谷比赛 U5442 买(最长链)

    U5442 买 题目提供者bqsgwys 标签 树形结构 树的遍历 洛谷原创 题目背景 小E是个可爱的电路编码员. 题目描述 一天小E又要准备做电路了,他准备了一个电路板,上面有很多个电路元器件要安装 ...

  7. websocket 连接测试端口服务是否正常代码

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  8. vue使用子路由时,默认的子路由视图不显示问题

    解决办法是,将父级的name去掉.(大多数情况下是按name来跳转的,不过这样一改,调到父级就得用路径跳转了): 下面上一下路由的配置: { path: "/index", com ...

  9. BeautifulSoup4 提取数据爬虫用法详解

    Beautiful Soup 是一个HTML/XML 的解析器,主要用于解析和提取 HTML/XML 数据. 它基于 HTML DOM 的,会载入整个文档,解析整个 DOM树,因此时间和内存开销都会大 ...

  10. Android中关于回调概念的笔记

    一.回调函数 回调函数就是一个通过函数指针调用的函数.如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数.回调函数不是由该函数的实现方直接调 ...