递归函数就是函数直接或间接调用自己
//无限递归
function fu() {
console.log('hello');
}
fu() //使用无限递归时必须注意给函数设置出口
作用域scope
作用域的产生时间:函数定义(声明)的时候产生
作用域的分类:
1、全局作用域
2、局部作用域:包含函数、和块级{}
3、eval()作用域
执行上下文Execution Context
执行上下文的产生时间:函数调用时产生
生命周期
全局上下文
生命周期:窗口打开=>窗口关闭,node打开=>node关闭
局部上下文(函数上下文)
生命周期:函数调用=>调用完毕
概述
每当调用一个一个函数时一个新的EC就会被创建出来,在js内部有三个阶段
1、EC创建阶段
2、EC激活阶段
3、EC销毁阶段
当js执行时就会进入不同的ec,而每个ec的组成
组成部分 | 内容 |
---|---|
Variable Object | {arguments,function declarations,variables} |
Scope | 指向作用域链 |
this | 指向一个环境对象 |
EC的创建发生在:当调用一个函数但又在执行函数体内具体代码前。创建的同时,其内部还会分别为:
1、建立变量对象VO。及包含变量对象,无法访问。如果是函数则还会建立arguments
2、建立作用域链SC
3、确定this指向
激活阶段
1、函数引用
2、变量赋值
3、执行其他代码
变量对象VO
Variable Object是EC创建阶段内部创建的一个对象,用来记录当前作用域所有可用的变量
VO中的属性是不可被访问的,只有等激活AO(Active Object)后才可被访问
VO与AO实际上是同一个对象,只是状态不同
function outer() {
console.log(a); //undefined
console.log(inner()); //2
var a=1;
function inner() {
return 2;
}
console.log(a); //1
}
outer();
function fn(a,b) {
var c=10;
function fnD() {}
var e =function fnE(){}
b=20;
console.log(a,b,c); //10,20,10
}
fn(10);
var a=1;
function fn(){
console.log(a); //第二步:undefined
a=2;
console.log(a); //第三步:2
var a=3;
console.log(a); //第四步:3
}
console.log(a);//第一步:1
fn()
console.log(a);//第五步:1
var fn=function(){
console.log(a); //第一步:function a
var a=1
console.log(a); //第二步:1
a(); //第三步:报错
function a(){}
}
fn();
function foo(number) {
if (number>3) {
foo(--number);
}
console.log(number); //3,3,4
}
foo(5);
scope chain 作用域链:
SC:访问规则是:先访问自身EC,若找不到变量,则访问声明时所在的EC里的变量,依次类推,直到找到全局EC
var a=1;
function eat() {
console.log(a); //第三步:1 (在当前找a,当前没有在全局找)
}
function foo() {
var a=2;
eat();//第二步
}
foo();//第一步
垃圾回收机制
1、不在使用的变量
2、没有引用的对象
3、局部变量
function foo() {
let a=10;
当变量进入环境 let b='hello' //进入环境
}
foo(); //离开环境
function fn() {
let i=5;
console.log(i);
}
fn();
console.log(i);
//函数作用域中的i在调用完成后销毁
let obj={
name:'zhangsan'
}
obj=null;
//obj是引用堆中的对象,当obj赋值为空,堆中的对象就是垃圾
闭包Closure
function outer() {
let number=5;
return function () {
console.log(number);
}
}
let result= outer() ;
result();//5
//函数套函数,返回出一个函数,反出去的函数在声明处以外被调用,访问外部作用域
闭包由函数以及声明该函数的EC组合而成,正常情况下该销毁的数据,由于外部还有引用,就形成了闭包
广义:所有函数都是闭包(每当函数被创建时,就会在函数生成时生成闭包)
狭义:函数在执行完成时,该销毁的,由于继续被引用,就没有被销毁
函数的嵌套
内部函数在访问外部函数的作用域
内部函数在自身所在作用域意外被调用
闭包的优缺点:
1、延长了生命周期,或者说扩大局部变量的作用范围
2、实现了变量的私有化
3、在内存维持一个变量,就避免了变量污染(重名),但作为常驻内存,使用不当容易造成无效内存
4、变量该销毁的没有销毁,浪费内存,性能略低
多维数组转一维数组
let arr = ['a', 'b', 'c', ['a1', 'b1', 'c1', ['a2', 'b2', 'c2']]];
//字符串方法
let str = arr.join(',')
let arr1 = str.split(',')
//数组的方法
let arr1=arr.flat(Infinity);
//扁平化
let arr2 = []
function render(arr) {
for (let key in arr) {
let item=arr[key];
if (Array.isArray(arr[key])) {
render(item);
} else {
arr2.push(item);
}
}
return arr2
}
render(arr)
console.log(arr2);
函数柯里化
function render(province,region) {
return function (name,age) {
console.log(`我叫${name},今年${age},来自${province}的${region}`);
}
}
let i=render('xx公司','xx部门')
i('张三','29')