本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循署名-非商业用途-保持一致的创作共用协议.
写作意图: 一直通过一些视频或者网页零碎的学习JavaScript, 整个语言一直难以形成系统, 所以希望利用一些时间系统的学习一下JavaScript语言.
写作目标:
- 希望构建JavaScript语言体系, 将零碎知识点系统化
- 希望以简单的语言, 能够使有编程基础的人快速入门JavaScript
注意: 本书并不针对零基础者, 很多基础内容会一笔带过, 本书大部分内容基于<JavaScript权威指南>进行总结
.
#0. JavaScript概述
JavaScript是一门高端的, 动态的, 弱类型的编程语言
- 语言区分大小写
- 用
unicode
编码编写
- 注释使用
// 单行注释
或者/* 多行注释 */
, 和C++相同
- 以
;
作为行间分隔符可选, 可以使用C++风格或者Python风格
#1. 类型/值和变量
JavaScript的数据类型分为: 原始类型和对象类型.
- 原始类型包括数字类型, 字符串类型, 布尔类型, null和undefined(
最后两种是特殊的原始值
)
- 对象类型包括普通对象, 具有特有行为的array数组对象和函数对象
JavaScript的数据类型也可分为: 可变类型和不可变类型
- 可变类型包括数组和对象(可变类型的比较是引用的比较)
- 不可变类型包括数字类型, 字符串类型, 布尔类型, null和undefined(不可变类型的比较为值的比较)
1 2 3 4 5 6 7 8
| var a = [], b = []; a === b; var a = []; var b = a; b[0] = 1; a[0]; a === b;
|
JavaScript变量是无类型的, 可以被赋予任何类型值
数字
JavaScript无整数和浮点数之分, 所有数字都用浮点数(IEEE754)表示
JavaScript中算数运算在溢出, 下溢或被零整除时不会报错, 溢出或者被零整除返回Infinity
或-Infinity
, 下溢返回0和-0,
判断变量x是否是NaN(非数字值), 若x != x为true, 则x为NaN
字符串
1 2 3 4
| "" 'string' "JavaScript" 'name = "Andrew"'
|
- 支持字符串
+
拼接操作
- 字符串length属性, str.length
- 支持定位, 取子串, 切片, 分割, 替换等字符串操作
字符串本身是不可变的!
布尔值
true和false
- undefined, null, 0, -0, NaN, “”转换为false
- 其他值包括对象(数组)会转换成true
类型转换
显式类型转换
1 2 3 4
| Number("3"); String(false); Boolean([]); Object(3);
|
隐式类型转换
+
运算一个操作数是字符串, 会将另一个操作数转换为字符串
!
运算会把操作数转换为布尔值
变量声明
使用var关键字声明
1 2 3
| var i = 0; var str = "hello"; var first = function() {}
|
作用域
- 函数体内, 同名局部变量的优先级高于全局变量(局部变量和全局变量同名不是好的编程习惯)
- JavaScript中没有块级作用域, 使用函数作用域(函数内声明在整个函数体都是可见的)
#2. 表达式和运算符
1 2 3 4 5 6 7 8
| var matrix = [1, 2, 3]; var p = { x: 2.3, y: 1.8}; var square = function { return x * x; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 基本算术表达式: + - * / % 一元算术运算符: + - ++ -- // 会把操作数转换为数字 位运算符: & | ~ << >> >>> // 操作数为32位整数, 最后一个运算符为无符号右移 比较运算符: < > <= >= == === // 注意区分最后两个 逻辑表达式: && || ! 三目运算符: ? : // 基本类似C++中的运算符 typeof (typeof value == "string") ? "'" + value + "'" : value; delete // 删除对象属性或者数组元素 var o = {x: 1, y: 2} delete o.x; "x" in o; // false in // 希望左操作是一个字符串, 右侧对象拥有一个名为左操作数值的属性名则返回true var point = { x: 1, y: 1}; "x" in point; // true "z" in point; // false instanceof // 希望做操作数是一个对象, 如果左侧对象是右侧类的实例, 则返回true var d = new Date(); d instanceof Date; // true d instanceof Number; // false
|
#3. 语句
定义变量
1 2 3 4
| var x = 2, f = function(x) { return x * x; }, y = f(x);
|
定义函数
1 2 3 4 5 6
| function funcname([arg1 [, arg2 [..., argn]]]) { statement; } var f = function(x) { return x * x; }
|
if else语句
1 2 3 4 5 6 7 8 9
| if (expression) { } else if (expression) { } else { }
|
switch语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| switch(expression) { case value1: break; case value2: break; case value3: break; default: break; }
|
四种循环while,do-while,for,for-in
1 2 3 4 5 6 7 8 9 10 11 12
| while(expression) { statement; } do { statement; } while(expression); for(initialize; test; increment) { statement; } for(variable in object) { statement; }
|
- break语句跳转到循环(最内层)或者其他语句结束
- continue语句终止本次循环并开始下一次循环
- return语句让解释器跳出函数体执行(
直接return; 返回的是undefined
)
- throw语句触发或者抛出一个异常
#4. 对象
JavaScript对象都是关联数组
所有JavaScript对象都从Object.prototype继承属性
创建对象
三种方法:
1 2 3
| var point = {x: 0, y: 1}; var point1 = new Object(); var point2 = Object.create({x: 1, y: 2});
|
查询和设置属性
1 2
| var first = point.x; var second = point["y"];
|
删除属性
1 2
| delete point.x; (delete point["y"]) == true;
|
delete不可删除继承属性, 只能删除自有属性
检测属性
1 2 3 4
| "x" in point; "toString" in point point.hasOwnProperty("toString"); point.propertyIsEnumerable("toString");
|
枚举属性
1 2 3 4 5 6 7 8
| for(p in point) { if (!point.hasOwnProperty(p)) continue; else if (typeof point[p] === "function") continue; else: console.log(p); }
|
属性getter和setter
1 2 3 4 5 6
| var point = { date_prop: value, get accessor_prop() {}, set accessor_prop(value) {} };
|
对象的三属性
- 原型prototype
- 类class
- 可扩展性extensible attribute
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| var p = {x: 1}; var o = Object.create(p); p.isPrototypeOf(o); Object.prototype.isPrototypeOf(p); function classof(o) { if (o === null) { return "Null"; } if (o === undefined) { return "Undefined"; } return Object.prototype.toString.call(o).slice(8, -1); }
|
序列化对象
1 2 3 4 5 6 7 8
| var point = { x: 1, y: { z: [false, null, ""] } } s = JSON.stringify(point); p = JSON.parse(s);
|
#5. 数组
JavaScript数组是动态的额, 数组元素无类型(可以存放任意类型), 数组是对象的特殊形式
创建数组
1 2 3 4 5
| var misc = [1.1, true, "a",]; var undes = [,,]; var a = new Array(); var a = new Array(10); var a = new Array(1, 2, 3, "test");
|
数组的读取和写入
下标访问, 从零开始
通过var a = new Array(10);
可以创建稀疏数组
1 2 3 4
| var a = []; a.push(5); a.pop(); delete a[0];
|
多维数组
使用数组的数组进行近似, 风格和访问与C++中类似
数组方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| var a = [1, 2, 3]; a.join(); // 数组拼接, 默认使用,拼接成字符串, 是String.split的逆方法 a.reverse(); // 数组逆序 a.sort(); // 数组排序 a.concat([1, 2]); // 数组拼接, 返回新数组 a.slice(0, 2); // 数组切片 a.splice(删除起始位置[, 删除个数[, 插入元素]]); // 在数组中插入或删除元素 a.push(), a.pop(); // 数组模拟栈 a.unshift(), a.shift(); // 数组头部添加和删除元素 a.toString(); // 每个元素字符串化 /* ECMAScript 5数组方法 */ forEach(); // 从头到尾遍历数组, 为每个元素调用指定函数 map(); // 对每个元素调用指定元素, 并返回数组 filter(); // 返回满足函数逻辑的元素的数组(子集) every(), some(); // 对数组元素应用指定函数判定, 返回true或false reduce(操作函数[, 可选传递给操作函数的初始参数]) indexOf(), lastIndexOf(); // 搜索整个数组具有给定值的元素 Array.isArray([]); // true, 判断是否为数组
|
#6. 函数
函数即对象
函数定义
1 2 3
| function funcname(参数) { }
|
- 函数length属性代表函数参数的数量
- prototype属性
- call()和apply()方法
- bind()方法, 将一个函数绑定到对象中, 作为对象的方法
- Function()构造函数, 可以传入任意数量的字符串实参, 最后一个实参表示的文本就是函数体(
不能用来实现闭包
), 匿名
1 2 3 4 5
| f.call(o) f.apply(o) var f = new Function("x", "y", "return x * y;");
|
#7. 类和模块
JavaScript中类的实现基于原型
继承机制, JavaScript中的类是动态可继承的
关键字new调用构造函数,自动创建一个新对象,构造函数的prototype属性被用作新对象的原型,构造函数对已创建的对象初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function Range(from, to) { this.from = from; this.to = to; } Range.prototype = { constructor: Range, // 显式设置构造函数反向引用 includes: function(x) { return this.from <= x && x <= this.to; }, foreach: function(f) { for(var x = Math.ceil(this.from); x <= this.to; x++) f(x); }, toString: function() { return "(" + this.from + "..." + this.to + ")" } }; var r = new Range(1, 3);
|
检测任意对象的类:
- 使用instanceof运算符
- constructor属性(前两种方法在多个执行上下文场景中无法正常工作)
- 构造函数的名字
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
| var r = new Range(1, 3); r.constructor === Range function type(o) { var t, c, n; if (o === null) return "null"; if (o !== o) return "nan"; if ((t = typeof o) !== "object") return t; if ((c = classof(o)) !== "Object") return c; if (o.constructor && typeof o.constructor === "function" && (n = o.constructor.getName())) return n; return "Object"; } function classof(o) { return Object.prototype.toString.call(o).slice(8, -1); } Function.prototype.getName = function() { if ("name" in this) return this.name; return this.name = this.toString().match(/function\s*([^(]*)\(/)[1]; }
|
子类
B是A的子类
1 2
| B.prototype = inherit(A.prototype); B.prototype.constructor = B;
|