深入理解 JavaScript 变量的作用域和作用域链
一个变量的作用域(scope)是程序源代码中定义这个变量的区域。简单的说,作用域就是变量与函数的可访问范围。全局变量拥有全局作用域,在JavaScript代码中的任何地方都有定义。局部变量是在函数体内声明而且只作用在函数体内部以及该函数体的子函数的变量。下面我们对全局作用域和局部作用域来做一个深入的理解。
1. 全局作用域(Global Scope)
全部变量拥有全局作用域,在代码的任何地方都有定义,一般来说以下几种情形拥有全局作用域:
(1)最外层函数和在最外层函数外面定义的变量拥有全局作用域,例如:
var scope="global"; //声明一个全局变量
function checksope(){
function showglobal(){
alert(scope); //弹窗全局变量
}
showglobal();
}
checksope() // global 内部函数可以访问全局变量
(2)所有末定义直接赋值的变量自动声明为拥有全局作用域,例如:
function checksope(){
var scope="local";
scopeglobal="global";
alert(scope);
}
checksope(); // local
alert(scopeglobal); // global 不带var关键词声明的变量,
直接升级为全局变量,同时也是全局变量
的一个属性
alert(scope); //脚本错误
变量scopeglobal拥有全局作用域,而scope在函数外部无法访问到。
(3)所有window对象的属性拥有全局作用域
一般情况下,window对象的内置属性都拥有全局作用域,例如window.name、window.location、window.top等等。
2. 局部作用域(Local Scope)
局部作用域一般只在固定的代码片段内可访问到,最常见的例如函数内部,所有在一些地方也会看到有人把这种作用域称为函数作用域,例如下列代码中的blogName和函数innerSay都只拥有局部作用域。
function checksocpe(){
var socpe="local";
function inner(){
alert(socpe);
}
inner();
}
alert(socpe); //脚本错误
inner(); //脚本错误 函数外部无法访问内部定义的函数
函数作用域和提前声明
一、函数作用域
在一些类似c语言的编程语言中,花括号内的每一段代码都具有各自的作用域,而且变量在声明它们的代码段之外是不可见的,我们称之为块级作用域,而JavaScript没有块级作用域。JavaScript取而代之地使用了函数作用域:变量在声明他们的函数体以及这个函数体嵌套的任意函数体内都有定义的。
在如下所示的代码中,在不同位置定义了变量i、j、k,它们都在同一个作用域内——这三个变量在函数体内均是有定义的。
function test(0){
var i = 0; // i在行函数体内时有定义的,
if(typof 0 == "object"){
var j = 0; //j在函数体内是有定义的,不仅仅是在循环内
for(var k=0; k<10;k++){ //k在行函数体内是有定义的,不仅仅是在循环内
console.log(k);//输出数字0-9 }
console.log(k); //k 已经定义了,输出10
}
console.log(j); //j 已经定义了,但是可能没有初始化
}
二、函数的提前声明
javascript的函数作用域是指在函数内声明的所有变量在函数体内始终是可见的,有意思的是,这意味着变量在声明之前甚至已经可用。JavaScript的这个特性被非正式地称为声明提前,即JavaScript函数里声明的所有变量(但不涉及赋值)都被"提前"至函数体的顶部。
tip:"声明提前"这步操作是在JavaScript引擎的"预编译"时进行的,是在代码开始运行之前。
var scope = "glocal";
function f(){
console.log(scope);//输出"undefined",而不是"global"
var scope = "local";//变量在这里赋初始值,但变量本身在函数体内任何地方均是有定义的
console.log(scope);//输出"local"
}
也许您会误认为第一行会输出“global”,因为代码还没有执行到var语句声明局部变量的地方。其实不然,由于函数作用域的特性,局部变量在整个函数体始终是有定义的,也就是说,在函数体内局部变量覆盖了同名全局变量。尽管如此,只有在程序执行到var语句的时候,局部变量才会被真正赋值。因此,上述过程等价于:将函数内的变量声明“提前”至函数顶部,同时变量初始化留在原来的位置:
function f(){
var scope; //在函数定部声明了局部变量
console.log(scope); //undefined
scope = "local";//
console.log(scope); // 输出local
}
作为属性的变量
当声明一个全局变量时,实际上是定义了全局对象的一个属性。使用var声明的变量不可配置,未声明的可配置。如下:
var truvar = 1; //声明一个不可删除的全局变量
fakevar = 2; //创建全局对象的一个可删除的属性
this.fakecar2 = 3;//同上
delete truevar //=> false:变量并没有被删除
delete fakevar //=> true:变量并没有被删除
delete this.fakevar2 //=> true:变量并没有被删除
此规则只对全局变量有效。
作用域链
1、作用域链变量的寻址
如果讲一个局部变量看做是自定义实现的对象的属性的话,那么可以换一个角度来理解作用域。
每一段javascript代码(全局代码或函数)都有一个与之关联的作用域链。这个作用域连是一个对象列表或者链表,这组对象定义了这段代码“作用域中”的变量。当javascript需要查找变量x的值的时候(这个过程称作“变量解析”),它会从链中的第一个对象开始查找,如果这个对象有一个名为x属性,则会直接使用这个属性的值,如果第一个对象中不存在,则会继续寻找下一个对象,依次类推。如果作用域链上没有任何一个对象含有属性x,则抛出错误(ReferenceError)异常。
2、不同的层级作用域上对象的分布
- 在javascript的最顶层(也就是不包含任何函数定义内的代码),作用域链由一个全局对象组成。
- 在不包含嵌套的函数体内,作用域链上有两个对象,第一个是定义函数参数和局部变量的对象,第二个是全局对象。
- 在一个嵌套的函数体内,作用域链上至少有三个对象。当调用这个函数时,它创建一个新的对象来存储它的局部变量,它实际上保存在同一个作用域链。
对于嵌套函数来讲,事情更有趣,每次调用外部函数时,内部函数又会重新定义一遍。因为每次调用外部函数的时候,作用域链都是不同的。内部函数每次定义的时候都有微妙的差别——在每次调用外部函数时,内部函数的代码都是不相同的,而且关联这段代码的作用域链也不相同。
深入理解 JavaScript 变量的作用域和作用域链的更多相关文章
- 深入理解javascript中执行环境(作用域)与作用域链
深入理解javascript中执行环境(作用域)与作用域链 相信很多初学者对与javascript中的执行环境与作用域链不能很好的理解,这里,我会按照自己的理解同大家一起分享. 一般情况下,我们把执行 ...
- 深入理解Javascript变量作用域
在学习JavaScript的变量作用域之前,我们应当明确几点: a.JavaScript的变量作用域是基于其特有的作用域链的. b.JavaScript没有块级作用域. c.函数中声明的变量在整个函数 ...
- 深入理解javascript函数定义与函数作用域
最近在学习javascript的函数,函数是javascript的一等对象,想要学好javascript,就必须深刻理解函数.本人把思路整理成文章,一是为了加深自己函数的理解,二是给读者提供学习的途径 ...
- JavaScript 变量、函数与原型链
定义 || 赋值 1-函数的定义 函数定义的两种方式: “定义式”函数:function fn(){ alert("哟,哟!"); } “赋值式”函数:var fn = funct ...
- 如何理解JavaScript中的原型和原型链
首先是一张关系图,避免抽象化理解时产生的困难 Function对象 函数对象是JavaScript学习中不可避免的一部分,而且这一部分相对重要且抽象 函数的创建方式有2种: 字面量创建 var foo ...
- 深入理解JavaScript中的继承:原型链篇
一.何为原型链 原型是一个对象,当我调用一个对象的方法时,如果该方法没有在对象里面,就会从对象的原型去寻找.JavaScript就是通过层层的原型,形成原型链. 二.谁拥有原型 任何对象都可以有原型, ...
- 三张图较为好理解JavaScript的原型对象与原型链
最近从网上看到别人详细得讲解了js的原型对象和原型链,看完感觉是看得最清晰的一个,于是,摘录到自己博客里 对于新人来说,JavaScript的原型是一个很让人头疼的事情,一来prototype容易与_ ...
- Javascript变量名混淆细节
前言 UglifyJS会对JS文件的变量名进行混淆处理.要理解Javascript变量混淆的细节.我们须要回答下面几个问题: 1.遇到一个变量myName,我们怎么知道这个myName变量要不要混淆 ...
- JavaScript中作用域和作用域链的简单理解(变量提升)
通过阅读<JS高级程序设计>这本书,对js中的作用域和作用域链知识有了初步的了解和认识,准备成笔记供大家参考,笔记中字数比较多,但个人认为叙述的挺详细的,所以希望读者耐心看.再者,本人了解 ...
随机推荐
- 关于EventEmitter的用法
EventEmitter的基本用法: var EventEmitter = require("events").EventEmitter; var ee = new EventEm ...
- angular学习笔记(二十八-附2)-$http,$resource中的promise对象
下面这种promise的用法,我从第一篇$http笔记到$resource笔记中,一直都有用到: HttpREST.factory('cardResource',function($resource) ...
- 机器学习之sklearn——聚类
生成数据集方法:sklearn.datasets.make_blobs(n_samples,n_featurs,centers)可以生成数据集,n_samples表示个数,n_features表示特征 ...
- linux系统top命令查看系统状态
Linux系统可以通过top命令查看系统的CPU.内存.运行时间.交换分区.执行的线程等信息.通过top命令可以有效的发现系统的缺陷出在哪里.是内存不够.CPU处理能力不够.IO读写过高. 使用SSH ...
- html5标签知多少
此文为前段时间团队研究会出品,与小伙伴兮兮圆一起研究的成果,意外地上了公司km的今日推荐,今日挪过来,为新开张的博客先暖暖场吧. 一.常用标签 <header>.<footer> ...
- [LeetCode] Find All Anagrams in a String 找出字符串中所有的变位词
Given a string s and a non-empty string p, find all the start indices of p's anagrams in s. Strings ...
- 初步学习border-radius
1.属性解析 border-radius是css3属性,他可以使div的角进行一定程度的弯曲. 比如说下面这个width和height的正方形div 经过设置border-radius之后四个角会出现 ...
- typedef
第一.四个用途 用途一: 定义一种类型的别名,而不只是简单的宏替换.可以用作同时声明指针型的多个对象.比如:char* pa, pb; // 这多数不符合我们的意图,它只声明了一个指向字符变量的指针, ...
- IDEA快捷键
[常规] Ctrl+Shift + Enter,语句完成 "!",否定完成,输入表达式时按 "!"键 Ctrl+E,最近的文件 Ctrl+Shift+E,最近更 ...
- QGEditors.WinForms WinForms下使用的部分扩展控件
Nuget: https://www.nuget.org/packages/QGEditors.WinForms/ PM> Install-Package QGEditors.WinForms ...