视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
JavaScript变量声明和局部变量替代全局变量用法详解
2020-11-27 20:16:07 责编:小采
文档

定义在函数体外的都属于全局变量,定义在函数体内的属于局部变量。这里的定义是指通过var声明的。

JavaScript有隐含的全局概念,意味着你不声明的任何变量都会成为一个全局对象属性。例如:

function test(){
 myname = "huming";
 alert(myname);
}
test();  // "huming"
alert(myname);  //"huming"

两个结果是一样的,说明myname是一个全局变量。

那么,隐式全局变量和明确定义的全局变量有没有区别呢。答案肯定是有的,看下面的例子:

// 定义三个全局变量
var global_test1 = 1;
global_test2 = 2; // 反面教材
(function () {
 global_test3 = 3; // 反面教材
}());
// 试图删除
delete global_test1; // false
delete global_test2; // true
delete global_test3; // true
// 测试该删除
alert(typeof global_test1); // "number"
alert(typeof global_test2); // "undefined"
alert(typeof global_test3); // "undefined"

由上面的例子可以看出:在函数之外通过var定义的global_test1不能被删除,而没有经过var定义的global_test2和global_test3都被删除了(无论是否是在函数体内创建)。

总结来说,在函数体外通过var声明的全局变量不能被删除,而隐式全局变量是可以删除的。

这里要注意了:JavaScript有一种行为叫做“hoisting”(悬置/置顶解析/预解析)。

我们通过一个例子来说明:

var myname = "huming"; //声明全局变量
function test() {
 alert(myname);
 var myname = "local_huming";
 alert(myname);
}
test();

你猜两次alert的内容一致吗??显然不一致,一致还用说吗。实际输出是:"undefined", "local_huming"。

上面的例子等同于

var myname = "huming"; //声明全局变量
function test() {
  var myname;
  alert(maname);<br>  myname = "local_huming";
  alert(myname); // "local"
}
test();

第一次alert输出的myname并不是你以为的全局变量,而是和它在一个作用域(一个函数体)内的局部变量。虽然它还没有被声明,但被当作是声明了。这就是所谓的“hoisting”。

这样应该就明白了吧。当你在函数体中使用了一个变量,又在之后重新声明的话,就可能产生错误。

书写规范:

function test() {
 var a = 1,
 b = 2,
 c = a + b,
 d = {},
 e,
 f;
 // function body...
}

好处在于:

1、所有局部变量都定义在函数开始,方便查找;

2、防止变量在定义之前使用的逻辑错误。

如何替换?

在如何提高JavaScript性能这个问题上,大家最常听到的建议应该就是尽量使用局部变量(local variables)来代替全局变量(global variables)。在我从事Web开发工作的九年时间里,这条建议始终萦绕在我的耳边,并且从来没有质疑过,而这条建议的基础,则来自于 JavaScript处理作用域(scoping)和标识符解析(identifier resolution)的方法。

首先我们要明确,函数在JavaScript中具体表现为对象,创建一个函数的过程,其实也就是创建一个对象的过程。每个函数对象都有一个叫做 [[Scope]]的内部属性,这个内部属性包含创建函数时的作用域信息。实际上,[[Scope]]属性对应的是一个对象(Variable Objects)列表,列表中的对象是可以从函数内部访问的。比如说我们建立一个全局函数A,那么A的[[Scope]]内部属性中只包含一个全局对象(Global Object),而如果我们在A中创建一个新的函数B,那么B的[[Scope]]属性中就包含两个对象,函数A的Activation Object对象在前面,全局对象(Global Object)排在后面。

当一个函数被执行的时候,会自动创建一个可以执行的对象(Execution Object),并同时绑定一个作用域链(Scope Chain)。作用域链会通过下面两个步骤来建立,用于进行标识符解析。

首先将函数对象[[Scope]]内部属性中的对象,按顺序复制到作用域链中。

其次,在函数执行时,会创建一个新的Activation Object对象,这个对象中包含了this、参数(arguments)、局部变量(包括命名的参数)的定义,这个Activation Object对象会被置于作用域链的最前面。

在执行JavaScript代码的过程中,当遇到一个标识符,就会根据标识符的名称,在执行上下文(Execution Context)的作用域链中进行搜索。从作用域链的第一个对象(该函数的Activation Object对象)开始,如果没有找到,就搜索作用域链中的下一个对象,如此往复,直到找到了标识符的定义。如果在搜索完作用域中的最后一个对象,也就是全局对象(Global Object)以后也没有找到,则会抛出一个错误,提示用户该变量未定义(undefined)。这是在ECMA-262标准中描述的函数执行模型和标识符解析(Identifier Resolution)的过程,事实证明,大部分的JavaScript引擎确实也是这样实现的。需要注意的是,ECMA-262并没有强制要求采用这种结构,只是对这部分功能加以描述而已。

下载本文
显示全文
专题