JavaScript 基础知识
1 标识符
- 第一个字符,可以是任意 Unicode 字母(包括英文字母和其他语言的字母),以及美元符号($)和下划线(_)
- 第二个字符及后面的字符,除了 Unicode 字母、美元符号和下划线,还可以用数字0-9
- 大小写敏感
- JavaScript 有一些保留字,不能用作标识符
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。
2 标签
- JavaScript 语言允许,语句的前面有标签(label),相当于定位符,用于跳转到程序的任意位置
label:
语句
foo: {
console.log(1);
break foo;
console.log('本行不会输出');
}
console.log(2);
3 数据类型
- 通常,数值、字符串、布尔值这三种类型,合称为原始类型(primitive type)的值,即它们是最基本的数据类型,不能再细分了。对象则称为合成类型(complex type)的值,因为一个对象往往是多个原始类型的值的合成,可以看作是一个存放各种值的容器。至于undefined和null,一般将它们看成两个特殊值。
- 对象是最复杂的数据类型,又可以分成三个子类型。
1 狭义的对象(object)
2 数组(array)
3 函数(function)
number
string
boolean
undefined
null
object
array
function
4 typeof 运算符
- JavaScript 有三种方法,可以确定一个值到底是什么类型。
1 typeof 运算符
2 instanceof 运算符
3 Object.prototype.toString 方法
// 数值、字符串、布尔值分别返回number、string、boolean
typeof 123 // "number"
typeof '123' // "string"
typeof false // "boolean"
// 函数返回function
function f() {}
typeof f // "function"
// undefined返回undefined
typeof undefined // "undefined"
// 对象返回object
typeof window // "object"
typeof {} // "object"
typeof [] // "object"
// 数组属于一种特殊的对象,特殊性体现在,它的键名是按次序排列的一组整数(0,1,2...)
// null返回object,由于历史原因造成
typeof null // "object"
5 boolean
- 如果 JavaScript 预期某个位置应该是布尔值,会将该位置上现有的值自动转为布尔值。转换规则是除了下面六个值被转为false,其他值都视为true。
undefined
null
false
0
NaN
""或''(空字符串)
- 注意,空数组(
[])和空对象({})对应的布尔值,都是true
6 Number
// parseInt方法用于将字符串转为整数
parseInt('123') // 123
// parseFloat方法用于将一个字符串转为浮点数
parseFloat('3.14') // 3.14
7 字符串
// length属性返回字符串的长度
var s = 'hello';
s.length // 5
8 Base64 转码
- 所谓 Base64 是一种编码方法,可以将任意值转成 0~9、A~Z、a-z、+和/这64个字符组成的可打印字符
- btoa():任意值转为 Base64 编码
- atob():Base64 编码转为原来的值
- 将非 ASCII 码字符转为 Base64 编码,必须中间插入一个转码环节,再使用这两个方法。
var string = 'Hello World!';
btoa(string) // "SGVsbG8gV29ybGQh"
atob('SGVsbG8gV29ybGQh') // "Hello World!"
function b64Encode(str) {
return btoa(encodeURIComponent(str));
}
function b64Decode(str) {
return decodeURIComponent(atob(str));
}
b64Encode('你好') // "JUU0JUJEJUEwJUU1JUE1JUJE"
b64Decode('JUU0JUJEJUEwJUU1JUE1JUJE') // "你好"
9 对象
- 对象就是一组“键值对”(key-value)的集合,是一种无序的复合数据集合
- 对象的所有键名都是字符串,所以加不加引号都可以。但是不符合标识符的键需要加上引号,否则会报错
// 属性可以动态创建,不必在对象声明时就指定
var obj = {};
obj.foo = 123;
obj.foo // 123
//如果属性的值还是一个对象,就形成了链式引用
var o1 = {};
var o2 = { bar: 'hello' };
o1.foo = o2;
o1.foo.bar // "hello"
// 查看一个对象本身的所有属性,可以使用Object.keys方法
var obj = {
key1: 1,
key2: 2
};
Object.keys(obj); // ['key1', 'key2']
// delete命令用于删除对象的属性,删除成功后返回true。注意,删除一个不存在的属性,delete不报错,而且返回true
var obj = { p: 1 };
Object.keys(obj) // ["p"]
delete obj.p // true
obj.p // undefined
Object.keys(obj) // []
var obj = {};
delete obj.p // true
// in运算符用于检查对象是否包含某个属性,如果包含就返回true,否则返回false
var obj = { p: 1 };
'p' in obj // true
'toString' in obj // true
10 函数
- 函数执行时所在的作用域,是定义时的作用域,而不是调用时所在的作用域
function 函数名(参数)
{
函数代码
return 返回值
}
11 运算符
// 加法运算符是在运行时决定,到底是执行相加,还是执行连接。也就是说,运算子的不同,导致了不同的语法行为,这种现象称为“重载”(overload)。由于加法运算符存在重载,可能执行两种运算,使用的时候必须很小心。
'3' + 4 + 5 // "345"
3 + 4 + '5' // "75"
// 余数运算结果的正负号由第一个运算子的正负号决定
-1 % 2 // -1
1 % -2 // 1
// 自增和自减,放在变量之后,会先返回变量操作前的值,再进行自增/自减操作;放在变量之前,会先进行自增/自减操作,再返回变量操作后的值。
var x = 1;
var y = 1;
x++ // 1
x //2
++y // 2
y // 2
// 数值运算符的作用在于可以将任何值转为数值(与Number函数的作用相同),数值运算符号和负数值运算符,都会返回一个新的值,而不会改变原始变量的值。
+true // 1
+[] // 0
+{} // NaN
// 指数运算符(**)完成指数运算,前一个运算子是底数,后一个运算子是指数,右结合
2 ** 3 ** 2 // 512,相当于 2 ** (3 ** 2)
// 右结合还有:赋值运算符(=)和三元条件运算符(?:)。
// 对象转换成原始类型的值,算法是先调用valueOf方法;如果返回的还是对象,再接着调用toString方法
// 相等运算符(==)比较两个值是否相等,严格相等运算符(===)比较它们是否为“同一个值”。如果两个值不是同一类型,严格相等运算符(===)直接返回false,而相等运算符(==)会将它们转换成同一个类型,再用严格相等运算符进行比较。
// 以下六个值取反后为true,其他值都为false。
undefined
null
false
0
NaN
空字符串('')
!undefined // true
!null // true
!0 // true
!NaN // true
!"" // true
// && 如果第一个运算子的布尔值为true,则返回第二个运算子的值(注意是值,不是布尔值);如果第一个运算子的布尔值为false,则直接返回第一个运算子的值,且不再对第二个运算子求值。
// || 如果第一个运算子的布尔值为true,则返回第一个运算子的值,且不再对第二个运算子求值;如果第一个运算子的布尔值为false,则返回第二个运算子的值。
12 强制转换数据类型
- 强制转换主要指使用
Number()、String()和Boolean()三个函数,手动将各种类型的值,分别转换成数字、字符串或者布尔值。
// 使用Number函数,可以将任意类型的值转化成数值
// 原始类型
// 数值:转换后还是原来的值
Number(324) // 324
// 字符串:如果可以被解析为数值,则转换为相应的数值
Number('324') // 324
// 字符串:如果不可以被解析为数值,返回 NaN
Number('324abc') // NaN
// 空字符串转为0
Number('') // 0
// 布尔值:true 转成 1,false 转成 0
Number(true) // 1
Number(false) // 0
// undefined:转成 NaN
Number(undefined) // NaN
// null:转成0
Number(null) // 0
// 对象
// Number方法的参数是对象时,将返回NaN,除非是包含单个数值的数组。
Number({a: 1}) // NaN
Number([1, 2, 3]) // NaN
Number([5]) // 5
// 原始类型
// String函数可以将任意类型的值转化成字符串
String(123) // "123"
String('abc') // "abc"
String(true) // "true"
String(undefined) // "undefined"
String(null) // "null"
// 对象
// String方法的参数如果是对象,返回一个类型字符串;如果是数组,返回该数组的字符串形式。
String({a: 1}) // "[object Object]"
String([1, 2, 3]) // "1,2,3"
// Boolean()函数可以将任意类型的值转为布尔值。除了以下六个值的转换结果为false,其他的值全部为true
undefined
null
0(包含-0和+0)
NaN
''(空字符串)
false
Boolean(undefined) // false
Boolean(null) // false
Boolean(0) // false
Boolean(NaN) // false
Boolean('') // false
Boolean(false) // false
Boolean(true) // true
// 将表达式转换为布尔值
// 写法一
expression ? true : false
// 写法二
!! expression
13 Object 对象
// 以构造函数形式来调用
new Object([value])
// 访问对象的属性
objectName.propertyName
// 访问对象的方法
objectName.methodName()
14 属性描述对象
- JavaScript 提供了一个内部数据结构,用来描述对象的属性,控制它的行为,比如该属性是否可写、可遍历等等。这个内部数据结构称为“属性描述对象”(attributes object)。每个属性都有自己对应的属性描述对象,保存该属性的一些元信息。
// 属性描述对象提供6个元属性。
{
value: 123,
writable: false,
enumerable: true,
configurable: false,
get: undefined,
set: undefined
}
// 属性描述对象的各个属性称为“元属性”,因为它们可以看作是控制属性的属性。
// Object.getOwnPropertyDescriptor()方法可以获取属性描述对象。它的第一个参数是目标对象,第二个参数是一个字符串,对应目标对象的某个属性名。Object.getOwnPropertyDescriptor()方法只能用于对象自身的属性,不能用于继承的属性。
var obj = { p: 'a' };
Object.getOwnPropertyDescriptor(obj, 'p')
// Object { value: "a",
// writable: true,
// enumerable: true,
// configurable: true
// }
// 存取器:存值函数称为setter,使用属性描述对象的set属性;取值函数称为getter,使用属性描述对象的get属性。一旦对目标属性定义了存取器,那么存取的时候,都将执行对应的函数。
// 控制对象状态:有时需要冻结对象的读写状态,防止对象被改变。JavaScript 提供了三种冻结方法,最弱的一种是Object.preventExtensions,其次是Object.seal,最强的是Object.freeze。
// Object.preventExtensions方法可以使得一个对象无法再添加新的属性
var obj = new Object();
Object.isExtensible(obj) // true
Object.preventExtensions(obj);
Object.isExtensible(obj) // false
// bject.seal方法使得一个对象既无法添加新属性,也无法删除旧属性,Object.seal实质是把属性描述对象的configurable属性设为false。但是并不影响修改某个属性的值,因为已存在属性的可写性由writable决定。
var obj = { p: 'hello' };
Object.seal(obj);
delete obj.p;
obj.p // "hello"
obj.x = 'world';
obj.x // undefined
// Object.freeze方法可以使得一个对象无法添加新属性、无法删除旧属性、也无法改变属性的值,使得这个对象实际上变成了常量。
15 Array 对象
var arr = [1, 2];
// push方法用于在数组的末端添加一个或多个元素,并返回添加新元素后的数组长度。注意,该方法会改变原数组。pop方法用于删除数组的最后一个元素,并返回该元素。注意,该方法会改变原数组。push和pop结合使用,就构成了“后进先出”的栈结构(stack)
var arr = [];
arr.push(1, 2);
arr.push(3);
arr.pop();
arr // [1, 2]
// shift()方法用于删除数组的第一个元素,并返回该元素。注意,该方法会改变原数组。push()和shift()结合使用,就构成了“先进先出”的队列结构(queue)
var a = ['a', 'b', 'c'];
a.shift() // 'a'
a // ['b', 'c']
// unshift()方法用于在数组的第一个位置添加元素,并返回添加新元素后的数组长度。注意,该方法会改变原数组。
var arr = [ 'c', 'd' ];
arr.unshift('a', 'b') // 4
arr // [ 'a', 'b', 'c', 'd' ]
// join()方法以指定参数作为分隔符,将所有数组成员连接为一个字符串返回。如果不提供参数,默认用逗号分隔。如果数组成员是undefined或null或空位,会被转成空字符串。
[undefined, null].join('#') // '#'
['a',, 'b'].join('-') // 'a--b'
// concat方法用于多个数组的合并。它将新数组的成员,添加到原数组成员的后部,然后返回一个新数组,原数组不变。
['hello'].concat(['world'], ['!'])
// ["hello", "world", "!"]
[].concat({a: 1}, {b: 2})
// [{ a: 1 }, { b: 2 }]
// 如果数组成员包括对象,concat方法返回当前数组的一个浅拷贝。所谓“浅拷贝”,指的是新数组拷贝的是对象的引用。
var obj = { a: 1 };
var oldArray = [obj];
var newArray = oldArray.concat();
obj.a = 2;
newArray[0].a // 2
// reverse方法用于颠倒排列数组元素,返回改变后的数组。注意,该方法将改变原数组。
var a = ['a', 'b', 'c'];
a.reverse() // ["c", "b", "a"]
a // ["c", "b", "a"]
// slice()方法用于提取目标数组的一部分,返回一个新数组,原数组不变。左闭右开
arr.slice(start, end);
var a = ['a', 'b', 'c'];
a.slice(0) // ["a", "b", "c"]
a.slice(1) // ["b", "c"]
a.slice(1, 2) // ["b"]
a.slice(2, 6) // ["c"]
a.slice() // ["a", "b", "c"]
a.slice(-2) // ["b", "c"]
a.slice(-2, -1) // ["b"]
// sort方法对数组成员进行排序,默认是按照字典顺序排序。排序后,原数组将被改变。
['d', 'c', 'b', 'a'].sort()
// ['a', 'b', 'c', 'd']
[4, 3, 2, 1].sort()
// [1, 2, 3, 4]
[11, 101].sort()
// [101, 11]
[10111, 1101, 111].sort()
// [10111, 1101, 111]
// 如果想让sort方法按照自定义方式排序,可以传入一个函数作为参数。
[
{ name: "张三", age: 30 },
{ name: "李四", age: 24 },
{ name: "王五", age: 28 }
].sort(function (o1, o2) {
return o1.age - o2.age;
})
// [
// { name: "李四", age: 24 },
// { name: "王五", age: 28 },
// { name: "张三", age: 30 }
// ]
// map()方法将数组的所有成员依次传入参数函数,然后把每一次的执行结果组成一个新数组返回。
var numbers = [1, 2, 3];
numbers.map(function (n) {
return n + 1;
});
// [2, 3, 4]
numbers
// [1, 2, 3]
// map()方法接受一个函数作为参数。该函数调用时,map()方法向它传入三个参数:当前成员、当前位置和数组本身
[1, 2, 3].map(function(elem, index, arr) {
return elem * index;
});
// [0, 2, 6]
// map()方法还可以接受第二个参数,用来绑定回调函数内部的this变量
var arr = ['a', 'b', 'c'];
[1, 2].map(function (e) {
return this[e];
}, arr)
// ['b', 'c']
// forEach()方法与map()方法很相似,也是对数组的所有成员依次执行参数函数。但是,forEach()方法不返回值,只用来操作数据。这就是说,如果数组遍历的目的是为了得到返回值,那么使用map()方法,否则使用forEach()方法。forEach()的用法与map()方法一致,参数是一个函数,该函数同样接受三个参数:当前值、当前位置、整个数组。
function log(element, index, array) {
console.log('[' + index + '] = ' + element);
}
[2, 5, 9].forEach(log);
// [0] = 2
// [1] = 5
// [2] = 9
// forEach()方法也可以接受第二个参数,绑定参数函数的this变量。
var out = [];
[1, 2, 3].forEach(function(elem) {
this.push(elem * elem);
}, out);
out // [1, 4, 9]
// forEach()方法无法中断执行,总是会将所有成员遍历完。如果希望符合某种条件时,就中断遍历,要使用for循环。
// filter()方法用于过滤数组成员,满足条件的成员组成一个新数组返回。它的参数是一个函数,所有数组成员依次执行该函数,返回结果为true的成员组成一个新数组返回。该方法不会改变原数组。
[1, 2, 3, 4, 5].filter(function (elem) {
return (elem > 3);
})
// [4, 5]
// filter()方法的参数函数可以接受三个参数:当前成员,当前位置和整个数组。
[1, 2, 3, 4, 5].filter(function (elem, index, arr) {
return index % 2 === 0;
});
// [1, 3, 5]
// filter()方法还可以接受第二个参数,用来绑定参数函数内部的this变量。
var obj = { MAX: 3 };
var myFilter = function (item) {
if (item > this.MAX) return true;
};
var arr = [2, 8, 3, 4, 1, 3, 2, 9];
arr.filter(myFilter, obj) // [8, 4, 9]
// indexOf方法返回给定元素在数组中第一次出现的位置,如果没有出现则返回-1。indexOf方法还可以接受第二个参数,表示搜索的开始位置。
var a = ['a', 'b', 'c'];
a.indexOf('b') // 1
a.indexOf('y') // -1
a.indexOf('a', 1) // -1
// lastIndexOf方法返回给定元素在数组中最后一次出现的位置,如果没有出现则返回-1
var a = [2, 5, 9, 2];
a.lastIndexOf(2) // 3
a.lastIndexOf(7) // -1
16 包装对象
// 所谓“包装对象”,指的是与数值、字符串、布尔值分别相对应的Number、String、Boolean三个原生对象。这三个原生对象可以把原始类型的值变成(包装成)对象。
// 这三个对象作为构造函数使用(带有new)时,可以将原始类型的值转为对象;作为普通函数使用时(不带有new),可以将任意类型的值,转为原始类型的值。
// 字符串转为数值
Number('123') // 123
// 数值转为字符串
String(123) // "123"
// 数值转为布尔值
Boolean(123) // true
// valueOf()方法返回包装对象实例对应的原始类型的值
new Number(123).valueOf() // 123
new String('abc').valueOf() // "abc"
new Boolean(true).valueOf() // true
// toString()方法返回对应的字符串形式。
new Number(123).toString() // "123"
new String('abc').toString() // "abc"
new Boolean(true).toString() // "true"
// 包装对象还可以自定义方法和属性,供原始类型的值直接调用。在它的原型对象上定义
String.prototype.double = function () {
return this.valueOf() + this.valueOf();
};
'abc'.double()
// abcabc
Number.prototype.double = function () {
return this.valueOf() + this.valueOf();
};
(123).double() // 246
17 定时器
// setTimeout函数用来指定某个函数或某段代码,在多少毫秒之后执行。它返回一个整数,表示定时器的编号,以后可以用来取消这个定时器。
var timerId = setTimeout(func|code, delay);
// setInterval指定某个任务每隔一段时间就执行一次,也就是无限次的定时执行。
var hash = window.location.hash;
var hashWatcher = setInterval(function() {
if (window.location.hash != hash) {
updatePage();
}
}, 1000);
// setTimeout和setInterval函数,都返回一个整数值,表示计数器编号。将该整数传入clearTimeout和clearInterval函数,就可以取消对应的定时器。
var id1 = setTimeout(f, 1000);
var id2 = setInterval(f, 1000);
clearTimeout(id1);
clearInterval(id2);
// NodeList可以包含各种类型的节点,HTMLCollection只能包含 HTML 元素节点。
// Node.childNodes返回的是一个动态集合,其他的 NodeList 都是静态集合
// HTMLCollection没有forEach方法,只能使用for循环遍历。HTMLCollection实例都是动态集合,节点的变化会实时反映在集合中。
18 Document 节点
document.links instanceof HTMLCollection // true
document.images instanceof HTMLCollection // true
document.forms instanceof HTMLCollection // true
document.embeds instanceof HTMLCollection // true
document.scripts instanceof HTMLCollection // true
// 打印文档所有的链接
var links = document.links;
for(var i = 0; i < links.length; i++) {
console.log(links[i]);
}
// 打印文档所有图片链接
var imglist = document.images;
for(var i = 0; i < imglist.length; i++) {
console.log(imglist[i].src)
}
}
// HTML 标签名是大小写不敏感的,因此getElementsByTagName()方法的参数也是大小写不敏感的。
var paras = document.getElementsByTagName('p');
paras instanceof HTMLCollection // true
// 参数可以是多个class,它们之间使用空格分隔。
var elements = document.getElementsByClassName('foo bar');
// document.getElementsByName()方法用于选择拥有name属性的 HTML 元素(比如<form>、<radio>、<img>、<frame>、<embed>和<object>等),返回一个类似数组的的对象(NodeList实例)
// 注意,该方法的参数是大小写敏感的。比如,如果某个节点的id属性是main,那么document.getElementById('Main')将返回null。
var elem = document.getElementById('para1');
// document.createElement方法用来生成元素节点,并返回该节点。document.createElement的参数可以是自定义的标签名。
var newDiv = document.createElement('div');
document.createElement('foo');
19 Element 节点
Element节点对象对应网页的 HTML 元素。每一个 HTML 元素,在 DOM 树上都会转化成一个Element节点对象
// 视口高度(即浏览器窗口的高度)
document.documentElement.clientHeight
// 网页总高度
document.body.clientHeight
// 返回网页的总高度
document.documentElement.scrollHeight
document.body.scrollHeight
// 查看整张网页的水平的和垂直的滚动距离
document.documentElement.scrollLeft
document.documentElement.scrollTop
20 网页添加样式表
// 一种是添加一张内置样式表,即在文档中添加一个<style>节点。
// 写法一
var style = document.createElement('style');
style.setAttribute('media', 'screen');
style.innerHTML = 'body{color:red}';
document.head.appendChild(style);
// 写法二
var style = (function () {
var style = document.createElement('style');
document.head.appendChild(style);
return style;
})();
style.sheet.insertRule('.foo{color:red;}', 0);
// 另一种是添加外部样式表,即在文档中添加一个<link>节点,然后将href属性指向外部样式表的 URL。
var linkElm = document.createElement('link');
linkElm.setAttribute('rel', 'stylesheet');
linkElm.setAttribute('type', 'text/css');
linkElm.setAttribute('href', 'reset-min.css');
document.head.appendChild(linkElm);
21 Mutation Observer API
- 它等待所有脚本任务完成后,才会运行(即异步触发方式)。
- 它把 DOM 变动记录封装成一个数组进行处理,而不是一条条个别处理 DOM 变动。
- 它既可以观察 DOM 的所有类型变动,也可以指定只观察某一类变动。
// 读取子元素变动记录
var callback = function (records){
records.map(function(record){
console.log('Mutation type: ' + record.type);
console.log('Mutation target: ' + record.target);
});
};
var mo = new MutationObserver(callback);
var option = {
'childList': true,
'subtree': true
};
mo.observe(document.body, option);
// 追踪属性变动
var callback = function (records) {
records.map(function (record) {
console.log('Previous attribute value: ' + record.oldValue);
});
};
var mo = new MutationObserver(callback);
var element = document.getElementById('#my_element');
var options = {
'attributes': true,
'attributeOldValue': true
}
mo.observe(element, options);
22 Event 对象
- 事件发生以后,会产生一个事件对象,作为参数传给监听函数。浏览器原生提供一个Event对象,所有的事件都是这个对象的实例,或者说继承了Event.prototype对象。
// 只读属性返回一个布尔值,表示当前事件是否会冒泡。
Event.bubbles
// 只读属性返回一个整数常量0-3,表示事件目前所处的阶段
// 0,事件目前没有发生。
// 1,事件目前处于捕获阶段,即处于从祖先节点向目标节点的传播过程中。
// 2,事件到达目标节点,即Event.target属性指向的那个节点。
// 3,事件处于冒泡阶段,即处于从目标节点向祖先节点的反向传播过程中。
Event.eventPhase
// 取消事件
function preventEvent(event) {
if (event.cancelable) {
event.preventDefault();
} else {
console.warn('This event couldn\'t be canceled.');
console.dir(event);
}
}
// 属性返回事件当前所在的节点,即事件当前正在通过的节点,也就是当前正在执行的监听函数所在的那个节点。随着事件的传播,这个属性的值会变。
Event.currentTarget
// 属性返回原始触发事件的那个节点,即事件最初发生的节点。这个属性不会随着事件的传播而改变。
Event.target
23 鼠标事件
click://按下鼠标(通常是按下主按钮)时触发。
dblclick://在同一个元素上双击鼠标时触发。
mousedown://按下鼠标键时触发。
mouseup://释放按下的鼠标键时触发。
mousemove://当鼠标在一个节点内部移动时触发。当鼠标持续移动时,该事件会连续触发。为了避免性能问题,建议对该事件的监听函数做一些限定,比如限定一段时间内只能运行一次。
mouseenter://鼠标进入一个节点时触发,进入子节点不会触发这个事件(详见后文)。
mouseover://鼠标进入一个节点时触发,进入子节点会再一次触发这个事件(详见后文)。
mouseout://鼠标离开一个节点时触发,离开父节点也会触发这个事件(详见后文)。
mouseleave://鼠标离开一个节点时触发,离开父节点不会触发这个事件(详见后文)。
contextmenu://按下鼠标右键时(上下文菜单出现前)触发,或者按下“上下文菜单键”时触发。
wheel://滚动鼠标的滚轮时触发,该事件继承的是WheelEvent接口。
24 键盘事件
keydown://按下键盘时触发。
keypress://按下有值的键时触发,即按下 Ctrl、Alt、Shift、Meta 这样无值的键,这个事件不会触发。对于有值的键,按下时先触发keydown事件,再触发这个事件。
keyup://松开键盘时触发该事件。
25 Storage 接口
sessionStorage保存的数据用于浏览器的一次会话(session),当会话结束(通常是窗口关闭),数据被清空;localStorage保存的数据长期存在,下一次访问该网站的时候,网页可以直接读取以前保存的数据。除了保存期限的长短不同,这两个对象的其他方面都一致。
- 保存的数据都以“键值对”的形式存在。也就是说,每一项数据都有一个键名和对应的值。所有的数据都是以文本格式保存。
- 这个接口很像 Cookie 的强化版,能够使用大得多的存储空间。
// Storage.length:返回保存的数据项个数。
window.localStorage.length
// Storage.setItem()方法用于存入数据。它接受两个参数,第一个是键名,第二个是保存的数据。如果键名已经存在,该方法会更新已有的键值。该方法没有返回值。
window.sessionStorage.setItem('key', 'value');
window.localStorage.setItem('key', 'value');
// 下面三种写法等价
window.localStorage.foo = '123';
window.localStorage['foo'] = '123';
window.localStorage.setItem('foo', '123');
// Storage.getItem()方法用于读取数据。它只有一个参数,就是键名。如果键名不存在,该方法返回null。
window.sessionStorage.getItem('key')
window.localStorage.getItem('key')
// Storage.removeItem()方法用于清除某个键名对应的键值。它接受键名作为参数,如果键名不存在,该方法不会做任何事情。
sessionStorage.removeItem('key');
localStorage.removeItem('key');
// Storage.clear()方法用于清除所有保存的数据。该方法的返回值是undefined。
window.sessionStorage.clear()
window.localStorage.clear()
// Storage.key()方法接受一个整数作为参数(从零开始),返回该位置对应的键名。
for (var i = 0; i < window.localStorage.length; i++) {
console.log(localStorage.key(i));
}
// Storage 接口储存的数据发生变化时,会触发 storage 事件,可以指定这个事件的监听函数。监听函数接受一个event实例对象作为参数。
function onStorageChange(e) {
console.log(e.key);
}
window.addEventListener('storage', onStorageChange);
26 History 对象
- History.length:当前窗口访问过的网址数量(包括当前网页)
- History.state:History 堆栈最上层的状态值(详见下文)
History.back():移动到上一个网址,等同于点击浏览器的后退键。对于第一个访问的网址,该方法无效果。
History.forward():移动到下一个网址,等同于点击浏览器的前进键。对于最后一个访问的网址,该方法无效果。
History.go():接受一个整数作为参数,以当前网址为基准,移动到参数指定的网址,比如go(1)相当于forward(),go(-1)相当于back()。如果参数超过实际存在的网址范围,该方法无效果;如果不指定参数,默认参数为0,相当于刷新当前页面
// 当前窗口访问过多少个网页
window.history.length // 1
// History 对象的当前状态
// 通常是 undefined,即未设置
window.history.state // undefined
history.back();
history.forward();
history.go(-2);
history.go(0); // 刷新当前页面
27 ES6 新语法
27.1 定义变量
var
// 全局作用域、函数作用域
function
let
// let所声明的变量,只在let命令所在的代码块内有效。块级作用域。
const
// const声明一个只读的常量。一旦声明,常量的值就不能改变。const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。
import
class
27.2 解构赋值
- 解构失败,变量的值等于
undefined。
- 默认值生效的条件是,对象的属性值严格等于
undefined。
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3
// 数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
let { baz } = { foo: 'aaa', bar: 'bbb' };
baz // undefined
let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
f // 'hello'
l // 'world'
// 变量的解构赋值的主要用途
// 交换变量的值
let x = 1;
let y = 2;
[x, y] = [y, x];
// 获取函数返回的多个值
// 返回一个数组
function example() {
return [1, 2, 3];
}
let [a, b, c] = example();
// 返回一个对象
function example() {
return {
foo: 1,
bar: 2
};
}
let { foo, bar } = example();
// 提取json数据
let jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
};
let { id, status, data: number } = jsonData;
console.log(id, status, number);
// 42, "OK", [867, 5309]
27.3 字符串新增
// 模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。模板字符串中嵌入变量,需要将变量名写在${}之中。
String.fromCodePoint()
String.fromCodePoint(0x20BB7)
// "𠮷"
// codePointAt()方法是测试一个字符由两个字节还是由四个字节组成的最简单方法。
function is32Bit(c) {
return c.codePointAt(0) > 0xFFFF;
}
is32Bit("𠮷") // true
is32Bit("a") // false
let s = 'Hello world!';
s.startsWith('Hello') // true
s.endsWith('!') // true
s.includes('o') // true
27.4 正则表达式
// ES6 出现之前,字符串对象共有 4 个方法,可以使用正则表达式:match()、replace()、search()和split()
27.5 箭头函数
var f = () => 5;
// 等同于
var f = function () { return 5 };
var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
return num1 + num2;
};
27.6 尾调用