作用域,作用域链&&闭包
作用域&&作用域链
作用域
全局作用域: 最外层函数定义的变量拥有全局作用域,即对任何内部函数来说,都是可以访问的
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