《你不知道的JS(上)》学习笔记 第一部分

内建类型

  • string
  • nubmer
  • boolean
  • undefined
  • object (包括null)
  • symbol (ES6+)

类型转换

Truthy&Falsy

  • Boolean(NaN) === false
  • Boolean([]) === true
  • Boolean({}) === true

等价

1
2
3
4
5
6
7
8
9
var a = [1,2,3]
var b = [1,2,3]
var c = "1,2,3"

a == c // true
b == c // true
a == b // false

[1,2,3].toString() // "1,2,3"

不等价

1
2
3
4
5
6
7
8
var a = 42
var b = "foo"

a < b // false
a > b // false
a == b // false

Number("foo") // NaN

NaN既不大于其他值,也不小于其他值

变量

标识符必须以a-z,A-Z,$,或_开头。它可以包含任意这些字符外加数字0-9。

Strict模式

使代码符合一组更安全和更合理的指导方针,代码对引擎有更强的可优化性。

立即调用表达式IIFE

1
2
3
4
(function IIFE(){
console.log( "Hello!" );
})();
// "Hello!"

闭包

1
2
3
4
5
6
7
8
function makeAdder(x) {
function add(y) {
return y + x;
};
return add;
}
var plusOne = makeAdder( 1 );
plusOne( 3 ); // 4 <-- 1 + 3

模块

模块让你定义对外面世界不可见的私有实现细节(变量,函数),和对外面可访问的公有API。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function User() {
var username;
var password;
function doLogin(user,pw) {
username = user;
password = pw;
}
var publicAPI = {
login: doLogin
};
return publicAPI;
}
// 创建实例
var fred = User();
fred.login('jack', '123');

this标识符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function foo() {
console.log( this.bar );
}
var bar = "global";
var obj1 = {
bar: "obj1",
foo: foo
};
var obj2 = {
bar: "obj2"
};

foo(); // "global"
obj1.foo(); // "obj1"
foo.call( obj2 ); // "obj2"
new foo(); // undefined
  • foo()最终在非strict模式中将this设置为全局对象;在strict模式中this将会是undefined
  • obj1.foo()this设置为对象obj1
  • foo.call(obj2)this设置为对象obj2
  • new foo()this设置为一个新的空对象

原型

1
2
3
4
5
6
7
8
var foo = {
a: 42
};
// 创建 `bar` 并将它链接到 `foo`
var bar = Object.create( foo );
bar.b = "hello world";
bar.b; // "hello world"
bar.a; // 42 <-- 委托到 `foo`

作用域

LHS查询 var a
RHS查询 a = 2

欺骗

  1. eval

    1
    2
    3
    4
    5
    6
    function foo(str, a) {
    eval( str );
    console.log( a, b );
    }
    var b = 2;
    foo( "var b = 3;", 1 ); // 1 3
  2. with

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function foo(obj) {
    with (obj) {
    a = 2; // o2时,隐式 var a
    }
    }
    var o1 = { a: 3 };
    var o2 = { b: 3 };

    foo( o1 );
    console.log( o1.a ); // 2
    foo( o2 );
    console.log( o2.a ); // undefined
    console.log( a ); // 2 泄漏到了全局作用域

性能

evalwith都不建议使用,且受严格模式限制。引擎不会优化evalwith,使用多会卡。

函数与块级作用域

1
2
3
4
5
6
7
8
9
function doSomething(a) {
function doSomethingElse(a) {
return a - 1;
}
var b;
b = a + doSomethingElse( a * 2 );
console.log( b * 3 );
}
doSomething( 2 ); // 15

bdoSomethingElse(..) 对任何外界影响都是不可访问的,而是仅仅由 doSomething(..) 控制。它的功能和最终结果不受影响,但是这种设计将私有细节保持为私有的,这通常被认为是好的软件。

命名空间

1
2
3
4
5
var MyReallyCoolLibrary = {
awesome: "stuff",
doSomething: function() {},
doAnotherThing: function() {}
};

匿名函数命名

1
2
3
4
5
setTimeout( function timeoutHandler(){
console.log( "I waited 1 second!" );
}, 1000 );
// ES6 箭头函数
var timeoutHandler = () => console.log( "I waited 1 second!" );

IIFE

1
2
3
4
5
6
7
var a = 2;
(function IIFE( global ){
var a = 3;
console.log( a ); // 3
console.log( global.a ); // 2
})( window );
console.log( a ); // 2

UMD模块

1
2
3
4
5
6
7
8
var a = 2;
(function IIFE( def ){
def( window );
})(function def( global ){
var a = 3;
console.log( a ); // 3
console.log( global.a ); // 2
});

def 函数表达式在这个代码段的后半部分被定义,然后作为一个参数(也叫 def)被传递给在代码段前半部分定义的 IIFE 函数。最后,参数 def (函数)被调用,并将 window 作为 global 参数传入。

块级作用域

  • With
  • try/catch

    1
    2
    3
    4
    try { throw 2 } catch(a) {
    console.log( a ); // 2
    }
    console.log( a ); // ReferenceError
  • let

  • const

垃圾回收

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// bad
function process(data) { .. }
var someReallyBigData = { .. };
process( someReallyBigData );
var btn = document.getElementById( "my_button" );
btn.addEventListener( "click", function click(evt){
console.log("button clicked");
}, /*capturingPhase=*/false );

// 优化
function process(data) { ..}
// 运行过后,任何定义在这个块中的东西都可以消失了
{
let someReallyBigData = { .. };
process( someReallyBigData );
}
var btn = document.getElementById( "my_button" );
btn.addEventListener( "click", function click(evt){
console.log("button clicked");
}, /*capturingPhase=*/false );

提升

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 函数优先
foo(); // 1
var foo; // var 被抛弃
function foo() {
console.log( 1 );
}
foo = function() {
console.log( 2 );
};

// 函数声明覆盖
foo(); // 3
function foo() {
console.log( 1 );
}
var foo = function() {
console.log( 2 );
};
function foo() {
console.log( 3 );
}

闭包

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
// 错误 1
for (var i=1; i<=5; i++) {
setTimeout( function timer(){
console.log( i ); // 打印6个6
}, i*1000 );
}
// 错误 2
for (var i=1; i<=5; i++) {
(function(){
setTimeout( function timer(){
console.log( i );
}, i*1000 );
})();
}
// 正确
for (var i=1; i<=5; i++) {
(function(){
var j = i; // IIFE作用域内部变量
setTimeout( function timer(){
console.log( j );
}, j*1000 );
})();
}
// 优雅
for (var i=1; i<=5; i++) {
(function(j){
setTimeout( function timer(){
console.log( j );
}, j*1000 );
})( i ); // 传参方式
}
// ES6
for (let i=1; i<=5; i++) {
setTimeout( function timer(){
console.log( i );
}, i*1000 );
}

基于闭包的模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
};
}
var foo = CoolModule();
foo.doSomething(); // cool
foo.doAnother(); // 1 ! 2 ! 3