你不知道的javascript上卷(笔记一)

let声明

  • 在同一个函数或者同一个作用域中重复用let定义一个变量会引起TypeError,而var不会

    1
    2
    3
    4
    if (true) {
    let a = 1;
    let a = 2; // Identifier 'a' has already been declared
    }
  • 在使用let声明的变量不会在块作用域中进行提升(MDN上的解释:let的暂存死区与错误),而var声明的变量会提升

    1
    2
    3
    4
    5
    function foo() {
    console.log(a);
    let a = 2;
    }
    foo(); // a is not defined, 如果使用var声明,变量a会得到提升,结果输出undefined
  • 使用let声明的变量会产生块级作用域

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 用var声明
    for (var i = 0; i < 5; i++) {
    console.log(i); // 输出0,1,2,3,4
    }
    console.log("循环结束后的i值为 " + i); // 输出5
    // 用let声明
    for (let i = 0; i < 5; i++) {
    console.log(i); // 输出0,1,2,3,4
    }
    console.log("循环结束后的i值为 " + i); // 抛出ReferenceError: i is not defined,因为let声明的变量产生了块级作用域,所以在for循环外部无法访问

const声明

  • const声明创建一个只读的常量,拥有块级作用域
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    if (true) {
    var a = 2;
    const MY_NUM = 3; // 在if作用域内的常量
    a = 4; // a被重新赋值为4
    MY_NUM = 6; // 抛出错误,通过const声明的变量MY_NUM只能被赋值1次
    }
    console.log(a); // 4
    console.log(MY_NUM); // 抛出错误, MY_NUM只在if的块级作用域内

模块

  • 模块模式的必备条件
    • 必须有外部的封闭函数,该函数必须至少被调用一次(每次调用都会创建一个新的模块实例)
    • 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或者修改私有的状态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 模块模式
function coolModule() {
var name = "whiskey";
var hobby = ["running", "swimming", "reading"];
function sayName() {
console.log(name);
}
function sayHobby() {
console.log(hobby.join("!"));
}
return {
sayName: sayName,
sayHobby: sayHobby
}
}
var foo = coolModule();
foo.sayName(); // "whiskey"
foo.sayHobby(); // "running-swimming-reading"

单例模式

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
// 命名将作为公共API返回的对象
var foo = (function coolModule(id) {
function change() {
// 修改公共API
publicAPI.identify = identify2;
}
function identify1() {
console.log(id);
}
function identify2() {
console.log(id.toUpperCase());
}
var publicAPI = {
change: change,
identify: identify1
};
return publicAPI;
})("whiskey");
foo.identify(); // "whiskey"
foo.change(); // 将publicAPI的identify属性指向了identify2
foo.identify(); // "WHISKEY"

现代的模块机制△△

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
var myModules = (function Manager() {
var modules = {};
function define(name, deps, impl) {
for (var i = 0; i < deps.length; i++) {
deps[i] = modules[deps[i]];
}
// 为模块的定义引入了包装函数,并且将返回值储存在一个根据名字来管理的模块列表中
modules[name] = impl.apply(impl, deps);
}
function get(name) {
return modules[name];
}
return {
define: define,
get: get
};
})();
myModules.define("bar", [], function() {
function hello(who) {
return "This is: " + who;
}
return {
hello: hello
};
});
myModules.define("foo", ["bar"], function(bar) {
var baby = "whiskey";
function awesome() {
console.log(bar.hello(baby).toUpperCase());
}
return {
awesome: awesome
};
});
var bar = myModules.get("bar");
var foo = myModules.get("foo");
console.log(bar.hello("lily")); // "This is: lily"
foo.awesome(); // "THIS IS: WHISKEY"

this词法

  • 作为对象的方法调用this指向该对象,作为普通函数调用this指向全局对象,通过call()apply()可以改变this的指向

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // 全局变量name
    var name = "window";
    var obj = {
    name: "whiskey",
    getName: function() {
    return function() {
    return this.name;
    };
    }
    };
    var fn = obj.getName();
    console.log(fn); // 返回内层的匿名函数
    console.log(fn()); // window,作为普通函数调用this指向全局
    console.log(fn.call(obj)); //whiskey 通过call()绑定obj改变this指向
  • 用变量that储存this

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // 全局变量name
    var name = "window";
    var obj = {
    name: "whiskey",
    getName: function() {
    // 把this对象赋值给一个that变量
    var that = this;
    return function() {
    return that.name;
    };
    }
    };
    var fn = obj.getName();
    console.log(fn()); // whiskey,函数返回后that仍然引用着obj
  • ES6的箭头函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    //全局变量name
    var name = "window";
    var obj = {
    name: "whiskey",
    getName: function() {
    // ES6的箭头函数,放弃了所有普通函数this绑定的规则,取而代之的是用当前词法作用域覆盖了this本来的值
    return () => {
    return this.name;
    };
    }
    };
    var fn = obj.getName();
    console.log(fn()); // whiskey,箭头函数继承了getName()函数的this绑定
  • bind()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    var name = "window";
    var obj = {
    name: "whiskey",
    getName: function() {
    return function() {
    return this.name;
    }.bind(this);
    }
    };
    var fn = obj.getName();
    console.log(fn()); //whiskey 运用了bind()