基本概念

本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2021-12-23

JavaScript实现

ECMAScript:语法,提供核心语言功能。

文档对象模型(DOM): 提供访问和操作网页内容的方法和接口

浏览器对象模型(BOM):提供与浏览器交互的方法和接口

数据类型

typeof操作符

检测给定变量的数据类型,根据类型会返回相应类型的字符串, “undefined”、 “boolean”、 “string”、 “number”、 “object”、 “function”。

值得注意的是,typeof操作会对nullarray(数组)都会返回 “object”

Number

  • 以0开头会被当做八进制解析

  • 以0x开头会被当做十六进制解析

  • ES能够保存的最小数值保存在Number.MAX_VALUE中

  • ES能够保存的最小数值保存在Number.MIN_VALUE中

  • isFinite()函数在参数位于最小与最大值之间时会返回true

  • Number(),parseFloat(),parseInt()可以把非数值转换为数值,无法转换时时返回NaN。parseInt()函数可以接受第二个参数:转换时使用的进制,默认为十进制。

toString()方法:

除了null和undefined,其他所有的数据类型(包括String类型本身)都拥有toString()方法。在调用数值的toString()方法时,可以接受一个参数,指定转换的进制。

操作符

补码

正数和负数都会以二进制码存储在计算机中,但是是以补码的形式存储的。例如,18的二进制码就是0000 0000 0000 0000 0000 0000 0001 0010,第一位为符号位,1为负数,0为正数;正数的补码就是原码,而负数的补码,绝对值的二进制码、取反、+1三步操作。例如-18的补码就经历3次转换

  • 0000 0000 0000 0000 0000 0000 0001 0010 绝对值18的二进制
  • 1111 1111 1111 1111 1111 1111 1110 1101 取反
  • 1111 1111 1111 1111 1111 1111 1110 1110 加1

最后的1111 1111 1111 1111 1111 1111 1110 1110即为-18在计算机中存储的形式。

值得注意的是,如果以二进制字符串形式输出一个负数时,会在二进制码前加上一个负号

var num1 = 18;
console.log(num1.toString(2));  // "10010"
var num2 = -18;
console.log(num2.toString(2)); // "-10010"

位操作符

  • ~按位非:本质就是操作数的负值减1。应用在数组的一些方法中会用返回-1的情况;~-1等于0即等于false,即在使用indeOf()返回-1时,可以这么用
if (!~arr.indexOf("a")){
  //arr未包含"a"时的处理 
}

var num1 = 18;
var num2 = ~num1;  // -19

var num1 = -18;
var num2 = ~num1;  // 17

var num1 = -1;
var num2 = ~num1;  // 0
~18   结果为 -19

0000 0000 0000 0000 0000 0000 0001 0010 
1111 1111 1111 1111 1111 1111 1110 1101  取反

<!-- 转换为十进制 1开头为负数 先减1 再取反 转换为十进制再加符号 -->
1111 1111 1111 1111 1111 1111 1110 1100  减1
0000 0000 0000 0000 0000 0000 0001 0011  取反 为10011 即 19 然后加符号为 -19
  • &按位与:将两个数值的二进制的每一位对齐,对应位都为1时返回1,任何一位为0都返回0
18 & 3   结果为2

0000 0000 0000 0000 0000 0000 0001 0010 
0000 0000 0000 0000 0000 0000 0000 0011 
<!-- 等于 -->
0000 0000 0000 0000 0000 0000 0000 0010  即2
  • |按位或:将两个数值的二进制的每一位对齐,对应位只要有1时返回1,都为0时才返回0。
18 | 3   结果为19
0000 0000 0000 0000 0000 0000 0001 0010 
0000 0000 0000 0000 0000 0000 0000 0011 
<!-- 等于 -->
0000 0000 0000 0000 0000 0000 0001 0011  即19
  • ^按位异或:将两个数值的二进制的每一位对齐,对应位不同时返回1,相同时返回0。
18 ^ 3   结果为17
0000 0000 0000 0000 0000 0000 0001 0010 
0000 0000 0000 0000 0000 0000 0000 0011 
<!-- 等于 -->
0000 0000 0000 0000 0000 0000 0001 0001  即17
  • 左移 :由两个小于号表示«; 二进制码左移5位,后面以0填充。不会影响到操作数的符号位
var oldValue = 2; 
var newValue = oldValue  << 5; //2的二进制为10 左移5位为 10 00000 等于64;
console.log(newValue); // 64
// -2 << 5  ---  -2左移5位结果为-64  不会影响到操作数的符号位
  • 右移 :由两个大于号表示»; 二进制码右移5位,前面以0填充。不会影响到操作数的符号位
var oldValue = 64; 
var newValue = oldValue >> 5; // 64的二进制为1000000 右移5位为10 等于2;
console.log(newValue); // 2
// 65 >> 5  结果也是2  --- 1000001 右移五位也是10
  • 无符号右移 :由三个大于号表示»>; 二进制码右移5位,前面以0填充,因为0就代表正数,所以正数的无符号右移与右移结果一样,负数就不一样了。
var oldValue = -64; 
var newValue = oldValue  >>> 5; 
console.log(newValue); // 134217726

因为-64的二进制码1111 1111 1111 1111 1111 1111 1100 0000 ,无符号右移会将其当做正数的二进制码(当做不存在符号位)右移5位为0000 0111 1111 1111 1111 1111 1111 1110,即134217726

加性操作符

  • 如果有一个操作符为NaN,结果为NaN
  • Infinety 加 Infinety 结果为 Infinety
  • -Infinety 加 -Infinety 结果为 -Infinety
  • -Infinety 加 Infinety 结果为NaN,减法一样 按常理返回0的均返回NaN
  • 如果两个操作数有一个为字符串,则进行字符串拼接(若另一个不是字符串,则将其转化为字符串进行拼接,对象、数组、布尔值调用toString()方法取得相应的字符串。对于undefined和null调用String()函数取得"undefined"和"null"

关系操作符 > < >= <=

  • 如果两个操作数都是数值,则执行数值比较。
  • 如果两个操作数都是字符串,则按位比较字符编码值。
  • 如果一个操作数是数值,则将另一个操作数转换为数值,然后执行数值比较。
  • 如果一个操作数是布尔值,则先将其转换为数值,然后执行数值比较。
  • 如果一个操作数为对象,则调用对象的valueOf()方法,用得到的结果按照上面的规则进行比较,如果对象没有valueOf()方法,则调用对象的toString()方法,并用得到的结果按照上面的规则进行比较

相等操作数 == === != !==

  • 相等和不相等 先转换在比较,全等和不全等仅比较不转换
  • 相等和不相等在转换时,遵循以下规则
    • 如果有一个操作数为布尔值,则比较前先将其转换为数字,false为0,true为1。
    • 如果一个数值,一个是字符串,会将字符串转换为数字在进行比较
    • 如果一个操作数是对象,另一个不是,会尝试使用对象的valueOf()和toString()方法将对象转换为原始值,用得到的基本类型的值按照前面的规则进行比较,如果两个操作数都是对象则比较的是它们的指针是否指向同一个对象。
    • 任何值和NaN用相等符比较都是false,包括NaN

语句

with语句

with语句的作用是将代码的作用域设置到一个特定的对象中。

var qs = location.search.substring(1);
var hostName = location.hostname;
var url = location.href;

// 可以写成

with(location) {
  var qs = search.substring(1);
  var hostName = hostname;
  var url = href;
}

break和continue语句

break 和continue 语句用于在循环中精确地控制代码的执行。其中,break 语句会立即退出循环,强制继续执行循环后面的语句。而continue 语句虽然也是立即退出循环,但退出循环后会从循环的顶部继续执行。

var num = 0;
for (var i=1; i < 10; i++) {
  if (i % 5 == 0) {
    break;
  }
  num++;
}
alert(num); //4


var num = 0;
for (var i=1; i < 10; i++) {
  if (i % 5 == 0) {
    continue;
  }
  num++;
}
alert(num); //8

switch语句

可以在switch语句中使用任何数据类型(在很多其他语言中只能使用数值),无论是字符串,还是对象都没有问题。其次,每个case的值不一定是常量,可以是变量,甚至是表达式。

switch 语句在比较值时使用的是全等操作符,因此不会发生类型转换。

switch (i) {
  // 合并两种情形
  case 25:
  case 35:
    alert("25 or 35");
    break;
  case 45:
    alert("45");
    break;
  default:
    alert("Other");
}

垃圾收集

JavaScript具有自动垃圾收集机制,也就是说,执行环境会负责管理代码执行过程中使用的内存。这种垃圾收集机制的原理其实很简单:找出那些不再继续使用的变量,然后释放其占用的内存。为此,垃圾收集器会按照固定的时间间隔(或代码执行中预定的收集时间),周期性地执行这一操作。

函数中局部变量的正常生命周期。局部变量只在函数执行的过程中存在。而在这个过程中,会为局部变量在栈(或堆)内存上分配相应的空间,以便存储它们的值。然后在函数中使用这些变量,直至函数执行结束。此时,局部变量就没有存在的必要了,因此可以释放它们的内存以供将来使用。在这种情况下,很容易判断变量是否还有存在的必要;但并非所有情况下都这么容易就能得出结论。垃圾收集器必须跟踪哪个变量有用哪个变量没用,对于不再有用的变量打上标记,以备将来收回其占用的内存。用于标识无用变量的策略可能会因实现而异,但具体到浏览器中的实现,则通常有两个策略。

标记清除

JavaScript中最常用的垃圾收集方式是标记清除(mark-and-sweep)。当变量进入环境(例如,在函数中声明一个变量)时,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为“离开环境”。

可以使用任何方式来标记变量。比如,可以通过翻转某个特殊的位来记录一个变量何时进入环境,或者使用一个“进入环境的”变量列表及一个“离开环境的”变量列表来跟踪哪个变量发生了变化。

垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记。然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记。其他还拥有标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾收集器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。

引用计数

引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则该值的引用次数加1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减1。

当这个值的引用次数变成0 时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾收集器下次再运行时,它就会释放那些引用次数为零的值所占用的内存。

缺陷:循环引用。循环引用指的是对象A 中包含一个指向对象B 的指针,而对象B 中也包含一个指向对象A 的引用。那么这两个对象的引用永远不会为0

管理内存

JavaScript在进行内存管理及垃圾收集时面临的问题还是有点与众不同。其中最主要的一个问题,就是分配给Web浏览器的可用内存数量通常要比分配给桌面应用程序的少。这样做的目的主要是出于安全方面的考虑,目的是防止运行JavaScript的网页耗尽全部系统内存而导致系统崩溃。内存限制问题不仅会影响给变量分配内存,同时还会影响调用栈以及在一个线程中能够同时执行的语句数量。

因此,确保占用最少的内存可以让页面获得更好的性能。而优化内存占用的最佳方式,就是为执行中的代码只保存必要的数据。 一旦数据不再有用,最好通过将其值设置为null 来释放其引用——这个做法叫做解除引用(dereferencing)。这一做法适用于大多数全局变量和全局对象的属性。局部变量会在它们离开执行环境时自动被解除引用。

解除一个值的引用并不意味着自动回收该值所占用的内存。解除引用的真正作用是让值脱离执行环境,以便垃圾收集器下次运行时将其回收。