JavaScript基础(end)

这一期很重要,涉及以后你对函数变量的理解,以及你调用函数不生效时怎么办,最主要的原因是讲的这些玩意面试时是一定会问的。

JS执行流程

1.预编译阶段,代码的检查装载阶段,此过程中进行变量和函数的声明,但是不对变量进行赋值,变量默认值为undefined 

怎么理解,就是所有的声明(此时可以理解为变量声明),都提升到当前作用域最上面的位置,有变量名,但是没有值,就是声明不赋值,所以默认是undefined 。

例如我声明了一个变量:

  var a=99;

  console.log(a);

结果很正常就是99,那我们不声明变量,相当于直接:

console.log(a);

d4e338e188c8429198234f6de21bccad.png

结果是not defined,我们再声明一个变量不赋值:

 var a;

 console.log(a);

fbd991eeb95148bebd87a77868f0d860.png

由此我们得出:

not defined:没有声明就调用,找不到这个变量。

undefined:声明了变量,没有赋值,找不到这个变量。

那我把调用放前面:

console.log(a);

var a=99;

2c3001521a9e4b71b98bd7e1714f034c.png

按道理说,代码从上到下执行,在前面找不到a相当于没有声明变量,不应该是not defined吗,为什么是undefined?这是因为函数执行两个阶段:

①预编译阶段,把所有的全局变量提升到作用域的最上面,只声明不赋值

②函数执行阶段

所以预编译阶段把var a=99;提升到了最上边,就成了undefined。

那好我再写个函数:

 fn();

  function fn(){

    console.log('我是fn函数');

  }

结果是什么?undefined吗?还是not defined?都不是

ece452428b5e4d92a59590c17ed9f100.png

因为上面我没说,在函数的预编译阶段还有:

把整个函数提升到最上面,函数在变量之前。

还没完:

fn();

  var fn = function(){

    console.log('我是fn函数');

  }

那这个结果是什么?

e0c4d0b2b0d1412bacbe72f800943202.png

很好解释了,它把函数赋值给变量,把变量提升到最高,所以用函数的方法调用,当然就告诉你这不是一个函数了,如果改成console.log(fn);就又和原来一样是undefined了。

还没完!如果是下面这个东西会有什么结果:

var a =99;
function fn(){
    console.log(a);
    var a=100;
    console.log(a);
  }                                                                  fn();

5a8397fd5dc24939a85ee6d3b96dcc66.png

你可以叫它局部变量遮蔽,因为在预编译阶段也会在函数内部(局部),把局部变量提升到局部最高,

function fn(){

    var a;相当于提升到这

    console.log(a);//undefined

    var a=100;

    console.log(a);//100

  }

提升完之后,它会向上找它就近的,这是一个就近原则。

2.代码的执行阶段,对变量进行赋值和函数的声明。

作用域链

前面也算是提过了,直接总结:

原则:①就近原则

       ②向上取值

       ③永不向下取值

解释一下向上取值,如果就近没有,就会向外取值,可能一解释反而糊涂了,就相当于上面说的局部作用域中没有,就会向上一级其父作用域中找。就像套娃一样,这个娃里没有,就到套它的娃里面找,以此类推。

闭包

闭包就是能够读取其他函数内部变量的函数。

像管道一样连接起函数内外。

比如这个函数

function fn(){
    var a=100;
  }

a是个局部变量,外边直接拿拿不到,有同学说可以return出来,好:

function fn(){

    var a=100;

    return a;

  }

var a=fn();

console.log(a);

没问题,但这个方法,拿出来的只是这个值,后面会说垃圾回收机制,它会快速把这玩意给你清除掉,我们就不讨论了。

那怎么拿?我们刚说了作用域链,让它的子作用域向上取值给它带出来:

function fn(){

    var a=100;

    function fn2(){

      console.log(a);

    }

    fn2();

  }

fn();

7e8079fb43114191bcb747761d7ee4b4.png

好,思路没问题,那是不是可以让它儿子当个特务,给它return出来,


function fn(){
    var a=100;
    function fn2(){
      return a;
    }
    return fn2();
  }
console.log(fn());

589eb90e72d34966bead33dab7227e9c.png

 打印fn( ),它要给你return出fn2( ),正好fn2要给你return a,又向上取值把a取出来,不这么写,直接return fn2这个函数,

function fn(){

    var a=100;

    function fn2(){

      return a;

    }

    return fn2;

  }

var fn3 =fn();

console.log(fn3());

取出fn2整个函数,再赋值调用,是不是也能取出来,还能写简单点:

function fn(){

    var a=100;

  return  function(){

      return a;

    }

  }

var fn3 =fn();

console.log(fn3());

整成匿名函数在前面return出去,也是可以的,这相当于:

var fn3 =  function(){

      return a;

    } 赋值给了fn3,

其实这些靠卧底儿子取爹东西的写法就是闭包,因为return的结果不但把函数拿出来了,还连带把作用域return出来了,通过作用域链向上取出来。

闭包的好处

保护变量不被污染,保护什么变量?保护全局变量不被污染。

现在,把这句话牢牢记住:尽可能少的声明全局变量

先要理解什么是污染,例如这有个函数:

var a =0;

function fn(){

  a++;

  console.log(a);

}

fn();

fn();

fn();

fn(); 结果很明显是1,2,3,4

08efb69dbc094b1287b82ce7f24b8c27.png

 但我写了很多东西,我在调用前有给它赋上别的值,后面就出错了,此时就对这个变量产生了污染。

此时我们只想让变量在那个函数内生效,那怎么做?用闭包:

function fn(){

  var a =0;

  return function(){

  a++;

  console.log(a);

}

}

a=2;

var fn1=fn();

fn1();

fn1();

da8ec181d7174f6f8e2c03e106ec0955.png

不受影响,非常完美,就算再次赋值,调用也是从头开始,互相不受影响

闭包的缺点与解决办法

function a(){
  return function(){
    console.log('你好');
  }
}
var a1=a();
var a2=a();
console.log(a1==a2);

c953a78d8ba04a538755390988de88b3.png

a1为什么和a2不是一个东西?每次调用都开辟一块新空间,所以两者不同,a1 a2之间没有关系,保证了不被污染。

 

那缺点呢?

平时使用网页为什么它会越用越卡呢,因为每次调用不断的去开辟新空间,关闭页面对你的内存进行释放就不卡了,对于垃圾机制而言,用完后没用的变量它会进行一个回收,没有用的函数会被清空作用域。

但是关键在于一个没用,有用的肯定不清除,那如果它迟迟不清除,就会造成这个问题:

内存泄露

说个题外话,对于一些单机游戏,有很多外挂,改个金币属性什么的,它就是通过一些手段,从你内存里堆积的变量中找到对应的值改掉就行,很多游戏是用c需要来写的,上大学或多或少都学过,这是门比较难的语言,难就难在它的垃圾回收上,c语言没有垃圾回收机制,要手动清理,很多东西就会忘记清理,慢慢的堆积在那就形成了内存泄漏,所以就可以侵入到内存中找到对应的值修改就可以。

讲了这么多胡话,应该对内存泄露有了一个更直观的了解。

归根结底,一直保存在内存中就造成内存泄露,垃圾回收机制就是把没用的给你清理掉。

后面还会更准确的介绍两种垃圾回收机制的办法。

 

对于全局变量来说,只有在关闭页面的时候它会被回收,那垃圾回收机制为什么不把它清理掉,因为我们定义全局变量,就是为了在任意的时候来进行调用,所以不能随便回收,只要页面不关闭,它就会一直在页面中,就会造成内存泄露,这就是为什么要让大家尽量少声明全局变量。

那闭包为什么会造成内存泄露,就拿这个当例子:

function fn(){

  var a =0;

  return function(){

  a++;

  console.log(a);

}

} fn有一个变量,而且内部又有一个函数指向它,它既然有用为什么要清除它,那有同学说,把里面的变量清空不就行了,确实,但闭包完美的卡了个bug:闭包内的变量和参数都不会被回收

这也就导致闭包这个东西不会被清空。

面试聊这个问题,下一个肯定是怎么解决闭包问题,解决方法也比较简单,想释放时,给该闭包函数,赋一个null值

一赋值空就自动被回收了,

还是要提醒慎用闭包,忘记清空会让页面越用越卡。

js的基础到这里就弄完了,下面就是js的高级进阶学习了。

谢谢诸位陪伴,祝身体健康,事业有成。

  • 21
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值