1 精华

  • 函数
  • 弱类型
  • 动态对象
  • 对象字面量:JSON的灵感来源
  • 原型继承:争议
  • 基于全局变量的编程模型

JS 主要是基于词法作用域(lexical scoping)的顶级对象。
JS 是第一个成为主流的 Lambda 语言,与 Lisp、Scheme 有很多的共同点

Lambda :是一套用于研究函数定义、函数应用和递归的形式系统,对函数式编程有巨大的影响
Lisp:一种基于 λ 演算的函数式编程语言
Scheme:一种多范型的编程语言,是两种 Lisp 主要的方言之一

定义新的方法:

  1. Function.prototype.method = function(name, func) {
  2. this.prototype[name] = func;
  3. return this;
  4. }

2 语法

2.1 数字 number

  • 64位浮点数,没有分离出整数类型
  • NaN:isNaN(number)
  • Math.floor(number)

2.2 字符串 String

  • 16位
  • \u :指定数字字符编码
  • str.length

2.3 语句

  • 条件语句: if 和 switch
  • 循环语句: while、for、do
  • 强制跳转语句:break、return、throw
  • 函数调用

2.3.1 假(falsy)

  • false
  • null
  • undefined
  • 空字符串 ‘ ‘
  • 数字 0
  • 数字 NaN

2.3.2 for in 语句

会枚举一个对象的所有属性名,通常你需要检测
object.hasOwnProperty(variable) 来确认这个属性名是该对象的成员,而不是来自于原型链
typeof 来排除函数
属性值的顺序是不确定的

  1. for (variable in obj) {
  2. if (obj.hasOwnProperty(variable)) {...}
  3. }

2.3.3 do 语句

就像while 语句,不同的是至少执行一次,是在代码块执行之后而非之前检测表达式的值

2.3.4 try catch 语句

try 执行一个代码块,并捕获该代码块抛出的任何异常,catch 从句定义一个新的变量 variable 来接收抛出的异常对象

2.3.5 throw 语句

抛出一个异常,如果 throw 语句在一个 try 代码块中,那么控制流会跳转到 catch 从句中。如果 throw 语句在函数中,则该函数调用被放弃,控制流跳转到调用该函数的 try 语句的 catch 从句中

exception 对象:

  • name: 识别异常类型
  • message:描述

3 对象

3.1 可变的键控集合

伪对象:Number、String、Boolean
可变的键控集合:Array、Function、RegExp

对象是属性的容器:
属性名:包括空字符串在内的任意字符串,如果属性名是合法的 JS 标识符且不是保留字,则并不强制要求用引号括住属性名,如 “first-name”、 first_name
属性值:除 undefined 值之外的任何值

检索:从 undefined 的成员属性中取值,会导致 TypeError,通过 && 来避免错误

  1. flight.equipment // undefined
  2. flight.equipment && flight.equipment.model

对象通过引用来传递,永远不会被复制

3.2 原型对象

所有通过对象字面量创建的对象都连接到 Object.prototype,它是 JS 中的标配对象

3.3 delete

不会触及原型链中的任何对象,只要该对象中不再独有该属性,就会返回 true

4 函数

每个函数在创建时会附加两个隐藏属性:

  • 函数的上下文
  • 实现函数行为的代码:JS 创建一个函数对象时,会给该对象设置一个“调用”书香,当JS调用一个函数时,可理解为调用此函数的“调用”属性

  • [ ] 函数声明时定义的形参:过多的实参会被忽略,过多的形参会被定义为 undefined

  • this:上下文,指向调用对象,取决于调用方式
  • arguments: 实参

4.1 扩充Function

  1. Function.prototype.method = function (name, func) {
  2. if (!this.prototype[name]) {
  3. this.prototype[name] = func;
  4. return this;
  5. }
  6. }

4.2 基本类型的原型

提取数字中的整数部分:Number.integer

  1. Number.method('integer', function () {
  2. return Math[this < 0 ? 'ceil' : 'floor'](this)
  3. })

移除字符串首尾空白的方法:

  1. String.method('trim', function() {
  2. return this.replace(/^\s + |\s + $/g, '')
  3. })
  4. " news ".trim()

4.3 递归

trivial solution:寻常解或明显解
一个递归函数调用自身去解决它的子问题
递归函数可以非常高效地操作树形结构,比如浏览器端的文档对象模型(DOM),每次递归调用时处理指定的树的一小段
尾递归:一种在函数的最后执行递归调用语句的特殊形式的递归
尾递归优化:如果一个函数返回自身递归调用的结果,那么调用的过程会被替换为一个循环,可以显著提高速度。有些语言提供了该优化,但 JS 没有提供

4.4 闭包

内部函数可以访问外部函数的参数和变量(除了 this 和 arguments)
内部函数拥有比它的外部函数更长的生命周期
内部函数访问的自由变量并不是一个副本而是该变量本身,并且只要内部函数需要,该变量就会持续保留
避免在循环中创建函数,它可能只会带来无谓的计算,还会引起混淆

  1. // 设置一个DOM节点为黄色,然后把它渐变为白色
  2. var fade = function (node) {
  3. var level = 1;
  4. var step = function () {
  5. var hex = level.toString(16);
  6. node.style.backgroundColor = '#FFFF' + hex + hex;
  7. if (level < 15) {
  8. level += 1;
  9. setTimeout(step, 100);
  10. }
  11. setTimeout(step, 100);
  12. }
  13. }
  14. fade(document.body)
  1. // 单击一个节点时,将会弹出一个对话框显示节点的序号
  2. var add_the_handlers = function (nodes) {
  3. var helper = function (i) {
  4. return function(e) {
  5. alert(i);
  6. }
  7. };
  8. var i;
  9. for (i = 0; i < nodes.length; i++) {
  10. nodes[i].onclick = helper(i)
  11. }
  12. }

4.5 模块

模块是一个提供接口却隐藏状态与实现的函数或对象
模块模式:一个定义了私有变量和函数的函数,利用闭包创建可以访问私有变量和函数的特权函数,最后返回这个特权函数,或者把它们保存到一个可访问到的地方。

4.6 级联

让方法返回 this ,就可以启用级联,使得可以在单独一条语句中依次调用同一个对象的很多方法。这些方法每一个都返回该对象,所以每次调用返回的结果可以被下一次调用所用。

4.7 记忆(memoization)

将先前操作的结果记录在某个对象里,从而避免无谓的重复运算

4.71 斐波那契数列

  1. var fibonacci = function (n) {
  2. return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2)
  3. }

将存储结果保存到 memo 数组中,并隐藏到闭包中

  1. var fibonacci = function (n) {
  2. var memo = [0, 1];
  3. var fib = function(n) {
  4. var result = memo[n];
  5. if (typeof result !== 'number') {
  6. result = fib(n-1) + fib(n-2);
  7. memo[n] = result;
  8. }
  9. return result;
  10. }
  11. return fib;
  12. }()

4.72 通用记忆函数

  1. var memoizer = function (memo, formula) {
  2. var recur = function(n) {
  3. var result = memo[n];
  4. if (typeof result !== 'number') {
  5. result = formula(recur, n);
  6. memo[n] = result;
  7. };
  8. return result;
  9. }
  10. return recur
  11. }
  1. var factorial = memoizer([0, 1], function(recur, n){
  2. return n * recur(n-1)
  3. })

5 继承

其他语言的类继承:

  • 代码重用
  • 类型系统
  • 对象是类的实例,并且类可以从另一个类继承

JS:

  • 不需要类型转换,对象继承无关紧要
  • 对象直接从其他对象继承
  • 插入一个多余的间接层,通过构造器函数产生对象

5.1 伪类

  1. var Mammal = function (name) {
  2. this.name = name;
  3. }
  4. Mammal.prototype.get_name = function () {
  5. return this.name
  6. }
  7. Mammal.prototype.says = function () {
  8. return this.saying || ''
  9. }
  10. var Cat = function(name) {
  11. this.name = name;
  12. this.saying = 'meow';
  13. }
  14. Cat.prototype = new Mammal() // 将原型对象替换成父类
  15. // 扩充新原型对象,增加 purr 和 get_name 方法
  16. Cat.prototype.get_name = function (n) {
  17. return this.says() + ' ' + this.name + ' ' + this.says()
  18. }

5.2 原型

差异化继承:通过定制一个新的对象,指明它与所基于的基本对象的区别
对某些数据结构继承于其他数据结构的情形非常有用
对象的所有属性都是可见的,无法得到私有变量和私有函数

  1. var myMammal = {
  2. name: 'Herb the Mammal',
  3. get_name: function() {
  4. return this.name;
  5. },
  6. says: function() {
  7. return this.saying || ''
  8. }
  9. }
  10. var myCat = Object.create(myMammal)
  11. myCat.name = 'lulu'
  12. myCat.saying = 'WoW'
  13. myCat.get_name = function () {
  14. return this.says + ' ' + this.name + ' ' + this.says;
  15. }

5.3 函数化

  1. var constructor = function (spec, my) {
  2. var that, 其他的私有实例变量;
  3. my = my || {};
  4. // 把共享的变量和函数添加到 my
  5. that = 一个新对象
  6. // 添加给 that 的特权方法
  7. return that;
  8. }
  1. var mammal = function (spec) {
  2. var that = {};
  3. that.get_name = function () {
  4. return spec.name;
  5. }
  6. that.says = function () {
  7. return spec.saying || '';
  8. }
  9. return that;
  10. }
  11. var myMammal = mammal({name: 'Herb'})
  12. var cat = function (spec) {
  13. spec.saying = spec.saying || 'lulu';
  14. var that = mammal(spec);
  15. that.get_name = function () {
  16. return that.says() + ' ' + spec.name + ' ' + that.says();
  17. }
  18. return that;
  19. }
  20. var myCat = cat({name: 'Tiantian'})
  21. myMammal.get_name()
  22. myCat.get_name()

5.4 部件

从一套部件中把对象组装出来
给任何对象添加简单事件处理特性的函数:

  • on()
  • fire()
  • 私有的事件注册表对象
  1. var eventuality = function (that) {
  2. var registry = {}
  3. // 在一个对象上触发一个事件,该事件可以是一个包含事件名称的字符串
  4. // 或者是一个拥有包含事件名称的 type 属性的对象
  5. // 通过 “on" 方法注册的事件处理程序中匹配事件名称的函数将被调用
  6. that.fire = function (event) {
  7. var array, func, handler, i, type = typeof event === 'string' ? event : event.type
  8. // 如果这个事件存在一组事件处理程序,那么就遍历它们并按顺序依次执行
  9. if (registry.hasOwnProperty(type)) {
  10. array = registry[type];
  11. for (var i = 0; i < array.length; i++) {
  12. handler = array[i];
  13. // 每个处理程序包含一个方法和一组可选的参数
  14. // 如果该方法是一个字符串形式的名字,那么寻找到该函数
  15. func = handler.method;
  16. if (typeof func === 'string') {
  17. func = this[func];
  18. }
  19. func.apply(this, handler.parameters || [event])
  20. }
  21. }
  22. return this;
  23. }
  24. // 注册一个事件,构造一条处理程序条目,将它插入到处理程序数组中
  25. // 如果这种类型的事件还不存在,就构造一个
  26. that.on = function (type, method, parameters) {
  27. var handler = {
  28. method: method,
  29. parameters: parameters
  30. };
  31. if (registry.hasOwnProperty(type)) {
  32. registry[type].push(handler);
  33. } else {
  34. registry[type] = [handler];
  35. }
  36. return this;
  37. }
  38. }

6 数组

  • JS 的 length 没有上界,不会发生数组越界错误
  • ES262标准:数组的下标必须是大于等于0并小于 232-1的整数
  • 把 length 设小将导致所有下标大于等于新 length 的属性被删除
  • 设置更大的 length 不会给数组分配更多的空间

6.1 is_array

  1. var is_array = function(value) {
  2. return Object.prototype.toString.apply(value) === '[object Array]'
  3. }