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 ...
随机推荐
- CSS三栏布局
一.绝对定位 <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> < ...
- 在 Server 2008 企业版下, 安装 IIS 7 后,勾选好 请求筛选模块了。安装完毕后,"请求筛选"却不显示!
下载并安装 Administration Pack 到你的Windows Server 2008 上.可以通过下面的链接来下载Administration Pack. https://www.iis. ...
- Java基础之多线程篇(线程创建与终止、互斥、通信、本地变量)
线程创建与终止 线程创建 Thread类与Runnable接口的关系 public interface Runnable { public abstract void run(); } public ...
- C++ 函数模板&类模板
函数模板 #include <iostream> #include <string> using namespace std; template <typename T& ...
- php中DateTime、diff
手册地址:http://php.net/manual/en/dateinterval.format.php $january = new DateTime('2010-01-01'); $februa ...
- npm install报错 npm ERR! enoent ENOENT: no such file or directory
在npm之后出现如下错误: $ npm install npm WARN checkPermissions Missing write access to /Users/lucas/code/js/v ...
- Spring Cloud 入门教程(九): 路由网关zuul
在微服务架构中,需要几个关键的组件,服务注册与发现.服务消费.负载均衡.断路器.智能路由.配置管理等,由这几个组件可以组建一个简单的微服务架构.客户端的请求首先经过负载均衡(zuul.Ngnix),再 ...
- 蓝牙baseband概述
从蓝牙specispecification中看,基带协议主要分为8个部分来介绍的,分别是概述.物理信道.物理连接.逻辑传输.逻辑连接.封包.比特流的处理.组网行为.这里面会涉及到很多的概念,主要是在概 ...
- 微服务之Sping Cloud
版本说明 Finchley SR2 价值简要 微服务之间是松耦合,跨不同业务部门,提供非常充分的灵活性,加快项目开发完成效率,方便组件化独立可扩展性及复用. 微服务应用结构表现 组件简要 1. Eur ...
- BGFX 渲染引擎中着色器代码的调试方法
在实时渲染的图形开发中,着色器代码(Shader)越来越复杂,于是单纯的靠经验和不断试错的开发和调试方法早已不能满足实际需求.使用调试工具进行调试,成为开发中重要的方法.Bgfx 是一款跨平台.抽象封 ...