简单介绍变量
在js中变量是很灵活的,举个简单的例子:即便一开始赋值了number类型,后续还可以改成字符串。
引用《Javascript高级程序设计》的话是:javascript变量是松散类型的,而且变量不过就是特定时间点一个特定值的名称而已,变量的值和数据类型在脚本生命周期内可以改变
用代码表示就是:
let param = 'val';
setTimeout(() => {
param = 20;
}, 500);
当然为了规范开发,不推荐在开发过程中改变变量保存值的类型,现在的开发人员也会使用ts来让变量类型保持从一而终
数据类型
-
基础类型(也称为原始类型)
Undefined,Null,String,Number,Boolean,Symbol,BigInt其中
Symbol是ECMAScript6新增的数据类型,BigInt是ECMAScript2020新增数据类型 -
Object(对象) —— 一组数据和功能的集合
变量可以包含两种不同类型的数据
-
原始值
最简单的数据,即上文中基础类型的任意一种, 按值访问
-
引用值
多个值构成的对象(
Object),按引用访问
声明变量
-
var
var声明的范围是函数作用域function test() { var name = 'echo'; // 在函数内部声明变量 age = 12; // 未使用var定义,age就变成了全局变量;严格模式下报错 var job = 'Doctor'; } test(); var name = 'Global'; // 声明全局变量 console.log(name); // 'Global' console.log(age); // 12 console.log(job); // ReferenceError: job is not defined当函数退出时,函数的内部变量会被销毁(在闭包内引用内部变量时除外)
使用
var声明的变量会自动提升到函数作用域顶部,所以先使用后定义也不会报错使用
var声明变量时,由于声明会被提升,JavaScript引擎会自动将多余的声明在作用域顶部合并为一个声明(所以重复定义变量也不会报错) -
let
let声明的范围是块作用域,块作用域是函数作用域的子集
不允许同一个块作用域中出现冗余声明(
SyntaxError;标识符 properName 已经声明过了)let与var的另一个重要区别是,let声明的变量不会在作用域中被提升。在let声明之前的执行瞬间被称为 “暂时性死区”,所以不能先使用后声明,否则会抛出
ReferenceError使用
let在全局作用域中声明的变量不会成为window对象的属性(var声明的则会)经典使用场景之一 ——
for循环中的变量声明有问题的代码
for(var i = 0; i < 5; i++) { setTimeout(() => console.log(i), 0) }运行以上代码,我们期望的值是 0, 1, 2, 3, 4;可实际得到的却是 5, 5, 5, 5, 5。
那么如果我们把其中的
var改成let呢? 没错,期望的结果就会出现!这是因为使用
let之后,迭代变量的作用域仅限于for循环块内部,js引擎在后台为每个迭代循环声明一个新的迭代变量,每个setTimeout引用的都是不同的变量实例,所以能按期望输出。而使用
var,迭代变量保存的是导致循环退出的值:5,在之后执行超时逻辑时,所有的i都是同一个变量,就会导致同一个值输出5次。这种每次迭代声明一个独立变量实例的行为适用于所有风格的
for循环,包括for-in和for-of循环 -
const
与
let基本相同,唯一区别是它用来声明常量(声明时必须同时初始化变量,且变量不可修改)。const name = 'echo'; name = 'Jon'; // TypeErrorconst声明的限制只适用于它指向的变量的引用。说人话就是,如果const变量引用的是一个对象,那么这个对象内部可修改。const person = {}; person.name = 'echo'; // ok
-
最佳实践
有了
let和const,在开发中尽量避免使用var,这样有助于提升代码质量const优先,let次之 —— 使用const可以让静态代码分析工具提前发现不合法的赋值操作,很多开发者认为应该优先使用const,只在提前知道未来会修改的变量再使用let
理解作用域 & 词法作用域 & 作用域链
现在我们已经能成功创建变量了,那么然后呢?这些变量存储在哪里?程序需要的时候又试如何找到它们的呢?
那么接下来,理解作用域就成为下一个学习点了!