解释 JavaScript 中的作用域与变量声明提升?
答案:
- 我对作用域的理解是只会对某个范围产生作用,而不会对外产生影响的封闭空间。在这样的一些空间里,外部不能访问内部变量,但内部可以访问外部变量。
- 所有申明都会被提升到作用域的最顶上
- 同一个变量申明只进行一次,并且因此其他申明都会被忽略
- 函数声明的优先级优于变量申明,且函数声明会连带定义一起被提升
作用域的分类
答案:全局作用域、函数作用域、块级作用域
解析:
全局作用域
在代码的最外层的变量和函数称为全局作用域,有以下特点:
①在代码的任何地方都可以访问得到
②变量声明时如果省略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新增的知识点,有以下特点:
①只能在{}内部起作用,if和for的{}也是属于块级作用域
②变量声明时使用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等循环语句中,可以避免变量外泄
JS 块级作用域、变量提升
答案:
- 块级作用域
JS 中作用域有:全局作用域、函数作用域。没有块作用域的概念。ECMAScript 6(简称 ES6)中新增了块级作用域。块作用域由 { } 包括,if 语句和 for 语句里面的{ }也属于块作用域。
- 变量提升
- 如果变量声明在函数里面,则将变量声明提升到函数的开头
- 如果变量声明是一个全局变量,则将变量声明提升到全局作用域的开头
解析:
<script type="text/javascript">
{
var a = 1;
console.log(a); // 1
}
console.log(a); // 1
// 可见,通过var定义的变量可以跨块作用域访问到。
(function A() {
var b = 2;
console.log(b); // 2
})();
// console.log(b); // 报错,
// 可见,通过var定义的变量不能跨函数作用域访问到
if(true) {
var c = 3;
}
console.log(c); // 3
for(var i = 0; i < 4; i++) {
var d = 5;
};
console.log(i); // 4 (循环结束i已经是4,所以此处i为4)
console.log(d); // 5
// if语句和for语句中用var定义的变量可以在外面访问到,
// 可见,if语句和for语句属于块作用域,不属于函数作用域。
{
var a = 1;
let b = 2;
const c = 3;
{
console.log(a); // 1 子作用域可以访问到父作用域的变量
console.log(b); // 2 子作用域可以访问到父作用域的变量
console.log(c); // 3 子作用域可以访问到父作用域的变量
var aa = 11;
let bb = 22;
const cc = 33;
}
console.log(aa); // 11 // 可以跨块访问到子 块作用域 的变量
// console.log(bb); // 报错 bb is not defined
// console.log(cc); // 报错 cc is not defined
}
</script>
拓展:
var、let、const 的区别
- var 定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问。
- let 定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。
- const 用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改。
- 同一个变量只能使用一种方式声明,不然会报错
<script type="text/javascript">
// 块作用域
{
var a = 1;
let b = 2;
const c = 3;
// c = 4; // 报错
// let a = 'a'; // 报错 注:是上面 var a = 1; 那行报错
// var b = 'b'; // 报错:本行报错
// const a = 'a1'; // 报错 注:是上面 var a = 1; 那行报错
// let c = 'c'; // 报错:本行报错
var aa;
let bb;
// const cc; // 报错
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
console.log(aa); // undefined
console.log(bb); // undefined
}
console.log(a); // 1
// console.log(b); // 报错
// console.log(c); // 报错
// 函数作用域
(function A() {
var d = 5;
let e = 6;
const f = 7;
console.log(d); // 5
console.log(e); // 6 (在同一个{ }中,也属于同一个块,可以正常访问到)
console.log(f); // 7 (在同一个{ }中,也属于同一个块,可以正常访问到)
})();
// console.log(d); // 报错
// console.log(e); // 报错
// console.log(f); // 报错
</script>