ECMAScript6 直接上手使用见:JavaScript/ES6 快速上手教程。小程序学习笔记计划都更新到 这里 记录。
对于前端程序员来说,小程序的技术栈和前端开发相差不大,比较容易上手。如果没有接触过前端技术,有必要先补一下前端的知识,对前端有一个整体的认识,《现代前端技术解析》是非常好的入门书。
这篇笔记里主要记录 JavaScript 的知识,JavaScript 的标准在不停演进,微信小程序支持了绝大多数 ES6 API ,同时为了安全,小程序不支持动态执行 JS代码 详情见:微信小程序 JavaScript 支持情况。
ECMAScript 是 ECMA International 指定的标准,目的是创建标准化的 JavaScript,已经发展到了 2018 年的 ES9。不过主流似乎是 2015 年制定 ES6,譬如微信小程序就明确说自己实现了大多数 ES6 的接口。
下面的资料分别是最权威的英文资料和比较权威的中文资料:
第一份是在 ECMA International 的标准列表中找到的 ES9,奇怪的是没有历史年份的连接。 第二份是阮一峰编写的,在我的认知里,阮一峰在前端方面的造诣很高,博客文章清晰易懂,所以他写的《ECMAScript 6 入门]》应该也不错。 第三份是阮一峰在第二份资料中给出 JavaScript 入门教程,这份是阮一峰维护的,很新。
2013年,ECMA 正式推出 JSON 的国际标准,这意味着 JSON 格式已经变得与 XML 格式一样重要和正式了。
2015年3月,Facebook 公司发布了 React Native 项目,将 React 框架移植到了手机端,可以用来开发手机 App。它会将 JavaScript 代码转为 iOS 平台的 Objective-C 代码,或者 Android 平台的 Java 代码,从而为 JavaScript 语言开发高性能的原生 App 打开了一条道路。
2015年6月,ECMA 标准化组织正式批准了 ECMAScript 6 语言标准,定名为《ECMAScript 2015 标准》。JavaScript 语言正式进入了下一个阶段,成为一种企业级的、开发大规模应用的语言。这个标准从提出到批准,历时10年,而 JavaScript 语言从诞生至今也已经20年了。
2015年6月,Mozilla 在 asm.js 的基础上发布 WebAssembly 项目。这是一种 JavaScript 引擎的中间码格式,全部都是二进制,类似于 Java 的字节码,有利于移动设备加载 JavaScript 脚本,执行速度提高了 20+ 倍。这意味着将来的软件,会发布 JavaScript 二进制包
。
2016年6月,《ECMAScript 2016 标准》发布。与前一年发布的版本相比,它只增加了两个较小的特性。
2017年6月,《ECMAScript 2017 标准》发布,正式引入了 async 函数
,使得异步操作的写法出现了根本的变化。
2017年11月,所有主流浏览器全部支持 WebAssembly
,这意味着任何语言都可以编译成 JavaScript,在浏览器运行。
摘录自:JavaScript 周边大事记。
按照 ECMAScript 6 入门 的建议先把 JavaScript 教程 学习一下,简直让人崩溃…… 快速翻阅了 JavaScript 教程 ,就一个感觉, JavaScript 这门语言太变态了,看了一天,头晕脑胀。
建议将 JavaScript 教程 大概学习一下,直接就使用 ECMAScript 6 入门 的语法和用法,直接学习使用正确的方式,而不是记住很多种不建议的用法!
如果对 js 的运行环境、DOM 和浏览器模型等不熟悉,可以熟读 JavaScript 教程 中的相关章节。
各大浏览器对 ES6 的支持情况:查看。
Babel、Traceur 可以将 ES6 代码转为 ES5 代码。
基本概念分为两部分,第一部分是每种编程语言都会有的基本概念,第二部分是 JavaScript 的运行场景带来的一些特有的属性或操作,主要与浏览器相关。
语句
变量
标识符
注释
区块
条件语句
循环语句
运算符
数据类型转化
错误处理
标准库
面向对象
异步操作
statement,以;
结尾。
用 var 声明(可省略):
undifined
;全局作用域
、函数作用域
,ES6 增加了块级作用域
。identifier,用来标记变量、函数等的名字:
下面这些保留的关键字不能做标识符:
arguments、break、case、catch、class、const、continue、debugger、default、delete、do、else、enum、eval、export、extends、false、finally、for、function、if、implements、import、in、instanceof、interface、let、new、null、package、private、protected、public、return、static、super、switch、this、throw、true、try、typeof、var、void、while、with、yield。
//
;/* */
;<!--
和行首的 -->
也被看作单行注释。block,用 { }
包裹的任意数量的语句,注意 js 中区块不是 var 声明的变量的作用域。
在区块中声明的变量,在区块外可以使用,区块不构成变量的作用域,因此一般也不单独使用:
{
var a = 1;
}
a // 1
if ... else if ...
;switch ... case ...
;true
,伪是 false
,优先使用「严格相等运算符」 ===
; ====
,且需要用 break 跳出;?...:
。if 语句:
if (m === 0) {
// ...
} else if (m === 1) {
// ...
} else if (m === 2) {
// ...
} else {
// ...
}
switch 语句,需要使用 break 终止,case 的判断采用的是 ===
:
switch (fruit) {
case "banana":
// ...
break;
case "apple":
// ...
break;
default:
// ...
}
三元运算符:
var even = (n % 2 === 0) ? true : false;
for
、while
、do...while
;break
跳出、用 continue
跳过;for 语句:
for (var i = 0; i < x; i++) {
console.log(i);
}
for ( ; ; ){
console.log('Hello World');
}
while 语句:
while (i < 100) {
console.log('i 当前为:' + i);
i = i + 1;
}
do {
console.log(i);
i++;
} while(i < x);
跳转到标签:
top:
for (var i = 0; i < 3; i++){
for (var j = 0; j < 3; j++){
if (i === 1 && j === 1) break top;
console.log('i=' + i + ', j=' + j);
}
}
截止 ES6 总过有七种数据类型:
数值
字符串
布尔值
undifined
null
对象,又可以分为普通对象、数组、函数。
Symbol (ES6 新增)
js 的变量是动态类型,可以用下面的方法判断一个值的类型:
typeof 的返回值分别是number
、string
、boolean
、function
、undifined
、object
:
typeof 123 // "number"
typeof '123' // "string"
typeof false // "boolean"
typeof null // "object"
typeof window // "object"
typeof {} // "object"
typeof [] // "object"
...
null
与 undefined
值的含义很接近,在 if 语句中都会被转转成 false,它俩也被 ==
认为相同。undefined 存在是历史原因,谷歌开发的替代语言 Dart 只有 null 没有 undefined:
undefined == null // true
但是,判断一个变量是否存在,typeof 返回的是 undefined,不是 null。
应当使用 boolean 的位置,上面的值自动转换为布尔值。
被转换为 false 的类型:
undefined
null
false
0
NaN
""或''(空字符串)
除此之外,全部是 true。
所有数字都是 64 位的浮点数,1 和 1.0 相同,使用国际标准 IEEE754,符号位 1 位,指数 11 位,小数 52 位。
需要特别注意都是浮点数不是精确值,小数结果需要特别注意:
0.1 + 0.2 === 0.3
// false
0.3 / 0.1
// 2.9999999999999996
(0.3 - 0.2) === (0.2 - 0.1)
// false
能够精确表示的十进制数范围是 -2^53 ~ 2^53
(15位以及以内的十进制数)。
能表示的最大整数是 2^1024,超过这个值时,返回 Infinity:
Math.pow(2, 1024) // Infinity
小于等于 2^(-1075)的数值,返回 0:
Math.pow(2, -1075) // 0
具体的最大值和最小值用下面的方法获取:
Number.MAX_VALUE // 1.7976931348623157e+308
Number.MIN_VALUE // 5e-324
支持科学计数:
123e3 // 123000
123e-3 // 0.123
-3.1E+12
.1e-23
八进制前缀 0o
、0O
,十六进制前缀 0x
、0X
,二进制前缀 0b
、0B
。
js 的零有正负之分,在作为分母的时候,两者是有区别的,结果分别是 +Inifinity
和 -Inifinity
:
(1 / +0) === (1 / -0) // false
NaN 表示非数值,NaN 本身一个 number,不等于任何值包括自己,与任何其它数的运算都是 NaN:
5 - 'x' // NaN
typeof NaN // 'number'
NaN === NaN // false
NaN + 32 // NaN
Infinity 大于 NaN 以外的一切数值,-Inifinity 小于 NaN 以外的一切数值,与 NaN 比较全是返回 false,0 乘以 Infinity 结果是 NaN:
0 * Infinity // NaN
0 / Infinity // 0
Infinity / 0 // Infinity
Infinity + Infinity // Infinity
Infinity * Infinity // Infinity
Infinity - Infinity // NaN
Infinity / Infinity // NaN
undefined + Infinity // NaN
undefined - Infinity // NaN
undefined * Infinity // NaN
undefined / Infinity // NaN
Infinity / undefined // NaN
null * Infinity // NaN
null / Infinity // 0
Infinity / null // Infinity
5 * Infinity // Infinity
5 - Infinity // -Infinity
Infinity / 5 // Infinity
5 / Infinity // 0
全局方法 parsetInt(),第一个参数是要转换的字符串,第二个参数是进制:
parseInt('123') // 123,字符串转换为整数
parseInt('1.23') // 1
parseInt('8a') // 8
parseInt('12**') // 12
parseInt('12.34') // 12
parseInt('15e2') // 15
parseInt('15px') // 15
parseInt('10', 37) // NaN
parseInt('10', 1) // NaN
parseInt('10', 0) // 10
parseInt('10', null) // 10
parseInt('10', undefined) // 10
全局方法 parseFloat(),将一个字符串转换成浮点数:
isNaN() 判断一个值是否为 NaN。
function myIsNaN(value) {
return typeof value === 'number' && isNaN(value);
}
判断NaN更可靠的方法是,利用NaN为唯一不等于自身的值的这个特点,进行判断:
function myIsNaN(value) {
return value !== value;
}
isFinite方法返回一个布尔值,表示某个值是否为正常的数值 Infinity、-Infinity、NaN 和 undefined 以外的值都返回 true。
var s = 'hello';
s.length // 5
输出多行字符串,利用多行注释的变通:
(function () { /*
line 1
line 2
line 3
*/}).toString().split('\n').slice(1, -1).join('\n')
转义字符:
\0 :null(\u0000)
\b :后退键(\u0008)
\f :换页符(\u000C)
\n :换行符(\u000A)
\r :回车键(\u000D)
\t :制表符(\u0009)
\v :垂直制表符(\u000B)
\' :单引号(\u0027)
\" :双引号(\u0022)
\\ :反斜杠(\u005C)
Unicode 字符表示方法:
在非特殊字符前面使用反斜杠,则反斜杠会被省略。
在标识符中使用 Unicode 字符:
var f\u006F\u006F = 'abc';
foo // "abc"
一个四字节字符的长度:
'𝌆'.length // 2
JavaScript 原生提供两个 Base64 相关的方法:
btoa():任意值转为 Base64 编码
atob():Base64 编码转为原来的值
将非 ASCII 码字符转为 Base64 编码,须转码:
function b64Encode(str) {
return btoa(encodeURIComponent(str));
}
function b64Decode(str) {
return decodeURIComponent(atob(str));
}
b64Encode('你好') // "JUU0JUJEJUEwJUU1JUE1JUJE"
b64Decode('JUU0JUJEJUEwJUU1JUE1JUJE') // "你好"
对象定义:
var obj = {
foo: 'Hello',
bar: 'World'
};
读取属性:
var obj = {
p: 'Hello World'
};
obj.p // "Hello World"
obj['p'] // "Hello World"
属性后绑定:
var obj = { p: 1 };
// 等价于
var obj = {};
obj.p = 1;
查看对象所有属性:
var obj = {
key1: 1,
key2: 2
};
Object.keys(obj);
// ['key1', 'key2']
删除对象的属性:
var obj = { p: 1 };
Object.keys(obj) // ["p"]
delete obj.p // true
obj.p // undefined
Object.keys(obj) // []
遍历所有可以遍历的属性:
var obj = {a: 1, b: 2, c: 3};
for (var i in obj) {
console.log('键名:', i);
console.log('键值:', obj[i]);
}
// 键名: a
// 键值: 1
// 键名: b
// 键值: 2
// 键名: c
// 键值: 3
with 语句:
with (obj) {
p1 = 4;
p2 = 5;
}
// 等同于
obj.p1 = 4;
obj.p2 = 5;
建议不要使用 with 语句,可以考虑用一个临时变量代替 with:
with(obj1.obj2.obj3) {
console.log(p1 + p2);
}
// 可以写成
var temp = obj1.obj2.obj3;
console.log(temp.p1 + temp.p2);
function 命令声明函数:
function print(s) {
console.log(s);
}
函数表达式声明函数:
var print = function(s) {
console.log(s);
};
Function 构造函数声明:
var add = new Function(
'x',
'y',
'return x + y'
);
// 等同于
function add(x, y) {
return x + y;
}
函数的 name 属性:
function f1() {}
f1.name // "f1"
以字符串的形式返回函数的代码:
function f() {
a();
b();
c();
}
f.toString()
// function f() {
// a();
// b();
// c();
// }
原生函数返回 [native code]
:
Math.sqrt.toString()
// "function sqrt() { [native code] }"
函数内部修改的,不是参数对象的某个属性,而是替换掉整个参数,不会影响到原始值;
var obj = [1, 2, 3];
function f(o) {
o = [2, 3, 4];
}
f(obj);
obj // [1, 2, 3]
严格模式修改 arguments,对传入参数无影响:
var f = function(a, b) {
'use strict'; // 开启严格模式
arguments[0] = 3;
arguments[1] = 2;
return a + b;
}
f(1, 1) // 2
将 arguments 转换成数组:
var args = Array.prototype.slice.call(arguments);
// 或者
var args = [];
for (var i = 0; i < arguments.length; i++) {
args.push(arguments[i]);
}
var f = function () {
console.log(arguments.callee === f);
}
f() // true
立即执行定义的函数:
(function(){ /* code */ }());
// 或者
(function(){ /* code */ })();
严格模式 eval 不影响外部:
(function f() {
'use strict';
eval('var foo = 123');
console.log(foo); // ReferenceError: foo is not defined
})()
使用别名执行 eval,eval 内部用的是全局作用域:
var a = 1;
function f() {
var a = 2;
var e = eval;
e('console.log(a)');
}
f() // 1
数组的数字键不需要连续:
var arr = ['a', 'b'];
arr.length // 2
arr[2] = 'c';
arr.length // 3
arr[9] = 'd';
arr.length // 10
arr[1000] = 'e';
arr.length // 1001
把类似数组的对象转换成数组:
var arr = Array.prototype.slice.call('abc');
arr.forEach(function (chr) {
console.log(chr);
});
// a
// b
// c
算数运算符: +、-、*、/、**(指数)、%(余数)、++、–
比较运算符:>、<、<=、>=、==、===、!=、!==。
布尔运算符:!、&&、 | 、?…:… |
二进制运算符: | 、&、~、^、«、»、»>(头部补 0 右移动) |
其它运算符:void(执行表达式,不返回任何值)、,(返回后一个表达式的值)。
void 运算符常用语浏览器连接,方法执行,确不产生页面跳转:
<a href="javascript: void(document.form.submit())">
提交
</a>
加法运算符会被重载:
true + true // 2
1 + true // 2
'a' + 'bc' // "abc"
1 + 'a' // "1a"
false + 'a' // "falsea"
'3' + 4 + 5 // "345"
3 + 4 + '5' // "75"
加法外的运算符,所有运算子一律转为数值,再进行相应的数学运算:
1 - '2' // -1
1 * '2' // 2
1 / '2' // 0.5
运算子是对象,必须先转成原始类型的值(obj.valueOf().toString(),调用对象的 valueof() 方法,然后调用toString()),然后再相加:
var obj = { p: 1 };
obj.valueOf().toString() // "[object Object]"
可以自定定义 valueOf 和 toString 方法:
var obj = {
valueOf: function () {
return 1;
}
};
obj + 2 // 3
指数运算符是右结合的:
// 相当于 2 ** (3 ** 2)
2 ** 3 ** 2
// 512
利用位运算符特性,将数值转换成 32 位整数:
function toInt32(x) {
return x | 0;
}
错误实例:
var err = new Error('出错了');
err.message // "出错了"
自定义错误对象:
function UserError(message) {
this.message = message || '默认信息';
this.name = 'UserError';
}
UserError.prototype = new Error();
UserError.prototype.constructor = UserError;
用 new 调用构造函数:
function f() {
console.log(new.target === f);
}
f() // false
new f() // true
以现有对象为模版,创建新的对象:
var person1 = {
name: '张三',
age: 38,
greeting: function() {
console.log('Hi! I\'m ' + this.name + '.');
}
};
var person2 = Object.create(person1);
person2.name // 张三
person2.greeting() // Hi! I'm 张三.
bind 绑定方法:
var print = d.getTime.bind(d);
print() // 1481869925657
原型对象:
function Animal(name) {
this.name = name;
}
Animal.prototype.color = 'white';
var cat1 = new Animal('大毛');
var cat2 = new Animal('二毛');
cat1.color // 'white'
cat2.color // 'white'
父类:
function Shape() {
this.x = 0;
this.y = 0;
}
Shape.prototype.move = function (x, y) {
this.x += x;
this.y += y;
console.info('Shape moved.');
};
子类:
// 第一步,子类继承父类的实例
function Rectangle() {
Shape.call(this); // 调用父类构造函数
}
// 另一种写法
function Rectangle() {
this.base = Shape;
this.base();
}
// 第二步,子类继承父类的原型
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;
多重继承:
function M1() {
this.hello = 'hello';
}
function M2() {
this.world = 'world';
}
function S() {
M1.call(this);
M2.call(this);
}
// 继承 M1
S.prototype = Object.create(M1.prototype);
// 继承链上加入 M2
Object.assign(S.prototype, M2.prototype);
// 指定构造函数
S.prototype.constructor = S;
var s = new S();
s.hello // 'hello'
s.world // 'world'
var timerId = setTimeout(func|code, delay);
var id1 = setTimeout(f, 1000);
var id2 = setInterval(f, 1000);
clearTimeout(id1);
clearInterval(id2);
// f1的异步操作执行完成,就会执行f2。
var p1 = new Promise(f1);
p1.then(f2);
严格模式是从 ES5 进入标准,明确禁止一些不合理、不严谨的语法,减少 JavaScript 语言的一些怪异行为。
进入严格模式方法:
'use strict';
严格模式特点:
没有加分号的代码:
a = b = c = 1
a
++
b
--
c
console.log(a, b, c)
// 1 2 0
等同于:
a = b = c = 1;
a;
++b;
--c;
Object 对象:
对象的拷贝:
function copyObject(orig) {
var copy = Object.create(Object.getPrototypeOf(orig));
copyOwnPropertiesFrom(copy, orig);
return copy;
}
function copyOwnPropertiesFrom(target, source) {
Object
.getOwnPropertyNames(source)
.forEach(function (propKey) {
var desc = Object.getOwnPropertyDescriptor(source, propKey);
Object.defineProperty(target, propKey, desc);
});
return target;
}
ES 2017 引入的写法:
function copyObject(orig) {
return Object.create(
Object.getPrototypeOf(orig),
Object.getOwnPropertyDescriptors(orig)
);
}
属性描述对象:
Array 对象:
包装对象:
Boolean 对象:
Number 对象:
String 对象:
Math 对象:
Date 对象:
RegExp 对象:
JSON 对象:
DOM
事件
浏览器模型
console 是 JavaScript 的原生对象,用于输出信息到控制台,包含以下静态方法:
console.log()
console.info()
console.debug()
console.warn()
console.error()
console.table() // 将复合类型数据以表格形式显示
console.count() // console 计数器,以输入参数为标签,分类计数
console.dir() // 以目录形式显示
console.dirxml() // 以目录树形式显示
console.assert() // 断言,第一个参数是条件,第二个是参数是不成立时的显示值
console.time(),console.timeEnd() //用于计时,参数是计时器名称;
console.group(),console.groupEnd(),console.groupCollapsed() //显示分组边界
console.trace() // 显示调用堆栈
console.clear() // 清空控制台
这些方法会:
console 支持的占位符,其中 %c 必须对应 CSS 代码:
%s 字符串
%d 整数
%i 整数
%f 浮点数
%o 对象的链接
%c CSS 格式字符串
节点类型 7 种: Document、DocumentType、Element、Attribute、Text、Comment、DocumentFragment。
所有 DOM 对象都继承 Node 属性和方法:
Node.prototype.nodeType
Node.prototype.nodeName
Node.prototype.nodeValue
Node.prototype.textContent
Node.prototype.baseURI
Node.prototype.ownerDocument
Node.prototype.nextSibling
Node.prototype.previousSibling
Node.prototype.parentNode
Node.prototype.parentElement
Node.prototype.firstChild,Node.prototype.lastChild
Node.prototype.childNodes
Node.prototype.isConnected
Node.prototype.appendChild()
Node.prototype.hasChildNodes()
Node.prototype.cloneNode()
Node.prototype.insertBefore()
Node.prototype.removeChild()
Node.prototype.replaceChild()
Node.prototype.contains()
Node.prototype.compareDocumentPosition()
Node.prototype.isEqualNode(),Node.prototype.isSameNode()
Node.prototype.normalize()
Node.prototype.getRootNode()
Document 属性和方法:
document.cookie
document.designMode
document.implementation
document.open(),document.close()
document.write(),document.writeln()
document.querySelector(),document.querySelectorAll()
document.getElementsByTagName()
document.getElementsByClassName()
document.getElementsByName()
document.getElementById()
document.elementFromPoint(),document.elementsFromPoint()
document.caretPositionFromPoint()
document.createElement()
document.createTextNode()
document.createAttribute()
document.createComment()
document.createDocumentFragment()
document.createEvent()
document.addEventListener(),document.removeEventListener(),document.dispatchEvent()
document.hasFocus()
document.adoptNode(),document.importNode()
document.createNodeIterator()
document.createTreeWalker()
document.execCommand(),document.queryCommandSupported(),document.queryCommandEnabled()
document.getSelection()
Element 属性和方法:
Element.attributes
Element.className,Element.classList
Element.dataset
Element.innerHTML
Element.outerHTML
Element.clientHeight,Element.clientWidth
Element.clientLeft,Element.clientTop
Element.scrollHeight,Element.scrollWidth
Element.scrollLeft,Element.scrollTop
Element.offsetParent
Element.offsetHeight,Element.offsetWidth
Element.offsetLeft,Element.offsetTop
Element.style
Element.children,Element.childElementCount
Element.firstElementChild,Element.lastElementChild
Element.nextElementSibling,Element.previousElementSibling
Element.querySelector()
Element.querySelectorAll()
Element.getElementsByClassName()
Element.getElementsByTagName()
Element.closest()
Element.matches()
Element.scrollIntoView()
Element.getBoundingClientRect()
Element.getClientRects()
Element.insertAdjacentElement()
Element.insertAdjacentHTML(),Element.insertAdjacentText()
Element.remove()
Element.focus(),Element.blur()
Element.click()
Element.getAttribute()
Element.getAttributeNames()
Element.setAttribute()
Element.hasAttribute()
Element.hasAttributes()
Element.removeAttribute()
Mutation Observer API 用来监视 DOM 变动。DOM 的任何变动,比如节点的增减、属性的变动、文本内容的变动,这个 API 都可以得到通知
EventTarget.addEventListener()
EventTarget.removeEventListener()
EventTarget.dispatchEvent()