# 编译过程
- 词法分析
- 语法分析
- 代码生成
# 理解作用域
- 引擎:从头到尾负责整个JavaScript程序的编译及执行过程。
- 编译器:负责语法分析及代码生成。
- 作用域:负责收集和维护由所有声明的变量组成的一系列查询,并确定当前执行代码对当前操作变量的访问权限(作用域是一套规则,用于确定在何处以及如何查找变量)。
# LHS,RHS
- LHS: 赋值操作的目标是谁(查找变量的容器本身)
- RHS: 谁是赋值操作的源头(查找某个变量的值)
非严格模式下,LHS查询在全局作用域未查找到目标变量时,会在全局作用域创建一个目标变量。
如果查找的目的是赋值,则使用LHS查询;如果目的是获取变量的值,则使用RHS查询。
# 异常结果的判别
- ReferenceError:作用域判断失败相关
- TypeError:作用域判别成功了,但是对结果的操作不合理
# 欺骗词法作用域的语法
- eval(...)用于执行被动态创建的代码
function foo(str, a){
eval(str); // 此处会遮蔽全局作用域中的b
console.log(a, b); // 1, 3
}
var b = 0;
foo('var b=3;', 1);
1
2
3
4
5
6
2
3
4
5
6
在严格模式下,eval有自己的词法作用域,其中声明的变量无法修改所在的作用域。
- with关键字
# 声明提升
- 函数优先:函数声明提升会优先于变量声明提升
foo(); // 2
var foo ;
function foo(){
console.log(2);
}
foo = function(){
console.log(3);
}
// 以上代码片段会被引擎理解为:
function foo(){
console.log(2);
}
var foo;
foo(); // 2
foo = function(){
console.log(3)
}
// -----------------------------------------------------------------------------------
// 后面的函数声明会覆盖前面的
foo(); // 3
function foo(){
console.log(1)
}
var foo = function(){
console.log(2);
}
function foo(){
console.log(3);
}
// 以上代码片段会被引擎理解为:
function foo(){
console.log(1)
}
function foo(){
console.log(3);
}
var foo = function(){
console.log(2);
}
foo();
// ------------------------------------------------------------------------------------
// 块级作用域
foo(); // TypeError: foo in not a function (非严格模式下,LHS查询在全局作用域未查找到目标变量时,会在全局作用域创建一个目标变量。)
var flag = true;
if( flag){
function foo(){ // 此处的函数声明不会提升到全局作用域
console.log('true');
}
}else {
function foo(){
console.log('false');
}
}
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
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
# 闭包
1. 无论使用何种方式对函数类型的值进行传递,当函数在别处被调用时都可以观察到闭包
2. 无论通过何种手段将内部函数作用域传递到所在的词法作用域以外,它都会持有对原始作用域的引用,无论在何处执行这个引用都会使用到闭包。
function foo(){
var a = 2;
function baz(){
console.log(a);
}
bar(baz);
}
function bar(fn){
fn(); // 这就是闭包
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 模块
# 模块模式具备的两个条件:
- 必须有外部的封闭函数,该函数至少被调用一次(每调用一次都会生成一个新的模块实例);
- 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域下形成闭包,并且可以访问或者修改私有的状态。
function coolModule(){
var something = 'cool';
var another = [1,2,3];
function doSomething(){
console.log(something);
}
function doAnother(){
console.log(another.join('~'));
}
return { // 返回至少一个内部函数
doSomething: doSomething,
doAnother: doAnother
}
// 只有数据属性没有闭包函数的对象不是真正的模块
// return {
// something: something
// }
}
var foo = coolModule(); // 创建一个新的模块实例
foo.doSomething(); // 看吧,这就是闭包,拥有对coolModule内部作用域的引用
foo.doAnother();
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
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
# 关键字
- import:可以将一个模块中的一个或多个API导入到当前作用域中,并分别绑定到一个变量上;
- module:将整个模块的API导入并绑定到一个变量上;
- export:将当前模块的一个标识符(变量或函数)导出为公共API。
// bar.js
function hello (who){
console.log('hello'+who+'!');
}
export hello;
// foo.js
import bar from 'bar.js'
var name = 'goodbye';
function sayGoodbye(){
bar.hello(name).split('-');
}
export sayGoodbye
// baz.js
module bar from 'bar.js'
module foo from 'foo.js'
foo.sayGoodbye()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# this
this既不指向函数自身也不指向函数的词法作用域。
this是在运行时绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。
# 绑定规则
默认绑定
- 在严格模式下绑定到undefined,否则绑定到全局对象
function foo(){ console.log(this.name) ; } var name = 'KO'; foo(); // KO , 在非严格模式下,this绑定到全局对象,既window
1
2
3
4
5隐式绑定
- 由上下文调用,则绑定到那个上下文对象
function foo(){ console.log(this.name); } var obj = { name: 'obj1', foo: foo } obj.foo(); // obj1,上下文对象为obj,则this指向这个obj
1
2
3
4
5
6
7
8显示绑定
- 由call或者apply(或者bind)调用,则绑定到指定的对象
function foo(){ console.log(this.name); } var obj = { name: '张三' } foo.call(obj); // 张三
1
2
3
4
5
6
7new绑定
- 绑定到新创建的对象
function foo(name){ this.name = name ; } var bar = new foo('张三'); console.log(bar.name); // 张三
1
2
3
4
5
# 对象
# 对象的定义形式
- 声明形式
- 构造形式
# 类型
- string、number、boolean、null、undefined、object
# 内置对象
- String、Number、Boolean、Object、Function、Array、Date、RegExp、Error
# 属性描述符
- Object.getOwnPropertyDescriptor(...)
var myObject = {
a:2
}
console.log(Object.getOwnPropertyDescriptor(myObject, 'a'));
// {
// value: 2,
// writable: true,
// enumerable: true,
// configurable: true
// }
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
Object.defineProperty(...)
- 把configurable修改成false是单向操作,无法撤销!
# 类型和语法
# 类型
- 对变量执行typeof操作时,得到的结果并不是该变量的类型,而是该变量持有的值的类型;
- typeof运算符总是返回一个字符串;
# 数组
- 使用delete将单元从数组中删除,length属性不会发生变化;
# 数字
- 一些常见的方法
- toFixed:指定小数部分的显示位数
- toPercision:指定有效数位的显示位数
JS设计模式 →