欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

作用域,作用域链&&闭包

程序员文章站 2022-06-10 14:53:21
作用域&&作用域链作用域​全局作用域: 最外层函数定义的变量拥有全局作用域,即对任何内部函数来说,都是可以访问的var outerVar = "outer";function fn(){console.log(outerVar);}fn();//输出:outer​|注意: 只要函数内定义了一个局部变量,函数在解析的时候都会将这个变量“提前声明”var scope = "global";function fn(){console.log(scope);//und...

作用域&&作用域链

作用域

全局作用域: 最外层函数定义的变量拥有全局作用域,即对任何内部函数来说,都是可以访问的

var outerVar = "outer";
function fn(){
	console.log(outerVar);
}
fn();//输出:outer

​ |注意: 只要函数内定义了一个局部变量,函数在解析的时候都会将这个变量“提前声明”

var scope = "global";
function fn(){
	console.log(scope);//undefined   没有输出global是因为下面定义的scope变量提升了
    var scope = "local";
    console.log(scope);//result:local;
}
fn();

局部作用域: 局部作用域一般只在固定的代码片段内可访问到,而对于函数外部是无法访问的。

​ |注意: 函数内部声明变量的时候,一定要使用var,否则实际上声明了一个全局变量

function fn(){
	var inner = "inner";
}
fn();
console.log(innerVar);//inner is not defined =>inner在函数内部被定义

作用域与执行上下文的区别

​ |局部作用域在函数定义时就确定了;函数执行上下文是在调用函数时,函数体代码执行时确定

​ |作用域是静态的,只要函数定义好了就一直存在,且不会再发生变化;上下文环境是动态的,函数调用结束后上下文环境会被释放

​ |上下文环境(对象)从属于所在的作用域

作用域链

​ 首先在当前作用域下的执行上下文查找,若没有则在上一级作用域的执行上下文查找,没有找到则重复此操作,直到全局作用域,全局也没有则报错

var x = 10
function fn(){
    console.log(x)  //在fn里找x fn里没有在全局找 =>10 与show里的20无关
}
function show(f){
    var x= 20
    f()
}
show(fn)  //传函数fn
var fn = function(){
    console.log(fn)
}
fn() //可以输出fn函数
var obj = {
    fn2:function(){
        console.log(fn2)
    }
}
obj.fn2()  //报错:fn2 is not defined => 需要修改成this.fn2才可以找到fn2这个函数

闭包

定义

​ 当一个内部函数被调用,就会形成闭包,闭包就是能够读取其他函数内部变量的函数 (内部函数引用了外部函数的数据/函数); 可以理解成“函数中的函数“

function f1(){
   var m = 10;
   function f2(){
     console.log(m); // 10
   }
     f2()
}
f1()

使用

​ 1.可以读取自身函数外部的变量(沿着作用域链寻找)

​ 2.让外部变量始终保存在内存中 (用一个变量指向它)

常见的闭包

​ 1.将函数作为另一个函数的返回值(不知道我的理解对不对)

function fn1(){
    var a=2
    console.log('111')
    function fn2(){
        a++
        console.log(a)
    }
    return fn2
}
var f = fn1() //只会输出111

//加上这两行代码 => 产生两个闭包
f() //3 相当于将fn2 return了给f =>fn1()() 不会输出111
f()	//4 闭包不会消失(因为f指向了fn2) 在原来的基础上+1

​ 2.将函数作为实参传递给另一个函数调用

function showDelay(msg,time){
    setTimeout(function(){
        alert(msg) //可以alert
    },time)
}
showDelay('qwq',1000)

生命周期

​ 产生:在嵌套内部函数定义执行完时产生(不是调用)

​ 死亡:嵌套的内部函数成为垃圾对象(上述例子,令f = null )

应用:自定义JS模块

var myfun = fn1()
function fn1() {
    var m='我的模块1';
    function a() {
        console.log(m+':a函数');
    }
    function b() {
        console.log(m+':b函数');
    }
    return {
      a:a,
      b:b
    };
}
myfun.a()
myfun.b()

(function(){
    var m='我的模块1';
    function a() {
        console.log(m+':a函数');
    }
    function b() {
        console.log(m+':b函数');
    }
    window.myfun = {
        a:a,
        b:b
    }
})()
myfun.a()
myfun.b()

以上两种方式都可以建立模块,第二种方式更加简单一些,不需要执行函数(fn1)就能得到闭包

内存溢出与内存泄漏

​ 闭包缺点:函数执行完后,局部变量未释放,占用内存时间变长,容易造成内存泄漏

​ 内存泄漏程序还能正常运行,内存溢出会崩溃

内存溢出

​ 程序运行需要的内存超过了剩余的内存

内存泄漏

​ |占用的内存没有及时释放

​ |内存泄漏积累过多 => 内存溢出

​ |常见的内存泄漏:

​ 1.意外的全局变量

​ 2.没有及时清理计时器或回调函数

​ 3.闭包

测试题

var name = "window"
var object = {
	name:"my object",
    getName: function(){
      	return function(){
            return this.name
        }
    }
}
alert(object.getName()())  //window 直接执行return function那个函数 相当于window调用 =>this是window
var name = "window"
var object = {
	name:"my object",
    getName: function(){
        var that = this
      	return function(){
            return that.name
        }
    }
}
alert(object.getName()())  //my object this依旧是window 但return的是that => object

一道面试题

function fun(n,o){
    console.log(o);
    return {
        fun:function(m){
            return fun(m,n);
        }
    };
 }

//问:三行a,b,c的输出分别是什么?
var a = fun(0);   a.fun(1);   a.fun(2);  a.fun(3);//undefined  0  0  0
var b = fun(0).fun(1).fun(2).fun(3);////undefined  0  1  2
var c = fun(0).fun(1);  c.fun(2);  c.fun(3);//undefined  0  1  1

/*当调用函数的时候,传入的实参比函数声明时指定的形参个数少时,剩下的形参都被设定为undefined
所以调用函数var a=fun(0) o未被定义输出undefined*/
//a的值是调用fun(0)后返回的对象
fun:function(m){
	return fun(m,0);
}
//a.fun(1); a.fun(2); a.fun(3) o都为0 所以都输出0

/*b和a的不同在于,var a = fun(0); 之后一直用的是a这个对象,而b每次用的都是上次返回的对象*/
//b = fun(0).fun(1)
function fun(n,o){ //n的值为1,o的值为0
        console.log(o);
        return {
            fun:function(m){
                return fun(m,n);//n的值为1
            }
        };
}
fun(1,0);  //输出0,并返回一个对象,这个对象有一个fun的方法,这个方法调用后,会返回外层fun函数调用的结果,并且外层函数的第二个参数是n的值,也就是1  之后同理 返回fun(2,1) fun(3,2)

/*c 与他们的不同,只是var c = fun(0).fun(1); 之后用的是同一个对象罢了,c相当于b与a的结合*/

本文地址:https://blog.csdn.net/YogaLin_qwq/article/details/107544401

相关标签: javascript