JavaScript 变量

79 阅读9分钟

JavaScript变量是用于存储信息的"容器",变量储存在栈里面

<!DOCTYPE html>
<html>
  <body>

    <script>
      var x = 5;
      var y = 6;
      var z = x + y;
      document.write(x + "<br>");
      document.write(y + "<br>");
      document.write(z + "<br>");
    </script>

  </body>
</html>

在代数中,我们使用字母(比如 x)来保存值(比如 5), 通过上面的表达式 z=x+y,我们能够计算出 z 的值为 1, 在 JavaScript 中,这些字母被称为变量 image.png您可以把变量看做存储数据的容器

JavaScript 变量定义声明与赋值

与代数一样,JavaScript 变量可用于存放值(比如 x=5)和表达式(比如 z=x+y) 变量可以使用短名称(比如 x 和 y),也可以使用描述性更好的名称(比如 age, sum, totalvolume)

  • 变量必须以 字母开头
  • 变量也能以$ 和 _ 符号开头(不过我们不推荐这么做)
  • 变量名称对大小写敏感(y 和 Y 是不同的变量)
//使用 var 关键词来声明变量
var carname       //在计算机程序中,经常会声明无值的变量。未赋值的变量为undefined
carname = "Volvo" //变量初始化:使用等号把"Volvo" 赋值carname

//声明变量的同时进行初始化(赋值):
var carname = "Volvo"

//声明多个变量再赋值
var a, b, c;
a = 1, b = 2, c = 3;
a2 = a; b2 = b; c2 = b;


//一条语句,多个变量
var lastname = "Doe", age = 30, job = "carpenter"


//声明也可横跨多行
var lastname = "Doe",
    age = 30,
    job = "carpenter"



//一条语句中声明的多个不可以赋同一个值:
var x, y, z = 1  //x,y 为 undefined, z 为 1


/*重新声明 JavaScript 变量
如果重新声明 JavaScript 变量,该变量的值不会丢失
在以下两条语句执行后,变量 carname 的值依然是 "123":
*/
var carname = "123";
var carname;

JavaScript 变量种类

在JavaScript中,若按照变量作用域和变量生命周期来划分,可将变量分为全局变量和局部变量

全局变量

从作用域角度,全局变量位于作用域链的最顶端 从变量生命周期角度,全局变量生存期为整个程序生命周期,即直到程序结束,全局变量才销毁

var address = "Shanghai"  //全局变量
function GetUserInfo() {
  var userName = "Ken"  //userName局部变量
  return userName + "-" + address
}

console.log(GetUserInfo())  //Ken-Shanghai
console.log(address)  //Shanghai

局部变量

从作用域角度,局部变量位于特定的局部域,如特定的函数内部 从变量生命周期角度,局部变量声明周期只在其所处的特定作用域内,超出该作用域,就失效。只要函数运行完毕,本地变量就会被删除 如函数变量,只在函数内部有效, 所以只能在函数内部访问它(该变量的作用域是局部的)

function GetUserInfo() {       
   var userName = "Ken"           
   return userName  
}    

console.log(GetUserInfo())  //Ken
console.log(userName)       //报错:userName is not defined

JavaScript 作用域

在 JavaScript 中, 对象和函数同样也是变量, 作用域为可访问变量,对象,函数的集合 JavaScript 函数作用域: 作用域在函数内修改

JavaScript 局部作用域

变量在函数内声明,变量为局部作用域 局部变量:只能在函数内部访问

function myFunction() {
  var carName = "Volvo"; 
  console.log(carName); // 函数内可可以使用 carName 变量
}
myFunction() // 函数即使被调用后,外部不能使用carName变量,局部变量只能在局部使用
console.log(carName); //ReferenceError: carName is not defined

因为局部变量只作用于函数内,所以不同的函数可以使用相同名称的变量 局部变量生命周期 局部变量在函数开始执行时创建,函数执行完后局部变量会自动销毁

JavaScript 全局作用域

变量在函数外定义,即为全局变量 全局变量有 全局作用域: 网页中所有脚本和函数均可使用

var carName = " Volvo";
function myFunction() {
  console.log(carName) // 函数内可调用 carName 变量
}

console.log(carName); // 此处可使用 carName 变量
myFunction()

全局变量生命周期 在整个程序结束后销毁

如果变量在函数内没有声明(没有使用 var 关键字),该变量为全局变量。 以下实例中 carName 在函数内,但是为全局变量

// 此处可使用 carName 变量
 
function myFunction() {
    carName = "Volvo"; // 函数内可使用 carName 变量
}

HTML 中的全局变量

在 HTML 中, 全局变量是 window 对象: 所有数据变量都属于 window 对象

<script>
  function myFunction() {
    carName = "Volvo";
  }
  myFunction()
  console.log(carName); //此处可使用 window.carName
</script>

变量链式作用域及访问

JS中的变量作用域是通过this指针,从当前的作用域开始,从当前作用域由内向外查找,直到找到位置,这里分为几个逻辑: a.从当前作用域由内向外查找,若找到,就停止查找,否则,继续查找,直到查到window全局作用域为止,若任然未找到,则会出错,提示该变量未定义; b.当内部作用域变量名与外部作用域变量名相同时,内部作用域的覆盖外部作用域

var dateTime = '2018-09-16' 
function GetUserInfo() {
  var age = 120 
  var name = "Ken" 
  
  function Say() {
    var name = "老王" 
    var address = "shanghai" 
    console.log(address + "-" + name + "-" + age + "-" + dateTime) //shanghai-老王-120-2018-09-16
  }
  return Say() 
}

GetUserInfo()

来分析一下变量及其作用域:   如上图,有4个作用域,当函数执行如下语句时,发生如下过程:

console.log(address+"-"+name+"-"+age+"-"+dateTime)
  • 当前this环境作用域为4作用域
  • this指针寻找变量:addresss,name,age,dateTime,从当前作用域向外作用域逐层寻找,直到寻找到变量为止,若寻找到最外层作用域任然没找到,则会出错,提示该变量未声明
  • 当内外层变量相同时,内层变量覆盖外层变量,如4作用域的name覆盖3作用域的name

JavaScript 变量提升

变量提升:函数声明和变量声明总是会被解释器悄悄地被"提升"到方法体的最顶部

  • JavaScript 中,函数及变量的声明都将被提升到函数的最顶部
  • JavaScript 中,变量可以在使用后声明,也就是变量可以先使用再声明

JavaScript 初始化不会提升

var x = 5; // 初始化 x
console.log("x=%s, y=%s", x, y);  //输出 x=5, y=undefined
var y = 7; // 初始化 y

 为什么 y= undefined? 这是因为变量声明 (var y) 提升了,但是初始化(y = 7) 并不会提升,所以 y 变量是一个未定义的变量

hoisting(变量提升)

以下两个实例将获得相同的结果:

<!DOCTYPE html>
<html>
  <body>

    <p id="demo"></p>
    <script>
      x = 5  // 变量 x 设置为 5
      elem = document.getElementById("demo") // 查找元素 
      elem.innerHTML = x                     // 在元素中显示 x
      var x  // 声明 x, 因为上面使用了x=5,所以变量提升了
    </script>

  </body>
</html>
<!DOCTYPE html>
<html>
  <body>

    <p id="demo"></p>
    <script>
      var x; // 声明 x
      x = 5; // 变量 x 设置为 5
      elem = document.getElementById("demo"); // 查找元素 
      elem.innerHTML = x;                     // 在元素中显示 x
    </script>

  </body>
</html>

在头部声明你的变量

对于大多数程序员来说并不知道 JavaScript 变量提升 如果程序员不能很好的理解变量提升,他们写的程序就容易出现一些问题 为了避免这些问题,通常我们在每个作用域开始前声明这些变量,这也是正常的 JavaScript 解析步骤 ⚠️**注意: JavaScript"严格模式(strict mode)" **不允许使用未声明的变量

拓展

省略声明关键字变量初始化

declareGlobalParam = 1           //无论在哪里定义,不用var定义的都是全局变量
console.log(declareGlobalParam)  //1

function GlobalParam() {
  a = 1, b = 2, c = 3
  var x = 1, y = 2, z = 3
}

GlobalParam()
console.log(b, c)     //2 3
console.log(y, z)     //y is not defined

非规范化定义全局变量(不推荐)

function GlobalParam() {
  a = 1, b = 2, c = 3
}

GlobalParam()
console.log(a, b, c) //1 2 3

如上,我们只需调用GlobalParam()函数一次,userName变量就有了定义,就可以在函数外部访问它了 如下定义,是访问不了的。因为未调用函数GlobalParam(),从而变量userName就没定义

function GlobalParam() {
  a = 1, b = 2, c =3 
}

console.log(a, b, c) //报错: a is not defined

尽管可以这样定义全局变量,但是不推荐这种方式 (1) 在局部作用域中定义的变量,很难维护 (2) 由于变量必须先声明,再使用,若忽略var关键字,导致变量不会立刻有定义,而导致错误,如上代码即可证明 (3) 在严格模式下,会出现错误ReferenceError 错误 (4) 可能出现局部变量修改全局变量危险。看看如下例子

var scope = "Global";//全局变量

function setParam() {
  scope = "Local";//局部变量(导致局部变量修改全局变量危险)
  return scope;
}
console.log(scope);//Global

setParam();
console.log(scope); //Local

JavaScript中没有块级作用域

在后端语言中,我们非常熟悉if和for语句,我们先来看看如下C语言、Go语言的for语句:

#include <stdio.h>

int main(int argc, char const *argv[])
{
  int result = 0;
  for (int i = 0; i < 10; i++){
    result = result + i;
    printf("i=%d result=%d\n", i, result);
  }
}

/* 
i=0 result=0
i=1 result=1
i=2 result=3
i=3 result=6
i=4 result=10
i=5 result=15
i=6 result=21
i=7 result=28
i=8 result=36
i=9 result=45
*/
package main
import "fmt"

func main() {
  var result int = 0;
  for  i  := 0; i < 10; i++{
    result = result + i;
    fmt.Printf("i=%d result=%d\n", i, result);
	}
    fmt.Println(result);
}

/* 
i=0 result=0
i=1 result=1
i=2 result=3
i=3 result=6
i=4 result=10
i=5 result=15
i=6 result=21
i=7 result=28
i=8 result=36
i=9 result=45
45
*/

其他语言里,在for语句中定义了两个变量i和result,这两个变量只能在for语句有效,不能在for语句外访问。同样地,在if语句中的变量也只能在if内部访问,外部不能访问

if (true) {
  string  userName = "Ken";
}

在条件语句、循环语句中(if for while do...while switch等)均是相同的,它们内部的变量,只能在其内部访问,不能在其外部访问,我们将其称之为“块级作用域”

然而,在JavaScript中,是没有块级作用域的

var maxNum = 100
for (var i = 0; i < maxNum; i++) {
  var sum = 0
  sum = sum + i
}
console.log(sum) //99。因为JavaScript没有块级作用域,因此能在for语句外部访问变量sum

在TypeScript中使用变量 let const 可以规避JavaScript的块级作用域的缺陷。更多请查看

匈牙利命名风格

类型命名方法例子
Object对象ooDiv
Array数组aaItems
Boolean布尔值bbIsComplete
String字符串ssUserName
Integer整数iiItemCount
Float浮点数ffPrice
Function函数fnfnHandler
RegExp正则表达式rereEmailCheck

常见考试题

请分析如下例子的结果

<script>
var address = "Shanghai"         //全局变量

  function GetUserInfo() {
    var userName = "Ken"  //userName局部变量
    // this.userName = "Ken" //注释这样行会导致 console.log(window.userName)为undefined
    return userName + "-" + address
  }
  console.log(GetUserInfo())    //Ken-Shanghai
  console.log(address)          //Shanghai
  console.log(window.address)   //Shanghai
  console.log(window.userName)  //undefined
  console.log(userName)         //报错:userName is not defined
</script>

向未声明的 JavaScript 变量分配值

如果您把值赋给尚未声明的变量,该变量将被自动作为 window 的一个属性。

carname="Volvo";

将声明 window 的一个属性 carname 非严格模式下给未声明变量赋值创建的全局变量,是全局对象的可配置属性,可以删除

var var1 = 1 // 不可配置全局属性
var2 = 2     // 没有使用 var 声明,可配置全局属性

console.log(var1)        // 1
console.log(this.var1)   //〖web浏览器中〗1 【node中】undefined

console.log(global.var1) // 1 〖web浏览器中〗用windows:1 【node中】用global:undefined
console.log(global.var2) // 2 〖web浏览器中〗用windows:2 【node中】用global:2


console.log(delete var1) // false 无法删除
console.log(delete var2) // true
// console.log(var2)        // 已经删除 报错变量未定义