摘要:前言作为面向对象的弱类型语言,继承也是其非常强大的特性之一。那么如何在中实现继承呢让我们拭目以待。
前言
JS作为面向对象的弱类型语言,继承也是其非常强大的特性之一。那么如何在JS中实现继承呢?让我们拭目以待。
JS继承的实现方式既然要实现继承,那么首先我们得有一个父类,代码如下:
// 定义一个动物类 function Animal (name) { // 属性 this.name = name || "Animal"; // 实例方法 this.sleep = function(){ console.log(this.name + "正在睡觉!"); } } // 原型方法 Animal.prototype.eat = function(food) { console.log(this.name + "正在吃:" + food); };
1、原型链继承
核心: 将父类的实例作为子类的原型 function Cat(){ } Cat.prototype = new Animal(); Cat.prototype.name = "cat"; // Test Code var cat = new Cat(); console.log(cat.name); console.log(cat.eat("fish")); console.log(cat.sleep()); console.log(cat instanceof Animal); //true console.log(cat instanceof Cat); //true
特点:
非常纯粹的继承关系,实例是子类的实例,也是父类的实例
父类新增原型方法/原型属性,子类都能访问到
简单,易于实现
缺点:
可以在Cat构造函数中,为Cat实例增加实例属性。如果要新增原型属性和方法,则必须放在new Animal()这样的语句之后执行。
无法实现多继承
来自原型对象的引用属性是所有实例共享的。(详细请看附录代码:示例1)
创建子类实例时,无法向父类构造函数传参
推荐指数:★★(3、4两大致命缺陷)
2、构造继承
核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
function Cat(name){ Animal.call(this); this.name = name || "Tom"; } // Test Code var cat = new Cat(); console.log(cat.name); console.log(cat.sleep()); console.log(cat instanceof Animal); // false console.log(cat instanceof Cat); // true
优点:
解决了1中,子类实例共享父类引用属性的问题
创建子类实例时,可以向父类传递参数
可以实现多继承(call多个父类对象)
缺点:
实例并不是父类的实例,只是子类的实例
只能继承父类的实例属性和方法,不能继承原型属性/方法
无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
推荐指数:★★(缺点3)
3、实例继承
核心:为父类实例添加新特性,作为子类实例返回
function Cat(name){ var instance = new Animal(); instance.name = name || "Tom"; return instance; } // Test Code var cat = new Cat(); console.log(cat.name); console.log(cat.sleep()); console.log(cat instanceof Animal); // true console.log(cat instanceof Cat); // false
特点:
不限制调用方式,不管是new 子类()还是子类(),返回的对象具有相同的效果
缺点:
实例是父类的实例,不是子类的实例
不支持多继承
推荐指数:★★
4、拷贝继承
function Cat(name){ var animal = new Animal(); for(var p in animal){ Cat.prototype[p] = animal[p]; } Cat.prototype.name = name || "Tom"; } // Test Code var cat = new Cat(); console.log(cat.name); console.log(cat.sleep()); console.log(cat instanceof Animal); // false console.log(cat instanceof Cat); // true
特点:
支持多继承
缺点:
效率较低,内存占用高(因为要拷贝父类的属性)
无法获取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到)
推荐指数:★(缺点1)
5、组合继承
核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
function Cat(name){ Animal.call(this); this.name = name || "Tom"; } Cat.prototype = new Animal(); // 组合继承也是需要修复构造函数指向的。 Cat.prototype.constructor = Cat; // Test Code var cat = new Cat(); console.log(cat.name); console.log(cat.sleep()); console.log(cat instanceof Animal); // true console.log(cat instanceof Cat); // true
特点:
弥补了方式2的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法
既是子类的实例,也是父类的实例
不存在引用属性共享问题
可传参
函数可复用
缺点:
调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)
推荐指数:★★★★(仅仅多消耗了一点内存)
6、寄生组合继承
核心:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点
function Cat(name){ Animal.call(this); this.name = name || "Tom"; } (function(){ // 创建一个没有实例方法的类 var Super = function(){}; Super.prototype = Animal.prototype; //将实例作为子类的原型 Cat.prototype = new Super(); })(); // Test Code var cat = new Cat(); console.log(cat.name); console.log(cat.sleep()); console.log(cat instanceof Animal); // true console.log(cat instanceof Cat); //true 该实现没有修复constructor。 Cat.prototype.constructor = Cat; // 需要修复下构造函数
特点:
堪称完美
缺点:
实现较为复杂
推荐指数:★★★★(实现复杂,扣掉一颗星)
附录代码:示例一:
function Animal (name) { // 属性 this.name = name || "Animal"; // 实例方法 this.sleep = function(){ console.log(this.name + "正在睡觉!"); } //实例引用属性 this.features = []; } function Cat(name){ } Cat.prototype = new Animal(); var tom = new Cat("Tom"); var kissy = new Cat("Kissy"); console.log(tom.name); // "Animal" console.log(kissy.name); // "Animal" console.log(tom.features); // [] console.log(kissy.features); // [] tom.name = "Tom-New Name"; tom.features.push("eat"); //针对父类实例值类型成员的更改,不影响 console.log(tom.name); // "Tom-New Name" console.log(kissy.name); // "Animal" //针对父类实例引用类型成员的更改,会通过影响其他子类实例 console.log(tom.features); // ["eat"] console.log(kissy.features); // ["eat"]
原因分析:
关键点:属性查找过程
执行tom.features.push,首先找tom对象的实例属性(找不到),
那么去原型对象中找,也就是Animal的实例。发现有,那么就直接在这个对象的
features属性中插入值。
在console.log(kissy.features); 的时候。同上,kissy实例上没有,那么去原型上找。
刚好原型上有,就直接返回,但是注意,这个原型对象中features属性值已经变化了。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/107894.html
摘要:可以通过构造函数和原型的方式模拟实现类的功能。原型式继承与类式继承类式继承是在子类型构造函数的内部调用超类型的构造函数。寄生式继承这种继承方式是把原型式工厂模式结合起来,目的是为了封装创建的过程。 js继承的概念 js里常用的如下两种继承方式: 原型链继承(对象间的继承) 类式继承(构造函数间的继承) 由于js不像java那样是真正面向对象的语言,js是基于对象的,它没有类的概念。...
摘要:首先为了模拟类创建对象的功能搞出了构造函数。也就是名字肤色肤色这里是继承里的自有属性生命值这里继承的共有属性的方法攻击力兵种美国大兵攻击防御死亡肤色 JS面向对象之五 【继承】 我们已经准备了很多前置知识,包括 原型链,对象和对象之间的关系 this,对象和函数之间的关系 new, 用函数批量创建特定的对象的语法糖 JS面向对象的前世今生 我们说,面向对象是一种写代码的套路。因为如...
摘要:对象创建的三种方式字面量创建方式系统内置构造函数方式自定义构造函数构造函数原型实例之间的关系实例是由构造函数实例化创建的,每个函数在被创建的时候,都会默认有一个对象。 JS 对象创建的三种方式 //字面量创建方式 var person= { name:jack } //系统内置构造函数方式 var person= new Object(); person.name = jack; ...
阅读 2872·2021-11-24 09:39
阅读 1126·2021-11-02 14:38
阅读 4048·2021-09-10 11:26
阅读 2722·2021-08-25 09:40
阅读 2279·2019-08-30 15:54
阅读 457·2019-08-30 10:56
阅读 2710·2019-08-26 12:14
阅读 3194·2019-08-26 12:13