关注前端小讴,浏览更多原创技术文章

变量

  • ECMAScript 变量是涣散类型的:变量能够保留任何类型的数据
  • 3 个申明变量的关键字:var、const、let

相干代码 →

var 关键字

  • 不初始化时,变量保留 undefined
var messageconsole.log(message) // undefined
  • 初始化变量只是设置变量的值,能够扭转保留的值,也能够扭转值的类型
var message = 'hi'message = 100 // 非法,但不举荐console.log(message) // 100

var 申明作用域

  • 应用 var 操作符定义的变量,会成为蕴含它的函数局部变量
  • 函数退出(调用)时,该变量被销毁
function test() {  var messageTest = 'hi' // 在函数外部创立变量并赋值}test() // 调用函数,外部变量被销毁console.log(messageTest) // ReferenceError: messageTest is not defined
  • 函数内定义变量时省略 var 操作符,能够创立一个全局变量
function test() {  messageTest = 'hi' // 省略var操作符,全局变量}test() // 调用函数,定义外部的全局变量console.log(messageTest) // 'hi
  • 不举荐在函数外部通过省略 var 关键字定义全局变量:

    • 部分作用域中定义的全局变量很难保护
    • 会造成困惑,无奈判定省略 var 是无意为之还是语法错误
    • 严格模式下,抛出 ReferenceError

var 申明晋升

  • 应用 var 申明的变量,(无论理论申明的地位在何处)会主动晋升到函数作用域顶部;如果申明不在函数内,则被晋升到全局作用域顶部
function foo() {  console.log(age)  var age = 30}foo() // undefined,不报错,变量申明晋升到函数作用域顶部console.log(age)var age = 30 // undefined,不报错,变量申明晋升到全局作用域顶部
  • 以上代码等价于:
var agefunction foo() {  var age  console.log(age)  age = 30}foo() // undefinedconsole.log(age) // undefinedage = 30
  • 可重复屡次应用 var 申明同一个变量
function fooAge() {  var age = 16  var age = 26  var age = 36  console.log(age)}fooAge() // 36,可重复屡次应用var申明同一个变量

let 申明

  • 与 var 作用差不多,但申明范畴是块作用域(var 是函数作用域)
if (true) {  var nameVar = 'Matt'  console.log(nameVar) // 'Matt'}console.log(nameVar) // 'Matt'if (true) {  let nameLet = 'Matt' // 作用域仅限块外部  console.log(nameLet) // 'Matt'}console.log(nameLet) // ReferenceError: nameLet is not defined
  • let 呈现过的同一个块作用域中, 不容许呈现冗余申明
var nameVarvar nameVar // var容许反复申明同一个变量let nameLetlet nameLet // Identifier 'nameLet' has already been declared,冗余申明let nameVar // Identifier 'nameVar' has already been declaredvar nameLet // Identifier 'nameLet' has already been declared
  • 若同一个块中没有反复申明,可嵌套应用雷同的标识符
let ageNest = 30console.log(ageNest) // 30if (true) {  let ageNest = 28  console.log(ageNest) // 28,在不同的块中}

暂时性死区

  • let 申明的变量不会在作用域中被晋升(var 能够晋升)
  • 在 let 申明之前的执行霎时被称为“暂时性死区”,此阶段援用任何候面才申明的变量都会抛出 ReferenceError
console.log(ageVarPromote) // undefinedvar ageVarPromote = 26console.log(ageLetPromote) // ReferenceError: ageLetPromote is not definedlet ageLetPromote = 26

全局申明

  • 在全局作用域中,let 申明的变量不会成为 window 对象的属性(var 申明的变量则会)
var nameVarWhole = 'Matt'console.log(window.nameVarWhole) // 'Matt0',vscode没有window对象,在浏览器印证let nameLetWhole = 'Matt'console.log(window.nameLetWhole) // undefined,vscode没有window对象,在浏览器中印证

条件申明

  • 对于 let 申明,不能依赖条件申明模式(条件申明是反模式,如果发现正在应用这个模式,则定有更好的代替形式)
// typeof操作符if (typeof nameLetCondition === 'undefined') {  let nameLetCondition = 'Matt' // 仅在块级作用域内  console.log(nameLetCondition) // 'Matt'}console.log(nameLetCondition) // ReferenceError: nameLetCondition is not defined// try/catch语句try {  console.log(nameLetCondition2)} catch (error) {  let nameLetCondition2 = 'Matt' // 仅在块级作用域内  console.log(nameLetCondition2) // 'Matt'}console.log(nameLetCondition2) // ReferenceError: nameLetCondition2 is not defined

for 循环中的 let 申明

  • let 申明迭代变量的作用域同样仅限于 for 循环块外部(var 会渗透到循环体内部)
for (var iVar = 0; iVar < 5; iVar++) {}console.log(iVar) // 5,循环体内部受影响for (let iLet = 0; iLet < 5; iLet++) {}// console.log(iLet) // ReferenceError: iLet is not defined
  • let 申明迭代变量,JS 引擎会为每个迭代循环申明新的迭代变量(var 保留的是导致循环退出的值)
// 应用var申明:退出循环时,迭代变量保留的是导致循环退出的值for (var iVarDelay = 0; iVarDelay < 5; iVarDelay++) {  // 超时逻辑在退出循环后执行,此时变量的值为5  setTimeout(() => {    console.log(iVarDelay) // 5、5、5、5、5  }, 0)}// 应用let申明:为每个迭代循环申明新的迭代变量for (let iLetDelay = 0; iLetDelay < 5; iLetDelay++) {  // 超时逻辑在退出循环后执行,变量值别离为每个新的迭代变量  setTimeout(() => {    console.log(iLetDelay) // 0、1、2、3、4  }, 0)}

const 申明

  • 与 let 行为基本相同,但申明时必须赋初始值
const ageConst // SyntaxError: Missing initializer in const declaration
  • 尝试批改 const 定义的变量,会报错(或者说定义的是常量)
const ageConst = 26ageConst = 28 // TypeError: Assignment to constant variable.
  • const 也不容许反复申明
const ageConst = 26const ageConst = 28 // SyntaxError: Identifier 'ageConst' has already been declared
  • const 申明的作用域也是块
if (true) {  const nameConst = 'Nicholas'}console.log(nameConst) // ReferenceError: nameConst is not defined
  • const 申明的限度只实用于指向的内存地址不得改变(指针):

    • 对于根本类型来说,不得批改值
    • 对于援用类型的对象来说,不得重写地址,但能够批改其外部属性
const person = {}console.log(person.name) // undefinedperson.name = 'Matt'console.log(person.name) // 'Matt',未重写地址,仅批改对象的外部属性person = { name: 'Matt' } // TypeError: Assignment to constant variable,重写地址
  • 不能用 const 申明迭代变量,因为迭代变量会自增
for (const index = 0; index < 5; index++) {} // TypeError: Assignment to constant variable.
  • 能够用 const 申明不会被批改的 for 循环变量,这对于 for-in 和 for-of 循环特地有意义
let i = 0for (const j = 7; i < 5; i++) {  console.log(j) // 7、7、7、7、7}for (const key in { a: 1, b: 2 }) {  console.log(key) // a、b}for (const value of 'Matt') {  console.log(value) // 'M'、'a'、't'、't'}

申明格调及最佳实际

  • 尽量不应用 var,只应用 let 和 const

    • 变量有了明确的作用域申明地位不变的值
  • 优先应用 const,let 次之

    • 让浏览器运行时强制放弃变量不变,让动态代码剖析工具提前发现不非法的赋值操作
    • 提前晓得会有批改再应用 let

总结 & 问点

操作符重写值作用域申明晋升在全局作用域中申明申明冗余条件申明模式for 循环中最佳实际
var能够函数作用域成为 window 对象的属性容许影响全局属性迭代变量保留的是导致循环退出的值尽量不必
let能够块作用域不成为 window 对象的属性不容许只影响块中属性每个迭代循环申明一个新的迭代变量次之
const不可,可批改对象外部属性块作用域不成为 window 对象的属性不容许只影响块中属性只能申明不会被批改的 for 循环变量首选
  • 如何了解“JS 的变量是涣散类型”的?JS 变量初始化后,能够扭转值的类型么?
  • 在函数中,用 var 操作符定义的变量,在调用函数后会怎么?若省略 var 操作符呢?
  • 变量晋升的含意是什么?为什么不举荐在函数中用省略 var 操作符的方法定义全局变量?
  • let 申明的范畴是什么?其在全局申明、条件申明和迭代循环时,有哪些特点?
  • 具体阐明 let 申明和 var 申明有哪些异同?
  • 能够批改用 const 申明的数组或对象的值或属性吗?为什么?
  • 具体阐明 let 申明和 const 申明有哪些异同?
  • 申明变量时,有哪些助于晋升生代码品质的最佳实际?