前言
首先我们在正式讲作用域之前,我们要先清楚两个概念,一是js是一门弱类型动态语言,二是在在执行代码前往往是需要进行编译的。
什么是弱类型语言动态语言?
举个例子
ini
var A=5;
var B="5"
sumA=A+B;
sumB=A-B;
sumA=55,系统默认+字符连接符,将A转化为字符串类型;而sumB=0;系统认为-是算数运算符,从而将B转化为int类型,所以sum为5-5=0;
为什么在js执行代码前往往是需要进行编译的?在说明白这个之前,我们要先知道什么是作用域?
什么是作用域?
作用域又分为全局作用域、局部作用域、块级作用域、欺骗词法作用域 下面我将给出几段代码辅助大家更快了解:
1.全局作用域:函数外面声明的变量,称之为全局变量。可以在页面任何地方被访问
rust
全局变量生命周期 : 从页面加载 -> 到页面关闭
ini
var a = 1;//全局变量
var b = 2;//全局变量
function foo() {
console.log(a, b);
}
foo();
这里的变量a,b就是全局变量,所以输出结果为 1,2
2.局部作用域:: 函数里面声明的变量,称之为局部变量。 只能在函数里面被访问
局部变量生命周期 : 从函数开始执行 -> 到函数执行结束
ini
var a = 1;//全局变量
function foo() {
var a = 2;//局部变量
console.log(a);
}
foo();
让我们看看代码输出结果: 这时候有同学会问了,为什么这里的输出结果为2而不是1呢?
这就和我们前面所说的先编译再运行有关了,在这段代码中在执行函数foo时,返回function foo中找到a的值为2,所以console.log直接输出a的值。
下面再举几个例子帮助大家理解:
scss
function foo() {
var a = 2; //局部变量
}
foo();
console.log(a);
这时的输出结果为:
scss
function foo(){
var a = 1;
console.log(a);
function bar(){
var c = 2;
console.log(c);
}
}
foo()
bar()
输出结果为:
这是因为bar()函数在function foo()局部作用域而最后一行的代码bar()定义在全局作用域中, 所以我们可以得出结论:变量的查找会先内到外的作用域中查找,不能从外到内!
3.块级作用域:(1)let/const关键字声明 (2) 大括号里面声明
块级变量声明周期 : 从大括号开始 -> 到大括号结束
ini
if(true){
var a = 1;//全局变量
let b = 2;//块级变量(块级作用域 :只在大括号内部起作用)
console.log(a,b);//1,2
};
console.log(a);//1
// console.log(b);// b is not defined
从上面的例子我们可以知道:
- var声明的变量存在声明提升 ,提上到当前作用域的顶端
- let + {} 会形成块级作用域,但是不会声明提升
什么是声明提升呢
ini
console.log(a);
var a = 1;
上面代码等价于下面这段代码:
ini
var a;
console.log(a);
a = 1;
-
声明提升 :通过 var、let 和 const 声明的变量在代码执行之前被 JavaScript 引擎提升到当前作用域的顶部 JavaScript 的代码在生成前,会先对代码进行编译,编译的一部分工作就是找到所有的声明,然后建立作用域将其关联起来,因此,在 当前作用域内 包括变量和函数在内的所有声明都会在任何代码被执行前首先被处理。
注意这里是 声明 会被提前处理,赋值 并没有, 定义声明是在编译阶段进行的,而赋值是在执行阶段进行的 。也就是说声明提升了,赋值还留着原地,等待执行。
let 是块作用域
块是由
{}
界定的代码块。一个块存在于花括号中。花括号内的任何内容都是一个块,let不会声明提升,不能重复声明同一变量
ini
let a = 1;
let a = 2;
console.log(a);
输出结果会报错,与 var 不同,let 变量不能在其作用域内重新声明。但是如果同一个变量定义在不同的作用域,就不会报错:
ini
let a = "Hi";
if (true)
let a = "Hello";
console.log(a); // 这里的a输出"Hello"
}
console.log(a); //这里的a输出"Hi"
这是因为两个都被视为不同的变量,因为它们具有不同的作用域。
const 声明是块作用域
用 const 声明的变量保持恒定值。 const 声明与 let 声明有一些相似之处。与 let声明一样,const声明只能在它们声明的块内访问。注意:const 不能更新或重新声明 这意味着用 const声明的变量的值在其作用域内保持不变。它不能被更新或重新声明。
这三个声明方法有以下区别:
- var 声明是全局作用域或函数作用域,而 let和 const 是块作用域。
- var变量可以在其作用域内更新和重新声明;let变量可以更新但不能重新声明;const变量既不能更新也不能重新声明。
- var和 let可以在不初始化的情况下声明,而 const必须在声明时初始化。
欺骗词法作用域
1.eval()
观察下面代码:
scss
var b = 2; // 被覆盖
function foo(str, a) {
eval(str); // 欺骗
console.log(a, b);//输出: 1, 3
}
foo("var b = 3;", 1);
eval(..) 调用中的 "var b = 3;" 这段代码会被当原本就在那里一样来处理这段代码实际上在 foo(..) 内部创建了一个变量 b,并覆盖了外部也就是全局作用域中的同名变量。 console.log(..) 被执行时,会在 foo(..) 的内部同时找到 a 和 b,但是永远也找不到外部的 b。因此会输出"1, 3"
2.with
ini
var obj={
a:1,
b:2,
c:3,
d:4,
}
with(obj){
a=2,
b=3,
c=4
}
function foo(obj){
with(obj){
a=2
}
}
var o1={
a:3
}
var o2={
b:4
}
foo(o2)
console.log(a);
结果是输出为 2 的,尽管with块可以将一个对象处理为词法作用域,但是这个块内部正常的var声明并不会被限制在这个块的作用域中,而是被添加到with所处的函数作用域中。
新人报道,请多关照~