思考问题:
一般都把js放在body末尾
1,为什么?
2,放在head可以吗?怎样才可以放到head中和放到body末尾一样效果?
3,script标签有两个属性:defer/async,这两个属性是做什么的?
评论区找答案,帮忙解决?
数据渲染机制
var a =12;
1.先声明-一个变量a,没有赋值(默认值是undefined)
2.在当前作用域中开辟- -个位置存储12这个值
3.让变量a和12关联在- -起(定义:赋值)
当浏览器加载页面(加载js代码)的时候,首先会形成一个全局作用域window,提供代码之上而下执行的环境。
当 ary1=null(空对象),ary2=null,那么那个堆就不被谁占用了,也就可以回收了。
栈内存:作用域
1.提供-个供JS代码自上而下执行的环境(代码都是在栈中执行的)
2.由于基本数据类型值比较简单,他们都是直接在栈内存中开辟;一个位置,把值直接存储进去的
=>当栈内存被销毁,存储的那些基本值也都跟着销毁了
堆内存:引用值对应的空间
1.存储引用类型值的(对象:键值对函数:代码字符串)= >当前堆内存释放销毁,那么这个引用值彻底没了
=>堆内存的释放:当堆内存没有被任何的变量或者其它东西所用,浏览器会在空闲的时候,自主的进行内存回收,把所有不被占用的堆内存销毁掉(谷歌浏览器)
执行过程:作用域形成->栈内存/堆内存形成->代码执行
变量提升机制
在当前作用域形成之后,js代码自上而下执行之前,浏览器首先会把所有带var,function关键词的变量进行提前的声明或者(声明和定义)
过程:
1 2
图解浏览器数据渲染过程 注意: 只有function类型才可以在变量提升阶段进行赋值,其他不行,包括数组
私有作用域里的私有变量和外边没关系,这就是闭包
ES6中let不存在变量提升!
老套的变量提升机制(es2,es3)不严谨,你在上面提升的地方能用,下面也能用,不严谨,也不方便维护。
ES6语法:
ES6切断了全局变量和window属性的映射机制
在相同作用域中,基于let不能声明相同名字的变量,会报错,还没执行代码就会报错
第一行报错:
一个小案例: 结果:
20 20
10 20
没有let和const就和ES6没有关系
在代码中同时有老语法和新语法的时候,浏览器会自己识别,然后分别用老机制和新机制
闭包作用域
两道题区分全局作用域和私有作用域
例题1: 函数内部执行过程(首先形成私有作用域)
1,形参赋值
a=12
2,变量提升
var b;
记住哈,是先形参赋值
=>在私有作用域中,只有以下两种情况是私有变量
1:声明过的变量(带VAR/FUNCTION)
2:形参也是私有变量
剩下的都不是自己私有的变量,都需要基于作用域链的机制向上查找
重点:形参也是私有变量
结果:
b是undefined,是因为首先b是私有变量,然后它用的是老语法,所以按照变量提升,私有作用域第一行打印时还没有赋值。
形参也是私有变量,所以a是私有变量
而不是私有变量的c会去升一级作用域找。
例题2: 图解: 函数里面的ary是形参变量,是私有变量,虽然它和外面那个ary名字一样,所指向的地址也一样,但是它们不是同一个变量。。
无变量提升
ary=[100]是一个新数组,开辟一个新的堆内存。此时私有变量指向这个新的堆内存 而且一个变量只能指向一个空间,所以,
结果:
寻找上级作用域
两道题搞懂寻找上级作用域
例题1:
结果:
12
哈哈,没想到吧?居然是这样的
原因:一个函数函数在执行前,形成一个私有作用域A,A的上级作用域和函数在哪里执行没有关系,只和它在哪里创建有关系
一个知识点-了解即可(现在js开发是严格模式,不让用了)
arguments:实参集合
arguments. callee:函数本身FN
arguments. callee. caller:当前函数在哪执行的, CALLER就是谁(记录的是它执行的宿主环境)
比如:
可以看到打印的就是fn()的宿主环境
在全局下执行,caller的结果是null
例题2: 结果:21,22,23,10
var x =fn()其实就是在执行fn(),执行打印了21,然后顺便返回了f()的地址给x
所以,下两步的x()都指向f
画图分析: