个人向,对JS知识进行了查漏补缺,主要来源于《JS高级程序设计》和网上博客,本文内容主要包括以下:
- 在HTML中引入JS
- 数据类型基础
- 作用域
- 垃圾回收
一、在HTML中引入JS
- async 和 defer
普通情况<script src="script.js"></script>
遇到该语句,阻塞原渲染过程,立刻加载并执行脚本
async<script async src="script.js"></script>
遇到该语句立刻异步加载,加载时不阻塞原渲染,加载完毕立刻执行,执行时阻塞原渲染
defer<script defer src="myscript.js"></script>
遇到该语句立刻异步加载,加载时不阻塞原渲染,加载完毕不执行,等到html都渲染好了再执行。
目前来说,就实用性兼容性,把script丢在body最后面是最佳的。
二、数据类型基础
- 数据类型
- 简单数据类型:Undefined Null Number String Boolean Symbol
- 复杂数据类型:Object
- typeof
- typeof null -> 'object'
- typeof (function(){}) -> 'function‘
- null 和 undefined
- null是空对象指针。
- undefined由null派生,所以 undefined == null。
- Number:
- 浮点数相加精度不够
0.1+0.2 === 0.30000000000000004 - 超过数字Number.MAX_VALUE就会变成Infinity,小于最小就是-Infinity。【检测函数 isFinite()】
- NaN 不是一个数字。任何涉及NaN的运算操作都返回NaN。而且NaN不等于自身【检测函数 isNaN() 把不能转换为数字的都返回True】
- 把对象转为数字的方法: Number() ParseInt() ParseFloat()
- 浮点数相加精度不够
- String
- 把对象转化为字符串的方法:String() 和 toString()
- Object
- valueOf() 返回对象的字符串、数值、布尔值表示,通常返回值和toString()一样(通常返回字符串)
- 函数
- 函数默认返回:undefined
- arguments对象:函数参数可以访问传入的任意数量,都会被收入argument对象。
三、 作用域
执行环境(execution context):定义了变量或函数有权访问的其他数据,决定了它们各自的行为。
变量对象(variable object):与执行环境关联,环境中定义的所有变量和函数都保存在这个对象中。(代码不可访问,但解析器会使用它)
关于执行环境和变量对象的扩展,摘自知乎:执行环境(Execution Context,简称Context)只是一个抽象概念,在具体JS Engine实现中,它对应很多内容,变量对象(Variable Object,简写VO)是其一,还有Scope Chain,this等,这些共同组成了执行环境这个概念。
全局执行环境:在Web浏览器中,全局执行环境是window对象,因此所以全局变量和函数都是作为window对象的属性和方法创建的。
环境的销毁:环境中的所有代码执行完毕后,该环境销毁,保存在其中的所有变量和函数定义也随之销毁。而全局执行环境是在应用程序退出后销毁。(比如window环境在关闭浏览器窗口时销毁)
执行流:每个函数都有自己的执行环境,当执行流进入一个函数的时候,函数的环境会被推入一个环境栈(execution context stack),函数执行后,环境出栈,控制权返还给原先的执行环境。
❗作用域链(scope chain):
(1)创建:代码在环境中执行,就会创建变量对象的作用域链。
(2)用途:保证对 执行环境 “有权访问的所有变量和函数” 的 有序访问。
(3)描述:作用域链的最前端,就是当前执行代码所在环境的变量对象。下一个变量对象来自包含环境,再下一个来自下一个包含环境,延续到全局执行环境。(如果环境是函数,则将其活动对象(activation object) 作为变量对象,活动对象最开始只包含一个变量,即arguments对象)
(4)延长作用域链:try-catch的catch内会将"错误对象"添加到作用域链前端,with会将"指定对象"添加到作用域链前端
做个题增强理解一下:
/* 以下的代码的输出是什么? */
var name = 'globalName';
function func() {
console.log(name);
var name = 'funcName';
console.log(name);
console.log(age);
}
funcA();
有些人会认为是: globalName funcName ReferenceError
其实是: undefined funcName ReferenceError
为什么呢?因为根据作用域链最前面的是所在环境的变量对象,所以我们应该先局限于func内去想一下会出现什么结果:由于func内存在name声明赋值语句,根据变量提升,name的声明将被提升到函数的最顶部。初始值这时候应该是默认的undefined。由于在此变量对象中找到了想要的东西,就不用去拿下一个变量的对象的值了。
四、垃圾收集
JS的垃圾回收是自动的,垃圾收集器的最基本原理是:找出那些不再继续使用的变量,然后释放其占用的内存。垃圾回收器会按照固定的时间间隔周期性地执行这些操作。
JS的垃圾回收方法有:
-
标记清除(mark and sweep):
- 标记:当一个变量进入环境的时候,标志该变量为“进入环境”,当变量离开环境的时候,将其标记为“离开环境”。因为只要执行流进入相应的环境,就有可能用到这些变量,所以在环境的变量不能被清除。
- 清除过程:垃圾收集器在运行的时候给存储在内存的所有变量都加上标记,然后,去掉环境中的变量和被它们引用的变量的标记。然后把剩下的有标记的当成垃圾,销毁他们的值并回收他们的空间,完成内存清除。
-
引用计数(reference counting) - 不常见 有缺陷
- 垃圾收集器会清除引用次数为0的值。
- 模拟过程:
声明变量并赋值引用类型:该值的引用次数 = 1
赋给另一个变量: 该值的引用次数 + 1 = 2
引用该值的变量取了另外一个值:该值的引用次数 - 1 = 1
该值的引用次数为0:被清理回收
管理内存:当你的对象不需要的时候,可以手动解引用,赋一个null,最根本的方法,让值脱离环境。