闲话
根据记忆曲线,#¥……&……@,反正不经常复习使用,忘记一个东西就很快,所有,在这写一个js知识列表,方便自己复习。
该列表和廖雪峰老师的教程目录一样,非常感谢廖雪峰老师提供这么好的教程。
基础
基本语法
- 四格缩进
- 注释使用//, 多行/* */
数据类型和运算符
Number
布尔值
string
转义字符(反斜杠)
多行字符
字符串连接及模板字符
1
2
3
4var name = '小明';
var age = 20;
var message = `你好, ${name}, 你今年${age}岁了!`;
alert(message);字符串操作
- 索引取值/length获取长度
- toUpperCase, toLowerCase, indexOf, subString
数组
- length取得Array的长度
- 可以通过索引把对应的元素修改为新的值
- 且如果通过索引赋值时,索引超过了范围,同样会引起Array大小的变化
- indexOf, slice, push/pop, unshift/shift, sort, reverse, splice, concat, join
- 多维数组
对象
- 命名和调用
- 检测对象是否拥有某一属性,可以用in操作符
1
2
3
4
5
6var xiaoming = {
name: '小明',
birth: 1990
};
'name' in xiaoming; // true
'grade' in xiaoming; // false
逻辑运算符
比较运算符
null: null表示一个“空”的值,它和0以及空字符串’’不同,0是一个数值,’’表示长度为0的字符串,而null表示“空”
undefined: 表示“未定义”
- 区分两者的意义不大。大多数情况下,我们都应该用null。undefined仅仅在判断函数参数是否传递的情况下有用。
条件判断
循环
- for/while/do{}while
- for (… in …)
- 把一个对象的属性依此循环出来,也可以是数组(循环出index)
Map&Set
Map是一组键值对的结构,具有几块的查找速度
创建
new Map(二维数组/或空)
1
var m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]);
常见使用
1
2
3
4
5
6
7var m = new Map(); // 空Map
m.set('Adam', 67); // 添加新的key-value
m.set('Bob', 59);
m.has('Adam'); // 是否存在key 'Adam': true
m.get('Adam'); // 67
m.delete('Adam'); // 删除key 'Adam'
m.get('Adam'); // undefined
set和map类似,但是一组key的组合
- 不能更改,只能add()和delete()
- 创建
- new Set(一维数组/或空)
iterable
- 可迭代对象,Array,Map,Set均为iterable类型
- for (.. of ..)
- forEach()
1
2
3
4var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
m.forEach(function (value, key, map) {
console.log(value);
});
函数
函数的定义与调用
额外参数arguments,指向函数调用者传入的所有参数
- 最常用于判断传入参数的个数
rest参数只能写在最后,前面用…标识,从运行结果可知,传入的参数先绑定a、b,多余的参数以数组形式交给变量rest;如果传入的参数连正常定义的参数都没填满,也不要紧,rest参数会接收一个空数组(注意不是undefined)
1
2
3
4
5function foo(a, b, ...rest) {
console.log('a = ' + a);
console.log('b = ' + b);
console.log(rest);
}JavaScript引擎有一个在行末自动添加分号的机制,这可能让你栽到return语句的一个大坑
变量作用域和解构赋值
- 查找变量机制
- 变量提升
- 全局作用域
- 名字空间(解决不同文件相同全局变量的命名冲突)
- 局部作用域
- var 作用域,函数内部
- let for循环等语句块
- 常量const
- 可以进行赋值,但是不会生效
- 解构赋值
- 嵌套层次和位置
- 将变量名变化和对应属性名不一致(用冒号)
- 若属性不存在,会得到undefined,可以通过赋初值来解决(用等号)
- 对已声明变量的赋值(用括号包裹)
- 应用
- 对象的快速赋值
- 函数传参
方法
对象中绑定函数,称为该对象的方法
重点: this关键字的指向
- apply()或call()控制this指向
- 装饰器
高阶函数
map()
map方法,传入一个函数,然后作用在调用map的数组中每个元素上,并返回一个新的数组
reduce()
Array的reduce()把一个函数作用在这个Array的[x1, x2, x3…]上,这个函数必须接收两个参数,reduce()把结果继续和序列的下一个元素做累积计算,其效果就是:
1
[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)
filter()
filter也是一个常用的操作,它用于把Array的某些元素过滤掉,然后返回剩下的元素。
Array的filter()也接收一个函数。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是true还是false决定保留还是丢弃该元素。
filter()接收的回调函数,其实可以有多个参数。通常我们仅使用第一个参数,表示Array的某个元素。回调函数还可以接收另外两个参数,表示元素的位置和数组本身
sort()
- 可传入自定义排序函数
- 坑: Array的sort()方法默认把所有元素先转换为String再排序!!!
闭包
我们在函数parent中又定义了函数child,并且,内部函数child可以引用外部函数parent的参数和局部变量,当parent返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。
特性: 实现了状态的保存和隐藏
箭头函数
不仅是匿名函数的一种简写,还有一个明显的区别————this
generator
生成器,与python中generator的概念和语法比较想象
标准对象
Date
再JavaScript中,用Date对象来表示日期和时间
- 获取当前系统时间
- 如果要创建一个指定日期和时间的Date对象
- 可以传入三种格式
- 易错: 月份0-11
RegExp
JSON
- JSON的字符串规定必须用双引号””,Object的键也必须用双引号””
- 对象序列化成一个JSON格式的字符串
- 格式化
- 精确控制如何序列化对象
- 将JSON格式的字符串,只需要把它反序列化成一个JavaScript对象
面向对象的编程
class的使用及继承
通过改变原型创建
JavaScript的原型链和Java的Class区别就在,它没有“Class”的概念,所有对象都是实例,所谓继承关系不过是把一个对象的原型指向另一个对象而已。
当我们用obj.xxx访问一个对象的属性时,JavaScript引擎先在当前对象上查找该属性,如果没有找到,就到其原型对象上找,如果还没有找到,就一直上溯到Object.prototype对象,最后,如果还没有找到,就只能返回undefined。
新的关键字class从ES6开始正式被引入到JavaScript中。class的目的就是让定义类更简单。
和C语言的类定义方法和继承方法相近
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// Student类
class Student {
constructor(name) {
this.name = name;
}
hello() {
alert('Hello, ' + this.name + '!');
}
}
// PrimaryStudent子类
class PrimaryStudent extends Student {
constructor(name, grade) {
super(name); // 记得用super调用父类的构造方法!
this.grade = grade;
}
myGrade() {
alert('I am at grade ' + this.grade);
}
}ps: 下面两部分内容是ES6前要实现对象模型的方法,当然现在使用class更加方便,但下两话题,对理解原型很有帮助。
创建对象
- JavaScript对每个创建的对象都会设置一个原型,指向它的原型对象。
- 原型链
- 对象的原型链
- 函数的原型链
- 构造函数
- 定义时为普通的函数定义,调用时用关键字new来调用这个函数,并返回一个对象
- 构造函数的原型链(重要概念constructor,prototype)
- 如何共享一个方法,有点是什么
- 原型链
- JavaScript对每个创建的对象都会设置一个原型,指向它的原型对象。
原型继承
JavaScript的原型继承实现方式就是:
- 定义新的构造函数,并在内部用call()调用希望“继承”的构造函数,并绑定this;
- 借助中间函数F实现原型链继承,最好通过封装的inherits函数完成;
- 继续在新的构造函数的原型上定义新方法。
浏览器
浏览器对象
window
window对象不但充当全局作用域,而且表示浏览器窗口。
- 具有innerWidth/innerHeight属性,可以获取浏览器窗口的内部宽度和高度。内部宽高是指除去菜单栏、工具栏、边框等占位元素后,用于显示网页的净宽高。
- 也有outerWidth/outerHeight属性,可以获取浏览器窗口的整个宽高
navigaor
navigator对象表示浏览器的信息,常用属性有:
- navigator.appName:浏览器名称;
- navigator.appVersion:浏览器版本;
- navigator.language:浏览器设置的语言;
- navigator.platform:操作系统类型;
- navigator.userAgent:浏览器设定的User-Agent字符串。
1
2
3
4// 提到短路运算符
// 充分利用JavaScript对不存在属性返回undefined的特性,直接用 **短路运算符||** 计算
var width = window.innerWidth || document.body.clientWidth;
screen
screen对象表示访问者 屏幕 的信息,常见属性有:
- screen.width
- screen.height
- screen.colorDepth
location
location对象表示当前页面的URL信息。
常用属性和方法:
- href,protocol,host,port,pathname,search,hash
- assign() 加载一个新页面
- reload() 重新加载当前页面
document💎
document对象表示当前页面。由于HTML在浏览器中以DOM形式表示为树形结构,document对象就是整个DOM树的根节点。
history
history对象保存了浏览器的历史记录,JavaScript可以调用history对象的back()或forward (),相当于用户点击了浏览器的“后退”或“前进”按钮。这个对象属于历史遗留对象,对于现代Web页面来说,由于大量使用AJAX和页面交互,简单粗暴地调用history.back()可能会让用户感到非常愤怒。
新手开始设计Web页面时喜欢在登录页登录成功时调用history.back(),试图回到登录前的页面。这是一种错误的方法。
任何情况,你都不应该使用history这个对象了。
操作DOM
获取DOM节点
- 获取已存在的DOM节点,document对象提供的getElementById()和getElementsByTagName()可以按ID获得一个DOM节点和按Tag名称获得一组DOM节点
- 创建一个DOM节点(如: var div1 = document.createElement(‘div’);)
更新DOM
修改节点的文本,方法有两种
一种是修改innerHTML属性
1
2
3var p = document.getElementById('p-id');
p.innerHTML = 'ABC';
p.innerHTML = 'ABC <span style="color:red">RED</span> XYZ';用innerHTML时要注意,是否需要写入HTML。如果写入的字符串是通过网络拿到了,要注意对字符编码来避免XSS攻击。
第二种是修改innerText或textContent属性,这样可以自动对字符串进行HTML编码,保证无法设置任何HTML标签
1
2
3
4
5
6// 获取<p id="p-id">...</p>
var p = document.getElementById('p-id');
// 设置文本:
p.innerText = '<script>alert("Hi")</script>';
// HTML被自动编码,无法设置一个<script>节点:
// <p id="p-id"><script>alert("Hi")</script></p>- innerText和textContent的区别在于,读取属性时,innerText不返回隐藏元素的文本,而textContent返回所有文本。
修改样式
- DOM节点的style属性对应所有的CSS,可以直接获取或设置。因为CSS允许font-size这样的名称,但它并非JavaScript有效的属性名,所以需要在JavaScript中改写为驼峰式命名fontSize
1
2
3
4
5
6// 获取<p id="p-id">...</p>
var p = document.getElementById('p-id');
// 设置CSS:
p.style.color = '#ff0000';
p.style.fontSize = '20px';
p.style.paddingTop = '2em';
- DOM节点的style属性对应所有的CSS,可以直接获取或设置。因为CSS允许font-size这样的名称,但它并非JavaScript有效的属性名,所以需要在JavaScript中改写为驼峰式命名fontSize
添加id, class等属性,都是通过p.id=’xx’这种形式
插入DOM
若这个DOM节点为空(如:
),那么,直接使用innerHTML = ‘child‘就可以修改DOM节点的内容,相当于“插入”了新的DOM节点。若该节点不为空
appendChild(),,把一个子节点添加到父节点的最后
- 如果我们要插入的js节点已经存在于当前的文档树,因此这个节点首先会从原先的位置删除,再插入到新的位置。
- 当然,更多时候是插入一个我们新建的节点
1
2
3
4
5var list = document.getElementById('list'),
var haskell = document.createElement('p');
haskell.id = 'haskell';
haskell.innerText = 'Haskell';
list.appendChild(haskell);
insertBefore(newElement, referenceElement), 可以把子节点插入到指定的位置referenceElement之前
- 可见,使用insertBefore重点是要拿到一个“参考子节点”的引用。很多时候,需要循环一个父节点的所有子节点,可以通过迭代children属性实现:
1
2
3
4
5
6var
i, c,
list = document.getElementById('list');
for (i = 0; i < list.children.length; i++) {
c = list.children[i]; // 拿到第i个子节点
}
- 可见,使用insertBefore重点是要拿到一个“参考子节点”的引用。很多时候,需要循环一个父节点的所有子节点,可以通过迭代children属性实现:
动态创建一个节点然后添加到DOM树中,可以实现很多功能。举个例子,下面的代码动态创建了一个style节点,然后把它添加到< head>节点的末尾,这样就动态地给文档添加了新的CSS定义
1
2
3
4var d = document.createElement('style');
d.setAttribute('type', 'text/css');
d.innerHTML = 'p { color: red }';
document.getElementsByTagName('head')[0].appendChild(d);
删除DOM
- 要删除一个节点,首先要获得该节点本身以及它的父节点,然后,调用父节点的removeChild把自己删掉。
- 注意到删除后的节点虽然不在文档树中了,但其实它还在内存中,可以随时再次被添加到别的位置。
操作表单
获取值value/checked
设置值(直接对相应DOM节点赋值)
HTML5控件
- HTML5新增了大量标准控件,常用的包括date、datetime、datetime-local、color等,它们都使用< input>标签
提交表单
- 一般都是通过响应< form>本身的onsubmit事件,浏览器还会根据onsubmit()的返回值,return true来告诉浏览器继续提交,如果return false,浏览器将不会继续提交form,这种情况通常对应用户输入有误,提示用户错误信息后终止提交form。
操作文件
- 在HTML表单中,可以上传文件的唯一控件就是
<input type="file">
- 注意: 当一个表单包含
<input type="file">
时,表单的enctype必须指定为multipart/form-data,method必须指定为post,浏览器才能正确编码并以multipart/form-data格式发送表单的数据。 - 出于安全考虑,浏览器只允许用户点击
<input type="file">
来选择本地文件; 当用户选择了上传某个文件后,JavaScript也无法获得该文件的真实路径。 - JavaScript可以在提交表单时对文件扩展名做检查,以便防止用户上传无效格式的文件
- 注意: 当一个表单包含
- 随着HTML5的普及,新增的File API允许JavaScript读取文件内容,获得更多的文件信息。