一:作用域
什么是作用域?
作用域(scope),程序设计概念, 通常来说,一段程序代码中所用到的名字并不总是有效/可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。
作用域的使用提高了程序逻辑的局部性,增强程序的可靠性,减少名字冲突。
对于对象而言(其他也是一样的),在main函数中,对象的作用域为他所在的最近的一对花括号内。在后花括号处 析构函数被调用; 全局的对象的作用域为声明之后的整个文件,析构函数在最后被调用。另外,临时产生的对象在使用完后立即会被析构。
作用域的目的是减少名字冲突。
常见的作用域:全局作用域(global/window)、函数作用域(function)、块状作用域({})、动态作用域(this)。
建议阅读:JavaScript作用域和作用域链
- 1.全局作用域
变量在函数或者代码块 {} 外定义,即为全局作用域。
注意:在函数内部或代码块中没有定义的变量实际上是作为 window/global 的属性存在,而不是全局变量。
- 2.函数作用域
在函数内部定义的变量,就是局部作用域。外部无法直接访问(可以借助 return 或者闭包)。
- 3.块状作用域(ES6新增)
{} 就是“块”,外部无法访问这个变量。
- 4.动态作用域
通俗的讲变量的作用域是在定义时决定而不是执行时决定,也就是说词法作用域取决于源码,通过静态分析就能确定,因此词法作用域也叫做静态作用域。 相反,只能在执行阶段才能决定变量的作用域,那就是动态作用域。
二:Let(ES6新增声明变量)
Let与var的区别特性:
- 1.let 声明的全局变量不是全局对象window的属性
let s = 99
console.log(window.s) // undefined
- 2.用let定义变量不允许重复声明
let a = 1
let a = 3
console.log(a) //Uncaught SyntaxError: Identifier 'a' has already been declared
-3. let声明的变量不存在变量提升
function foo() {
console.log(a1)
let a1 = 99
}
foo()
// Uncaught ReferenceError: Cannot access 'a1' before initialization
- 4.let声明的变量具有暂时性死区
在代码块内,使用 let 命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”.
function foo(m = n, n = 2) {
console.log(n, m)
}
foo()
// Uncaught ReferenceError: Cannot access 'n' before initialization
-5.let 声明的变量拥有块级作用域
{
let a2 = 1213
}
console.log(a2) // undefined
例:
for (var i = 0 ;i < 3; i++) {
console.log('循环内:' + i) //循环内:0 循环内:1 循环内:2
}
console.log('循环外:' + i) //循环外:3
for (let i = 0 ;i < 3; i++) {
console.log('循环内:' + i) //循环内:0 循环内:1 循环内:2
}
console.log('循环外:' + i) //Uncaught ReferenceError: i is not defined
三:Const(定义常量,必须初始化)
使用const声明的常量:
1.不属于顶层对象window
2.不允许重复声明
3.不存在变量提升
4.暂时性死区
5.块级作用域
注意:
- 1.基本数据类型存储在 栈内存 中,引用数据类型存储在 堆内存 中然后在栈内存中保存 引用地址 。
const a1 = {
name: 'xiaohu',
age: 34
}
a1.school = 'chongda'
console.log(a1)
// {name: "xiaohu", age: 34, school: "chongda"}
- 2.const 实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。
- 3.让对象或者数组这种引用数据类型也不被改变:Object.freeze() 只是浅层冻结,只会对最近一层的对象进行冻结,并不会对深层对象冻结。
四:解构赋值 Desctructuring
允许按照一定模式,从数组和对象中提取值,对变量进行赋值。
注意:解构赋值重点是在赋值,赋值的元素是要拷贝出来赋值给变量,赋值的元素本身是不会被改变的。
- 1.数组解构赋值
(1).赋值的元素不仅是数组,它可以是任意可遍历的对象
let [q,w,e,r] = "qwer" // ["q", "w", "e","r"]
let [one, two, three] = new Set([1, 2, 3])
console.log([one, two, three]) //[1, 2, 3]
- (2).被赋值的变量还可以是对象的属性,不局限于单纯的变量。
- (3).解构赋值在循环体中的应用,可以配合 entries 和 map 对象使用。
let man = {
name: 'Ben',
}
for (let [key, value] of Object.entries(man)) {
console.log(`${key}:${value}`) //name:Ben
}
let man = new Map()
man.set('name', 'Ben')
man.set('age', '99')
for (let [key, value] of man.entries()) {
console.log(`${key}:${value}`) // name:Ben, age:99
}
- (4).如果想忽略数组的某个元素对变量进行赋值,可以使用逗号来处理。
let [a, , d] = ['aaa', 'bbb', 'ccc', 'ddd']
console.log( d ) //ccc
- (5).rest 参数(只能放在被赋值变量的最后一个位置上)
let [a1, a2, ...rest] = ["A", "B", "C", "D"]
console.log(a1) // A
console.log(a2) // B
console.log(rest[0]) // C
console.log(rest[1]) //D
console.log(rest) //["C", "D"]
console.log(rest.length) //2
- (6).默认值 如果数组的内容少于变量的个数,并不会报错,没有分配到内容的变量会是 undefined,也可以给变量赋予默认值。
let [a1,a2] = []
console.log(a1) //undefined
console.log(a2) //undefined
let [b1="123",b2="234"] = ["111"]
console.log(b1) //111
console.log(b2) //234
- 2.对象解构赋值
- (1).基本用法
let options = {
sss: "Menu",
width: 1,
height: 2
}
let sss, width, height} = options
console.log(sss) // Menu
console.log(width) // 1
console.log(height) // 2
注意:在这个结构赋值的过程中,左侧的“模板”结构要与右侧的 Object 一致,但是属性的顺序无需一致。
(2).默认值
let options = {
sss: "Menu"
}
let {width = 1, height = 2, sss} = options
console.log(sss) // Menu
console.log(width) // 1
console.log(height) // 2
- (3).rest 运算符
let options = {
sss: "Menu",
height: 2,
width: 1
}
let {sss, ...rest} = options
console.log(rest.height) // 2
console.log(rest.width) // 1
- (4).嵌套对象 只要被赋值的结构和右侧赋值的元素一致就好了。
- 3.字符串解构赋值(和数组的解构相似)
let string = 'qwert'
let [a, b, c, d, e] = string
console.log(a, b, c, d, e) //q w e r t