当前位置: 移动技术网 > IT编程>网页制作>技巧 > 作用域,作用域链&&闭包

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

2020年07月24日  | 移动技术网IT编程  | 我要评论

作用域&&作用域链

作用域

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

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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网