变量、数据类型(一)
变量声明
变量的声明有三种关键字let、const和var
var操作符
- 作用域
var
操作符定义的变量会成为包含它的函数的局部变量,变量在函数体内部申明,则变量的作用域为整个函数体
,在函数体外不可引用- 函数内省略var操作符定义的变量会被创建为一个全局变量(当然这是在非严格模式下,严格模式不允许不声明)
- 逗号分割变量及可选的初始化
var message = 'acs', password = 'ass',age;
- 可以反复声明,允许重复定义,后面的会被忽略
- 变量提升
- var关键字声明的变量会自动提升到函数作用域顶部
-
for循环渗透,for循环中用var声明的变量会泄露到for循环之外
-
可以被删除
let操作符
- 作用域
- 声明的作用域为块级作用域(块级作用域是函数作用域的子集)
- 不允许在一个块级作用域中出现冗余声明,不允许被重复定义,重复声明会爆出SyntaxError
- 暂时性死区
- 在let声明执行之前的瞬间被称为“暂时性死区”,在此阶段引用后面才声明的变量会抛出ReferenceError(在解析代码时,JavaScript引擎也会注意出现在块后面的let声明,只不过在此之前不能以任何方式来引用未声明的变量。)
-
不会进行变量提升
-
全局声明
- 全局声明的变量不会成为window对象的属性,var声明的会
- 条件声明
- 不能依赖条件声明模式
-
不允许被删除
-
for循环不会渗透
for (var i = 0 ; i < 5 ; i++) {
setTimeout(() = > {
console.log(i);
},2000);
} // 55555
for (let i = 0 ; i < 5 ; i++) {
setTimeout(() = > {
console.log(i);
},2000);
} // 01234
let和var
这两个关键字声明的并不是不同类型的变量,而是只是指出变量在相关作用域如何存在。
区别
- 作用域
- 变量提升和暂时性死区
- 全局声明
- for渗透
- 冗余声明
- 能否被删除
const
- 块级作用域、不会进行变量声明提升
- 初始化时赋值
- 声明的变量值不允许修改(对象的话,只是那个引用不能改)
- 使用const来定义常量,基本和let相同,唯一重要区别就是
声明变量的时候必须同时初始化变量
,尝试修改const声明
的变量(基本数据类型)会导致运行错误
- 若const声明的变量是
引用数据类型
的,那个变量只是一个指针,存储在栈内存,所指向的对象
存储在堆内存,故可以修改的
。 - 如果想要整个对象都不能修改,可以使用Object.freeze()
作用域总结
- 两个不同函数各自声明同一个变量,则其
只在各自的函数体内
起作用,互相独立,互不影响 - JS函数可以
嵌套
,此时内部函数可以访问外部函数定义的变量,反之不行,若内部和外部重名,则JS函数在查找变量时从自身函数定义
开始,由内向外查找,重名则屏蔽外面
的 - 变量提升,JS会先扫描整个函数体的语句,将所有var声明的
变量提升
到函数顶部;函数也存在变量提升,let声明的则无变量提升 - 全局作用域,JS默认有一个全局对象
window
(浏览器环境下),全局作用域的变量实际被绑定成window的一个属性,以变量方式定义的函数也是一个全局变量
var course = 'xxx'
alert(course);
alert(window.course);
//等价
- 全局变量会绑定到
window
上,不同的JS文件如果使用了相同的全局变量或者定义了相同名字
的顶层函数会造成命名冲突
,减少冲突的方法是把自己的所有变量和函数全部绑定到一个全局变量
中, - 由于JS的变量作用域实际上是
函数内部
,故在for循环
等语句块中是无法按定义具有局部作用域
的变量的,为了解决这个问题,ES6引入新的关键词let
来声明一个块级作用域变量
function () {
for (var i = 0 ; i < 100 ; i ++) {
}
i += 100;//仍然可以使用
}
function () {
for (let i = 0 ; i < 100 ; i ++ ){
}
i += 100; //错误
}
- ES6引入新的关键字
const
来定义常量,具有块级作用域
const PI = 3.14;
- 声明的风格及最佳实践,尽量使用let和const,不用var;其中const更好,const可以让给浏览器运行时保持变量不变,也可以让静态代码分析工具提前发现不合法的赋值操作。只有在知道变量会变的时候才用let
运算符
- '+'操作符
可以用来加法运算或者字符串的连接,如果用一个字符串加上一个数字,那么操作数都会首先转换为字符串 - 比较操作符
==
与===
不严格相等与严格相等,===
在比较之前会进行自动类型转换。
控制结构
- if…else(else if)
- while和do-while
- for循环
- for…of
for…of语句在可迭代对象
(包括Array、Map、Set、String(迭代其中的每一个字符)、TypedArray、arguments对象等)上创建一个的迭代循环
,通过调用自定义迭代钩子
为每个不同属性的值执行语句
// 语法
for (variable of iterable) {
//statements
}
// 迭代arguments
(function() {
for (let argument of arguments) {
console.log(argument);
}
})(1, 2, 3);
// 1
// 2
// 3
- for…in
for…in以任意顺序
迭代一个对象的除Symbol
以外的可枚举属性
,包括继承的可枚举属性
。forin是为了遍历对象属性而构建的,可以更方便的去检查对象属性。不建议与数组一起使用
- 可枚举属性是指那些内部"可枚举标志"设置为true的属性,通过直接的赋值和属性初始化的属性默认设置true,通过Object.defineProperty等定义的默认为false
- 属性的所有权是通过判断该属性是否直接属于某个对象决定的
- 三元操作符
- switch
for…of、for…in、hasOwnProperty、in
- for…of遍历的是可迭代对象,for…in遍历的是一个对象的可枚举属性,包括继承得到的,导致其遍历数组是得到的是下标索引,那个才是可枚举的
- hasOwnProperty可以确定某个属性是否是
对象本身的属性
或者使用getOwnPropertyNames(),所有继承了Object的对象都会继承到这个方法。需要注意的是,js并没有保护这个属性名
,当某一个对象可以自有一个占用
该属性名的属性时,我们需要使用外部的这个方法来获得正确的结果,例如是引用Object原型上的hasOwnProperty属性
,如查看foo对象上的bar属性是否是自身的属性,Object.property.hasOwnProperty.call(foo,'bar')
- in运算符,如果指定的属性在
指定的对象
或其原型链
中,则in运算符返回true,in右操作数必须是一个对象值
var trees = new Array("redwood", "bay", "cedar", "oak", "maple");
0 in trees // 返回true
6 in trees // 返回false
"bay" in trees // 返回false (必须使用索引号,而不是数组元素的值)
"length" in trees // 返回true (length是一个数组属性)
Symbol.iterator in trees // 返回true (数组可迭代,只在ES2015+上有效)