Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
2769 lines (1948 sloc) 84.2 KB

本文正在持续更新中,感兴趣的朋友们敬请留意!!

欢迎访问个人网站导航页

前端JavaScript整理汇总

本文说明

本文中,标有[ES6]标记的,表示属于ECMA Script 6新增的内容

关于本文中的语句,表达式与值三个词的说明

通常来说:

  • 语句指得是一个完整语法的一行代码,比如alert(1 < 2);
  • 表达式指得是一个需要经过运算才能得到值的代码,比如1 < 2
  • 值指的是一个确定的量,如false,也可以是一个表达式表示的值,如3 > 4,也可以是一个语句的返回值,比如let a = 3;

关于本文中对象,数组与函数三个词的说明

本文认为,数组和函数是一种特殊的对象.在本文中,凡是没有做出特别说明的对象,都是包含数组和函数的.

例如:对象属于复杂数据类型.则表示非数组和函数的对象,数组与函数都属于复杂数据类型.


主流浏览器

IE/Edge、Chrome、Firefox、Safari、Opera

国内常用:360浏览器(chrome和ie的内核)、qq浏览器、搜狗浏览器

浏览器由外壳内核组成,浏览器的内核分为渲染引擎JS引擎,内核见下表:

浏览器 渲染引擎 JS引擎
IE Trident JScript -> Chakra
Firefox Gecko Monkey系列
Chrome Webkit -> Blink V8
Safari Webkit SquirrelFish系列
Opera Presto -> Blink Carakan

渲染引擎:将html和css代码渲染成图形界面

js引擎:解析js代码

V8引擎为解析js代码最快的js引擎

JS的诞生

在1995 年 Netscape,一位名为 Brendan Eich 的工程师创造了 JavaScript,随后在 1996 年初,JavaScript 首先被应用于 Netscape 2 浏览器上。最初的 JavaScript 名为 LiveScript(活力脚本),后来因为 Sun Microsystem 的 Java 语言的兴起和广泛使用,Netscape 出于宣传和推广的考虑,将它的名字从最初的 LiveScript 更改为 JavaScript——尽管两者之间并没有什么共同点。这便是之后混淆产生的根源。

几个月后,Microsoft 随着 IE 3 推出了一个与之基本兼容的语言 JScript(注意微软的人生哲学,当它发现别人的东西很好的时候,就必须拧巴的推出自己的,然后自己的又被市场排斥,又开始兼容别人的)。又几个月后,Netscape 将 JavaScript 提交至 Ecma International(一个欧洲标准化组织), ECMAScript 标准第一版便在 1997 年诞生了,随后在 1999 年以 ECMAScript 第三版的形式进行了更新,从那之后这个标准没有发生过大的改动。由于委员会在语言特性的讨论上发生分歧,ECMAScript 第四版尚未推出便被废除,但随后于 2009 年 12 月发布的 ECMAScript 第五版引入了第四版草案加入的许多特性。第六版标准已经于2015年六月发布 。

JS的发展

2003年: 页面上漂浮的广告、弹窗广告;所以当时的浏览器就推出一个功能,禁用广告,实际上本质就是禁用JavaScript。

2004年:谷歌打开了Ajax这个潘多拉的盒子,从此JavaScript被人重视,很多人开始学习JS语言。

2007年:三层分离,iPhone发布,人们开始重视用户体验。大家发现了,JavaScript是web页面中制作交互效果唯一的语言,所以把JS的重视程度,提到了相当高的一个地位。

2008年:Chrome浏览器发布,V8引擎加快了JS的解析,之前的浏览器解析JS的时候卡顿卡顿的,动画效果是蹦蹦的。在Chrome里,它的引擎叫做V8,运行JS很流畅。

2009年:jQuery变得流行,解决了浏览器兼容问题,制作页面效果变得简单,越来越多的初学者愿意学习JavaScript。

2011年:Node.js得到广泛应用,实际上就是把JavaScript运行在了服务器上,单线程非阻塞,能够让企业用最小的成本实现后台网站,比如之前4万的服务器都卡,现在用了node之后,4000的机器都很流畅。

2015年:ECMA Script 6发布,叫做ECMA Script 2015。重量级的改变,把语言的特性颠覆性的一个增强。

JS的特点及组成

特点:弱类型,解释型,单线程

组成:ECMAScript,DOM和BOM

1.杂项

[ES6]const定义一个常数,定义时必须赋值,不允许重新赋值

encodeURI(x)将x进行URL encode编码(ASCII字母和数字以及URL中个分隔符不进行编码)

encodeURIComponent(x)将x进行URL encode编码(ASCII字母和数字不进行编码)

decodeURI(x)encodeURI编码的x进行解码

decodeURIComponent(x)encodeURIComponent编码的x进行解码

alert(x)弹窗x(本意是警告弹窗,但通常用语不需要确认和输入内容的弹窗,应用很少)

prompt(x,y)输入弹窗,x是提示内容,y为在输入框中的提示内容,会返回输入的字符串,如果点"取消"则返回null

confirm(x)确认弹窗,x是提示内容,点击"是"或"确定"一类的返回true,否则返回false

isNaN(x)判断x是否是NaN若是则返回true,否则返回false

JSON.parse(x)返回JSON格式的字符串x转换为的对象(IE7及其以下不支持)

JSON.stringify(obj)返回obj对象转换为的JSON字符串(IE7及其以下不支持)

try-catch执行代码出错后不阻碍后面代码执行,例如:

//try中的代码遇报错则跳过错误继续执行,并执行catch中的内容,try中内容无报错则不执行catch中的内容.
try{
    conSole.log(1);
}catch(e){ //e为错误信息
    //throw e;    //抛出错误
    console.log(e);
}

'use strict'使用严格模式,通常写在第一行,在严格模式下,有如下特殊要求:

  • 禁用with
  • 禁止给未声明的变量赋值
  • arguments对象是传入函数内实参列表的静态脚本,而不是与对应的实参指向同一个值得引用
  • 禁止删除生命的变量或参数
  • 禁止定义重复的变量名或属性名
  • 八进制必须以0o开头
  • eval有独立的作用域
  • 禁止给基础数据类型添加属性
  • 函数自执行时,this指向undefined而不是window
  • 禁止使用arguments.callee
  • arguments和eval呗当做关键字,不能被赋值和用作变量名
  • call,apply,bind传入null或undefined时函数的this指向不改变而不是转换为window

with[严格模式不支持][不推荐使用]延长作用域链里面的变量都从传入的对象中取得,例如:

var a=20,
    obj={a:30};
(function(){
	var a=10;
    console.log(a);
    with(window){
        console(a);
    }
    with(obj){
        console(a);
    }
});

2.变量

2.1 变量命名规则

  • 不能以数字开头
  • 严格区分大小写
  • 不能使用关键词保留词

2.2 变量命名规范

命名规范是为了方便代码维护\阅读\团队协作开发而制定的,违反这些规范并不会报错,而且不同团队会有自己的规范,如果在团队中开发,遵循对应规范即可.这里列出的是推荐的通常被大多数团队使用的规范.

  • 只允许使用 数字(0-9)/字母(A-Z,a-z)/下划线(_)/美元符号($)
  • 语义化命名
  • 小驼峰命名
  • 不覆盖系统自带API

2.3 定义变量及赋值

可以使用var关键字定义一个变量,在定义变量时可以直接赋值,例如:var a = 10,也可以先定义后续再赋值.

[ES6]在ES6中可以使用let关键词定义变量

[ES6]使用var定义变量,实际是在window对象中加入一个属性,而使用let定义变量,其顶层对象并不是window,举例如下:

var a = 10;
let b = 20;
console.log(window.a); // 10
console.log(window.b); //undefined

定义变量是可以通过加逗号一定定义多个变量,如var a = 10, b = '20', c;

在非严格模式下,允许变量未经定义直接赋值,也允许使用var关键字在同一个作用域内多次定义同一个变量,但不建议这样做

使用var定义变量会有变量提升的问题,请参考作用域与解析顺序章节

在严格模式下,变量必须先定义,后赋值

变量定义后未经赋值的,值为undefined

[ES6]给变量赋值除了使用赋值运算符=外,在es6中还可以通过解构赋值的方式进行赋值,只要变量与数值具有相同的结构就可以进行赋值,例如:

let [a,b] = [10,20]; // 相当于let a = 10, b = 20;
let {x: c, y: d} = {x: 50, y: 80}; // 相当于let c = 50, d = 80;
	// 上面一条也可写为 let {c, d} = {c: 50, d: 80};
	// 完整写出来为 let {c: c, d: d} = {c: 50, d: 80};
let [aa,bb,cc,dd] = '解构赋值'; // 相当于 let aa = '解', bb = '构', cc = '赋', dd = '值';

3. console控制台输出

.log普通输出

.info输出信息

loginfo只是语义上的差别,效果上差别不大,在有有的浏览器中有info的标记,但在Google Chrome中完全一样

.warn输出警告信息,不阻碍代码继续执行

.error输出错误信息,不阻碍代码继续执行

如想阻止代码继续执行,可以使用throw抛出错误,例如

throw 'xxx';
// 或
// throw new Error('xxx');

.dir如果输出的是对象,则可以显示对象的所有属性与方法,如果不是对象,则与log无明显差异

4.数据类型

4.1 JavaScript含有的数据类型

其中object称为复杂数据类型(或引用数据类型),其他的称为简单数据类型(或基础数据类型)

  • number

  • string

  • boolean

  • [ES6]symbol

  • null

  • undefined

  • object

4.2 部分数据类型的说明

string类型

字符串的两端需要用下列符号中的任何一个括起来:

  • 单引号(') - 例如:'hello world'
  • 双引号(") - 例如:"hello world"
  • [ES6]`符号 - 例如:`hello word` - 使用此符号时,内部可以换行,而且可以使用${表达式}来直接得到对应的值,避免多次拼接

字符串常用方法

字符串可以加下标(用中括号表示),表示取其中字符,只读,低版本IE不支持,x超出下标范围时返回undefined

.charAt(x)只读,用法同字符串的下标,任何浏览器都支持,x超出下标范围时返回空字符串

.length只读,获取字符长的长度

.charCodeAt(x)第x位的编码

String.fromCharCode(x)返回对应编码的字符

.subString(x,y)截取字符串,如果x小于y则从第x个字符(含)开始截取到y个字符(不含);如果x大于y则从第y个字符(含)开始截取到x个字符(不含)。x与y传负数时按0计

.substr(x,y)截取字符串,从第x个字符(含)开始截取y个字符,y可以省略,省略时截取到末尾。x可以传负数,表示从结尾开始计

.slice(x,y)截取字符串,从第x个字符(含)开始截取到y个字符(不含),y可以省略,省略时截取到末尾。x与y可以传负数,表示从结尾开始计

.toLowerCase()字符串转换为小写,无参数

.toUpperCase()字符串转换为大写,无参数

.toLocaleLowerCase()按照本地方式将字符串转换为小写,无参数

.toLocaleUpperCase()按照本地方式将字符串转换为大写,无参数

只有少数几种语言(如土耳其语)具有地方特有的大小写映射,所有该方法的返回值通常与不带Local的函数一样。

.split(x)以参数中的字符切割字符串,返回一个数组,如果传参为空字符串,则逐字符切割,如果不穿参数则将整体作为数组的一个元素,也可以传入正则表达式(但不常传正则)

.indexOf(x,y)返回从第y位开始查询,返回x在字符串中第一次出现的位置,x应为字符串,若未出现该字符串,则返回-1,y可以省略表示从第一个字符开始查询

.lastIndexOf(x,y)返回从第y位开始反向查询,返回x在字符串中第一次出现的位置,x应为字符串,若未出现该字符串,则返回-1,y可以省略表示从最后一个字符开始查询

.search(x)返回x在字符串中第一次出现的位置,x可以为字符串也可以为正则表达式(传正则时可以忽略大小写,不执行全局检索),若未出现该字符串,则返回-1

.concat(a,b,c,...)字符串拼接,将参数a,b,c,...拼接在字符串后面

.trimLeft()去除左侧空格

.trimRight()去除右侧空格

.trim()去除两侧空格

.replace(旧内容,新内容)用新内容替换旧内容,返回替换后的字符串,不改变原字符串.

  • 旧内容可以是一段字符串,也可以是一个正则表达式,旧内容为字符串时,只替换第一个匹配的内容
  • 新内容可以是一段字符串,也可以是一个函数,如果是函数则用函数的返回值替换就内容,传给函数的第一个实参为替换前的旧内容(形参通常用$0接收)
let str = '这是一段test用的字符串-123-456-789';
console.log(str[1]); // '是'
console.log(str.charAt(1)); // '是'
console.log(str.length); // 25
console.log(str.charCodeAt(0)); // 36825
console.log(str.slice(14,17)); // '123'
console.log(str.toLocaleUpperCase()); // '这是一段TEST用的字符串-123-456-789'
console.log(str.split('-')); // ['这是一段test用的字符串','123','456','789']
console.log(str.indexOf('-'); // 13
console.log(String.fromCharCode(36825)); // '这'

[ES6]padStart(数字,字符串)字符串的方法,如果不够对应位数,则在开头添加参数中指定的字符串,补满位数

[ES6]padEnd(数字,字符串)字符串的方法,如果不够对应位数,则在结尾添加参数中指定的字符串,补满位数

number类型

JavaScript中的数字有最大值和最小值的限制,对于正数最大值可以利用Number.MAX_VALUE取得,若数字大于此值,则会转换为Infinity最小值可以利用Number.MIN_VALUE取得,若数字小于此值,则为0;对于负数则为-Infinity0

在设置数字的时候可以设置十六进制/十进制/八进制/二进制

  • 书写十六进制数字必须以0x开头
  • 书写八进制必须以00o开头
  • 书写二进制必须以0b开头
console.log(017); // 15
console.log(0o15); // 13
console.log(0xab); // 171
console.log(0b10110); //22
// 对于八进制,以0开头,但数字超过了7会按照十进制来解析
console.log(088); // 88
// 对于八进制,以0o开头,但数字超过了7会报错
console.log(0o88); // 报错 Uncaught SyntaxError: invalid or unexpected token

NaN也属于数字类型,这种数据通常出现在类型转换时,将无法转换为数字的数据转换为数字类型或者计算类似0/0或负数的平方根等情况,就会出现NaN这个值.(NaN即Not a Number)

object类型

我们可以通过如下方式来创建一个对象

  • 使用{键1:值1,键2:值2,...键n:值n}的方式来定义对象,其中可以有若干个键值对(键也可以称为属性)
  • 通过new Object()的方式来创建一个对象
  • 通过Object.create(x)来创建一个对象,创建的对象的原型指向x

在es5中,键必须是字符串形式,可以加引号也可以不加引号,但不加引号时必须符合变量的命名规则

[ES6]在es6中键可以写[变量],这时会使用变量的值转换为字符串形式作为对象的键,例如:

let key = "name";
let obj = {
    [key]: "Rivalsa"
};
console.log(obj.name); // Rivalsa

值可以是任意一种数据类型(包含对象),也可以是变量

[ES6]当对象值得变量名与属性名相同时可以简写,如下所示。方法也可以简写,如下所示:

//未简写
let x = 10,
    y = 20,
    obj = {
        x: x,
        y: y,
        sum:function() {
            return this.x + this.y;
        }
    };
//简写
let x = 10,
    y = 20,
    obj = {
        x,
        y,
        sum() {
            return this.x + this.y;
        }
    };

读取对象中不存在的属性不会报错,会返回undefined

使用delete可以删除对象中的变量,例如:delete obj.pet,删除成功返回true否则返回false(在严格模式下无法使用)

获取对象属性的值有以下两种方案:

  • 可以使用对象名.属性名的方式来获取对应的值,但属性名必须满足变量的命名规则,且不能使用变量,属性名会自动按字符串处理
  • 也可以使用对象名[属性]的方式来获取对应的值,属性名可以使用变量,如果中括号中的数据为数字,则会转换为字符串处理。

将对象赋值给一个变量是将对象的指针给这个变量,对变量的修改等价于直接修改对象,举例如下:

let obj = {
    name: 'Rivalsa',
    age: 18,
    pet: 'cat'
};
let a = obj;
a.age = 16;
console.log(obj.age); // 16

[ES6]对象的keys(),values()entries()方法:这三个方法需要通过Object.keys(对象)的方式来调用,分别表示获取对象的键,值以及键值对

利用for 变量 in 对象可以遍历对象的键(能遍历到键了,自然也能取到值了)。


数组属于对象的一种,可以使用[值1,值2,值3,...,值n]来定义一个数组,也可以通过new Array(x,y,z,...)来创建一个数组,如果x为数字且没有其他参数,则返回的为有x项的数组,每项内容为空,否则,返回有x,y,x...组成的数组

数组常用方法

数组中有length属性可以取得数组元素的个数

[ES6]利用for...of...遍历对象(实际是遍历数组),但不常用,因为常用forEach来遍历,举例如下:

//遍历对象
let obj = {name:"Rivalsa",age:18,sex:"M"};
for(let key of Object.keys(obj)){ //keys对应换成values或entries
    console.log(key);
}
//遍历数组
let arr = [1,2,3,4];
for(let value of arr){
    console.log(value);
}

.length返回数组的长度(存储的数据的个数)

.push(x,y,z,...)向数组中添加新数据x,y,z,...(新增到结尾),返回新增数据后数组的长度.(改变原数组)

.unshift(x,y,z,...)向数组中添加新数据x,y,z,...(新增到开头),返回新增数据后数组的长度.(改变原数组)

.pop()无参数,把原数组的最后一位删掉,改变原数组,返回被删除的数据

.shift()无参数,把原数组的第一位删掉,改变原数组,返回被删除的数据

.splice(a,b[,data[,data[,data[,...]]]])从数组下标为a的位开始删除b个数据,再增加数据data,改变原数组,返回由被删除的数据组成的数组(若未删除则返回空数组)

  • a可以传入负数,表示从末尾的某处开始删除

.indexOf(x,y)从下标为y处开始查找数组中的第一个数据x,返回数据下标,若未找到则返回-1,省略y则从第一个元素开始查找

lastIndexOf(x,y)从下标为y处开始反向查找数组中的第一个数据x,返回数据下标,若未找到则返回-1,省略y则从最后一个元素开始查找

.slice(x,y)截取数组,从下标为x的数据(含)开始到下标为y的数据(不含)结束,如果省略y则截取到最后,不修改原数组,返回得到的新数组

  • x与y均可以传入负数,表示从末尾开始计

.sort(x)当不传入参数x时将数组中的数据从小到大排列,改变原数组,返回排序后的数组

x可以为函数,此处不详细写明,但有一种常用用法,举例如下

var arr=[2,5,1,6,4];
//从大到小排列
arr.sort(function(a,b){return b-a;});
console.log(arr)

.reverse()颠倒顺序,改变原数组,返回改变后的数组

.concat(x,y,z,...)将x,y,z...拼接在数组后面,x,y,z...既可以是一个数组,也可以是其他数据类型的新数据,不改变原有数组,返回新数组

.join(x)用x将数组的元素连接在一起(不传x则默认为逗号),不修改原数组,返回拼接好的字符串

Array.isArray(x)判断x是否是数组,如果是返回true,否则返回false

array.forEach(function(currentValue, index, arr), thisValue)数组的遍历

  • currentValue为当前的数据
  • index为当前的序号
  • arr为数组本身(很少使用)
  • thisValue为函数中this的指向

forEach不兼容IE8及其以下

类数组HTMLCollection不支持使用forEach

类数组NodeList支持使用forEach

.map(x)数组的映射(遍历数组并产生新数组,新数组的元素是x中的返回值),返回值为新数组,不改变原数组.x是一个function,函数执行时会传入三个参数(需要选中设置形参接收),分别是item,index,arr

  • item为当前的数据
  • index为当前的序号
  • arr为数组本身(很少使用)

.filter(x)数组的筛选(遍历数组,并产生新数组,新数组的元素是x返回为true时对应原数组的元素),返回值为新数组,不改变原数组.x是一个function,函数自行是会传入三个参数(需要选中设置形参接收),分别是item,index,arr

  • item为当前的数据
  • index为当前的序号
  • arr为数组本身(很少使用)

函数属于对象的一种

  • 定义函数可以通过function关键字进行定义,如function 函数名(){},也可以通过赋值的方式进行定义,如var 函数名 = function(){};,也可以不给函数取名(称为匿名函数),例如function(){}
  • 对于通过function 函数名(){}关键字定义的函数或通过var/let 函数名 = function(){}来定义的函数,可以在函数内部以及作用域内通过函数名来调用函数
  • 对于通过var 函数名1 = function 函数名2(){}来定义的函数,可以在函数内部以及作用域内通过函数名1来调用函数,但在函数名2只能在函数的内部使用
  • 当函数中有return时,函数的返回值为对应的内容
  • 当函数中没有return或内容为空时,返回undefined
  • 函数表达式可以加小括号去执行,函数定义时加小括号可以变为表达式,在后面加小括号,称为立即执行函数(或自执行函数或IIFE).立即执行函数可以将代码模块化,避免污染全局环境
  • 函数实际传入的参数称为实参,在函数内部用于接受实参值的变量称为形参,实参与形参按顺序一一对应
function fn(形参1,形参2,...,形参n) {
    ...
}
fn(实参1,实参2,...,实参n);
  • 实参个数可以等于形参个数,也可以大于形参个数,也可以小于形参个数.传参时,形参与实参按顺序一一对应,对于形参多于实参的情况,未传值的形参值为undefined,对于实参多于形参的情况,在函数中无法通过形参拿到值,但可以通过arguments(实参列表)来拿到值
  • [ES6]在es6中函数有length和name属性,length表示形参的个数,name为函数的名字
function fn(a,b,c) {};
console.log(fn.length, fn.name); // 3 "fn"
  • 在函数中可以调用函数自身,这种方式成为递归

  • 函数中存在一个this,会指向调用函数的对象,函数直接加括号执行属于window调用的,但在严格模式下,函数加括号直接执行this指向undefined

  • 箭头函数

=>的方式来定义函数,例如:

let a = (x, y) => {
 return x + y;
}
a();

箭头函数的简写

  • 当形参有且仅有一个时,小括号可以不写(小括号里面的形参要写)
  • 当函数体中只有一条语句时,大括号可以不写(大括号里面的语句要写),但省略大括号后,函数默认的返回值变为语句的返回值

箭头函数与普通函数的异同点

箭头函数() = > {} 普通函数function(){} 举例
this指向与外界一致,没有自己的this this指向调用这个函数的对象 例1
不能用new执行 可以用new执行
没有arguments 有arguments

例1

document.addEventListener("click",function(){
    console.log(this); // this指向document
});
document.addEventListener("click",() => {
    console.log(this); // this指向window
});

[ES6]Symbol类型

每次新建的Symbol都是不一样的。ES6中symbol数据也可以当做属性名,例如

let obj={name:"Rivalsa",age:18};
let name=Symbol();
obj[name]="Jerry";
console.log(obj);

Symbol的参数是它的标识,只是便于开发者区分,没有实际意义。

4.3 数据类型的检测

typeof x返回x的数据类型字符串(也可以写为typeof(x)),但存在特殊情况,下面列出所有可能的输出值

  • number类型返回number
  • string类型返回string
  • boolean类型返回boolean
  • undefined类型返回undefined
  • null类型返回object
  • object类型(function除外)返回object
  • function返回function

4.4 数据类型之间的转换

4.4.1 转换为数字

Number(x)将x转换为数字类型,返回转换后的结果如下:

  • 若x为纯由数字组成的字符串(可以有正负号或小数点,空格会被忽略),则返回对应的数字
  • 若x为含有非数字字符的字符串(空格会被忽略),则返回结果为NaN
  • 若x为空字符串,则返回0
  • 若x为true,则返回1
  • 若x为false,则返回0
  • 若x为symbol类型,则报错
  • 若x为null,则返回0
  • 若x为undefined或对象,则先转换为字符串,再将得到的字符串按照上述规则转换为数字,返回得到的数字

parseInt(string, radix)将以数字开头的字符串转换为整数数字(第一个非数字字符后面的内容不再解析,正负号算作数字),返回获得的结果,第二个参数为进制的基数,表示第一个参数是以多少进制表示的

parseFloat(x)将以数字开头的字符串转换为数字(第一个非数字且非第一个.字符后面的内容不再解析,正负号算作数字),返回获得的结果

在ES6中parseIntparseFloat可以用Number.parseIntNumber.parseFloat代替

4.4.2 转换为字符串

String(x)将x转换为字符串,返回转换后的结果

x.toString()将x转换为字符串,返回转换后的结果,括号中可以传入一个数字参数,表示将数字转换为对应进制数的字符串(如果x不是数字则忽略参数)

将x转换为字符串的结果如下:

  • 若x为数字,则转换为对应的纯数字的字符串
  • 若x为truefalsenullundefinedNaN则对应转换为'true''false''null''undefined'NaN
  • 若x为symbol类型,则转换为定义时的对应值,例如String(Symbol(123)) === 'Symbol(123)'true
  • 若x为对象类型(函数和数组除外),则转换为'[object Object]'
  • 若x为数组,则转换为经扩展运算符(...)运算后的值
  • 若x为函数,则转换为整个函数体

4.4.3 转换为布尔值

Boolean(x)将x转换为布尔值,返回结果如下:

  • 若x为数字0,则返回false
  • 若x为空字符串"",则返回false
  • 若x为undefined,则返回false
  • 若x为null则返回false
  • 若x为NaN则返回false
  • 其他情况均返回true

5.运算符

运算符按照按照参数的个数可以分为一元运算符,二元运算符三元运算符

运算符按照作用可以分为赋值运算符,算数运算符,比较运算符,逻辑运算符,三目运算符,逗号运算符位运算符

5.1 赋值运算符

赋值运算符=的左侧必须为一个变量,右侧可以为一个值或表达式,表示将值或表达式的值赋给左侧的变量

赋值运算符的返回值为"="右侧的值或右侧表达式的值

var a;
console.log(a = 20); // 20
console.log(a); // 20

自增赋值或自减赋值

自增和自减用++--表示,可以写在数据前面也可以写在数据后面

console.log(i++);
// 上述代码相当于
// console.log(i);
// i++;

console.log(++i);
// 上述代码相当于
// i++;
// console.log(i);

// 自减与自加类似

赋值简写

let a = 10;
a += 1; // 相当于 a = a + 1;
a -= 1; // 相当于 a = a - 1;
a *= 1; // 相当于 a = a * 1;
a /= 1; // 相当于 a = a / 1;
a %= 1; // 相当于 a = a % 1;

5.2 算数运算符

算数运算符分为+(正)\-(负)\+(加)\-(减)\*(乘)\/(除)\%(取余)

加减乘除和取余数按照数学上的运算规则进行运算

数字运算符运算过程中的强制类型转换

涉及一元运算符都会强制转换为数字类型再运算,例如:

console.log('24'); // '24' - 字符串
console.log(+'24'); // 24 - 数字
console.log(+'24d'); // NaN - 数字
console.log(-'24'); // -24 - 数字

涉及减\乘\除\取余都会强制转换为数字类型再运算

涉及加会按照如下规则进行运算:

  • 如果+两侧有一侧是字符串,另一侧非symbol类型,则会将另一侧转换为字符串类型后拼接为新字符串
  • 如果+两侧有一侧是对象,另一侧非symbol类型,则会将两侧全部转换为字符串再进行运算
  • 如果+两侧有一侧是symbol类型,则会报错
  • 其他情况一律全部转换为数字再进行运算

5.3 比较运算符

比较运算符包含<,<=,>,>=,==,===,!=,!==

=====的区别,!=!==的区别

===的比较需要数据类型与值都完全相同才会返回true,否则返回false

==的比较会按照相关规则进行类型转换

!=!==的区别于=====的区别相似

对象与对象的比较

对象在比较时,只比较地址是否一致,例如:

let obj1 = {x:1};
let obj2 = {x:1};
console.log(obj1 == obj2); // false
console.log(obj1 === obj2); // false

对象间有关大于或小于的比较全部返回false

字符串与字符串的比较

字符串与字符串的比较按照首字符的编码大小进行比较,如果首字符相同,则按照第二个字符的编码大小进行转换,以此类推,例如:

console.log('30' > '4'); // false

数字与数字的比较

按照数学上的规则进行比较

任何数字都小于Infinity

任何数字都大于-Infinity

除了数字与数字/字符串与字符串/对象与对象外的比较

除了数字与数字/字符串与字符串/对象与对象外,会将两侧都转换为数字类型后再进行比较

与NaN的比较

任何与NaN的比较都返回false,即使NaN == NaN也会返回false

5.4 逻辑运算符

逻辑运算符包含&&,||,!

&&称为与运算符,使用方法为表达式1 && 表达式2,运算时,首先判断表达式1转换为布尔值是否为false,如果是,则直接返回表达式1的值(注意:是返回表达式1的值,而不是返回表达式1转换为的布尔值),如果不是,则直接返回表达式2的值

||称为或运算符,使用方法为表达式1 || 表达式2,运算时,首先判断表达式1转回为布尔值是否为true,如果是,则直接返回表达式1的值(注意:是返回表达式1的值,而不是返回表达式1转换为的布尔值),如果不是,则直接返回表达式2的值

!称为非运算符,使用方法为!表达式如果表达式转换为布尔值为true则返回false,否则返回true

举例:

console.info( 1 && 2 && null && 4 ); // null
console.info( null || undefined || 10 || false ); // 10  
var a = 0 && alert('hhh'); // 弹窗不执行, a为0
var b = alert("hhh") && 0; // 弹窗,b为undefined

5.5 三目运算符

三目运算符语法为表达式 ? 值1 : 值2

当表达式为true时,整个返回值1,否则返回值2

5.6 逗号运算符

逗号运算符的语法为语句1,语句2,执行时,先执行语句1,再执行语句2,整个表达式的返回值为语句2的值

5.7 位运算符

位运算符包括&,|,~,^,<<,>>,>>>

位运算是在JavaScript中为数不多的直接操作数字二进制位的运算,位运算会将数字转换为有符号的32位二进制数进行运算,运算完成后再转换为十进制数字返回结果

&表示按位与运算

|表示按位或运算

~表示按位非运算

^表示按位异或运算

<<表示左移运算

>>表示带符号右移运算

>>>表示无符号右移运算

无符号右移运算返回的运算结果为无符号的32位二进制数转换为的十进制数

更多关于位运算的知识,请参考《JavaScript中的位运算》

[ES6]5.8 扩展运算符

扩展运算符用...表示,将数组拆分为用逗号分隔的参数序列

还可以作为函数的参数接收多于的实参,例如:

let a = [1,2,'3',true];
console.log(...a);
let fn = function(a, b, ...c) {
    console.log(c);
}
fn(1,2,3,4,5,6); // [3,4,5,6]

5.9 运算符的优先级

请参考这份资料

6 判断与循环

6.1 if...else...

用法1

if(表达式) {
    // 表达式转换为布尔值为true是执行此处代码
    ...
}

用法2

if(表达式) {
    // 表达式转换为布尔值为true是执行此处代码
    ...
} else {
    // 表达式转换为布尔值为false是执行此处代码
    ...
}

用法3

if(表达式1) {
    // 表达式1转换为布尔值为true时执行此处代码
    ...
} else if(表达式2) {
    // 表达式1转换为布尔值为false且表达式2转换为布尔值为true是执行此处代码
    ...
} ... {
    ...
} else if(表达式n) {
    // 表达式1转换为布尔值为false且表达式2转换为布尔值为false且...且表达式n转换为布尔值为true是执行此处代码
    ...
} else {
    // 上述所有表达式转换为布尔值都为false时执行此处代码
}

用法3中可以没有最后一个else部分

如果某个代码块中只有一条语句时,可以省略大括号

较简单的判断语句可以用逻辑运算符或三目运算符代替

6.2 switch

基本语法

switch(表达式) {
    case 值1:
        代码块1
    case 值2:
        代码块2
    ...
    	...
    case 值n:
        代码块n
    default:
        默认代码块
}

在执行switch时,首先会用表达式和值1/值2/.../值n依次进行匹配,若某个值匹配成功后,则从此代码块开始依次执行后面的所有代码块(包括默认代码块),如果所有的值都不匹配,则执行默认代码块.

执行代码块时,如果遇到break,则直接跳出switch判断

switch的比较需要值与数据类型都完全相同

6.3 for

语法

for(语句1;语句2;语句3) {
    // 循环体
    ...
}

循环语句按照如下顺序执行:

  1. 执行语句1
  2. 判断语句2的转换为的布尔值,如果为false则跳出循环,否则继续向下执行
  3. 执行循环体
  4. 执行语句3
  5. 返回第2步继续执行

语句1,语句2和语句3均可以省略不写,但分号不可省略

6.4 while

语法

while(表达式) {
    // 循环体
    ...
}

循环语句按照如下顺序执行:

  1. 判断表达式转换为的布尔值,如果为false则跳出循环,否则继续向下执行;
  2. 执行循环体
  3. 返回第1步继续执行

while循环即for循环的一种特殊写法,for循环只是把与循环有关的代码集中在了一个位置

6.5 do ... while

语法

do {
    // 循环体
    ...
} while(表达式);

循环语句按照如下顺序执行:

  1. 执行循环体
  2. 判断表达式转换为的布尔值,如果为false则跳出循环,否则继续向下执行;
  3. 返回第1步继续执行

6.6 跳出循环

break

跳出循环,例如:

for(let i = 0; i < 5; i++) {
    if(i === 3) break;
    console.log(i); // 控制台依次输出0 1 2
}

continue

跳出本次循环,立即开始下一次循环,例如:

for(let i = 0; i < 5; i++) {
    if (i === 3) continue;
    console.log(i); // 控制台依次输出0 1 2 4
}

跳出循环时,只跳出breakcontinue所在的循环,想跳出外层循环请参考循环命名

6.7 循环命名

用于跳出指定的循环

aaa:for(var i = 0; i < 5; i++) {
    console.log(`i:${i}`);
    for(var j = 0; j < 5; j++) {
        console.log(`j:${j}`);
        break aaa;
    }
}

注意:除非必要,尽量避免死循环

6.8 循环添加异步事件中的循环变量

例如如下代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>循环添加异步事件中的循环变量</title>
</head>
<body>
    <button>按钮0</button>
    <button>按钮1</button>
    <button>按钮2</button>
    <button>按钮3</button>
    <button>按钮4</button>
    <script>
    	var aBtn = document.getElementsByTagName('button');
        for(var i = 0; i < aBtn.length; i++) {
            aBtn[i].onclick = function() {
                alert(i);
            };
        }
    </script>
</body>
</html>

执行上述代码时,会发现点击任意一个按钮,都会弹框5,而不是点那个按钮就弹对应的序号,这种现象的原因是给按钮添加的点击事件属于异步代码,在添加点击事件时,其中的代码不会执行,等到点击按钮执行代码时,循环变量i早已循环到5了,所以点击每个按钮都会弹窗5。

解决此问题有两种方案:利用自定义属性和利用闭包,请参考《循环添加异步事件中的循环变量》

7.改变this指向

call函数加.call()后,会立即执行函数,传入的第一个实参为函数中的this指向(原参数对应写在后面) 例如:

function fn(a, b){
    console.log(this);
    console.log(a + b);
}
fn.call(document, 5, 6);

apply函数加.apply后,会立即执行函数,传入的第一个实参为函数中的this指向(原参数作为数组在第二个参数传入)(无须改变this指向时可以在第一个参数传null)

例如:

function fn(a, b){
    console.log(this);
    console.log(a + b);
}
fn.apply(document, [a, b]);

bind*[不兼容IE8及其以下]*函数加.bind后,不执行这个函数,其返回值为新函数,但新函数的内容与原函数相同,新函数的this指向为参数中规定的指向(如果传入函数的参数,则可以固定参数,期返回的函数无须再传此参数也无法修改,传参的方法与call相同)(无须改变this指向时可以在第一个参数传null)

function fn(a, b){
    console.log(this);
    console.log(a + b);
}
fn.bind(document, 1)(2);

8.作用域与解析顺序

8.1 作用域及作用域链

作用域可以通俗的理解为起作用的范围,分为全局作用域与局部作用域,在es5中,只有函数运行时会创建一个局部作用域,[ES6]在es6中只要执行大括号中代码时就会创建一个局部作用域

在引用变量时,会现在自己的作用域内查找,如果找不到就到父级作用域内查找,一直找到全局作用域,如果全局也找不到,则报错,这个称为作用域链

全局作用域中的变量保存在GO(全局对象)中,局部作用域中的变量保存在对应的AO(活跃对象)中,能产生局部作用域的代码在每次执行时,会产生全新的AO对象

var a, b;
function fn() {
    var a = 10, c = 20;
    b = 30;
    console.log(a,b,c);
}
fn(); // 10 20 30
console.log(a,b); // undefined 30
console.log(c); // 报错:Uncaught ReferenceError: c is not defined

对于函数可以通过[[Scopes]]记录了其作用域(链)

当AO对象今后无法再用到时,这个作用域内的AO对象会被自动删除,将对应内存释放,称为垃圾回收机制

8.2 闭包

现有A、B和C三个作用域,B是A的子作用域,C是B的子作用域,在C作用域中用到了B作用域中的AO对象,当C作用域中的内容直接在A作用域中被访问时,由于没有执行B作用域内的内容,也就没有在B作用域内创建全新的AO对象,所以C作用域中能用到B作用域内上次的值。将这种情况称为闭包,举例如下:

未形成闭包的情况

function outer() {
    var a = 0;
    function inner() {
        console.log(a++);
    }
    inner();
}
outer(); // 0
outer(); // 0
outer(); // 0

上述例子中,有三个作用域,全局作用域,outer形成的局部作用域和inner形成的局部作用域,outer形成的作用域是全局作用域的子作用域,inner形成的作用域是outer形成的作用域的子作用域,inner形成的作用域中用到了outer形成的作用域中的变量a,但是inner形成的作用域无法在全局作用域中直接访问(也就是说无法在不执行outer的情况下直接执行inner),所以每次执行都会创建新的a。

形成了闭包的情况

var fn;
function outer() {
    var a = 0;
    function inner() {
        console.log(a++);
    }
    fn = inner;
}
outer();
fn(); // 0
fn(); // 1
fn(); // 2

上述例子中,有三个作用域,全局作用域,outer形成的局部作用域和inner形成的局部作用域,outer的是全局作用域的子作用域,inner的是outer的子作用域,inner的作用域中用到了outer的作用域中的变量a,且inner形成的作用域可以在全局作用域中直接访问(通过执行fn可以在不执行outer的情况下直接执行inner),在直接执行时由于outer中的代码没有执行,所以没在outter作用域内创建全新的变量a,所以inner中就可以直接使用outer中原有的a

8.3 变量提升与解析顺序

在es5中,即使把定义变量的语句写在后面,也会先进行变量的定义(但不会赋值),把这个现象称为变量提升,例如:

alert(b); // 弹窗undefined
var b = 10;
alert(a); // 报错:Uncaught ReferenceError: a is not defined

解析顺序

1.定义(预解析)

  • 执行var定义变量(但不赋值)
  • 如果在函数内,则自动创建形参并将实参赋值给形参
  • 执行function定义变量

2.执行 - 从上到下执行剩余代码

8.4 作用域与解析顺序的举例

例0

if(false) {
    var a = 10;
    let b = 20;
}
alert(a);
alert(b);

执行结果

弹窗:undefined

报错:Uncaught ReferenceError: b is not defined

例1

if(true) {
    var a = 10;
    let b = 20
}
alert(a);
alert(b);

执行结果

弹窗:10

报错:Uncaught ReferenceError: b is not defined

例2

var x = 5;
a();
function a() {
    alert(x);
    var x = 10;
}
alert(x);

执行结果

弹窗:undefined

弹窗:5

例3

var x = 5;
a();
function a() {
    alert(x);
    x = 10;
}
alert(x);

执行结果

弹窗:5

弹窗:10

例4

a();
function a() {
    alert(x);
    var x = 10
}
alert(x);

执行结果

弹窗:undefined

报错:Uncaught ReferenceError: x is not defined

例5

function a() {
    alert(1);
}
var a;
alert(a);

执行结果

弹窗:(函数体)

例6

alert(a);
var a = 10;
alert(a);
function a() {alert(20)}
alert(a);
var a = 30;
function a() {alert(40)}
alert(a);

执行结果

弹窗:(alert(40)的函数体)

弹窗:10

弹窗:10

弹窗:30

例7

var a = 10;
alert(a);
a();
alert(a);
function a() {
    alert(20);
}

执行结果

弹窗:10

报错: Uncaught TypeError: a is not a function

例8

a();
var a = function() {alert(1);};
a();
function a() {alert(2);}
a();
var a = function() {alert(3);};
a();

执行结果

弹窗:2

弹窗:1

弹窗:1

弹窗:3

例9

var a = 10;
function fn() {
    alert(a);
    var a = 1;
    alert(a);
}
fn();
alert(a);

执行结果

弹窗:undefined

弹窗:1

弹窗:10

例10

fn();
alert(a);
var a = 10;
alert(a);
function fn() {
    var a = 1;
}

执行结果

弹窗:undefined

弹窗:10

例11

fn();
alert(a);
var a = 10;
alert(a);
function fn() {
    a = 1;
}

执行结果

弹窗:1

弹窗:10

例12

fn()();
var a = 0;
function fn() {
    alert(a);
    var a = 3;
    function c() {
        alert(a);
    }
    return c;
}

执行结果

弹窗:undefined

弹窗:3

例13

var a = 5;
function fn() {
    var a = 10;
    alert(a);
    function b() {
        alert(++a);
    }
    return b;
}
var c = fn();
c();
fn()();
c();

执行结果

弹窗:10

弹窗:11

弹窗:10

弹窗:11

弹窗:12

9.Math对象

  • .abs(x)返回x的绝对值
  • .random()返回0-1之间的随机数(包含0,不包含1)
  • .round(x)返回x的四舍五入取整值
  • .ceil(x)返回x向上取整(进1取整)的值
  • .floor(x)返回x向下取整(舍尾取整)的值
  • .min(a,b,c,...)接收多个数字参数,返回最小值(如果不传参返回Infinity
  • .max(a,b,c,...)接收多个数字参数,返回最大值(如果不传参返回-Infinity
  • .pow(a,b)返回a的b次幂([ES6]在es6中可以用a ** b表示)
  • .sqrt(x)返回x的算术平方根
  • .cos(x)计算x的余弦值,x是以弧度为单位的数字
  • .sin(x)计算x的正弦值,x是以弧度为单位的数字
  • .tan(x)计算x的正切值,x是以弧度为单位的数字
  • .PI圆周率的值
  • .LN1010的自然对数
  • .LN22的自然对数
  • .LOG10E以10为底e的对数
  • .LOG2E以2为底e的对数

10.定时器

setInterval(a,b,c,d,e,...)设置重复定时器,参数a为一个函数(或JS语句字符串,不建议),当定时器时间到后执行函数或字符串中的语句,参数b为定时时间,c,d,e,...为给a函数传的实参,返回定时器的编号。(除了第一个参数外,均是可选的)

setTimeout(a,b,c,d,e,...)设置单次定时器,参数a为一个函数(或JS语句字符串,不建议),当定时器时间到后执行函数或字符串中的语句,参数b为定时时间,c,d,e,...为给a函数传的实参,返回定时器的编号。(除了第一个参数外,均是可选的)

setTimeout((...arr) => {
    console.log(arr);
}, 1000, null, 2, 'a', true, [1,2,3,false], {pet: 'cat', name: 'Tom'});

clearInterval(a)取消编号为a的重复定时器

clearTimeout(a)取消编号为a的单次定时器

实际上,clearInterval也可以取消setTimeout定时器,clearTimeout也可以取消setInterval定时器

requestAnimationFrame(a)按照刷新率执行函数a,相当于一个重复定时器,时间定为刷新率

cancelAnimationFrame(a)取消编号为a的requestAnimationFrame定时器

11.日期对象

Date()为构造函数(类),需要通过new创建一个对象

Date后面加括号可以指定时间(年,月,日,时,分,秒),月份从0起,注意减一,或者括号中可以直接写以毫秒为单位的时间戳,或者传格式为"年/月/日 时:分:秒"的字符串("/"可以替换为"-")).传字符串时月份不必考虑加减1的问题

.toLocalString()转换为本地格式的日期时间

.toLocalDateString()转换为本地格式的日期

.toLocalTimeString()转换为本地格式的时间

.getFullYear()获取完整年份

.getYear()获取年份(19xx年用两位数表示,后续会超过100,不常用)

.getMonth()获取月份,0-11月,所以通常要加1

.getDay()获取星期,从零开始,星期日为0

.getDate()获取日

.getHours()获取时

.getMinutes()获取分

.getSeconds()获取秒

.getMilliSeconds()获取毫秒

.toUTCSting()转换为UTC时间,返回值为字符串

.getTime()转换为以毫秒为单位的时间戳(从1970年1月1日0时整开始计算)

.getTimezoneOffset()获取GMT时间与本地时间相差的分钟数(GMT时间减去本地时间)

setFullYear(x)将年份设置为x

setMonth(x)将月份设置为x,范围为0-11

setDate(x)将日设置为x

setHours(x)将小时设置为x

setMinutes(x)将分钟设置为x

setSeconds(x)将秒设置为x

setMilliseconds(x)将毫秒设置为x

日期对象可以相减,返回值为两个日期相差的毫秒数(相当于时间戳相减)

12.面向对象

12.1 构造函数

构造函数的本质就是一个函数(箭头函数不能作为构造函数)。但构造函数通常不是直接调用,而是通过new关键词来调用,直接调用与通过new调用的区别如下:

  • 使用new执行函数时会自动创建一个对象,函数中的this指向这个对象,函数执行结束后自动返回这个对象。
  • 构造函数的函数体中存在return语句时,用new来执行函数时,如果return的是基础数据类型则会忽略函数体中的return,返回自动创建的对象,如果return的是复杂数据类型则会返回return的这个对象。

构造函数的函数名通常使用大驼峰命名。

对于基础数据类型,本身是没有属性的,但字符串、数字等具有一个自带的构造函数(如String,Number),当通过.[]来读取或运行属性时,系统会自动通过自带的构造函数创建一个对象,再执行或读取对象的属性。我们把这个自带的构造函数称为包装类。举例如下:

let str = '我是字符串';
console.log(str.length);
// 第二行代码相当于如下代码
console.log(new String(str).length);

12.2 原型与原型链

在函数中,存在一个prototype属性,这个属性值是一个对象,.此函数通过new操作符创建的对象中会默认含有一个__proto__属性,此属性值与其构造函数的prototype属性指向同一个对象.我们将这个对象称为原型

任何一个对象都有自己的原型,原型的属性值是一个对象,则此对象的原型还有自己的原型...所以我们将此称为原型链,原型链的终点为Object

一个对象在调用属性时会首先找它本身的属性,如果找到,则使用这个属性值,如果找不到,则会在它的原型中查找,如果找到,则使用这个属性值,如果找不到,则在其原型的原型中查找...也就是说,对象找不到的属性会在原型链中继续查找.

例1

let Cat = function(){};
Cat.prototype.aa = 0;
let tom = new Cat();
console.log(tom.aa); // 0
console.log(tom.__proto__ === Cat.prototype); // true

例2

function O(){
    this.name = "rivalsa";
    this.sex = "M";
}
O.prototype.tt = "123";
var obj = new O;
console.log(obj);

每一个原型上都默认有一个construotor指向对应的的函数

由于利用构造函数创造出来的所有的对象的原型都是指向同一对象的指针,所以利用原型及原型链可以减少不必要的内存开销

构造函数私有属性及其原型(链)的继承

现存在一个构造函数,我们创造一个新的构造函数,新的构造函数可以调用原构造函数的所有的属性及原型,则称新的构造函数是由原构造函数继承而来的

私有属性的继承

JavaScript中没有继承构造函数的方法,但我们可以通过一些方式来模拟出这个过程

私有属性的继承方法如下:

function Person(n, a) {
 this.name = n;
 this age = a;
   }
   function Teacher(n, a, id) {
 Person.call(this,n,a); // 继承的私有属性
 this.id = id; // 新增的私有属性
   }

原型链的继承

JavaScript中没有继承构造函数的方法,但我们可以通过一些方式来模拟出这个过程

原型链的继承方法如下:

function Person(n,a){
 this.name = n;
 this.age = a;
}
Person.prototype.showName = function(){
 console.log(this.name);
}
Person.prototype.showAge = function(){
 console.log(this.age);
}
function Teacher(n,a,id){
 Person.call(this,n,a); // 继承的私有属性
 this.id = id; // 新增的私有属性
}
function _Person(){} // 新定义的构造函数,和原函数的原型相同,没有多余的私有属性,防止创建无用属性
_Person.prototype = Person.prototype; // 设置相同的原型函数
Teacher.prototype = new _Person; // 继承原型(实际是将_Person实例化一个对象作为Teacher的原型)
Teacher.prototype.constructor = Teacher; // 补充constructor属性
Teacher.prototype.showId = function(){ // 新增的原型
	console.log(this.id);
}

instanceof

可以利用instanceof来判断一个对象的原型链上是否存在指定的构造函数,如果存在则返回true,否则返回false

用法:object instanceof function

12.3 [ES6]类及其继承

在ES5中没有类的概念,用构造函数代替,在ES6中可以用class定义一个类,定义的类只能用new执行,不能自执行,继承可以直接使用extends,大大简化了原型及原型链的复杂逻辑.举例如下:

class Person{
    //私有属性需要在constructor中定义
    constructor(n,a){
        this.name=n;
        this.age=a;
    }
    //定义原型中的方法
    showName(){
        console.log(this.name);
    }
    showAge(){
        console.log(this.age);
    }
}
class Teacher extends Person{ //Teacher继承自Person
    constructor(n,a,i){
        super(n,a); //继承所有私有属性(传值过去)
        this.id=i; //新增的私有属性
    }
    showName(){ //修改原有的原型中的方法
        super.showName(); //执行原有的原型方法
        console.log("123"); //原型方法中新增的语句
    }
    showId(){ //新增的原型方法
        console.log(this.id);
    }
}
let r=new Teacher("Rivalsa",16,1112);
console.log(r);
r.showName();

13. [ES6]Set和Map两种数据结构

ES6中新增Set和Map两种数据结构,需用new创建对象

set对象将传进的数组去重,例如:

let arr=[1,2,4,6,6,8,3,8,1];
let x=[...new Set(arr)];
console.log(x);

Set的属性(方法)(不常用)

  • size 长度
  • add 添加数据(数据有相同内容时不会被添加)
  • clear 全部清除
  • delete 删除数据(删除哪一个数据,没有序号)
  • 等等

Map可以让任何数据类型都能作为键,但只能set存值,get取值。例如:

let obj={};
let map=new Map();
map.set(true,123);
map.set(obj,"qwe");
console.log(map.get(obj),map.get(true));

14. [ES6]回调地狱及Promise

如果代码中有多处异步代码(异步中还有异步),例如:

setTimeout(() => {
    setTimeour(() => {
        setTimeout(() => {
            console.log(4);
        },100);
        console.log(3)
    },100);
    console.log(2);
},100);
console.log(1);

可以看到,上述代码有横向发展的趋势,看起来不好看,也不利于代码维护.通常把这种情况称为回调地狱.

Promise

Promise是一个构造函数(类),需要用new来创建一个对象,创建这个对象时,需要两个参数,这两个参数在内部会被定义为函数,第一个表示成功(一般使用resolve),第二个表示失败(一般使用reject),当这部分代码执行完毕后,需要根据状态调用resolvereject,调用resolve后会停止当前的执行,并执行属性then传入的函数的第一个参数,调用reject或会停止档期至今,并执行属性then传入的第二个参数或属性catch传入的参数

执行到resolve或reject后,会直接进入对应部分代码

实例化后的Promise对象有两个常用属性,一个是then,另一个是catch,这两个属性都是函数,then需要传入两个函数参数,第一个是resolve时执行的函数,第二个是reject时执行的函数,但通常习惯上不传入第二个参数,而是用catch来捕捉reject.

代码如下:

new Promise((resolve,reject) => {
    ...
    if(...) resolve(); else reject();
}).then(() => {
    //resolve时执行的代码
    ...
}).catch(() => {
    //reject时执行的代码
    ...
});

由于当Promise实例执行到resolve时会继续执行then传入的代码,则可以在then部分通过return设置返回值,这个返回值就是整个实例的返回值,如果让整个实例返回一个新的Promise对象,则可以实例又可以继续有then及catch属性,这样就可以将横向发展的代码变成纵向发展,也就解决了回调地狱问题,代码如下:

new Promise (res => {
    ...
    res();
}).then(() => {
    return new Promise(res => {
        ...
        res();
    });
}).then(() => {
    return new Promise(res => {
        ...
        res();
    });
}).then(() => {
    ...
});

如果需要捕捉reject则在最后增加catch即可,由于上面的代码中没有任何地方用到rejectcatch并没有意义,所以没有定义catch

利用此方法可以解决本节第一个代码额回调地狱问题,代码如下:

console.log(1);
new Promise((resolve,reject) => {
    setTimeout(() => {
        console.log(2);
        resolve();
    },100);
}).then(() => {
    return new Promise(resolve ={
        setTimeout(() => {
            console.log(3);
            resolve();
        },100);
	});
}).then(() => {
    setTimeout(() => {
        console.log(4);
    },100);
});

15.DOM操作

DOM(文档对象模型 document object model)是针对HTML文档的一个API. DOM 描绘了一个层次化的节点树,允许开发人员添加、移除和修改页面元素(元素的文字也是DOM的一个节点)

DOM节点共有12种

  • 元素节点(nodetype为1)
  • 文本节点(nodetype为3,nodeValue为文本的内容)
  • 属性节点(nodetype为2,nodeValue为属性的值)
  • 注释节点(nodetype为8,nodeValue为注释的内容)
  • (其他8种)

nodeValue是可读可写的

nodeName为节点的名称(以大写字母表示,只读)

attributes为节点的属性

15.1 获取DOM对象基础方式

通过ID获取

document.getElementById('ID'); // 返回对应的DOM元素

通过class获取(不兼容IE8及其以下)

element.getElementsByClassName('className'); // 返回类数组HTMLCollection, element的后代元素

通过标签名获取

element.getElementsByTagName('tagName'); // 返回类数组HTMLCollection, element的后代元素

通过name获取

document.getElementsByName('name'); // 返回类数组NodeList

在低版本的IE浏览器中,此方法会获取ID为对应字符串的元素

通过选择器获取(静态方法,不兼容IE7及其以下)

element.querySelector('选择器'); // 返回对应的DOM元素(第一个), element的后代元素
element.querySelectorAll('选择器') // 返回类数组NodeList, element的后代元素

几个特殊元素的获取方式

获取html:document.documentElement

获取title:document.title

获取body:document.body

获取head:document.head

15.2 操作DOM的属性及内容

操作标签原有属性

DOM元素的各种属性都在DOM对象中,所以可以直接修改DOM对象的属性即可,举例如下:

var oBox = document.getElementById("box");
oBox.href = "#";
oBox.className = "box1";
oBox.style.display = "none" ;

特殊情况

  • 修改DOM元素的class属性,可以使用classNameclassList来操作
  • DOM对象的style属性也是一个对象,此对象的各种属性就是对应元素行内的CSS样式

对特殊情况的举例

例1

<html>
<head>
	<style>
        #wrap {
            width: 300px;
            height: 300px;
            background-color: blue;
        }
    </style>
</head>
<body>
    <div id="wrap"></div>
    <script>
    	let oWrap = document.getElementById('wrap');
        oWrap.style.backgroundColor = 'red';
        // 上一行代码也可以写成如下形式
        // oWrap.style['backgroucd-color'] = 'red';
    </script>
</body>
</html>

在例1中,通过的读写oWrap.style只能拿到或写入行内样式的值,如果样式写在style标签中,是无法通过oWrap.style取得值的.

例2

<html>
<head>
	<style>
        #wrap {
            width: 300px;
            height: 300px;
            background-color: blue;
        }
    </style>
</head>
<body>
    <div id="wrap"></div>
    <script>
    	let oWrap = document.getElementById('wrap');
        oWrap.style.cssText = 'background-color:red; width:100px';
    </script>
</body>
</html>

在例2中,通过oWrap.style.cssText可以一次读取或修改多个行内样式.

例3

<html>
<head>
	<style>
        .wrap {
            width: 300px;
            height: 300px;
            background-color: blue;
        }
        .wrap.app {
            background-color: red;
        }
    </style>
</head>
<body>
    <div class="wrap"></div>
    <script>
    	let oWrap = document.getElementsByClassName('wrap')[0];
        oWrap.className = 'wrap app';
    </script>
</body>
</html>

通过修改className可以直接修改标签对应的类名,通过修改类名可以修改对应样式

例4

<html>
<head>
	<style>
        .wrap {
            width: 300px;
            height: 300px;
            background-color: blue;
        }
        .wrap.app {
            background-color: red;
        }
    </style>
</head>
<body>
    <div class="wrap"></div>
    <script>
    	let oWrap = document.getElementsByClassName('wrap')[0];
        oWrap.addEventListener('click',function() {
            this.classList.add('app');
        });
    </script>
</body>
</html>

通过classList的各种API可以直接修改类名(不支持IE9及其以下),classList包括以下API:

  • add(类名)添加类名
  • remove(类名)移除类名
  • toggle(类名)添加或删除类名(有则删除,无则添加),删除了类名会返回false,添加了类名会返回true
  • contains(类名)判断是否存在类名,存在则返回true,不存在则返回false

获取标签当前样式

getComputedStyle(x,y)省略y时返回对象x的计算样式表,y取'after''before'时返回x的对应伪元素的计算样式表,只读属性(IE8及其以下不支持,用element.currentStyle代替,IE8以上不支持第2个参数)(动态的)例如:

HTML

<div id="wrap"></div>

CSS

#wrap{
    width:100px;
    height:100px;
    background-color:red;
}

JavaScript

var oWrap=document.getElementById("wrap"),
    wrapCss=window.getComputedStyle(oWrap);
console.log(wrapCss.width);
console.log(wrapCss.height);
console.log(wrapCss.backgroundColor);

操作标签data-自定义属性

dataset也是一个对象,可以直接读取或修改“data-”开头的属性

操作标签的属性(原有的和自定义的都可以)

获取属性:getAttribute('属性名');

新增或修改属性:setAttribute('属性名','属性值');

移除属性:removeAttribute('属性名');

var oBox = document.getElementById("box");
oBox.getAttribute("data-title"); // 获取
oBox.setAttribute("data-name","mydata"); // 修改、添加
oBox.removeAttribute("data-name"); // 删除

innerHTML/innerText

改变元素的内容

var oBox = document.getElementById("box");
oBox.innerHTML = "ABC";
oBox.innerText = "123";

innerHTML 与 innerText 的区别:

  • innerHTML会解析其中的html标签
  • innerText不会解析hmtl标签,原样替换所设置的内容

低版本火狐浏览器不支持innerText,用textContent代替

15.3 更多DOM操作

.childNodes获取元素的所有子节点(主流浏览器是获取所有子节点,低版本IE浏览器是获取所有子元素节点)

.children获取元素的所有子元素节点

.parentNode获取元素父节点

.parentElement获取元素父元素节点

通常元素的父节点与元素的父元素节点是相同的,因为通常只有元素节点才会有子节点

.offsetParent【兼容性差】

  • 对于主流浏览器:获取元素最近的有定位属性的父节点,找不到则为body
  • 对于IE7及其以下:

如果本身没有定位属性:获取最近的有设置宽度或高度的元素,找不到则为body

如果本身没有定位属性:与主流浏览器相同

.createElement(x)创建X元素节点

.createTextNode("xx")创建内容为xx的文本节点

.creatComment('xx')创建内容为xx的注释节点

.createDocumentfragment()创建文档片段

.appendChild(x)在子节点的末尾添加节点x

.insertBefore(a,b)在子节点中节点b之前新增节点a

.replaceChild(a,b)在子节点中用a节点替换b节点,返回b节点

.remove()删除本节点

.removeChild(x)在子节点中删除节点x,返回被删除的节点

.hasChildNodes()有子节点则返回true,否则返回false

.firstElementChild获取第一个子元素节点(不支持IE9及其以下)

.firstChild

  • 主流浏览器:获取第一个子节点
  • IE9及其以下:获取第一个子元素节点

.lastElementChild获取最后一个子元素节点(不支持IE9及其以下)

.lastChild

  • 主流浏览器:获取最后一个子节点
  • IE9及其以下:获取最后一个子元素节点

.nextElementSibling获取下一个兄弟元素节点(不支持IE9及其以下)

.nextSibling

  • 主流浏览器:获取下一个兄弟节点
  • IE9及其以下:获取下一个兄弟元素节点

.previousElementSibling获取上一个兄弟元素节点(不支持IE9及其以下)

.previousSibling

  • 主流浏览器:获取上一个兄弟节点
  • IE9及其以下:获取上一个兄弟元素节点

16. DOM事件

16.1 0级事件

采用赋值的方式绑定函数,新事件替代旧事件

element.event = fun;

如需要移除事件,将其赋值为null即可

鼠标事件

oncontextmenu鼠标右键点击事件

onselectstart选中开始事件

ondblclick鼠标双击事件

onclick鼠标单击事件

onmousedown鼠标任意键按下

onmousemove鼠标移动

onmouseup鼠标任意键抬起

ommouseenter鼠标移入(不冒泡)

onmouseleave鼠标移出(不冒泡)

onmouseover鼠标移入(冒泡)

onmouseout鼠标移出(冒泡)

关于鼠标移入移出事件,冒泡与不冒泡的说明:

  • 此处的冒泡:父级盒子设置有移入事件,则鼠标从父级盒子移入子级盒子时也会触发事件,从子级盒子移入父级盒子时也会触发事件;
  • 此处的不冒泡:父级盒子设置有移入事件,则鼠标从父级盒子移入子级盒子时不会触发事件,从子级盒子移入父级盒子时不会触发事件;
  • 无论是否冒泡,从父级盒子外部直接移入子级盒子内部(即使子级盒子显示的位置不在父级盒子显示的位置内部)都会触发事件

鼠标滚轮滚动事件

  • mousewheel鼠标滚轮事件(非火狐浏览器)
  • DOMMouseScroll鼠标滚轮事件(火狐浏览器专用,仅支持2级事件)

键盘事件

onkeydown键盘按键按下事件

onkeypress键盘能输入内容的按键(不含功能键)按下事件

onkeyup键盘按键抬起事件

表单事件

onfocus表单获得焦点事件(window和表单均码有此事件)

onblur表单失去焦点事件(window和表单均码有此事件)

onchange表单内容发生改变事件

  • 对于text:在失去焦点之前触发(需要失去焦点时刻与获得焦点时刻内容不同才会触发)
  • 对于radio:被选中的单选框触发
  • 对于checkbox:被选中和被取消选中时均会触发
  • 对于select:选择的内容发生改变时会被触发

oninputinput框输入或删除字符事件

onsubmit表单提交事件

onreset表单重置事件

onselect文本框的文本内容被选中事件

表单事件方法

  • focus()获得焦点
  • blur()失去焦点
  • submit()提交表单
  • reset()重置表单

滚动条事件

onscroll滚动条滚动事件

16.2 2级事件

2级事件的绑定与解绑方式与0级事件不同,所有0级事件都可以在2级事件中使用

新事件与旧事件共存,与0级事件不冲突

.addEventListener(a,b,c)添加事件监听器(低版本IE不支持,用.attachEvent(a,b)代替(没有第3个参数),且IE中事件函数的this指向window)

  • a为事件字符串,不写on(使用attachEvent时需要写on)
  • b为事件函数(参数为事件对象)
  • c为布尔值,true表示事件绑定在捕获阶段,false表示事件绑定在冒泡阶段(参考事件捕获与事件冒泡章节

removeEventListener(a,b,c)移除事件监听器(低版本IE不支持,用detachEvent(a,b)代替(没有第3个参数))

移除事件时的参数应与绑定事件时的参数相同(函数需要指针相同)

DOM 3级事件的绑定与解绑方式与DOM 2级事件相同,只是增加了一些新的事件,在此暂不列出。

16.3 事件捕获与事件冒泡

当采用2级事件绑定事件函数时,第三个参数为true则事件绑定在捕获阶段。

当采用2级事件绑定事件函数时,第三个参数为false则事件绑定在冒泡阶段。

事件源没有捕获阶段,即使第三个参数为true也是绑定在冒泡阶段的,有时会将事件源的冒泡阶段称为执行阶段

0级事件是绑定在事件冒泡阶段的

当某个事件被触发后,会先进入此事件的捕获阶段,随后进入此事件的冒泡阶段。

事件的捕获阶段

当某元素的某个事件被触发后,首先会执行window或document(随浏览器的不同而不同)上此事件的捕获阶段的事件函数,随后执行...的捕获阶段的事件函数,随后执行此元素的父级的父级的父级的捕获阶段的事件函数,随后执行此元素的父级的父级的捕获阶段的事件函数,随后执行此元素的父级的捕获阶段的事件函数,随后进入冒泡阶段。

如果同一个元素的同一个事件绑定了多个捕获阶段的事件,则后绑定的先执行

事件的冒泡阶段

当某元素的某个事件被触发后,会先进入捕获阶段,随后执行此元素冒泡阶段的事件函数,随后执行此元素的父级的冒泡阶段的事件函数,随后执行此元素的父级的父级的冒泡阶段的事件函数,随后执行此元素的父级的父级的父级的冒泡阶段的事件函数,随后执行...的冒泡阶段的事件函数,随后执行window或document(随浏览器的不同而不同)上此事件的冒泡阶段的事件函数。

如果同一个元素的同一个事件绑定了多个冒泡阶段的事件,则先绑定的先执行

举例:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <title>事件捕获与事件冒泡</title>
    <style>
        #out {
            width: 300px;
            height: 300px;
            background-color: aqua;
        }
        #mid {
            width: 200px;
            height: 200px;
            background-color: pink;
        }
        #in {
            width: 100px;
            height: 100px;
            background-color: skyblue;
        }
    </style>
</head>
<body>
    <div id="out">
        <div id="mid">
            <div id="in"></div>
        </div>
    </div>
    <script>
        'use strict'
        let oOut = document.getElementById('out'),
            oMid = document.getElementById('mid'),
            oIn = document.getElementById('in');
        oOut.onclick = () => {
            console.log('0级out');
        };
        oMid.onclick = () => {
            console.log('0级mid');
        };
        oIn.onclick = () => {
            console.log('0级in');
        };
        oOut.addEventListener('click', () => {
            console.log('冒泡out');
        });
        oMid.addEventListener('click', () => {
            console.log('冒泡mid');
        });
        oIn.addEventListener('click', () => {
            console.log('冒泡in');
        });
        oOut.addEventListener('click', () => {
            console.log('捕获out');
        }, true);
        oMid.addEventListener('click', () => {
            console.log('捕获mid');
        }, true);
        oIn.addEventListener('click', () => {
            console.log('捕获in');
        }, true);
    </script>
</body>
</html>

执行结果:

当点击最内侧的in元素时,控制台会输出如下内容:

捕获out

捕获mid

0级in

冒泡in

捕获in

0级mid

冒泡mid

0级out

冒泡out

阻止事件冒泡的方法请参考事件对象一节

16.4事件委托

把事件加给父级,利用事件对象中的target来判断是哪个元素触发的。

16.5 事件对象

在主流浏览器中:当事件被触发时,会默认传一个实参,为事件对象,可以在事件函数中设置形参(通常用e或ev)接收

在IE8及其以下的浏览器中,不会传这个实参,但在全局中有window.event为事件对象

以下都是事件对象中常用的属性:

通用属性

type事件的类型(没有on)

stopPropagation()停止事件流的传播(不再继续捕获或冒泡)(IE8及其以下浏览器不支持,用cancelBubble=true代替)

preventDefault()阻止默认事件(IE8及其以下浏览器不支持preventDefault,用returnValue=false代替)(在0级事件中也可以用return false阻止默认事件,所有浏览器均支持)

target触发事件的DOM元素(IE8及其以下浏览器不支持,用srcElement代替)

currentTarget当前执行事件函数的DOM元素(通常会使用this代替此属性)

鼠标事件属性

altKey/shiftKey/ctrlKey 布尔值,表示alt/shift/ctrl键是否按下

clientX/clientY 鼠标到浏览器左上角的距离(不是到文档,与滚动条无关)

pageX/pageY 鼠标到文档左上角的距离(与滚动条有关)

offsetX/offsetY 鼠标到触发事件元素左上角的距离

layerX/layerY鼠标距离定位父级左上角的距离【没试过但感觉兼容性差】

screenX/screenY 鼠标到用户屏幕左上角的距离

button按下的是鼠标左键还是中键还是右键(值分别为0,1,2)

wheelDelta(非火狐浏览器可用)滚轮向上滚动值为120,向下滚动值为-120

detail(火狐浏览器可用)滚轮向上滚动值为-3,向下滚动值为3

键盘事件属性

altKey/shiftKey/ctrlKey 布尔值,表示alt/shift/ctrl键是否按下

keyCode按键的键值(也有用witch的)

17.BOM相关

BOM事件

onresize窗口大小改变事件(window的事件)

onload页面加载完成事件

onerror页面加载出错事件

onscroll滚动条滚动事件

onfocus获得焦点事件

onblur失去焦点事件

获得焦点与失去焦点事件通常与定时器配合,在失去焦点时取消定时器

其他BOM相关内容

location当前地址信息对象

  • .href完整的URL
  • .protocol使用的协议
  • .hostname主机名
  • .port端口号
  • .host主机名+端口号
  • .pathname路径
  • .search参数(?后的字符)
  • .hash #后的字符

history前进后退等历史信息对象

  • .length历史页面的个数
  • .back()转跳到前一个页面
  • forward()转跳到后一个页面
  • .go(n)转跳到下n个页面,n可为非0的整数

navigator浏览器相关信息(例如用户代理)对象

screen屏幕相关信息对象

18.元素各种尺寸和距离

window.innerHeight浏览器窗口高度(带窗口边框)

window.innerWidth浏览器窗口宽度(带窗口边框)

.clientWidth 盒子padding-box的宽度

.clientHeigth 盒子padding-box的高度

.offsetWidth 盒子border-box的宽度

.offsetHeight 盒子border-box的高度

当盒子没有设置高度,而是由内容撑开时,在IE6中clientHeigth为0(这里我记不准是直接为0还是content-box部分高度为0,有知道的小伙伴请e-mail至fans@samail.cn告知)

.scrollHeight 【兼容性差】内容实际占用的高度,超出盒子的部分也计算(有无overflow:hidden时不一样,不同浏览器不一样)

.scrollWidth 【兼容性差】内容实际占用的宽度,超出盒子的部分也计算(有无overflow:hidden时不一样,不同浏览器不一样)

.offsetLeft【兼容性差】元素左侧到定位父级左侧的距离(对transform:translate不响应,显示的是没有此属性时的距离)

.offsetTop【兼容性差】元素顶部到定位父级顶部的距离(对transform:translate不响应,显示的是没有此属性时的距离)

.clientLeft左边框宽度

.clientTop上边框宽度

.getBoundingClientRect()

.scrollTop滚动高度(可读可写)

.scrollTop滚动宽度(可读可写)

获取页面滚动高/宽度

  • pageXOffset/pageYOffset
  • document.documentElement.scrollLeft/document.documentElement.scrollTop
  • document.body.scrollLeft/document.body.scrollTop

document.documentElement.scrollTop在老版本的Google Chrome,及有些手机版的浏览器中不支持,可使用document.body.scrollTop代替

window.scrollTo(top:x)将滚动高度设置为x

19.正则表达式

可以用来高效便捷的处理字符串

19.1 定义正则表达式

双斜杠定义

例如:let reg = /abc/;

利用RegExp定义

例如let reg = new RegExp("x");括号中可以传入一个字符串变量,也可以直接传入字符串(字符串的内容为正则表达式)

19.2 正则表达规则

19.2.1 转义字符

普通转移字符\(将有特殊意义的字符变为普通字符)

\n匹配换行符

\t匹配制表符(tab)

\r

\d匹配所有数字字符(1位)

\D匹配除了数字字符外的所有字符

\s匹配如下字符:

  • (空格字符)
  • \t
  • \n
  • \r

\S匹配除了下面字符外的所有字符

  • (空格字符)
  • \t
  • \n
  • \r

\w匹配如下字符:

  • 数字
  • 字母
  • 下划线

\W匹配除了下面字符外的所有字符

  • 数字
  • 字母
  • 下划线

\b匹配如下内容:

  • 起始位置
  • 结束位置
  • \W能匹配的所有字符

\B匹配除了下面内容外的所有内容

  • 起始位置
  • 结束位置
  • \W能匹配的所有字符

19.2.2 标识

写在正则表达式结尾/的后面,可以写多个,不区分先后顺序,在使用RegExp定义时,标识以字符串形式作为第二个参数传入

g全局,表示在整个字符串中匹配(而不是只匹配到第一个就结束)

i不区分大小写

m换行匹配

19.2.3 量词

写在对应规则后面

{n}n个

{n,m}最少n个,最多m个,包括n和m

{n,}最少n个,包括n

+最少1个,等价于{1,}

*最少0个,等价于{0,}

?0个或1个,等价于{0,1}

量词的贪婪和惰性

贪婪(默认):尽量按多的去匹配

惰性(在量词后面加?表示惰性量词):尽量按少的去匹配

19.2.4 子项

使用小括号可以将里面的内容作为一个子项

19.2.5 字符集

用中括号表示

  • 表示"或者"

    • [abc]表示字符a或字符b或字符c
    • [abc]{2}表示aaabacbabbbccacbcc
  • 字符集中的-表示一个区间(按Unicode码的区间)

    • [0-5]表示05之间的任意数字
    • [a-z]表示所有小写字母
    • [A-Z]表示所有大写字母
    • [0-<]表示所有数字或冒号或分号或小于号(Unicode码中冒号和分号和小于号是9后面紧接着的三个)

    在字符集之外-没有特殊意义

  • 如果字符集中第1个字符是^整个子项表示排除

    • [^abc]表示除了字符a和字符b和字符c外的任意字符
  • 字符集中小括号/大括号/正斜杠/问号/星号/加号等无特殊含义

19.2.6 其他有特殊意义的字符

  • ^表示起始位置
  • $表示结束位置
  • .表示匹配除了以下内容的任意字符
    • \n
    • \r
  • |表示或者(前后是两个独立的正则)

19.2.7 捕获组

\数字x表示第x个子项,再次匹配第x个子项

并不是第x个子项的匹配规则,而是第x个子项的内容

19.2.8 断言

(?=xx)(不算做子项)某字符后面要含有xx字符,但匹配到的东西不包含xx

(?!xx)(不算做子项)某字符后面要不含有xx字符,但匹配到的东西不包含xx

(?<=xx)(不算做子项)某字符前面要含有xx字符,但匹配到的东西不包含xx

(?<!xx)(不算做子项)某字符前面要不含有xx字符,但匹配到的东西不包含xx

19.3 使用正则表达式的方法

正则表达式的方法

.test(字符串)检查字符串中是否存在对应正则规则,存在则返回true否则返回false

.exec(字符串)返回第一次出现对应规则的字符串有关的对象,若为匹配成功则返回null(不常用)

字符串的方法

.match(正则表达式)返回字符串中匹配成功的字符串组成的数组(数组有匹配的内容与子项组成,在规则中使用全局g则组成的数组中不包含子项)

19.4 RegExp对象

RegExp中存储了上一次的子项,可以通过这个对象直接拿到数据.(可以先test然后通过RegExp得到子项)

20.ajax

ajax即“Asynchronous Javascript And XML”(异步 JavaScript 和 XML),是指一种创建交互式网页应用的网页开发技术。

ajax可以在不刷新页面的前提下向后端 发送/请求 数据,在开发中是必然会用的技术。

20.1 JavaScript原生ajax

let xhr;
if (window.XMLHttpRequest) {  // 标准浏览器
    xhr = new XMLHttpRequest();
} else if (window.ActiveXObject) { // 低版本IE
    try {
        xhr = new ActiveXObject('Msxml2.XMLHTTP');
    } catch () {
            xhr = new ActiveXObject('Microsoft.XMLHTTP');
    }
}
if (xhr) {
    xhr.onreadystatechange = onReadyStateChange;
    xhr.open('POST', '/url', true);
    // 设置 Content-Type 为 application/x-www-form-urlencoded
    // 以表单的形式传递数据
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    xhr.send('username=admin&password=root');
}

// onreadystatechange 方法
function onReadyStateChange() {
    /*
    xhr.readyState:
        0: 请求未初始化
        1: 服务器连接已建立
        2: 请求已接收
        3: 请求处理中
        4: 请求已完成,且响应已就绪
     */
    if (xhr.readyState === 4) {
        // 请求处理到了最后一步
        //xhr.status HTTP状态码
        if (xhr.status >= 200 && xhr.status < 300) {
            console.log(xhr.responseText);//xhr.responseText请求返回的文本内容
        } else {
            console.log('There was a problem with the request.');
        }
    } else {
        // 请求还没处理到最后一步
        console.log('still not ready...');
    }
}

20.2 jQuery的ajax

// 列出部分参数
$.ajax({
    method : "POST" // 请求方式
    ,url : "/url" // 请求地址
    ,data : {} // 需要发送的数据
    ,dataType : "json" // 对请求返回的数据预处理
    ,success : function(data){} // 请求成功的回调函数
    ,error : function(err){} // 请求失败的回调函数
});

20.3 axios

发送单个请求

axios({
	method : "post",
    url : "http://example.com",
    data : {name:"Rivalsa",age:18}
}).then(res => {
    console.log(res);
}).catch(err => {
    console.log(err);
});

发送多个请求

同时发送两个请求,全都处理完成后才执行回调

function reqA(){
    return axios.get("url1");
}
function reqB(){
    return axios.get("url2");
}
axios.all([ reqA(),reqB() ]).then(res => {
    console.log(res);
});

20.4 跨域问题

发送ajax请求时需要确保当前页面与请求页面同源(必须协议\主机\端口号全都相同),否则需要后端发送相应的HTTP Header才能正常访问.

20.5 jsonp

由于HTML页面中调用JavaScript是没有同源限制的,所以可以利用此方法发送数据,举例如下:

假设:请求地址为https://example.com返回数据为`rivalsa({"name":"rivalsa","age":18});`

则前端请求为:

<script>
    let rivalsa = m => {
        console.log(m);
    };
</script>
<script src="https://example.com"></script>
You can’t perform that action at this time.