[从jQuery看JavaScript]-变量与作用域链(Variable and Scope Chain)

news/2024/11/14 15:25:41

http://blog.csdn.net/natineprince/archive/2009/11/06/4775008.aspx

jQuery片段: 

view plain copy to clipboard print ?
  1. var   
  2.     // Will speed up references to window, and allows munging its name.  
  3.     window = this,  
  4.     // Will speed up references to undefined, and allows munging its name.  
  5.     undefined,  
  6.     // Map over jQuery in case of overwrite  
  7.     _jQuery = window.jQuery,  
  8.     // Map over the $ in case of overwrite  
  9.     _$ = window.$,  
  10.   
  11.     jQuery = window.jQuery = window.$ = function( selector, context ) {  
  12.         // The jQuery object is actually just the init constructor 'enhanced'  
  13.         return new jQuery.fn.init( selector, context );  
  14.     },  
  15.   
  16.     // A simple way to check for HTML strings or ID strings  
  17.     // (both of which we optimize for)  
  18.     quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,  
  19.     // Is it a simple selector  
  20.     isSimple = /^.[^:#\[\.,]*$/;  
var // Will speed up references to window, and allows munging its name. window = this, // Will speed up references to undefined, and allows munging its name. undefined, // Map over jQuery in case of overwrite _jQuery = window.jQuery, // Map over the $ in case of overwrite _$ = window.$, jQuery = window.jQuery = window.$ = function( selector, context ) { // The jQuery object is actually just the init constructor 'enhanced' return new jQuery.fn.init( selector, context ); }, // A simple way to check for HTML strings or ID strings // (both of which we optimize for) quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/, // Is it a simple selector isSimple = /^.[^:#\[\.,]*$/;

  从这一节开始,我们剥掉jQuery的外衣,看看里面藏着些什么。前一节中曾经提及,如果单看外层的匿名函数,不看里面的实现的话,这个实现肯 定不是闭包。但是,如果把jQuery的实现加上的话,这个肯定就是一种闭包应用。万丈高楼平地起,要了解闭包应用,就首先要了解它的基础。而这一节,我 们遇到的片段,就是这个基础的所在——变量。(虽然这个片段包含众多知识点,但请容许我一个个慢慢分说。)

  • 声明变量

  变量的英文名为variable,其前三个字母正是我们在JS声明变量的关键字——var。那么,我们先来看一下如何去声明一个变量:

view plain copy to clipboard print ?
  1. /* 
  2.  * 声明变量的格式为 
  3.  * var 变量名 初始化变量表达式列表(可选) 
  4.  */  
  5. var a=1, b, c="test", d=a+b;// 虽然b还没有初始化,但是声明是合法的  
  6. alert(a);// "1"  
  7. alert(b);// "undefined"  
  8. alert(c);// "test"  
  9. alert(d);// "NaN"  
  10. alert(e);// 这里将引发编译错误:"e"未定义  
  11. // 同样地,如果在初始化中使用未定义的变量,也会引发编译错误。  
/* * 声明变量的格式为 * var 变量名 初始化变量表达式列表(可选) */ var a=1, b, c="test", d=a+b;// 虽然b还没有初始化,但是声明是合法的 alert(a);// "1" alert(b);// "undefined" alert(c);// "test" alert(d);// "NaN" alert(e);// 这里将引发编译错误:"e"未定义 // 同样地,如果在初始化中使用未定义的变量,也会引发编译错误。

  如上例所示,声明变量需要使用var关键字,然后在空格后紧跟变量的名字。在声明变量的同时,我们也可以选择帮变量初始化。初始化的值可以是任何类型的值或表达式,但是,如果你尝试使用未定义的变量名来初始化,JS的编译器将会判定发生编译错误,并阻止程序继续往下运行。无 论你是否对声明的变量进行初始化,你都可以继续声明第二个变量而无须使用var关键字。你所需要的只是运算符“,”。(关于运算符将在稍后章节详细讨 论。)但当你没有对声明的变量进行初始化时,变量将会被赋予值“undefined”——undefined也是JS的固有类型之一。PS:JS中使用的运算符必须是半角的英文字符,甚至空格也一样。

  • 重复声明的变量?!

  当我们声明了一个变量,而又在后续的代码中再次对他进行声明,结果会怎么样呢?或许在很多其他语言中,这都会引起重复定义的错误,但在JS中,这完全是合法的。并且,由于JS是弱数据类型,所以变量能被赋予任何类型的值。请看以下例子:

view plain copy to clipboard print ?
  1. var a=1;  
  2. alert(typeof a); // "number"  
  3. var a;  
  4. alert(typeof a); // "number"  
  5. var a="1";  
  6. alert(typeof a); // "string"  
  7. a=new String("1");  
  8. alert(typeof a); // "object"  
var a=1; alert(typeof a); // "number" var a; alert(typeof a); // "number" var a="1"; alert(typeof a); // "string" a=new String("1"); alert(typeof a); // "object"

  看完上面的例子,你可能会产生两个疑问:

  a)为什么第二个a还是number?

  b)为什么第四个a是object?

  为了解答第一个问题,我们首先要了解声明一个变量到底是怎么运作的。而第二个问题,我们将他放到下一节再讨论。

  • var 变量声明的工作步骤

  当我们使用var关键字去声明变量的时候,JS解释器将会进行如下操作:

  1)预编译javascript代码块中所有非函数块内的var关键字;

  2)生成变量名标识并在其所在作用域分配空间;

  3)按代码顺序运行至第一个var关键字所在行;

  4)按变量声明列表表达式次序计算初始化表达式的值;

  5)每计算完一条初始化表达式,就将其计算结果赋予给对应的声明变量;

  6)继续运行后续代码至下一var关键字;

  7)重复4-7步到代码块结束;

  8)继续编译运行下一个代码块。

  PS:JS将以一个代码块,也就是一个script标签为单位去运行一段JS代码。

  正是因为var的工作方式,实际上程序执行时,解释器是根本看不到var关键字的。他执行的只是初始化表达式的赋值语句而已——所以问题a的答 案就是例子中的第三句实际上什么事也没有做。所以,你一点也不用为代码中会否出现重复定义的变量名而烦恼。你真正需要担心的是,初始化语句所产生的变量的 值的变化是否如你预期。除此之外,请不要尝试使用保留字作为变量名。这几乎在所有语言中都必须遵循的规范。

  另外,在函数块中声明变量的工作步骤也是类似的,但不同的是,他们是在函数运行时才创建的。

  • 没有var的变量声明?!

  很多朋友都应该有这个经验——“我们根本不需要使用var来声明变量也能直接赋值啊!”。这是因为JS解释器在遇到赋值表达式的时候,会先在作 用域链中寻找这个变量是否已经声明。如果这个变量没有声明,则隐式强制为其在全局(Global)作用域中声明,并将表达式的值赋予给该变量。

  但究竟为什么会这样呢?其实一切都源自于变量的获取规则和作用域链的化合作用外加赋值运算符的催化作用。

  • 作用域链

  每个运行时的上下文都有与其对应的一个作用域。而作用域链正是把这些作用域连接起来的桥梁。它的作用与程序寻找某一变量标识有关:

  1)JS解释器会按调用的顺序把作用域加进作用域链(像栈般早进入的作用域会在作用域链的底部);

  2)然后在程序寻找某一变量标识时进入作用域链中的第一个索引,并在其中寻找该变量标识;

  3)如果没有找到该标识,则前往下一个索引继续寻找;

  4)如果已经找到该标识,则将该标识及其值返回;

  5)当搜索到最后一个索引仍未能找到该标识,则在最后的索引上创建该标识,并使其值为null,最后返回该标识与值。

  PS:而上述的第5步发生的前提是该标识处于赋值运算符表达式左侧。

  因此,当你没有使用var声明变量而直接使用对该变量作初始化操作(简单赋值)时,JS会自动为你创建该空值标识,并让它可以顺利执行赋值语句。

  • 变量与作用域链

  从上面的描述,我们可以很轻易的看到变量与作用域链的关系。因为只要程序需要寻找变量,就必须通过作用域链。而前面所谈及的闭包问题正是由此而来的。回想一下我们前面的示例代码:

view plain copy to clipboard print ?
  1. var abc=function(y){  
  2. var x=y;// 这个是局部变量  
  3. return function(){  
  4.   alert(x++);// 就是这里调用了闭包特性中的一级函数局部变量的x,并对它进行操作  
  5.   alert(y--);// 引用的参数变量也是自由变量  
  6. }}(5);// 初始化  
  7. abc();// "5" "5"  
  8. abc();// "6" "4"  
  9. abc();// "7" "3"  
  10. alert(x);// 报错!“x”未定义!  
var abc=function(y){ var x=y;// 这个是局部变量 return function(){ alert(x++);// 就是这里调用了闭包特性中的一级函数局部变量的x,并对它进行操作 alert(y--);// 引用的参数变量也是自由变量 }}(5);// 初始化 abc();// "5" "5" abc();// "6" "4" abc();// "7" "3" alert(x);// 报错!“x”未定义!

  再联系一下我们上面说的,自己回答一下这个问题:究竟这个闭包中的变量声明和初始化是如何执行的?我们将在“闭包”的那一节再解答这个问题。

  最后说明一下,标题中的英文是为了大家能更好的查找英文文献用的——因为我的整理只会是中文版。^0^y

  • 关于保留关键字(补遗)

  在ECMA-262规范中,规定了如下关键字作为保留关键字:

break else new var
case finally return void
catch for switch while
continue function this with
default if throw
delete in try
do instanceof typeof

  这些关键字不能并声明为变量名。但其实还有如下预留关键字是作为未来扩展使用的:

abstract enum int short
boolean export interface static
byte extends long super
char final native synchronized
class float package throws
const goto private transient
debugger implements protected volatile
double import public

  但实测中,以上预留关键字,仅有部分浏览器对部分关键字有作保留。例如IE中的class。(实测浏览器为IE6,FF3,Chrome,Opera)。但为了以后的兼容性,以上预留关键字最好还是别用来作为变量标识为好。

  • 关于变量标识(补遗)

  变量的标识必须要符合以下要求:

  1)不能是保留关键字;

  2)必须以英文字母或下划线(_)或美元符号($)开头;

  3)后续字母除了可以为英文字母或下划线和美元符号外,还能是数字。

  此外,JS是区分大小写的,变量a和变量A是两个不同的变量标识。


http://www.niftyadmin.cn/n/1733117.html

相关文章

制作OLED文字

制作OLED文字 陈拓 2021/06/26-2021/06/29 1. 概述 以ASCII码顺序制作英文字模为无中文字库的LOED显示屏制作中文小字库 2. 文字取模软件 PCtoLCD2002。软件可以从下面的网址获取&#xff1a; 《用于LCD&#xff0c;OLED的文字取模工具软件》 https://download.csdn.net/d…

使用图片地图减少HTTP请求数量

前言 最近在看《高性能网站建设》&#xff0c;记录一下所学。 现在很多网站都是图片形式的导航&#xff0c;点击图片跳转到对应的链接。如果导航项目很多的话&#xff0c;图片的数量就会很多&#xff0c;每需要加载一张图片就会多一个HTTP请求。优化的方式之一就是使用图片地图…

FPGA实验五:信号发生器设计

目录 一、实验目的 二、设计要求 三、实验代码 1.代码原理分析 2.代码设计思路 3.IP核的设计与配置 四、实验结果及分析 1、引脚锁定 2、仿真波形及分析 &#xff08;1&#xff09;关于波形一些指标的介绍 &#xff08;2&#xff09;对波形转换功能的验证 &#xf…

Adafruit GFX Library字体规范

Adafruit GFX Library字体规范 陈拓 2021/06/29-2021/06/30 1. 概述 什么是Adafruit GFX Library 看看Adafruit官方网站的介绍。 https://learn.adafruit.com/adafruit-gfx-graphics-library/overview Arduino的Adafruit_GFX库为我们所有的LCD和OLED显示器&#xff08;Adafr…

[从jQuery看JavaScript]-数据类型和对象(Type and Object)(一)

http://blog.csdn.net/natineprince/archive/2009/11/08/4787689.aspxjQuery片段&#xff1a; view plaincopy to clipboardprint?var // Will speed up references to window, and allows munging its name. window this, // Will speed up references to u…

前端同学大福利,最全的面试题目整理

自己整理了一些关于前端这个行业面试题&#xff0c;好多都不会好难过&#xff0c;I NEED AV……. HTML&CSS 常用那几种浏览器测试&#xff1f;有哪些内核(Layout Engine)? (Q1) 浏览器&#xff1a;IE&#xff0c;Chrome&#xff0c;FireFox&#xff0c;Safari&#xff0c…

实现文字颜色渐变

前言 主要用到三个CSS属性&#xff1a; linear-gradient() 用来实现渐变的图像 background-clip 指定对象的背景图像向外裁剪的区域 text-fill-color 指定文字的填充颜色 实例 background: linear-gradient(to bottom,#fff 0%,#333 100%);background-clip: text;…

OLED非等宽字体格式

OLED非等宽字体格式 陈拓 2021/07/02-2021/07/07 1. 概述 用Arduino IDE进行ESP32和ESP8266开发时可以使用内置字体&#xff1a; 这3种字体都是非等宽的&#xff0c;在OLED这样的小屏幕上非等宽字体可以显示更多的字符&#xff0c;也更美观。 如果我们使用ESP-IRF进行ESP32开…