作用域
变量能够被访问的范围
局部作用域
函数作用域
在函数内部声明的变量只能在函数内部被访问,外部无法直接访问
1.函数的参数也是函数内部的局部变量
2.函数执行完毕后,函数内部的变量就被清空了
块作用域
在{}内部的代码称为代码块,代码块内部声明的变量外部可能无法访问
let声明的变量会产生块级作用域,var不会产生块级作用域
const声明的常量也会产生块级作用域
全局作用域
<script>标签和.js文件的最外层就是全局作用域,在此声明的变量,在函数内部也能被访问
全局作用域中声明的变量,任何其它作用域都可以被访问
为window对象动态添加的属性默认为全局作用域,不推荐
函数中未使用任何关键字声明的变量为全局变量,不推荐
尽可能少的声明全局变量,防止全局变量被污染
作用域链
作用域链本质是底层的变量查找机制
在函数被执行时,会优先查找当前函数作用域中查找变量
如果当前作用域查找不到,则会依次逐级查找父级作用域直到全局作用域
案例:遵守就近原则
这里的 a = 2不是不用关键字声明,即不是修改全局变量,而是把函数的a重新赋值为2,
let a = 1
let b = 2
function f() {
let a = 1
function g() {
a = 2
console.log(a);//2
}
g()
console.log(a);//2
}
f()
console.log(a);//1
这里的b = 1 是不用任何关键字声明,即修改全局变量中的b为1,然后作用域链查找变量机制。
let a = 1
let b = 2
function f() {
let a = 1
function g() {
b = 1
console.log(b);//1
}
g()
console.log(b);//1
}
f()
console.log(b);//1
js垃圾回收机制
垃圾回收机制简称GC
js中分配的内存,一般如下生命周期:
内存分配:当我们声明变量、函数、对象时,系统会自动分配内存
内存使用:使用变量、函数
内存回收:使用完毕,由垃圾回收器自动回收不再使用的内存
说明:
全局变量一般不会回收(关闭页面回收)
一般情况下局部变量的值不用了就会被自动回收
内存泄漏:程序中分配的内存由于某种原因未释放或无法释放叫做内存泄漏
js垃圾回收机制-算法说明
浏览器垃圾回收算法
引用计数法(了解,现在不用了)
IE采用的引用计数算法,定义“内存不再使用”,就是看一个对象是否有指向它的引用,没有引用了就回收对象
算法:
跟踪记录被引用的次数
如果被引用了依次就记录次数1,多次引用累加++
如果减少一个引用就减1--
如果引用次数是0,则释放内存
const arr = [1,2,3,4]
arr = null
![](https://i-blog.csdnimg.cn/blog_migrate/c38a69b6c87cd12bbc3f42f387cb57e5.png)
第一行代码,引用一次,记录1
![](https://i-blog.csdnimg.cn/blog_migrate/4deaee562b13e575f0206b06c97ecfc6.png)
第二行代码,地址被修改为null,不再引用,记录0,自动回收
标记清除法
核心:
不再使用的对象定义为无法到达的对象
从根部(全局对象)出发,能找到不回收,找不到就回收
闭包
闭包 = 内层函数 + 外层函数的变量
function outer() {
const a = 1
function fn() {
console.log(a);
}
fn()
}
outer()
闭包的作用:外部也可以访问函数内部的变量
function outer() {
let a = 10
function fn() {
console.log(a);
}
return fn
}
// outer() // couter() === fn === function fn() {console.log(a)}
const fun = outer()
fun()
闭包应用:实现数据的私有
普通形式:全局变量易被修改
let i = 0
function fn() {
i++
console.log(i);
}
fn()//1
fn()//2
// i是全局变量,容易被修改
i = 100
fn()//101
闭包形式:实现数据的私有,外部不能修改,不会被回收,因为fun在全局声明,存在内存泄漏
function count() {
let i = 0
function fn() {
i++
console.log(i);
}
return fn
}
const fun = count()
fun()//1
fun()//2
let i = 100
fun()//3
函数内部的变量会被回收,无法统计函数被调用次数
count()()//1
count()()//1
let i = 100
count()()//1
变量提升
变量提升只存在于var声明的变量,在代码执行之前,会把var声明的变量提前到当前作用域最前面。
只提升声明,不提升赋值。没有赋值的变量,会自动赋值undefined。
函数进阶
函数提升
函数提升会把所有函数声明提升到当前作用域的最前面,只提升函数声明,不提升调用
fun()//1
function fun() {
console.log(1);
}
函数表达式不能函数提升
fun()//报错,因为fun是变量,不是函数,所以无法函数提升
var fun = function () {
console.log(1);
}
函数参数
动态参数
arguments是函数内部内置的伪数组变量,它包含了调用函数时传入的所有实参
function getSum() {
console.log(arguments);//伪数组,有索引无数组方法
}
getSum(2, 3, 4)
不确定传递多少个参数时,用arguments
function getSum() {
// console.log(arguments);//伪数组,有索引无数组方法
let sum = 0
for (let i = 0; i < arguments.length; i++) {
sum += arguments[i]
}
return sum
}
console.log(getSum(2, 3, 4));//9
console.log(getSum(6, 8, 22, 10));//46
剩余参数
剩余参数允许我们将一个不定数量的参数表示为一个数组
...是语法符号,置于最末函数形参之前,用于获取多余的实参
借助...获取的剩余实参,是个真数组
function getSum(...arr) {
console.log(arr);
let sum = 0
for (let i = 0; i < arr.length; i++) {
sum += arr[i]
}
return console.log(sum);
}
getSum(1, 2, 3)// [1, 2, 3] 6
getSum(1, 2, 3, 4, 5)//[1, 2, 3, 4, 5] 15
展开运算符
展开运算符(...),将一个数组进行展开
不会影响原数组,主要用于求数组极值
const arr = [1, 2, 3]
console.log(...arr);//1 2 3
console.log(arr);//[1, 2, 3]
//利用...求数组极值
const arr = [1, 2, 3]
console.log(Math.max(1,2,3));//3
console.log(Math.max(...arr));//3
//合并数组
const arr1 = [1, 2, 3]
const arr2 = [4, 5, 6]
// 合并数组
const arr = [...arr1,...arr2]
剩余参数:函数参数使用,得到真数组
展开运算符:数组中使用,数组展开
箭头函数
引入箭头函数的目的是更简短的函数写法并且不绑定this,箭头函数的语法比函数表达式更简洁
箭头函数更适用于那些本来需要匿名函数的地方
基本语法
//普通函数
const fn1 = function () {
console.log(123);
}
fn1()
//箭头函数
const fn2 = () =>{
console.log(123);
}
fn2()
//箭头函数带参
const fn2 = (x) =>{
console.log(x);//1
}
fn2(1)
//只有一个形参,()可省略
const fn3 = x=>{
console.log(x);//1
}
fn3(1)
//只有一行代码可以省略大括号{}
const fn4 = x => console.log(x);//1
fn4(1)
//只有一行代码可以省略return
const fn5 = x => x + x
console.log(fn5(1));//2
//箭头函数可以直接返回一个对象
const fn = (uname) => ({ uname: uname })
console.log(fn('刘德华'));//{uname: '刘德华'}
1.箭头函数属于表达式函数,因此不存在函数提升
2.箭头函数只有一个参数时可以省略圆括号()
3.箭头函数函数体只有-行代码时可以省略花括号{},并自动做为返回值被返回
4.加小括号的函数体返回对象
箭头函数参数
普通函数有arguments动态参数
箭头函数没有arguments动态参数,但是有剩余参数...arr
//利用箭头函数来求和
const getSum = (...arr) => {
sum = 0
for (let i = 0; i < arr.length; i++) {
sum += arr[i]
}
return sum
}
console.log(getSum(123, 456, 789));//1368
箭头函数this
es5的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,它只会从自己的作用域链的上一层沿用this。
const fn = () => {
console.log(this);//window对象,箭头函数不创建this,沿作用域链逐级向上寻找
}
fn()
const obj = {
uname: 'andy',
sayHi: () => {
console.log(this);//window对象
}
}
obj.sayHi()
const obj = {
name: 'andy',
sayHi: function () {
let i = 10
const count = () => {
console.log(this);//obj对象
}
count()
}
}
obj.sayHi()
解构赋值
解构赋值是一种快速为变量赋值的简洁语法,本质上仍然是为变量赋值。
数组解构
数组解构是将数组的单元值快速批量赋值给一系列变量 的简洁语法。
语法:
赋值运算符=左侧的[]用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量
变量的顺序对应数组单元值的位置依次进行赋值操作
const arr = [100, 60, 80]
const [max, min, avg] = arr
//类似于
// const max = arr[0]
// const min = arr[1]
// const avg = arr[2]
console.log(max);
console.log(min);
console.log(avg);
//另一种写法:
const [max, min, avg] = [100,60,80]
// 交换两个变量
let a = 1
let b = 2;//必须加分号
[b, a] = [a, b]
console.log(a, b);//2 1
必须加分号的两种情况
多个立即执行函数之间要加分号
解构赋值前面有代码时要加分号
//变量多于数组元素
const [a, b, c, d] = [1, 2, 3]
console.log(d);//undefined
//变量少于数组元素
const[e,f,...g] = [1,2,3,4]
console.log(e,f,g);//1 2 [3, 4]
//防止 undefined传递可设置默认值
const [a = 0, b = 0, c = 0] = [1, 2]
console.log(a, b, c);//1 2 0
//按需导入赋值
const [a, b, , d] = [1, 2, 3, 4]
console.log(a, b, d);//1 2 4
const arr = [1, 2, [3, 4]]
console.log(arr[2][0]);//3
//多维数组解构
const [a, b, [c, d]] = [1, 2, [3, 4]]
console.log(c);//3
对象解构
两种语法格式
const obj = {
uname: 'andy',
age: 18
}
const { uname, age } = obj//把属性赋值给变量
console.log(uname, age);//andy 18
const { uname, age } = { uname: 'andy', age: 18 }//把属性赋值给变量
console.log(uname, age);//andy 18
//要求变量名和属性名相同
//变量名多于属性名
const { uname, age, hoby } = { uname: 'andy', age: 18 }//把属性赋值给变量
console.log(uname, age, hoby);//andy 18 undefined
//外部变量与解构赋值的变量同名,旧变量名:新变量名
const uname = 'jack'
const { uname: username, age } = { uname: 'andy', age: 18 }
console.log(username, age);//andy 18
数组对象的解构
const pig = [{ uname: 'peki', age: 6 }]
const [{ uname, age }] = pig
console.log(uname, age);//peki 6
//多级对象解构
const pig = {
uname: 'peki',
family: {
mother: '猪妈妈',
father: '猪爸爸',
brother: 'george'
},
age: 6
}
const { name, family: { mother, father, brother }, age } = pig
console.log(uname, mother, father, brother, age);
遍历数组forEach方法
forEach()方法用于调用数组的每个元素,并将元素传递给回调函数
主要使用场景:遍历数组的每个元素
被遍历的数组.forEach(function(当前数组元素,当前元素索引){函数体})
const arr = ['red','green','blue']
const res = arr.forEach(function (item,[index]) {
console.log(item);//元素 red green blue
console.log(index);//索引 0 1 2
})
console.log(res);//undefined
筛选数组filter方法
filter()方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素
主要使用场景:筛选数组符合条件的元素,并返回筛选之后元素的新数组
const arr = [10, 20, 30]
const nweArr = arr.filter(function (item, [index]) {
console.log(item);//10 20 30
console.log(index);//0 1 2
return item >= 20
})
console.log(nweArr);//[20, 30]
const arr = [10, 20, 30]
const newArr = arr.filter(item => item >= 20)
console.log(newArr);//[20, 30]
操作数组元素map方法
const arr = [10, 20, 30]
const nweArr = arr.map(function (item, [index]) {
console.log(item);//10 20 30
console.log(index);//0 1 2
return item + 10
})
console.log(nweArr);//[20, 30, 40]
构造函数
构造函数:是一种特殊的函数,主要用来初始化对象
使用场景:常规的{...}语法允许创建一个对象。比如我们创建了佩奇的对象,继续创建乔治的对象还需要重新写一遍,此时可以通过构造函数来快速创建多个类似的对象。
它们的命名以大写字母开头。
它们只能由"new"操作符来执行。
function Pig(name, age, gender) {
this.name = name
this.age = age
this.gender = gender
}
const pig1 = new Pig('peki', 6, 'female')
console.log(pig1);
const pig2 = new Pig('george', 5, 'male')
console.log(pig2);
const pig3 = new Pig('dady', 33, 'male')
console.log(pig3);
const pig4 = new Pig('mum', 30, 'female')
console.log(pig4);
使用new关键字调用函数的行为被称为实例化
实例化构造函数时没有参数时可以省略()
构造函数内部无需写return,返回值即为新创建的对象
构造函数内部的return返回的值无效,所以不要写return
new Object () new Date ()也是实例化构造函数
new关键字作用:
1.创建新对象
2.构造函数this指向新对象
3.执行构造函数代码,修改this,添加新的属性
4.返回新对象
实例成员与静态成员
实例成员
通过构造函数创建的对象称为实例对象,实例对象中的属性和方法称为实例成员(实例属性和实例方法)
为构造函数传入参数,创建结构相同、值不同的对象
构造函数创建的实例对象彼此独立互不影响
静态成员
构造函数的属性和方法被称为静态成员(静态属性和静态方法)
静态成员只能构造函数来访问
静态方法中的this指向构造函数
const now = new Date()
now.getFullYear()
now.getMonth()
数据常用函数
基本包装类型
js底层把简单数据类型包装成引用数据类型,使其拥有属性和方法便于操作数据。
Object
三个常用静态方法
之前for in遍历获取对象里属性
const o = { uname: 'peki', age: 6 }
for (const k in o) {
console.log(k);//uname age
console.log(o[k]);// peki 6
}
Object.keys静态方法获取对象中所有属性(键)
Object.values静态方法获取对象中所有属性(值)
const o = { uname: 'peki', age: 6 }
console.log(Object.keys(o));//['uname', 'age']返回一个数组,属性名
console.log(Object.values(o));//['peki', 6],返回一个数组,属性值
Object. assign静态方法常用于对象拷贝
//对象的拷贝
const o = { uname: 'peki', age: 6 }
const obj = {}
Object.assign(obj, o)
console.log(obj);//{uname: 'peki', age: 6}
常用于给对象添加新属性
const o = { uname: 'peki', age: 6 }
Object.assign(o, { gender: 'female' })
console.log(o);//{uname: 'peki', age: 6, gender: 'female'}
Array
![](https://i-blog.csdnimg.cn/blog_migrate/35d9a5f150339b72bb1dba3661ac2166.png)
reduce
reduce返回累计处理的结果,经常用于求和等
arr.reduce(function(上一次的值,当前值) {},初始值)
//没有初始值
const arr = [1,5,9]
const total = arr.reduce(function (prev,current) {
return prev + current
})
console.log(total);//15
//有初始值
const arr = [1, 5, 9]
const total = arr.reduce(function (prev, current) {
return prev + current
}, 10)
console.log(total);//25
//有初始值加上初始值,箭头函数方式
const arr = [1, 5, 9]
const total = arr.reduce((prev, current) => prev + current, 10)
console.log(total);//25
reduce执行过程:
1.每次循环有两个数组元素进行操作
2.如果没有起始值,第一个数组元素作为起始值与下一个数组元素进行操作,操作后得到一个返回值
3.每一次循环,把返回值作为下一次循环的第一个元素,然后与循坏里的第二个元素进行操作
4.循环完毕,最后得到一个返回值
5.如果有起始值,则从起始值开始
对象数组计算属性不可省略初始值,初始值设为0,要加上current.属性
const arr = [
{
name: '张三',
salary: 10000
},
{
name: '李四',
salary: 10000
},
{
name: '王五',
salary: 10000
},
]
const total = arr.reduce((prev, current) => prev + current.salary,0)
console.log(total);//30000
find()方法
返回数组中满足提供的测试函数的第一个元素的值。 否则返回undefined。
const arr = ['red','green','blue']
const res = arr.find(function (element) {
return element === 'blue'
})
console.log(res);//blue
使用场景
const arr = [
{
name: 'xiaomi',
price: 1999
},
{
name: 'huawei',
price: 3999
}
]
const res = arr.find(item => item.name === 'xiaomi')
console.log(res);//{name: 'xiaomi', price: 1999}
实例方法every
检测数组所有元素是否都符合指定条件,如果所有元素都通过检测返回true, 否则返回false(重点)
const arr = [10, 20, 30]
const res = arr.every(function (element) {
return element >= 10
})
console.log(res);//true
实例方法some
检测数组中的元素是否满足指定条件如果数组中有元素满足条件返回true, 否则返回false
const arr = [10, 20, 30]
const res = arr.some(function (element) {
return element >= 30
})
console.log(res);//true
Array.from() 伪数组转换为真数组
const lis = document.querySelectorAll('ul li')
const liss = Array.from(lis)
liss.pop()
console.log(liss);//[li, li]
String
split('分隔符') 将字符串转换为数组
const str = 'pink,red'
const arr = str.split(',')
console.log(arr);//['pink', 'red']
substring()
substring()方法返回一个字符串在开始索引到结束索引之间的一个子集,或从开始索引直到字符串的末尾的一个子集。
str.substring(indexStart[,indexEnd])
返回[start-char , end-char) 左闭右开
若无参数则截取全部,若没有第二个参数默认截取后面的所有字符
const str = '我要暴富'
console.log(str.substring(2));//暴富
console.log(str.substring());//我要暴富
console.log(str.substring(1,3));//要暴
startsWith(),检测是否以该字符串为开头,返回布尔值
str.startsWith('string'[,index])
没有第二个参数,默认从索引0开始
const str = 'To be, or not to be, that is the question'
console.log(str.startsWith('To be'));//true
console.log(str.startsWith('not to be'));//false
console.log(str.startsWith('not to be', 10));//true
includes()
includes() 方法执行区分大小写的搜索,以确定是否可以在另一个字符串中找到一个字符串,并根据情况返回 true 或 false。
str.includes('string'[,index])
从index开始搜索,若无此参表示从索引0开始搜索
const str = 'To be, or not to be, that is the question'
console.log(str.includes('To be'));//true
console.log(str.includes('question'));//true
console.log(str.includes('hello'));//false
console.log(str.includes('To be',1));//false
console.log(str.includes('be',5));//true
![](https://i-blog.csdnimg.cn/blog_migrate/885f3face014c8d6a48e0710918c031d.png)
Number
toFixed() 保留几位小数
const num = 3.1415926
console.log(num.toFixed());//3
console.log(num.toFixed(1));//3.1
console.log(num.toFixed(2));//3.14
console.log(num.toFixed(3));//3.142