Javascript Closures (1)
作者:Dreamer 来源:Dreamer’s Blog 发布时间:2009-03-18 12:14:00
昨天又翻了下前段时间WD内部培训的幻灯片,发现了kejun推荐的一篇好文:Javascript Closures,看了之后受益匪浅。这篇文章不是简单地告诉你Javascipt 中的 closure 的好处和用法,而是从ECMAScript中的定义开始,帮助你从底层的机制和原理深入了解closure。了解了底层的机制之后你就不会单纯停留在使用的层面上,还会明白所有变量的初始化过程、作用域等,才算是真正掌握了closure。由于文章太长了,我就不翻译了,仅在此做一些笔记,仅供参考。另外,文中很多东西需要仔细琢磨一下,一下子总结出来对我来说比较吃力,所以决定分成几个部分来说,本文是第一部分。
1.简介
闭包(closure) 是 JS 最强大的特性之一,简单地说,闭包就是内部方法,即定义在方法内的方法,它们可以访问外部方法内的变量和参数,即使外部方法的执行已经终止。例如:
function example(arg1){
var localVar = 2;
return function inner(arg2){
return arg1+localVar + arg2;
}
}
要了解闭包的内部机制不是一件简单的事情,有许多准备工作要做。我们先来看一下有关对象的一些东西。
2.对象和对象属性
在 JS 中一切都是对象,包括 function 。对象可能会有一些属性,这些属性的值可能是另外一个对象,也可能是原生的数据类型:String, Number, Boolean, Null 或者 Undefined 。下面我们来看一下给对象属性赋值和读取对象属性的原理。
var o = new Object();
o.testNumber = 5;
上面的代码中我们先是创建了一个对象 o ,然后对其属性 testNumber 赋值。请注意,一开始的时候对象o是没有 testNumber 这个属性的,所以当赋值的时候 JS 会先检查,发现没有这个属性就先创建这个属性,然后再对其进行赋值。如果这个时候再执行下面的代码:
o.testNumber = 8;
JS 同样会先检查对象o ,发现它有testNumber这个属性之后,就不会再创建该属性,而是直接重新设置它的值。下面看一下读取对象属性:
o.testNumber = 8;
var val = o.testNumber;
读取对象属性的时候如果对象有这个属性就会返回它的值。很简单,不过有意思的地方在于它的内部机制 :) 首先我们先来了解一下原型链(prototype chain)的概念,所有的对象都会有一个原型(prototype),而它们的原型也是一个对象,这就是说它们的原型也可能有自己的原型,于是原型链就形成了。在JS中,Object 的默认原型是 null ,所以:
var o = new Object();
会创建一个原型为 Object.prototype 的对象 o,由于 Object.prototype 的prototype 为空 ,所以对象 o 的原型链就只有一个,就是最原始的 Object.prototype 。但是下面的代码就复杂一点:
function MyObject1(formalParameter){
this.testNumber = formalParameter;
}
function MyObject2(formalParameter){
this.testString = formalParameter;
}
MyObject2.prototype = new MyObject1( 8 );
var objectRef = new MyObject2( "String_Value" );
objectRef 所指向的 MyObject2 的实例有一个原型链,原型链中的第一个对象是 MyObject1 的prototype,而 MyObject1 的 prototype 的 prototype 是最原始的 Object.prototype ,而Object.prototype的prototype为空,所以原型链至此结束。
OK。下面我们来继续说对象属性的读取,其实读取一个对象属性的过程可能会涉及到该对象原型链中的所有对象。在上面那段代码中,我们来读取下面的属性:
var val = objectRef.testString;
由于objectRef 所指向的 MyObject2 的实例上有这个名为 testString 的属性,所以就会直接把这个属性的值”String_Value”赋给变量 val 。但是:
var val = objectRef.testNumber;
我们会发现 val 会被赋值为 8 ,可是objectRef 所指向的 MyObject2 的实例上没有这个名为 testNumber 的属性啊?为什么val 并不会被赋值为 undefined 呢?因为在对象自身上找不到该属性的时候,它就会检查对象的原型链,于是发现objectRef 指向的 MyObject2 的实例的prototype 是 MyObject1 的一个实例,而在这个实例中由一个值为 8 的 testNumber 属性,那么JS就会把这个值赋给 val 。我们看到,在 MyObject1 和 MyObject2 中都没有定义一个叫做 toString 的属性,但是当我们这样获取 objectRef 的 toString :
var val = objectRef.toString;
还是可以获取到。相信聪明的你已经想到了,因为 objectRef 的原型链的末端是 Object.prototype ,而在这个对象上面由一个名为 toString 的方法。最后:
var val = objectRef.madeUpProperty;
这次 val 终于是 undefined 了,因为检查完所有原型链都找不到名为 madeUpProperty 的属性,于是只好放弃。
综上,我们可以看到,在读取对象属性的时候,会先检查对象本身,然后依次检查它的原型链中的对象,在检查过程中发现符合要求的值就终止检查,然后返回。也就是说,它会返回第一个符合要求的值。了解了对象的赋值和读取过程之后,我们会发现一个有意思的事情,如果我们在上面代码的基础上执行:
objectRef.testNumber = 3;
由于objectRef 所指向的 MyObject2 的实例上没有 testNumber 这个属性,所以就会在该实例上创建一个名为 testNumber 的属性并赋值为 3 ,注意,这里并没有改变原型链上 MyObject1 的实例上的 testNumber 属性的值。而当我们再次获取 objectRef.testNumber 的值的时候,JS 会首先检查 objectRef 所指向的实例,然后就发现有这个属性,于是直接返回它的值:3 。这样一来,objectRef 原型链上的那个值为 8 的testNumber 属性就被隐藏了起来。
阅读下一篇:Javascript Closures (2)
猜你喜欢
- reshape(shape) : 不改变数组元素,返回一个shape形状的数组,原数组不变。是对每行元素进行处理resize(shape)
- 前言本专栏将非常细致的讲解相关与计算机视觉OpenCV的相关知识即操作,非常的简单易懂。本文主要讲解相关与计算机视觉的相关入门内容,关于图像
- 首先澄清一个应用场景问题。研究(1)中指出,对于结构复杂的网站,不少设计师们喜欢采用960固定宽度布局。但要注意的是,960并不是万能钥匙,
- 误区 #9: 数据库文件收缩不会影响性能错误! 收缩数据
- 1、控制"纵打"、 横打”和“页面的边距。 (1)<script defer> function SetPr
- 前言:多态的实现必须满足两个前提条件1.继承:多态一定是发生在子类和父类之间2.重写:多态子类重写了父类的方法记住这两点再结合代码示例有助于
- 1. 像素基本操作1.1 读取、修改像素可以通过[行,列]坐标来访问像素点数据,对于多通道数据,返回一个数组,包含所有通道的值,对于单通道数
- 1、查询时间区间日期列表,不会由于数据表数据影响select a.date from ( select curda
- 由于一些原因,视频录制要告一段落了。再写一篇关于cntk的文章分享出来吧。我也很想将这个事情进行下去。以后如果条件允许还会接着做。cntk2
- 在使用mysql时,有时需要查询出某个字段不重复的记录,虽然mysql提供有distinct这个关键字来过滤掉多余的重复记录只保留一条,但往
- 昨晚收到客服MM电话,一用户反馈数据库响应非常慢,手机收到load异常报警,登上主机后发现大量sql执行非常慢,有的执行时间超过了10s优化
- 前言弹幕可以给观众一种“实时互动”的错觉,虽然不同弹幕的发送时间有所区别,但是其只会在视频 * 定的一
- 一 时间元组1. 时间元组和时间戳的互化import time,datetime# 获取当前时间的时间元组t = time.localtim
- <!--#include file="Include/Conn.asp"--><%If(Request
- 工作中经常遇到阿拉伯数字转换称为中文数字或者大写金额,在网上搜了下,cn2an口碑较好,遂进行了一番学习。安装pip install cn2
- 本文实例讲述了MySQL触发器简单用法。分享给大家供大家参考,具体如下:mysql触发器和存储过程一样,是嵌入到mysql的一段程序,触发器
- window.showModalDialog() 使用方法:var returnValue = window.showModalDialog
- 1. 目标通过hadoop hive或spark等数据计算框架完成数据清洗后的数据在HDFS上爬虫和机器学习在Python中容易实现在Lin
- wp_mail() 函数用来发送邮件,类似于 PHP 的 mail() 函数。默认的发件人名称是 WordPress,发件人邮箱是类似 wo
- python中字典可以一键多值,也就是意味着一个键可以对应多个值。例:#encoding=utf-8print '中国'#字