知识大全 JavaScript的模块化:封装(闭包),继承(原型) 介绍

Posted 函数

篇首语:人生难得几回搏,此时不搏待何时。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 JavaScript的模块化:封装(闭包),继承(原型) 介绍相关的知识,希望对你有一定的参考价值。

JavaScript的模块化:封装(闭包),继承(原型) 介绍  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!

在复杂的逻辑下 JavaScript 需要被模块化 模块需要封装起来 只留下供外界调用的接口 闭包是 JavaScript 中实现模块封装的关键 也是很多初学者难以理解的要点  

  虽然 JavaScript 天生就是一副随随便便的样子 但是随着浏览器能够完成的事情越来越多 这门语言也也越来越经常地摆出正襟危坐的架势 在复杂的逻辑下 JavaScript 需要被模块化 模块需要封装起来 只留下供外界调用的接口 闭包是 JavaScript 中实现模块封装的关键 也是很多初学者难以理解的要点 最初 我也陷入迷惑之中 现在 我自信对这个概念已经有了比较深入的理解 为了便于理解 文中试图 封装一个比较简单的对象

  我们试图在页面上维护一个计数器对象 ticker 这个对象维护一个数值 n 随着用户的操作 我们可以增加一次计数(将数值 n 加上 ) 但不能减少 n 或直接改变 n 而且 我们需要时不时查询这个数值

  门户大开的 JSON 风格模块化

  一种门户大开的方式是

复制代码 代码如下: var ticker =     n:     tick:function()         this n++;     ;

  这种方式书写自然 而且确实有效 我们需要增加一次计数时 就调用 ticker tick() 方法 需要查询次数时 就访问 ticker n 变量 但是其缺点也是显而易见的 模块的使用者被允许自由地改变 n 比如调用 ticker n 或者 ticker n= 我们并没有对 ticker 进行封装 n 和 tick() 看上去是 ticker 的“成员” 但是它们的可访问性和 ticker 一样 都是全局性的(如果 ticker 是全局变量的话) 在封装性上 这种模块化的方式比下面这种更加可笑的方式 只好那么一点点(虽然对有些简单的应用来说 这一点点也足够了)

复制代码 代码如下: var ticker = ; var tickerN = ; var tickerTick = function()     tickerN++;

  tickerTick();

  值得注意的是 在 tick() 中 我访问的是 this n ——这并不是因为 n 是 ticker 的成员 而是因为调用 tick() 的是 ticker 事实上这里写成 ticker n 会更好 因为如果调用 tick() 的不是 ticker 而是其他什么东西 比如

复制代码 代码如下: var func = ticker tick; func();

  这时 调用 tick() 的其实是 window 而函数执行时会试图访问 window n 而出错

  事实上 这种“门户大开”型的模块化方式 往往用来组织 JSON 风格的数据 而不是程序 比如 我们可以将下面这个 JSON 对象传给 ticker 的某个函数 来确定 ticker 从 开始计数 每次递进

复制代码 代码如下: var config =     nStart:     step:

  作用域链和闭包 来看下面的代码 注意我们已经实现了传入 config 对 ticker 进行自定义

复制代码 代码如下: function ticker(config)     var n = config nStart;     function tick()         n += config step;     console log(ticker n); // >undefined

  你也许会疑惑 怎么 ticker 从对象变成了函数了?这是因为 JavaScript 中只有函数具有作用域 从函数体外无法访问函数内部的变量 ticker() 外访问 ticker n 获得 undefined 而 tick() 内访问 n 却没有问题 从 tick() 到 ticker() 再到全局 这就是 JavaScript 中的“作用域链”

  可是还有问题 那就是——怎么调用 tick() ? ticker() 的作用域将 tick() 也掩盖了起来 解决方法有两种

  • )将需要调用方法作为返回值 正如我们将递增 n 的方法作为 ticker() 的返回值 • )设定外层作用域的变量 正如我们在 ticker() 中设置 getN

复制代码 代码如下: var getN; function ticker(config)     var n = config nStart;     getN = function()         return n;     ;     return function()         n += config step;     ;

  var tick = ticker(nStart: step: ); tick(); console log(getN()); // >

  请看 这时 变量 n 就处在“闭包”之中 在 ticker() 外部无法直接访问它 但是却可以通过两个方法来观察或操纵它

  在本节第一段代码中 ticker() 方法执行之后 n 和 tick() 就被销毁了 直到下一次调用该函数时再创建 但是在第二段代码中 ticker() 执行之后 n 不会被销毁 因为 tick() 和 getN() 可能访问它或改变它 浏览器会负责维持n 我对“闭包”的理解就是 用以保证 n 这种处在函数作用域内 函数执行结束后仍需维持 可能被通过其他方式访问的变量 不被销毁的机制

  可是 我还是觉得不大对劲?如果我需要维持两个具有相同功能的对象 ticker 和 ticker 那该怎么办? ticker() 只有一个 总不能再写一遍吧?

  new 运算符与构造函数 如果通过 new 运算符调用一个函数 就会创建一个新的对象 并使用该对象调用这个函数 在我的理解中 下面的代码中 t 和 t 的构造过程是一样的

复制代码 代码如下: function myClass() var t = new myClass(); var t = ; t func = myClass; t func(); t func = undefined;

  t 和 t 都是新构造的对象 myClass() 就是构造函数了 类似的 ticker() 可以重新写成

复制代码 代码如下: function TICKER(config)     var n = config nStart;     this getN = function()         return n;     ;     this tick = function()         n += config step;    

  var ticker = new TICKER(nStart: step: ); ticker tick(); console log(ticker getN()); // > var ticker = new TICKER(nStart: step: ); ticker tick(); ticker tick(); console log(ticker getN()); // >

  习惯上 构造函数采用大写 注意 TICKER() 仍然是个函数 而不是个纯粹的对象(之所以说“纯粹” 是因为函数实际上也是对象 TICKER() 是函数对象) 闭包依旧有效 我们无法访问 ticker n

  原型 prototype 与继承 上面这个 TICKER() 还是有缺陷 那就是 ticker tick() 和 ticker tick() 是互相独立的!请看 每使用 new 运算符调用 TICKER() 就会生成一个新的对象并生成一个新的函数绑定在这个新的对象上 每构造一个新的对象 浏览器就要开辟一块空间 存储 tick() 本身和 tick() 中的变量 这不是我们所期望的 我们期望 ticker tick 和 ticker tick 指向同一个函数对象

  这就需要引入原型

  JavaScript 中 除了 Object 对象 其他对象都有一个 prototype 属性 这个属性指向另一个对象 这“另一个对象”依旧有其原型对象 并形成原型链 最终指向 Object 对象 在某个对象上调用某方法时 如果发现这个对象没有指定的方法 那就在原型链上一次查找这个方法 直到 Object 对象

  函数也是对象 因此函数也有原型对象 当一个函数被声明出来时(也就是当函数对象被定义出来时) 就会生成一个新的对象 这个对象的 prototype 属性指向 Object 对象 而且这个对象的 constructor 属性指向函数对象

  通过构造函数构造出的新对象 其原型指向构造函数的原型对象 所以我们可以在构造函数的原型对象上添加函数 这些函数就不是依赖于 ticker 或 ticker 而是依赖于 TICKER 了

  你也许会这样做

复制代码 代码如下: function TICKER(config)     var n = config nStart; TICKER prototype getN = function     // attention : invalid implementation     return n; ; TICKER prototype tick = function     // attention : invalid implementation     n += config step; ;

  请注意 这是无效的实现 因为原型对象的方法不能访问闭包中的内容 也就是变量 n TICK() 方法运行之后无法再访问到 n 浏览器会将 n 销毁 为了访问闭包中的内容 对象必须有一些简洁的依赖于实例的方法 来访问闭包中的内容 然后在其 prototype 上定义复杂的公有方法来实现逻辑 实际上 例子中的 tick() 方法就已经足够简洁了 我们还是把它放回到 TICKER 中吧 下面实现一个复杂些的方法 tickTimes() 它将允许调用者指定调用 tick() 的次数

复制代码 代码如下: function TICKER(config)     var n = config nStart;     this getN = function()         return n;     ;     this tick = function()         n += config step;     ; TICKER prototype tickTimes = function(n)     while(n> )         this tick();         n ;     ; var ticker = new TICKER(nStart: step: ); ticker tick(); console log(ticker getN()); // > var ticker = new TICKER(nStart: step: ); ticker tickTimes( ); console log(ticker getN()); // >

  这个 TICKER 就很好了 它封装了 n 从对象外部无法直接改变它 而复杂的函数 tickTimes() 被定义在原型上 这个函数通过调用实例的小函数来操作对象中的数据

  所以 为了维持对象的封装性 我的建议是 将对数据的操作解耦为尽可能小的单元函数 在构造函数中定义为依赖于实例的(很多地方也称之为“私有”的) 而将复杂的逻辑实现在原型上(即“公有”的)

  最后再说一些关于继承的话 实际上 当我们在原型上定义函数时 我们就已经用到了继承! JavaScript 中的继承比 C++ 中的更……呃……简单 或者说简陋 在 C++ 中 我们可能会定义一个 animal 类表示动物 然后再定义 bird 类继承 animal 类表示鸟类 但我想讨论的不是这样的继承(虽然这样的继承在 JavaScript 中也可以实现) 我想讨论的继承在 C++ 中将是 定义一个 animal 类 然后实例化了一个 myAnimal 对象 对 这在 C++ 里就是实例化 但在 JavaScript 中是作为继承来对待的

  JavaScript 并不支持类 浏览器只管当前有哪些对象 而不会额外费心思地去管 这些对象是什么 class 的 应该具有怎样的结构 在我们的例子中 TICKER() 是个函数对象 我们可以对其赋值(TICKER= ) 将其删掉(TICKER=undefined) 但是正因为当前有 ticker 和 ticker 两个对象是通过 new 运算符调用它而来的 TICKER() 就充当了构造函数的作用 而 TICKER prototype 对象 也就充当了类的作用

cha138/Article/program/Java/JSP/201311/20429

相关参考

知识大全 基于javascript 闭包基础分享

  如果对作用域函数为独立的对象这样的基本概念理解较好的话理解闭包的概念并在实际的编程实践中应用则颇有水到渠成之感在DOM的事件处理方面大多数程序员甚至自己已经在使用闭包了而不自知在这种情况下对于浏览

知识大全 javascript闭包的高级使用方法实例

这篇文章介绍了javascript闭包的高级使用方法实例有需要的朋友可以参考一下   扩展Code:复制代码代码如下:varblogModule=(function(my) myA

知识大全 JavaScript自执行闭包的小例子

JavaScript自执行闭包的小例子  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!JQuery是

知识大全 JavaScript模块编程之写法

JavaScript模块编程之写法  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  随着网站逐渐变

知识大全 javascript继承机制

  澄清在先Java和Javascript是雷锋和雷峰塔的关系Javascript原名Mocha当时还叫做LiveScript创造者是BrendanEich现任Mozilla公司首席技术官  年历史上

知识大全 Javascript 继承实现方式

Javascript继承实现方式  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  面向对象与基于对

知识大全 从内部类继承

  由于内部类构建器必须同封装类对象的一个句柄联系到一起所以从一个内部类继承的时候情况会稍微变得有些复杂这儿的问题是封装类的秘密句柄必须获得初始化而且在衍生类中不再有一个默认的对象可以连接解决这个问题

知识大全 面向对象三大特性之封装

  java学习已经到了最核心的章节面向对象的三大特性封装继承和多态接下来会写多篇博文(数目不定请关注近期博客吧!午饭们!)来总结这里面的每一个细节小可很希望每一篇文章的阐述会给你带来一点启发小可也是

知识大全 JavaScript文件尺寸验证

PHP典型模块与项目实战大全:JavaScript文件尺寸验证  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来

知识大全 面向对象的设计法确保软件的健壮性

  软件设计的质量在很大程度上受到系统封装关系的影响模块间松耦合而模块内部又保持高度一致性是高质量设计软件的关键之一所以评定软件设计的水平的手段之一就是考察它的模块间的关系对系统的可重用性和可维护性水