JavaScript进阶
一、作用域&解构&箭头函数
1.作用域
作用域:规定了变量能够被访问的“范围”,离开这个“范围”将不能被访问到。可分为局部作用域和全局作用域。
1.1.局部作用域
局部作用域分为:函数作用域和块级作用域
函数作用域:函数作用域:1.只能在函数内部调用,函数外部无法访问 2.函数的参数也算函数的局部变量 3.不同函数内部声明的变量无法相互访问 4.函数执行完毕后函数内部的局部变量将会被清空。
function sum(num) {
let a = 100;
console.log(a,num); //100,10
}
sum(10)
console.log(a); // error:a is not defined
块作用域:在JavaScript中用{}包裹的叫代码块,代码块内部声明的变量将有可能被外部访问,不同代码块间的变量无法相互访问。
// 用let 和const 声明的变量会产生块级作用域,无法被外界访问
for (let i = 0; i < 3; i++) {
console.log(i); // 0,1,2
}
console.log(i); //error: i is not defined
// 用var声明的变量不会产生块级作用域,可以被外界访问到
for (var j = 0; j < 3; j++) {
console.log(j); //0,1,2
}
console.log(j); //3
1.2.全局作用域
const a = 1; //方1.在<script>或.js文件最外层声明的变量即为全局变量(推荐使用)
function fn() {
window.b = 2; //2.window对象动态添加属性(不推荐)
c = 3; // 3.未用任何关键字声明的变量视为全局变量
var d = 4;
}
fn()
console.log(a); //1
console.log(b);//2
console.log(c); //3
// 尽量减少全局变量,防止全局变量被污染
1-3.作用域链
作用域链本质上是底层变量的寻找机制。在函数执行时,会优先查找当前作用域变量,如果找不到则依次逐级查找父级作用域知道全局作用域。一层一层的作用域串联形成了作用域链。子作用域能访问父级作用域变量,父级作用域无法访问子作用域变量。
let a = 1;
let b = 2;
function f() {
let a= 1;
console.log(a);
function g() {
a = 3;
console.log(3);
}
g()
}
f()
1.4.垃圾回收机制
内存的生命周期
js中分配的内存中有以下声生命周期:
内存分配:当声明变量、函数或对象时,系统将为他们分配内存空间
内存使用: 内存读取,即使用变量、函数、对象
内存回收:使用完毕,由垃圾回收器自动回收不再使用的内存
说明: 1.全局变量一般不会被回收(页面关闭时回收) 2.局部变量的值,不再使用了将会被自动回收
内存泄漏: 当分配的内存由于某种原因未释放或无法释放时将产生内存泄漏
垃圾回收机制算法
**引用计数法:**跟着记录被引用的次数,被引用则加1,减少引用减1.当引用次数为0,则释放内存。弊端:嵌套引用,循环引用时不会被回收,导致内存泄漏(多数浏览器不在使用)
function fn() {
let obj1 = {}
let obj2 = {}
obj1.a = obj2;
obj2.a = obj1
}
fn()
标记清除法: 从根部(即js全局对象)出发定时扫描内存中的对象,凡是从根部到达的对象,都是需要使用的,那些无法从根部到达的对象将标记为不再使用,稍后将进行回收。
1-4.闭包
**闭包:**一个函数对周围状态的引用捆绑在一起,内部函数中访问到其外部函数的作用域。简单理解:闭包 = 内部函数+外部函数的变量(要被调用才能形成闭包)
**闭包作用:**1.封闭数据,实现数据私有化 2. 外部也可以访问到函数内部的变量
// 数据被污染
let a = 0
function Fun() {
a++
console.log(a);
}
Fun()//1
Fun() //2
a = 1000
Fun() //1001 // 数据不存在私有被污染
// 封闭数据
function cont() {
let i = 0
return function () {
i++
console.log(i);
}
}
let fn = cont();
fn() // 1
fn() //2 //外部可访问函数内部变量
i = 1000
fn() //3 //数据私有化,不会被污染
**闭包引起的问题:**内存泄漏,这里的i因被外部调用(从根出发有被引用,将不会被回收器回收),不会被销毁掉,造成内存泄
2.预解析
2-1.变量提升
**变量提升:**用var声明的变量将存在变量提升。提升流程:将var 定义的变量提升到作用域最前面,只提升变量声明不赋值
(function () {
console.log(a); //undefined :存在变量提升 只提升到当前作用域最前面,不赋值
var a = 0
})()
console.log(a); //error: a is not defined
2-2.函数提升
函数提升:当使用函数声明时存在函数提升(函数表达式不存在函数提升)
(function () {
console.log(fn); //function:函数提升,只提升到当前作用域最前面,不提升函数调用
function fn() {
console.log(1
}
fn() // 1
})()
console.log(fn); //error: fn is not defined
//函数声明 以function关键字开头的
function fn(){}
//函数表达式
let fn =- function(){}
2-3.函数的动态参数&剩余参数&展开运算符
//动态参数:arguments:是函数内部变量的伪数组变量集合,它包含了函数的所有实参(箭头函数没有arguments)
//伪数组:可以使用索引对数据进行操作;2、具有length(长度)属性;3、但是不能使用数组的方法,如push,pop等数组方法
function fn() {
console.log(arguments); //[1,2,3]
}
fn(1, 2, 3)
let f = () => {
console.log(1);
}
f()
//剩余参数: 将一个不定量的参数组成一个集合 ...组成,箭头函数也支持剩余参数
function fn(...arr) {
console.log(arr); // [1,2,3]
};
fn(1, 2, 3);
let f = (...arr) => {
console.log(arr); // [1,2,4]
}
f(1, 2, 4)
// 将一个数组集合展开
let arr = [1, 9, 62, 3, 14, 25, 6]
console.log(...arr); //1, 9,62, 3, 14, 25, 6
console.log(Math.max(...arr)); //利用展开运算符求最大值 max:62