前言
为了准备秋招,开始了算法刷题之路,痛苦的刷了十几道后,惊觉自己还没有系统地、扎实地学习JavaScript的数据结构,虽然通过刷题也能刺激学习,但总归东一榔头西一棒槌,不成体系,且基础知识结构都没掌握好,耗时必然久且痛苦。故下定决心先把《学习JavaScript数据结构与算法》该书通读一遍,再来刷题巩固。此乃系列的第一篇:基础知识。
变量
变量类型
- ES5的5种:Null,undefined,Boolean,Number,String, ES6新增:Symbol 表示独一无二的值 。ES10新增:BigInt 表示任意大的整数
- 一种引用数据类型:(本质上是由一组无序的键值对组成):
引用数据类型:Object。包含Object、Array、 function、Date、RegExp。 JavaScript不支持创建任何自定义类型的数据,也就是说JavaScript中所有值的类型都是上面8中之一。
如何定义变量
JavaScript并不是一种强类型语言。在强类型语言中,声明变量时需要指定变量的类型(例如,在Java中声明一个整型变量,要使用intnum = 1;)。在JavaScript中,我们只需要使用关键字var、let、const,而不必指定变量类型。(如果要对变量设定类型,见TypeScript)
其中,let 和 const 关键字是在 ES6 中才新增的。
let和var的区别
1.作用域
用 var 声明的变量的作用域是它当前的执行上下文,即如果是在任何函数外面,则是全局执行上下文,如果在函数里面,则是当前函数执行上下文。换句话说,var 声明的变量的作用域只能是全局或者整个函数块的。
而 let 声明的变量的作用域则是它当前所处代码块,即它的作用域既可以是全局或者整个函数块,也可以是 if、while、switch等用{}限定的代码块。
let 声明的变量的作用域可以比 var 声明的变量的作用域有更小的限定范围,更具灵活
function varTest() {
var a = 1;
{
var a = 2; // 函数块中,同一个变量
console.log(a); // 2
}
console.log(a); // 2
}
function letTest() {
let a = 1;
{
let a = 2; // 代码块中,新的变量
console.log(a); // 2
}
console.log(a); // 1
}
varTest();
letTest();
即在块作用域中两者的区别较为明显, let只在for()循环中可用,而 var是对于包围for循环的整个函数可用:
function aFun1(){
// i 对于for循环外的范围是不可见的(i is not defined)
for(let i = 1; i<5; i++){
// i只有在这里是可见的
}
// i 对于for循环外的范围是不可见的(i is not defined)
}
function aFun2(){
// i 对于for循环外的范围是可见的
for(var i = 1;i<5; i++){
// i 在for 在整个函数体内都是可见的
}
// i 对于for循环外的范围是可见的
}
2.重复声明
var 允许在同一作用域中重复声明,而 let 不允许在同一作用域中重复声明,否则将抛出异常。
var:
var a = 1;
var a = 2;
console.log(a) // 2
function test() {
var a = 3;
var a = 4;
console.log(a) // 4
}
test()
let:
if(false) {
let a = 1;
let a = 2; // SyntaxError: Identifier 'a' has already been declared
}
3.绑定全局对象 this
var 在全局环境声明变量,会在全局对象里新建一个属性,而 let 在全局环境声明变量,则不会在全局对象里新建一个属性。
var foo = 'global'
let bar = 'global'
console.log(this.foo) // global
console.log(this.bar) // undefined
4.变量提升与暂存死区
var 声明变量存在变量提升
定义:变量提升是当栈内存作用域形成时,JS代码执行前,浏览器会将带有var, function关键字的变量提前进行声明 declare(值默认就是 undefined),定义 defined(就是赋值操作), 这种预先处理的机制就叫做变量提升机制也叫预定义。
在变量提升阶段:带 var 的只声明还没有被定义,带 function 的已经声明和定义。所以在代码执行前有带 var 的就提前声明,比如这里的 a 就赋值成 undefined,在代码执行过程中遇到创建函数的代码浏览器会直接跳过。
console.log(a) // undefined
var a = 1;
console.log(a) // 1
使用是在执行阶段,而在此之前的创建阶段就已经将声明的变量添加到了变量对象中,所以执行阶段通过标识符可以在变量对象中查找到,也就不会报错
let 声明变量存在暂存死区,ES6 规定了其初始化过程是在执行上下文的执行阶段(即直到它们的定义被执行时才初始化),使用未被初始化的变量将会报错。
在变量初始化前访问该变量会导致 ReferenceError,因此从进入作用域创建变量,到变量开始可被访问的一段时间(过程),就称为暂存死区(Temporal Dead Zone)。
console.log(bar); // undefined
console.log(foo); // ReferenceError: foo is not defined
var bar = 1;
let foo = 2;
var foo = 33;
{
let foo = (foo + 55); // ReferenceError: foo is not defined
}
小结
1.var 声明的变量在执行上下文创建阶段就会被**「创建」和「初始化」,因此对于执行阶段来说,可以在声明之前使用**。
2.let 声明的变量在执行上下文创建阶段只会被**「创建」而不会被「初始化」,因此对于执行阶段来说**,如果在其定义执行前使用,相当于使用了未被初始化的变量,会报错。
const
const 声明的是一个只读变量,声明之后不允许改变其值.其实 const 其实保证的不是变量的值不变,而是保证变量指向的内存地址所保存的数据不允许改动(即栈内存在的值和地址)
const 只能保证指针是不可修改的,至于指针指向的数据结构是无法保证其不能被修改的(在堆中):
const obj = {
value: 1
}
obj.value = 2
console.log(obj) // { value: 2 }
obj = {} // TypeError: Assignment to constant variable
.
.
运算符
JavaScript包含算术运算符、赋值运算符、比较运算符、逻辑运算符、位运算符、一元运算符和其他运算符。
-
算术运算符
-
赋值运算符
-
比较运算符
-
逻辑运算符
-
位运算符
JavaScript还支持delete运算符,可以删除对象里的属性
var myObj = {name: 'John', age: 21};
delete myObj.age;
console.log(myObj); // 输出对象{name: "John"}
.
总结
在JavaScript中应该尽量少用全局变量,即var来定义。代码质量可以用全局变量和函数的数量来考量(数量越多越糟)。