让天下没有难学的js之JavaScript中变量的那些事

5次阅读

共计 5292 个字符,预计需要花费 14 分钟才能阅读完成。

本篇文章面向群体:入门级
难度等级:★☆☆☆☆
内容较多,建议点赞收藏后阅读

什么是变量

变量作为 js 中最常见也是我们最早接触的 js 知识点,相信大家都不陌生,变量几乎存在于所有的编程语言中,百度百科中对于变量的解释为 变数或变量,是指没有固定的值,可以改变的数。变量以非数字的符号来表达,一般用拉丁字母。 而 JavaScript 中的变量是松散类型(弱类型)的,所谓松散类型就是可以用来保存任何类型的数据,在声明变量时无需指定变量的类型。所以,当我们声明一个变量之后,可以存储任意类型的数据。

怎么去声明一个变量

变量名

在 js 中声明一个变量通常通过关键字加一个变量名的形式来声明一个变量,那对于变量名在 js 中是如何要求的呢?

  • 变量名必须以字母、下划线 (_) 或者美元符($)开头,后面可以跟字母、下划线、美元符或者数字
  • 变量名的长度不能超过 255 个字符
  • 变量名必须区分大小写
  • 变量名中间不可有空格换行符及其他标点符号
  • 不能使用脚本语言保留的关键字作为变量名,如 true、false、function 等(具体关键字列表可参考菜鸟教程 JavaScript 保留关键字)

除了这些 js 明文规定的变量命名规则之外,为了代码可读性更高,我们通常也对变量命名有行业通用的命名规范。

  • 尽量采用符合当前语义的单词进行命名,如agetime,避免使用无意义的字符组合,如aaabbb
  • 由于部分框架可能使用 $ 作为关键字,所以我们也应尽量避免使用 $ 作为变量名
  • 如果命名单词超过两个单词,尽量采用驼峰法命名,如userAgemyFirstName
  • 尽量避免使用中文作为变量名,尽管那样不会报错

声明变量

上面说了,js 声明变量的方法为关键字加一个变量名,说完了变量名,我们就来说一下声明变量的关键字,js 中声明变量的关键字有以下三种

var

var关键字是我们学习 js 最先接触也是早期声明变量最为常用的关键字,使用方法直接在 var 关键字后面直接跟变量名即可,如

var message = 'hello world'
console.log(message) // hello world

var 关键字特点

  • 可重复声明,但并没有什么卵用
  • 声明后的变量可修改
var message = 'hello'
message = 'world'
console.log(message) // world
  • 有初始值,每一个用 var 声明的变量初始值均为undefined
var message
console.log(message) // undefined
  • 可同时声明多个变量
var message, test, handleName
console.log(message) // undefined
console.log(test) // undefined
console.log(handleName) // undefined
  • var 关键字可以省略
message = 'hello'
console.log(message) // hello

// js 中如果省略 var 关键字,则会自动创建全局变量
function foo() {
    message = 'hello'
    console.log(message) // hello
}
foo()
console.log(message) // hello

// 如果使用,则会创建当前作用域的变量
function foo() {
    var message = 'hello'
    console.log(message) // hello
}
foo()
console.log(message) // message is not defined

<span style=”color:red”>*</span> 注意,在严格模式下不可省略关键字,否则会报错

let

let 关键字是 ES2015(ES6) 新增加的重要的 JavaScript 关键字,用法和 var 一样

let message = 'hello world'
console.log(message) // hello world

let 关键字与 var 不同的地方

  • 不可重复声明,已经声明过的变量再次声明会报错(在同一作用域内)
var message
let message // Identifier 'message' has already been declared

let test = 'test'
let test = 'hello' // Identifier 'test' has already been declared
  • let 所声明的变量,只在 let 命令所在的代码块内有效
{
  let a = 10;
  var b = 1;
}

a // ReferenceError: a is not defined.
b // 1

利用 let 的这一特性可以解决很经典的一个问题

for (var index = 0; index < 6; index++) {setTimeout(function() {console.log(index)
  }, 100)
}

这段代码的结果输出的是 6 个 6,这是因为这里的 index 是用 var 定义的,每一轮循环所用的 index 都是全局变量,所以等到 100 毫秒后执行的时候,index 已经循环完变成了 6,那么利用 let 仅在当前代码块有效的这个特性,就可以很好的解决这个问题

for (let index = 0; index < 6; index++) {setTimeout(function() {console.log(index)
  }, 100)
}
// 0 1 2 3 4 5

因为 let 仅在当前代码块有效,所以这里每一轮循环都相当于定义了一个新的 index,所以最后输出的时循环时候的值。你可能会问,如果每一轮循环的变量 i 都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量 i 时,就在上一轮循环的基础上进行计算。

const

const 关键字是 ES2015(ES6) 新增加的重要的 JavaScript 关键字,用法和 var 一样,只不过 const 生成的是一个或多个常量,在有些时候,我们定义的值不希望被覆盖或者修改,我们就可以用 const

const message = 'hello world'
console.log(message) // hello world

const 关键字 和 var 关键字不同的地方

  • 不可重复声明
  • 声明时必须初始化,否则会报错
const a // Missing initializer in const declaration
const b = 'test' // 正确
  • 声明的常量不可进行赋值修改
const a = 'hello'
a = 'world' // Uncaught TypeError: Assignment to constant variable.
其实 const 声明的常量并非严格意义上的常量,因为当我们用 const 定义一个常量的值为引用类型(下面会讲基本类型和引用类型)时候,虽然我们不能进行重新赋值,但我们可以修改引用类型的值。
const a = {name: '小明'}
a.name = '小豪'
console.log(a.name) // 小豪

变量的值:基本类型与引用类型

我们知道,js 中的值可以保存所有数据类型,那么 js 中的数据类型又都有什么呢?
JavaScript 中的数据类型有六种,其中有五种简单数据类型(也叫基本数据类型),还有一种复杂的数据类型——对象(Object),由于本文主要讲的是变量相关知识,所以对数据类型不深入讲解,知识大概说一下。

基本类型

五种基本的数据类型:Undefined、Null、Boolean、Number 和 String。这 5 种基本数据类型是按值访问的,因为可以操作保存在变量中的实际的值。我们就直接按正常思路来就行了。

var a = '小明'
var b = a
a = '小豪'
console.log(b) // '小明'

引用类型

由于对象的知识相对较多,后续会有专门的文章进行介绍总结。

如果有点基础同学都知道,原始类型存储的是值,对象类型存储的是地址(指针),
那么当我们定义一个变量的值为对象的时候,由于存储的实际是这个对象在内存中的地址,相当于我们在这里只是引用了这个对象,所以在对一些变量进行复制赋值和修改时候就会出现一些意想不到的事情,比如下面的代码

var a = {name: '小明'}
var b = a
b.name = '小豪'
console.log(a.name) // 小豪

在上面的例子中,我们把 a 赋值给 b,实际上只是把 a 所引用的对象地址赋值给了 b,这时候 a 和 b 其实指向的是一个对象,所以当我们修改其中任何一个的时候,都是在对同一对象进行修改

这样很好的解释了我们上面所说的 const 定义的常量可修改的问题,当我们用 const 定义的常量为对象时,其实我们在这个常量里保存的只是一个对象的地址,无论我们怎么修改这个对象,const 定义的常量里保存的地址是没有变化的,所以上面例子中对 const 定义的对象进行修改其实并没有违背 const 定义的变量不可修改的原则,只有我们给这个常量重新赋值一个新对象(也就是新地址)的时候,才会触发 const 定义的常量不可修改的规则。

变量作用域

作用域,我们这里为了理解,可以简单的理解为作用域就是变量可以生效及访问的地方。JavaScript 中分为全局作用域和局部作用域,全局作用域里的变量在所有的地方都可以访问,局部作用域只能在当前作用域被访问。

在一些类似于 c 语言的编程语言中,每一对花括号包裹的区域都有自己的作用域,我们称之为块状作用域,而在 JavaScript 中没有块级作用域(es6 之前),取而代之的是函数作用域,所以我们通常所说的局部作用域也就是函数作用域。

var a = "hello" // 全局作用域
function foo() {
    // 在此作用域可以访问到 a
    console.log(a) // 'hello'
    var b = 'world'
}
foo()
console.log(a) // 'hello'
// 由于变量 b 定义在函数 foo 内,所以在函数 foo 的作用域外访问不到
console.log(b) // b is not defined

在 js 中可以存在函数嵌套函数的情况,所以我们非常容易见到函数作用域嵌套的情况,这时候就会形成一条作用域链,在 js 中,当你使用一个变量时,JavaScript 引擎会首先在当前作用域内寻找,如果找不到,就会到上一层作用域寻找,如果一直找到顶层作用域(也就是全局作用域)还找不到时,就会报错。

ES2015(ES6) 新增加了 let 关键字,从而可以让我们在块级作用域(大括号)中声明变量。

变量提升

在 JavaScript 中,函数及变量的声明都将被提升到当前作用域的最顶部。

//var a   <------------------
console.log(a) // undefined  ↑
var a   //  ---------------->

在上面的例子中,我们虽然声明语句在打印语句的后面,但是我们打印 a 却并没有报错,就是因为这里的变量声明被提到了当前作用域的最上面,我们称之为变量提升。

在 JavaScript 中 var a = 'test' 其实是分为两步进行的,分别是变量声明 var a 和 变量赋值 a = 'test', 变量声明会被提前,但是变量赋值不会被提前,我们来看下面的例子

console.log(a) // undefined
var a = 'test'

// 上述代码的执行步骤可以用下面的代码理解

var a
console.log(a)
a = 'test'

结合上面的函数作用域,我们来看一下下面的例子

var a = 'test'
function foo() {console.log(a)
  var a = 'hello'
}
foo()

大家猜一下打印的结果是什么,没错,就是 undefined,我们这里定义了全局变量 a,又定义了函数局部变量 a,所以我们在执行函数 foo() 时,其实是找的函数 foo() 里的局部变量 a,局部变量 a 通过变量提升到了函数 foo() 的顶部,但是变量赋值却没有提升,所以最后打印结果为 undefined,上面的代码可以理解为下面这样

var a = 'test'
function foo() {
  var a
  console.log(a)
  a = 'hello'
}
foo()

需要注意的是 letconst 不存在变量提升,在你定义他们之前使用会报错。比较有意思的是,letconst的报错还不一样

console.log(test) // Cannot access 'test' before initialization
const test = 'hello'

console.log(message) // message is not defined
let message = 'hello'

// 如果我们在局部作用域里写,报错就一样了
function foo() {console.log(message) // Cannot access 'message' before initialization
  let message = 'world'
}
foo()

作者确实基础较差,写文章更多的目的是为了提高自己的能力,但是总结写下来难免有啰嗦和失误的地方,如果可以帮到别人当然十分开心,如果大家发现什么问题也欢迎随时提出,我也会持续的学习,不断的对文章新型修改,希望大家一起进步,加油

正文完
 0