细说 JS 中的变量和作用域
本文源自 JavaScript 学习 0402 课时
一、变量的类别
var
声明全局变量,允许提升,会在window
上 创建一个 新属性 。
// 初始化,未给 test1 赋值
var test1;
console.log(test1); //打印:undefined
// 赋值
var test1 = "赋值的RQ桑(test1)";
console.log(test1); //打印:赋值的RQ桑(test1)
// 用 var 更新,也可以用 js 的专有名词 “提升”
var test1 = "用var更新的RQ桑(test1)";
console.log(test1); //打印:用var更新的RQ桑(test1)
// 直接更新
test1 = "直接更新的RQ桑(test1)";
console.log(test1); //打印:直接更新的RQ桑(test1)
// var 声明的变量 会 在 window 上创建一个新属性。
console.log(window.test1); //打印:直接更新的RQ桑(test1)
注意!!
- 直接 声明一个值,事实上是对属性赋值操作,可以参考如下代码。
//使用直接赋值方式声明的是全局变量(全局对象属性)
test4 = "RQ桑(test4)";
console.log(test4); //打印:RQ桑(test4)
// 事实上是对属性赋值操作,可以在全局找到这个属性
console.log(window.test4); //打印:RQ桑(test4)
console.log(window); //在window下面找到刚才声明的 `test1` 和 `test4`;
//test4 在函数内部仍然是一个全局属性,可以全局访问
let
声明全局变量,不允许提升,不会在window
上创建一个新属性
// 初始化, 先不赋值
let test2;
console.log(test2); // 打印: undefined
// 赋值,提升是被禁止的
// let test2 = 'RQ桑(test2)'; // 语法错误,不 OK
// 变量允许更新
test2 = "更新后的RQ桑(test2)";
console.log(test2); //打印:更新后的RQ桑(test2)
// let 声明的变量 不会 在 window 上创建一个新属性。
console.log(window.test2); //undefined
const
声明一个常量,不允许提升,不会在window
上创建一个新属性,声明时必须赋值,不允许更改。
// const test2; //这种写法是不 ok 的,会报错
[!!报错信息:Identifier 'test2' has already been declared]
// 赋值
const test3 = "RQ桑(test3)";
console.log(test3); //打印:RQ桑(test3)
// const 声明的变量 不会 在 window 上创建一个新属性。
console.log(window.test3); //undefined
下面两种写法都会直接报错
// 不能用 const 提升
const test3 = "用const修改过的RQ桑(test3)";
// const 常量 不允许 更新RQ桑
test3 = "更新后的RQ桑(test3)";
!! 注意,使用 let
直接用 test(n) = '更新值'
是修改操作,不会在 window 上创建新属性。
var、let、const
三者的区别
不同点
-
用
var
定义的变量,会在window
上创建一个新属性。let
和const
不会。 -
const
声明 常量 前必须先赋值,且不支持修改。 -
let
声明的变量可以提升,但let
声明的变量不能提升,但两者都支持更新。
相同点
- 三者都是全局变量(常量),作用于全局、是默认的、不可删除。
二、函数的作用域
1. 函数中声明的变量为私有变量。
function demo1() {
//在函数内部可以访问到外部的全局变量
let test5 = "京东商城";
var test6 = "小米商城";
// 使用直接赋值方式声明的是全局变量(全局对象属性)。
test7 = "天猫商城"; // 隐式声明(为全局变量的一个属性)
return `网络购物平台:${test5}、 ${test6}、${test7}`;
}
console.log(demo1()); //打印:网络购物平台:京东商城、 小米商城、天猫商城
console.log(test5); //无法访问私有成员
console.log(test6); //无法访问私有成员
console.log(test7); //打印:天猫商城(全局成员)
2. 函数的作用域链
// 内部的变量 --> 到它的上一级作用域中去查找变量
let test8 = "全局的test8";
let test9 = "全局的test9";
function demo2() {
// 当函数体内返回对象在局部找不到时,会触发作用域链
// 这时函数内部可以访问到外部的全局变量
let test8 = "函数内的test8";
return `我是:${test8} \n 我是:${test9}`;
}
console.log(test8); // 全局的test8
console.log(test9); //全局的test9
console.log(demo2()); //test9找不到,所以找到他的`上一级`
// 我是:函数内的test8 我是:全局的test9
三、块作用域
这里先通过阅读技术博客:[Js 块级作用域],了解为什么要有作用域?
1. let 和 const 支持块作用域
{
let a = 1;
const B = "hello";
}
console.log(a); //外部访问不到
console.log(B); //外部访问不到
2. var:不支持块作用域
{
var m = 1;
}
console.log(m); //打印1
3. 块作用域的作用
在使用 for 循环或者 if 等条件判断语句时,可防止内部变量被污染,示例如下
let colors = ["red", "green", "js"];
for (let i = 0; i < colors.length; i++) {
console.log("%c%s", "color:violet", colors[i]); // 输出(样式为粉色) red green js
}
console.log(i); //报错:ReferenceError: i is not defined
let colors = ["red", "green", "js"];
for (var i = 0; i < colors.length; i++) {
console.log("%c%s", "color:violet", colors[i]); // 输出(样式为粉色) red green js
}
console.log(i); //输出 3