Skip to content

变量

var变量声明

变量用于存储数据值,JavaScript 可以使用 var 关键词来声明变量

声明之后,变量是没有值的(技术上,它的值是 undefined),我们可以在声明变量的同时进行赋值:var person = 'Bill Gates';(变量的声明一般都是直接包含声明和赋值)

可以在一条语句声明多个变量:声明也可以横跨多行,每行以逗号结尾:

js
var person = 'Bill Gates', carName = 'porsche';

重复声明的变量会继承之前的值,除非重新声明时又赋了新的值

变量声明的变量名,js会自动的通过内存地址将存储的内容进行拿取

js中,变量的声明是弱类型的,不需要指定变量是什么类型的,变量的类型是根据其值来发生改变的

变量提升

在代码执行之前,解析器会将全部代码先分析一遍,在执行的过程中,就会将变量声明进行提升

js
var web = 'baidu.com'
console.log(web)
var class = 1

上述代码会因为class是特殊字符不能作为变量命名,导致控制台报错,同时控制台不会执行前面的打印操作,是因为变量声明会被提升到前面,前面的内容报错了,后续的打印操作也就不会执行

变量声明是可以被提升的,但是变量赋值是不能被提升的

js
console.log(web)
var web = 'baidu.com'
// 结果打印的是undefined

上述的代码可以正常在控制台中进行打印,因为变量声明会被提升到前面,但是打印的结果是undefined,是因为变量赋值操作是不能被提升的

js
console.log(web)
var web
web = 'baidu.com'
// 结果打印的是undefined

如果某一行没有执行,但是这一行有变量声明,在解析时还是会将其变量声明进行提升

js
if(false){
    var web = 'baidu.com';
}
console.log(web)
// 结果打印的是undefined

letconst变量声明

  • let 声明的变量只在 let 命令所在的代码块内有效;

  • const 声明一个只读的常量,一旦声明,常量的值在同一个作用域中就不能改变,如:

    js
    const web = 'baidu.com';
    web = '百度';
    // 结果报错

    在不同的作用域const定义的变量值是可以重新声明赋值改变的:

    js
    const web = "baidu.com";
    function show() {
        const web = "百度";
    }

    结果不报错,严格意义上两个web都不是同一个变量了,因为在不同的作用域中

var变量声明是可以进行变量提升的,变量提升带来的不全是好处,如:某一行没有执行,但是这一行有变量声明,为了解决这个情况,就有了let变量声明,let变量声明没有变量提升,必须先声明再使用,否则会报错

js
let web = 'baidu.com';
console.log(web)

TDC暂时性死区

TDC暂时性死区也叫临时性死区(Temporal Dead Zone

letconst声明的变量不会进行变量的提升,如果在声明前使用就会报错,因为这个变量此时还存在与暂时性死区里面

js
let web = 'baidu.com';
function func(){
    console.log(web);
    let web = '123.com';
}
func();
// 结果报错

如果在一个局部的区域中使用了外部声明的变量,在块区域中也有声明语句,声明语句必须放到使用之前

js
function run(a = 3, b = a){}
run(); // 不报错

function run(a = b, b = 3){}
run(); // 报错

varletconst的共同点

  1. 局部空间是可以访问到外部的公共空间的,局部中可以调用(访问到)外部全局声明的变量

    js
    let/var/const web = 'baidu.com';
    function show() {
        consloe.log(web)
    }
    show()  // baidu.com
  2. 如果内部有该变量的重新声明,那么内部的调用内部声明的变量,外部调用外部声明的变量;如果内部不是声明,而是重新给变量赋值,那么改变的是这个全局变量(总而言之,在函数体内部声明变量后,这个函数体就是这个变量的私有领地,新开辟空间来存储数据,如果不声明,调用变量时,就会去上一级空间去进行查找)


变量的全局污染

js中变量的命名如果前面没有加变量的声明符是不会报错的:web = 'baidu.com' (尽量避免这种写法,这种写法会导致变量的全局污染)

当我们从外界调用这个变量时,如果这个变量使用上述不规则的写法:

js
// one.js
function show() {
    web = 'baidu.com';  // web变量污染到了全局
}
html
<!--two.html-->
<script src="one.js"></script>
<script>
    web = '百度';
    show();      // 变量污染到了全局
    console.log(web);  // 结果显示为baidu.com
</script>

为了变量不污染全局,一定要对变量进行声明,声明了变量之后,该变量只在其局部可以调用,不会污染全局


块作用域

JavaScript 中的作用域:

  • 全局作用域:在全局(函数之外)声明的变量有全局作用域,该变量可以在JavaScript程序中的任何位置访问

  • 函数作用域:在局部(函数内)声明的变量有局部作用域,该变量只能在它们被声明的函数内访问

  • 块作用域:在块{}内声明的变量无法从块外进行访问,块作用域可以理解为{}中的内容作用域

letconst关键词在 JavaScript 中提供了块作用域(Block Scope)变量(和常量);var是没有块作用域的

js
{ 
  var x = 10; 
}
// 此处可以使用 x

{ 
  let x = 10;
}
// 此处不可以使用 x

因为let变量声明是有块作用域的,不会对全局造成影响,后续都推荐使用let代替var进行变量声明

在块作用域内使用 const 声明与 let 声明变量类似,const不能更改常量原始值,但我们可以更改或添加常量对象的属性,但是无法重新为常量对象赋值

js
const PI = 3.141592653589793;
PI = PI + 10;   // 会出错
// 创建 const 对象:
const car = { type:'porsche', model:'911', color:'Black' };
// 可以更改属性:
car.color = 'White';
// 可以添加属性:
car.owner = 'Bill';
// 不能重新赋值,报错
car = { type:'Volvo', model:'XC60', color:'White' };

通过const创建的常数数组是可以更改其数组的元素的,同理也无法重新为常量数组进行赋值:

js
// 创建常量数组:
const cars = ['Audi', 'BMW', 'porsche'];
// 可以更改元素:
cars[0] = 'Honda';
  • 在同一作用域或块中,不允许将已有的 varlet 变量重新声明或重新赋值给 const

  • 在同一作用域或块中,为已有的 const 变量重新声明或赋值是不允许的

  • 在另外的作用域或块中重新声明 const 是允许的


循环作用域

在循环中使用的变量使用 var ,会重新声明了循环之外的变量:

js
var i = 7;
for (var i = 0; i < 10; i++) {
  // 一些语句
}
// 此处i为 10

在循环中使用的变量使用let,不会重新声明循环外的变量:

js
let i = 7;
for (let i = 0; i < 10; i++) {
  // 一些语句
}
// 此处i为 7

window全局对象

var声明变量时,就会将该对象保存到window全局对象当中,我们可以通过window.变量,读取到这个变量

js
var web = 'baidu.com';
console.log(window.web)
// 结果打印 baidu.com

window窗口对象有其特有的内置对象,如screenLeft,表示距离屏幕左侧的像素距离,如果按照上述进行声明变量,可能会导致其特有的内置对象失效,这是var声明变量可能会出现的问题,因为通过var声明变量,会被放到全局中,作为一个window的一个属性

但是通过let这样声明对象,是不会出现这样的问题,通过window窗口调用,返回的是undefined

HTML 中,全局作用域是window对象,通过 var 关键词定义的全局变量属于 window 对象,通过 let 关键词定义的全局变量不属于 window 对象

js
var carName = 'porsche';
// 此处的代码可使用 window.carName

let carName = 'porsche';
// 此处的代码不可使用 window.carName

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

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

console.log(this.var1);   // 1
console.log(window.var1); // 1
console.log(window.var2); // 2

delete var1; // false 无法删除
console.log(var1); // 1

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

变量的重复声明

在程序中的任何位置都允许重新声明 JavaScript var 变量,系统是不会进行报错的,其变量值是最后一次声明的值,之前声明的值会被覆盖掉

但是通过letconst在同一个作用域中声明变量,如果重复声明,系统会给出报错提示:Identifier 'web' has already been declared

  • 在相同的作用域/块中,通过 let 重新声明一个 var 变量是不允许的

  • 在相同的作用域/块中,通过 let 重新声明一个 let 变量是不允许的

  • 在相同的作用域/块中,通过 var 重新声明一个 let 变量是不允许的

  • 在不同的作用域/块中,通过 let 重新声明变量是允许的


变量冻结

我们知道通过const声明常量,其值是不能改变的,但是我们可以改变其属性的值,如果我们想要其属性的值都不能改变,我们就要通过变量冻结的方法来锁定变量,使其在后续过程中都不能发生改变

js
const HOST = {
    url: 'baidu.com',
    port: 8080
};
// HOST.url的值目前是可以进行改变的
Object.freeze(HOST)  // 变量冻结,之后HOST的所有属性值都不可发生改变

传值和传址

对于基本量,如数值,字符串,进行传值,系统会新开辟一块空间

js
let a = 1;
let b = a;

之后b发生变化,a不受到影响

对于比较大的空间,如对象,进行传址,会共用地址,不会开辟一块新空间

js
let a = { web: ’baidu.com‘ };
let b = a;  // 共用一个内存地址,当b的值发生改变时,会使a的值也发生改变
b.web = '123.com';
console.log(a, b)  // { web: '123.com' }, { web: '123.com' }

nullundefined

  • null表示有值

  • undefined表示没有值

当一个变量只是声明没有赋值时,返回的是undefined

对于变量,在不进行赋值时:

  • 如果想要该变量保持引用的类型,可以在初始的时候定义成null,如:let config = null,也可以用另外一种写方法:let config = {}

  • 如果想要该变量保持基本的类型,可以在初始的时候定义成undefined,如:let config = undefined,也可以用另外一种写方法:let config = ''

对于一个函数,如果函数没有返回值,就会返回undefined

Released under the MIT License.