JavaScript中的原型prototype【2】

2024-05-11 04:18
文章标签 java script prototype 原型

本文主要是介绍JavaScript中的原型prototype【2】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

转载自:原型prototype[2]


前面说了介绍了原型,这一部分说说原型的一大作用:继承。在面向对象中,继承有两种方法,分别是接口继承和实现继承。但是在JavaScript中,函数不能只签名,所以JavaScript中只有实现继承。

JavaScript继承的基本思路就是让一个引用类型继承另一个引用类型的属性和方法。

  function SuperType(){this.property = true;};SuperType.prototype.getSuperValue = function(){return this.property;};function SubType(){this.subproperty = false;};//继承SuperTypeSubType.prototype = new SuperType();SubType.prototype.getSubValue = function(){return this.subproperty;};var instance = new SubType();console.log(instance.getSupterValue());   //print: true

首先我们先来看下上面函数之间的关系,如下图:

上面代码的原型关系图

1 由上面的图我们可以看到,继承其实就是通过改写原型实现的。我们将SuperType的一个实例赋值给SubType.prototype,使得SubType.prototoype含有指向SuperType.prototype的指针,这样就将原型之间的关系联系起来,构成了原型链(prootype chain)。
2 注意观察,可以发现在SubType.prototype原型中含有SuperType构造函数中的属性property。这是因为SubType.prototype是作为SuperType的一个实例,所以自然保存有SuperType实例中的属性,但是对于getSuperValue方法则还是保存在SuperType.prototype中。
3 SubType.prototype中含有的属性都是共享的,所以对于property属性,如果SubType有多个实例则是共享该属性值。
4 原型继承之后的原型搜索,还是和之前一样,先搜索实例对象的属性,然后在沿着原型链逐步向上搜索,如果最后还是没有找到则直接返回undefined。
5 在JavaScript中,所有的对象都是继承自Object,所以所有函数的默认原型都是Object的实例,因此默认原型都会有一个内部指针,指向Object.prototype。这也是为什么每一个函数都会有toString(), valueOf()等默认方法的基本原因。 6 对于使用原型继承的时候,不能使用字面量的形式来重写原型,如:

   function SuperType(){this.property = true;};SuperType.prototype.getSuperValue = function(){return this.property;};function SubType(){this.subproperty = false;};//继承SuperTypeSubType.prototype = new SuperType();//使用字面量添加新方法,相当于重写了原型,会上面一行代码无效SubType.prototype = {getSubValue: function(){return this.subproperty;},someOtherMethod: function(){return false;}};var instance = new SubType();console.log(instance.getSuperValue());   //print: error!找不到该方法

1 上面的代码首先将SubType.prototype赋值为SuperType的实例,这个时候是建立了继承关系;但是在后面又重写了SubType.prototype,这时候SubType.prototype变成了Object对象的一个实例,因此SubType和SuperType之间的关系已经被切断,两者之间没有了关系。

原型链存在的问题

  function SuperType(){this.color = ['red', 'blue', 'green'];};function SubType(){}//继承了SuperTypeSubType.prototype = new SuperType();var instanceFirst = new SubType();instanceFirst.color.push('black');console.log(instanceFirst.color);         //print: ['red', 'blue', 'green', 'black']var instanceSecond = new SubType();console.log(instanceSecond.color);        //print: ['red', 'blue', 'green', 'black']

首先还是来看下函数原型之间的关系,如下图:

上面代码的原型关系图

1 由上面的图可以看到,由于SubType.prototype是作为SuperType的实例,所以在SubType.prototype中保存有一份SuperType实例对象都具有的属性,即color属性。这就会导致SubType的所有实例都共享一份color;
2 又因为在原型中所有属性都是共享的,所以在SubType的实例中都能引用到color的属性值。因此我们可以看到在instanceFirst和instanceSecond都是共享同一个color数组,这也就是为什么instanceFirst改变了color的值之后,instanceSecond的color数组也跟着改变的原因。
3 原型链继承存在的问题:通过原型继承,原型实际上是变成另一个类型的实例,于是原先的实例属性也就顺理成章的变成了现在原型的属性。

原型链问题的解决方法

1 对于从SuperType构造函数继承下来的属性到原型中的问题,可以通过在SubType的构造函数中调用SuperType构造函数。可以通过call和apply来实现。

  function SuperType(){this.colors = ['red', 'blue', 'green'];};function SubType(){//继承SuperType//在this的作用域上调用SuperType构造函数SuperType.call(this);};var instanceFirst = new SubType();instanceFirst.colors.push('black');console.log(instanceFirst.colors);        //print: ['red', 'blue', 'green', 'black']var instanceSecond = new SubType();console.log(instanceSecond.colors);       //print: ['red', 'blue', 'green']

上面的这种方法通常称之为经典继承方法,但是该方法还是存在构造函数模式的缺点,即所有的方法都在构造函数中定义,因此函数的复用就无从谈起。因此很多情况下都是使用下面的一种方法:组合继承。

2 组合继承即在上面的方法中添加如原型链的形式。

  function SuperType(name){this.name = name;this.colors = ['red', 'blue', 'green'];};SuperType.prototype.sayName = function(){console.log(this.name);};function SubType(name, age){//继承SuperType属性SuperType.call(this, name);this.age = age;};SubType.prototype = new SuperType();SubType.prototype.sayAge(){console.log(this.age);};var instanceFirst = new SubType("Louis", 23);instanceFirst.colors.push('black');console.log(instanceFirst.colors);        //print: ['red', 'blue', 'green', 'black']console.log(instanceFirst.sayName());     //print: Louisconsole.log(instanceFirst.sayAge());      //print: 23var instanceSecond = new SubType("June", 21);console.log(instanceSecond.colors);       //print: ['red', 'blue', 'green']console.log(instanceSecond.sayName());    //print: Juneconsole.log(instanceSecond.sayAge());     //print:21

这种方法避免了原型链和经典继承的弊端,因此较为常用。

这篇关于JavaScript中的原型prototype【2】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/978481

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

一篇文章彻底搞懂macOS如何决定java环境

《一篇文章彻底搞懂macOS如何决定java环境》MacOS作为一个功能强大的操作系统,为开发者提供了丰富的开发工具和框架,下面:本文主要介绍macOS如何决定java环境的相关资料,文中通过代码... 目录方法一:使用 which命令方法二:使用 Java_home工具(Apple 官方推荐)那问题来了,

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三

Java中的.close()举例详解

《Java中的.close()举例详解》.close()方法只适用于通过window.open()打开的弹出窗口,对于浏览器的主窗口,如果没有得到用户允许是不能关闭的,:本文主要介绍Java中的.... 目录当你遇到以下三种情况时,一定要记得使用 .close():用法作用举例如何判断代码中的 input

Spring Gateway动态路由实现方案

《SpringGateway动态路由实现方案》本文主要介绍了SpringGateway动态路由实现方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随... 目录前沿何为路由RouteDefinitionRouteLocator工作流程动态路由实现尾巴前沿S