JavaScript变量常见问题

变量提升

使用 var 声明代码会被提升到前面

console.log(a); //undefined
var a = 1;
console.log(a);  //1
//刷新一下网站,以上代码解析器执行过程如下
var a;
console.log(a); //undefined
a = 1;
console.log(a); //1

下面是 if(false) 中定义的var也会发生变量提升,注释掉if 结果会不同

var web = "ttw0";
function tt() {
  if (false) {
    var web = "Titus";
  }
  console.log(web);//undefined
}
tt();
/*
  该代码解析过程如下,注意if后面是false,此时变量被被提升后,
  没有在if中赋值,故function作用域中的web为undefined
*/
var web = "ttw0";
function tt() {
	var web;
	if (false) {
		web = "Titus";
	}
	console.log(web);
}
tt();

使用 var 定义的代码,声明会被提升到前面,赋值还在原位置

console.log(tt);
var hd = '山雀';
//以上代码解析器执行过程如下
var tt;
console.log(tt); //山雀
tt = '山雀';

块级作用域

var/let/const共同点是全局作用域中定义的变量,可以在函数中使用。函数中声明的变量,只能在函数及其子函数中使用。但也有不同点,对于

var

没有块作用域很容易污染全局,下面函数中的变量污染了全局环境

function run() {
  web = "Titus";
}
run();
console.log(web); //Titus

没有块作用作用域时var也会污染全局

for (var i = 0; i < 10; i++) {
  console.log(i);
}
console.log(i);

下例中体验到 var 没有块作用域概念, do/while 定义的变量可以在块外部访问到

var num = 0;

function show() {
  var step = 10;
  do {
    var res = 0;
    console.log(num = step++);
    res = num;
  } while (step < 20);
  console.log(`结果是${res}`);
}
show();

var 全局声明的变量也存在于 window对象中

var tt = "Titus";
console.log(window.tt); //Titus

以往没有块作用域时使用立即执行函数模拟块作用域

(function() {
  var $ = this.$ = {};
  $.web = "Titus";
}.bind(window)());

console.log($.web);

有了块作用域后实现就变得简单多了

{
  let $ = (window.$ = {});
  $.web = "Titus";
}
console.log($.web);

有一道面试题,

for (var i = 0; i < 5; i++) {
    setTimeout(function() {
        console.log(new Date, i);
    }, 1000);
}

console.log(new Date, i);
/*
作者:王仕军
链接:https://juejin.im/post/58f1fa6a44d904006cf25d22
来源:掘金
*/

其结果为

Wed Mar 04 2020 14:20:58 GMT+0800 (中国标准时间) 5

Wed Mar 04 2020 14:20:59 GMT+0800 (中国标准时间) 5
Wed Mar 04 2020 14:20:59 GMT+0800 (中国标准时间) 5
Wed Mar 04 2020 14:20:59 GMT+0800 (中国标准时间) 5
Wed Mar 04 2020 14:20:59 GMT+0800 (中国标准时间) 5
Wed Mar 04 2020 14:20:59 GMT+0800 (中国标准时间) 5

这就是for中的var污染全局变量的例子,至于为什么计时器中的i不是01234,这就涉及到了任务队列,setTimeout执行了以后,会将其中的callback放在任务队列中,然后主线程继续向下执行,当执行到最下面的输出函数时,i = 5,然后执行完以后,任务队列的callback放回主函数,由于之前跳出了循环中,其中的i值已经在全局中值为5,故输出5。

另外时间呢?由于五次循环中的计时器速度都很快,故输出时并不是每一个间隔一秒5 -> 5 -> 5 -> 5 -> 5,而是间隔一秒后一次性输出。
这道题在之后的函数专题中会再次提到。

对window对象全局变量的污染

如果var全局声明的变量和window的成员同名,则会污染其值。

console.log(window.screenLeft);//返回浏览器左边框离屏幕左边的距离

//定于screenLeft全局变量
var screenLeft = 250;
//那么window.screenLeft的值就都是250,不会变化了

let

  • 与 var 声明的区别是 let/const 拥有块作用域
  • 建议将let在代码块前声明
  • 用逗号分隔定义多个
let i = 100;
for (let i = 0; i < 6; i++) {
  console.log(i);//123456
}
console.log(i);//100

let存在块作用域特性,变量只在块域中有效,块内部是可以访问到上层作用域的变量。

每一层都是独立作用域,里层作用域可以声明外层作用域同名变量,但不会改变外层变量

function run() {
  tt = "Titus";
  sayHi = "Hi";
  if (true) {
    let tt = "ttw0?";
    console.log(tt); //ttw0?
    console.log(sayHi); //Hi
  }
  console.log(tt); //Titus
}
run();

const

使用 const 用来声明常量,这与其他语言差别不大,比如可以用来声明后台接口的URI地址。

  • 常量名建议全部大写
  • 只能声明一次变量
  • 声明时必须同时赋值
  • 不允许再次全新赋值
  • 可以修改引用类型变量的值
  • 拥有块、函数、全局作用域
try {
  const URL = "https://www.google.com";
  URL = "https://www.bing.com"; //产生错误
} catch (error) {
  throw new Error(error);
}

改变常量的复杂类型值

const INFO = {
  url: 'https://www.google.com',
  port: '8080'
};
INFO.port = '443';
console.log(INFO);

Object.freeze()可以用于冻结变量,冻结变量后,变量也不可以修改了

"use strict"
const INFO = {
  url: 'https://www.houdunren.com',
  port: '8080'
};
Object.freeze(INFO);
INFO.port = '443'; //Cannot assign to read only property
console.log(INFO);

在不同作用域中可以重名定义常量

const NAME = '山雀';

function show() {
  const NAME = 'Titus';
  return NAME;
}
console.log(show());
console.log(NAME);

let和const的TDZ临时性锁区

正是由于我们要避免变量提升,let和const就不会有此特性,要是使用了一个被let和const后声明的变量,就会报错

console.log(x); // Cannot access 'x' before initialization
let x = 1;

下面代码b没有声明赋值不允许直接使用

function tt(a = b, b = 3) {}
tt(); //Cannot access 'b' before initialization

undefined和null

undefined

未赋值与未定义的变量值都为 undefined

let tt;
console.log(typeof tt);//undefined
console.log(typeof td);//undefined

null

null 用于定义一个空对象,即如果变量要用来保存复杂类型,可以在初始化时将其设置为null

var tt = null;
console.log(typeof tt);

基本类型的传值,与复杂类型的传地址

基本类型复制是值的复制,互相不受影响。下例中将a变量的值赋值给b变量后,因为基本类型变量是独立的所以a的改变不会影响b变量的值。

let a = 100;
let b = a;
a = 200;
console.log(b);//这个b是有自己独自的内存地址的

复杂类型内容就像被存储在一个开辟的堆空间里一样,而复杂类型变量保存的是指向内容的地址(指针)。变量间赋值时其实赋值是变量的指针,这样多个变量就指向了同一个对象。

let a = {
  web: "Titus"
};
let b = a;
a.web = "oho";
console.log(b);//{web: "oho"}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值