Damn1t
for you I bleed myself dry
FRIENDS
baidu

JS(1)

2019-10-28 js

JS(1)

前言

JavaScript 是一种轻量级的脚本语言。所谓“脚本语言”(script language),指的是它不具备开发操作系统的能力,而是只用来编写控制其他大型应用程序(比如浏览器)的“脚本”。

JavaScript 也是一种嵌入式(embedded)语言。它本身提供的核心语法不算很多,只能用来做一些数学和逻辑运算。JavaScript 本身不提供任何与 I/O(输入/输出)相关的 API,都要靠宿主环境(host)提供,所以 JavaScript 只合适嵌入更大型的应用程序环境,去调用宿主环境提供的底层 API。

目前,已经嵌入 JavaScript 的宿主环境有多种,最常见的环境就是浏览器,另外还有服务器环境,也就是 Node 项目。

从语法角度看,JavaScript 语言是一种“对象模型”语言。各种宿主环境通过这个模型,描述自己的功能和操作接口,从而通过 JavaScript 控制这些功能。但是,JavaScript 并不是纯粹的“面向对象语言”,还支持其他编程范式(比如函数式编程)

基本语法

变量提升

JavaScript 引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。这造成的结果,就是所有的变量的声明语句,都会被提升到代码的头部,这就叫做变量提升(hoisting)

标识符

JavaScript 语言的标识符对大小写敏感,所以aA是两个不同的标识符

非法标识符:

1
2
3
4
5
1a  // 第一个字符不能是数字
23 // 同上
*** // 标识符不能包含星号
a+b // 标识符不能包含加号
-d // 标识符不能包含减号或连词线

中文是合法的标识符,可以用作变量名。

1
var 临时变量 = 1;

三元运算符 ?:

1
(条件) ? 表达式1 : 表达式2

上面代码中,如果“条件”为true,则返回“表达式1”的值,否则返回“表达式2”的值。

1
var even = (n % 2 === 0) ? true : false;

标签(label)

JavaScript 语言允许,语句的前面有标签(label),相当于定位符,用于跳转到程序的任意位置,标签的格式如下。

标签可以是任意的标识符,但不能是保留字,语句部分可以是任意语句。

标签通常与break语句和continue语句配合使用,跳出特定的循环。

1
2
3
4
5
6
7
8
9
10
11
top:
for (var i = 0; i < 3; i++){
for (var j = 0; j < 3; j++){
if (i === 1 && j === 1) break top;
console.log('i=' + i + ', j=' + j);
}
}
// i=0, j=0
// i=0, j=1
// i=0, j=2
// i=1, j=0

标签也可以用于跳出代码块。

1
2
3
4
5
6
7
8
foo: {
console.log(1);
break foo;
console.log('本行不会输出');
}
console.log(2);
// 1
// 2

数据类型

简介

  • 数值(number):整数和小数(比如1和3.14)
  • 字符串(string):文本(比如Hello World)。
  • 布尔值(boolean):表示真伪的两个特殊值,即true(真)和false(假)
  • undefined:表示“未定义”或不存在,即由于目前没有定义,所以此处暂时没有任何值
  • null:表示空值,即此处的值为空。
  • 对象(object):各种值组成的集合。

数值、字符串、布尔值这三种类型,合称为原始类型(primitive type)的值.对象则称为合成类型(complex type)的值,因为一个对象往往是多个原始类型的值的合成,可以看作是一个存放各种值的容器。至于undefinednull,一般将它们看成两个特殊值。

对象又可以分为:

  • 狭义的对象(object)
  • 数组(array)
  • 函数(function)

数据类型确定

JavaScript 有三种方法,可以确定一个值到底是什么类型。

  • typeof运算符
  • instanceof运算符
  • Object.prototype.toString方法

数值

整数和浮点数

JavaScript 内部,所有数字都是以64位浮点数形式储存,即使整数也是如此。所以,11.0是相同的,是同一个数。

JavaScript 语言的底层根本没有整数,所有数字都是小数(64位浮点数)。容易造成混淆的是,某些运算只有整数才能完成,此时 JavaScript 会自动把64位浮点数,转成32位整数,然后再进行运算

1
2
3
4
5
6
7
8
0.1 + 0.2 === 0.3
// false

0.3 / 0.1
// 2.9999999999999996

(0.3 - 0.2) === (0.2 - 0.1)
// false

数值精度

根据国际标准 IEEE 754,JavaScript 浮点数的64个二进制位,从最左边开始,是这样组成的。

  • 第1位:符号位,0表示正数,1表示负数
  • 第2位到第12位(共11位):指数部分
  • 第13位到第64位(共52位):小数部分(即有效数字)

一个数在 JavaScript 内部实际的表示形式,精度最多只能到53个二进制位,这意味着,绝对值小于2的53次方的整数,即-253到253,都可以精确表示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Math.pow(2, 53)
// 9007199254740992

Math.pow(2, 53) + 1
// 9007199254740992

Math.pow(2, 53) + 2
// 9007199254740994

Math.pow(2, 53) + 3
// 9007199254740996

Math.pow(2, 53) + 4
// 9007199254740996

JavaScript 对15位的十进制数都可以精确处理

1
2
3
4
5
6
Math.pow(2, 53)
// 9007199254740992

// 多出的三个有效数字,将无法保存
9007199254740992111
// 9007199254740992000

上面示例表明,大于2的53次方以后,多出来的有效数字(最后三位的111)都会无法保存,变成0。

数值范围

根据标准,64位浮点数的指数部分的长度是11个二进制位,意味着指数部分的最大值是2047(2的11次方减1)。也就是说,64位浮点数的指数部分的值最大为2047,分出一半表示负数,则 JavaScript 能够表示的数值范围为2^10242^-1023(开区间),超出这个范围的数无法表示。

如果一个数大于等于21024次方,那么就会发生“正向溢出”,即 JavaScript 无法表示这么大的数,这时就会返回Infinity

1
Math.pow(2, 1024) // Infinity

如果一个数小于等于2-1075次方(指数部分最小值-1023,再加上小数部分的52位),那么就会发生为“负向溢出”,即 JavaScript 无法表示这么小的数,这时会直接返回0。

1
Math.pow(2, -1075) // 0

下面是一个实际的例子。

1
2
3
4
5
6
7
var x = 0.5;

for(var i = 0; i < 25; i++) {
x = x * x;
}

x // 0

上面代码中,对0.5连续做25次平方,由于最后结果太接近0,超出了可表示的范围,JavaScript 就直接将其转为0。

JavaScript 提供Number对象的MAX_VALUEMIN_VALUE属性,返回可以表示的具体的最大值和最小值。

1
2
Number.MAX_VALUE // 1.7976931348623157e+308
Number.MIN_VALUE // 5e-324

数值表示

除了一般表示法以外,也可以用科学记数法

1
2
123e3 // 123000
123e-3 // 0.123

科学计数法允许字母eE的后面,跟着一个整数,表示这个数值的指数部分。

以下两种情况,JavaScript 会自动将数值转为科学计数法表示,其他情况都采用字面形式直接表示。

  1. 小数点前的数字多于21位。
  2. 小数点后的零多于5个。

正零和负零

1
2
3
-0 === +0 // true
0 === -0 // true
0 === +0 // true

唯一有区别的场合是,+0-0当作分母,返回的值是不相等的。

1
(1 / +0) === (1 / -0) // false

infinity

NaN

与数值相关的全局方法

parseInt()
parseFloat()
isNaN()
isFinite()

字符串

字符

由于 HTML 语言的属性值使用双引号,所以很多项目约定 JavaScript 语言的字符串只使用单引号

Base64

JavaScript 原生提供两个 Base64 相关的方法。

  • btoa():任意值转为 Base64 编码
  • atob():Base64 编码转为原来的值

字符集

JavaScript 使用 Unicode 字符集。JavaScript 引擎内部,所有字符都用 Unicode 表示。

每个字符在 JavaScript 内部都是以16位(即2个字节)的 UTF-16 格式储存。也就是说,JavaScript 的单位字符长度固定为16位长度,即2个字节。

但是,UTF-16 有两种长度:对于码点在U+0000U+FFFF之间的字符,长度为16位(即2个字节);对于码点在U+10000U+10FFFF之间的字符,长度为32位(即4个字节),而且前两个字节在0xD8000xDBFF之间,后两个字节在0xDC000xDFFF之间。举例来说,码点U+1D306对应的字符为𝌆,它写成 UTF-16 就是0xD834 0xDF06

对于码点在U+10000U+10FFFF之间的字符,JavaScript 总是认为它们是两个字符(length属性为2)。所以处理的时候,必须把这一点考虑在内,也就是说,JavaScript 返回的字符串长度可能是不正确的。

对象

概述

简单说,对象就是一组“键值对”(key-value)的集合,是一种无序的复合数据集合。

如果键名是数值,会被自动转为字符串。

对象的每一个键名又称为“属性”(property),它的“键值”可以是任何数据类型。如果一个属性的值为函数,通常把这个属性称为“方法”,它可以像函数那样调用

1
2
3
4
5
6
7
var obj = {
p: function (x) {
return 2 * x;
}
};

obj.p(1) // 2

如果属性的值还是一个对象,就形成了链式引用

1
2
3
4
5
var o1 = {};
var o2 = { bar: 'hello' };

o1.foo = o2;
o1.foo.bar // "hello"

属性可以动态创建,不必在对象声明时就指定。

如果不同的变量名指向同一个对象,那么它们都是这个对象的引用,也就是说指向同一个内存地址。修改其中一个变量,会影响到其他所有变量。

1
2
3
4
5
6
7
8
var o1 = {};
var o2 = o1;

o1.a = 1;
o2.a // 1

o2.b = 2;
o1.b // 2

上面代码中,o1o2指向同一个对象,因此为其中任何一个变量添加属性,另一个变量都可以读写该属性。

此时,如果取消某一个变量对于原对象的引用,不会影响到另一个变量。

1
2
3
4
5
var o1 = {};
var o2 = o1;

o1 = 1;
o2 // {}

上面代码中,o1o2指向同一个对象,然后o1的值变为1,这时不会对o2产生影响,o2还是指向原来的那个对象。

但是,这种引用只局限于对象,如果两个变量指向同一个原始类型的值。那么,变量这时都是值的拷贝。

但是,这种引用只局限于对象,如果两个变量指向同一个原始类型的值。那么,变量这时都是值的拷贝。

如果行首是一个大括号,它到底是表达式还是语句?

为了避免这种歧义,JavaScript 引擎的做法是,如果遇到这种情况,无法确定是对象还是代码块,一律解释为代码块。
如果要解释为对象,最好在大括号前加上圆括号。因为圆括号的里面,只能是表达式,所以确保大括号只能解释为对象。

1
2
({ foo: 123 }) // 正确
({ console.log(123) }) // 报错

属性操作

读取对象的属性,有两种方法,一种是使用点运算符,还有一种是使用方括号运算符。

点运算符和方括号运算符,不仅可以用来读取值,还可以用来赋值。

查看一个对象本身的所有属性,可以使用Object.keys方法。

delete命令用于删除对象的属性,删除成功后返回true

in运算符用于检查对象是否包含某个属性(注意,检查的是键名,不是键值),如果包含就返回true,否则返回false。它的左边是一个字符串,表示属性名,右边是一个对象。

1
2
3
var obj = { p: 1 };
'p' in obj // true
'toString' in obj // true

可以使用对象的hasOwnProperty方法判断一下,是否为对象自身的属性

with语句(不推荐

函数

JavaScript 语言将函数看作一种值,与其它值(数值、字符串、布尔值等等)地位相同

1
2
3
4
5
6
7
8
9
10
11
12
13
function add(x, y) {
return x + y;
}

// 将函数赋值给一个变量
var operator = add;

// 将函数作为参数和返回值
function a(op){
return op;
}
a(add)(1, 1)
// 2

如果同时采用function命令和赋值语句声明同一个函数,最后总是采用赋值语句的定义。

1
2
3
4
5
6
7
8
9
var f = function () {
console.log('1');
}

function f() {
console.log('2');
}

f() // 1

Author: damn1t

Link: http://microvorld.com/2019/10/28/JS挖坑/js(1)/

Copyright: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.

< PreviousPost
JS:异步操作
NextPost >
http异步攻击:请求走私
CATALOG
  1. 1. JS(1)
    1. 1.1. 前言
    2. 1.2. 基本语法
      1. 1.2.1. 变量提升
      2. 1.2.2. 标识符
      3. 1.2.3. 三元运算符 ?:
      4. 1.2.4. 标签(label)
    3. 1.3. 数据类型
      1. 1.3.1. 简介
      2. 1.3.2. 数据类型确定
      3. 1.3.3. 数值
        1. 1.3.3.1. 整数和浮点数
        2. 1.3.3.2. 数值精度
        3. 1.3.3.3. 数值范围
        4. 1.3.3.4. 数值表示
        5. 1.3.3.5. 正零和负零
        6. 1.3.3.6. infinity
        7. 1.3.3.7. NaN
        8. 1.3.3.8. 与数值相关的全局方法
      4. 1.3.4. 字符串
        1. 1.3.4.1. 字符
        2. 1.3.4.2. Base64
        3. 1.3.4.3. 字符集
      5. 1.3.5. 对象
        1. 1.3.5.1. 概述
        2. 1.3.5.2. 属性操作
      6. 1.3.6. 函数