老司机1小时快速入门 javascript

老司机1小时快速入门系列

作为一个老程序员,当然不能像新人学一门语言一样,从变量、类型、语法等等一点一点学,这太慢了。老程序员经常需要学习各种新的语言,如何快速入手?

我们按以下三步曲:

  • 首先,拿到一门新语言,来一段长点的示例程序,这段程序里包括了各种常见的语法和用法。
  • 然后,分析它的几个关键的核心概念,把这几个核心概念搞清楚,可以让你遇事不慌且不误入歧途!
  • 最后,花10分钟回顾一下上面的示例程序,对于一个老手来说,就已经可以开始动手写可用的程序了。

示例程序

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
console.log('Hello, This is 1 hour javascript tutorial.');
console.log('You can run this script by nodejs.');

// Javascript support only 5 types. float, string, boolean, list, object(map).
// NO interger and char.
var a = 1.0;
var b = 'hello string';
var c = true;
var d = [1,2,3];
var e = {a:a, ok:'yes', v:88};
console.log(e.a);
console.log(e['ok']);


// type transform
var aa = a.toString(); // a = 1.0, but aa == "1", it mean that javascript don't have integer type
var ab = parseInt('688.33 abc');
var ac = '23' - 0;
var ad = !!aa; // transfer to boolean
console.log(aa);
console.log(ab);
console.log(ac);


// operator
console.log((null == undefined).toString() + " | " + (null === undefined).toString());
console.log('toString' in e);
console.log(e instanceof Object);
console.log(delete e.v);
console.log(e);
for (dd in d) {
console.log(dd + ": " + d[dd]);
}
for (ee in e) {
console.log(ee + ": " + e[ee]);
}


// statement
if (parseInt(Math.random() * 10) % 2 == 1) {
console.log('IS 1');
} else {
console.log('NOT 1');
}
while(a < 5) {
a += 1;
if (a>=3) {
continue;
}
console.log(a);
}
switch(a) {
case 4:
console.log('a is 4');
break;
case 5:
console.log('a is 5');
break;
}


// 函数则是在实际运行之前,当Js代码被解析时定义的
console.log(f(4)); //显示16,函数在解析时已经定义了
var f=0; //重新覆盖了f的定义function
function f(x) { return x*x; }
console.log(f); //显示0

// js local scope didn't very intuition
(function () {
console.log(local_var); // local_var has defined here, but not init.
var local_var = 3;
console.log(local_var);
})();


// list
var d = [4,5,67,1,2,3];
console.log(d.join('|'));
console.log(d.reverse().join('|'));
console.log(d.sort().join('|'));
d.push('last item');
console.log(d.join('|'));
d.pop();
console.log(d.join('|'));
d.shift('first item');
console.log(d.join('|'));
d.unshift();
console.log(d.join('|'));
console.log(d.splice(2,2));


// function
function function_arguments() {
console.log(arguments); // show varity paramers
}
function_arguments(4,5,6,'abc'); // this point global object(window object in web browser)
function_arguments.call(d, 8, 9); // this point to d
function_arguments.apply(d, [1,2,3]);


// exception
try {
console.log(asdfasdfasdfwe.oaksdfasd); // no this symbol, will throw ReferenceError
} catch(e) {
console.log(e);
}

try {
throw "exception abcdefg";
} catch(e) {
console.log(e);
} finally {
console.log('finally');
}


// class
function Cache(name) {
this.name = name; // object property
}
Cache.prototype.show = function() { // object function
console.log(this); // this point to object
console.log(this.name + ' show');
}
Cache.Unit = new Cache('Unit'); // class property
Cache.Say = function() { // class function
console.log(this); // this point to construct function
console.log('Say()');
}

var a = new Cache('Jack');
console.log(Cache.constructor); // Cache is a construct function, so it's construactor is Function
console.log(a.constructor); // object's constructor property point construct function
console.log(a.name);
a.show();
console.log(Cache.Unit);
Cache.Say();


// inhert
function NewCache(name) {
Cache.call(this, name);
}
NewCache.prototype = new Cache();
NewCache.prototype.constructor = NewCache; // fix prototype.constructor because has been overwrite by privious statement
NewCache.prototype.additionShow = function() {
console.log('addition show()');
}
var nc1 = new NewCache('new cache1');
var nc2 = new NewCache('new cache2');
console.log(nc1.name);
console.log(nc2.name);
nc1.additionShow();

输出如下:

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
Hello, This is 1 hour javascript tutorial.
You can run this script by nodejs.
1
yes
1
688
23
true | false
true
true
true
{ a: 1, ok: 'yes' }
0: 1
1: 2
2: 3
a: 1
ok: yes
NOT 1
2
a is 5
16
0
undefined
3
4|5|67|1|2|3
3|2|1|67|5|4
1|2|3|4|5|67
1|2|3|4|5|67|last item
1|2|3|4|5|67
2|3|4|5|67
2|3|4|5|67
[ 4, 5 ]
{ '0': 4, '1': 5, '2': 6, '3': 'abc' }
{ '0': 8, '1': 9 }
{ '0': 1, '1': 2, '2': 3 }
ReferenceError: asdfasdfasdfwe is not defined
at Object.<anonymous> (/Users/chuanqi/github/self/javascript-study/1hour/1hour.js:103:17)
at Module._compile (module.js:541:32)
at Object.Module._extensions..js (module.js:550:10)
at Module.load (module.js:456:32)
at tryModuleLoad (module.js:415:12)
at Function.Module._load (module.js:407:3)
at Function.Module.runMain (module.js:575:10)
at startup (node.js:160:18)
at node.js:445:3
exception abcdefg
finally
[Function: Function]
{ [Function: Cache] Unit: Cache { name: 'Unit' }, Say: [Function] }
Jack
Cache { name: 'Jack' }
Jack show
Cache { name: 'Unit' }
{ [Function: Cache] Unit: Cache { name: 'Unit' }, Say: [Function] }
Say()
new cache1
new cache2
addition show()

核心概念

作用域链

作用域链其实就是直观的作用域查找的过程,这点与其它的主流语言类似。先查找局部作用域,再查找再高一级的作用域,直到全局作用域。
每个符号都有一个自己的作用域链,在符号所拥有的作用域链上的其它所有符号都不会被GC回收,这也是javascript的闭包的实现机制。

原型链(prototype chain)

每个由constructor function创建的object拥有一个指向construtor function 的 prototype 属性值的 隐式引用(implicit reference, ES6已经规范化为 __proto__),这个引用称之为 原型(prototype)。进一步,每个原型可以拥有指向自己原型的隐式引用(即该原型的原型),如此下去,这就是所谓的 原型链(prototype chain)。

理解闭包

最通俗的理解就是:”闭包就是定义在一个函数内部的子函数,为了在该函数的外部读取它的内部变量“。

内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后。 因为,函数的作用域链在定义时就已经确定了,而不是在运行时确定。

  • 如何从函数外部访问函数内部的变量,只有通过函数内部的子函数
1
2
3
4
5
6
7
8
function f1() {
var v = 99;
return function () {
return v;
};
}
get = f1();
console.log(get());

上面代码输出了函数内部变量的值99,f1函数已经返回,理论上f1生命周期已经终结了,但是通过闭包依然可以访问它的内部变量的值。说明f1的内部变量v并没有被GC立刻回收,因为get这个变量所对应的作用域链上包括了v。

  • 因为 函数 是 JavaScript 中唯一拥有自身作用域的结构,因此闭包的创建依赖于函数。

比如javascript中对象其实就是一个类似map的东西,当然没有public, private, protected之类的访问控制符了。不过,可以通过闭包的技巧实现私有变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
function Foo() {
var obj = {};
var private = 2;

obj.setPrivate = function(value) {
private = value; // 只能通过这个函数修改private值,就可以加以控制了。
}

obj.getPrivate = function() {
return private;
}
return obj;
}

其它概念

作用域

JavaScript 看起来支持一对花括号创建的代码段,但是并不支持 块级作用域; 而仅仅支持 函数作用域。ES6中的let, const关键字才真正支持块级作用域。而且JavaScript 会提升变量声明。这意味着 var 表达式和 function 声明都将会被提升到当前作用域的顶部。
Javascript中可以用匿名包装器来模拟命名空间, (function () {...})();

new A 到底发生了什么

1
var a = new A('hehe')

解析引擎内部隐式地执行了以下代码:

1
2
3
var a = new Object();
a.__proto__ = A.prototype;
A.call(a, 'hehe'); // implicit return this, current this point a

NULL和undefined

基本上这两者完全等价,只有一个细微的区别

  • null表示”没有对象”,即该处不应该有值,是预料之中不该有值的。
    • 典型用于原型链的终点,Object.getPrototypeOf(Object.prototype) === null
  • undefined表示”缺少值”,就是此处应该有一个值,此处缺少值是个意外。
    • 绝大部分情况下用undefined。

回顾

回过头去看一遍 示例程序 吧,老司机,相信你已经入门了javascript了。

参考

  1. Javascript秘密花园: http://bonsaiden.github.io/JavaScript-Garden/zh/
0%