JavaScript进阶(一)
一、作用域
1. 局部作用域
1.1 函数作用域
在**函数内部声明的变量**只能在函数内部被访问,外部无法直接访问
<script>
function getSum(){
// 函数内部是函数作用域 属于局部变量
const num = 10
}
console.log(num) // 此处报错 函数外部不能使用局部作用域变量
</script>
1.2 块作用域
1. 在JavaScript中使用**{}**包裹的代码称为代码块,代码块内部(let/const)声明的变量外部将无法被访问,(var声明时可被访问)
2. let/const(推荐使用)声明的变量会产生块作用域,var不会;不同代码块之间的变量无法互相访问
for(let t = 1; t <= 6; t++){
// t 只能在该代码块中被访问
console.log(t) // 正常
}
// 超出了t的作用域
console.log(t) // 报错
2. 全局作用域
<script>标签和.js文件的[最外层]就是全局作用域,在此声明的变量在函数内部也可以被访问
(不推荐)为window对象动态添加属性,未使用任何关键字声明;
<script>
// 全局作用域下声明了num变量
const num = 10
function fn() {
// 函数内部可以使用全局作用域的变量
console.log(num)
}
</script>
3. 作用域链
作用域链本质是底层的变量查找机制
(就近原则查找)
函数被执行时,会优先查找当前
函数作用域中查找变量
如果当前作用域查找不到会依次逐级查找父级作用域
直到全局作用域
<script>
// 全局作用域
let a = 1
let b = 2
// 局部作用域
function f(){
let a = 1
// 局部作用域
function g(){
a = 2
console.log(a)
// 优先查找当前函数作用域,所以a=2,若不存在a=2,则查找父级let a=1
}
g()
}
f()
</script>
4. Js垃圾回收机制
4.1 内存生命周期
Js环境中分配的内存,有如下生命周期:
1. 内存分配:声明变量、函数、对象时,系统会自动分别配内存
const age = 18
const obj = {
age = 18
}
2. 内存使用:读写内存,就是使用变量、函数
3. 内存回收:使用完毕,由垃圾回收器自动回收不再使用的内存
**说明**:
全部变量一般不会回收(关闭页面回收)
一般情况下,局部变量的值,不用时会被自动回收掉
内存泄漏:程序中分配的内存由于某种原因程序**未释放**或**无法释放**叫做内存泄漏
4.2 算法说明
常见浏览器垃圾回收算法:引用计数法和标记清楚法(常用)
引用计数法(现在浏览器已不再采用):记录被引用的次数(引用一次加1),根据次数决定是否回收对象(存在的问题:两个对象相互引用,即使已不再使用,也不会进行回收,导致内存泄漏)
标记清楚法:从根部(全局对象)定时扫描内存中的对象。可以找到的对象(打标记)是需要使用的;找不到的对象,进行回收
function fn(){
let o1 = {}
let o2 = {}
o1.a = o2
o2.a = o1
return '引用计数无法回收'
}
fn()
标记清除:根部已经访问不到(属于局部,从根部无法进入函数内部),所以自动清除
5. 闭包
闭包简单理解:闭包 = 内层函数 + 外层函数的变量
函数套函数是不会产生闭包的,内层函数用到外层函数变量(一个函数对周围状态的引用捆绑在一起),才会产生闭包
常见闭包形式 外部可以访问使用 函数内部的变量:
function outer(){
let a = 10
function fn(){
// 这里内层函数使用了外层变量
console.log(a)
}
return fn
}
const fun = outer()
fun()
// 外层函数使用内部函数的a,要加return
6. 变量提升
变量提升:允许在变量声明之前被访问(仅存在于var声明变量,let/const声明的变量不存在变量提升)
```实例开发中推荐先声明再访问变量```
<script>
//1. 把所有var声明的变量提升到 当前作用域最前面
//2. 只提升声明,不提升赋值
//var sum
console.log(num + '件')
var sum = 10
</script>
二、函数进阶
1. 函数提升
函数提升:指函数在声明之前被调用
<script>
//1. 把所有函数声明提升到 当前作用域最前面
//2. 只提升声明,不提升函数调用
fn()
function fn(){
console.log('函数提升')
}
-------------------------
// 不存在提升现象
bar() //错误
var bar = function(){
console.log('函数表达式不存在提升现象')
}
</script>
2. 函数参数
2.1 动态参数
arguments
是函数内部内置的伪数组变量,它包含了调用函数时传入的所有实参
<script>
当不确定传递多少个实参时,使用arguments动态参数
function getSum() {
// arguments 动态参数 只存在于 函数里面
// consol.log(arguments) [2,3,4]
let sum = 0
for(let i = 0;i < arguments.length; i++){
sum += arguments[i]
}
console.log(sum)
}
getSum(2,3,4)
</script>
2.2 剩余参数
<script>
// ...用来获取(函数内部)多余实参,是真数组(可以使用pop,push等方法)
function config(baseURL,...others){
console.log(baseURL) // 得到'http://xxx.com'
console.log(other) //得到['get','json']
}
// 调用函数
config('http://xxx.com','get','json')
</script>
2.3 展开运算符
<script>
// 展开运算符(展开数组)使用场景:求数组最大值(最小值)、合并数组等
const arr = [1,5,3,8,2]
// console.log(...arr) // 1 5 3 8 2
console.log(Math.max(...arr)) //8
console.log(Math.min(...arr)) //1
------------------------------
// 合并数组
const arr1 = [1,2,3]
const arr2 = [4,5,6]
const arr3 = [...arr1,...arr2]
console.log(arr3) //[1,2,3,4,5,6]
</script>
3. 箭头函数
3.1 基本语法
<script>
// 1. 箭头函数基本语法
const fn = (x) => {
console.log(x)
}
fn(1)
// 2. 只有一个形参的时候,可以省略小括号
const fn = x => {
console.log(x)
}
fn(1)
// 3. 只有一行代码时,可以省略大括号
const fn = x => console.log(x)
fn(1)
// 4. 只有一行代码的时候,可以省略return
const fn = x => x + x
console.log(fn(1))
// 5. 箭头函数可以直接返回一个对象
const fn =(uname) => ({name:uname})
console.log(fn('username'))
</script>
3.2 箭头函数参数
箭头函数没有arguments动态参数,但是有剩余参数...args
<script>
const getSum = (...args) => {
let sum = 0
for(let i = 0;i<args.length;i++){
sum += args[i]
}
return sum // 函数体有多行代码需要return
}
console.log(getSum(1,2,3)) // 6
</script>
3.3 箭头函数this
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this
Dom事件回调函数(不太推荐)使用this
<script>
// 1. 箭头函数的this 是上一层作用域的this 指向
const fn = () =>{
console.log(this) //window(局部作用域没有this,往上一层找,找到script)
}
fn()
--------------------------------
// 2. 对象方法箭头函数 this
const obj = {
uname: 'username',
sayHi: () => {
console.log(this) //this指向window(箭头函数没有this,网上找obj,obj = window.obj)
}
}
obj.sayHi()
--------------------------------
const obj = {
uname: 'username',
sayHi: function(){
console.log(this) // this指向obj
let i = 10
const count = () =>{
console.log(this) // this指向obj(局部作用域没有this,找到sayHi里面有this(this指向调用者obj))
}
count()
}
}
obj.sayHi()
</script>
三、解构赋值
1. 数组解构
数组解构是将数组的单元值快速批量赋值给一系列变量的简介语法。
基本语法:
<script>
// 数组解构 赋值
const arr = [100,60,80]
const [max,min,avg] = arr
// 相当于⬇
const max = arr[0]
const min = arr[1]
const.avg = arr[2]
consoel.log(max) //100
console.log(min) //60
console.log(avg) //80
</script>
-------------------------------
交互两个变量:
let a =1
let b =2; // 必须有分号
[b,a] = [a,b]
console.log(a,b) // 2,1
1.1 加分号情况
<script>
// 1.立即执行函数
(function(){ })();
(function(){ })();
// 2.使用数组的时候
// const arr = [1,2,3]
const str = 'arr'; // 必须加分号
[1,2,3].map(function(item){
console.log(item)
})
</script>
1.2 数组解构细节
<script>
// 1. 变量多,单元值少,undefined
const [a,b,c,d] = [1,2,3]
console.log(d) // undefined
// 2. 剩余参数: 变量少,单元值多
const[a,b,...c] = [1,2,3,4]
console.log(c) //真数组[3,4]
</script>
2. 对象解构
对象解构是将对象属性和方法快速批量赋值给一系列变量的简洁语法
// 1. 解构语法
const {name,age} = {name:'useranme',age:18}
// 要求属性名和变量名必须一致
console.log(name) // username
console.log(age); // 18
----------------------------------
// 2. 给新的变量名赋值(旧变量名:新变量名)
const {name: uname,age} = {name:'useranme',age:18}
console.log(uname) //username
-----------------------------------
// 3. 多级解构赋值
function render({data}){
console.log(data)
}
render(msg) //msg为多级对象,实参传形参时解构出data
3. forEach遍历数组
forEach()方法用于调用数组的每个元素,并将元素传递给回调函数(遍历数组的每个元素)
<script>
const arr = ['pink','red','green']
arr.forEach(function(item,index){
// 使用与map相似,但map有return
console.log(item) //pink,red,green
console.log(index) //0,1,2
})
</script>