1.作用域
1)局部作用域
-
函数作用域
注:函数执行完毕后,函数内部的变量实际被清空了
-
块级作用域
在js中使用{}包裹的代码称为代码块,代码块内部声明的变量外部将{有可能}无法访问
let声明的变量会产生块作用域,var不会产生块作用域
const声明的常量也会产生块作用域
不同代码块之间的变量无法相互访问
推荐使用let和const
2)全局作用域
在script
标签和.js文件中的最外层就是全局变量,在此声明的变量在函数内部也可以被访问。全局作用域中声明的变量,任何其他作用域都可以被访问。
为windows对象动态添加的属性默认也是全局的,不推荐。
函数中未使用任何关键字声明的变量为全局变量,不推荐。
尽可能少的声明全局变量,防止全局变量被污染。
3)作用域链
作用域链本质上是底层的变量查找机制。
在函数被执行时,会优先查找当前函数作用域中查找变量。
如果当前作用域查找不到,则会依次逐级查找父级作用域直到全局作用域。(类似于冒泡)
嵌套关系的作用域串联起来形成了作用域链。
相同作用域链中按着从小到大的规则查找变量。(先查找当层)
子作用域能够访问父作用域,父作用域无法访问子作用域。
4)JS垃圾回收机制
-
什么是垃圾回收机制?
简称GC(garbage collection)
JS中内存的分配和回收都是自动完成的,内存不使用的时候,会被垃圾回收器自动回收。
但如果不了解JS的内存管理机制,会非常容易造成内存泄漏的情况。
内存泄漏:不再用到的内存,没有及时释放。
-
内存生命周期
内存分配——>内存使用——>内存回收
内存分配:系统自动为变量、函数对象分配内存
内存使用:读写内存,使用变量、函数
内存回收:使用完毕,由垃圾回收自动回收不再使用的内存
全局变量一般不会回收(关闭页面回收)
一般情况下局部变量的值,不用了,会被自动回收
<script> let num = 10 function fn() { const str = 'andy' console.log(str) } //注:因为函数调用后 会回收str 所有多次调用后 const str不会报错 fn() fn() fn() </script>
-
垃圾回收算法
-
引用计数法
看一个对象是否指向它的引用
跟踪记录每个值被引用的次数
如果这个值被引用了一次,就记录一次
多次引用会累加
如果减少一个引用就-1
如果引用次数为0,则释放内存
let persopn = { age:18 } let p = person person = 1 p = null
-
//如果出现以下情况 会导致大量的内存泄漏(相互引用)就不能使用引用计数
function fun(){
let o1 = {}
let o2 = {}
o1.a = o2
o2.a = o1
}
-
标记清除法
从根部(在JS中就是全局对象global)出发定时扫描内存中的对象。凡是能从根部到达的对象,都是还需要使用的。
无法从根部出发触及到的对象都标记为不再使用,稍后进行回收
5)闭包
闭包 = 内层函数 + 外层函数的变量
//内层函数调用外层函数的变量 简单写法
function outer(){
const a = 1
function f(){
console.log(a)
}
f()
}
outer()
//常见闭包形式 外部可以使用函数内部的变量
// function outer() {
// const a = 100
// function f() {
// console.log(a)
// }
// return f
// }
// outer()
// const fun = outer()
// fun()
// function outer() {
// const a = 100
// return function f() {
// console.log(a)
// }
// }
// outer()
// const fun = outer()
// fun()
function outer() {
const a = 100
return function f() {
return a
}
}
const fun = outer()
console.log(fun())
-
闭包应用
实现数据私有
<script> function fun() { let i = 0 function count() { i++ console.log(`函数被调用了${i}次`) } return count } const fn = fun() </script>
注意:闭包可能引起内存泄漏
6)变量提升
<script>
//1.把所有var声明的变量提升到当前作用域的最前面
//2.只提升声明,不提升赋值
// console.log(num+"件")
// var num = 10
function fun() {
console.log(num)
var num = 10
}
fn()
</script>
变量提升过程:
1.把所有var声明的变量提升到当前作用域的最前面
2.只提升声明,不提升赋值注:
变量在var声明之前被访问,变量值为undefined
let和const不存在变量提升(推荐)
2.函数进阶
1)函数提升
函数在声明之前可以被提升
<script>
//会把所有函数声明提升到当前作用域的最前面
//只提升函数声明,不提升函数调用
fn()
function fn() {
console.log('函数提升')
}
</script>
注:函数表达式 必须先声明和赋值,后调用否则会报错
var fun = function(){}
2)函数参数
-
动态参数
arguments是函数内部内置的伪数组变量,它包含了调用函数时传入的所有实参。
<script> function getSum() { let sum = 0 //arguments 动态参数 只存在于函数内 //是个伪数组 for (let i = 0; i < arguments.length; i++) { sum += arguments[i] } return sum } console.log(getSum(1, 2, 3, 4, 5, 6, 7, 8, 9)) console.log(getSum(16, 58, 79)) console.log(getSum(1000000000000, 58, 999999999)) </script>
-
剩余参数
将一个不定数量的参数表示为一个数组
<script> function getSum(...arr) { console.log(arr) } getSum(1, 2) getSum(1, 2, 3) function getTotal(a, b, ...Arr) { console.log(Arr) } //返回一个空数组 1赋值给a,2赋值给b getTotal(1, 2) //返回有两个数的数组 getTotal(1, 2, 3, 4) //返回有一个值的数组 getTotal(1, 2, 3) </script>
两者的区别:
…是语法符号,置于最末函数形参之前,用于获取多余的参数
借助…获取的剩余实参,是个真数组
开发中,提倡多使用剩余参数
-
展开运算符(…)
<script> const arr1 = [1, 2, 3, 4, 5] console.log(...arr) //求最值 console.log(Math.max(...arr)) //合并数组 const arr2 = [6, 7, 8, 9] const arr = [...arr1, ...arr2] console.log(arr) </script>
注:
不会修改原数组
应用场景:求数组的最值、合并数组
-
箭头函数(表达式函数)
目的:书写更简短的函数,并不绑定this
使用场景:适用于本来需要匿名函数的地方
<script> //1.普通函数 // const fun = function fn() { // console.log(111) // } // fun() //箭头函数 // const fun = (x) => { // console.log(x) // } // fun(123) //只有一个形参 括号可以省略 // const fun = x => { // console.log(x) // } // fun(123) //只有一行代码 大括号可以省 // const fun = x => console.log(x) // fun(123) //只有一行代码 return可省 const fun = (x, y) => x + y console.log(fun(3, 9)) //返回对象 const fn = (uname) => ({ name: uname }) console.log(fn('姚老师')) </script>
-
箭头函数参数
没有动态参数arguments,但有剩余参数
<script> const getSum = (...arr) => { let sum = 0 for (let i = 0; i < arr.length; i++) { sum += arr[i] } return sum } const result = getSum(2, 3, 5) console.log(result) </script>
-
箭头函数this
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this
<script> //以前的this指向 console.log(this) //window function fn() { console.log(this) //window } fn() const obj = { name: 'andy', sayHi: function () { console.log(this) //obj } } obj.sayHi() //箭头函数中的this //{}中是一个局部作用域 是上一层作用域的this const fun = () => { console.log(this) //window } fun() //对象方法箭头函数 const user = { uname: '嘎吱~', sayHi: () => { console.log(this) //window } } user.sayHi() const people = { uname: '花花', sayHi: function () { let i = 10 const count = () => { console.log(this) //obj } count() } } people.sayHi() </script>
-
3.解构赋值
1)数组解构
数组解构将数组的单元值快速赋值给若干个变量的语法
<script>
const arr = [100, 60, 80]
//数组解构
const [max, min, avg] = arr
console.log(max)
</script>
典型应用:交换两个变量
let a = 1
let b = 2;
[b, a] = [a, b]
console.log(a, b)
注:记得加分号; const不能重新赋值
必须加分号的情况:立即执行函数,数组解构
<script>
//1.立即执行函数
// (function () { })();
//2.使用数组
//如果不加分号 默认为是没有换行
// const arr = [1, 2, 3]
const str = 'pink老师';
[1, 2, 3].map(function (item) {
console.log(item)
})
</script>
-
特殊情况
const [a, b, c, d] = [1, 2, 3] console.log(a) //1 console.log(b) //2 console.log(c) //3 console.log(d) //undefined const [e, f, ...arr] = [1, 2, 3, 4, 5] console.log(e) //1 console.log(f) //2 console.log(arr) //{3,4,5} //防止undefined传递 添加默认值 const [g = 0, h = 0] = [] console.log(g) console.log(h) //按需导入赋值 const [i, j, , k] = [1, 2, 3, 4] console.log(i) //a console.log(j) //2 console.log(k) //4
-
支持多维数组
const [a, b, [c, d]] = [1, 2, [3, 4]] console.log(a) //1 console.log(b) //2 console.log(c) //3
2)对象结构
快速解构属性和方法的语法
<script>
//变量名与属性名必须相同 uname = uname
//如果名称不相同 返回undefined
// const { uname, age } = { uname: 'pink老师', age: 18 }
// console.log(uname)
// console.log(age)
//与变量名冲突的问题
// const uname = '花花'
// const { uname: username, age } = { uname: 'pink老师', age: 18 }
// console.log(username)
// console.log(age)
//解构数组对象
const pig = [
{
uname: '佩奇',
age: 18
}
]
const [{ uname, age }] = pig
console.log(uname)
console.log(age)
</script>
-
多级对象解构
<script> const pig = { name: '佩奇', family: { mother: '猪妈妈', father: '猪爸爸', sister: '乔治' }, age: 16 } const { name, family: { mother, father, sister }, age } = pig console.log(name) console.log(mother) console.log(father) console.log(sister) console.log(age) const person = [ { name: '佩奇', family: { mother: '猪妈妈', father: '猪爸爸', sister: '乔治' }, age: 16 } ] const [{ name, family: { mother, father, sister }, age }] = person console.log(name) console.log(mother) console.log(father) console.log(sister) console.log(age) </script>
遍历数组forEach
被遍历的数组.forEach(function(当前数组元素,当前元素索引号){
//函数体
})
只遍历,不返回数组