JavaScript 装饰器和转发

装饰器模式

装饰器是一个特殊的函数,它接受一个函数作为参数,并改变它的行为。

它非常重要和普遍的一种应用就是缓存函数返回结果,节省重复计算的时间。

1
2
3
4
5
6
7
8
9
10
11
12
function cacheDecorator(fun) {
const cache = new Map();
return function(x) {
// 如果命中缓存,则直接返回缓存中的计算好的值
if (cache.has(x)) return cache.get(x);
// 如果没有命中缓存,则使用传入的函数重新计算结果
const result = fun(x);
// 将新的结果进行缓存
cache.set(x, result);
return result;
}
}

callapply 都允许显式地设置调用函数执行时的上下文(即 this 值),在作用上它们几乎相同。

不同的是 call 以参数列表的形式接收参数,而 apply 以类数组的形式接收参数。

JavaScript 正则表达式

JavaScript 正则表达式

JavaScript 正则表达式

什么是正则表达式?

正则表达式是一些用来匹配和处理文本的字符串。

语法

可以使用字面量、构造器和工厂标记来创建正则表达式

1
2
3
/partern/flags
new RegExp(pattern[, flags])
RegExp(pattern[, flags])

其中 flags

  • g 全局匹配
  • i 不区分大小写
  • m 多行匹配
  • u Unicode 字符匹配

正则表达式中的特殊字符

字符类

  • . 匹配除行终止符(\n,\r,\u2028 or \u2029)之外的任何单个字符,若在字符集中则仅仅只匹配.
  • \d 匹配任何一个数字字符,相当于 [0-9]
  • \D 匹配任何一个非数字字符,相当于 [^0-9]
  • \w 匹配任何一个字母数字字符,包括下划线,相当于 [A-Za-z0-9_]
  • \W 匹配任何一个非字母数字字符或非下划线字符,相当于 [^A-Za-z0-9_]
  • \s 匹配任何单个空格字符,相当于 [\f\n\r\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]
  • \S 匹配除了空格外的单个字符,相当于 [^ \f\n\r\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]
  • \t 匹配一个横向制表符
  • \r 匹配一个回车
  • \n 匹配一个换行符
  • \v 匹配一个垂直制表符
  • \f 匹配一个换页符
  • [\b] 匹配一个删除符
  • \0 匹配 NUL 字符
  • \cX 匹配控制字符

字符集

  • [-a-z-] 匹配方括号中字符中的任何一个,可以使用连字符 - 来指定字符范围,但如果连字符是方括号中的第一个或最后一个字符,则作为普通字符包含在字符集中。也可以在字符集中包含字符类。
  • [^-A-Z-] 匹配除括号中包含的内容外的任何内容

可选字符

  • x|y 匹配 x 或 y 中的一个

边界

  • ^ 匹配开头,如果设置了多行匹配标记,则在换行符后立即匹配
  • $ 匹配结尾
  • \b 匹配单词边界,实际上它匹配的是一个能够构成单词的字符(\w)和一个不能构成单词的字符(\W)之间的位置
  • \B 匹配非单词边界

分组和反向引用

  • (x) 匹配 x 并记录匹配结果,称为捕获分组
  • \n 引用之前的第 n 个 表达式
  • (?:x) 匹配 x 但不记录匹配结果,称为非捕获分组

量词

  • x* 匹配 x 0次或更多次
  • x+ 匹配 x 1次或更多次
  • x? 匹配 x 0次或1次
  • x{n} 匹配 x n次
  • x{n,} 匹配 x 至少n次
  • x{n,m} 匹配 x 至少n次至多m次
  • ? 非贪婪匹配,在量词后加上 ? 表示量词的非贪婪模式,匹配尽可能少的字符

回溯引用

  • x(?=y) 前向查找,仅当 x 后跟 y 时,才匹配 x

    image-20211110161954405
  • (?<=y)x 后向查找,仅当 x 前面有 y 时,才匹配 x

    image-20211110162115215

常用的方法

  • exec()

    在指定的字符串上执行匹配搜索,返回结果数组或 null

    1
    2
    3
    4
    5
    6
    7
    8
    const str = "My ip address is 255.198.99.101 and your ip address 199.123.44.88";
    const reg = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/g;

    // Found 255.198.99.101, from index 17, last match from index 31
    // Found 199.123.44.88, from index 52, last match from index 65
    while((res = reg.exec(str)) !== null) {
    console.log(`Found ${res[0]}, from index ${res['index']}, last match from index ${reg.lastIndex}`);
    }
  • test()

    在指定的字符串和一个正则表达式之间执行匹配搜索,返回 truefalse

  • match()

    对给定的字符串进行匹配并返回匹配结果

    1
    2
    3
    4
    5
    const str = "My ip address is 255.198.99.101 and your ip address 199.123.44.88";
    const reg = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/g;

    // [ '255.198.99.101', '199.123.44.88' ]
    console.log(str.match(reg));
  • replace()

    使用给定的字符串替换匹配到的结果

  • search()

    在给定的字符串中搜索匹配,并返回首次匹配结果的索引

  • split()

    通过将字符串分隔为子字符串,将字符串拆分为数组。

常用正则表达式

  • 匹配中文字符

    1
    /^[\u4e00-\u9fa5]{0,}$/
  • 匹配双字节字符

    1
    /[^\x00-\xff]/
  • 匹配千分位

    1
    /\B(?=(\b{3})+(?!\d))/
  • 匹配两位小数

    1
    /^([1-9][0-9]*)(\.[0-9]{2})?$/
  • 匹配中国固定电话号码

    最开始的一位一定是 0,接着是 2,3,4位数字组成的区号,然后是7位或8位的电话号码,其中首位不为1

    1
    /\(?0[1-9]\d{1,3}\)?[ -]?[2-9]\d{2,3}[ -]?\d{4}/
  • 匹配统一社会信用代码

    统一社会信用代码由18位数字或者大写字母组成,但是字母不包括 I、O、Z、S、V

    一共由五部分组成:

    第一部分:登记管理部门代码1位 (数字或大写英文字母)

    第二部分:机构类别代码1位 (数字或大写英文字母)

    第三部分:登记管理机关行政区划码6位 (数字)

    第四部分:主体标识码(组织机构代码)9位 (数字或大写英文字母)

    第五部分:校验码1位 (数字或大写英文字母)

    1
    /[0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}/

    目前还有老的工商注册代码,也就是15位的社会信用代码,正则表达式如下:(弱校验)

    1
    /[1-9]\d{15}/

    同时支持18位和15位社会信用代码

    1
    /^([0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}|[1-9]\d{14})$/
  • 中国邮政编码

    前两位代表省、市、自治区,第三位代表邮区,第四位代表县、市,最后两位代表投递邮局,其中第二位不为 8。

    1
    /\d[0-7|9][0-9]{4}/
  • 中国身份证号码

    前六位是户口所在地编码,其中第一位是 1~8,此后是出生年月日,出生年份只能是 18、19、20,而且是可选的,最后一位校验位是数字或 x

    1
    /[1-8]\d{5}((18)|(19)|(20))?\d{2}(0[1-9]|1[0-2])((0|1|2)[1-9]|3[0-1])\d{3}[\dx]?/

JavaScript 中的特殊字符

Unicode 转义序列 含义 类别
\u0008 \b Backspace
\u0009 \t Tab 空白
\u000A \n 换行符 行终止符
\u000B \v 垂直制表符 空白
\u000C \f 换页 空白
\u000D \r 回车 行终止符
\u0022 " 双引号
\u0027 ' 单引号
\u005C \ \ 反斜杠
\u00A0 不间断空格 空白
\u0028 行分隔符 行终止符
\u0029 段落分割符 行终止符
\uFEFF 字节顺序标记 空白

JavaScript 文件操作

在大部分浏览器上,你不能直接存储你创建的文件,因为这会导致大量的安全问题。不过你可以将你的文件通过下载链接的方式提供给用户。

在客户端生成文件

静态方法 window.URL.createObjectURL(object) 会创建一个表示参数中给出的对象 objectURLDOMString ,其生命周期和创建它的 document 生命周期绑定。

object 可以是 FileBlobMediaSource

每次调用 createObjectURL() 都会为指定的对象创建一个新的 URL 对象,而不管是否已经用这个指定对象创建过 URL 对象。因此在创建的 URL 对象不再使用时,应该在安全的时机调用 revokeObjectURL() 方法来释放掉它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const objArrInJson = JSON.stringify([
{
"name": "一般职业",
"value": "0"
},
{
"name": "农牧业",
"value": "1",
"childs": [
{
"name": "机关、团体、公司",
"value": "1"
}
]
},]);

function generateF(raw) {
const datasInBlob = new Blob([raw], { type: 'application/json' });
return window.URL.createObjectURL(datasInBlob);
}

关于 FileReader

在网页应用程序中,使用 FileReader 可以通过使用 BlobFile 指定要异步的读取的文件内容(或数据缓冲区内容)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
</head>

<body>
<input type="file" multiple accept="*" id="fileInput">

<script>
const fileInput = document.getElementById('fileInput');

// change 事件当用户选择了文件时被触发
fileInput.addEventListener('change', (event) => {
const fileI = event.target;
console.log(fileI.files);
})
</script>
</body>

</html>

文件加载完成后返回的 files 是一个 FileList,其中的 File 的就是选择的文件的相关信息。

File 对象包含提供文件有关的信息,使得网页中的代码能够访问文件内容。

1
2
3
4
5
6
7
8
{
lastModified: 00000000000, // 只读属性,以毫秒表示的的文件上次修改时间
lastModifiedDate: '', // 只读属性,文件上次修改时间
name: 'file.text', // 只读属性, File 对象的关联的文件的名称
size: 1024, // 只读属性,以字节表示的文件大小
type: 'text/plain', // 只读属性,文件的 MIME 类型,类型不确定是返回 ""
webkitRelativePath: '' // 文件相关的 `Path` 或 `URL`
}

拖拽文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
</head>

<body>
<div class="dropbox" id="dropbox"></div>

<script>
const dropbox = document.getElementById('dropbox');

dropbox.addEventListener('dragenter', (e) => {
e.stopPropagation();
e.preventDefault();
}, false);
dropbox.addEventListener('dragover', (e) => {
e.stopPropagation();
e.preventDefault();
console.log(e);
}, false);
dropbox.addEventListener('drop', (e) => {
e.stopPropagation();
e.preventDefault();

const dt = e.dataTransfer;
console.log(dt);
const files = dt.files;
}, false);
</script>
<style>
.dropbox {
width: 100vw;
height: 100vh;
background-color: aliceblue;
}
</style>
</body>

</html>

JavaScript 中的深拷贝和浅拷贝

关于深拷贝和浅拷贝的定义:

  • 浅拷贝 - 创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
  • 深拷贝 - 将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。

使用 JSON.parse(JSON.stringify()) 来实现深拷贝

可以使用 JSON.parse(JSON.stringify(obj)) 来实现对目标对象的深拷贝,但是这种方法有很多缺陷:

  • 如果 undefinedSymbol 和 函数是对象的属性值或以Symbo值为键的属性值,拷贝的对象会丢失这些属性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const originObj = {
    name: 'Trump',
    age: 94,
    phone: undefined,
    idNo: Symbol('1900'),
    speak: () => console.log('I am Trump'),
    [Symbol.for('weight')]: 120,
    }

    const copyObj = JSON.parse(JSON.stringify(originObj));
    console.log(copyObj); // { name: 'Trump', age: 94 }
  • 无法处理循环引用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    const team = {
    teamName: 'MAGA',
    }

    const originObj = {
    name: 'Trump',
    age: 94,
    phone: undefined,
    idNo: Symbol('1900'),
    speak: () => console.log('I am Trump'),
    [Symbol.for('weight')]: 120,
    team,
    }

    team.leader = originObj;

    const copyObj = JSON.parse(JSON.stringify(originObj));
    console.log(copyObj); // TypeError: Converting circular structure to JSON

一个比较全面的深拷贝实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
const TYPE_OBJECT = '[object Object]';
const TYPE_MAP = '[object Map]';
const TYPE_SET = '[object Set]';
const TYPE_ARRAY = '[object Array]';
const TYPE_ARGUMENTS = '[object Arguments]';
const TYPE_BOOLEAN = '[object Boolean]';
const TYPE_DATE = '[object Date]';
const TYPE_NUMBER = '[object Number]';
const TYPE_STRING = '[object String]';
const TYPE_SYMBOL = '[object Symbol]';
const TYPE_ERROR = '[object Error]';
const TYPE_REGEXP = '[object RegExp]';
const TYPE_FUNCTION = '[object Function]';
const TYPE_UNDEFINED = '[object Undefined]';

const forEach = (array, iterator) => {
let index = -1;
let length = array.length;
while(++index < length) {
iterator(array[index], index);
}
return array;
}

const isObj = (value) => {
const type = typeof value;
return value !== null && (type === 'object' || type === 'function');
}

const getType = (value) => {
return Object.prototype.toString.call(value);
}

const canCopyTags = [TYPE_OBJECT, TYPE_ARRAY, TYPE_MAP, TYPE_SET, TYPE_ARGUMENTS];

const getInit = (value) => {
const ctor = value.constructor;
return new ctor();
}

const copyReg = (value) => {
const regFlags = /\w*$/;
const newReg = new value.constructor(value.source, regFlags.exec(value));
newReg.lastIndex = value.lastIndex;
return newReg;
}

const copySymbol = (value) => {
return Object(Symbol.prototype.valueOf.call(value));
}

const copyFunction = (value) => {
const bodyReg = /(?<={)(.|\n)+(?=})/m;
const paramReg = /(?<=\().+(?=\)\s+{)/;
if (value.prototype) {
const body = bodyReg.exec(value.toString());
const param = paramReg.exec(value.toString());
if (body) {
if (param) {
const paramArr = param[0].split(',');
return new Function(...paramArr, body[0]);
} else {
return new Function(body[0]);
}
} else {
return null;
}
} else {
return eval(value.toString());
}
}

const copyOther = (value, type) => {
const ctor = value.constructor;
switch(type) {
case TYPE_BOOLEAN:
case TYPE_NUMBER:
case TYPE_STRING:
case TYPE_ERROR:
case TYPE_DATE:
return new ctor(value);
break;
case TYPE_REGEXP:
return copyReg(value);
break;
case TYPE_SYMBOL:
return copySymbol(value);
break;
case TYPE_FUNCTION:
return copyFunction(value);
break;
default:
return null;
}
}

const copy = (origin, relationMap = new WeakMap()) => {
if (!isObj(origin)) return origin;

const type = getType(origin);
let target;
if (canCopyTags.includes(type)) {
target = getInit(origin);
} else {
return copyOther(origin, type);
}

if (relationMap.get(origin)) return relationMap.get(origin);
relationMap.set(origin, target);

if (type === TYPE_SET) {
origin.forEach((value) => {
target.add(copy(value));
});
return target;
}

if (type === TYPE_MAP) {
origin.forEach((value, key) => {
target.set(key, copy(value));
});
return target;
}

const keys = type === TYPE_ARRAY ? undefined : Object.keys(origin);
forEach(keys || origin, (value, key) => {
if (keys) {
key = value;
}
target[key] = copy(origin[key], relationMap);
});

return target;
}

const originObj = {
name: 'Trump',
age: 94,
phone: undefined,
idNo: Symbol('1900'),
speak: () => console.log('I am Trump'),
[Symbol.for('weight')]: 120,
team: {
leader: 'Ywank',
office: {
address: 'white house'
}
},
employee: [
{ name: 'pense', },
{ name: 'fox', },
],
}

originObj.president = originObj;

console.log(copy(1)); // 1
console.log(copy(undefined)); // undefined
console.log(copy(/\d/)); // /\d/
console.log(copy(new Date())); // 2018-05-16T08:06:45.361Z

const someFun = function(num1, num2) {
console.log(num1 + num2);
return num1 + num2;
}

const copyFun = copy(someFun);
console.log(copyFun(1, 3)); // 4

const someSet = new Set();
someSet.add(1);
console.log(copy(someSet)); // Set(1) { 1 }

const copyObj = copy(originObj);
console.log(originObj);
console.log(copyObj);
copyObj.speak(); // I am Trump

为什么使用 while 实现遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
const ARRAY_SIZE = 10000000;
const LARGE_ARRY = new Array(ARRAY_SIZE).fill(1);

// for...in
let forInCopy = 0;
console.time('FOR_IN');
for (let key in LARGE_ARRY) {
forInCopy += LARGE_ARRY[key];
}
console.timeEnd('FOR_IN');

// for
let forCopy = 0
console.time('FOR');
for (let index = 0; index < LARGE_ARRY.length; index += 1) {
forCopy += LARGE_ARRY[index];
}
console.timeEnd('FOR');

// while
let whileCopy = 0;
let index = 0;
console.time('WHILE');
while(index < ARRAY_SIZE) {
whileCopy += LARGE_ARRY[index];
index += 1;
}
console.timeEnd('WHILE');

// forEach
let forEachCopy = 0
console.time('FOR_EACH');
LARGE_ARRY.forEach((value) => {
forEachCopy += value;
});
console.timeEnd('FOR_EACH');
遍历方式 for...in for forEach while
100 0.103ms 0.012ms 0.027ms 0.007ms
10 0.088ms 0.005ms 0.022ms 0.003ms
1000 0.313ms 0.036ms 0.052ms 0.028ms
10000 1.332ms 0.299ms 0.316ms 0.252ms
100000 14.167ms 1.691ms 1.977ms 1.428ms
1000000 140.967ms 2.928ms 14.94ms 3.012ms
10000000 2.748s 13.197ms 141.273ms 15.944ms
20000000 7.824s 22.389ms 254.557ms 28.07ms
30000000 17.107s 33.414ms 375.766ms 44.301ms

综合来看,在百万量级的数据下,使用 while 进行遍历所耗费的时间最少。

判断引用类型

1
2
3
4
const isObj = (value) => {
const type = typeof value;
return value !== null && (type === 'object' || type === 'function');
}

获取数据类型

每一个引用类型都有toString方法,默认情况下,toString()方法被每个Object对象继承。如果此方法在自定义对象中未被覆盖,toString() 返回 "[object type]",其中type是对象的类型。

1
2
3
const getType = (value) => {
return Object.prototype.toString.call(value);
}

JSON.stringify 的解析

JSON.stringify(value[, replacer [, space]]) 方法将一个 JavaScript 对象或值转换为 JSON 字符串,如果指定了一个 replacer 函数,则可以选择性地替换值,或者指定的 replacer 是数组,则可选择性地仅包含数组指定的属性。

Note 1

JSON.stringify 方法对对象进行序列化时,如果对象的属性是 undefined/Symbol 值或任意函数时,不会对其进行处理。

这会导致被序列化的对象中的属性并不会按照原定的顺序被输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const someObj = {
_id: 'id',
productCode: 'P28120068',
productDetail: {
name: 'Star Insure',
price: 1200,
amount: 1000000
},
created_at: 'Fri Nov 13 2020 09:25:31 GMT+0800 (China Standard Time)',
updated_at: 'Fri Nov 13 2020 09:25:31 GMT+0800 (China Standard Time)',
nullValue: null,
undefinedValue: undefined, // JSON.stringify 不会序列化 undefined
symbolValue: Symbol('TypeScript'), // JSON.stringify 不会序列化 Symbol
arrowLog: () => console.log(this), // JSON.stringify 不会序列化 箭头函数
funLog: function() { console.log(this) }, // JSON.stringify 不会序列化 函数
}

/**
* Note 1 : undefined、Symbol、任何函数作为对象的属性值时,JSON.stringify 不对其(忽略)进行序列化
* {
* "_id":"id",
* "productCode":"P28120068",
* "productDetail":{"
* name":"Star Insure",
* "price":1200,
* "amount":1000000
* },
* "created_at":"Fri Nov 13 2020 09:25:31 GMT+0800 (China Standard Time)",
* "updated_at":"Fri Nov 13 2020 09:25:31 GMT+0800 (China Standard Time)",
* "nullValue":null
* }
*/
console.log(JSON.stringify(someObj));

Note 2

JSON.stringify 方法对数组进行序列化时,如果数组元素的值是 undefinedSymbol 值或任意函数时,会被序列化为 null

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const someArr = [
null,
undefined, // JSON.stringify 将数组中的 undefined 序列化为 null
Symbol('TypeScript'), // JSON.stringify 将数组中的 Symbol 序列化为 null
() => console.log(this), // JSON.stringify 将数组中的 箭头函数 序列化为 null
function() { console.log(this) } // JSON.stringify 将数组中的 函数 序列化为 null
]

/**
* Note 2 : undefined, Symbol, 任意函数作为数组的元素时,JSON.stringify 将其序列化为 null
* [null,null,null,null,null]
*/
console.table(JSON.stringify(someArr));

Note 3

如果 undefinedSymbol 值或任意函数作为单独的值被 JSON.stringify 序列化为 undefined

1
2
3
4
5
6
/**
* Note 3 : undefined, Symbol, 任意函数作为单独的值时,JSON.stringify 序列化会返回 undefined
*/
console.log(JSON.stringify(undefined)); // undefined
console.log(JSON.stringify(Symbol('TypeScript'))); // undefined
console.log(JSON.stringify(() => console.log(this))); // undefined

Note 4

如果目标值有 toJSON 方法,则 toJSON 来决定 JSON.stringify 返回的值。

1
2
3
4
5
console.log(JSON.stringify({
name: 'Jimmy',
age: 20,
toJSON: () => 'this is a toJSON function',
})); // this is a toJSON function

Note 5

NaN, InInfinitynull 会被JSON.stringify 序列化为 null

1
2
3
4
5
6
7
8
9
/**
* NaN, InInfinity 及 null 会被 JSON.stringify 序列化为 null
*/
console.log(NaN); // NaN
console.log(JSON.stringify(NaN)); // null
console.log(Infinity); // Infinity
console.log(JSON.stringify(Infinity)); // null
console.log(null); // null
console.log(JSON.stringify(null)); // null

Note 6

数字、字符串和布尔值的包装值在被 JSON.stringify 序列化时会解包成对应的基础值。

1
2
3
const packArr = [ new Number(1), new String('TypeScript'), new Boolean(false)];
console.log(packArr); // [ [Number: 1], [String: 'TypeScript'], [Boolean: false] ]
console.log(JSON.stringify(packArr)); // [1,"TypeScript",false]

Note 7

JSON.stringify 在序列化对象时,仅会序列化可枚举的属。

1
2
3
4
5
const enumerableObj = Object.create(null, {
name: { value: 'jack', enumerable: true },
age: { value: 20, enumerable: false }
});
console.log(JSON.stringify(enumerableObj)); // { name: 'jack' }

Note 8

如果 JSON.stringify 的目标对象发生循环引用时,会报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 对于相互引用的对象,会报错
* TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Object'
| property 'refA' -> object with constructor 'Object'
--- property 'refB' closes the circle
*/
const objA = {
name: 'Object A',
refB: null,
};

const objB = {
name: 'Object B',
refA: objA,
};

objA.refB = objB;

Note 9

Symbol 值为键的属性值,在被 JSON.stringify 序列化时会被忽略。

1
2
3
4
5
6
7
8
9
/**
* 以 Symbol 值为键的属性值,在被 JSON.stringify 序列化时会被忽略
*/
const symbolObj = {
name: 'Symbol',
[Symbol.for('Age')]: 20,
};
console.log(symbolObj); // { name: 'Symbol', [Symbol(Age)]: 20 }
console.log(JSON.stringify(symbolObj)); // {"name":"Symbol"}

Note 10

JSON.stringify 的第二个参数是函数时, 可单独对某个值进行处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const someObj = {
_id: 'id',
productCode: 'P28120068',
productDetail: {
name: 'Star Insure',
price: 1200,
amount: 1000000
},
created_at: 'Fri Nov 13 2020 09:25:31 GMT+0800 (China Standard Time)',
updated_at: 'Fri Nov 13 2020 09:25:31 GMT+0800 (China Standard Time)',
nullValue: null,
undefinedValue: undefined, // JSON.stringify 不会序列化 undefined
symbolValue: Symbol('TypeScript'), // JSON.stringify 不会序列化 Symbol
arrowLog: () => console.log(this), // JSON.stringify 不会序列化 箭头函数
funLog: function() { console.log(this) }, // JSON.stringify 不会序列化 函数
}

/**
* 当 JSON.stringify 的第二个参数是函数时, 可单独对某个值进行处理
*/
console.log(JSON.stringify(someObj, (k, v) => {
console.log(v)
if (typeof v === 'undefined') return 'undefined';
return JSON.stringify(v);
}));

JSON.stringify 的第二个参数是数组时,可在数组中加入属性名,来规定返回哪些属性的序列化值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const someObj = {
_id: 'id',
productCode: 'P28120068',
productDetail: {
name: 'Star Insure',
price: 1200,
amount: 1000000
},
created_at: 'Fri Nov 13 2020 09:25:31 GMT+0800 (China Standard Time)',
updated_at: 'Fri Nov 13 2020 09:25:31 GMT+0800 (China Standard Time)',
nullValue: null,
undefinedValue: undefined, // JSON.stringify 不会序列化 undefined
symbolValue: Symbol('TypeScript'), // JSON.stringify 不会序列化 Symbol
arrowLog: () => console.log(this), // JSON.stringify 不会序列化 箭头函数
funLog: function() { console.log(this) }, // JSON.stringify 不会序列化 函数
}

/**
* 当 JSON.stringify 的第二个参数是数组时,可在数组中加入属性名,来规定返回哪些属性的序列化值
* 但当值是 undefined、Symbol、任何函数时无效
*/
console.log(JSON.stringify(someObj, ['productCode', 'arrowLog'])); // { productCode: 'P28120068' }

JavaScript 高级程序设计 - 引用类型

应用类型的值是应用类型的一个实例。在 ECMAScript 中,引用类型是一种数据结构,用于将数据和功能组织在一起,它也常被称为 ,但这种称呼并不妥当。尽管 ECMAScript 从技术上讲是一门面向对象的语言,但它不具备传统的面向对象语言所支持的类和接口等基本结构。引用类型有时候也被称为对象定义,因为它们描述的是一类对象所具有的属性和方法。

Object 类型

Object 是 ECMAScript 中使用最多的一个类型。虽然 Object 的实例不具备多少功能,但对于在应用程序中存储和传输数据而言,它们确实是非常理想的选择。

创建 Object 实例的两种方式 :

  • 使用 new 操作符

    1
    2
    var person = new Object();
    person.name = "CocoaLei";
  • 使用对象字面量

    1
    2
    3
    var person = {
    name = "CocoaLei"
    };

    对象字面量是对象定义的一种简写形式 ,目的在于简化创建包含大量属性的对象的过程。

    对象字面量也是向函数传递大量可选参数的首选方式。

一般来说,访问对象属性时使用的都是点语法,这也是很多面向对象语言中通用的语法。不过,在 JavaScript 中也可以使用方括号表示法来访问对象的属性。

1
2
3
4
5
6
7
var person = {
name = "CocoaLei"
};
console.log(person.name); // CocoaLei
console.log(person["name"]); // CocoaLei
var propertyName = "name";
console.log(person[propertyName]); // CocoaLei

通常,除非必须使用变量来访问属性,否则建议使用点语法。

Array 类型

Array 类型是出了 Object 之外 ECMAScript 中最常用的类型了。而且,ECMAScript 中的数组与其它语言中的数组有着相当大的区别。虽然 ECMAScript 数组与其它语言中的数组都是数据的有序列表,但与其它语言不通的是, ECMAScript 中的数组的每一项可以保存任何类型的数据。并且,ECMAScript 数组的大小是可以动态调整的,即可以随着数据的添加自动增长以容纳新增数据。

创建数组的两种方式 :

  • Array 构造函数

    1
    var colors = new Array();

    如果预先知道数组要保存的项目数量,也可以给构造函数传递该数量,而改数量会自动编程 length 的值。

    1
    var colors = new Array(255);
  • 使用数组字面量

    1
    var colors = ["red", "yellow", "blue"];

在读取和设置数组的值时,要使用方括号并提供相应值的基于 0 的数字索引。

数组最多可以包含 4294967295 个项,这几乎已经能够满足任何编程需求了。如果想添加的项数超过这个上限值,就会发生异常。而创建一个初始大小与这个上限值接近的数组,则可能会导致运行时间超长的脚本错误。

检测数组

如果假定只有一个全局执行环境,使用 instanceof 操作符就能检测值是不是数组。但是,实际上存在的全局环境不止一个,那么 Array 构造函数也不止一个。

为了解决这个问题,ECMAScript 5 新增了 Array.isArray() 方法。这个方法的母的是最终确定某个值到底是不是数组,而不管它在哪个全局执行环境中创建的。

1
Array.isArray(value);

转换方法

所有对象都具有 toLocalString()toString()valueOf() 方法。调用数组的 toString() 方法会返回数组中每个值得字符串形式拼接而成的一个以逗号分隔的字符串。调用 valueOf() 方法返回的还是数组。

数组继承的 toLocalString()toString()valueOf() 方法,在默认情况下都会以逗号分隔的字符串的形式返回数组项。而如果使用 join() 方法,则可以使用不同的分隔符来构建这个字符串。join() 方法只接受一个参数,即用作分隔符的字符串,然后返回包含所有数组项的字符串。

如果数组中的某一项的值是 nullundefined , 那么该值在 toLocalString()toString()valueOf() 方法返回的结果中以空字符串表示。

栈方法

ECMAScript 数组也提供了一种让数组的行为类似于其它数组结构的方法。具体来说,数组可以表现的像栈一样。

ECMAScript 专门为数组提供了push()pop() 方法,以便实现类似栈的行为。

push() 方法可以接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度。而 pop() 方法则从数组末尾移除最后一项,减少数组的 length 值,然后返回移除的项。

1
2
3
var sampleArr = new Array();
var arrLength = sampleArr.push("A","B"); // arrLength is 2 and sampleArr is ["A", "B"]
var removedItem = sampleArr.pop(); // removedItem is "B"

队列方法

栈数据结构的访问规则是后进先出,而队列数据结构的访问规则是先进先出。队列在列表的末端添加项,从列表的前端移除项。由于 push() 是向数组末端添加项的方法,因此要模拟队列只需一个从数组前端取得项的方法。实现这一操作的数组方法就是 shift() ,它能够移除数组中的第一个项并返回该项,同时将数组长度减 1 。结合使用 shift()push() 方法,就可以像使用队列一样使用数组。

1
2
3
var sampleArr = new Array();
sampleArr.push("A", "B");
var firstItem = sampleArr.shift(); // firstItem is "A"

ECMAScript 还未数组提供了一个 unshift() 方法,它能在数组前端添加任意个项并返回新数组的长度。

重排序方法

数组中已经存在两个可以直接用来重排序的方法 :reverse()sort()

数组的 reverse() 方法会反转数组的顺序 :

1
2
var originalArr = [3, 7, 1, -1, 22];
originalArr.reverse(); // 22, -1, 1, 7, 3

在默认情况下,sort() 方法按升序排列数组。为了实现排序,sort() 方法会调用数组中每项的 toString() 方法,然后比较得到的字符串,以确定如何排序。这种排序方式在很多情况下都不是最佳方案。不过,sort() 方法可以接收一个比较函数作为参数,以便我们指定哪个值位于哪个值的前面。

1
2
3
4
5
6
7
8
9
10
// 升序
function compare (value_1, value_2) {
if (value_1 < value_2) {
return -1;
} else if (value_1 > value_2) {
return 1;
} else {
return 0;
}
}

reverse()sort() 方法的返回值是经过排序后的数组。

对于数值类型或者其 valueOf() 方法会返回数值类型的对象类型,可以使用一个更简单的比较函数。这个函数只要用第二个值减第一个值即可。

1
2
3
function compare (value_1, value_2) {
return value_2 - value_1;
}

由于比较函数通过返回一个小于零、等于零或大于零的结果来影响排序,因此减法操作就可以适当地处理所有这些情况。

操作方法

ECMAScript 为操作已经包含在数组中的项提供了很多方法。

contact()

contact() 方法可以基于当前数组的所有项创建一个新数组。具体来说,这个方法会先创建当前数组的一个副本,然后将接收到的参数添加到这个副本的末尾,最后返回新构建的数组。在没有给 contact() 方法传递参数的情况下,它只是复制当前数组并返回副本。如果传递给 contact() 方法的是一或多个数组,则该方法会将这些数组中的每一项都添加到结果数组中。如果传递的不是数组,这些值就会被简单地添加到结果数组的末尾。

1
2
3
var sampleArr = nes Array();
sampleArr.push("A", "B");
var copyArr = sampleArr.contact("C"); // now sampleArr is ["A", "B", "C"]
slice()

slice() 方法能够基于当前数组中的一个或多个项创建一个新数组。slice() 方法可以接受一个或两个参数,即要返回项的起始和结束位置。在只有一个参数的情况下,slice() 方法返回从该参数指定位置开始到当前数组末尾的所有项。如果有两个参数,该方法返回起始和结束位置之间的项(不包括结束位置)。

1
2
3
var sampleArr = [1, 2, 3, 4, 5, 6, 7];
var resultArr = sampleArr.slice(3); // resultArr is [4, 5, 6, 7]
resultArr = sampleArr.slice(3,5); // resultArr is [4, 5]

如果 slice() 方法中有一个负数,则用数组长度加上该数来确定相应的位置。如果结束位置小于起始位置,则返回空数组。

splice()

splice() 方法主要用途是向数组的中部插入项,但使用这种方法的方式则有如下三种 :

  • 删除 :可以删除任意数量的项,只需指定要删除的第一项的位置和要删除的项数两个参数

    1
    2
    var sampleArr = [1, 2, 3, 4, 5, 6, 7];
    sampleArr.splice(5, 1); // sampleArr is [1, 2, 3, 4, 5, 6]
  • 插入 :可以向指定位置插入任意数量的项,只需提供起始位置、要删除的项数 (设为0) 和要插入的项

    1
    2
    var sampleArr = [1, 2, 3];
    sampleArr.splice(1, 0, 2.1, 2.2); // sampleArr is [1, 2, 2.1, 2.2, 3]
  • 替换 :可以向指定位置插入任意数量的项,同时删除任意数量的项

    1
    2
    var sampleArr = [1, 2, 3];
    sampleArr.splice(2, 1, 4); // sampleArr is [1, 2, 4]

splice() 方法始终都会返回一个数组,该数组包含从原始数组中删除的项 (如果没有删除任何项,则返回一个空数组) 。

位置方法

ECMAScript 5 为数组实例添加了两个位置方法 :indexOf()lastIndexOf() 。这两个方法都接收两个参数 :要查找的项和表示查找七点位置的索引。其中,indexOf() 从数组的开头开始向后查找,lastIndexOf() 则是从数组的末尾开始向前查找。

这两个方法都返回要查找的项在数组中的位置,或者在没有找到的情况下返回 -1 。在比较第一个参数与数组中的每一项时,会使用全等操作符。

1
2
3
var sampleArr = [1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1];
sampleArr.indexOf(4); // 3
sampleArr.lastIndexOf(4); // 7

迭代方法

ECMAScript 5 为数组定义了 5 个迭代方法,每个方法都接收两个参数 :要在每一项上运行的函数和运行该函数的作用域对象。传入这些方法中的函数会接收三个参数 :数组项的值,该项在数组中的位置和数组对象的本身。

  • every() : 对数组中的每一项运行给定函数,如果该函数对每一项都返回 true ,则返回 true
  • filter() :对数组中的每一项运行给定函数,返回该函数会返回 true 的项组成的数组
  • forEach() :对数组中的每一项运行给定函数,这个方法没有返回值
  • map() :对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组
  • some() :对数组中的每一项运行给定函数,如果该函数对任一项返回 true ,则返回 true

以上这些方法都不会修改数组中包含的值。

归并方法

ECMAScript 5 增加了两个归并数组的方法 :reduce()reduceRight() 。这两个方法都会迭代数组所有的项,然后构建一个最终返回的值。其中,reduce() 方法从数组的第一项开始,逐个遍历到最后。而 reduceRight() 则从数组的最后一项开始,向前遍历到第一项。

这两个方法都接收两个参数 :一个在每一项上调用的函数和作为归并基础的初始值。传给 reduce()reduceRight() 的函数接收 4 个参数 :前一个值、当前值、项的索引和数组对象。这个函数返回的任何值都会作为第一个参数自动传给下一项。第一次迭代发生在数组的第二项上,因此第一个参数是数组的第一项,第二个参数是数组的第二项。

求数组中所有值之和 :

1
2
3
4
var sampleArr = [1, 2, 3, 4, 5];
var sum = sampleArr.reduce(function(prev, cur, index, array) {
return prev + cur;
}); // sum is 15

Date 类型

ECMAScript 中的 Date 类型是在早期 Java 中的 java.util.Date 类基础上构建的。为此,Date 类型使用 UTC 1970 年 1 月 1 日午夜开始经过的毫秒数来保存日期。这使用这种数据存储格式的条件下,Date 类型保存的日期能够精确到 1970 年 1 月 1 日之前或之后的 285616 年。

在调用 Date 构造函数而不传递参数的情况下,新创建的对象自动获得当前的日期和时间。如果想根据特定的日期和时间创建日期对象,必须传入表示该日期的毫秒数。为了简化这一计算过程,ECMAScript 提供了两个方法 : Date.parse()Date.UTC()

其中,Date.parse() 方法接收一个表示日期的字符串参数,然后尝试根据这个字符串返回相应日期的毫秒数。ECMA 没有定义 Date.parse() 应该支持哪种日期格式,因此这个方法的行为因实现而异。

Date.UTC() 方法同样也返回表示日期的毫秒数,但它与 Date.parse() 在构建值时使用不同的信息。Date.UTC() 的参数分别是年份、基于 0 的月份、月中的哪一天、小时数、分钟、秒以及毫秒数。在这些参数中,只有前两个参数是必需的。

Date.now() 方法会返回表示调用这个方法时的日期和时间的毫秒数。

继承的方法

与其它引用类型一样,Date 类型也重写了 toLocalString()toString()valueOf() 方法 。Date 类型的 toLocalString() 方法会按照与浏览器设置的地区相适应的格式返回日期和时间。而 toString() 方法则通常返回带有时区信息的日期和时间。Date 类型的 valueOf() 方法,则根本不返回字符串,而是返回日期的毫秒表示,常用来比较日期值的大小。

1
2
3
4
5
var date_pre = new Date(2017, 0, 1);
var date_next = new Date(2017, 1, 1);
if (date_pre < date_next) {
consolo.log("");
}

日期格式化方法

Date 类型用于将日期格式化为字符串的方法 :

  • toDateString() :以特定于实现的格式显示星期几、月、日和年
  • toTimeString() :以特定于实现的格式显示时、分、秒和时区
  • toLocalDateString() :以特定于地区的格式显示星期几、月、日和年
  • toLocalTimeString() :以特定于实现的格式显示时、分、秒
  • toUTCString() :以特定于实现的格式显示完整的 UTC 日期

RegExp 类型

ECMAScript 通过 RegExp 类型来支持正则表达式 :

1
var expression = /pattern/flags;

其中的模式部分可以是任何简单或复杂的正则表达式,可以包含字符类、限定符、分组、向前查找以及反向引用。每个正则表达式都可以带有一或多个标志,用以表明正则表达式的行为。

正则表达式的匹配模式下支持下列 3 个标志 :

  • g :全局模式,即模式将被应用于所有字符串,而非在发现第一个匹配项时立即停止
  • i :不区分大小写模式,即在确定匹配项时忽略模式与字符串的大小写
  • m :多行模式,即在到达一行文本末尾时还会继续查找下一行中是否在与模式匹配的项

模式中使用的所有元字符都必须转义。正则表达式中的元字符包括 :

1
( [ { \ ^ $ | ) ? * + . ] }

这些元字符在正则表达式中都有一或多种特殊用途,因此如果想要匹配字符串中包含的这些字符,就必须对它们进行转义。

使用 RegExp 构造函数构造正则表达式时,需要注意 :传递的两个参数都是字符串。由于 RegExp 的构造函数的模式参数是字符串,所以在某些情况下要对字符进行双重转义。所有元字符都必须双重转义,那些已经转义过的字符也是如此。

1
2
3
字面量模式			等价的字符串
/\[bc\]at/ “\\[bc\\]at”
/\w\\hello\\123/ "\\w\\\\heollo\\\\123"

使用正则表达式字面量和使用 RegExp 构造函数创建的正则表达式不一样。在 ECMAScript 3 中,正则表达式字面量始终会共享同一个 RegExp 实例,而使用构造函数创建的每一个新 RegExp 实例都是新实例。

ECMAScript 5 明确规定,使用正则表达式字面量必须像直接调用 RegExp 构造函数一样,每次赌创建新的 RegExp 实例。

RegExp 实例属性

RegExp 的每个实例都具有下列属性 :

  • global :布尔值,表示是否设置了 g 标志
  • ignoreCase :布尔值,表示是否设置了 i 标志
  • lastIndex :整数,表示开始搜索下一个匹配项的字符位置,从 0 算起
  • multiline :布尔值,表示是否设置了 m 标志
  • source :正则表达式的字符串表示,按照字面量模式而非传入构造函数中的字符串模式返回

RegExp 实例方法

RegExp 对象的主要方法是 exec() ,该方法是专门为捕获组而设计的。 exec() 接受一个参数,即要应用模式的字符串,然后返回包含第一个匹配项信息的数组或者在没有匹配项的情况下返回 null 。返回的数组虽然是 Array 的实例,但包含两个额外的属性 :indexinput 。其中 index 表示匹配项在字符串的位置,而 input 表示应用正则表达式的字符串。在数组中,第一项是与整个模式匹配的字符串,其它项是与模式中单的捕获组匹配的字符串。

对于 exec() 方法而言,即使在模式中设置了全局标志,它每次也只会返回一个匹配项。在不设置全局标志的情况下,在同一个字符串上多次调用 exec() 将始终返回第一个匹配项的信息。而在设置全局标志的情况下,每次调用 exec() 则都会在字符串中继续查找新匹配项。

正则表达式的第二个方法是 test() ,它接受一个字符串参数。在模式与该参数匹配的情况下返回 true ;否则返回 false 。在只想知道目标字符串与某个模式是否匹配,但不需要知道其文本内容的情况下,使用这个方法

JavaScript 高级程序设计 - 变量、作用域和内存问题

基本类型和引用类型

ECMAScript 变量可能包含两种不同数据类型的值 :基本类型值和引用类型值。基本类型值指的是简单的数据段,而引用数据类型指那些可能由多个值构成的对象。

在将一个值赋值给变量时,解析器必须确定这个值时基本数据类型还是引用类型值。基本类型的值是按值访问的,因为可以操作保存在变量中的实际的值。

引用类型的值时保存在内存中的对象,JavaScript 不允许直接访问内存中的位置,即不能直接操作对象的内存空间。在操作对象时,实际上是操作对象的引用而不是实际的对象(当复制保存着对象的某个变量时,操作的是对象的引用;但在为对象添加属性时,操作的是实际的对象)。

在很多其它语言中,字符串以对象的形式来表示,因此被认为是引用类型的。

动态的属性

对于引用类型的值,我们可以为其添加属性和方法,也可以改变和删除属性和方法 :

1
2
var aObject = new Object();
aObject.name = "ObjectName";

复制变量值

除了保存的方式不同之外,在从一个变量向另一个变量复制基本类型值和引用类型值时,也存在不同。

如果从一个变量向另一个变量复制基本类型值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。

当从一个变量向另一个变量复制引用类型值时,同样也会将存储在变量对象的值复制一份放到为新变量分配的空间中。不同的是,这个值的副本实际上是一个指针,而这个指针指向存储在堆中的一个对象。复制操作结束后,两个变量实际上将引用同一个对象。

传递参数

ECMAScript 中函数的参数都是按值传递的。基本类型值得传递如同基本类型变量的复制一样,而引用类型值得传递,则如同引用类型变量的复制一样。

在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量(即命名参数)。在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反映在函数的外部。

检测类型

基本类型检测的最佳工具是 typeof 操作符。但在检测引用类型的值时,这个操作符的用处不大,我们并不是想知道某个值是对象,而是想知道它是什么类型的对象。

使用 instanceof 操作符来确定实例是否是给定的引用类型 :

1
result = variable instanceof constructor

所有引用类型的值都是 Object 的实例。因此,在检测一个引用类型值和 Object 构造函数时,instanceof 操作符始终会返回 true 。当然,如果使用 instanceof 操作符检测基本类型的值,则该操作符始终会返回 false , 因为基本类型不是对象。

执行环境及作用域

执行环境是 JavaScript 中最为重要的一个概念。执行环境定义了变量或函数有权访问的其它数据,决定了它们各自的行为。每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。

全局执行环境是最外围的一个执行环境。根据 ECMAScript 实现所在的宿主环境不同,表示执行环境的对象也不一样。在 Web 浏览器中,全局执行环境被认为是 window 对象。

某个执行环境中的所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁(全局执行环境直到应用程序退出时,才会被销毁)。

每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。

当代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行代码所在环境的变量对象。如果这个环境是函数,则将其活动对象作为变量对象。活动对象在最开始时只包含一个变量,即 arguments 对象。作用域链中的下一个变量对象来自包含环境,而再下一个变量对象则来自下一个包含环境。这样,一直延续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象。

标识符解析是沿着作用域链一级一级地搜索标识符的过程。搜索过程始终从作用域链的前端开始,然后逐级地向后回溯,直到找到标识符为止(如果找不到标识符,通常会导致错误发生)。

延长作用域链

虽然执行环境的类型总共只有两种 :全局和局部,但是还有其它办法来延长作用域链。这是因为有些语句可以在作用域链的前端临时增加一个变量对象,该变量对象会在代码执行后被移除。

在两种情况下会发生这种现象 :

  • try-catch 语句的 catch
  • with 语句

这两个语句都会在作用域链的前端添加一个变量对象。对 with 语句来或,会将指定的对象添加到作用域链中。对于 catch 语句来说,会创建一个新的变量对象,其中包含的是被抛出的错误对象的声明。

没有块级作用域

JavaScript 没有块级作用域经常会导致理解上的困惑。在其它类 C 语言中,由花括号封闭起来的代码块都有自己的作用域,因而支持根据条件来定义变量。

1
2
3
4
if (true) {
var color = "red";
}
console.log(color); // red

上面的例子中,在 if 语句中定义了变量 color ,它被添加到当前的执行环境,所以在 if 语句结束后还能够被访问。

在使用 for 语句时尤其要牢记这一差异 :由 for 语句创建的循环变量即使 for 循环执行结束后,也依旧会存在于循环外部的执行环境中。

声明变量

使用关键字 var 声明的变量会自动被添加到最接近的环境中。在函数内部,最接近的环境就是函数的局部环境;在 with 语句中,最接近的环境是环境函数。如果初始化变量时没有使用 var 声明,该变量会自动被添加到全局环境。

在编写 JavaScript 代码的过程中,不声明而直接初始化变量是一个常见的错误做法。

查询标识符

当在某个环境中为了读取或写入而引用一个标识符时,必须通过搜索来确定该标识符实际代表什么。搜索过程从作用域链的前端开始,向上逐级查询与给定名字匹配的标识符。如果在局部环境中找到了该标识符,搜索过程停止,变量就绪。如果在局部环境中没有找到该变量名,则继续沿作用域链向上搜索。搜索过程将一直追溯到全局环境的变量对象。如果在全局环境中也咩有找到这个标识符,则意味着该变量尚未声明。

变量查询也不是没有代价的。很明显,访问局部变量要比访问全局变量更快。

垃圾收集

JavaScript 具有自动垃圾收集机制,也就是说,执行环境会负责管理代码执行过程中使用的内存。在 JavaScript 中,所需内存的分配和无用内存的回收完全实现了自动管理 :找出那些不再继续使用的变量,然后释放其占用的内存。为此,垃圾收集器会按照固定的时间间隔,周期性的执行这一操作。

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

标记清除

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

标记变量的方式很多,它并不重要,关键在于才去什么策略。

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

性能问题

垃圾收集器是周期性运行的,而且如果为变量分配的内存数量很可观,那么回收工作量也是相当大的。这种情况下,确定垃圾收集的时间间隔是一个非常重要的问题。

管理内存

使用具备垃圾收集机制的语言编写程序,开发人员一般不必操心内存管理问题。但是,JavaScript 在进行内存管理及垃圾收集时面临的最主要问题是分配给 Web 浏览器的可用内存数量通常比分配桌面应用程序的少。这样做的目的是防止运行 JavaScript 的网页耗尽全部系统内存而导致系统崩溃。内存限制不仅会影响给变量分配内存,同时还会影响调用栈以及在一个线程中能够同时执行的语句数量。

因此,确保占用最少的内存可以让页面获得更好的性能。而优化内存占用的最佳方式,就是为执行中的代码只保存必要的数据。一旦数据不再有用,最好通过将其设置为 null 来释放其引用。这一做法适用于大多数全局变量和全局对象的属性。

解除一个值的应用的真正作用是让值脱离执行环境,以便垃圾收集器下次运行时将其回收。

JavaScript 高级程序设计 - 基础概念篇

JavaScript 简介

JavaScript 诞生于 1995 年,当时, 它的主要目的是处理以前由服务器端语言负责的一些输入验证操作。而今,JavaScript 已经从一个简单的输入验证器发展成为一门强大的编程语言:功能全面,能够处理复杂的计算和交互,拥有了闭包、匿名函数,甚至元编程等特性。

虽然 JavaScript 和 ECMAScript 通常被人们用来表达相同的含义,但 JavaScript 的含义却又要多得多。一个完整的 JavaScript 实例由 ECMAScript、DOM 和 BOM 组成。

ECMAScript

ECMAScript-262 定义的 ECMAScript 与 Web 浏览器没有依赖关系,它定义的只是这门语言的基础,而在此基础上可以构建更完善的脚本语言。常见的 Web 浏览器只是 ECMAScript 实现可能的宿主环境之一 : 宿主环境提供基本的 ECMAScript 实现,同时也会提供该语言的扩展,以便语言与环境之间对接交互。

文档对象模型 DOM

文档对象模型(DOM,Document Object Model)是针对 XML 但经过扩展用于 HTML 的应用程序编程接口。DOM 把整个页面映射为一个多层节点结构。即 HTML 或 XML 页面中的每个组成部分都是某种类型的节点,这些节点又包含着不同类型的数据。而开发人员通过 DOM 提供的 API 可以轻松自如的删除、添加或修改任何节点。

浏览器对象模型 BOM

浏览器对象模型使开发人员可以控制浏览器显示的页面以外的部分。从根本上讲,BOM 只处理浏览器窗口和框架,但是人们习惯上也把所有针对浏览器的 JavaScript· 扩展算作 BOM 的一部分。

在 HTML 中使用 JavaScript

向 HTML 页面中插入 JavaScript 的主要方法,就是使用 <script> 元素。这个元素由 Netscape 创造并在 Netscape Navigator 2 中首先实现。后来,这个元素被加入到正式的 HTML 规范中。HTML 4.01 为 <script> 定义了6个属性

  • async : 表示应该立即下载脚本,但不应妨碍页面中的其它操作。这是一个可选属性,只对外部脚本文件有效。
  • charset : 可选。表示通过 src 属性指定的代码的字符集。
  • defer : 可选。表示脚本可以延迟到文档完全被解析和显示之后再执行。只对外部脚本文件有效。
  • language : 已废弃。原来用于表示编写代码使用的脚本语言。
  • src :可选。表示包含要执行代码的外部文件。
  • type : 可选。可以看成是 language 的替代属性;表示编写代码使用的脚本语言的内容类型(MIME 类型)。虽然 text/javscripttext/ecmascript 都已经不被推荐使用,但人们一直以来使用的都还是 text/javascript 。实际上,服务器在传送 JavaScript 文件时使用的是 MIME 类型通常是 application/x-javascript , 但在 type 中设置这个值却可能导致脚本被忽略。考虑到约定俗成和最大限度的浏览器兼容性,目前 type 属性的默认值依旧还是 text/javascript

使用 <script> 元素的方式有两种 :直接在页面中嵌入 JavaScript 代码和包含外部 JavaScript 文件。

在使用 <script> 元素嵌入 JavaScript 代码时,只需为 <script> 指定 type 属性,然后直接把 JavaScript 代码放在元素内部即可。

1
2
3
<script>
// JavaScript Code
</script>

包含在 <script> 元素内部的 JavaScript 代码将被从上到下依次解释。在解释器对 <scirpt> 元素内部的所有代码求值完毕以前,页面中的其余内容都不会被浏览器加载或显示。

如果要通过 <script> 元素来包含外部 JavaScript 文件,那么 src 属性就是必须的。这个属性的值是一个指向外部 JavaScript 文件的链接。

1
<script type="text/javascript src="example.js"></script>

与解析嵌入式 JavaScript 代码一样,在解析外部 JavaScript 文件(包括下载该文件)时,页面的处理也会暂时停止。

无论如何包含代码,只要不存在 deferasync 属性,浏览器都会按照 <script> 元素在页面中出现的先后顺序对它们依次进行解析。

因此,在包含较多外部 JavaScript 文件的 HTML 页面中,将 <script> 标签放在 <head> 元素中,可能会造成严重的延迟。一般通过将全部 JavaScript 引用放在 <body> 元素中页面内容的后面。

在 HTML 中嵌入 JavaScript 代码虽然没有问题,但一般认为最好的做法还是尽可能使用外部文件来包含 JavaScript 代码。

基本概念

任何语言的核心都必然会描述这门语言最基本的工作原理,而描述的内容通常都要涉及这门语言的语法、操作符、数据类型、内置功能等用于构建复杂解决方案的基本概念。

语法

ECMAScript 的语法大量借鉴了 C 及其它类 C 语言的语法。

区分大小写

ECMAScript 中的一切都区分大小写(变量、函数名和操作符)。

标识符

所谓标识符,就是指变量、函数、属性的名字,或者函数的参数。标识符的组合规则如下:

  • 第一个字符必须是一个字母、下划线或一个美元符号;
  • 其它字符可以是字母、下划线、美元符号或数字。

按照惯例,ECMAScript 标识符采用驼峰大小写格式,也就是第一个字母小写,剩下的每个单词的首字母大写。

严格模式

ECMAScript 5 引入了严格模式的概念。严格模式是为 JavaScript 定义了一种不同的解析与执行模型。

1
"use strict";	// 这是一个编译指示,用于告诉支持的 JavaScript 引擎切换到严格模式

语句

ECMAScript 中的语句以一个分号结尾;如果省略分号,则由解析器确定语句的末尾。虽然语句末尾的分号不是必须的,但建议任何时候都不要省略它。

关键字和保留字

ECMAScript 描述了一组具有特定用途的关键字,这些关键字可用于表示控制语句的开始或结束,或者用于执行特定操作,它们不能用作标识符。 ECMAScript 还描述了一组将来可能被用作关键字的保留字。

变量

ECMAScript 的变量是松散类型的,所谓松散类型就死可以用来保存任何类型的数据。即每个变量仅仅是一个用于保存值得占位符。未被初始化的变量会保存一个特殊的值 :undefined 。ECMAScript 也支持直接初始化变量。

修改变量的值得同时也可以同时修改值得类型,虽然这样做是可行而且有效的,但是并不推荐。

如果定义变量时省略了 var 操作符,变量将会被定义为全局变量。给未经声明的变量赋值在严格模式下会导致抛出 ReferenceError 错误。

数据类型

ECMAScript 中有 5 中简单数据类型 :Undefined、Null、Boolean、Number 和 String。还有一种复杂数据类型 - Object。ECMAScript 不支持任何创建自定义类型的机制,所有值最终都将是上述 6 种数据类型之一。

typeof 操作符

ECMAScript 是松散类型的,因此需要有一种手段来监测给定变量的数据类型,使用 typeof 可以完成这个目的。

  • “undefined” : 未定义
  • “boolean” :布尔值
  • “string” :字符串
  • “number” :数值
  • “object” : 对象或 null
  • “function” : 函数

Undefined 类型

如果使用 var 声明变量但未对其加以初始化时,这个变量的值就是 undefined

值得注意的是,对未初始化的变量执行 typeof 操作符会返回 undefined 值,而对未声明的变量执行 typeof 同样也会返回 undefined 值。这两种变量虽然从技术角度上看有本质区别,但实际上无论对哪种变量也不可能执行真正的操作。

即便未初始化的变量会被自动赋予 undefined 值,但显式地初始化变量依然是明智的选择。如果能够做到这一点,那么当 typeof 返回 undefined 值时,我们就知道被检测的变量还没有被生命,而不是尚未初始化。

Null 类型

从逻辑角度来看,null 值表示一个空对象指针,而这也正是使用 typeof 操作符检测 null 值时会返回 “object” 的原因。

如果定义的变量准备在将来用于保存对象,那么最好将该变量初始化为 null 而不是其它值。这样一来,只要直接检查 null 值就可以知道相应的变量是否已经保存了一个对象对的引用。

实际上,undefined 是派生自 null 值得,因此规定它们的相等性测试返回 true

Boolean 类型

ECMAScript 中的 Boolean 类型只有两个字面值 :truefalse 。这两个值与数字值不是一回事,因此 true 不一定等于 1,而 false 也不一定等于 0 。

虽然 Boolean 类型的字面值只有两个,但 ECMAScript 中所有类型的值都有与这两个 Boolean 值等价的值。使用转型函数 Boolean() 可以将一个值转换为其对应的 Boolean 值。

数据类型 转换为 true 的值 转换为 false 的值
Boolean true false
String 任何非空字符串 “”(空字符串)
Number 任何非零数字值 0 和 NaN
Object 任何对象 null
Undefined n/a undefined

Number 类型

ECMAScript 中的 Number 类型使用 IEEE754 格式来表示整数和浮点数值。为了支持各种数值类型,ECMA 定义了不同的数值字面量格式。

1
2
3
var intNum = 55;	// 整数
var octalNum = 070; // 八进制整数 56
var hexNum = 0xA; // 十六进制整数 10

需要注意的是,八进制字面量在严格模式下是无效的,会导致支持的 JavaScript 引擎抛出错误。

在进行算数运算时,所有以八进制和十六进制表示的数值最终都将被转换为十进制数值。

浮点数值

保存浮点数值需要的内存空间是保存整数值的两倍,ECMAScript 会不失时机的将浮点数值转换为整数值。

对于那些极大或极小的数值,可以用科学表示法表示的浮点数值表示。

1
var floatNum = 3.14e7;	// 等价于 3.14 * 10^7

浮点数值的最高精度是17位小数,但在进行算数运算时其精确度远远不如整数。例如,0.1 加 0.2 的结果不是 0.3, 而是 0.30000000000000004 。这个舍入误差会导致无法测试特定的浮点数值。

数值范围

由于内存的限制,ECMAScript 并不能保存所有的数值。它能够表示的最小数值为 Number.MIN_VALUE ,在大多数浏览器中,这个值是 5e-234 。能够表示的最大数值为 Number.MAX_VALUE ,在大多数浏览器中,这个值是 1.976931348623157e+308 。如果某次计算的结果得到了一个 超出 JavaScript 数值范围的值,那么这个数值将被自动转换为特殊的 Infinity 值。如果某次计算返回了正或负的 Infinity 值,那么该值将无法继续参与下一次的计算,因为 Infinity 不是能够参与计算的数值。

可以使用 isFinite() 函数确定一个数值是不是无穷的。

NaN

NaN,即非数值,它是一个特殊的数值,这个数值用于表示一个本来要返回数值的操作数未返回数值的情况。比如在 ECMAScript 中,0 除以 0 会返回 NaN,正数除以 0 会返回 Infinity ,负数除以 0 返回 -Infinity

NaN 本省有两个非同寻常的特点。

首先,任何涉及 NaN 的操作都会返回 NaN,这个特点在多步计算中可能导致问题。

其次,NaN 与任何值都不相等,包括 NaN 本身。

针对 NaN 的特点,ECMAScript 定义了 isNaN() 函数。这个函数接受一个参数,该参数可以是任何类型,而函数会帮我们确定这个参数是否 “不是数值”。 isNaN() 在接收到一个值后,会尝试将这个值转换为数值。某些不是数值的值会直接转换为数值,而任何不能被转换为数值的值都会导致这个函数返回 true

数值转换

有三个函数可以将非数值转换为数值 :Number() , paresInt() , parseFloat()Number() 可以用于任何数据类型,而 parseInt() , parseFloat() 则用于把字符串转换成数值。

Number() 函数的转换规则如下 :

  • truefalse 将分别转换为 1 和 0
  • null 返回 0
  • undefined 返回 NaN
  • 如果是字符串,则
    • 如果只包含数字,返回十进制数值
    • 如果为十六进制格式,返回相同大小的十进制数值
    • 如果是空字符串,则返回 0
    • 如果包含除上述格式之外的字符,返回 NaN

由于 Number() 函数在转换字符串时比较复杂而且不够合理,因此处理整数的时候更常用的是 parseInt() 函数。它会忽略字符串前面的空格,直到找到第一个非空格字符。如果第一个字符不是数字字符或者负号,则返回 NaN。如果第一个字符是数字字符,parseInt() 会继续解析第二个字符,直到解析玩所有后续字符或者遇到了一个非数字字符。

需要注意的是,”1.1” 这样的字符串会被 parseInt() 转换为 1 ,因为小数点并不是有效的数字字符。

还可以为 parseInt() 函数指定第二个参数 :转换时使用的基数。

1
var num = parseInt("0xAF", 16);	// 以十六进制转换字符串	

parseInt() 不同的是,parseFloat() 只解析十进制值。

String 类型

String 类型用于表示由零活多个 16 位 Unicode 字符组成的字符序列,即字符串。字符串可以由双引号或单引号表示。

字符字面量

String 数据类型包含一些特殊的字符字面量,也叫转移序列,用于表示非打印字符,或者具有其它用途的字符。

字面量 含义
\n 换行
\t 制表
\b 空格
\r 回车
\\ 斜杠
\' 单引号
\" 双引号
\xnn 以十六进制代码表示的一个字符
\unnn 以十六进制代码表示的一个 Unicode 字符
\f 进纸

这些字符字面量可以出现在字符串中的任意位置,而且也将被作为也给字符来解析。

字符串的特点

ECMAScript 中的字符串是不可变的,也就是说,字符串一旦创建,它们的值就不能改变。要改变某个变量保存的字符串,首先要销毁原来的字符串,然后再用另一个包含新值得字符串填充该变量。

转换为字符串

几乎每个值都有 toString() 方法,它将返回相应值得字符串表现(nullundefined 没有这个方法 )。多数情况下,调用 toString() 方法不必传递参数,但是在调用数值的 toString() 方法时,可以传递一个表示输出数值基数的参数。

在不知道要转换的值是不是 nullundefined 的情况下,还可以使用转型函数 String() ,这个函数能够将任何类型的值转换为字符串 :

  • 如果值有 toString() 方法,则调用该方法
  • 如果值是 null ,则返回 “null”
  • 如果值是 “undefined” ,则返回 “undefined”

Object 类型

ECMAScript 中的对象其实就是一组数据和功能的集合。对象可以通过执行 new 操作符后跟要创建的对象类型的名称来创建。而创建 Object 类型的实力并为其添加属性和方法,就可以创建自定义对象。

在 ECMAScript 中,Object 类型是所有它的实例的基础。

Object 的每个实例都具有下列属性和方法 :

  • constructor : 保存着用于创建当前对象的函数。
  • hasOwnProperty(propertyName) : 用于检查给定的属性在当前的对象实例中是否存在。
  • isPrototypeOf(object) : 用于检查传入的对象是否是传入的对象的原型。
  • propertyIsEnumerable(propertyName) : 用于检查给定的属性是否能够使用 for-in 语句来枚举。
  • toLocalString() : 返回与执行环境的地区对应的字符串表示。
  • toString() : 返回对象的字符串表示。
  • valueOf() : 返回对象的字符串、数值或布尔值表示。

操作符

一元操作符

递增和递减操作符

前置递增/递减操作符,变量的值都是在包含它的语句被求值之前改变的,而后置递增/递减操作符的则是在包含它的语句被求值之后改变变量的值。

而且递增/递减操作符不仅适用于整数,还可以用于字符串、布尔值、浮点数职和对象 :

  • 在应用于一个包含有效数字字符的字符串时,先将其转换为数字值,再执行加减 1 的操作。字符串变量变成数值变量。
  • 在应用于一个不包含有效数字字符的字符串时,将变量的值设置为 NaN。字符串变量变成数值变量。
  • 在应用于布尔值时,先将其转换为 1 或 0,再执行加减 1 的操作。布尔值变量变成数值变量。
  • 在应用于对象时,先调用对象的 valueOf() 方法以取得一个可供操作的值。然后对该值应用前述规则。如果结果是 NaN,则在调用 toString() 方法后再应用前述规则。对象变量变成数值变量。
加和减操作符

+ 放在数值之前,对数值不会产生影响。不过,对非数值应用 + 时,它会像 Number() 转型函数一样对这个值进行转换。

- 主要用于表示负数。对非数值应用时,先转换为数值,再转换为负数。

位操作符

位操作符用于在最基本的层次上,即按内存中表示数值的位来操作数值。ECMAScript 中的所有数值都以 IEEE-754 64 位格式存储,但位操作符并不直接操作 64 位的值,而是先将 64 位的值转换成 32 位的整数,然后执行操作,最后再讲结果转换回 64 位。

对于有符号的整数,32 位中的前 32 位用来表示整数的值,第 32 位用于表示数值的符号 :0 表示正数,1 表示负数。这个表示符号的位叫做符号位,符号位的值决定了其它位数值的格式。其中,正数以纯二进制格式存储,32 位中的每一位都表示 2 的幂,没有用到的位以 0 填充,即忽略不计。负数同样以二进制码存储,但使用的格式是二进制补码。

要注意的是,在处理有符号整数时,是不能访问位 31 的。

对数值进行位操作时,NaN 和 Infinity 会被当做 0 来处理。

默认情况下,ECMAScript 中的所有整数都是有符号整数。不过,当然也存在无符号整数,对于无符号整数来说,第 32 位不再表示符号,因为无符号整数只能是整数。

按位非

按位非操作符由 ~ 表示,执行按位非的结果就是返回数值的反码。即操作数的负值减 1.

按位与

按位与操作符由 & 表示,它有两个操作符数。从本质上来讲,按位与操作就是将两个数值的每一位对齐,然后对相同位置上的两个数执行 AND 操作。

按位或

按位或操作符由 | 表示,同样有两个操作数。从本质上来讲,按位或操作就是将两个数值的每一位对齐,然后对相同位置上的两个数执行 OR 操作。

按位异或

按位异或操作符由 ^ 表示,同样有两个操作数。从本质上来讲,按位异或操作就是将两个数值的每一位对齐,然后对相同位置上的两个数执行 XOR 操作。

左移

左移操作符由 << 表示,它会将数值的所有位向左移动指定的位数,出现的空位用 0 进行填充。

注意,左移不会影响操作数的符号位。

有符号右移

右移操作符由 >> 表示,它会将数值的所有位向右移动指定的位数,但保留符号位,原数值中出现的空位以符号位的值来填充所有空位。

无符号右移

无符号左移由 <<< 表示,它会将数值的所有 32 位都向右移动。对正数来说,无符号右移的结果与有符号右移相同。但对负数来说,其结果就不一样了,而且无符号右移会将负数的二进制码当成整数的二进制码,因此导致无符号右移后的结果非常之大。

布尔操作符

逻辑非

逻辑非操作符由 ! 表示,可以应用于 ECMAScript 中的任何值。无论这个值是什么数据类型,这个操作符都会返回一个布尔值。逻辑非操作符首先会将它的操作数转换为一个布尔值,然后再对其求反。

逻辑非操作符也可以用于讲一个值转换为与其对应的布尔值。而同时使用两个逻辑非操作符,实际上就会模拟 Boolean() 转型函数的行为。

逻辑与

逻辑与操作符由 && 表示,有两个操作数。逻辑与操作可以应用于任何类型的操作数,而不仅仅是布尔值。在有一个操作数不是布尔值的情况下,逻辑与操作就不一定返回布尔值 :

  • 如果第一个操作数是对象,则返回第二个操作数
  • 如果第二个操作数是对象,则只有在第一个操作数的求值结果为 true 的情况下才会返回该对象
  • 如果两个操作数都是对象,则返回第二个操作数
  • 如果有一个操作数是 null , NaNundefined,则返回 null , NaNundefined

逻辑与操作属于短路操作,即如果第一个操作数能够决定结果,那么就不会对第二个操作数求值。

注意,不能在逻辑与操作中使用未定义的值。

逻辑或

逻辑或操作由 || 表示,有两个操作数。与逻辑与操作相似,如果有一个操作数不是布尔值,逻辑或也不一定返回布尔值 :

  • 如果第一个操作数是对象,则返回第一个操作数
  • 如果第一个操作数的求值结果为 false ,则返回第二个操作数
  • 如果两个操作数都是对象,则返回第一个操作数
  • 如果两个操作数都是 null , NaNundefined ,则返回 null , NaNundefined

逻辑或操作符也是短路操作符,也就是说,如果第一个操作数的求值结果为 true ,就不会对第二个操作数求值了。

乘性操作符

ECMAScript 定义了 3 个乘性操作符 :乘法、除法和求模,在操作数为非数值的情况下会执行自动的类型转换。

乘法

乘法操作符由 * 表示,用于计算两个数值的乘积。

  • 如果乘积结果超过了 ECMAScript 的数值表示范围,则返回 Infinity-Infinity
  • 如果有一个操作数是 NaN ,则结果是 NaN
  • Infinity 与 0 相乘,结果是 NaN
  • Infinity 与非 0 数值相乘,结果是 Infinity-Infinity ,取决于有符号操作数的符号
  • 如果 InfinityInfinity 相乘,则结果是 Infinity
  • 如果有一个操作数不是数值,则使用 Number() 将其转换为数值
除法

除法操作符由 / 表示,执行第二个操作数除第一个操作数的计算。

  • 如果商超过了 ECMAScript 的数值表示范围,则返回 Infinity-Infinity
  • 如果有一个操作数是 NaN ,则结果是 NaN
  • InfinityInfinity 除,结果是 NaN
  • 如果 0 被 0 除,结果是 NaN
  • 如果非零的有限数被零除,则结果是 Infinity-Infinity ,取决于有符号的操作数
  • 如果 Infinity 被任何非 0 数值除,则结果是 Infinity-Infinity ,取决于有符号的操作数
  • 如果有一个操作数不是数值,则使用 Number() 将其转换为数值
求模

求模操作符由 % 表示。

  • 如果被除数是无穷大值而出除数是有限大的数值,则结果是 NaN
  • 如果被除数是有限大的数值而除数是 0 ,则结果是 NaN
  • 如果是 InfinityInfinity 除,则结果是 NaN
  • 如果被除数是有限大的数值而除数是无穷大的数值,则结果是被除数
  • 如果被除数是 0 ,则结果是0
  • 如果有一个操作数不是数值,则使用 Number() 将其转换为数值

加性操作符

加法

加法操作符由 + 表示。

如果两个操作数都是数值,执行常规的加法计算,然后按照下列规则返回结果 :

  • 如果一个操作数是 NaN ,则结果是 NaN
  • 如果是 InfinityInfinity ,则结果是 Infinity
  • 如果是 -Infinity-Infinity ,则结果是 -Infinity
  • 如果是Infinity-Infinity ,则结果是 NaN

如果有一个操作数是字符串 :

  • 如果两个操作数都是字符串,则将它们拼接起来
  • 如果只有一个操作数是字符串,则将另一个操作数转换为字符串,然后再将两个字符串拼接起来

如果有一个操作数是对象、数值或布尔值,则调用它们的 toString() 方法取得相应的字符串值,然后再应用前述规则。

对于 undefinednull ,则分别调用 String() 函数并取得字符串 “undefined” 和 “null” 。

减法

减法操作符由 - 表示。

  • 如果有一个操作数是 NaN ,则结果是 NaN
  • 如果是 InfinityInfinity ,则结果是 NaN
  • 如果是 -Infinity-Infinity ,则结果是 NaN
  • 如果是 Infinity-Infinity ,则结果是 Infinity
  • 如果是 -InfinityInfinity ,则结果是 Infinity
  • 如果有一个操作数是字符串、布尔值、nullundefined ,则先调用 Number() 函数将其转换为数值,然后在按照前述规则执行减法计算。如果转换的结果是 NaN ,则减法的结果就是 NaN
  • 如果有一个操作数是对象,则调用对象的 valueOf() 方法以取得表示该对象的数值。如果得到的是 NaN ,则减法的结果就是 NaN 。如果对象没有 valueOf() 方法,则调用其 toString() 方法并将得到的字符串转换为数值

关系操作符

与 ECMAScript 中其它操作符一样,当关系操作符的操作数使用了非数值时,也要进行数据转换或完成某些奇怪的操作 :

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

在比较两个字符串时,实际比较的是两个字符串中对应位置的每个字符的字符编码值,因此一般需要将两个操作数转换为相同的大小写形式,然后再执行比较。

相等操作符

相等和不相等

ECMAScript 中的相等操作符由 == 表示,如果两个操作数相等,则返回 true 。而不相等操作符由 != 表示,如果两个操作数不相等,则返回 true 。这两个操作符都会先转换操作数,然后在比较它们的相等性。

在转换不同的数据时,需遵守以下规则 :

  • 如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值 – false 转换为 0 ,true 转换为 1
  • 如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值
  • 如果一个操作数是对象,另一个操作数不是,则调用对象的 valueOf() 方法,用得到的基本类型值按照前述规则进行比较

这两个操作符在进行比较时要遵守下列规则 :

  • nullundefined 时相等的
  • 比较相等性之前,不能将 nullundefined 转换为其它任何值
  • 如果有一个操作数是 NaN ,则相等操作符返回 false ,而不相等操作符返回 true
  • 如果两个数都是对象,则比较它们是否是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回 true ;否则, 返回 false

特殊情况 :

1
2
3
4
5
6
7
8
9
10
11
null == undefined 	// true
true == 1 // true
true == 2 // false
"NaN" == NaN // false
5 == NaN // false
undefined == 0 // false
NaN == NaN // false
null == 0 // false
NaN != NaN // true
"5" == 5 // true
false == 0 // true
全等和不全等

除了在比较之前不能转换为操作数之外,全等和不全等操作符与相等和不相等操作符没有什么区别。全等操作符由 === 表示,它只在两个操作数未经转换就想等的情况下返回 true

由于相等和不相等操作符存在类型转换问题,为了保持代码中数据类型的完整性,推荐使用全等和不全等操作符。

条件操作符

1
variable = boolean_expression ? true_value : false_value;	

基于对 boolean_expression 的求值结果,决定给变量 variable 赋什么值。

语句

if 语句、do-while 语句、while 语句、for 语句、for-in 语句与其它语言并没有不同,在此不做介绍。

label 语句

使用 label 语句可以在代码中添加标签,以便将来使用。

breakcontinue 语句

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

with 语句

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

1
with (expression) statment;

定义 with 语句的目的主要是为了简化多次编写同一个对象的工作。

1
2
3
4
5
6
7
8
9
10
11
var qs = location.search.substring(1);
var hostName = location.hostName;
var url = location.href;

// 使用 with 语句改写

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

严格模式下不允许使用 with 语句,视为语法错误。

由于大量使用 with 语句会导致性能下架,同时也会给调试代码造成困难,因此在开发大型应用程序时,不建议使用 with 语句。

swithc 语句

ECMAScript 中的 switch 有两个特点 :

  • 可以在 switch 中使用任何数据类型
  • 每个 case 的值不一定是常量,可以是变量,甚至是表达式

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

函数

ECMAScript 中的函数使用 function 关键字来声明,后跟一组参数及函数体。

1
2
3
function functionName (arg_0, arg_1, ...) {
statments
}

ECMAScript 中的函数在定义时不必指定是否返回值。

return 语句也可以不带有任何返回值。在这种情况下,函数在停止执行后将返回 undefined 值。这种用法一般用在需要提前停止函数执行而又不需要返回值的情况下。

严格模式下 :

  • 不能把函数或参数命名为 eval 或 arguments

  • 不能出现两个命名参数同名的情况

参数

ECMAScript 函数的参数不介意传递进来多少个参数,也不在乎传进来的参数是什么数据类型。ECMAScript 中函数的参数在内部是用一个数组来表示的,函数接收到的始终都是这个数组,而不关心数组中包含哪些参数。在函数体内,可以通过 arguments 对象来访问这个参数数组,从而获得传递给函数的每一个参数。

命名的参数只提供便利,但并不是必需的。而且在 ECMAScript 中,解析器不会验证命名参数。

没有传递值得命名参数将自动被赋予 undefined 值。

严格模式对如何使用 arguments 对象做出了一些限制 :无法对命名参数进行赋值;重写 arguments 的值会导致语法错误。

arguments 本质上并不是一个数组,只是与数组类似,你可以使用方括号语法来访问它的每一个元素。

没有重载

ECMAScript 中函数的参数有包含零或多个值得数组来表示,所以没有函数签名。而没有函数签名,真正的重载是不可能做到的。如果在 ECMAScript 中定义了同名函数,则该名字只属于后定义的函数。

JavaScript Tutorial

JavaScript Where To

Script can be placed in the , or in the section of an HTML page , or in both. But placing scripts at the bottom of the element improves the display speed , because script complation slow down the display.

Scripts can also be placed in external files and it is a practical method to solve the problem the same code is used in many different web pages . It’s important to note that you can’t contain tags in external scripts . Placing scripts in external files has some advantages as below :

  • It separates HTML and code
  • It makes HTML and JavaScript easier to read and maintain
  • Cached JavaScript files can speed up page loads

External scripts can be referenced with a full URL or with a path relative to the current web page .

JavaScript Output

JavaScript can display data use innerHTML/document.write()/window.alert()/console.log() .

  • Using innerHTML

    1
    document.getElementById().innHTML = 'content';

    Changing the innerHTML property of an HTML element is a common way to display data in HTML.

  • Using document.write()

    For testing purposes , it is convenient to use document.write() .

    1
    document.write('content');

    Noted: Using document.write() after an HTML document is fully loaded , will delete all existing HTML .

  • Using window.alert()

    1
    window.alert('content');
  • Using console.log()

    For debugging purpose, you can use the console.log() method to display data.

    1
    console.log('content');

JavaScript Statements

A JavaScript program is a list of programming statements. In HTML, JavaScript programs are executed by the web browser. A JavaScript statement consists of values, operators, expressions, keywords and comments. The statements are executed one by one, in the same order as they are written.

Semicolons

JavaScript statements separated by semicolons.

1
2
3
4
var a, b, c;
a = 1;
b = 2;
c = 3;

When separated by semicolons, you are allowed write multiple statements on one line.

Ending statements with semicolons is not required, but highly recommended.

JavaScript White Space

JavaScript ignores multiple spaces. You can add white space to your script to make ite more readable and put spaces around operators is a good practice.

JavaScript Line Length and Line Breaks

You should avoid code lines longer than 80 characters for best readability.

JavaScript Code Blocks

JavaScript statements can be grouped together in code blocks, inside curly brackets {}. The purpose of code blocks is to define statements to be executed together.

JavaScript Keywords

JavaScript statements often start with a keyword to identify the JavaScript action to be performed and they are all reserved words. Reserved words cannot be used as names of variables.

JavaScript Syntax

JavaScript syntax is the set of rules, how JavaScript programs are constructed.

JavaScript Values

The JavaScript syntax defines two types of values: Fixed values and variable values. Fixed values are called literals. Variable values are called variables.

JavaScript Operators

JavaScript uses arithmetic operators (+ - * /) to compute values and use an assignment operator (=) to assign values to variables.

JavaScript Expressions

An expressions is a combination of values, variables, and operators, which computes to a value.

JavaScript is Case Sensitive

All JavaScript identifiers are case sensitive.

JavaScript and Camel Case

Historically, programmers have used different ways of joining multiple words into one variable name .

  • Hyphens are not allowed in JavaScript. They are reserved for subtractions
  • Underscore
  • Upper Camel case (Pascal Case)
  • Lower Camel Case

JavaScript tend to use camel case that start with a lowercase letter, such as :

1
firstName, lastName, masterCard, interCity

JavaScript Character Set

JavaScript uses the Unicode character set

JavaScript Comments

JavaScript comments can be used to explain JavaScript code, and to make it more readable.

  • Single line comments start with //. Any text between // and the end of line will be ignored by JavaScript
  • Multi-line Comments start with /* and end with /. Any text between /\ and */ will be ignored by JavaScript

JavaScript Variables

JavaScript variables are containers for storing data values. In programming, just like in algebra, we use variables to hold values. All JavaScript variables must be identified with unique names. These unique names are called identifiers.

The general rules for constructing names for varialbes ard :

  • Names can contain letters, digits, underscores, and dollar signs.
  • Names must begin with a letter
  • Names can also begin with $ and __
  • Names are case sensitive
  • Reserved words cannot be used as names

It is a good programming practice to declare all variables at the beginning of a script.

A variable declared without a value will have the value undefined .

If you put a number in quotes, the rest of numbers will be treated as strings and concatenated.

JavaScript Data Types

JavaScript variables can hold many data types: numbers, string, objects and mored. In programming, data types is an import concept. To be able to operate on variables, it is important to konw something about the type.

When adding a number and a string, JavaScript will treat the number as a string.

JavaScript evaluates expression from left to right. Different sequences can produce different results.

JavaScript has dynammic types. This means that the same variable can be used to hold different data types.

You can use the JavaScript typeof operator to find the type of a JavaScript variable. The typeof operator returns the type of a variable or an expression.

In JavaScript, a variable without a value, has the value undefined . The typeof is also undefined .

In JavaScript null is “nothing”. It is supposed to be something that doesn’t exist. Unfortunately, in JavaScript, the data of null is an object. You can consider it a bug in JavaScript that typeof null is an object. It should be null.

Difference Between Undefined and Null

Undefined and null are equal in value but different in type

1
2
3
4
5
typeof undefined	// undefined
typeof null // object

null === undefined // false
null == undefined // true

JavaScript Functions

A JavaScript function is a block of code designed to perform a particular task. A JavaScript function is executed when “something” invokes it.

Syntax

A JavaScript function is defined with the function keyword, followed by a name, followed by parentheses (). Function names can contain letters, digits, underscored, and dollar signs. The parentheses may include parameter names separated by commas: (parameter, parameter, …).

Invocation

The code inside the function will execute when “something” invokes the function :

  • when an event occurs
  • When it is invoked from JavaScript code
  • Automatically

Return

When JavaScript reaches a return statement, the function will stop executing. If the function was invoked from a statement, JavaScript will return to execute the code after the invoking statement. Function often compute a return value. The return value is “returned” back to the caller.

1
2
3
4
5
6
7
// define a function
function convertStringToLowerCase (text) {
return text.toLocaleLowerCase();
}

// invoke a function
var lowerCaseStr = convertStringToLowerCase("Hello JavaScript !");

Accessing a function without () will return the function definition instead of the function result.

Why Functions?

You can reuse code by using function. As it is : Define the code once, and use it many times.

Functions used as variable values

Functions can be used the same way as you use variables, in all types of formulas, assignments, and calculations.

1
console.log("text".toLocalLowerCase());

JavaScript Object

JavaScript objects are containers for named values called properties or methods.

Object Definition

You define a JavaScript object with an object literal

1
var object = {key: value, key: value, ...};

Object Properties

The name:values pairs in JavaScript objects are called properties. You can access object properties in two ways :

  • objectName.propertyName
  • objectName["propertyName"]

Object Methods

Objects can also have methods - actions that can be performed on objects. Methods are stored in properties as function definitions. A method is a function stored as a property.

1
2
3
4
5
6
var objct = {
key: value,
key: value,
...
methodName: function () {}
};

The this keyword

In a function definition, this refers to the “owner” of the function.

Accessing Object Mehtods

You access an object method with the following syntax :

1
objctName.methodName();

Do Not Declare Strings, Numbers, and Booleans as Objects!

When a JavaScript variable is declared with the keyword “new”, the variable is created as an object. Avoid String, Number, and Boolean objects. They complicate your code and slow down execution speed.

JavaScript Scope

Scope determines the accessibility of variables.

Function Scope

In JavaScript there are two types of scope:

  • Local scope
  • Global scope

JavaScript has function scope: Each function creates a news scope. Scope determines the accessibility of these variables.

Variables defined inside a function are not accessible from outside the function.

Local Variables

Variables declared within a JavaScript function, become Local to the function. Local variables have local scope: They can only be accessed within the function.

Since local variables are only recognized inside their functions, variables with the same name can be used in different functions. Local variables are created when a function starts, and deleted when the function is completed.

Global Variables

A variable declared outside a function, become Global. A global variable has global scope: All scripts and functions on a web page can access it.

JavaScript Variables

In JavaScript, objects and functions are also variables.

Scope determines the accessibility of variables, objects, and functions from different parts of the code.

Automatically Global

If you assign a value to a variable that has not been declared, it will automatically become a global variable, even if the variable inside a function.

Strict Mode

All modern browsers support running JavaScript in “Strict Mode”. In Strict mode, global variables are not created automatically.

Global Variables in HTML

With JavaScript, the global variable scope is the complete JavaScript environment.

In HTML, the global scope is the window object. All global variables belong to the window object.

The Lifetime of JavaScript Variables

The lifetime of a JavaScript variable starts when it is declared.

Local variables are deleted when the function is completed.

In a web browser, global variables are deleted when you close the browser window, but remain available to new pages loaded into the same window.

JavaScript Events

HTML events are “things” that happen to HTML elements. When JavaScript is used in HTML pages, JavaScript can “react” on these events.

HTML Events

An HTML event can be something the browser dose, or somthing a user does.

JavaScript lets you execute code whtn events are detected. HTML allows event handler attributes, with JavaScript code, to be added to HTML elements.

1
<element event="some JavaScript">

JavaScript code is often several lines long. It is more common to see event attributes calling functions.

1
<element event="functoinName">

What can JavaScript Do ?

Event handlers can be used to handle, and verify, user input, user actions, and browser actions.

JavaScript Strings

JavaScript strings are used for storing and manipulating text.

String Length

The length of a string is found in the built in property length.

1
2
var txt = "AaBbCcDdEeFfGg";
var length = txt.length;

Special Characters

Because strings must be written within quotes, JavaScript will misunderstand this string and the solution to avoid this problem, is to use the backslash escape character.

Breaking Long Code Lines

For best readability, programmers often like to avoid code lines longer than 80 characters. If a JavaScript statements does not fit on one line, the best place to break it is after an operator.

1
2
document.getElementById("element-id").innerHTML =
"content";

You can also break up a code line within a text string with a single backslash.

1
2
3
var text = "Line - 1 \
Line - 2
";

Strings Can be Objects

Normally, JavaScript strings are primitive values, created from literals, but strings can also be defined as objects with the keyword new.

1
2
var literalStr = "literal string";
var objectStr = new String("object string");

JavaScript String Methods

Primitive values cannot have propertiess or methods, because they are not objects.

But with JavaScript, methods and properties are also available to primitive values, because JavaScript treats primitive values as objects when executing methods and properties.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
var orignalStr = "AaBbCcDdAaBbCcDd";
var subStr = "aB";

// the length property returns the length of a string
// strLength is 8
var strLength = orignalStr.length;

// the indexOf() method returns the index of the first occurrence of a specified text in a string
// subStrIndex is 1

// the lastIndexOf() method returns the index of the last occurrence of a specified text in a string
// lastIndexOfSubStr is 9

// Both indexOf() and lastIndexOf() return -1 if the text is not found
// Both indexOf() and lastIndexOf() accept a second parameter as the starting position for search.
var subStrIndex = orignalStr.indexOf(subStr);
var lastIndexOfSubStr = orignalStr.lastIndexOf(subStr);

// the search() method search a string for a specified value and return the position of the match
// search() method cannot accept second parameter and can take regular expressions as search values
// subStrIndex is 1
var subStrIndex = orignalStr.search(subStr);

// slice(start, end),substring(start, end), and substr(start, length) for extracting a part of a string.
// slice() extracts a part of a string and returns the extracted part in a new string
// substring() is similar to slice(), the difference is that substring() cannot accept negative indexes
// substr() is similar to slice(), the difference is that the second parameter specifies the length of the extracted part.

// now resultStr1, resultStr2, resultStr3 is AaBb
var resultStr1 = orignalStr.slice(0, 3);
var resultStr2 = orignalStr.substring(0, 3);
var resultStr3 = orignalStr.substr(0, 4);

// the replace() method replaces a specified value with anthor value in a string
// resultStr now is Hello JavaScript
// the replace() method does not change the string it is called on, it returns a new string.
// By default, the replace() function replaces only the first match
// To replace all matches, use a regular expression with a /g flag
orignalStr = "Hello Lee Code";
resultStr = orignalStr.replace("Lee Code", "JavaScript");

// A string is converted to upper case with toUpperCase()
// now resultStr is ABCDEFG
orignalStr = "abcdefg";
resultStr = orignalStr.toUpperCase();
// A string is converted to lower case with toLowerCase()
resultStr = resultStr.toLowerCase();

// contact() joins two or more strings
var contactStr = "A".contact("B","C","D");

// trim() method remove whitespace from both sides of a string
// now resultStr is ABC
orignalStr = "A B C";
resultStr = orignalStr.trim();

// the charAt() method returns the character at a specified index in a string
// now resultStr is A
resultStr = orignalStr.charAt(0);

// Accessing a string as an array is unsafe
// You can accessing a string as an array but it is upsafe and unpredictable
// If you want read a string as an array, convert it to an array first
// split() method can convert a string to an array
// now charArr is [a, b, c, d, e, f, g]
orignalStr = "a,b,c,d,e,f,g";
var charArr = orignalStr.split(",");

All string methods return a new string. They don’t modify the orignal string.

Formally said: Strings are immutable: Strings cannot be changed, only replaced.

JavaScript Numbers

JavaScript has only one type of number. Numbers can be writtenn with or without decimals.

Extra large or extra small numbers can be written with scientific notation.

1
2
let x = 123e5;
let y = 123e-5;

JavaScript Numbers are always 64-bit Floating Point

JavaScript numbers are always stored as double precision floating point numbers, following the international IEEE 754 standard.

This format stores numbers in 64 bits, where the number is stored in bits 0 to 51, the exponent in bits 52 to 62, and the sign in bit 63.

Precision

Integers are accurate up to 15 digits.

The maximum number of decimals is 17, but floating point arithmetic is not always 100% accurate.

1
2
// x will be 0.3000000000000004
let x = 0.2 + 0.1;

To solve the problem above, it helps to multiply and divide

1
let x = (0.2*10 + 0.1*10) / 10;

Adding Numbers and Strings

JavaScript uses the + operator for both addition and concatenation. Numbers are added. Strings are concatenated.

1
2
3
4
5
6
7
8
9
// strResult will be 12
let strX = "1";
let strY = "2";
let strResult = strX + strY;

// numberResult will be 3
let numberX = 1;
let numberY = 2;
let numberResult = numberX + numberY;

The JavaScript compiler works from left to right.

Numeric Strings

JavaScript strings can have numeric content and JavaScript will try to convert strings to numbers in all numeric operations.

1
2
3
4
// result will be 0.5
let x = "1";
let y = "2";
let result = x / y;

NaN - Not a Number

NaN is a JavaScript reserved word indicating that a number is not a legal number.

Try to do arithmetic with a non-numberic string will result in NaN.

1
2
// x will be NaN
let x = 100 / "a";

You can use the global JavaScript function isNaN() to find out if a value is a number.

NaN is a number : typeof NaN returns number:

1
2
// returns "number"
typeof NaN;

Infinity

Infinity is the value JavaScript will return if you calculate a number outside the largest possible number.

Division by 0 also generates Infinity. Infinity is a number: typeof Infinity returns number.

Hexadecimal

JavaScript interprets numeric constants as hexadecimal if they are preceded by 0x.

Never write a number with a leading zero. Some JavaScript versions interpret numbers as octal if they are written with a leading zero.

By default, JavaScript displays numbers as base 10 decimals. But you can use the toString() method to output numbers from base 2 to base 36.

1
2
3
4
5
6
let number = 32;
number.toString(10); // 32
number.toString(32); // 10
number.toString(16); // 20
number.toString(8); // 40
number.toString(2); // 100000

Numbers Can be Objects

Normally JavaScript numbers are primitive values created from literals, but numbers can also be defined as objects with the keyword new.

1
2
let x = 123;				// typeof x return number
let y = new Number(123); // typeof y return object

Do not create Number objects. It slows down execution speed.

Or even worse, Objects cannot be compared.

JavaScipt Number Methods

Number methods help you work with numbers.

All number methods can be used on any type of numbers.

  • toString()

    toString() returns a number as a string

  • toExponential()

    toExponential() returns a string, with a number rounded and written using exponential notation.

    A parameter defines the number of characters behind the decimal point. The parameter is optional, if you don’t specify it, JavaScript will not round the number.

    1
    2
    3
    4
    let x = 1.23456;
    x.toExponential(2); // 1.23e+0
    x.toExponentail(4); // 1.2345e+0;
    x.toExponentail(6); // 1.234560e+0
  • toFixed()

    toFixed() returns a string, with the number written with a specified number of decimals.

    1
    2
    3
    4
    5
    let x = 1.2345;
    x.toFixed(0); // 1
    x.toFixed(2); // 1.23
    x.toFixed(4); // 1.2345
    x.toFixed(6); // 1.234500
  • toPrecision()

    toPrecision() returns a string, with a number written with a specified length

    1
    2
    3
    4
    5
    let x = 1.2345;
    x.toPrecision(); // 1.2345
    x.toPrecision(2); // 1.2
    x.toPrecision(4); // 1.235
    x.toPrecision(6); // 1.234500
  • valueOf()

    valueOf() returns a number as a numer. This method is used internally in JavaScript to convert Number objects to primitive values.

    All JavaScript data types have a valueOf() and a toString() method.

Converting Variables to Numbers

There are 3 JavaScript methods that can be used to convert variables to numbers : Number() , parseInt() , parseFloat() . These methods are not number methods, but global JavaScript methods.

JavaScript global methods can be used on all JavaScript data types.

  • Number()

    This method returns a number, converted from its argument.

    1
    2
    3
    4
    5
    6
    Number(true);	// 1
    Number(false); // 0
    Number("10"); // 10
    Number(" 10"); // 10
    Number("10 20"); // NaN
    Number("John"); // NaN

    If the number cannot be converted, NaN is returned.

    Number() can also convert a date to a number

    1
    Number(new Date("2016-06-10");	// returns the number of milliseconds since 1.1.1970
  • parseFloat()

    This method parses its argument and returns a floating point number. Space are allowed.

    1
    2
    3
    4
    5
    parseFloat("10");			// 10
    parseFloat("10.33"); // 10.33
    parseFloat("10 20"); // 10
    parseFloat("10 years"); // 10
    parseFloat("years 10"); // NaN
  • parseInt()

    This method parsed its argument and returns an integer. Space are allowed.

    1
    2
    3
    4
    5
    parseInt("10");			// 10
    parseInt("10.33"); // 10
    parseInt("10 20 30"); // 10
    parseInt("10 years"); // 10
    parseInt("years 10"); // NaN

Number Properties

Number properties belongs to the JavaScript’s number object wrapper called Number. These properties can only be accessed as Number.propertyName . And these properties cannot be used on variables.

  • MAX_VALUE

    Returns the largest number possible in JavaScript

  • MIN_VALUE

    Returns the smallest number possible in JavaScript

  • NEGATIVE_INFINITY

    Represents negative infinity (returned on overflow)

  • NaN

    Represents a “Not-a-Number” value

  • POSITIVE_INFINITY

    Return infinity (return on overflow)

JavaScript Arrays

JavaScript arrays are used to store multiple values in a single variable. An array can hold many values under a single name, and you can access the values by referring to an index number.

Creating an Array

Using an array litral is the easiest way to create a JavaScript Array.

1
var array_name = [item, item, item, ...];

Using the keyword new create an array.

1
var array_name = new Array(item, item, ...);

Accessing the Elements of an Array

You refer to an array element by referring to the index number.

1
2
3
4
let arrayNum = [1, 2, 3, 4];
let element = arrayNum[0]; // element is 1
arrayNum[1] = 0;
element = arrayNum[1]; // element is 0

Accessing the Full Array

With JavaScript, the full array can be accessed by referring to the array name.

Arrays are a special type of objects, with numberd indexes. The typeof operator in JavaScript returns “object” for arrays.

How to Recognize an Array?

  • Array.isArray()

  • Create your own isArray() function

    1
    2
    3
    function isArray(arr) {
    return arr.constructor.toString().indexOf("Array") > -1;
    }
  • instanceof operator

    1
    2
    let arrayNum = [1, 2, 3, 4];
    arrayNum instanceof Array // true

JavaScript Array Methods

  • toString()

    The method toString() converts an array to a string of array values.

    1
    2
    let arrayStr = ["Hello ", " World", " !"];
    let resultStr = arrayStr.toString(); // result is Hello World !

    JavaScript automatically converts an array to a comma separated string when a primitive value is expected. This is always the case when you try to output an array.

    1
    2
    let strArr = ["Apple", "Google", "Facebook", "Twitter"];
    let result = strArr.toString(); // result is "Apple","Google","Facebook","Twitter"
  • join()

    The method join() alos joins all array elements into a string. It behaves just like toString(), but in addition you can specify the separator.

    1
    2
    let arrayNum = [1, 2, 3, 4];
    let rusultStr = arrayNum.join("*"); // resultStr is 1*2*3*4
  • pop()

    The method pop() removes the last element from an array

    1
    2
    let arrayNum = [1, 2, 3, 4];
    let result = arrayNum.pop(); // arrayNum now is [1, 2, 3] and result is 4
  • push()

    The method push() adds a new element to an array at the end and return the new array length.

    1
    2
    let arrayNum = [1, 2, 3];
    let result = arrayNum.push(4); // now arrayNum is [1, 2, 3, 4] and result is t
  • shift()

    The method shift() removes the first array element and shifts all other elements to a lower index.

    1
    2
    let arrayNum = [1, 2, 3, 4];
    let result = arrayNum.shift(); // arrayNum is [2, 3, 4] and result is 1
  • unshift()

    The method unshift() method adds a new element to an array at the beginning and return the new array length

    1
    2
    let arrayNum = [1, 2, 3, 4];
    let result = arrayNum.unshift(0); // now arrayNum is [0, 1, 2, 3, 4] and result is 5
  • splice()

    The method splice() method can be used to add new items to an array.

    1
    2
    let arrayNum = [1, 2, 3];
    arrayNum.splice(2, 0, 0.1, 0.2); // now arrayNum is [1, 2, 0.1, 0.2, 3]

    Also can be used to remove elements

    1
    2
    let arrayNum = [1, 2, 3];
    arrayNum.splice(0, 1); // now arrayNum is [2, 3]
  • concat()

    The method concat() creates a new arry by merging existing arrays and it can take any number fo array arguments.

    1
    2
    3
    let arrX = [1, 2, 3];
    let arrY = [4, 5];
    let result = arrX.concat(arrY); // result is [1, 2, 3, 4, 5]

    The concat() method can also take values as arguments.

  • slice()

    The slice() method slices out a piece of an array into a new array and creates a new array. It does not remove any elements from the source array.

    1
    2
    3
    let strArr = ["A", "B", "C", "D"];
    let result = strArr.slice(1); // result is ["B", "C", "D"]
    result = strArr.slice(1, 3); // now result is ["B", "C"]

Sorting an Array

  • sort()

    The sort() method sorts an array alphabetically

    1
    2
    let strArr = ["b", "d", "a", "c"];
    strArr.sort(); // now strArr is ["a", "b", "c", "d"]

    By default, the sort() function sorts values as strings. Because of this, the sort() method will produce incorrect result when sorting numbers. You can fix this by providing a compare function.

    1
    2
    3
    4
    5
    6
    7
    let numArr = [100, 20, 50 10, 40];
    numArr.sort( function (numX, numY) {
    return numX - numY;
    }); // now numArr is [10, 20, 40, 50, 100]
    numArr.sort( function (numX, numY) {
    return numY - numX;
    }); // now numArr is [100, 50, 40, 20, 10]

    The purpose of the compare function is to define an alternative sort order. The compare function should return a negative, zero, or positive value, depending on the arguments.

    You can even sorting an array in random order.

    1
    2
    3
    4
    let numArr = [1, 2, 4, 3, -1];
    numArr.sort( function (numX, numY) {
    return 0.5 - Math.random();
    });
  • reverse()

    The reverse() method reverses the elements in an array.

    1
    2
    3
    let strArr = ["b", "d", "a", "c"];
    strArr.sort(); // now strArr is ["a", "b", "c", "d"]
    strArr.reverse(); // now strArr is ["d", "c", "b", "a"]
  • Find the highest (or lowest) array value

    There are no built-in functions for finding the max or min value in an array.

    However, after you have sorted an array, you can use the index to obtain the highest and lowest values

    1
    2
    3
    4
    5
    6
    7
    8
    9
    let numArr = [100, 20, 50, 10, 40];
    numArr.sort( function (numX, numY) {
    return numX - numY;
    }); // now numArr is [10, 20, 40, 50, 100]
    const minValue = number[0]
    numArr.sort( function (numX, numY) {
    return numY - numX;
    }); // now numArr is [100, 50, 40, 20, 10]
    const maxValue = numArr[0];

    You can also use Math.max.apply to find the highest number in an array

    1
    2
    3
    4
    5
    let numArr = [100, 20, 50, 10, 40];
    function maxValueInArr (arr) {
    return Math.max.apply(null, arr);
    };
    const maxValue = maxValueInArr(numArr); // maxValue is 100

    Or ues Math.min.apply() to find th lowest number in an array

    1
    2
    3
    4
    5
    let numArr = [100, 20, 50, 10, 40];
    function minValueInArr (arr) {
    return Math.min.apply(null, arr);
    };
    const minValue = minValueInArr(numArr); // minValue is 100

    The fastest solution is to use a “home made” method.

    The function loops through an array comparing each value with the highest value found.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function maxValueInArr (arr) {
    let arrLen = arr.length;
    let maxValue = - Infinity;
    while (arrLen --) {
    if (arr[arrLen] > max) {
    maxValue = arr[arrLen];
    }
    }
    return maxValue;
    }

    The function loops through an array comparing each value with lowest value found.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function minValueInArr (arr) {
    let arrLen = arr.length;
    let minValue = Infinity;
    while (arrLen--) {
    if (arr[arrLen] < minValue) {
    minValue = arr[arrLen];
    }
    }
    retrun minValue;
    }

    JavaScript arrays often contain objects. Even if objects have properties of different data types, ther short() method can be used to sort the array. The solution is to write a compare function to compare the property values.

    1
    2
    3
    4
    5
    6
    7
    8
    let objArr = [{name:"a", age: 19}, {name:"b", age: 9}, {name:"ac", age: 80}];
    objArr.sort(function sortObjByAge (objX, objY) {
    let ageX = objX.age;
    let ageY = objY.age;
    if (ageX < ageY) { return -1;}
    if (ageX > ageY) {return 1;}
    return 0;
    });

JavaScript Array Iteration

Array iteration methods operate on every array item.

  • Array.forEach()

    The forEach() method calls a function once fro each array element.

    1
    2
    3
    4
    5
    let strArr = ["a", "b", "c", "d"];
    let result = "";
    strArr.forEach( function (value, index, arr) {
    result = result + value + "\n";
    });
  • Array.map()

    The map() method creates a new array by performing a function on each arry element.

    1
    2
    3
    4
    let numArr = [12, 2, 33, 21, 4];
    let newNumArr = numArr.map( function (value, index, array) {
    return value*2;
    });
  • Array.filter()

    The filter() method creates a new array with array elements that passes a test.

    1
    2
    3
    4
    let numArr = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0];
    let evenNumArr = numArr.filter( function (value, index, arr) {
    if (value % 2 == 0) { return value;}
    });
  • Array.reduce()

    The reduce() method reduces an array to single variable.

    1
    2
    3
    4
    let numArr = [1, 3, 5, 7, 9];
    let result = numArr.reduce( (sum, value) => {
    return sum + value;
    });
  • Array.every()

    The every() method check if all array values pass a test.

    1
    2
    3
    4
    let numArr = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0];
    let evenNumArr = numArr.every( function (value) {
    if (value % 2 == 0) { return value;}
    });
  • Array.indexOf()

    Search an array for an element value and returns its position.

    1
    2
    let numArr = [1, 3, 5, 7, 9];
    let result = numArr.idnexOf(5);
  • Array.lastIndexOf()

    Array.lastIndexOf() is the same as Array.indexOf() , but searches from the end of the array.

JavaScript Date Objects

By default, JavaScript will use the browser’s time zone and display a date as a full text string :

Fri Jun 13 2016 14:24:54 GMT+0800 (China Standard Time)

Creating Date Objects

Date objects are created with the new Date() constructor.

1
2
3
4
5
6
7
8
9
// new Date() create a new date object with current date and time
new Date();
// new Date(year, month, ...) create a new date object with a specified date and time
new Date(year, month, day, hours, minutes, seconds, milliseconds);
// JavaScript store dates as number of milliseconds since January 01, 1970, 00:00:00 UTC
// new Date(milliseconds) creates a new date object as zero time plus milliseconds
new Date(milliseconds);
// new Date(date string) creates a new date object from a date string
new Date(date string);

Date Methods

When a Date object is created, a number of methods allow you to operate on it.

  • toString()

    JavaScript will output dates in full text string format in HTML with the toString() method.

  • toUTCString()

    The toUTCString() method convert a date to a UTC string

  • toDateString()

    The toDateString() method converts a date to a ore readable format.

Date Formats

There are generally 3 types of JavaScript date input formats

  • ISO Date - “2016-06-10”

    The ISO format follows a strict standard in JavaScript. ISO 8601 is the international standard for the representation of date and times. The ISO 8601 syntax (YYYY-MM-DD) is also the preferred JavaScript date format.

    The computed date will be relative to your time zone.

  • Short Date - “06/10/2016”

    In some browsers, months or days with no leading zeroes may produce an error.

  • Long Date - “Jun 10 2016”

    Long dates are most often written with a “MMM DD YYYY” syntax like this.

Time Zone

When setting a date, without specifying the time zone, JavaScript will use the browser’s time zone.

Date Input - Parsing Dates

If you have a valid date string, you can use the Date.parse() method to convert it to milliseconds. Date.parse() returns the number of milliseconds between the date and January 1, 1970.

1
let milliSeconds = Date.parse("Jun 10, 2016");

You can the use the number of milliseconds to convert it to a date object.

Get Date Methods

These methods can be used for getting information from a date object

Method Description
getFullYear() Get the year as a four digit number (yyyy)
getMonth() Get the month as a number (0-11)
getDate() Get the day as a number(1-31)
getHours() Get the hour(0-23)
getMinutes() Get the minute(0-59)
getSeconds() Get the second(0-59)
getMilliseconds() Get the millisecond(0-999)
getTime() Get the time(milliseconds since January 1, 1970)
getDay() Get the weekday as a number(0-6)

Set Date Methods

Set date methods let you set date values for a date object

Method Description
setDate() Set the day as a number (1-31)
setFullYear() Set the year
setHours() Set the hour (0-23)
setMilliseconds() Set the milliseconds (0-999)
setMinutes() Set the minutes (0-59)
setMonth() Set the month (0-11)
setSeconds() Set the seconds (0-59)
setTime() Set the time (milliseconds since January 1, 1970)

Compare Dates

Dates can easily be compared. JavaScript counts months from 0 to 11. January is 0. December is 11.

JavaScript Math Object

The JavaScript Math object allows you to perform mathematical tasks on numbers.

  • Math.round()

    Math.round(x) returns the value of x rounded to its nearest integer

    1
    const result = Math.round(4.1); // result is 4
  • Math.pow()

    Math.pow(x, y) returns the value of x to the power of y.

    1
    const result = Math.pow(2, 10);	// result is 1024
  • Math.sqrt()

    Math.sqrt(x) returns the square root of x

    1
    const result = Math.sqrt(64); // result is 8
  • Math.abs()

    Math.abs(x) returns the absolute values of x

    1
    const result = Math.abs(-1); // result is 1
  • Math.ceil()

    Math.ceil(x) returns the value of x rounded up to its nearest integer

    1
    const result = Math.ceil(4.4); // result is 5
  • Math.floor()

    Math.floor(x) returns the value of x rounded down to its nearest integer.

    1
    const result = Math.floor(4.7); // result is 4
  • Math.sin()

  • Math.cos()

  • Math.min() and Math.max()

    Math.min() and Math.max() can be used to find lowest or highest value in a list of arguments

  • Math.random()

    Math.random() returns a random number between 0 and 1

Math Properties

JavaScript provides 8 mathematical constants that can be accessed with the Math object.

1
2
3
4
5
6
7
8
Math.E 			// returns Euler's number
Math.PI // returns PI
Math.SQRT2 // returns the square root of 2
Math.SQET1_2 // returns the square root of 1/2
Math.LN2 // returns the natural logarithm of 2
Math.LN10 // returns the natural logarithm of 10
Math.LOG2E // returns the base 2 logarithm of E
Math.LOG10E // returns the base 10 logarithm of E

All methods and properties can be used without creating a Math object first.

Random

Math.random() returns a random number between 0(inclusive), and 1(exclusive)

1
2
3
4
5
Math.floor(Math.random() * 10);	// returns a random integer from 0 to 9

function getRandomInteger (min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}; // This function returns a random number min(include) to max(excluded)

Booleans

JavaScript has a Boolean data types. It can only take the values true or false.

The Boolean() Function

You ca use the Boolean() function to find out if an expression is true

1
const result = Boolean(-1 > 0);	// result is false

Everything with a “Value” is true. Everything without a “Value” is false.

1
2
3
4
5
6
7
8
9
10
11
12
13
// result is true
Boolean(100);
Boolean(-100);
Boolean("100");
Boolean("false");

// result is false
Boolean(0);
Boolean(-0);
Boolean("");
Boolean(null);
Boolean(false);
Boolean(NaN);

Object can’t be compared.

JavaScript Comparison and Logical Operators

Comparison and Logical operators are used to test for true or false. Comparison operators are used in logical statements to determine equality or difference between variables or values.

Operator Description
== Equal to
=== Equal value and equal type
!= Not equal
!== Not equal value or not equal type
> Greater than
< Less than
>= Greater than or equal to
<= Less than or equal to

Logical operators are used to determine the logic between variables or values.

Operator Description
&& and
`
! not

JavaScript also contains a conditionnal operator that assigns a value to a variable based on some condition.

1
variableName = (condition) ? valueX:valueY;

Comparing data of different types may give unexpected results. To secure a proper result, variables should be converted to the proper type before comparison.

JavaScript Bitwise Operators

Operator Name Description
& AND Sets each bit to 1 if both bits are 1
` ` OR
^ XOR Sets each bit to 1 only one of two bits is 1
~ NOT Inverts all the bits
<< Zero fill left shift Shifts left by pushing zeros in from the right and let the leftmost bits fall of
>> Signed right shift Shifts right by pushing copies of the leftmost bit in from the left, and let the rightmost bits fall off
>>> Zero fill right shift Shifts right by pushing zeros in from the left, and let the rightmost bits fall off

JavaScript stores numbers as 64 bits floating point numbers, but all bitwise operations are performed on 32 bits binary numbers. Before a bitwise operation is performed, JavaScript converts numbers to 32 bits signed integers. After the bitwise operation is performed, the result is converted back to 64 bits JavaScript numbers.

JavaScript Regular Expressions

A regular expression is a sequence of characters that forms a search pattern. The serarch pattern can be used for text search and tet replace operations.

A regular expression ban be a single character, or more complicated pattern. Regular expression can be used to perform all types of text search and text replace operations.

1
/pattern/modifiers;

Using string methods

In JavaScript, regular expressions are often used with the two string methods: search() and replace(). The search() method uses an expression to search for match, and returns the position of the match. The replace() method returns a modified string where the pattern is replaced.

1
2
3
4
let originStr = "Hello JavaScript!";
let searchResult = originStr.search("Hello"); // reulst is 0
let replaceResult = originStr.replace("Hi", "Hello"); // result is Hi JavaScript!
repalceResult = originStr.replace(/hi/i, "Visit"); // result is Visit JavaScript!

Regular Expression Modifiers

Modifier Description
i Perform case-insensitive matching
g Perform a global match (find all matches rather than stopping after the first match)
m Perform multiline matching

Regular Expression Patterns

Brackets are used to find a range of characters

Expression Description
[a-zA-Z] Find any of the characters between the brackets
[0-9] Find any of the digits between the brackets
[x|y] Find any of the alternatives separated with `

Metacharacters are characters with a special meaning

Metacharacter Description
\d Find a digit
\s Find a whitespace character
\b Find a match at the beginning or at the end of a word
\uxxxx Find the Unicode character specified by the hexadecimal number xxxx

Quantifiers define quantities

Quantifier Description
n+ Matches any string that contains at least one n
n* Matches any string that contains zero or more occurrences of n
n? Matches any string that contains zeor or one occurrences of n

Using test()

The test() method is a RegExp expression method. It searches a string for a pattern, and returns true or false, depending on the result.

1
2
let pattern = /e/;
pattern.test("The best things in life are free!"); // return true

Using exec()

The exec() method is a RegExp expression method. It searches a string for a specified pattern, and returns the found text. If no match is found, it returns null.

1
/e/.exec("The best things in life are free!");	// return e

JavaScript Errors

The try statments lets you test a block of code for errors. The catch statments lets you handle the error. The throw statments lets you create custom errors. The finally statment lets you execute code, after try and catch, regardless of the result.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
try {
// Block of code to try
} catch (err) {
// Block of code to handle errors
}

try {
throw 'error'; // you can throw a exception
} catch (err) {
// Block of code to handle errors
}

try {
// Block of code to try
} catch () {
// Block of code to handle errors
} finally {
// Block of code to be executed regardless of the try/catch result
}

Ther Error Object

JavaScript has built in error object that provides error information when an error occurs. The error object provides two useful properties: name and message.

Property Description
name Sets or returns an error name
message Sets or returns an error message

Error Name Values

Error Name Description
EvalError An error has occurred in the eval() function.(Newer versions of JavaScript does not throw an EvalError. Use SyntaxError instead.)
RangeError A number “out of range” has occurred
ReferenceError An illegal reference has occurred
SyntaxError A syntax error has occurred
TypeError A type error has occurred
URIError An error in encodeURI() has occurred

JavaScript Debugging

Debuggin is not easy. But fortunately, all modern browsers have a built-in JavaScript devugger. Built-in debugger can be turned on and off, forcing errors to be reported to the user.

The console.log() Method

If your browsers supports debugging, you can use console.log() to display JavaScript values in the debugger window.

1
2
3
<script>
console.log(value);
</script>

Setting Breakpoints

In the debugger window, you can set breakpoints in the JavaScript code. At each breakpoint, JavaScript will stop execution, and let you examine JavaScript values. After examining values, you can resume the execution of code.

The debugger Keyword

The debugger keyword stops the execution of JavaScript, and calls (if available) the debugging function.

1
2
3
let x = 0;
debugger;
document.getElementById("ele_id").innerHTML = x;

JavaScript Hoisting

Hoisting is JavaScript’s default behavior of moving declarations to the top. In JavaScript, a variable can be declared after it has been used. In others words, a variable can be used before it has been declared.

1
2
3
numberX = 5;
numberX += 10;
let numberX;

To understand this, you have to understand the term ‘hoisting’. Hoisting is JavaScript’s default behavior of moving all declarations to the top of current scope.

Beware, JavaScript only hoists declarations, not initializations.

Declare your variables at the top

Hoisting is an unknown or overlooked behavior of JavaScript. If a developer doesn’t understand hoisting, programs may contain bugs or errors. To avoid bugs, always declare all variables at the beginning of every scope. Since this is how JavaScript interprets the code, it is always a good rule.

JavaScript in strict mode does not allow variables to be used if they are not decalred.

JavaScript Use Strict

use strict; Defines that JavaScript code should be executed in ‘strict mode’.

The use strict directive was new in ECMAScript version 5. It is not a statment, but a literal expression, ignored by eraily version of JavaScript. The purpose of use strict is to indicate that the code should be executed in ‘strict mode’. With strict mode, you can not, for example, use undeclared variables.

You can use strict mdoe in all your programs. It helps you to write cleaner code, like preventing you from using undeclared variables.

Declaring Strict Mode

Strict mode is decalred by adding "use strict"; to the beginning of a script or a function.

Declared at beginning of a script, it has global scope.

1
2
3
4
<script>
"use strict";
// your JavaScript code
</script>

Declared inside a function, it has local scope

1
2
3
4
5
6
7
numberX = 3.14;	// this will not cause an error
testFunction();

function testFunction () {
"use strict";
numberY = 3.14; // this will cause an error
}

Why Strict Mode

Strict mdoe makes it easier to write “secure” JavaScript. Strict mode changes previously accepted “bad syntax” into real errors.

Not allowed in strict mode

  • Usging a variable/object, without declaring it, is not allowed.
  • Deleting a variable/object/function is not allowed.
  • Duplicating a parameter name is not allowed.
  • Octal numberic literals and octal escape characters are not allowed.
  • Writing to read-only/get-only propery is not allowed.
  • Deleting an undeletable property is not allowed.
  • The string “eval”/“arguments” cannot be used as a variable
  • The with statment is not allowed
  • For security reasons, eval() is not allowed to create variables in the scope from which it was called

The JavaScript this Keyword

In a function definition, this refers to the “owner” of the function.

Default Binding

When used along, this refers to the Global object. In a browser the global object is [object Window].

But in strict mode, this will be undefined, because strict mode does not allow default binding.

JavaScript Style Guide and Coding Conventions

Always use the same coding conventions for all your JavaScript projects.

JavaScript Coding Conventions

Coding conventions are style guidelines for programming. They typically over:

  • Naming and declaration rules for variables and functions.
  • Rules for the use of white space, indentation, and comments.
  • Programming practices and principles.

Coding conventions secure quality:

  • Improve code readability
  • Make code maintenance easier

Coding conventions can be documented rules for teams to follow, or just be your individual coding practice.

JavaScript Best Practices

Avoid global variables, avoid new, avoid ==, avoid eval()

Avoid Global Variables

Minimize the use of global variables. This inclueds all data types, objects, and functions. Global variables and functions can be overwritten by other scripts. Use local variables instead, and learn how to use closures.

Always Declare Local Variables

All variables used in a function should be declared as local variables.

Local variables must be declared with the var/let keyword, otherwise they will become global variables.

Declarations on Top

It is good coding practice to put all declarations at the top of each script or function.

This will:

  • Give cleaner code
  • Provide a single place to look for local variables.
  • Make it easier to avoid unwanted global variables
  • Reduce the possibility of unwanted re-declarations

Initialize Variables

It is good coding practice to initialize variables when you declare them.

Never Declare Number, String, or Boolean Objects

Always treat numbers, strings, or boolean as primitive values. Not as objects.

Declare these types as objects, slow down execution speed, and produces nasty side effects.

Beware of Automatic Type Conversions

Beware that numbers can accidentally be converted to string or NaN. JavaScript is loosely typed. A variable can contain different data types, and a variable can change its data type.

Use === Comparison

The == comparison operator always convert before comparison. The === operator forces comparison of values and type.

Use Parameter Defaults

If a function is called with a missing argument, the value of the missing argument is set to undefined.

Undefined values can break your code. It is a good habit to assign default values to arguments.

End Your Switches with Defaults

Always end your switch statments with a default. Even if you think there is no need fot it.

Avoid Using eval()

The eval() function is used to run text as code. In almost all cases, it should not be necessary to use it. Because it allows arbitaray code to be run, it also represents a security problem.

JavaScript JSON

JSON is a format for storing and transporting data. JSON is often used when data is sent from a server to a web page.

  • JSON stands for JavaScript Object Notation
  • JSON is lightweight data interchange format
  • JSON is language independent *
  • JSON is “self-describing” and easy to understand

The JSON syntax is derived from JavaScript object notation syntax, but JSON format is text only. Code for reading and generating JSON data can be written in any programming language.

The JSON format is syntactically identical to the code for creating JavaScript objects. Because of this similarity, a JavaScript program can easily convert JSON data into native JavaScript objects.

JSON data is written as name/value pairs, just like JavaScript object properties. A name/value pair consists of a field name, followed by a colon, followed by a value.

JSON names require double quotes, JavaScript names do not.

Converting a JSON Text to a JavaScript Object

A common use of JSON is to read data from a web server, and display the data in web page.

For simplicity, this can be demonstrated using a string as input.

JavaScript Objects

In JavaScript, alomost “everything” is an Object.

JavaScript Primitives

A primitive value is a value that has no properties or methods. A primitive data type is data that has a primitive value.

JavaScript defines 5 types of primitive data types:

  • string
  • number
  • boolean
  • null
  • undefined

Primitive values are immutable (they are hardcoded and therefore cannot be changed)

Objects are Variables containing Variables

JavaScript variables can contain single values. Objects are variables too. But objects can contain many values. The values are written as name : pairs.

1
2
3
4
5
let objectVar = {
key: value,
key: value,
...
}

A JavaScript object is a collection of named values.

Object Properties

The named values, in JavaScript objects, are called properties.

Object properties can be both primitive values, other objects, and functions.

Object Methods

Methods are actions that can be performed on objects.

An Object method is an object property containing a function definition.

JavaScript objects are containers for named values, called properties and mthods.

Creating a JavaScript Object

1
2
3
4
5
6
7
// Create an object using literal
let objectByLiteral = {keyX: "valueX", keyY: "valueY"};

// Create an object using keyword new
let objectByNew = new Object();
objectByNew.keyX = "valueX";
objectByNew.keyY = "valueY";

JavaScript Objects are Mutable

Objects are mutable: They are addressed by reference, not by value.

JavaScript variables are not mutable. Only JavaScript objects.

JavaScript Object Properties

Properties are the most important part of any JavaScript object. Properties are the values associated with a JavaScript object. A JavaScript object is a collection of unorderd properties. Properties can usually be changed, added, and deleted, but some are read only.

Accessing JavaScript Properties

The syntax for accessing the property of an object is :

1
2
3
4
5
objectName.propertyName
// or
objectName["property"]
// or
objectName[expression] // The expression must be evaluate to a property name

JavaScript for…in Loop

The JavaScript for…in statment loops through the properties of an object.

1
2
3
for (variable in object) {
code to be executed
}

The block of code inside of the for…in loop will be executed once for each property.

Add New Properties

You can add new properties to an existing object by simply giving it a value.

You cannot use reserved words for property names. JavaScript naming rules apply.

Deleting Properties

The delete keyword deletes a property from an object.

1
2
let objByLiteral = {keyX: "valueX"};
delete objByLiteral.keyX;

The delete keyword deletes both value of the property and the property itself. After deletion, the property cannot be used before it is added back again. The delete operator is designed to be used on object properties. It has no effect on variables or functions. The delete operator should not be used on predefined JavaScript object propertis. It can crash your application.

Property Attribute

All properties hava a name. In addition they also have a value. The value is one of the property’s attributes.

Other attributes are: enumerable, configurable, and writable. These attributes define how the property can be accessed.

JavaScript Object Methods

JavaScript methods are actions that can be performed on objects. A JavaScript method is a property containing a function definition.

Methods are functions stored as object properties.

Accessing Object Methods

You access an object method with the following syntax:

1
objectName.methodName();

If you access the method property, without () , it will return the function definition.

Adding a method to an Object

1
2
3
obj.methodName = function () {
code to be executed
}

JavaScript Object Constructors

It is considered good practice to name constructor functions with an upper-case first letter.

In a constructor function this does not have a value. It is a substitute for the new object. The value of this will become the new object whent a new object is created.

Note taht this is not a variable. It is a keyword. You cannot change the value of this.

JavaScript Prototypes

All JavaScript objects inherit properties and methods from a prototype.

Prototype Inheritance

Date objects inherit from Date.prototype. Array objects inherit from Array.prototype. The Object.prototype is on the top of the prototype inheritance chain.

Using the prototype Property

The JavaScript prototype allows you to add new properties to object constructors

1
2
3
4
function Person (value) {
this.key = value;
}
Person.prototype.keyNew = "valueNes";

The JavaScript prototype proterty also allows you to add new methods to objects constructors:

1
2
3
4
5
6
function Person (value) {
this.key = value;
}
Person.prototype.methodNew = function () {
code to be executed
}

Only modify your own prototypes. Never modify the prototypes of standard JavaScript objects.

JavaScript Function Definitions

JavaScript functions are defined with the function keyword. You can use a functin declaration or a function expression.

1
2
3
function functionName (parameters) {
code to be executed
}

Declared functions are not executed immediately. They are “saved for later use”, and will be executed later, when they are invoked.

Semicolons are used to separate executable JavaScript statments. Since a function declaration is not an executable statment, it is not common to end it with a semicolon.

Function Expressions

A JavaScript function can also be defined using an expression. A function expression can be stored in a variable.

1
let expressionFun = function (parameters) { execuate code };

After a function expression has been stored in a variable, the variable can be used as a function.

Function stored in variables do not nedd function names. They are always invoked using the variable name.

The Function() Constructor

JavaScript functions are defined with the function keyword.

Functions can also be defined with a built-in JavaScript function consturctor called Function() .

1
let newFunction = new Function("parameterX", "parameterY", ... " execuated code");

You actually don’t have to use the function constructor. Most of time, you can avoid using the new keyword in JavaScript.

Function Hoisting

Hoisting is JavaScript’s default behavior of moving declarations to the top of the current scopt.

Hoisting applies to variable declarations and to function declarations.

Because of this, JavaScript functions can be called before the declared.

Self-Invoking Functions

Function expression can be made “self-invoking”. A “self-invoking” expression is invoked automatically, without being called. Function expression will execute automatically if the expression is followed by ().

You cannot self-invoke a function declaration.

You have to add parentheses around the function to indicate that it is a function expression.

1
2
3
( function () {
executed code
}) ();

Functions Can Be Used as Values

JavaScript functions can be used as values.

1
2
3
4
5
function parameterFun (parameters) {
executed code
}

let result = parameterFun(parameters);

Functions are Objects

The typeof operator in JavaScirpt returns “function” for functions. But, JavaScript functions can best be descripted as objects. JavaScript functions have both properties and methods.

JavaScript Function Parameters

A JavaScript function does not perform any checking on parameter values. Function parameters are the names listed in the function definition. Function arguments are the real values passed to the function.

JavaScript function do not check the number of arguments received.

Parameter Defaults

If a function is called with missing arguments, the missing value are set to undefined . But sometimes it is better to assign a default value to the parameter.

1
2
3
4
5
function arguementsMissingFun (x, y) {
if ( x === undefined ) {
x = 0;
}
}

If a function is called with too many arguments, these arguments can be reached using the arguments object.

The Arguments Object

JavaScirpt functions have a built-in object called the arguments object. The argument object contains an array of the arguments used when the function was called. This way you can simply use a function to find the highest value in a list of numbers.

Arguments are Passed by Value

The parameters, in a function call, are the function’s arguments. JavaScript arguments are passed by value: The function only gets to know the values, not arguments’s locations. If a function changes an argument’s value, it does not change the parameter’s original value. That is, changes to arguments are not visible outside the function.

Objects are Passed by Reference

In JavaScript, object references are values. Because of this, objects will behave like they are passed by reference: If a function changes an object property, it changes the original value. That is, changes to object properties are visible outside the function.

JavaScript Function Invocation

The code inside a function is not executed when the function is defined. The code inside a function is executed when the function is invoked. It is common to use the term “call a function” instead of “invoke a function”. It is also common to say “call upon a function”, “start a function”, or “execute a function”.

1
2
3
4
5
6
function invokeFunction (numX, numY) {
return numX * numY;
}

// Invoking a Function as a Function
invokeFunction(1, 3);

The function above does not belong to any object. But in JavaScript there is always a default global object. In HTML the default object is the HTML page itself, so the function above “belongs” to the HTML page. In a browser the page object is the browser window. The function above automatically becomes a window function.

This is a common way to invoke a JavaScript function, but not a very good practice. Global variables, methods, or function can easily create name conflicts and bugs in the global object.

The this Keyword

In JavaScript, the thing called this, is the object that “owns” the current code. The value of this, when used in a function, is the object that “owns” the function.

Note that this is not a variable. It is a keyword. You cannot change the value of this.

The Global Object

When a function is called without an owner object, the value of this becomes the global object.

In a web browser the global object is the browser window.

Invoking a function as a global function, causes the value of this to be the global object. Using the window object as a variable can easily crash your program.

Invoking a Function as a Method

In JavaScript you can define functions as object methods.

1
2
3
4
5
6
7
8
let aObject = {
propertyX: "valueX",
methodY: function () {
// some execute code
}
}

aObject.methodY();

Invoking a Function with a Function Constructor

If a function invocation is preceded with the new keyword, it is constructor invocation.

1
2
3
4
5
6
7
8
// This is a function constructor
function aFun (argX, argY) {
this.propertyX = argX;
this.propertyY = argY;
}

// This create a new object
let result = new aFun("valueX", "valueY");

A counstructor invocation creates a new object. The new object inherits the properties and methods from its constructor.

The this keyword in the constructor does not have a value. The value of this will be the new object created when the function is invoked.

JavaScript Function Call

With the call() method, you can write a method that can be used on different objects.

In JavaScript all functions are object methods. If a function is not a method of a JavaScript object, it is a function of the global object.

The JavaScript call() Method

The call() method is a predefined JavaScript method. It can be used to invoke a method with an owner object as argument.

With call(), an object can use a method belonging to another object.

JavaScript Function Apply

With the apply() method, you can write a method that can be used on different objects. The apply() method is similar to the call() method.

The difference Between call() and apply() :

  • The call() method takes arguments separately.
  • The apply() method takes arguments as an array.

The apply() method is very handy if you want to use an array instead of an argument list.

The apply() method accepts arguments in an array.

JavaScript Function Closures

Global variables live as long as your application lives. Local variables have shourt lives. They are created when the function is invoked, and deleted when the function is finished.

All functions have access to the global scope. In fact, in JavaScript, all functions have access to the scope “above” them. JavaScript supports nested functions. Nested functions have access to the scope “above” them.

JavaScript closures make it possible for a function to have “private” variables.

A closures is a function having access to the parent scope, even after the parent function has closed.

JavaScript HTML DOM

With the HTML DOM, JavaScript can access and change all the elements of an HTML document.

When a web page is loaded, the browser creates a Document Object Model of the page. The HTML DOM model is constructed as a tree of Objects.

With the object model, JavaScript gets all the power it needs to create dynamic HTML :

  • JavaScript can change all the HTML elements in the page
  • JavaScript can change all the HTML attributes in the page
  • JavaScript can change all the CSS styles in the page
  • JavaScript can remove existing HTML elements and attributes
  • JavaScript can add new HTML elements and attributes
  • JavaScript can react to all existing HTML events in the page
  • JavaScript can create new HTML events in the page

The DOM is W3C standard and it defines a standard for accessing documents:

The W3C Document Object Model is a platform and language-neutral interface that allows programs and scripts to dynamically access and update the content, structure, and style of a document.

The W3C DOM standard is separated into 3 different parts:

  • Core DOM - standard model for all document types
  • XML DOM - standard model for XML documents
  • HTML DOM - standard model for HTML documents

The HTML DOM is a standard object model and programming interface for HTML. It defines :

  • The HTML elements as objects
  • The properties of all HTML elements
  • The mehtods to access all HTML elements
  • The events for all HTML elements

In other words: The HTML DOM is a standard for how to get, change, add, or delete HTML elements.

JavaScript HTML DOM Methods

HTML DOM methods are you can perform on HTML elements. HTML DOM properties are values of HTML elements that you can set or change.

The HTML DOM can be accessed with JavaScript. In the DOM, all HTML elements are defined as objects.

The getElementById() method

The most common way to access an HTML element is to use the id of the element.

1
document.getElementById("demo").innerHTML = "Hello DOM!";

The innerHTML Property

The easiest way to get the content of an element is by using the innerHTML property.

The innerHTML property can be used to get or change any HTML element, including and .

JavaScript HTML DOM Document

The HTML DOM document object is the owner of all other objects in your web page.

Finding HTML Elements

Method Description
document.getElementById(id) Find an element by element id
document.getElementsByTagName(name) Find elements by tag name
document.getElementsByClassName(name) Find elements by class name

Changing HTML Elements

Method Description
element.innerHTML = new html content Change the inner HTML of an element
element.attribute = new value Change the attribute value of an HTML element
element.setAttribute(attribute, value) Change the attribute value of an HTML element
element.style.property = new style Change the style of an HTML element

Adding and Deleting Elements

Method Description
document.createElement(element) Create an HTML element
document.removeChild(element) Remove an HTML element
document.appendChild(element) Add an HTML element
document.replaceChild(element) Replace an HTML element
document.write(text) Write into the HTML output stream

Adding Events Handlers

Method Description
document.getElementById(id).onclick() = function () {code} Add event handler code to an onclick event

Finding HTML Objects

Property Description
document.anchors Return all elements that have a name attribute
document.applets Return all elements (Deprecated in HTML 5)
document.baseURI Return the absolute base URI of the document
document.body Return the element
document.cookie Return the document’s cookie
document.doctype Return the document’s doctype
document.documnetElement Return the element
document.documentMode Return the mode used by the browser
document.documentURL Return the URI of the document
document.domain Return the domain name of the document server
document.domConfig Obsolete. Return the DOM configuration
document.embeds Return all elements
document.forms Return all
elements
document.head Return the element
document.images Return all elements
document.implementation Return the DOM implementation
document.inputEncoding Return the document’s encoding
document.lastModified Return the data and time the document was updated
document.links Return all and elements that have a href attribute
document.readyState Return the status of the document
document.referrer Return the URI of the referrer (the linking document)
document.scripts Return all