作用域
作用域规定了变量能够被访问的 ‘范围’,离开了这个范围变量便不能被访问
分为:
- 局部作用域
- 函数作用域
- 块级作用域 let/const
- 全局作用域
作用域链
嵌套关系的作用域串联起来形成了作用域链
作用:作用域链本质上是底层的变量的查找机制
- 函数被执行时,会优先查找当前函数作用域中的变量
- 如果当前作用域中查找不到会逐级向上查找父级作用域直到全局作用域
- 子作用域能够访问父作用域,父作用域无法访问子作用域
// 全局作用域
let a = 11
let b = 22
// 局部作用域
function fn() {
let a = 1
function g() {
a = 2
console.log(a) // 2
console.log(b) // 22
console.log(c) // c is not defined
}
g()
}
fn()
垃圾回收机制[面试题]
垃圾回收机制简称 GC(Garbage Collection)
JS中内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾回收机制自动回收
// 给变量分配内存
const age = 18;
// 给对象分配内存
const obj = {
age:12
}
// 给函数分配内存
function fn(){
const age = 13;
console.log(age);
}
内存的生命周期
js环境中分配的内存,有如下生命周期:
- 内存分配 :声明变量,函数,对象的时候,系统会自动为他们分配内存
- 内存使用: 既读写内存,使用变量 ,函数等
- 内存回收: 使用完毕,由内存回收器 自动回收不再使用的内存
说明:
- 全局变量一般不会回收(关闭页面回收)
- 一般情况下局部变量的值,不用了,会被自动回收掉
内存泄漏:程序中分配的内存由于某种原因程序未释放或无法释放 叫做内存泄漏
算法说明:
堆栈空间分配区别
1.栈:由操作系统自动分配释放函数的参数值,局部变量等,基本数据类型放到栈立
2.堆:一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收,复杂数据类型放到堆里
-
引用计数法
’内存不再使用‘ 就是看一个对象是否指向它的引用,没有引用了就回收该对象
- 跟踪记录被引用的次数
- 如果被引用了一次,那么就记录次数为1,多次引用会累加 ++
- 如果减少一个引用就减1
- 如果引用次数为0 则释放内存
let arr =[1,2,3,4] arr = null let person = { age:18, name:"佩奇" } let p = person person = 1 p = null
但它存在一个致命的问题, 嵌套引用
如果两个对象相互引用,尽管他们已不再使用,垃圾回收器不会进行回收,导致内存泄漏
// o1.a = o2 o2.a=o1 他们的引用次数永远不会是0 相互引用---导致内存泄漏 function fn(){ let o1={} let o2= {} o1.a = o2 o2.a = o1 return "引用计数无法回收" }
-
标记清除法
现代的浏览器已经不再使用引用计数法了,使用标记清除法
核心:
- 标记清除法 将 ’不再使用的对象‘ 定义为 无法达到的对象
- 就是从根部(js中就是全局对象) 出发 定时扫描内存中的对象,凡是能从根部到达的对象,都是还需要使用的
- 那些无法由根部触发触及到的对象被标记为不再使用,进行回收
function fn(){ let o1={} let o2= {} o1.a = o2 o2.a = o1 return "引用计数无法回收" }
闭包
闭包可以让开发者从内部函数访问外部函数的作用域
简单理解: 闭包 = 内层函数+外层函数的变量
作用: 实现数据的私有,避免全局污染,外部也可以访问里层函数的变量
// 闭包写法--实现了数据私用
function outer(){
let count = 1
function fn1(){
count++
console.log(`函数被调用${count}次`)
}
return fn1
}
result =outer()
result()
闭包很有用,它允许将函数与其所操作的某些数据关联起来
闭包可能引起 内存泄漏
创建对象三种方式
1.对象字面量创建对象
const o = {
name:"佩奇"
}
2.new Object创建对象
const o = new Object({name:"佩奇"})
3.利用构造函数创建对象
构造函数:是一种特殊的函数,主要用来创建对象(初始化对象)
使用场景:
{…}语法允许创建一个对象,继续创建相同类的对象还需要重新写一次,此时可以通过构造函数来创建多个类似的对象
构造函数在技术上是常规函数,有两个约定
- 命名以大写字母开头
- 通过new 关键字来调用构造函数,可以创建对象
// 构造函数 是一种特殊的函数,用来创建对象
// - 命名以**大写字母**开头
// - 通过**new** 关键字来调用构造函数,可以创建对象
function Person() {
}
console.log(new Person())
function Pig(name, age, gender) {
// this.name 属性
// name 是形参 也是属性值
// this指向创建的对象
this.name = name
this.age = age
this.gender = gender
// 不写return 默认返回创建的对象
}
let p1 = new Pig('佩奇', 6, '女')
console.log(p1)
let p2 = new Pig('乔治', 3, '男')
console.log(p2)
// 构造函数创建对象说明
// 1.new关键字 实例化对象--创建对象
// 2.如果构造函数没有参数,则可以省略小括号
// 3.构造函数里无需写return
// new Object() new Date() 都是在实例化对象
构造函数实例化执行过程
function Pig(name, age, gender) {
this.name = name
this.age = age
this.gender = gender
}
const peiqi = new Pig('佩奇', 6, '女')
console.log(peiqi);
/*
new实例化执行过程:
1.通过new创建新空对象
2.构造函数this指向新对象 动态添加属性
3.执行构造函数代码
4.返回新对象
*/