js全局变量与局部变量 预解析与作用域链详解

局部变量和全局变量同名时,会隐藏这个全局变量;

4.变量

关键字: 4.变量

4.1 变量的类型

JS是松散类型的语言

4.2 变量的声明

var 声明是变量的标准声明

var 声明的变量是永久性的,不能用delete运算符删除

全局对象,调用对象初始化时,JS解析器会在相应的代码段里寻找var声明的变量,

然后在全局对象,调用对象中创建相应的属性,此时它是未赋值的(undefined),

当程序执行到相应的var声明代码段时才会给相应对象的属性赋值

重复的声明:根据以上var声明作用的理解,重复的var声明不会造成任何错误,

var声明只是方便创建全局对象,调用对象的属性,代码只是赋值用的

遗漏的声明:给未声明的变量赋值,JS会隐式声明全局变量(在全局对象中增加相应的属性),然后给其赋值

4.3 变量的作用域

全局变量,局部变量

局部变量和全局变量同名时,会隐藏这个全局变量

4.3.1 没有块级作用域

函数中声明的变量,无论在哪里声明的,在整个函数中它们都是有定义的

4.3.2 未声明的变量和未赋值的变量

alert(u);会产生一个错误--使用未声明的变量会产生一个错误

var u;alert(u);会跳出undefined---使用未赋值的变量,使用的它的默认值undefined

4.4 基本类型和引用类型

第三章已经讲过

4.5 垃圾收集

同java

4.6 作为属性的变量

4.6.1 全局对象

JS解释器开始运行时,在执行任何JS代码前,会创建一个全局对象,这个对象的属性就是JS全局变量,

并初始化为undefined

var声明一个JS全局变量时,实际上就是定义了一个全局对象的属性,

JS解释器还会用预定义的值和函数来初始化全局对象的许多属性,如:Infinity parseInt Math

非函数内部可以用this来引用这个全局对象

客户端的JS中,Window对象代表浏览器窗口,它包含该窗口中的所有JS代码的全局对象,具有自我引用的window属性

4.6.2 局部变量:调用对象

函数的局部变量存放在调用对象的属性

调用对象是一个完全独立的对象,所以可以防止覆盖同名的全局变量

4.6.3 JS的执行环境

JS解释器执行一个函数时,会创建一个执行环境

JS允许同时存在多个全局执行环境,如:JS客户端的ifame的情况

4.7 深入理解变量作用域

每个JS执行环境都有一个和它相关联的作用域链它是一个对象列表或对象链.

查询x:变量名解析(variable name resolution)的过程,它开始查看作用域链的每一个对象,

如果有,返回值,如果没有继续查询下一个对象,以些类推.

作用域链的优先级:嵌套函数的调用对象>调用对象>全局对象

根据以上理解说明JS初始化的过程:

在JS解释器执行任何代码之前,创建全局对象

用预定义的值和函数来初始化全局对象中的属性,eg.Math,Infinity,parseInt

搜索函数外的var声明,创建全局对象相应的属性,初始化为undefined

创建全局的执行环境,作用域链只有一个对象-全局对象

依次执行代码

遇到var声明赋值语句给全局对象相应的属性赋值

遇到未声明赋值语句,在全局对象中增加相应的属性,并赋值

遇到函数调用,创建调用对象

搜索函数中的var声明和参数,创建调用对象相应的属性,初始化为undefined

创建函数执行环境,作用域链--第一对象:调用对象;第二对象:全局对象

依次执行代码

遇到var声明赋值语句给调用对象相应的属性赋值

遇到未声明赋值语句,在全局对象中增加相应的属性,并赋值

遇到函数调用,创建嵌套函数的调用对象

搜索嵌套函数中的var声明和参数,创建嵌套函数的调用对象相应的属性,初始化为undefined

创建嵌套函数执行环境,作用域链--第一对象:嵌套函数的调用对象;第二对象:调用对象;第三对象:全局对象

依此类推

eg1.

var scope="global";

function f(){

alert(scope);

var scope="local";

alert(scope);

}

f();

过程:

创建全局对象,搜索函数外的var声明语句,在全局对象中创建scope属性,scope=undefined

创建全局的执行环境,作用域链只有一个对象:全局对象

依次执行代码:

var scope="global"时,变量名解析开始,在全局对象属性中查找scope属性

把"global"赋给scope

遇到函数调用:创建调用对象

搜索函数中的var声明语句和参数,在调用对象中创建scope的属性,scope=undefined

创建函数执行环境,作用域链:调用对象>全局对象

依次执行代码:

alert(scope),查询scope,变量名解析,先搜索调用对象,找到scope属性,其值为undefined,执行

var scope="local",查询scope,变量名解析,先搜索调用对象,找到scope属性,scope="local"

alert(scope),查询scope,变量名解析,先搜索调用对象,找到scope属性,其值为"local",执行

eg2.

var scope="global";

function f(){

alert(scope);

scope="local";

alert(scope);

}

f();

过程:

创建全局对象,搜索函数外的var声明语句,在全局对象中创建scope属性,scope=undefined

创建全局的执行环境,作用域链只有一个对象:全局对象

依次执行代码:

var scope="global"时,变量名解析开始,在全局对象属性中查找scope属性

把"global"赋给scope

遇到函数调用:创建调用对象

搜索函数中的var声明语句和参数,没有找到var声明语句

创建函数执行环境,作用域链:调用对象>全局对象

依次执行代码:

alert(scope),查询scope,变量名解析,先搜索调用对象,没找到scope属性,再搜索全局对象,找到scope属性,其值为"global"执行

scope="local",查询scope,变量名解析,先搜索调用对象,没找到scope属性,,再搜索全局对象,找到scope属性,scope="local"

alert(scope),查询scope,变量名解析,先搜索调用对象,没找到scope属性,再搜索全局对象,找到scope属性,其值为"local",执行

eg3.

scope1="global";

alert(scope1);

function f(){

alert(scope2);

scope2="local";

}

f();

过程:

创建全局对象,没有找到var声明语句,没有自定义的全局对象属性

创建全局的执行环境,作用域链只有一个对象:全局对象

依次执行代码:

scope1="global"时,变量名解析开始,作用域链是没有找到scope1属性,在全局对象属性中创建scope1属性,并赋值为"global"

alert(scope1)时,变量名解析开始,作用域链是找到scope1属性,其值为"global",执行

遇到函数调用:创建调用对象

搜索函数中的var声明语句和参数,没有找到var声明语句

创建函数执行环境,作用域链:调用对象>全局对象

依次执行代码:

alert(scope2),查询scope2,变量名解析,作用域链是没有找到scope2属性,报错scope2 is not defined

函数

引申一下,function. 还记得上面提到的预解析,在javascript的预解析中,除了对var 变量的预定义,还包括了提取对函数的定义,所以可以在script的任何地方定义函数,在任何地方调用。不限于它之前.

但函数的定义方式,包括了一种叫字面量定义法, 用var的方法声明function.看下面

alert(typeof y3); //结果?

var y3 = function (){ console.log(‘1‘); }

还记得这个约定吧:调用必须出现在声明之后,为什么呢,如果理解了上面,其实这里答案已经明了。javascript 引擎在预解析 var 时 会给他们一个初始值 undefined,这样一来,如果我们在它的声明之前调用它,javascript 引擎还没拿到它的真实值,自然会报"xxx is not a function" 的错.这也理清了为什么同为函数声明,一个却关系到声明和调用的顺序,一个却无这样的约束。

结论

它是函数,是js执行,动态修改的结果,依然遵循了变量的预解析规则(在上面alert的时候,它还并没有拿到字面量函数的信息)。

如果是两个混合呢。看下面, 同时存在了为y4的变量和function。

alert(typeof y4); //结果?

function y4(){

console.log(‘y4‘)

}

var y4;

因为 javascript 在预解析时function的声明优先级高的缘故,所以y4自然为function类型, 但是在当y4 赋值之后(此时js引擎处于执行过程中),它对js的赋值操作将会覆盖function的声明。所以:

alert(typeof y5);

var y5 = ‘angle‘;

function y5(){

console.log(‘ghost‘);

}

alert(y5);

第一次alert结果,因为它处于js 执行过程中的顶端,所以为 function。 第二次再alert时, 它的值已经被重写为5(不要被function的定义位置在下所迷惑。)

从js的解析和执行分开来想,才发现眼前豁然开朗,很多问题的答案都很自然得浮出水面,正如那篇文章作者所说,

> "一旦理解了执行环境、调用对象、闭包、词法作用域、作用域链这些概念,JS语言的很多现象都能迎刃而解。"

原文地址:https://www.cnblogs.com/tongguilin/p/12230158.html

时间: 2024-10-20 22:51:00

js全局变量与局部变量 预解析与作用域链详解的相关文章

Js作用域与作用域链详解

一直对Js的作用域有点迷糊,今天偶然读到Javascript权威指南,立马被吸引住了,写的真不错.我看的是第六版本,相当的厚,大概1000多页,Js博大精深,要熟悉精通需要大毅力大功夫. 一:函数作用域 先看一小段代码: [javascript] view plaincopy var scope="global"; function t(){ console.log(scope); var scope="local" console.log(scope); } t(

js中作用域与作用域链详解

一:函数作用域 先看一小段代码: [javascript] view plaincopy var scope="global"; function t(){ console.log(scope); var scope="local" console.log(scope); } t(); 第一句输出的是: "undefined",而不是 "global" 第二讲输出的是:"local" 你可能会认为第一句会输

Js作用域与作用域链详解-转

转自:http://blog.csdn.net/yueguanghaidao/article/details/9568071 一直对Js的作用域有点迷糊,今天偶然读到Javascript权威指南,立马被吸引住了,写的真不错.我看的是第六版本,相当的厚,大概1000多页,Js博大精深,要熟悉精通需要大毅力大功夫. 一:函数作用域 先看一小段代码: 1 var scope="global"; 2 function t(){ 3 console.log(scope); 4 var scope

JavaScript作用域链详解

JavaScript的作用域链还是很有味道的,搞懂了这个知识点,闭包的问题也就迎刃而解咯 1.JavaScript的全局变量和局部变量 首先,先来看看js的全局变量和局部变量,js不是块级作用域,所以不能把你学过的C/C++作用域的知识用在js中! (1)全局变量 js的全局变量也可以看做window对象的属性,这句话怎么理解,请看以下代码: var x = 10; alert(window.x);//弹出10 也就是说var x = 10;等价于window.x=10; 再来看一段代码 fun

JavaScript作用域及作用域链详解、声明提升

相信大家在入门JavaScript这门语言时对作用域.作用域链.变量声明提升这些概念肯定会稀里糊涂,下面就来说说这几个 Javascript 作用域 在 Javascript 中,只有局部作用域和全局作用域.而只有函数可以创建局部作用域,像 if,for 或者 while 这种块语句是没办法创建作用域的. (当然 ES6 提供了 let 关键字可以创建块作用域.) Javascript 的这种特性导致 for 循环里面创建闭包时会产生让人意想不到的结果.比如下面这个例子: var i = 20;

执行环境,作用域,作用域链详解

声明:该文章有些概念摘自<JavaScript高级程序设计> 1.执行环境:也称"环境",执行环境定义了变量或函数有权访问的其他数据,决定了他们各自的行为.(全局定义的变量,函数里面可以访问.一般情况下,函数里面定义的变量,全局无法访问). 2.全局执行环境:全局执行环境是最外围的一直执行环境,(在web浏览器中,全局执行环境被认为是window对象,因此所有全局变量和函数都是作为window对象的属性和和方法创建的),全局环境直到应用程序退出(关闭网页或者刷新网页)才会被

标识符解析、作用域链、运行期上下文、原型链、闭包

本文讲到的是如何从数据访问层面上提高JS 代码的执行效率.总的来讲有以下几条原则: 函数中读写局部变量总是最快的,而全局变量的读取则是最慢的: 尽可能地少用with 语句,因为它会增加with 语句以外的数据的访问代价: 闭包尽管强大,但不可滥用,否则会影响到执行速度以及内存: 嵌套的对象成员会明显影响性能,尽量少用: 避免多次访问对象成员或函数中的全局变量,尽量将它们赋值给局部变量以缓存. 这么几句话看似简单,但要深刻理解其中的道理则需涉及到JS的 标识符解析.作用域链.运行期上下文(又称为执

Mysql Join语法解析与性能分析详解

一.Join语法概述 join 用于多表中字段之间的联系,语法如下: ... FROM table1 INNER|LEFT|RIGHT JOIN table2 ON conditiona table1:左表:table2:右表. JOIN 按照功能大致分为如下三类: INNER JOIN(内连接,或等值连接):取得两个表中存在连接匹配关系的记录. LEFT JOIN(左连接):取得左表(table1)完全记录,即是右表(table2)并无对应匹配记录. RIGHT JOIN(右连接):与 LEF

JS中的定时函数(setTimeout,clearTimeout,setInterval,clearInterval详解 )

设置定时器,在一段时间之后执行指定的代码,setTimeout与setInterval的区别在于setTimeout函数指定的代码仅执行一次 方法一: window.setTimeout("alert('ok')",5000); 方法二: window.setTimeout(function() { alert("Ok"); }, 5000); 方法三: function showAlert() { alert("ok"); } window.s