`

Javascript中的类和闭包

 
阅读更多

首先说类,要声明的是,至少到目前为止Javascript中没有类,所谓的“类”其实不是真正的类,它只是表现的像其他面向对象的语言中的类而已,它的本质是函数+原型对象(prototype)。有人说javascript也是面向对象的,只是它是prototype based,当然这只是概念上的区别,我不想讨论js是不是面向对象的,关键是想说明虽然javascript的类表现得很像其他语言中的类,但是内部的实现机理确不太一致,如果一味的把javascript中的类类比作其他语言中的类,有时候脑子会犯混。

先看一段简单的代码,一般教材上介绍如何新建一个类的时候都是这样的(当然还有更复杂的方法,不过本质上是一样的):

function MyClass(x) {
    this.x = x;
}
var obj = new MyClass('Hello class');
alert(obj.x);

毫无疑问,此时obj具有一个x属性,现在的值是 Hello class. 但是,obj到底是什么?MyClass仅仅是一个函数而已,我们称之为构造函数。在其他OO的语言中,构造函数是要放在class关键字内部的,也就是先要声明一个类。另外,函数体内的this又是什么?其他OO语言中,this的概念是很明确的,就是当前对象,因为它在构造函数执行之前已经声明了类,类的内部的一些字段都是已经定义好的。

先解释下,在javascript的函数中,this关键字表示的是调用该函数的作用域(scope),作用域的概念也不是太好理解,下面再解释。不过可以简单的认为它是调用函数的对象。再看MyClass函数,它内部的this是什么呢?

如果我们把代码改成:

var obj = MyClass('Hello class');

这是完全合乎语法的。如果这段代码是在浏览器中运行的,调试一下可以发现,this是window对象。而和obj没有任何关系,obj还是undefined,alert也不会有结果。原来的代码之所以可以工作,都是new关键字的功劳。new关键字把一个普通的函数变成了构造函数。也就是说,MyClass还是一个普通的函数,它之所以能构造出一个obj,基本上是new的功劳。当函数之前有new关键字的时候,javascript会创造一个匿名对象,并且把当前函数的作用域设置为这个匿名对象。然后在那个函数内部引用this的话就是引用的这个匿名对象,最后,即使这个函数没有return,它也会把这个匿名对象返回出去。那么obj自然就具有了x属性。

现在这个MyClass已经有点像一个类了。但是,这并不是new的工作的全部。Javascript同样可以方便的实现继承——依靠是prototype.prototype也是一个对象,毕竟除了原始类型,所有的东西都是对象,包括函数。更为重要的是,前面提到javascript是prototype based,它的含义就是在javascript中没有类的概念,类是不存在的,一个函数,它之所以表现的像类,就是靠的prototype. prototype可以有各种属性,也包括函数。上一段说的new在构造对象的过程中,在最终返回那个匿名对象之前,还会把那个函数的prototype中的属性一一复制给这个对象。这里的复制是复制的引用,而不是新建的一个对象,把内容复制过来,在其内部,相当于保留了一个构造它的函数的prototype的引用。有些教材含糊的说所有的“所有对象都有一个prototype属性”,这种说法是不确切的,虽然它内部确实有一个prototype属性,但是对外是不可见的。只有函数对象是有prototype属性的,函数对象的prototype默认有一个constructor属性。

看如下的代码:

function MyClass(x) {
    this.x = x;
}
var proObj = new MyClass('x');
InheritClass.prototype = proObj;
MyClass.prototype.protox = 'xxx';       
function InheritClass(y) {
    this.y = y;
}           
var obj = new InheritClass('Hello class');
MyClass.prototype.protox = 'changed';
proObj.x = 'Changed Too';
alert(obj.protox);
alert(obj.x);

 

输出的结果是changed和Changed Too。此代码说明了对象内部保留的是构造函数的prototype的引用,要注意的是,proObj中也是保留的它的构造函数的prototype的引用。如果把代码改成:

var obj = new InheritClass('Hello class');
        proObj.protox = 'I am winner';
        MyClass.prototype.protox = 'changed';
        proObj.x = 'Changed Too';
        alert(obj.protox);
        alert(obj.x);

输出的就是 I am winner 和 Changed Too了。事实上,这些prototype逐层引用,构成了一个prototype链。当读取一个对象的属性的时候,首先寻找自己定义的属性,如果没有,就逐层向内部隐含的prototype属性寻找。但是在写属性的时候,就会把它的引用覆盖掉,是不会影响prototype的值的。

 

再介绍闭包,首先说明下,这里的闭包(closure)和离散数学中关系的传递闭包中的不是一个概念,我曾以为他们之间有关联,后来仔细想想,似乎并无什么关联,恰好名字一样而已。先看定义:

 

Closure

 

A "closure" is an expression (typically a function) that can have free variables together with an environment that binds those variables (that "closes" the expression).

 

 

要完全理解闭包需要对Javascript函数的机理有比较透彻的理解,而这个机理有点复杂,并不是三言两语能讲清的,有兴趣的朋友可以看这里 Javascript clousures. 即使是这篇文章,也只是大概讲了下原理。大意就是任何一个函数调用都在一个运行上下文(Execution Context)中执行的,这个上下文中有一个作用域对象,其中包括了这个函数的局部变量、参数等。另外,如果一个函数是一个内部函数,它的作用域中含有它外部函数的作用域。在内部函数遇到一个变量名的时候,它是从内部的作用域找起,不断往外层的作用域找。因此,如果内部函数作为一个对象返回出外部函数的时候,即使外部函数已经执行完毕,但是由于其内部函数仍有引用指向它,内部函数不会被释放,因为内部函数有外部函数的作用域,因此外部函数的局部变量也不会被释放。这就构成了闭包。

分享到:
评论

相关推荐

    深入理解javascript原型和闭包

    深入理解javascript原型和闭包(01)——一切都是对象 深入理解javascript原型和闭包(02)——函数和对象的关系

    深度探讨javascript函数的原型链和闭包

    理解函数的原型链和闭包对于深入掌握JavaScript至关重要。 首先,让我们看看函数的定义方式。在JavaScript中,我们可以使用`function`关键字直接定义函数,如`function fn(a, b) {}`。此外,函数也可以通过赋值语句...

    JavaScript 匿名函数和闭包介绍

    在讨论JavaScript编程语言时,匿名函数和闭包是两个重要的概念,它们在函数式编程和模块化代码设计中扮演着核心角色。匿名函数是没有具体名称的函数,它们可以是独立的,也可以是表达式的一部分,通常用于定义临时...

    深入理解javascript原型和闭包.pdf

    闭包的优点是可以保存状态,缺点是可能会导致内存泄漏,因为闭包中保存了对外部函数变量的引用。 在文档中提到,通过原型链和作用域链,可以揭开JavaScript最神秘的面纱。原型链是指对象的原型对象还有自己的原型...

    理解_JavaScript_闭包

    本文结合 ECMA 262 规范详解了闭包的内部工作机制,让 JavaScript 编程人员对闭包的理解从“嵌套的函数”深入到“标识符解析、执行环境和作用域链”等等 JavaScript 对象背后的运行机制当中,真正领会到闭包的实质。

    JavaScript闭包函数

    闭包是ECMAScript (JavaScript)最强大的特性之一,但用好闭包的前提是必须理解闭包。闭包的创建相对容易,人们甚至会在不经意间创建闭包,但这些无意创建的闭包却存在潜在的危害,尤其是在比较常见的浏览器环境下...

    JavaScript闭包

    Javascript中有几个非常重要的语言特性——对象、原型继承、闭包。其中闭包 对于那些使用传统静态...本文将以例子入手来介绍Javascript闭包的语言特性,并结合一点 ECMAScript语言规范来使读者可以更深入的理解闭包。

    基于JavaScript编程语言之闭包技术在焦点轮播上的应用 (1).pdf

    本文通过实例代码和详细的解释,帮助读者深入了解JavaScript编程语言中的闭包技术和焦点轮播的实现方法。 知识点: 1. 闭包技术的定义和特点 2. 闭包技术在焦点轮播中的应用 3. 焦点轮播的实现方法,包括HTML结构...

    javascript 闭包

    闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。

    Javascript 闭包完整解释

    ### JavaScript闭包完整解释 #### 一、闭包的基本概念 **闭包**是一个非常重要的JavaScript概念,它指的是一个函数能够记住并访问其外部作用域中的变量的能力,即使该函数在其外部作用域之外被调用也是如此。具体...

    Javascript 中的类和闭包

    在Javascript中,闭包和类(构造函数)经常被一起使用。通过构造函数和原型链,我们能够创建具有继承关系的对象,而闭包则可以用来管理这些对象的私有状态。例如,在面向对象编程中,我们可以创建一个构造函数来定义...

    javascript闭包详解中文word版

    资源名称:javascript闭包详解 中文word版   内容简介: Javascript中有几个非常重要的语言特性——对象、原型继承、闭包。其中闭包 对于那些使用传统静态语言C/C 的程序员来说是一个新的...

    基于javascript 闭包基础分享

    闭包是JavaScript编程中的一个重要特性,它提供了创建私有变量、管理作用域和记忆化等强大功能,使得JavaScript编程更加灵活和高效。然而,由于闭包会延长变量的生命周期,因此需要合理管理内存使用,避免造成内存...

    javascript深入理解js闭包.docx

    在JavaScript中,闭包是一种特殊的函数,它能够访问并操作其定义时的作用域,即使该作用域在其执行时已经不存在。闭包的概念是基于JavaScript的词法作用域规则,即函数内部可以访问到外部的变量,但外部不能直接访问...

    基于JavaScript闭包的Web图片浏览控件的实现.pdf

    该控件使用JavaScript闭包原理,编写一个通用的Web图片浏览类,使用CSS控制显示外观,在网页中调用ShowPhotoLib类构造一个图片浏览器的对象,对图片进行控制显示。 4. 实现方法 实现ShowPhotoLib类,需要使用...

    浅析javascript语言中的函数闭包现象.pdf

    JavaScript中的函数闭包是一个重要的概念,它...总之,理解和掌握JavaScript中的闭包对于编写高效、安全的代码至关重要,它可以用于实现数据封装、模块化等高级功能,同时也需要注意避免由此引发的内存管理和逻辑错误。

    JavaScript闭包研究及典型应用.pdf

    JavaScript 闭包是一种强大的技术,能够在各种场景中发挥重要作用。本文将介绍 JavaScript 闭包的定义、使用场景和典型应用。 闭包函数的定义和使用场景 在 JavaScript 语言中,闭包函数是指在函数顶层可定义函数...

Global site tag (gtag.js) - Google Analytics