一、作用域分类

   定义:在js中,作用域是变量,对象,函数可访问的一个范围。

   分类:全局作用域,局部作用域,块级作用域

   全局作用域:全局代表了整个文档document,变量或者函数在函数外面声明,那它的就是全局变量和全局函数。之所以全局变量在这个文档的任何位置都可以访问是因为它是window下的属性,window是一个全局对象,它本身在页面中任何位置可以用,同样它身上的属性在页面的任何位置也是可以用的。

   声明全局作用域的方法:把变量或者是函数放在函数外声明或者变量不用var声明直接赋值(不管是在函数内还是函数外它都是一个全局变量).要避免使用全局变量,声明变量的时候一定要加上var.

<script>
var x = ; //在函数外,全局变量
function fn1() {
var c = ; //函数内,局部变量,它的范围仅限于该函数,函数运行完成后,函数内定义的变量将会自动销毁
c += ;
a=;
console.log(c); //13 x += ; // 在函数内修改了全局变量后,全局变量就完成了修改,外面调用时,也是修改后的
}
fn1();
console.log(x, "______"); //13 "______" b=;
console.log(a);// 18 没有用var声明,虽在函数内声明但也是全局变量
console.log(window); //可以看到a,b,x
</script>
<script>
console.log(x,a); //13 18 在这也可以访问
</script>

  全局作用域下带var,声明的一个变量,相当于给window全局对象设置了一个属性,变量的值就是属性值。

console.log(a);  //undefined
console.log(window.a);//undefined
console.log('a' in window);//true
//在变量提升阶段,在全局作用域中声明了一个变量a此时就已经把a当做属性赋值给window了,只不过此时还没有给a赋值,默认值为undefined
//in :检测某个属性是否隶属于这个对象
var a = ;
console.log(a);// 全局变量a 12
console.log(window.a);//window的一个属性名a 12 window.a = ; //全局变量值修改,window的属性值也跟着修改
console.log(a); //14
//全局变量和window中的属性存在“映射机制”

  全局作用域下不带var, 直接赋值,那声明的是window下的属性。不能这样做,有可能会修改window下的属性不安全。

//=> 不加var的本质是window下的属性
// console.log(n);//Uncaught ReferenceError: n is not defined
console.log(window.n);//undefined
console.log('n' in window);//false
n = ; // <=> window.n = 12
console.log(n);//
console.log(window.n);//

  局部作用域:变量在函数内声明,变量为局部作用域。只能在函数内部访问。所以不同函数可以使用相同名称的变量。函数执行完后局部变量会自动销毁。函数可以嵌套,嵌套的函数可以访问父函数里的内容。

  声明局部作用域的方法:var 变量,function 函数名(){}.

    <script>
function fn() {
var a = ;
var b = ;
function fn1() {
console.log(a + b); //50 嵌套函数可以访问父函数里的内容
}
fn1();
}
fn();
// console.log(a,b); //报错 a,b是局部变量,在外面访问不到
// fn1();//报错 fn1是局部函数,在外面也是问不到的 //全局变量与局部变量重名
var s1 = ;
function fn1() {
var s1 = ;
s1 += ;
window.s1 += ; //如果全局变量的名称在函数中和局部变量相同,想要调用全局变量时要在前面加上window前缀
console.log(s1); //
}
fn1();
console.log(s1); //20 在函数内全局变量进行了改变 var s2 = ;
function fn2() {
console.log(s2); //undefined 函数当中有定义局部变量,函数作用范围内所有位置都是这个局部变量,
                 //此函数中下文定义了局部变量s2,但是这里是在定义之前调用的,所以s2的值为undefined
s2 += ;
console.log(s2); //NaN undefined加数字为NaN
var s2 = ;
}
fn2();
</script>

  什么是作用域链,可以简单的将作用域链理解为变量与函数的查找规则。

  查找规则:如是一个函数需要用到一个变量,那它会先在自己的作用域里去找这个变量,如果自己有那它就直接用自己的,如果自己没有,那它就会一层层向外面找,直到找到外层的变量,找到后就用外层的变量(只会向外,不会向内找)。

function fn() {
//变量提升无
//console.log(m);//Uncaught ReferenceError: m is not defined
m = ;
console.log(m);//
console.log('m' in window);//true 在作用域链查找的过程中,如果找到window也没有这个变量,相当于给window设置了一个属性m
}
fn();
//console.log(m);//
var x = [, ];
function fn(y) {
y[] = ;
y = [];
y[] = ;
console.log(y);
fn(x);
console.log(x);

  在上面这个例子中我们要注意以下几点:

    1、创建函数的时候就已经定义了函数的作用域,函数执行的目的是想让存储在堆中的代码字符串执行,如果想让代码字符串执行那就必须有一个执行环境,所以会形成一个私有的上下文

    2、形成私有上下文后会进栈操作,把全局上下文放到栈的底部,新形成的上下文放到栈的顶部,执行完后出栈

    3、在私有上下文中也有可能创建变量,我们把它叫私有变量,私有变量放在AO中,AO是活动对象,函数中的变量对象都称为AO。AO是VO的一个分支都是变量对象。

    4、在代码执行前,要经过几个阶段:初始化作用域链、初始化this指向、初始化实参集合、形参赋值

    5、最后才是代码执行

  二、变量提升

  通过前面的知识我们得知,在当前上下文中,js代码自上而下执行之前,浏览器首先会把当前上下文中所有带“var / function”关键字进行提前的声明和定义,解析到它们对应作用域开始的位置(也可以理解为这是词法解析的一个环节,语法解析发生在代码执行前)这种预先处理的机制叫做变量提升,变量提升的意义在于创建变量前使用这个变量不报错。变量提升也可以称之为预解析。

  声明(declare): var a  / function sum(默认值为undefined)

  定义(defined): a = 12(定义其实就是赋值操作)

  在变量提升阶段,带var的只声明未定义,而带function声明和定义都完成了。

  变量提升只发生在当前作用域(如:开始加载页面的时候只对全局作用域下的进行提升,因为此时函数中存储的都是字符串而已);浏览器很懒,做过的事情不会重复的执行,也就是当代码执行遇到创建函数这部分代码后,直接跳过(在提升阶段已经完成了函数的赋值操作)。

  私有作用域中,带var的在变量提升阶段,声明为私有变量,与外界无关。不带var不是私有变量,会向上级作用域查找,看是否为上级的变量,不是,继续向上查找,它的上级作用域是谁和它在哪里执行无关,和它在哪里创建有关,在哪里创建,它的上级作用域就是谁。

      var foo = 1;
function bar() {
if (!foo) {
var foo = 10;
}
console.log(foo);
}
bar();
console.log(g, h);
var g = ,
h = ;
function fn() {
console.log(g, h);
var g = h = ;
console.log(g, h);
}
fn();
console.log(g, h);

var n = ;
function fn() {
var n = ;
function f() {
n++;
console.log(n);
}
f();
return f;
}
var x = fn();
x();
x();
console.log(n);

  

  匿名函数和普通函数的区别:只对等号左边的进行变量的提升。真实项目中建议用函数表达式创建函数,因为这样在变量提升阶段只会声明,不会赋值。最好是把函数表达式匿名函数“具名化”,因为虽然是起了函数有了名字,但是这个名字不能在函数外面进行访问。

/*
* 全局上下文中的变量提升
* func=函数 函数在这个阶段赋值都做了
*/
func();
function func() {
var a = ;
console.log('OK');
} func(); //=>Uncaught TypeError: func is not a function
var func = function () {
// 真实项目中建议用函数表达式创建函数,因为这样在变量提升阶段只会声明FUNC,不会赋值
console.log('OK');
};
func(); var func = function AAA() {
// 把原本作为值的函数表达式匿名函数“具名化”(虽说是起了名字,但是这个名字不能在外面访问 =>也就是不会在当前当下文中创建这个名字)
// 当函数执行,在形成的私有上下文中,会把这个具名化的名字做为私有上下文中的变量(值就是这个函数)来进行处理
console.log('OK');
// console.log(AAA); //=>当前函数
// AAA(); 递归调用 而不用严格模式下都不支持的 arguments.callee 了
};
// AAA(); //=>Uncaught ReferenceError: AAA is not defined
func();

  条件判断下的变量提升:在当前作用域下,不管条件是否成立都要进行变量提升。带var的还是只声明,带function的在老版本浏览器渲染机制下,声明和定义都完成,但考虑到es6中的块级作用域,新版本浏览器对于在条件判断中的函数不管条件是否成立只声明不定义。

console.log(i);//undefined
if ( === ) {
var i = ;
console.log(i);
}
console.log(i);//undefined console.log(fn);//undefined
if ( === ) {
console.log(fn); //函数本身
//当条件成立进入到判断体中(在es6中它是一个块级作用域)第一件事并不是代码执行,而是类似于变量提升一样,先把fn声明和定义了也就是判断体中代码执行之前,fn就已经赋值
function fn() {
console.log('ok');
}
}
fn();//函数本身

  基于var或者function在全局上下文中声明的变量(全局变量)会映射到GO(全局对象window)上,作为它的属性,而且一个修改另外一个也会跟着进行修改。

var a = ;
console.log(a); //=>12 全局变量
console.log(window.a); //=>12 映射到GO上的属性a window.a = ;
console.log(a); //=>13 映射机制是一个修改另外一个也会修改

   在es6中基于let/const等方式创建的变量或者是函数,不存在变量提升机制,它切断了全局变量和window属性的映射机制。 

  在相同作用域中,基于let不能声明相同名字的变量(不管用什么方式在当前作用下声明了变量,再次使用let创建都会报错)。虽然就有变量提升机制,但是在当前作用域代码自上而下执行之前,浏览器会做一个重复性检测(语法检测)自上而下查找当前作用域下所有变量,一旦发一有重复的,直接抛出异常,代码也不会在执行了(虽然没有把变量提前声明定义,但是浏览器已经记住了,当前作用域下有哪些变量)
// console.log(a);//报错
let a = ;
console.log(window.a); //undefined
console.log(a); // let b = ;
console.log(b);
let b = ; //Uncaught SyntaxError: Identifier 'b' has already been declared
console.log(b);

  现在最新版本要向前兼容es3/5规范, 要注意:1.判断体和函数体等不存在块级上下文,上下文只有全局和私有 2.不论条件是否成立,带function的都要声明和定义。

  向后兼容es6规范,要注意:1.存在块级作用域,大括号中出现let/const/function...都会被认为是块级作用域 2.不论是否成立,带function的只提前声明,不会提前赋值。

var a = ;
if (true) {
a = ;
function a() {}
a = ;
console.log(a);
}
console.log(a);

js重点——作用域——作用域分类(三)的更多相关文章

  1. js重点——作用域——简单介绍(一)

    一.作用域 定义:在js中,作用域为变量,对象,函数可访问的一个范围. 分类:全局作用域和局部作用域 全局作用域:全局代表了整个文档document,变量或者函数在函数外面声明,那它的就是全局变量和全 ...

  2. js重点——作用域——内部原理(二)

    本篇是深入分析和理解作用域的第一篇——内部原理和工作模型. 我们知道作用域是变量,对象,函数可访问的一个范围.这说明了我们需要一套良好的规则来存储变量,之后方便查找.所以我们首先要理解的是在哪里而且怎 ...

  3. 如何理解vue.js组件的作用域是独立的

    vue.js组件的作用域是独立,可以从以下三个方面理解: 1.父组件模板在父组件作用域内编译,父组件模板的数据用父组件内data数据:2.子组件模板在子组件作用域内编译,子组件模板的数据用子组件内da ...

  4. 聊一下JS中的作用域scope和闭包closure

    聊一下JS中的作用域scope和闭包closure scope和closure是javascript中两个非常关键的概念,前者JS用多了还比较好理解,closure就不一样了.我就被这个概念困扰了很久 ...

  5. JS中的作用域(一)-详谈

    本篇文章在于详细解读JavaScript的作用域,从底层原理来解释一些常见的问题,例如变量提升.隐式创建变量等问题,在和大家一起交流进步的同时,也算对自己知识掌握的记录,方便以后复习 首先,直接捡干的 ...

  6. JS变量、作用域、内存

    写到这个题目<JS变量.作用域,内存>,我就不由自主想起了黄金三嫖客.可能是名字有点像,嗯,一定是这样子的! JS接触下来,应该是要比Java简单不少的,所以,要学好啊.立个flag半年后 ...

  7. JS详细图解作用域链与闭包

    JS详细图解作用域链与闭包 攻克闭包难题 初学JavaScript的时候,我在学习闭包上,走了很多弯路.而这次重新回过头来对基础知识进行梳理,要讲清楚闭包,也是一个非常大的挑战. 闭包有多重要?如果你 ...

  8. JS基础学习——作用域

    JS基础学习--作用域 什么是作用域 变量的作用域就是变量能被访问到的代码范围,比如在下面的这个js代码中,变量a的作用域就是函数foo,因此在全局作用域内的console.log(a)语句不能访问到 ...

  9. JS中的作用域和作用域链

    本文原链接:https://cloud.tencent.com/developer/article/1403589 前言 作用域(Scope) 1. 什么是作用域 2. 全局作用域和函数作用域 3. ...

随机推荐

  1. 在基于Android以及Jetson TK平台上如何写32位的Thumb-2指令

    由于Android以及Jetson TK的编译工具链中的汇编器仍然不支持大部分的32位Thumb-2指令,比如add.w,因此我们只能通过手工写机器指令码来实现想要的指令.下面我将简单地介绍如何在AR ...

  2. python flask url参数

    python flask url参数 常见 url 传参中都是 xxx?xxx=xxx 问题来了 flask中我没有找到 关于xx? 问号的使用方式 是不是flask就不支持这种方式 如果有 rout ...

  3. Storm和Hadoop 区别

    Storm - 大数据Big Data实时处理架构   什么是Storm? Storm是:• 快速且可扩展伸缩• 容错• 确保消息能够被处理• 易于设置和操作• 开源的分布式实时计算系统- 最初由Na ...

  4. Node.js使用superagent模拟GET/POST请求样例

    示例代码: var superagent = require('superagent'); superagent.get("http://localhost:8091/user/all?re ...

  5. 1-18-2 LVM管理和ssm存储管理器使用&磁盘配额 (二)

    LVM管理和ssm存储管理器使用&磁盘配额  (二) 内容如下: ü  LVM快照 ü  ssm存储管理器的使用 ü  磁盘配额 第1章 LVM快照 lvm快照:为了保持系统的一致性,我们先做 ...

  6. Golang sync.WaitGroup的用法

    0x01 介绍 经常会看到以下了代码: 12345678910111213 package main import ( "fmt" "time") func m ...

  7. Flutter 底部的renderflex溢出

    一开始直接使用Scaffold布局,body:new Column  然后模拟器会提示捕获异常: 然后百度了一下Flutter的溢出问题,发现解决办法是使用SingleChildScrollView包 ...

  8. 文件夹中含有子文件夹,修改子文件夹中的图像存储格式(python实现)

    文件夹中含有子文件夹,修改子文件夹中的图像存储格式,把png图像改为jpg图像,python代码如下: import os import cv2 filePath = 'C:\\Users\\admi ...

  9. arduino系列文章

    arduino系列文章 1.Arduino基础入门篇-进入Arduino的世界 2.关于使用Arduino做开发的理解 3.详解Arduino Uno开发板的引脚分配图及定义(重要且基础) 4.Ard ...

  10. 【FFMEPG】windows下编译ffmpeg2.5——使用VS2013,ARMLINUX,ANDORID编译ffmpeg

    原文:http://blog.csdn.net/finewind/article/details/42784557 一.准备: 1. 本机环境: win7 64bit: 2. 安装MinGW到C:\M ...