第九章:类和模块

本文详细解析JavaScript中原型和原型链的运作机制,包括__proto__与prototype的区别,类和构造函数如何通过原型实现继承,以及模块化的概念及其优势。理解这些核心概念有助于提升对JavaScript面向对象编程的理解。
摘要由CSDN通过智能技术生成

原型原型链是JavaScript中的难点也是重点,这里建议先阅读第六章中关于原型和原型链的部分再来阅读此处。

原型

        所有对象 (null除外)都拥有 __proto__ 属性,该属性是一个指针,指向该对象的构造方法(对象)的prototype对象( 隐式原型 )。
        所有函数 拥有 prototype 属性( 显式原型 仅限函数 ),这个属性是一个指针, 指向一个对象 ,这个对象的用途就是 包含所有实例共享的属性和方法 (该对象叫做 原型对象 )。原型对象也有一个属性,叫做constructor ,这个 属性包含了一个指针,指回原构造函数 Javascript中所有的对象都是Object的实例 ,并继承Object.prototype的属性和方法,也就是说,Object.prototype是所有对象的父亲。
        在对象创建时,就会有一些预定义的属性,其中 定义函数的时候,这个预定义属性就是 prototype, 这个prototype是一个普通的对象。而 定义普通的对象的时候,就会生成一个 __proto__ 这个__proto__指向的是这个对象的构造函数(也是对象)的prototype
 

 

         如上图所示,数组a在创建时生成一个__proto__对象,该对象指向构造函数Array的prototype对象。同时Array.prototype.__proto__指向Object.prototype,所以a.__proto__.__proto__也指向Object.prototype。

        如上图所示,创建构造函数B以及它的实例c,则c.__proto__指向构造函数B的原型prototype,所以两者是相等的。

原型链

        前面提到过__proto__是一个指针对象(属性),它指向的是构造它的对象的构造函数(对象)的prototype属性(对象)。几乎所有的javascript对象都有__proto__这样一个指 针包括Object。

        如上图所示,可以看到a = 1;实际相当于a = new Number(1);所有内建对象及Object的__proto__指向的都是一个匿名函数,可以认为它们其实也是function的一个实例。并且我们知道Number.prototype对象(即a.__proto__)是没有toString()方法的,但是a却可以调用该方法,这就是原型链的作用。

        如上图所示,a.__proto__.__proto__指向的是Object对象,而Object对象是有toString()方法的,所以对象a能调用toStirng()方法。a的原型链就是由Number.prototype和Object.prototype成,当a访问一个方法或属性时,它会先在自身查找,然后再沿原型链找,找到则调用,没找到报错

 

        如果给Number.prototype添加toString()方法,则a在查找到原型链的Number.prototype(即a.__proto__)上时发现了toString()方法,则会使用该方法而不是继续沿着原型链查找,并且几乎所有的对象的原型链的终点都是Object.prototype。

        __proto__是实现原型链的关键,而prototype则是原型链的组成。
        
        在网上找了张图:

类和构造函数

构造函数
        JavaScript语言是一门面向对象的语言,但 JS中并没有类的概念(ES6中添加了class关键字用来创建类,实际上也是原型实现的进一步封装,我们习惯性还叫类) 。于是JavaScript 采用构造函数的方式来模拟类的效果 ,即我们 通过函数来创建对象 。这也证明了函数在JavaScript中具有非常重要的地位。
        构造函数简单的讲,即定义出来专供new 式调用的函数,它与普通函数的定义其实并 无不同,构造函数首字母一般要大写
        

 

        从上图可以看到,创建了构造函数A,使用new关键字创建对象x和不使用new创建对象y,当不使用new时并不会显示地报错,但是构造的对象y并不是A的实例,而是变成了undefined,同时x中还包含名为a的属性,其值为123

        原型对象是类的唯一标识:当且仅当两个对象继承自同一个原型对象时,它们才属于同 一个类的实例

        如上图所示,可以得到对象x和对象y属于类Test,因为它们继承自同一个原型对象Test.prototype,同时给Test的原型对象添加add()方法,则对象x和y都可以调用该方法。

        任何javascript函数都可以用做构造函数,并且调用构造函数是需要用到一个prototype属性的。因此,每个javascript函数都自动拥有一个prototype属性,这个属性的值是一个对象,这个对象包含唯一一个不可枚举属性constructor,constructor属性的值则是一个函数对象

        从上图可以看出类Test的原型对象的constructor指向Test类本身。同时,可以看到构造函数的原型中存在预先定义好的constructor属性,这就意味着对象继承的constructor属性指代它们的构造函数,由于构造函数是类的"公共标识",因此这个constructor属性为对象提供了类。需要注意的是,如果重写预定义的Test.prototype对象,这个新定义的原型对象并不会包含constructor属性,因此需要手动添加。

模块

        模块通常是指编程语言所提供的代码组织机制,利用此机制可将程序拆解为独立且通用的代码单元。所谓模块化主要是解决代码分割、作用域隔离、模块之间的依赖管理以及发布到生产环境时的自动化打包与处理等多个方面

模块的优点:
  • 可维护性: 因为模块是独立的,一个设计良好的模块会让外面的代码对自己的依赖越少越好,这样自己就可以独立去更新和改进。
  • 命名空间: 在 JavaScript 里面,如果一个变量在最顶级的函数之外声明,它就直接变成全局可用。因此,常常不小心出现命名冲突的情况。使用模块化开发来封装变量,可以避免污染全局环境。
  • 重用代码: 有时候会喜欢从之前写过的项目中拷贝代码到新的项目,这没有问题,但是更好的方法是,通过模块引用的方式,来避免重复的代码库。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值