Javascript中实现继承的方式
js中实现继承和传统的面向对象语言中有所不同:传统的面向对象语言的继承由类来实现,而在js中,是通过构造原型来实现的,原型与如下几个术语有关:
①构造函数:在构造函数内部拥有一个prototype属性,这个属性指向原型。在js中,构造函数和函数是属于一个概念范畴,都是引用类型,都可以实例化为对象。唯一不同的地方是使用上的不同,用new关键字来调用函数就能让这个函数变成一个构造函数,这一点很好理解,因为在像java、C#这中类C语言中构造函数就是和方法是同名的。而如果要实例化一个类,那么就用new来构造一个类的实例。prototype保存一个指针,指向这个构造函数的原型。实现继承后,原型中保存的引用类型(像Arrya、Function等)会被所有实例化后的对象所共享。原型是js中实现继承的重要方式,通过构造原型链,可以实现代码复用,后面会用代码来阐述这一点。
②原型:在原型内部保存一个constructor的属性,这个属性保存一个指针,指向构造函数。
③对象(实例):在实例内部保存一个[[prototype]]的指针,指向原型,通过这个[[prototype]],所有实例就可以访问原型中的所有属性和方法。
在继承上面需要注意的是原型中保存的基础类型的属性在继承后能被重写(由于js动态添加特性),这个是没有问题的(如string、number、bool等),问题出在引用类型的属性(和函数)上,如果在原型中定义了一个Array,那么在所有的实例中将共享这个属性,那么,在一个实例中对这个Array所做的任何改变都会体现到其他实例中,这就破坏了面向对象的程序设计中对封装的要求。所以,下面介绍一个如果去有效的建立继承,这里我记录两个最为理想的例子。
第一个:
'use strict';
function Person(name,age) {
this.name = name;
this.age = age;
this.address = ["a", "b"];
}
Person.prototype.getName = function() {
return this.name;
}
Person.prototype.favoriteColor = ["red", "blue"];
function Student(name, age) {
Person.call(this, name, age);
}
Student.prototype = new Person("parents", 60);
var student1 = new Student("xp", 20);
var student2 = new Student("xz", 30);
student2.address[2] = "c";
student1.favoriteColor[3] = "green";
console.log(student1.address);//['a','b']
console.log(student2.address);//['a','b','c']
console.log(student1.getName());//'xp'
console.log(student2.getName());//'xz'
console.log(student1.favoriteColor);//['red','green',blue']
console.log(student2.favoriteColor);//['red','green',blue']
上面这个代码说明了一个问题:使用构造原型链和借用构造函数这种组合的方式来实现继承的关键要点是在原型中声明函数,而在构造函数中声明属性,这种的意义在于原型中声明的属性或方法会被子类共享,如果属性是基础类型的那么不存在问题,但是如果声明的是引用类型的,那么在其中一个子类中对这个属性做出的修改会延续到其他的子类中,这样就破坏了封装性。我们要做的是在构造函数中声明属性,在原型中声明方法,这样就避免了上述出现的问题。
下面介绍第二种方式:
首先定义两个函数:
function object(o) {
function F() {
}
F.prototype = o.prototype;
return new F();
}
function inheritPrototype(subType, superType) {
var prototype = object(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}
一个函数是object(),这个函数接受一个构造函数的原型,object内部先定义一个构造函数,然后将传入的原型赋给这个定义好的构造函数的原型。
另一个函数是inheritPrototype,从字面意思上来讲就是继承原型。这个函数接受两个构造函数作为参数,在函数内部,使用object()函数返回一个实例化后的构造函数,接着将这个构造函数当作一个原型,并将为这个构造函数添加一个constructor的属性(constructor是原型中的属性,因为object函数返回的是一个构造函数,并没有constructor这个属性,所以要手动设置一个),并将constructor属性的值手动设置为传入的第一个参数(可以看出传入的第一个参数是作为子类,另一个是基类)。然后将第一个参数的原型设置为这个构造函数。这个函数基本解决了上面第一种所遇到的各种不好的情况:
"use strict";
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 BaseClass(name, age) {
this.name = name;
this.age = age;
this.favoriteColor = ["red", "green"]; //新添加的属性
} BaseClass.prototype.sayName = function () {
console.log(this.name);
};
function DerivedClass(name, age, job) {
this.job = job;
BaseClass.call(this, name, age);
}
inheritPrototype(DerivedClass, BaseClass);//替换DerivedClass.prototype=new BaseClass();
DerivedClass.prototype.sayHi = function () {
console.log(this.job);
}
var derived1 = new DerivedClass("bob", 16, "SoftWare Engineer");
derived1.favoriteColor.push("black");
console.log(derived1.favoriteColor);//["red","green","blacjk"]
derived1.sayName();//bob
var derived2 = new DerivedClass("loryn", 17, "doctor");
console.log(derived2.favoriteColor);//["red","green"]
首先解决的是构造函数的使用次数,这个优化后的只使用了一次构造函数:
BaseClass.call(this, name, age);
然后解决的是原型链的问题:
inheritPrototype(DerivedClass, BaseClass);//替换DerivedClass.prototype=new BaseClass();
在inheritPrototype函数中调用object函数,object函数直接将传入的原型赋给新创建的这个构造函数的原型。而在inheritPrototype函数中完成了两个构造函数之间的继承关系。
Javascript中实现继承的方式的更多相关文章
- JavaScript 中实现继承的方式(列举3种在前一章,我们曾经讲解过创建类的最好方式是用构造函数定义属性,用原型定义方法。)
第一种:对象冒充 function ClassA(sColor) { this.color = sColor; this.sayColor = function () { alert(this.col ...
- javascript中各种继承方式的优缺点
javascript中实现继承的方式有很多种,一般都是通过原型链和构造函数来实现.下面对各种实现方式进行分析,总结各自的优缺点. 一 原型继承 let Super = functioin(name = ...
- javascript中实现继承的几种方式
javascript中实现继承的几种方式 1.借用构造函数实现继承 function Parent1(){ this.name = "parent1" } function Chi ...
- JavaScript学习13 JavaScript中的继承
JavaScript学习13 JavaScript中的继承 继承第一种方式:对象冒充 <script type="text/javascript"> //继承第一种方式 ...
- 浅谈JavaScript中的继承
引言 在JavaScript中,实现继承的主要方式是通过原型链技术.这一篇文章我们就通过介绍JavaScript中实现继承的几种方式来慢慢领会JavaScript中继承实现的点点滴滴. 原型链介绍 原 ...
- 浅谈 JavaScript 中的继承模式
最近在读一本设计模式的书,书中的开头部分就讲了一下 JavaScript 中的继承,阅读之后写下了这篇博客作为笔记.毕竟好记性不如烂笔头. JavaScript 是一门面向对象的语言,但是 ES6 之 ...
- JavaScript中定义类的方式详解
本文实例讲述了JavaScript中定义类的方式.分享给大家供大家参考,具体如下: Javascript本身并不支持面向对象,它没有访问控制符,它没有定义类的关键字class,它没有支持继承的exte ...
- 深入理解JavaScript中的继承
1前言 继承是JavaScript中的重要概念,可以说要学好JavaScript,必须搞清楚JavaScript中的继承.我最开始是通过看视频听培训班的老师讲解的JavaScript中的继承,当时看的 ...
- JavaScript中的继承(原型链)
一.原型链 ECMAScript中将原型链作为实现继承的主要方法,基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法. 实例1: function SupType() { this.pro ...
随机推荐
- 不可不知 DDoS的攻击原理与防御方法
DoS攻击.DDoS攻击和DRDoS攻击相信大家已经早有耳闻了吧!DoS是Denial of Service的简写就是拒绝服务,而DDoS就是Distributed Denial of Service ...
- DataX的使用
简介 DataX 是阿里巴巴集团内被广泛使用的离线数据同步工具/平台,实现包括 MySQL.Oracle.HDFS.Hive.OceanBase.HBase.OTS.ODPS 等各种异构数据源之间高效 ...
- day14 Python百分号字符串拼接
拼接 # -*- coding:utf8 -*- #%s字符串,%d数字msg = '%s am %s my %s is %s'% (2,"charon","pluto& ...
- activemq5.14+zookeeper3.4.9实现高可用
一.activeMQ主要的几类部署方式比较1.默认的单机部署(kahadb)activeMQ的默认存储的单机方式,以本地kahadb文件的方式存储,所以性能指标完全依赖本地磁盘IO,不能提供高可用. ...
- Spring配置跨域请求
本文主要是Spring+SpringMVC+MyBatis/MyBatis Plus框架环境,包括SpringBoot同样适用. 1.编写拦截器 package com.interceptor; im ...
- win7/10下Qt Creator调试提示:The selected debugger may be inappropriate for the inferior的解决办法
在win7/10下Qt Creator调试提示:The selected debugger may be inappropriate for the inferior的错误提示内容如下图所示: 一般弹 ...
- 解决VC++6.0打开文件或添加文件到工程出错的问题
相信很多朋友在安装VC++6.0之后,发现无法使用打开文件命令.同时,打开了工程,却无法实现文件添加到工程的问题.一旦进行如此操作,便会出现应用程序错误,需要关闭应用程序.为此,不胜其烦.更有甚者,以 ...
- Photoshop 基础五 橡皮擦工具
橡皮擦工具,对图层消除 背景色橡皮擦工具,对图层,消除背景色 魔棒橡皮擦工具,对图层,颜色相近的消除
- Linux命令——cat/less/more的区别
cat命令:用于显示整个文件的内容,单独使用没有翻页功能,经常和 more 命令搭配使用,cat 命令还可以将数个文件合并成一个文件. more命令:让画面在显示满一页时暂停,此时可按空格健继续显示下 ...
- TerraGate SFS 4.5 版本 发布矢量数据使用的Cache数据如何再返回成shapefile文件
TerraGate SFS 4.5 版本 发布矢量数据使用的Cache数据如何再返回成shapefile文件? 两年前帮一个朋友解决过这个问题: 如果原来用4.5版本的时候,在网络环境下,为了提升调用 ...