原生Javascript实现继承方式及其优缺点详解

目录
  • 前言
  • 原型继承
    • 优点
  • 构造函数继承
    • 优点
    • 缺点
  • 组合式继承
  • 寄生式组合继承
  • 总结

前言

最近在复习javascript的一些基础知识,为开启新的征程做准备。所以开始记录一些自己学习的内容。

那今天的主题是 js的原生继承方式

废话少说,上代码!

首先是我们的父类代码。

在这里我们创建一个Person的类作为父类,它的构造函数需要2个参数name和age。

然后我们在它的原型上添加一个sayHi的方法。

//父类
function Person (name, age) {
    this.name = name || 'no name';
    this.age = age || 0;
}

Person.prototype.sayHi = function () {
    console.log('Hi, I\'m ' + this.name + ' and i\'m ' + this.age + ' years old!');
}

var p = new Person('A',20);
p.sayHi();//Hi, I'm A and i'm 20 years old!

原型继承

//原型继承
function Teacher(){
}

Teacher.prototype=new Person('B',22);
Teacher.prototype.constructor=Teacher;

var t = new Teacher();
t.sayHi();//Hi, I'm B and i'm 22 years old!
console.log(t instanceof Person);//true
console.log(t instanceof Teacher);//true

优点

从上面的代码来看,Teacher 的实例拥有了 Person 的属性和方法。并且实例对象既是 Person的实例也是 Teacher的实例。而且这种继承方式特别的简单。

缺点

我们可以很容易的就发现Teacher类的 name和 age是固定的,都是name=B和age=22,换句话说就是我们无法实现按照我们的意愿给父类的构造函数传参。并且一个我们不能给一个 Teacher 指定多个原型,也就是没法 多继承。然后我们看下下面这段代码:

var t1 = new Teacher();
var t2 = new Teacher();
Teacher.prototype.name = "C";
t1.sayHi();//Hi, I'm C and i'm 22 years old!
t2.sayHi();//Hi, I'm C and i'm 22 years old!

上面这段代码中我们可以看到当原型中的属性或者方法被改变时,所有的子类实例的属性和方法也会跟着被改变,也就是原型继承的另一个缺点:所有子类共享同一个原型对象

这里说到了原型,我很早之前也写过一个关于原型的随笔,不过可能也是有些模糊,现在的理解和当时有所不同,我会在后面重新写一篇关于原型的随笔。(写好了我会附上连接)

构造函数继承

//构造函数继承
function Teacher (name, age) {
    Person.call(this, name, age);
}

var t1 = new Teacher('B', 22);
var t2 = new Teacher('C', 30);
console.log(t1.name);//B
console.log(t2.name);//C
console.log(t1 instanceof Person);//false
console.log(t1 instanceof Teacher);//true
t1.sayHi();//TypeError: t1.sayHi is not a function
t2.sayHi();//TypeError: t1.sayHi is not a function

优点

相对于 原型继承 , 构造函数继承解决了所有的子类实例共享统一原型的问题,也可以给父类的构造函数传参,并且我们可以在子类的构造函数中调用多个父类的构造函数,实现所谓的多继承(这里的多继承是指子类通过call,apply等方法去调用父类的构造函数使其拥有父类的属性和方法,但是js中一个函数对象只存在一个 prototype,所以其实我们没法通过原型链的形式去体现出多继承)

缺点

上面的代码中我们可以看出创建的实例只是 子类的实例 并不是 父类的实例 ,不能直观的体现出继承,这种继承方式也无法继承父类的原型上的属性和方法。

组合式继承

//组合式继承
function Teacher (name, age) {
    Person.call(this, name, age);
}

Teacher.prototype = new Person();
Teacher.prototype.constructor = Teacher;

var t1 = new Teacher('B', 22);
var t2 = new Teacher('C', 30);
Teacher.prototype.name = "D";
console.log(t1.name);//B
console.log(t2.name);//C
t1.sayHi();//Hi, I'm B and i'm 22 years old!
t2.sayHi();//Hi, I'm C and i'm 30 years old!
console.log(t1 instanceof Person);//true
console.log(t1 instanceof Teacher);//true

组合式继承就是结合了原型继承和构造函数继承的优点,解决了两种方式存在的一些缺点。但是我们会发现每当我们去创建一个子类实例的时候都会去创建一个父类的实例,尽管父类实例不是同一个实例(内存地址不一样),但是他们其实属性和方法上完全一致,所以我们通过下面这种(寄生式组合继承)方式完善它,以避免不必要的实例构造。

寄生式组合继承

//寄生式组合继承
function Teacher (name, age) {
    Person.call(this, name, age);
}

Teacher.prototype = Object.create(Person.prototype);
Teacher.prototype.constructor = Teacher;

var t1 = new Teacher('B', 22);
var t2 = new Teacher('C', 30);
Teacher.prototype.name = "D";
console.log(t1.name);//B
console.log(t2.name);//C
t1.sayHi();//Hi, I'm B and i'm 22 years old!
t2.sayHi();//Hi, I'm C and i'm 30 years old!
console.log(t1 instanceof Person);//true
console.log(t1 instanceof Teacher);//true

上面的方式解决了我们没创建一个子类实例都去创建一个父类实例的问题,这也是最为常用的一种js的继承方式,如果我们通过Babel去把ES6中的class的继承转成ES5的代码,我们会发现就是用的寄生式组合继承。

总结

到此这篇关于原生Javascript实现继承方式及其优缺点的文章就介绍到这了,更多相关原生Javascript继承方式内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2021-07-18

不错的一篇关于javascript-prototype继承

1.最基本的用法 把ClassA的一个实例赋值给ClassB, ClassB就继承了ClassA的所有属性. 代码入下: function ClassA() { this.a='a'; } function ClassB() { this.b='b'; } ClassB.prototype=new ClassA(); var objB=new ClassB(); for(var p in objB)document.write(p+" "); [Ctrl+A 全选 注:如需引入外部Js

实现JavaScript中继承的三种方式

一.原型链继承 在原型链继承方面,JavaScript与java.c#等语言类似,仅允许单父类继承.prototype继承的基本方式如下: 复制代码 代码如下: function Parent(){} function Child(){} Child.prototype = new Parent(); 通过对象Child的prototype属性指向父对象Parent的实例,使Child对象实例能通过原型链访问到父对象构造所定义的属性.方法等. 构造通过原型链链接了父级对象,是否就意味着完成了对象

js中继承的几种用法总结(apply,call,prototype)

一,js中对象继承 js中有三种继承方式 1.js原型(prototype)实现继承 复制代码 代码如下: <SPAN style="BACKGROUND-COLOR: #ffffff"><SPAN style="FONT-SIZE: 18px"><html>  <body>  <script type="text/javascript">      function Person(na

深入了解javascript中的prototype与继承

通常来说,javascript中的对象就是一个指向prototype的指针和一个自身的属性列表.javascript创建对象时采用了写时复制的理念.只有构造器才具有prototype属性,原型链继承就是创建一个新的指针,指向构造器的prototype属性.prototype属性之所以特别,是因为javascript时读取属性时的遍历机制决定的.本质上它就是一个普通的指针. 构造器包括: 1.Object2.Function3.Array4.Date5.String 下面我们来举一些例子吧 复制代

js的2种继承方式详解

js中继承可以分为两种:对象冒充和原型链方式 一.对象冒充包括三种:临时属性方式.call()及apply()方式1.临时属性方式 复制代码 代码如下: function Person(name){     this.name = name;     this.say = function(){          alert('My name is '+this.name);     }}function F2E(name,id){     this.temp = Person;     thi

前端开发必须知道的JS之原型和继承

一. 原型与构造函数 Js所有的函数都有一个prototype属性,这个属性引用了一个对象,即原型对象,也简称原型.这个函数包括构造函数和普通函数,我们讲的更多是构造函数的原型,但是也不能否定普通函数也有原型.譬如普通函数: 复制代码 代码如下: function F(){ alert(F.prototype instanceof Object) //true; } 构造函数,也即构造对象.首先了解下通过构造函数实例化对象的过程. 复制代码 代码如下: function A(x){ this.x

Javascript基于对象三大特性(封装性、继承性、多态性)

Javascript基于对象的三大特征和C++,Java面向对象的三大特征一样,都是封装(encapsulation).继承(inheritance )和多态(polymorphism ).只不过实现的方式不同,其基本概念是差不多的.其实除三大特征之外,还有一个常见的特征叫做抽象(abstract),这也就是我们在一些书上有时候会看到面向对象四大特征的原因了. 一.封装性     封装就是把抽象出来的数据和对数据的操作封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员方法),

JS继承--原型链继承和类式继承

什么是继承啊?答:别人白给你的过程就叫继承. 为什么要用继承呢?答:捡现成的呗. 好吧,既然大家都想捡现成的,那就要学会怎么继承! 在了解之前,你需要先了解构造函数.对象.原型链等概念...... JS里常用的两种继承方式: 原型链继承(对象间的继承)类式继承(构造函数间的继承) 原型链继承: 复制代码 代码如下: //要继承的对象var parent={name : "baba" say : function(){ alert("I am baba");}} //

JavaScript是如何实现继承的(六种方式)

前言:大多OO语言都支持两种继承方式: 接口继承和实现继承 ,而ECMAScript中无法实现接口继承,ECMAScript只支持实现继承,而且其实现继承主要是依靠 原型链 来实现. 1.原型链 基本思想:利用原型让一个引用类型继承另外一个引用类型的属性和方法. 构造函数,原型,实例之间的关系:每个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针. 原型链实现继承例子: function SuperType() { this.property

js实现继承的5种方式

本文实例讲述了js实现继承的5种方式.分享给大家供大家参考,具体如下: 1.继承第一种方式:对象冒充 function Parent(username){ this.username = username; this.hello = function(){ alert(this.username); } } function Child(username,password){ //通过以下3行实现将Parent的属性和方法追加到Child中,从而实现继承 //第一步:this.method是作为

JS继承与闭包及JS实现继承的三种方式

前  言 在之前的两篇博客中,我们详细探讨了JavaScript OOP中的各种知识点(JS OOP基础与JS 中This指向详解 . 成员属性.静态属性.原型属性与JS原型链).今天我们来继续探讨剩余的内容吧. 我们都知道,面向对象的三大特征--封装.继承.多态. 封装无非就是属性和方法的私有化,所以我们JS中提供了私有属性和私有方法. 而JS中并没有多态,因此我们说JS是一门基于对象的语言,而非面向对象的语言. 那么,面向对象三大特征中,在JS中最重要的就是继承了. 一.继承的基本概念 使用

JS实现继承的几种常用方式示例

本文实例讲述了JS实现继承的几种常用方式.分享给大家供大家参考,具体如下: 1,原型链继承 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>原型链继承</title> </head> <body> <script> /** * 优点: * 实例是父类的实例也是子类的实例

JS实现面向对象继承的5种方式分析

本文实例讲述了JS实现面向对象继承的5种方式.分享给大家供大家参考,具体如下: js是门灵活的语言,实现一种功能往往有多种做法,ECMAScript没有明确的继承机制,而是通过模仿实现的,根据js语言的本身的特性,js实现继承有以下通用的几种方式 1. 使用对象冒充实现继承(该种实现方式可以实现多继承) 实现原理:让父类的构造函数成为子类的方法,然后调用该子类的方法,通过this关键字给所有的属性和方法赋值 function Parent(firstname) { this.fname=firs

详述JavaScript实现继承的几种方式(推荐)

ECMAScript只支持实现继承,而且其实现继承主要是依靠原型链来实现的. 原型链 原型链的基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法.每一个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的指针.如果:我们让原型对象A等于另一个类型B的实例,那么原型对象A就会有一个指针指向B的原型对象,相应的B的原型对象中保存着指向其构造函数的指针.假如B的原型对象又是另一个类型的实例,那么上述的关系依旧成立,如此层层递进,就构成了实例与原型的

Python tkinter模块中类继承的三种方式分析

本文实例讲述了Python tkinter模块中类继承的三种方式.分享给大家供大家参考,具体如下: tkinter class继承有三种方式. 提醒注意这几种继承的运行方式 一.继承 object 1.铺tk.Frame给parent: 说明: self.rootframe = tk.Frame(parent) tk.Label(self.rootframe) import tkinter as tk class MyApp(object): def __init__(self, parent)

理解js对象继承的N种模式

本文分享了js对象继承的N种模式,供大家参考. 一.原型链继承 function Person(){}; Person.prototype = { constructor: Person, name: "Oliver" }; function People(){}; People.prototype = new Person(); People.prototype.constructor = People; People.prototype.sayName = function(){

前端js文件合并的三种方式推荐

最近在思考前端js文件该如何合并,当然不包括不能合并文件,而是我们能合并的文件,想了想应该也只有三种方式. 三个方式如下: 1. 一个大文件,所有js合并成一个大文件,所有页面都引用它. 2. 各个页面大文件,各自页面合并生成自己所需js的大文件. 3. 合并多个共用大文件,根据实践情况合并出多个共用js文件,每个页面引用多个共用大文件. 另外在我看来,合并有两个目的: 1. 为了减少请求数. 2. 代码安全考虑(文件分得越多,越容易被人看清). PS:注意我说的不是压缩混淆,只是合并 1. 一

js字符串引用的两种方式(必看)

如下所示: function setName(obj) { obj.ok = "ccccccc"; } function aa() { var name = new String("hechangmin"); name.ok = "sdf"; //第一种方式 // String.prototype.ok = "aaaaa"; //第二种方式 alert(name.ok); // aaaaa setName(name); ale

详解node.js 下载图片的 2 种方式

具体代码如下所示: var request=require("request"); var fs=require("fs"); function download1(url,filename,fn){ request(url).pipe(fs.createWriteStream(filename).on("close",function(err,res){ if(err){ console.log(err); }else{ fn&&