变量提升
使用 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"}