作用域
局部作用域(除var以外,局部声明的变量外部不可使用)
函数作用域(函数内部声明、形参)
在函数内部声明的变量只能在函数内部访问,外部无法直接访问
块作用域
在大括号中声明的变量,如for循环、if 中、while循环等
全局作用域
在JS的最外层声明的变量
作用域链
本质
底层的变量查找机制(子作用域可以访问父作用域,但反之不行)
规则
- 在函数被执行时,会优先从当前函数作用域中查找变量
- 如果当前作用域查找不到,则会依次逐级查找父级作用域直到全局作用域
JS垃圾回收机制 Garbage Collection(GC )
内存的生命周期
- 内存分配:当声明变量、函数、对象时,系统会自动为他们分配内存
- 内存使用:即读写内存,也就是使用变量、函数等
- 内存回收:使用完毕,由垃圾回收器自动回收不再使用的内存
说明
- 一般情况下,全局变量一般不会被回收(关闭页面回收)
- 一般情况下,局部变量,不再使用了,会自动回收
内存泄漏
概念
程序中分配的内存由于某种原因程序未释放或无法释放叫做内存泄漏
算法说明
堆栈空间分配区别
- 栈(操作系统):由操作系统自动分配释放函数的参数值、局部变量等,基本数据类型放于栈中
- 堆(操作系统):一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收,复杂数据类型放于堆中
引用计数法(IE)
定义
看一个对象是否有指向他的引用,没有引用了就回收对象
算法
- 跟踪记录被引用的次数
- 如果被引用了一次,那么记录次数1,多次引用会累加 ++
- 如果减少一个引用就减一 --
- 如果引用次数是0,则释放内存
缺点
嵌套引用(循环引用)
如果两个对象相互引用,尽管他们已经不再使用,垃圾回收器不会进行回收(因为两个对象的引用次数一直大于零),导致内存泄漏
function fn() {
let o1 = {};
let o2 = {};
o1.a = o2;
o2.a = o1;
return "采用引用计数的回收机制o1 o2 无法被回收"
}
标记清除法(大部分游览器)
算法
- 标记清除法将“不再使用的对象”定义为“无法达到的对象”
- 就是从根部(在JS中就是全局变量)出发定时扫描内存中的对象,凡是能从根部到达的对象,都是还需要使用的
- 那些无法由根部出发触及到的对象标记为不再使用,稍后进行回收
闭包(Closure)
概念
一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域(内层函数使用到了外层函数的变量等)
简单理解:闭包 = 内层函数 + 外层函数变量
闭包的作用
封闭数据,提供操作,外部也可以访问函数内部的变量,在外部不能直接被修改
基本格式
function outer() {
let a = 1000;
function c() {
console.log(a--);
}
return c;
}
const fun = outer();
fun();
闭包的应用
实现数据的私有
闭包可能出现的问题
内存泄漏
变量提升(仅存在var声明的变量)
说明
把所有var声明的变量提升到当前作用域的最前面(只提升声明,不提升赋值)
注意
- 变量在未声明即被访问时会报语法错误
- 变量在var声明之前被访问,变量的值为undefined
- let/const 声明的变量不存在变量提升
- 变量提升出现在相同作用域中
- 实际开发中推荐先声明再访问变量
函数进阶
函数提升
函数作用域提升(提升在当前作用域)
会默认将所有函数声明提升到当前作用域的最前面,只提升函数声明,不提升函数调用
注意,当函数进行函数表达式声明时,即:
fun();
var fun = function() {
console.log("函数提升");
};
此时,会报错,因为此时不是函数提升,而是变量提升,在变量提升时,只提升声明,不提升赋值,所以fun的值为undefined,而不能视为函数,因此会报错,因此,函数表达式必须先声明后调用
函数参数
形参
实参
默认参数
动态参数(arguments)
arguments是函数内部内置的伪数组变量,它包含了调用函数时传入的所有实参(可以通过for循环依次得到传递过来的实参)
剩余参数
将不定数量的参数表示为一个数组(真数组),形式为(...参数名)使用的时候只需要写参数名即可
例:
function getSum(...arr) {
let sum = 0;
for (var i = 0; i < arr.length;i++) {
sum+=arr[i];
}
return sum;
}
注意(剩余参数与动态参数的优劣)
一般情况下不使用动态参数而使用的为剩余参数,因为他们效果一样,但是,剩余参数中的数组为真数组,可以进行使用操作数组的方法
展开运算符(...)
说明
不会修改原数组
作用
可以将一个对象展开,如数组等
例
const arr = [0, 1, 2];
console.log(...arr);
使用场景
求数组中的最大值(最小值同理)
const arr = [0, 1, 2];
console.log(Math.max(...arr));
合并数组
const arr1 = [0, 1, 2];
const arr2 = [1, 2, 3];
const arr = [...arr1,...arr2];
console.log(arr);
箭头函数
基本语法
const 函数名 = (形参) => {
函数体
}
省略
()中为形式参数,当形参只有一个时,括号可以省略(空参或者一个以上不能省)
const fun = x => {
console.log(x);
};
fun(100);
只有一行代码的时候,大括号可以省略
const fun = x => console.log(x);
fun(100);
返回值可省,当只有一行时
const fun = (x) => x + x;
console.log(fun(100));
直接返回一个对象(其中,对象要使用()包含,因为对象的{}会和函数的{}产生冲突)
const fun = uname => ({name:uname})
fun("yds");
箭头函数参数
箭头函数中没有arguments动态参数,有剩余参数...arr
箭头函数this
默认
谁调用指向谁
箭头函数的this
说明
箭头函数不会创建this,只会从自己的作用域链的上一层沿用this
DOM事件如果需要使用this不推荐使用箭头函数
移动端click事件延迟300ms问题(双击缩放)
1.禁用缩放 添加 <mata name="viewport" content="user-scalable = no" >
2.利用touch事件封装 松开时间-触摸时间(需去除移动的情况)
3.fastclick插件 引入
解构(原理为赋值,因此也称为解构赋值)
数组解构(支持多维数组的结构)
说明
数组解构是将数组的单元值快速批量赋值给一系列变量的简洁的语法
基本语法
赋值运算符(=)左侧的[]用于批量声明的变量,右侧数组的单元值被赋给左侧的变量,其中变量的顺序对应数组单元值的位置依次进行赋值操作
示例
const [min,max,avg] = [100,200,150];
或者
const arr = [100,200,150];
const [min,max,avg] = arr;
其中,若要使用100,直接用变量min即可,其他同理
应用
交换两个变量的值
let a = 1
let b = 2;
[b, a] = [a, b]
其中b后面需要加分号,因为此时,下面为数组,容易引发歧义(数组开头时前一句末尾或这句开头加分号)
js加分号的情况
- 立即执行函数
- 数组解构
特殊情况
变量多单元值少
多余的变量值为undefined
解决变量多单元值少出现undefined可以设置默认值
const arr = [0,1,2];
const [a = 0 , b = 0, c = 0, d = 0] = arr;
变量少单元值多
依次对变量进行赋值,多余的单元值不赋值
利用剩余参数解决变量少单元值多的情况
可以使用剩余参数来存储之后的单元值,返回值为真数组
按需导入赋值
const arr = [100, 200, 150];
const [a, , b] = arr;
其中两百忽略了,只需要用逗号隔开即可
对象解构
说明(无序,只需要根据变量名赋值)
将对象的属性和方法快速批量赋值给一系列变量的简洁语法
基本语法(基本与数组解构一致,但是,声明的解构的名字与属性名必须一致,而且外部没有冲突的变量)
const obj = {uname:"gj",age:18};
const {uname,age} = obj;
如果出现外部变量与对象属性名一致时,可以对声明的解构的名字进行更改,规范为(旧变量名:新变量名)
const uname = "gongjie";
const obj = {uname:"gj",age:18};
const {uname : username,age} = obj;
解构数组对象
const obj = [{uname:"gj",age:18}]
const [{uname,age}] = obj;
多级对象解构(按照相同括号)
const obj = {
name: "u",
family: {
uname1: "gj",
uname2: "gp",
},
};
const {
name,
family: { uname1, uname2 },
} = obj;
其中对象中的对象解构时,需要带上解构的对象名且加:,如以上的family
forEach遍历
作用
遍历数组中的每一个元素(一般用于遍历数组对象)
语法
和map类似
const arr = ["red","green","blue","yellow"];
arr.forEach(function(item,index) {
console.log(item);
});
其中:item为数组元素,index为数组下标,但是map方法返回新数组,forEach没有返回值
筛选数组filter方法
作用
筛选符合条件的元素,并返回筛选之后的新数组
语法(与map类似,但是map不能筛选)
let arr = [100,20,255,3000];
arr = arr.filter(function(item,index) {
return item > 100;
})
console.log(arr);
filter方法、forEach方法和map方法的区别
//一下方法的形参item和index均为数组元素与
数组下标
// forEach forEach方法,无返回值,高级遍历
let arr = [100,20,255,3000];
arr.forEach(function(item,index) {
console.log(item);
});
console.log("--------")
// filter filter方法,返回值为筛选后
的数组
arr = arr.filter(function(item,index) {
return item > 100;
})
console.log(arr);
console.log("--------")
// map map方法,返回值为改变后的数组
arr = arr.map(function(item,index) {
console.log(item);
})
深入对象
创建对象的方式
利用字面量创建对象
const obj = {
成员名称:成员属性
}
利用new Object创建对象
const obj = new Object({
对象成员名称:成员属性
});
构造函数创建对象
function 构造函数名(参数1,参数2...) {...}//构造函数
const 变量名 = 构造函数名(相应的实参);//创建对象