- 教程
【免费下载链接】javascript-in-one-pic
Learn javascript in one picture.
本指南围绕开源仓库 javascript-in-one-pic 的核心产物——一张汇总 JavaScript 语法与核心概念的脑图(Mind map)展开,系统介绍该项目的设计思路、仓库文件资源与完整的知识结构。读完本文,你可以快速建立对 JavaScript 的整体认知、按图定位具体语法细节,并把脑图内容直接用作日常开发时的语法速查手册。
javascript-in-one-pic 项目核心产物:JavaScript 语法知识脑图总览
项目定位:用一张图承载整个 JavaScript 语法体系
从个人笔记到开源语法手册
按项目 README(README.md)与中文版(README-zh.md)的说明,该项目最初是作者为个人使用而记录的 JavaScript 语法与基本概念笔记,同时兼作教学课程大纲。在整理过程中作者发现,用脑图(Mind map)来总结一门编程语言的效果出乎意料地好:这种树状结构看起来就像一本 JavaScript 手册的目录从细节上被逐层扩充,既能从宏观上把握语言全貌,又能非常快速地定位到某个实用的语法细节。
因此,项目的核心交付物只有一张图——js in one pic.png——它把 JavaScript 的标识符规则、数据类型、运算符、作用域、引用类型与流程控制等主干知识组织成一张可读的总览图。
适合谁使用
README-zh 对目标读者给出了清晰的分层建议:
- 完全没有编程经验的新手:不适合在无人指导的情况下独自学习;
- 拥有其他编程语言经验的 JavaScript 新手:可以把脑图作为学习提纲使用;
- 有经验的 JavaScript 开发人员:更适合把它当作语法手册随时查阅。
作者还建议将导出的 PDF 打印出来贴在电脑旁边,作为常驻的参考材料。
工具链与文件格式
从 README 的 "About this project" 一节可以确认项目使用的工具链与交付格式:
- 脑图使用Mindnode(macOS 思维导图工具)绘制;
- 同时导出PDF、PNG、OPML三种格式;
- 提供
*.mm文件,可用开源脑图软件FreeMind继续编辑; - 项目整体完成后计划再发布 SVG 格式(README 以 issues #9 跟踪该事项);
- README 的 "Online version" 一节还提供了在线脑图入口(Online Mindmap),并列出待办项 "Syntax highlight"。
仓库资源一览:五种格式各取所需
仓库根目录下与知识内容直接相关的文件如下,均可按需选用:
| 文件 | 格式 | 用途 |
|---|---|---|
| js in one pic.png | PNG 位图 | 一张总览图,直接查看、分享 |
| js in one pic.pdf | 打印输出,贴在电脑旁当手册 | |
| js in one pic.mm | FreeMind 工程 | 用 FreeMind 打开即可继续编辑 |
| js in one pic.opml | OPML(大纲标准格式) | 可导入大多数主流脑图/大纲工具 |
| js in one pic.mindnode | Mindnode 源工程 | 保留原始的绘制工程(含 contents.xml、viewState.plist 等) |
其中 js in one pic.mm 与 js in one pic.opml 是纯文本的结构化源文件,内容与 PNG 中的脑图一一对应,本文后续的"脑图内容详解"即以其为准逐条展开。此外仓库还提供 5 种语言的 README:README.md、README-zh.md、README-zh_TW.md、README-pt_BR.md、README-es_MX.md。
颜色标记:脑图里的"三色信号灯"
README 用三个徽章规定了脑图配色的语义,这在阅读和继续编辑脑图时非常关键:
- 不推荐使用(not recommended):#7E1600 深红
- 重要(important):#4E8D20 绿
- 注意(warning):#DE2B00 橙红
从 js in one pic.mm 的源码(FreeMind XML 的COLOR属性)可以印证这些颜色确实被落实到具体知识点上,例如:
a = 123; // global(未用var声明、直接赋值的全局变量)以#800000深红色标注,属于"不推荐"类写法(js in one pic.mm);- "Object (basic)" 与 "Object (OOP)" 两个主干节点以及 "root of everything" 以
#008000/#057F00绿色标注为重要内容(js in one pic.mm、js in one pic.mm); - 数组的
shift/unshift/sort/reverse/splice下方注释 "// will change the array" 以#FF0000红色标注为警告,提示这些方法会原地修改原数组(js in one pic.mm)。
另外脑图中还预留了一个空的 "Tips" 分支(js in one pic.mm),从结构看是用于日后补充易错提示的预留位置。
脑图内容详解(按主干逐层展开)
脑图共分六大主干:Identifier(标识符)、Comments(注释)、Data type(数据类型)、Operators(运算符)、Scope(作用域)、Reference type(引用类型),以及一个相对独立的 Flow control(流程控制)分支。以下按 js in one pic.mm 的实际内容逐一展开,并补充必要的原理说明。
Identifier 标识符
标识符分支(js in one pic.mm)覆盖以下规则:
- 大小写敏感:
a != A; - 开头字符:必须以字母、下划线
_或美元符号$开头,其中字母既包括 ASCII 字母也包括 Unicode 字母; - 关键字(key words):
break; case; catch; continue; default; delete; do; else; finally; for; function; if; in; instanceof; new; return; switch; this; throw; try; typeof; var; void; while; with; - 保留字(Reserved words),分两种场景:
- 非严格模式下:
class; const; enum; export; extends; import; super; 'use strict'严格模式下额外保留:implements; interface; let; package; private; protected; public; static; yield;
- 非严格模式下:
- 变量声明:
var a; // local(局部变量) a = 123; // global(未声明即赋值,成为全局变量)其中直接赋值而不加
var的写法在脑图中被标记为"不推荐"(深红色),因为它会在全局作用域上隐式创建变量,容易造成命名冲突与难以追踪的 bug。
Comments 注释
注释分支(js in one pic.mm)只列出两种基础写法:
// single line 单行注释 /* multi-line comments */ 多行注释Data type 数据类型
数据类型是脑图中最庞大的分支之一(js in one pic.mm),包含六类基础类型与函数类型。
Number(数字)
typeof返回'number';常见取值:
0、1.2、045(八进制)、0x56(十六进制)、0.314e2(科学计数法)、NaN;类型转换
Number()系列:parseInt("2.7", 10); // 2 parseInt('10', 2); // 2(按二进制解析) parseFloat('2.5'); // 2.5 isNaN(parseFloat('a')); // true支持运算符
+ - * / %以及比较运算符<, <=, >, >=, !=, ==。
String(字符串)
typeof返回'string';取值可用双引号
"Hello"或单引号'World';转换:
String()/toString():var a = 8; a.toString(2); // '1000'(转为二进制字符串)**不可变(immutable)**特性:
var a = 'ABC'; a[0]; // 'A' a[0] = 'D'; a; // 'ABC'(赋值不生效,字符串不可变) var a = 'abc'; a = a + 'd'; a; // 'abcd'(重新赋值得到新字符串)运算符:
+用于拼接,以及比较运算符<, <=, >, >=, !=, ==。
Boolean(布尔值)
typeof返回'boolean';取值只有
true/false;Boolean()转换规则(脑图按输入类型分组给出):- number:
Boolean(0); // false、Boolean(NaN); // false、Boolean(!0); // true - string:
Boolean(''); // false、Boolean('*'); // true - object:
Boolean(null); // false、Boolean({}); // true - undefined:
Boolean(undefined); // false
归纳起来即:0、NaN、空字符串、null、undefined 在布尔上下文中均为 falsy,其余值(包括空对象
{})为 truthy。- number:
Undefined(未定义)
typeof返回'undefined';唯一取值就是
undefined;undefined≠ "未声明"(Not defined),这是脑图特意强调的易错点:undefined == undefined; // true var a; a == undefined; // true(已声明但未赋值) console.log(d); // Error(d 从未声明,直接报 ReferenceError)
Object(基础)与 Null
typeof返回'object';字面量创建:
var p = { name: 'Ad', 'age': 24 }; var q = {}; q.name = 'Bob'; q.age = 24; console.log(q.name); // 'Bob' console.log(q['age']); // 24(既支持点号也支持方括号访问)构造函数创建:
var p = new Object(); p.sayHi = function(){ console.log('Hi!'); } p.sayHi(); // 'Hi!'Null(属于"类基本类型"):
typeof null同样返回'object'(历史遗留行为);null == undefined; // true(宽松相等成立,因为null被视为"未定义对象")。
Function(基础)
typeof返回'function';三种创建方式:
var a = function(arg1, arg2){ // 函数表达式 //func body; } function a(arg1, arg2){ // 函数声明 //func body; } (function(arg1, arg2){ // 立即执行函数表达式(IIFE) //func body; })函数内部(inside function):
- 参数按顺序传入:
var f = function(x, y){ console.log('x: ' + x + ', y: ' + y); }; var x = 'x', y = 'y'; f(x, y); // x: x, y: y f(y, x); // x: y, y: x - 缺省参数为
undefined:var f = function(x){ console.log(x); }; f(); // undefined - 内置
arguments对象按索引保存所有实参:var f = function(){ console.log(arguments); }; f(3, 2, 1); // { '0': 3, '1': 2, '2': 1 } return用于返回结果(脑图单列 "return" 节点提示其作用)。
- 参数按顺序传入:
Operators 运算符
运算符分支(js in one pic.mm)重点讲解类型转换与比较语义。
算术与强制类型转换(casting)
+ - * / %四个基础运算符中,脑图重点演示了+与-在混合类型下的转换优先级:
+的转换优先级:string > number > boolean(只要有一端是字符串,+就偏向拼接):'1' + 2; // '12' 3 + '4'; // '34' '5' + false;// '5false' 6 + true; // 7-的转换优先级:number > string > boolean(-没有拼接语义,两端都向数字转换):'1' - 2; // -1 3 - '4'; // -1 '5' - false;// 5 6 - true; // 5
自增自减与赋值
++a、a--、a+=1、a-=1被归为一组,提醒这些运算符既有返回值又会改变变量本身。
比较运算
- 关系比较:
>, >=, <, <=; - 宽松相等:
==, !=——只比较值(only value),比较前会做类型转换; - 严格相等:
===, !==——同时比较值与类型(both value & type); - 类型转换视角:
a > b本质上等价于a - b > 0,即先按数字规则转换再比较。
逻辑运算
!true; // false true && false; // false false || true; // trueScope 作用域
作用域分支(js in one pic.mm)是脑图中理论密度最高的一块,包含执行上下文、作用域链与闭包三个子主题,脑图以 ASCII 示意图辅助说明。
Execution context 执行上下文
脑图用下面的示意图说明"全局对象 + 执行上下文"的关系:在 Node.js 中全局对象是global,在浏览器中则是window;每个执行上下文内部都有一个 Variable Object(变量对象),存放该上下文中的变量、数据、函数与对象。
+-------------+ +-----------------------+ |Node +------+ |Execution Context | +-------------+ | +-----------------------+ |global = { | +--> |[Variable Object] = { | | | | | | |}; | | | variables | +-------------+ | | | | | data | | | | | | functions | +-------------+ | | | |Browser +-----+ | objects | +-------------+ | | |window = { | | ... | | | | | |}; | |}; | +-------------+ +-----------------------+每次调用函数都会创建新的执行上下文,并压入 Context Stack(上下文栈);当前执行上下文中的this指向"拥有该函数的对象"。示意图如下:
+-----------------------------------+ |function b() // create new context +-----+ +-----------------------------------+ | |Variable Object = { | | +---------------+ | arguments = ...; | | | | | | | | | | | | | | |}; | | | | |Scope chain = | | | . | | +-------------------------------+ | | | . | | |global | | | | . | | | +---> f = 'no'; | | | | | | | | | | | | | | | +---> function a() | | | | | | | +---> f = 'ye' | | | +---------------+ | | | | | +-> |function b | | | +---> function b() | | +---------------+ | | +---> | | |function a | | | | | | +---------------+ | | +---> | | |global context | | | | | +---------------+ | +-------------------------------+ | Context Stack |this = {Object owns b}; | +-----------------------------------+Scope chain 作用域链
脑图用如下结构说明作用域链的形态:全局作用域在最外层,函数作用域逐层嵌套,每个函数都有一个指向其外层 Variable Object 的引用,从而形成一条链。变量查找时沿这条链由内向外进行。
+--------------------------------------------+ |Scope Chain | +--------------------------------------------+ +-+global [Variable Object] | | | | | +-> var x; | | | | | | <---------------+ | | | + | | +-> function a() [Variable Object]<+ | | | + | | | | | | | | | +--> var x; | | | | | | | | | | + | | | +--> function b() [Variable Object] | | | | | | <---------------+ | | | + | | +-> function c() [Variable Object]<+ | | | + | | | | | | | | | +--> var x; | | | | | | | | | | + | | | +--> function d() [Variable Object] | | | | | v | +--------------------------------------------+Closure 闭包
闭包子主题给出的是"闭包 = 函数 + 其作用域链"的示意:内部函数inc引用了外层执行上下文中的变量x,即使外层函数执行完毕,inc通过作用域链依然能访问并修改x。脑图同时给出inc1、inc2两次调用inc()的示例,暗示每次调用都会各自捕获独立的x(从结构可以推断,这正是闭包实现"私有状态"的机制)。
+-------------------+ +-----------------+ |Execution context | +---+[Scope chain] | +-------------------+ | +-----------------+ +> | var x = 100; | | |[Function inc] | | | var inc1 = inc(); +--+ | + | | | var inc2 = inc(); +--+ | +-> x; | | | | | | | | | inc1(); | | +-> [Function] | | | inc1(); | | + | | | inc2(); | | +-> x++; | | | inc1(); | | | | | inc2(); | +-----------------+ +--+ x; | | | +-------------------+Reference type 引用类型
引用类型分支(js in one pic.mm)涵盖 Function、Array 与面向对象(OOP)的 Object。
Function:一等公民
typeof返回'function';属性:
f.length;(形参个数);函数作为值传递(callback 回调):
var api = function(){ return { name: 'rainy', age: 24 }; }; var handler = function(d){ console.log('Name: '+d.name+', Age: '+d.age); }; var request = function(api, callback){ callback(api()); }; request(api, handler); // Name: rainy, Age: 24方法 apply / call / bind:
// func.apply(thisObj, [arg1, arg2, ...]); // func.call(thisObj, arg1, arg2, ...); // func.bind(thisObj, arg1, arg2, ...); // 关于 thisObj,参见 Scope 分支 var sayHi = function(name){ console.log('Hello, ' + name + '!'); }; sayHi.call(this, 'rainy'); // Hello, rainy! sayHi.apply(this, ['rainy']); // Hello, rainy! sayHi.bind(this, 'rainy')(); // Hello, rainy!三者的共同点是指定函数体内的
this;区别在于call逐个传参、apply以数组传参、bind返回一个已绑定this(与参数)的新函数而不会立即执行。
Array:数组
typeof返回'object'(数组本质是对象);两种创建方式等价:
var a = [1, 2, '3', [4, true]]; var a = new Array(1, 2, '3', [4, true]);属性:
a.length; // 4;常用方法(脑图逐一给出):
a[0] == 1; // true(下标访问) a[3][1] == true; // true(二维数组访问) a.slice(0, 2); // [1, 2](切片,不修改原数组) a.indexOf(1); // 0(查找元素下标) a.push({}); // 返回新的 a.length(入栈,Mod 修改原数组) a.pop(); // 返回被弹出的元素(Mod 修改原数组) a.join('-'); // ?(自测题:拼接所有元素为字符串) a.toString() == a.join(','); // true var s = 'a,b,c,d'; s.split(','); // ['a', 'b', 'c', 'd'](字符串分割成数组) a.concat(['a', 'b']); // 返回新数组,原数组不变 —— 与 push()(原地修改)对比map / reduce(脑图标注了回调函数签名):
// arr.map(callback, thisObj) /* callback = function(element, index, arr){ * return element to the same pos of (returned)arr; * }; */ [55, 44, 33, 22, 11].map(function(e, i, arr){ return e/(arr.length-i); }); // [ 11, 11, 11, 11, 11 ] // arr.reduce(callback, init) /* callback = function(prev, curr, index, arr){ * curr 依次遍历 arr 的后续元素; * prev 缓存上一次的返回值,初始值为 init(未传 init 时取 arr[0])。 * }; */ [55, 44, 33, 22, 11].reduce(function(p, c, i, arr){ return p + c/(arr.length-i); }, 55); // 约 84.88(注:原图注释写作 0,实际累加结果约为 84.88)上述
map示例中每个元素被除以"该元素距离末尾的元素个数 + 1",恰好得到五个 11;reduce示例则演示了带初始值55的累加过程。filter / some / every:脑图列出但未展开代码,属于同类高阶迭代方法;
shift / unshift / sort / reverse / splice:以红色警告标注"// will change the array",提示这五个方法都会原地修改原数组;
遍历(iteration)的两种写法:
for(var i = 0; i < a.length; i++){ console.log(a[i]); } a.forEach(function(ele){ console.log(ele); });
Object(OOP):构造函数、原型链与继承
脑图以 "root of everything"(绿色重要标记)定位 Object,随后展开三个子主题:
构造函数(Constructor)
function Person(name){ this.name = name; this.sayHi = function(){ console.log('Hi ' + this.name + '!'); }; }; var p1 = new Person('Ad'); var p2 = new Person('Bob'); p1.name // 'Ad' p2.sayHi(); // 'Hi Bob!' p1.constructor === Person // true p1 instanceof Person // true p1.sayHi == p2.sayHi // false(每个实例各自拥有独立的 sayHi 函数)原型链(prototype chain)
function Person(){}; Person.prototype.name = 'Person'; var p1 = new Person(); var p2 = new Person(); p2.name = 'rainy'; console.log(p1.name); // 'Person'(来自原型) console.log(p2.name); // 'rainy'(实例自有属性覆盖原型)属性归属判断与遍历保护:
p1 instanceof Person // true p1.age = 24; p1.hasOwnProperty('name'); // false(name 来自原型,不是实例自有属性) Person.hasOwnProperty('name'); // true p1.hasOwnProperty('age'); // true(实例自有属性) 'name' in p1 // true(in 会沿原型链查找) 'age' in Person // false(Person 函数本身没有 age)原型属性共享的经典陷阱(红色警告部分):
// All properties on the prototype are shared among instances Person.prototype.friends = ['Ad', 'Bob']; p1.friends.pop(); // 'Bob'(p1 修改了共享数组) console.log(p2.friends); // ['Ad'](p2 看到的数组也被改掉了) // 推荐做法:结合构造函数与原型 function Person(){ this.friends = ['Ad', 'Bob']; // 引用类型属性放构造函数,每个实例独立一份 }; Person.prototype.name = 'Person'; // 方法/值类型属性放原型,共享**继承(Inheritance)**的两种方式:
原型链继承:
function Father(){}; Father.prototype.familyName = 'Good'; function Child(){}; Child.prototype = new Father(); // 让子类原型指向父类实例 var c = new Child(); console.log(c.familyName); // 'Good'借用构造函数(constructor stealing):
function Father(){ this.familyName = 'Good'; }; function Child(){ Father.call(this); // 在子类实例上调用父类构造函数 }; var c = new Child(); console.log(c.familyName); // 'Good'
两种方式的差异在于:原型链继承共享父类原型上的方法,而借用构造函数继承能让每个子类实例获得独立的父类实例属性(配合apply/call使用,参见 Reference type - Function 分支)。
Flow control 流程控制
流程控制分支(js in one pic.mm)覆盖五类语句:
if / 三元
if(cond){ state1; }else if(cond2){ state2; }else{ state3; } false ? a : b // 条件为 false 时取 bswitch
switch (day) { case MON: break; case TUE: break; case WEN: break; default: }while
do { state1; } while(cond) // 先执行一次再判断 while(cond){ state1; } break; continue; // 跳出循环 / 跳过本次迭代for
for(var i = 0; i < len; i++){ state1; } for(var k in Obj){ // if Obj.hasOwnProperty(k){ console.log('Obj[' + k + '] = ' + Obj[k]); // } }其中for...in的示例特意注释了hasOwnProperty过滤,这正是为了避开原型链上可枚举属性(与上一节原型链警告相呼应)。
try / catch / finally
var a = {}; try{ a.f(); // 调用不存在的方法 }catch(e){ console.log(e instanceof TypeError); // true (function(){ console.log('a.f()'); // 'a.f()' })(); }finally{ console.log('always'); // 'always'(无论是否抛错都会执行) }项目进度与 TODO
README 的 TODO 清单用复选框明确标注了脑图的内容覆盖情况,可归纳如下:
| 主题 | 状态 |
|---|---|
| Function(基础):函数内部 arguments、return | 已完成 |
| Function:作为值传递(callback 回调) | 已完成 |
| Function:方法 apply / call / bind | 已完成 |
| Array:方法 map / reduce | 已完成 |
| Object(OOP):构造函数、属性 | 已完成 |
| Object(OOP):继承 | 已完成 |
| Scope 作用域 | 已完成 |
| Function:闭包 | 已完成 |
| Flow control:try / catch | 已完成 |
| Operators:位运算(bitwise) | 待完成 |
| ES6 | 待完成 |
此外在线版本的 "Syntax highlight"(语法高亮)也处于待办状态。也就是说,当前脑图覆盖的是 ES5 时代 JavaScript 的核心语法骨架,位运算与 ES6 特性尚未纳入。
相关项目与参考资料
- 相关项目:仓库维护者以同样的"一张图"形式准备了 Python3 in one pic,Go 语言版本(Go in one pic)在 README 中标明"准备中"。
- 参考资料(详见 README.md 的 References 小节):《Professional JavaScript for Web Developers》(JavaScript 高级程序设计)、JavaScript Garden、MDN JavaScript 文档,以及作者撰写的"图解 Javascript 上下文与作用域"博文。脑图中 Scope 分支的 ASCII 示意图即源自后者,这一点从脑图文件中的署名与链接可以印证。
开源协议
项目基于 MIT 协议开源,许可与免责条款见 LICENSE。这意味着你可以自由地查看、打印、分发这张脑图,也可以基于*.mm/.opml源文件继续扩展内容(如完成位运算与 ES6 分支),只需保留相应的版权声明。
- 教程
【免费下载链接】javascript-in-one-pic
Learn javascript in one picture.
相关推荐
deit3_small_patch16_224.fb_in1k实战:如何用PyTorch快速实现图像分类
deit3_small_patch16_224.fb_in1k实战:如何用PyTorch快速实现图像分类 想要快速上手图像分类任务吗?🤔 今天我将为你介绍一款
为什么选择Buttercoin?开源比特币交易所的核心优势解析
为什么选择Buttercoin?开源比特币交易所的核心优势解析 Buttercoin是一款开源比特币交易所软件,旨在通过社区驱动的方式提供安全、透明的比特币交易
打造多进度条应用:BProgress 1.1新特性深度探索
打造多进度条应用:BProgress 1.1新特性深度探索 BProgress 是一款轻量级、可定制的进度条工具,旨在提升用户体验。作为NProgress的现代
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考