深入理解JavaScript的作用域和作用域链

一、作用域
(一)作用域是什么
了解作用域之前先看一下变量和函数,变量和函数都有一定的访问权限,就是必须满足条件或者在某个范围之内才能访问,这个范围就是作用域。它具体表现形式就是一段特定的代码,在该代码段中的变量和函数是封闭的、独立的,这样变量才不会泄露、污染。
var cat = '有鱼';
function Person(){
var name = '张三';
console.log(name);//张三,在函数内部,跟变量name在同一个作用域内
}
console.log(cat);//有鱼
console.log(name);//没有结果显示,因为name在函数内部是独立的,访问不了
(二)作用域分类
作用域一共有三种,分别为全局作用域、函数作用域、块作用域,其中块作用域是ES6新增的,同时函数作用域和块作用域统称为局部作用域。
全局作用域
在代码的最外层的变量和函数称为全局作用域,有以下特点:
①在代码的任何地方都可以访问得到
②变量声明时如果省略var关键字,则为全局变量,拥有全局作用域
③window对象的属性和方法,拥有全局作用域
④浏览器打开时开启,浏览器关闭或者页面关闭时销毁
var cat = '有鱼';
function Person(){
var name = '张三';
per = '卡卡';// 省略var,视为全局变量
console.log(cat);//有鱼 cat为全局变量,函数Person内部可以访问
}
console.log(cat);//有鱼 cat为全局变量,最外层代码可以访问
console.log(per);//卡卡 per为全局变量,最外层代码可以访问
全局作用域也有弊端,如:变量命名时容易重复,造成变量污染;
函数作用域
在函数内部的变量权限称为函数作用域,有以下特点:
①每个函数都有自己的作用域,而且调用一次就会生成新的作用域
②只能在函数内部才能访问,外部是没有权限访问的
③进入函数内部时开启,函数执行完毕后销毁
var cat = '有鱼';
function Person(){
var name = '张三';
console.log(name);//张三 name为函数作用域,函数Person内部可以访问
}
console.log(name);//有鱼 name为函数作用域,最外层代码没有权限访问

块作用域(ES6新增)
凡是由{}符号包裹起来的都是块作用域,ES6新增的知识点,有以下特点:
①只能在{}内部起作用
②变量声明时使用let或const关键字
③变量名不能重复,否则会报错
④变量不可以在声明之前使用
使用var进行展示:
if(true){
var cat = '有鱼';
console.log(cat);//有鱼
}
console.log(cat);//有鱼

使用let进行展示:
if(true){
let cat = '有鱼';
console.log(cat);// 有鱼
// let cat = '年年';// Uncaught SyntaxError: Identifier 'cat' has already been declared

}
console.log(cat);// 访问不了cat变量 报:Uncaught ReferenceError: cat is not defined
注意:块作用域一般用在for等循环语句中,可以避免变量外泄
(三)变量提升与函数提升
变量提升
①var关键字存在变量提升,let、const不存在;
②变量提升的意思是在程序执行前,先去整个代码中查看是否含有变量声明,即是否有var关键字,如果有则先执行声明,然后再去执行其他部分。
这里我我总结了4句话:
①js程序执行的顺序,先看var声明,声明部分在第一行,其他(赋值)按照正常顺序执行
②如果局部变量里面有var声明,就是局部变量,不用管外面的全局变量
③如果局部变量里面没有var声明,就去找外面的全局变量
④在局部变量里面,如何使用全局变量,使用window对象调用
var a = "Hello";
function person(){
var a;
console.log(a);//undifined
a = "World";
console.log(a);//World
}
person();
console.log(a);//Hello
// 先看整个脚本文件的声明部分,全局作用域有个变量a,局部作用域也有个变量a,而且都有var声明,说明他们是完全两个不同的变量,此时函数内部不用管外面的全局变量,此时变量a只是声明而没有赋值,所以输出为 undifined;
// 接着还是局部作用域里面,给a进行了赋值,此时输出时为world,且仍是局部变量;
// 函数外部的输出时,因为局部作用域已经结束,则又恢复到全局变量中,所以此时输出的是全局变量啊,值为hello。
var a = "Hello";
function person(){
console.log(a);//Hello
a = "World";
console.log(a);//World
}
person();
console.log(a);//World
// 先看整个脚本文件的声明部分,全局作用域有个变量a,局部作用域里面没有var声明,则此时a是一个全部变量,所以输出时a的值就是全部变量hello;
// 局部作用域里面对全局变量a进行了重新赋值,所以此时输出时为world
// 函数外部的输出时,因为跳出局部作用域,又恢复到全局变量中,之前因为已经重新赋值为world,此时输出时为world

var a = "Hello";
function person(){
console.log(a);//undifined
var a = "World";
console.log(a);//World
}
person();
console.log(a);// Hello
// 先看整个脚本文件的声明部分,全局作用域有个变量a,局部作用域也有个变量a,且有var声明,说明他们是完全两个不同的变量,此时函数内部不用管外面的全局变量,进行变量提升,记住只是声明提升,赋值不进行提升,所以输出为 undifined;
// 还是局部作用域里面,给a进行了赋值,此时输出时为World,且仍是局部变量
// 函数外部的输出时,因为局部作用域已经结束,则又恢复到全局变量中,所以此时输出时为Hello

var a = "Hello";
function person(){
console.log(window.a); //Hello 因为是window对象的属性,此时a是全局变量
var a = "World"; //局部变量a在这行定义
console.log(a); //World
}
person();
console.log(a);//Hello 全局变量a
// 在局部作用域使用全局变量使用window.全局变量名字

函数提升
函数有两种形式:函数表达式 var per = function(){} 、函数声明 function per(){},只有函数声明有函数提升特性
提升的意思是允许先调用,后声明
per();//可以先调用
function per(){
console('我是函数声明形式,允许提升');
}

二、作用域链
(一)什么是作用域链
每一层作用域其实就是一个执行环境(作用域和执行环境是有区别的,这里为了容易理解,姑且这样),在该环境内执行程序时,会创建一个变量对象,对象包含该有属于自己的变量和函数;
在当前执行环境下找不变量时,就会向上一层作用域里面寻找,具体过程还是先找到上一层的变量对象,再去里面找对应的变量,如果还是找不到,继续向上……
直到最外层的作用域,也就是全局作用域,这样就会形成一个链条,称为作用域链。
(二)作用域特点
作用域链最外层是全局作用域,最内层是当前作用域
内部环境可以通过作用域链访问所有外部环境,但外部环境不能访问内部环境的任何变量和函数
由于变量的查找是沿着作用域链来实现的,所以也称作用域链为变量查找的机制
var cat = '有鱼';
function Person() {
var name = '张三';
function Student() {
var age = 18;
console.log(name);//张三
console.log(cat);//有鱼
}
Student();
console.log(age);//Uncaught ReferenceError: age is not defined
}
Person();
①Student函数内部属于最内层作用域,找不到name,向上一层作用域Person函数内部找,找到了输出‘张三’;
②在Student内部输出cat时找不到,向上一层作用域Person函数找,还找不到继续向上一层找,即全局作用域,找到了输出‘有鱼’;
③在Person函数内部输出age时找不到,向上一层作用域找,即全局作用域,还是找不到输出‘age is not defined’;

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容