# 编译过程

  1. 词法分析
  2. 语法分析
  3. 代码生成

# 理解作用域

  • 引擎:从头到尾负责整个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

在严格模式下,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

# 闭包

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

# 模块

# 模块模式具备的两个条件:

  1. 必须有外部的封闭函数,该函数至少被调用一次(每调用一次都会生成一个新的模块实例);
  2. 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域下形成闭包,并且可以访问或者修改私有的状态。
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

# 关键字

  • 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

# this

this既不指向函数自身也不指向函数的词法作用域。

this是在运行时绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。

# 绑定规则

  1. 默认绑定

    • 在严格模式下绑定到undefined,否则绑定到全局对象
    function foo(){
        console.log(this.name) ;
    }
    var name = 'KO';
    foo(); // KO , 在非严格模式下,this绑定到全局对象,既window
    
    1
    2
    3
    4
    5
  2. 隐式绑定

    • 由上下文调用,则绑定到那个上下文对象
    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
  3. 显示绑定

    • 由call或者apply(或者bind)调用,则绑定到指定的对象
    function foo(){
        console.log(this.name);
    }
    var obj = {
        name: '张三'
    }
    foo.call(obj); // 张三
    
    1
    2
    3
    4
    5
    6
    7
  4. new绑定

    • 绑定到新创建的对象
    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
  • Object.defineProperty(...)

    • 把configurable修改成false是单向操作,无法撤销!

# 类型和语法

# 类型

  • 对变量执行typeof操作时,得到的结果并不是该变量的类型,而是该变量持有的值的类型;
  • typeof运算符总是返回一个字符串

# 数组

  • 使用delete将单元从数组中删除,length属性不会发生变化;

# 数字

  • 一些常见的方法
    1. toFixed:指定小数部分的显示位数
    2. toPercision:指定有效数位的显示位数
最后更新: 12/29/2020, 10:33:32 AM