JavaScript知识点大全

1488780841257569.gif

1473476925658848.gif

1473476935836031.gif

1473476945739921.gif

1517813267879178.png

1473476952296809.gif

1473476960662589.gif

1473476975191386.gif

1473476981856911.gif

1473476987139357.gif

计算机编程基础

编程:就是让计算机为解决某个问题而使用某种程序设计语言编写程序代码,并最终得到结果的过程。
计算机程序:就是计算机所执行的一系列的指令集合,而程序全部都是用我们所掌握的语言来编写的,所以人们要控制计算机一定要通过计算机语言向计算机发出命令。

计算机语言指用于人与计算机之间通讯的语言,它是人与计算机之间传递信息的媒介。
计算机语言的种类非常的多,总的来说可以分成机器语言,汇编语言和高级语言三大类。
实际上计算机最终所执行的都是 机器语言,它是由“0”和“1”组成的二进制数,二进制是计算机语言的基础。

可以通过类似于人类语言的 ”语言”来控制计算机,让计算机为我们做事情,这样的语言就叫做编程语言(Programming Language)。
编程语言是用来控制计算机的一系列指令,它有固定的格式和词汇(不同编程语言的格式和词汇不一样),必须遵守。
如今通用的编程语言有两种形式:汇编语言和高级语言。
汇编语言和机器语言实质是相同的,都是直接对硬件操作,只不过指令采用了英文缩写的标识符,容易识别和记忆。
高级语言主要是相对于低级语言而言,它并不是特指某一种具体的语言,而是包括了很多编程语言,常用的有C语言、C++、Java、C#、Python、PHP、JavaScript、Go语言、Objective-C、Swift等。

高级语言所编制的程序不能直接被计算机识别,必须经过转换才能被执行,为此,我们需要一个翻译器
翻译器可以将我们所编写的源代码转换为机器语言,这也被称为二进制化。 记住1和 0。

image.png

翻译器翻译的方式有两种:一个是编译,另外一个是解释。两种方式之间的区别在于翻译的时间点不同
编译器是在代码执行之前进行编译,生成中间代码文件
解释器是在运行时进行及时解释,并立即执行(当编译器以解释方式运行的时候,也称之为解释器)

image.png

类似于请客吃饭:
编译语言:首先把所有菜做好,才能上桌吃饭
解释语言:好比吃火锅,边吃边涮,同时进行

计算机组成

image.png

数据存储:

计算机内部使用二进制 0 和 1来表示数据。
所有数据,包括文件、图片等最终都是以二进制数据(0 和 1)的形式存放在硬盘中的。
所有程序,包括操作系统,本质都是各种数据,也以二进制数据的形式存放在硬盘中。平时我们所说的安装软件,其实就是把程序文件复制到硬盘中。
硬盘、内存都是保存的二进制数据。

数据存储单位:

bit < byte < kb < GB < TB<…..
位(bit): 1bit 可以保存一个 0 或者 1 (最小的存储单位)
字节(Byte):1B = 8b
千字节(KB):1KB = 1024B
兆字节(MB):1MB = 1024KB
吉字节(GB): 1GB = 1024MB
太字节(TB): 1TB = 1024GB

程序运行:

  1. 打开某个程序时,先从硬盘中把程序的代码加载到内存中
  2. CPU执行内存中的代码

注意:之所以要内存的一个重要原因,是因为 cpu 运行太快了,如果只从硬盘中读数据,会浪费cpu性能,所以,才使用存取速度更快的内存来保存运行时的数据。(内存是电,硬盘是机械)

初识JavaScript

JavaScript是什么?

JavaScript 是世界上最流行的语言之一,是一种运行在客户端的脚本语言 (Script 是脚本的意思)
脚本语言:不需要编译,运行过程中由 js 解释器( js 引擎)逐行来进行解释并执行
现在也可以基于 Node.js 技术进行服务器端编程

浏览器执行JS简介

浏览器分成两部分:渲染引擎和 JS 引擎

  • 渲染引擎:用来解析HTML与CSS,俗称内核,比如 chrome 浏览器的 blink ,老版本的 webkit。
  • JS 引擎:也称为 JS 解释器。 用来读取网页中的JavaScript代码,对其处理后运行,比如 chrome 浏览器的 V8

浏览器本身并不会执行JS代码,而是通过内置 JavaScript 引擎(解释器) 来执行 JS 代码 。JS 引擎执行代码时逐行解释每一句源码(转换为机器语言),然后由计算机去执行,所以 JavaScript 语言归为脚本语言,会逐行解释执行。

JS初体验

JS 有3种书写位置,分别为行内、内嵌和外部。

行内式 JS

  1. <input type="button" value="点我试试" onclick="alert('Hello World')" />
  • 可以将单行或少量 JS 代码写在HTML标签的事件属性中(以 on 开头的属性),如:onclick
  • 注意单双引号的使用:在HTML中我们推荐使用双引号, JS 中我们推荐使用单引号
  • 可读性差, 在html中编写JS大量代码时,不方便阅读;
  • 引号易错,引号多层嵌套匹配时,非常容易弄混;
  • 特殊情况下使用

内嵌 JS

  1. <script>
  2. alert('Hello World~!');
  3. </script>

外部 JS文件

  1. <script src="my.js"></script>
  • 利于HTML页面代码结构化,把大段 JS代码独立到 HTML 页面之外,既美观,也方便文件级别的复用
  • 引用外部 JS文件的 script 标签中间不可以写代码
  • 适合于JS 代码量比较大的情况

单行注释

  1. // 我是一行文字,不想被 JS引擎 执行,所以 注释起来

多行注释

  1. /*
  2. 获取用户年龄和姓名
  3. 并通过提示框显示出来
  4. */

输入输出语句

方法 说明 归属
alert(msg) 浏览器弹出警示框 浏览器
console.log(msg) 浏览器控制台打印输出信息 浏览器
prompt(info) 浏览器弹出输入框,用户可以输入 浏览器

变量

本质:变量是程序在内存中申请的一块用来存放数据的空间。

声明变量

  1. var age; // 声明一个 名称为age 的变量

赋值

  1. age = 10; // 给 age 这个变量赋值为 10

变量的初始化

  1. var age = 18; // 声明变量同时赋值为 18

更新变量

  1. var age = 18;
  2. age = 81; // 最后的结果就是81因为18 被覆盖掉了

同时声明多个变量

  1. var age = 10, name = 'zs', sex = 2;

变量命名规范

由字母(A-Za-z)、数字(0-9)、下划线(_)、美元符号( $ )组成,如:usrAge, num01, _name
严格区分大小写。var app; 和 var App; 是两个变量
不能 以数字开头。 18age 是错误的
不能 是关键字、保留字。例如:var、for、while
变量名必须有意义。 MMD BBD nl → age
遵守驼峰命名法。首字母小写,后面单词的首字母需要大写。 myFirstName

数据类型

为什么需要数据类型

在计算机中,不同的数据所需占用的存储空间是不同的,为了便于把数据分成所需内存大小不同的数据,充分利用存储空间,于是定义了不同的数据类型。
简单来说,数据类型就是数据的类别型号。比如姓名“张三”,年龄18,这些数据的类型是不一样的。

简单数据类型(基本数据类型)

image.png

获取检测变量的数据类型

typeof 可用来获取检测变量的数据类型:console.log(typeof num) // 结果 number。

字面量

字面量是在源代码中一个固定值的表示法,通俗来说,就是字面量表示如何表达这个值。
数字字面量:8, 9, 10
字符串字面量:’黑马程序员’, “大前端”
布尔字面量:true,false

什么是数据类型转换

使用表单、prompt 获取过来的数据默认是字符串类型的,此时就不能直接简单的进行加法运算,而需要转换变量的数据类型。通俗来说,就是把一种数据类型的变量转换成另外一种数据类型。
我们通常会实现3种方式的转换:

  • 转换为字符串类型
  • 转换为数字型
  • 转换为布尔型

转换为字符串

image.png

转换为数字型(重点)

image.png

转换为布尔型

  1. console.log(Boolean('')); // false
  2. console.log(Boolean(0)); // false
  3. console.log(Boolean(NaN)); // false
  4. console.log(Boolean(null)); // false
  5. console.log(Boolean(undefined)); // false
  6. console.log(Boolean('小白')); // true
  7. console.log(Boolean(12)); // true

运算符

运算符(operator)也被称为操作符,是用于实现赋值、比较和执行算数运算等功能的符号。

算术运算符概述

image.png

表达式和返回值

表达式:是由数字、运算符、变量等以能求得数值的有意义排列方法所得的组合
简单理解:是由数字、运算符、变量等组成的式子

表达式最终都会有一个结果,返回给我们,我们称为返回值

递增和递减运算符概述

如果需要反复给数字变量添加或减去1,可以使用递增()和递减( — )运算符来完成。
在 JavaScript 中,递增()和递减( — )既可以放在变量前面,也可以放在变量后面。放在变量前面时,我们可以称为前置递增(递减)运算符,放在变量后面时,我们可以称为后置递增(递减)运算符。
注意:递增和递减运算符必须和变量配合使用。

比较运算符概述

概念:比较运算符(关系运算符)是两个数据进行比较时所使用的运算符,比较运算后,会返回一个布尔值(true / false)作为比较运算的结果。

image.png

image.png

逻辑运算符概述

概念:逻辑运算符是用来进行布尔值运算的运算符,其返回值也是布尔值。后面开发中经常用于多个条件的判断

image.png

短路运算(逻辑中断)

短路运算的原理:当有多个表达式(值)时,左边的表达式值可以确定结果时,就不再继续运算右边的表达式的值;

  1. 逻辑与
    语法: 表达式1 && 表达式2
    如果第一个表达式的值为真,则返回表达式2
    如果第一个表达式的值为假,则返回表达式1
  2. 逻辑或
    语法: 表达式1 || 表达式2
    如果第一个表达式的值为真,则返回表达式1
    如果第一个表达式的值为假,则返回表达式2

赋值运算符

概念:用来把数据赋值给变量的运算符。

image.png

运算符优先级

image.png

流程控制

在一个程序执行的过程中,各条代码的执行顺序对程序的结果是有直接影响的。很多时候我们要通过控制代码的执行顺序来实现我们要完成的功能。
简单理解: 流程控制就是来控制我们的代码按照什么结构顺序来执行
流程控制主要有三种结构,分别是顺序结构、分支结构和循环结构,这三种结构代表三种代码执行的顺序。

顺序流程控制

顺序结构是程序中最简单、最基本的流程控制,它没有特定的语法结构,程序会按照代码的先后顺序,依次执行,程序中大多数的代码都是这样执行的。

分支流程控制 if 语句

分支结构

由上到下执行代码的过程中,根据不同的条件,执行不同的路径代码(执行代码多选一的过程),从而得到不同的结果

JS 语言提供了两种分支结构语句

  • if 语句
  • switch 语句

if 语句

  1. // 条件成立执行代码,否则什么也不做
  2. if (条件表达式) {
  3. // 条件成立执行的代码语句
  4. }

if else语句(双分支语句)

  1. // 条件成立 执行 if 里面代码,否则执行else 里面的代码
  2. if (条件表达式) {
  3. // [如果] 条件成立执行的代码
  4. } else {
  5. // [否则] 执行的代码
  6. }

if else if 语句(多分支语句)

  1. // 适合于检查多重条件。
  2. if (条件表达式1) {
  3. 语句1
  4. } else if (条件表达式2) {
  5. 语句2
  6. } else if (条件表达式3) {
  7. 语句3
  8. ....
  9. } else {
  10. // 上述条件都不成立执行此处代码
  11. }

三元表达式

表达式1 ? 表达式2 : 表达式3;

分支流程控制 switch 语句

switch 语句也是多分支语句,它用于基于不同的条件来执行不同的代码。当要针对变量设置一系列的特定值的选项时,就可以使用 switch。

  1. switch( 表达式 ){
  2. case value1:
  3. // 表达式 等于 value1 时要执行的代码
  4. break;
  5. case value2:
  6. // 表达式 等于 value2 时要执行的代码
  7. break;
  8. default:
  9. // 表达式 不等于任何一个 value 时要执行的代码
  10. }

switch :开关 转换 , case :小例子 选项

关键字 switch 后面括号内可以是表达式或值, 通常是一个变量

关键字 case , 后跟一个选项的表达式或值,后面跟一个冒号

switch 表达式的值会与结构中的 case 的值做比较

如果存在匹配全等(===) ,则与该 case 关联的代码块会被执行,并在遇到 break 时停止,整个 switch 语句代码执行结束

如果所有的 case 的值都和表达式的值不匹配,则执行 default 里的代码

注意: 执行case 里面的语句时,如果没有break,则继续执行下一个case里面的语句。

循环

在Js 中,主要有三种类型的循环语句: for 循环 while 循环 do…while 循环

for 循环

在程序中,一组被重复执行的语句被称之为循环体,能否继续重复执行,取决于循环的终止条件。由循环体及循环的终止条件组成的语句,被称之为循环语句。

  1. for(初始化变量; 条件表达式; 操作表达式 ) {
  2. //循环体
  3. }

初始化变量:通常被用于初始化一个计数器,该表达式可以使用 var 关键字声明新的变量,这个变量帮我们来记录次数。

条件表达式:用于确定每一次循环是否能被执行。如果结果是 true 就继续循环,否则退出循环。

操作表达式:每次循环的最后都要执行的表达式。通常被用于更新或者递增计数器变量。当然,递减变量也是可以的。

执行过程:

  1. 初始化变量,初始化操作在整个 for 循环只会执行一次。
  2. 执行条件表达式,如果为true,则执行循环体语句,否则退出循环,循环结束。
  3. 执行操作表达式,此时第一轮结束。
  4. 第二轮开始,直接去执行条件表达式(不再初始化变量),如果为 true ,则去执行循环体语句,否则退出循环。
  5. 继续执行操作表达式,第二轮结束。
  6. 后续跟第二轮一致,直至条件表达式为假,结束整个 for 循环。

断点调试:

断点调试是指自己在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住,然后你可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码行即显示错误,停下。
断点调试可以帮我们观察程序的运行过程
浏览器中按 F12—> sources —>找到需要调试的文件—>在程序的某一行设置断点
Watch: 监视,通过watch可以监视变量的值的变化,非常的常用。
F11: 程序单步执行,让程序一行一行的执行,这个时候,观察watch中变量的值的变化。
代码调试的能力非常重要,只有学会了代码调试,才能学会自己解决bug的能力。初学者不要觉得调试代码麻烦就不去调试,知识点花点功夫肯定学的会,但是代码调试这个东西,自己不去练,永远都学不会。
今天学的代码调试非常的简单,只要求同学们记住代码调试的这几个按钮的作用即可,后面还会学到很多的代码调试技巧。

双重 for 循环概述

很多情况下,单层 for 循环并不能满足我们的需求,比如我们要打印一个 5 行 5 列的图形、打印一个倒直角三角形等,此时就可以通过循环嵌套来实现。

循环嵌套是指在一个循环语句中再定义一个循环语句的语法结构,例如在for循环语句中,可以再嵌套一个for 循环,这样的 for 循环语句我们称之为双重for循环。

  1. for (外循环的初始; 外循环的条件; 外循环的操作表达式) {
  2. for (内循环的初始; 内循环的条件; 内循环的操作表达式) {
  3. 需执行的代码;
  4. }
  5. }

内层循环可以看做外层循环的语句

内层循环执行的顺序也要遵循 for 循环的执行顺序

外层循环执行一次,内层循环要执行全部次数

  1. //打印九九乘法表
  2. var str = ''
  3. for (var i = 1; i <= 9; i++) { // 外层for控制 行数 9行
  4. for (var j = 1; j <= i; j++) { // j 控制列数 列数和行数是一样的 j <= i
  5. str += j + " × " + i + " = " + i * j + '\t';
  6. }
  7. str += '\n';
  8. }
  9. console.log(str);

while 循环

while 语句可以在条件表达式为真的前提下,循环执行指定的一段代码,直到表达式不为真时结束循环。

  1. while (条件表达式) {
  2. // 循环体代码
  3. }

执行思路:
先执行条件表达式,如果结果为 true,则执行循环体代码;如果为 false,则退出循环,执行后面代码
执行循环体代码
循环体代码执行完毕后,程序会继续判断执行条件表达式,如条件仍为true,则会继续执行循环体,直到循环条件为 false 时,整个循环过程才会结束

do while 循环

do… while 语句其实是 while 语句的一个变体。该循环会先执行一次代码块,然后对条件表达式进行判断,如果条件为真,就会重复执行循环体,否则退出循环。

  1. do {
  2. // 循环体代码 - 条件表达式为 true 时重复执行循环体代码
  3. } while(条件表达式);

continue 关键字

continue 关键字用于立即跳出本次循环,继续下一次循环(本次循环体中 continue 之后的代码就会少执行一次)。

break 关键字

break 关键字用于立即跳出整个循环(循环结束)。

JavaScript 数组

数组是指一组数据的集合,其中的每个数据被称作元素,在数组中可以存放任意类型的元素。数组是一种将一组数据存储在单个变量名下的优雅方式。

数组的创建方式

  • 利用 new 创建数组
  • 利用数组字面量创建数组
  1. var arr = new Array(); // 创建一个新的空数组
  2. // 使用数组字面量方式创建空的数组
  3. var 数组名 = [];
  4. // 使用数组字面量方式创建带初始值的数组
  5. var 数组名 = ['小白','小黑','大黄','瑞奇'];

数组可以通过索引来访问、设置、修改对应的数组元素,我们可以通过“数组名[索引]”的形式来获取数组中的元素。

遍历数组

  1. var arr = ['red','green', 'blue'];
  2. for(var i = 0; i < arr.length; i++){
  3. console.log(arrStus[i]);
  4. }

数组的长度

  1. var arrStus = [1,2,3];
  2. alert(arrStus.length); // 3

数组和对象的区别

在 JavaScript 中,数组使用数字索引。在 JavaScript 中,对象使用命名索引。

数组是特殊类型的对象,具有数字索引。

何时使用数组,何时使用对象?

  • JavaScript 不支持关联数组
  • 如果希望元素名为字符串(文本)则应该使用对象。
  • 如果希望元素名为数字则应该使用数组。

避免 new Array()

没有必要使用 JavaScript 的内建数组构造器 new Array()。请使用 [] 取而代之!

如何识别数组

typeof 运算符返回 “object”,因为 JavaScript 数组属于对象。

  • 方案一:ECMAScript 5 定义了新方法 Array.isArray()。
  • 方案二:假如对象由给定的构造器创建,则 instanceof 运算符返回 true。

把数组转换为字符串

  • JavaScript 方法 toString() 把数组转换为数组值(逗号分隔)的字符串。
  • join() 方法也可将所有数组元素结合为一个字符串,它的行为类似 toString(),但是您还可以规定分隔符

JavaScript 数组方法

方法 描述
concat() 连接两个或多个数组,并返回已连接数组的副本。
array.copyWithin(target, start, end) 将数组中的数组元素(start到end),复制到指定位置target。
entries() 返回键/值对数组迭代对象。
every() 检查数组中的每个元素是否通过测试,返回布尔值。
fill() 用静态值填充数组中的元素。
filter() 使用数组中通过测试的每个元素创建新数组。
find() 返回数组中第一个通过测试的元素的值。
findIndex() 返回数组中通过测试的第一个元素的索引。
forEach() 为每个数组元素调用函数。
from() 从对象创建数组。
includes() 检查数组是否包含指定的元素。
indexOf() 在数组中搜索元素并返回其位置。
isArray() 检查对象是否为数组。
join() 将数组的所有元素连接成一个字符串。
keys() 返回 Array Iteration 对象,包含原始数组的键.
lastIndexOf() 在数组中搜索元素,从末尾开始,并返回其位置。
map() 使用为每个数组元素调用函数的结果创建新数组。
pop() 删除数组的最后一个元素,并返回该元素。
push() 将新元素添加到数组的末尾,并返回新的长度。
reduce() 将数组的值减为单个值(从左到右)。
reduceRight() 将数组的值减为单个值(从右到左)。
reverse() 反转数组中元素的顺序。
shift() 删除数组的第一个元素,并返回该元素。
slice() 选择数组的一部分,并返回新数组。
some() 检查数组中的任何元素是否通过测试。
sort() 对数组的元素进行排序。
splice() 从数组中添加/删除元素。
toString() 将数组转换为字符串,并返回结果。
unshift() 将新元素添加到数组的开头,并返回新的长度。
valueOf() 返回数组的原始值。

JavaScript 函数

函数:就是封装了一段可被重复调用执行的代码块。通过此代码块可以实现大量代码的重复使用。

函数的两种声明方式

自定义函数方式(命名函数)

  1. // 声明定义方式
  2. function fn() {...}
  3. // 调用
  4. fn();

因为有名字,所以也被称为命名函数
调用函数的代码既可以放到声明函数的前面,也可以放在声明函数的后面

函数表达式方式(匿名函数)

  1. // 这是函数表达式写法,匿名函数后面跟分号结束
  2. var fn = function(){...};
  3. // 调用的方式,函数调用必须写到函数体下面
  4. fn();

因为函数没有名字,所以也被称为匿名函数
这个fn 里面存储的是一个函数
函数表达式方式原理跟声明变量方式是一致的
函数调用的代码必须写到函数体后面

在声明函数时,可以在函数名称后面的小括号中添加一些参数,这些参数被称为形参,而在调用该函数时,同样也需要传递相应的参数,这些参数被称为实参。

  1. // 带参数的函数声明
  2. function 函数名(形参1, 形参2 , 形参3...) { // 可以定义任意多的参数,用逗号分隔
  3. // 函数体
  4. }
  5. // 带参数的函数调用
  6. 函数名(实参1, 实参2, 实参3...);

有的时候,我们会希望函数将值返回给调用者,此时通过使用 return 语句就可以实现。

在使用 return 语句时,函数会停止执行,并返回指定的值

如果函数没有 return ,返回的值是 undefined

return 只能返回一个值。如果用逗号隔开多个值,以最后一个为准。

  1. function sum(a, b) {
  2. return a, b
  3. }
  4. console.log(sum(2, 3)); // 3

arguments的使用

当我们不确定有多少个参数传递的时候,可以用 arguments 来获取。在 JavaScript 中,arguments 实际上它是当前函数的一个内置对象。所有函数都内置了一个 arguments 对象,arguments 对象中存储了传递的所有实参。

arguments展示形式是一个伪数组,因此可以进行遍历。伪数组具有以下特点:

  • 具有 length 属性
  • 按索引方式储存数据
  • 不具有数组的 push , pop 等方法
  1. //求最大值
  2. function maxValue() {
  3. var max = arguments[0];
  4. for (var i = 0; i < arguments.length; i++) {
  5. if (max < arguments[i]) {
  6. max = arguments[i];
  7. }
  8. }
  9. return max;
  10. }
  11. console.log(maxValue(2, 4, 5, 9));
  12. console.log(maxValue(12, 4, 9));

作用域

通常来说,一段程序代码中所用到的名字并不总是有效和可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。作用域的使用提高了程序逻辑的局部性,增强了程序的可靠性,减少了名字冲突。

JavaScript(es6前)中的作用域有两种,es6加入块级作用域:

全局作用域:作用于所有代码执行的环境(整个 script 标签内部)或者一个独立的 js 文件。

局部作用域(函数作用域):作用于函数内的代码环境,就是局部作用域。 因为跟函数有关系,所以也称为函数作用域。

块级作用域:块作用域由 { } 包括。

变量作用域的分类

在JavaScript中,根据作用域的不同,变量可以分为两种:全局变量,局部变量。

全局变量

在全局作用域下声明的变量叫做全局变量(在函数外部定义的变量)。
全局变量在代码的任何位置都可以使用
在全局作用域下 var 声明的变量 是全局变量
特殊情况下,在函数内不使用 var 声明的变量也是全局变量(不建议使用)

局部变量

在局部作用域下声明的变量叫做局部变量(在函数内部定义的变量)
局部变量只能在该函数内部使用
在函数内部 var 声明的变量是局部变量
函数的形参实际上就是局部变量

作用域链

只要是代码,就至少有一个作用域
写在函数内部的局部作用域
如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域
根据在内部函数可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问,就称作作用域链
作用域链:采取就近原则的方式来查找变量最终的值。

JavaScript 预解析

JavaScript 代码是由浏览器中的 JavaScript 解析器来执行的。JavaScript 解析器在运行 JavaScript 代码的时候分为两步:预解析和代码执行。

预解析:在当前作用域下, JS 代码执行之前,浏览器会默认把带有 var 和 function 声明的变量在内存中进行提前声明或者定义。
代码执行: 从上到下执行JS语句。
预解析只会发生在通过 var 定义的变量和 function 上。学习预解析能够让我们知道为什么在变量声明之前访问变量的值是 undefined,为什么在函数声明之前就可以调用函数。

变量预解析(变量提升)

预解析也叫做变量、函数提升。
变量提升: 变量的声明会被提升到当前作用域的最上面,变量的赋值不会提升。

函数预解析(函数提升)

函数提升: 函数的声明会被提升到当前作用域的最上面,但是不会调用函数。

JavaScript 对象

什么是对象?

在 JavaScript 中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等。
对象是由属性和方法组成的。
属性:事物的特征,在对象中用属性来表示(常用名词)
方法:事物的行为,在对象中用方法来表示(常用动词)

创建对象的三种方式

在 JavaScript 中,现阶段我们可以采用三种方式创建对象(object):

  • 利用字面量创建对象
  • 利用 new Object 创建对象
  • 利用构造函数创建对象

利用字面量创建对象

对象字面量:就是花括号 { } 里面包含了表达这个具体事物(对象)的属性和方法。
{ } 里面采取键值对的形式表示
键:相当于属性名
值:相当于属性值,可以是任意类型的值(数字类型、字符串类型、布尔类型,函数类型等)

  1. var star = {
  2. name : 'pink',
  3. age : 18,
  4. sex : '男',
  5. sayHi : function(){
  6. alert('大家好啊~');
  7. }
  8. };

对象的调用

对象里面的属性调用 : 对象.属性名 ,这个小点 . 就理解为“ 的 ”
对象里面属性的另一种调用方式 : 对象[‘属性名’],注意方括号里面的属性必须加引号,我们后面会用
对象里面的方法调用:对象.方法名() ,注意这个方法名字后面一定加括号

利用new Object创建对象

  1. var andy = new Obect();
  2. andy.name = 'pink';
  3. andy.age = 18;
  4. andy.sex = '男';
  5. andy.sayHi = function(){
  6. alert('大家好啊~');
  7. }

利用构造函数创建对象

构造函数 :是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与 new 运算符一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。
在 js 中,使用构造函数要时要注意以下两点:
构造函数用于创建某一类对象,其首字母要大写
构造函数要和 new 一起使用才有意义

  1. function Person(name, age, sex) {
  2. this.name = name;
  3. this.age = age;
  4. this.sex = sex;
  5. this.sayHi = function() {
  6. alert('我的名字叫:' + this.name + ',年龄:' + this.age + ',性别:' + this.sex);
  7. }
  8. }
  9. var bigbai = new Person('大白', 100, '男');
  10. var smallbai = new Person('小白', 21, '男');
  11. console.log(bigbai.name);
  12. console.log(smallbai.name);

new关键字

new 在执行时会做四件事情:

  1. 在内存中创建一个新的空对象。
  2. 让 this 指向这个新的对象。
  3. 执行构造函数里面的代码,给这个新对象添加属性和方法。
  4. 返回这个新对象(所以构造函数里面不需要return)。

遍历对象属性

  1. for (var k in obj) {
  2. console.log(k); // 这里的 k 是属性名
  3. console.log(obj[k]); // 这里的 obj[k] 是属性值
  4. }

JavaScript 内置对象

JavaScript 中的对象分为3种:自定义对象 、内置对象、 浏览器对象
前面两种对象是JS 基础 内容,属于 ECMAScript; 第三个浏览器对象属于我们JS 独有的, 我们JS API 讲解
内置对象就是指 JS 语言自带的一些对象,这些对象供开发者使用,并提供了一些常用的或是最基本而必要的功能(属性和方法)
内置对象最大的优点就是帮助我们快速开发
JavaScript 提供了多个内置对象:Math、 Date 、Array、String等

Math 概述

Math.PI // 圆周率
Math.floor() // 向下取整
Math.ceil() // 向上取整
Math.round() // 四舍五入版 就近取整 注意 -3.5 结果是 -3
Math.abs() // 绝对值
Math.max()/Math.min() // 求最大和最小值
Math.random() //随机返回一个小数,其取值范围是 [0,1)

Date 概述

  1. 获取当前时间必须实例化
    1. var now = new Date();
    2. console.log(now);
  1. Date() 构造函数的参数
    如果括号里面有时间,就返回参数里面的时间。例如日期格式字符串为‘2019-5-1’,可以写成new Date(‘2019-5-1’) 或者 new Date(‘2019/5/1’)
    如果Date()不写参数,就返回当前时间
    如果Date()里面写参数,就返回括号里面输入的时间

日期格式化

我们想要 2019-8-8 8:8:8 格式的日期,要怎么办?
需要获取日期指定的部分,所以我们要手动的得到这种格式。

image.png

Date 对象是基于1970年1月1日(世界标准时间)起的毫秒数

我们经常利用总的毫秒数来计算时间,因为它更精确

  1. // 实例化Date对象
  2. var now = new Date();
  3. // 1. 用于获取对象的原始值
  4. console.log(date.valueOf())
  5. console.log(date.getTime())
  6. // 2. 简单写可以这么做
  7. var now = + new Date();
  8. // 3. HTML5中提供的方法,有兼容性问题
  9. var now = Date.now();

字符串对象

基本包装类型

为了方便操作基本数据类型,JavaScript 还提供了三个特殊的引用类型:String、Number和 Boolean。
基本包装类型就是把简单数据类型包装成为复杂数据类型,这样基本数据类型就有了属性和方法。

  1. // 下面代码有什么问题?
  2. var str = 'andy';
  3. console.log(str.length);

按道理基本数据类型是没有属性和方法的,而对象才有属性和方法,但上面代码却可以执行,这是因为 js 会把基本数据类型包装为复杂数据类型,其执行过程如下 :

  1. // 1. 生成临时变量,把简单类型包装为复杂数据类型
  2. var temp = new String('andy');
  3. // 2. 赋值给我们声明的字符变量
  4. str = temp;
  5. // 3. 销毁临时变量
  6. temp = null;

字符串的不可变

指的是里面的值不可变,虽然看上去可以改变内容,但其实是地址变了,内存中新开辟了一个内存空间。

  1. var str = 'abc';
  2. str = 'hello';
  3. // 当重新给 str 赋值的时候,常量'abc'不会被修改,依然在内存中
  4. // 重新给字符串赋值,会重新在内存中开辟空间,这个特点就是字符串的不可变
  5. // 由于字符串的不可变,在大量拼接字符串的时候会有效率问题
  6. var str = '';
  7. for (var i = 0; i < 100000; i++) {
  8. str += i;
  9. }
  10. console.log(str); // 这个结果需要花费大量时间来显示,因为需要不断的开辟新的空间

根据字符返回位置

字符串所有的方法,都不会修改字符串本身(字符串是不可变的),操作完成会返回一个新的字符串。

  • indexOf(‘A’,begin):返回指定内容在字符串中的位置,找不到返回-1。
  • lastIndexOf()

根据位置返回字符(重点)

image.png

replace方法

replace() 方法用于在字符串中用一些字符替换另一些字符。

replace(被替换的字符串, 要替换为的字符串);

split()方法

split()方法用于切分字符串,它可以将字符串切分为数组。在切分完毕之后,返回的是一个新数组。

  1. var str = 'a,b,c,d';
  2. console.log(str.split(',')); // 返回的是一个数组 [a, b, c, d]

trim() 方法

会从一个字符串的两端删除空白字符,返回的是一个新的字符串。

简单类型与复杂类型

简单类型又叫做基本数据类型或者值类型,复杂类型又叫做引用类型。

  • 值类型:简单数据类型/基本数据类型,在存储时变量中存储的是值本身,因此叫做值类型
    string ,number,boolean,undefined,null
  • 引用类型:复杂数据类型,在存储时变量中存储的仅仅是地址(引用),因此叫做引用数据类型
    通过 new 关键字创建的对象(系统对象、自定义对象),如 Object、Array、Date等

堆和栈

堆栈空间分配区别:
  1、栈(操作系统):由操作系统自动分配释放存放函数的参数值、局部变量的值等。其操作方式类似于数据结构中的栈;
简单数据类型存放到栈里面
  2、堆(操作系统):存储复杂类型(对象),一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。
复杂数据类型存放到堆里面

image.png

image.png

简单类型传参

函数的形参也可以看做是一个变量,当我们把一个值类型变量作为参数传给函数的形参时,其实是把变量在栈空间里的值复制了一份给形参,那么在方法内部对形参做任何修改,都不会影响到的外部变量。

  1. function fn(a) {
  2. a++;
  3. console.log(a);
  4. }
  5. var x = 10;
  6. fn(x);
  7. console.log(x);

复杂类型传参

函数的形参也可以看做是一个变量,当我们把引用类型变量传给形参时,其实是把变量在栈空间里保存的堆地址复制给了形参,形参和实参其实保存的是同一个堆地址,所以操作的是同一个对象。

  1. function Person(name) {
  2. this.name = name;
  3. }
  4. function f1(x) { // x = p
  5. console.log(x.name); // 2. 这个输出什么 ? 刘德华
  6. x.name = "张学友";
  7. console.log(x.name); // 3. 这个输出什么 ? 张学友
  8. }
  9. var p = new Person("刘德华");
  10. console.log(p.name); // 1. 这个输出什么 ? 刘德华
  11. f1(p);
  12. console.log(p.name); // 4. 这个输出什么 ? 张学友

JavaScript高级

JavaScript面向对象

面向过程编程 POP(Process-oriented programming)

面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次调用就可以了。

面向对象编程 OOP (Object Oriented Programming)

面向对象是把事务分解成为一个个对象,然后由对象之间分工与合作。

举个栗子:将大象装进冰箱,面向对象做法。
先找出对象

  1. 大象对象 进去
  2. 冰箱对象
    打开
    关闭
  3. 使用大象和冰箱的功能 并写出这些对象的功能:

在面向对象程序开发思想中,每一个对象都是功能中心,具有明确分工。
面向对象编程具有灵活、代码可复用、容易维护和开发的优点,更适合多人合作的大型软件项目。

面向对象的特性:
封装性
继承性
多态性

面向过程和面向对象的对比

面向过程:优点:性能比面向对象高,适合跟硬件联系很紧密的东西,例如单片机就采用的面向过程编程。
缺点:没有面向对象易维护、易复用、易扩展

面向对象:优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统 更加灵活、更加易于维护。缺点:性能比面向过程低

ES6 中的类和对象

对象:

现实生活中:万物皆对象,对象是一个具体的事物,看得见摸得着的实物。例如,一本书、一辆汽车、一个人可以是“对象”,一个数据库、一张网页、一个与远程服务器的连接也可以是“对象”。

在 JavaScript 中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等。

对象是由属性和方法组成的:
属性:事物的特征,在对象中用属性来表示(常用名词)
方法:事物的行为,在对象中用方法来表示(常用动词)

对象方法:

Object.keys() 用于获取对象自身所有的属性,返回一个由属性名组成的数组。

Object.defineProperty() 定义对象中新属性或修改原有的属性。

  1. Object.defineProperty(obj, prop, descriptor)

obj:必需。目标对象
prop:必需。需定义或修改的属性的名字
descriptor:必需。目标属性所拥有的特性

Object.defineProperty() 第三个参数 descriptor 说明: 以对象形式 { } 书写

value: 设置属性的值 默认为undefined
writable: 值是否可以重写。true | false 默认为false
enumerable: 目标属性是否可以被枚举。true | false 默认为 false
configurable: 目标属性是否可以被删除或是否可以再次修改特性 true | false 默认为false

类 class:

类抽象了对象的公共部分,它泛指某一大类(class)
对象特指某一个,通过类实例化一个具体的对象

面向对象的思维特点:
抽取(抽象)对象共用的属性和行为组织(封装)成一个类(模板)
对类进行实例化, 获取类的对象

创建类:
  1. class name{...}

创建实例:

  1. var xx = new name();

类 constructor 构造函数:

constructor() 方法是类的构造函数(默认方法),用于传递参数,返回实例对象,通过 new 命令生成对象实例时,自动调用该方法。如果没有显示定义, 类内部会自动给我们创建一个constructor()

  1. class Person {
  2. constructor(name,age) { // constructor 构造方法或者构造函数
  3. this.name = name;
  4. this.age = age;
  5. }
  6. }

创建实例:

  1. var ldh = new Person('刘德华', 18);
  2. console.log(ldh.name)

类添加方法
  1. class Person {
  2. constructor(name,age) { // constructor 构造器或者构造函数
  3. this.name = name;
  4. this.age = age;
  5. }
  6. say() {
  7. console.log(this.name + '你好');
  8. }
  9. }

类的继承

继承
  1. class Father{ // 父类
  2. }
  3. class Son extends Father { // 子类继承父类
  4. }

实例:

  1. class Father {
  2. constructor(surname) {
  3. this.surname= surname;
  4. }
  5. say() {
  6. console.log('你的姓是' + this.surname);
  7. }
  8. }
  9. class Son extends Father{ // 这样子类就继承了父类的属性和方法
  10. }
  11. var damao= new Son('刘');
  12. damao.say();

super 关键字

super 关键字用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数

  1. class Person { // 父类
  2. constructor(surname){
  3. this.surname = surname;
  4. }
  5. }
  6. class Student extends Person { // 子类继承父类
  7. constructor(surname,firstname){
  8. super(surname); // 调用父类的constructor(surname)
  9. this.firstname = firstname; // 定义子类独有的属性
  10. }
  11. }

注意: 子类在构造函数中使用super, 必须放到 this 前面 (必须先调用父类的构造方法,在使用子类构造方法)

实例:

  1. class Father {
  2. constructor(surname) {
  3. this.surname = surname;
  4. }
  5. saySurname() {
  6. console.log('我的姓是' + this.surname);
  7. }
  8. }
  9. class Son extends Father { // 这样子类就继承了父类的属性和方法
  10. constructor(surname, fristname) {
  11. super(surname); // 调用父类的constructor(surname)
  12. this.fristname = fristname;
  13. }
  14. sayFristname() {
  15. console.log("我的名字是:" + this.fristname);
  16. }
  17. }
  18. var damao = new Son('刘', "德华");
  19. damao.saySurname();
  20. damao.sayFristname();

super关键字 用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数。

  1. class Father {
  2. say() {
  3. return '我是爸爸';
  4. }
  5. }
  6. class Son extends Father { // 这样子类就继承了父类的属性和方法
  7. say() {
  8. // super.say() super 调用父类的方法
  9. return super.say() + '的儿子';
  10. }
  11. }
  12. var damao = new Son();
  13. console.log(damao.say());

构造函数和原型

构造函数

构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与 new 一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。

在 JS 中,使用构造函数时要注意以下两点:

  1. 构造函数用于创建某一类对象,其首字母要大写
  2. 构造函数要和 new 一起使用才有意义

new 在执行时会做四件事情:

  1. 在内存中创建一个新的空对象。
  2. 让 this 指向这个新的对象。
  3. 执行构造函数里面的代码,给这个新对象添加属性和方法。
  4. 返回这个新对象(所以构造函数里面不需要 return )。

JavaScript 的构造函数中可以添加一些成员,可以在构造函数本身上添加,也可以在构造函数内部的 this 上添加。通过这两种方式添加的成员,就分别称为静态成员和实例成员。

静态成员:在构造函数本身上添加的成员称为静态成员,只能由构造函数本身来访问
实例成员:在构造函数内部创建的对象成员称为实例成员,只能由实例化的对象来访问

构造函数的问题

构造函数方法很好用,但是存在浪费内存的问题。

  1. function Star(uname, age) {
  2. this.uname = uname;
  3. this.age = age;
  4. this.sing = function() {
  5. console.log('我会唱歌'); }
  6. }
  7. var ldh = new Star('刘德华', 18);
  8. var zxy = new Star('张学友', 19);

image.png

我们希望所有的对象使用同一个函数,这样就比较节省内存,那么我们要怎样做呢?

构造函数原型 prototype

构造函数通过原型分配的函数是所有对象所共享的。

JavaScript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象。注意这个 prototype 就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。

我们可以把那些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法。

问答?

  1. 原型是什么 ?
    一个对象,我们也称为 prototype 为原型对象。
  2. 原型的作用是什么 ?
    共享方法。

对象原型 proto

对象都会有一个属性 proto 指向构造函数的 prototype 原型对象,之所以我们对象可以使用构造函数 prototype 原型对象的属性和方法,就是因为对象有 proto 原型的存在。

proto对象原型和原型对象 prototype 是等价的
proto对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象 prototype

image.png

constructor 构造函数

对象原型( proto)和构造函数(prototype)原型对象里面都有一个属性 constructor 属性 ,constructor 我们称为构造函数,因为它指回构造函数本身。

constructor 主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。

一般情况下,对象的方法都在构造函数的原型对象中设置。如果有多个对象的方法,我们可以给原型对象采取对象形式赋值,但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象 constructor 就不再指向当前构造函数了。此时,我们可以在修改后的原型对象中,添加一个 constructor 指向原来的构造函数。

构造函数、实例、原型对象三者之间的关系

image.png

原型链

image.png

JavaScript 的成员查找机制(规则)

  1. 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。
  2. 如果没有就查找它的原型(也就是 proto指向的 prototype 原型对象)。
  3. 如果还没有就查找原型对象的原型(Object的原型对象)。
  4. 依此类推一直找到 Object 为止(null)。
  5. proto对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线。

扩展内置对象

可以通过原型对象,对原来的内置对象进行扩展自定义的方法。比如给数组增加自定义求偶数和的功能。

注意:数组和字符串内置对象不能给原型对象覆盖操作 Array.prototype = {} ,只能是 Array.prototype.xxx = function(){} 的方式。

继承

ES6之前并没有给我们提供 extends 继承。我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承。

call()

调用这个函数, 并且修改函数运行时的 this 指向

fun.call(thisArg, arg1, arg2, …)

thisArg :当前调用函数 this 的指向对象
arg1,arg2:传递的其他参数

借用构造函数继承父类型属性

核心原理: 通过 call() 把父类型的 this 指向子类型的 this ,这样就可以实现子类型继承父类型的属性。

  1. // 父类
  2. function Person(name, age, sex) {
  3. this.name = name;
  4. this.age = age;
  5. this.sex = sex;
  6. }
  7. // 子类
  8. function Student(name, age, sex, score) {
  9. Person.call(this, name, age, sex); // 此时父类的 this 指向子类的 this,同时调用这个函数
  10. this.score = score;
  11. }
  12. var s1 = new Student('zs', 18, '男', 100);
  13. console.dir(s1);

借用原型对象继承父类型方法

一般情况下,对象的方法都在构造函数的原型对象中设置,通过构造函数无法继承父类方法。

核心原理:

将子类所共享的方法提取出来,让子类的 prototype 原型对象 = new 父类()

本质:子类原型对象等于是实例化父类,因为父类实例化之后另外开辟空间,就不会影响原来父类原型对象

将子类的 constructor 重新指向子类的构造函数

类的本质

  1. class本质还是function.
  2. 类的所有方法都定义在类的prototype属性上
  3. 类创建的实例,里面也有proto 指向类的prototype原型对象
  4. 所以ES6的类它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
  5. 所以ES6的类其实就是语法糖.
  6. 语法糖:语法糖就是一种便捷写法. 简单理解, 有两种方法可以实现同样的功能, 但是一种写法更加清晰、方便,那么这个方法就是语法糖

函数进阶

函数的定义方式

  1. 函数声明方式 function 关键字 (命名函数)
  2. 函数表达式 (匿名函数)
  3. new Function()

函数的调用方式

  1. 普通函数
  2. 对象的方法
  3. 构造函数
  4. 绑定事件函数
  5. 定时器函数
  6. 立即执行函数

this

函数内 this 的指向

image.png

改变函数内部 this 指向


  1. call() 方法调用一个对象。简单理解为调用函数的方式,但是它可以改变函数的 this 指向。
    fun.call(thisArg, arg1, arg2, …)
    thisArg:在 fun 函数运行时指定的 this 值
    arg1,arg2:传递的其他参数
    返回值就是函数的返回值,因为它就是调用函数
    因此当我们想改变 this 指向,同时想调用这个函数的时候,可以使用 call,比如继承

    call 方法

  2. apply() 方法调用一个函数。简单理解为调用函数的方式,但是它可以改变函数的 this 指向。
    fun.apply(thisArg, [argsArray])
    thisArg:在fun函数运行时指定的 this 值
    argsArray:传递的值,必须包含在数组里面
    返回值就是函数的返回值,因为它就是调用函数
    因此 apply 主要跟数组有关系,比如使用 Math.max() 求数组的最大值

    apply 方法

  3. bind() 方法不会调用函数。但是能改变函数内部this 指向
    fun.bind(thisArg, arg1, arg2, …)
    thisArg:在 fun 函数运行时指定的 this 值
    arg1,arg2:传递的其他参数
    返回由指定的 this 值和初始化参数改造的原函数拷贝
    因此当我们只是想改变 this 指向,并且不想调用这个函数的时候,可以使用 bind

    bind 方法

区别点:
call 和 apply 会调用函数, 并且改变函数内部this指向.
call 和 apply 传递的参数不一样, call 传递参数 aru1, aru2..形式 apply 必须数组形式[arg]
bind 不会调用函数, 可以改变函数内部this指向.

主要应用场景:
call 经常做继承.
apply 经常跟数组有关系. 比如借助于数学对象实现数组最大值最小值
bind 不调用函数,但是还想改变this指向. 比如改变定时器内部的this指向.

高阶函数

高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出。

  1. <script>
  2. function fn(callback){
  3. callback&&callback();
  4. }
  5. fn(function(){alert('hi')}
  6. </script>
  7. <script>
  8. function fn(){
  9. return function() {}
  10. }
  11. fn();
  12. </script>

闭包

闭包(closure)指有权访问另一个函数作用域中变量的函数。 ——- JavaScript 高级程序设计
简单理解就是 ,一个作用域可以访问另外一个函数内部的局部变量。

  1. function fn1(){ // fn1 就是闭包函数
  2.     var num = 10;
  3.     function fn2(){
  4.       console.log(num);
  5.     }
  6. return fn2()
  7.  }
  8. fn1()(); //10

提问:我们怎么能在 fn() 函数外面访问 fn() 中的局部变量 num 呢 ?

  1. function fn() {    
  2. var num = 10;    
  3. return function() {      
  4. console.log(num);     
  5. }
  6. }
  7. var f = fn();
  8. f() // 10

闭包作用:延伸变量的作用范围。

递归

如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。
简单理解:函数内部自己调用自己, 这个函数就是递归函数
递归函数的作用和循环效果一样
由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件 return。

浅拷贝和深拷贝

浅拷贝只是拷贝一层, 更深层次对象级别的只拷贝引用.
深拷贝拷贝多层, 每一级别的数据都会拷贝.
Object.assign(target, …sources) es6 新增方法可以浅拷贝

正则表达式

正则表达式( Regular Expression )是用于匹配字符串中字符组合的模式。在 JavaScript中,正则表达式也是对象。

正则表通常被用来检索、替换那些符合某个模式(规则)的文本,例如验证表单:用户名表单只能输入英文字母、数字或者下划线, 昵称输入框中可以输入中文(匹配)。此外,正则表达式还常用于过滤掉页面内容中的一些敏感词(替换),或从字符串中获取我们想要的特定部分(提取)等 。

其他语言也会使用正则表达式,本阶段我们主要是利用 JavaScript 正则表达式完成表单验证。

特点:

  1. 灵活性、逻辑性和功能性非常的强。
  2. 可以迅速地用极简单的方式达到字符串的复杂控制。
  3. 对于刚接触的人来说,比较晦涩难懂。比如: ^\w+([-+.]\w+)@\w+([-.]\w+).\w+([-.]\w+)*$
  4. 实际开发,一般都是直接复制写好的正则表达式. 但是要求会使用正则表达式并且根据实际情况修改正则表达式. 比如用户名: /[1]{3,16}$/

创建正则表达式

  1. 通过调用 RegExp 对象的构造函数创建
    1. var 变量名 = new RegExp(/表达式/);
  1. 通过字面量创建
    1. var 变量名 = /表达式/;

测试正则表达式 test

test() 正则对象方法,用于检测字符串是否符合该规则,该对象会返回 true 或 false,其参数是测试字符串。

regexObj.test(str)

regexObj 是写的正则表达式
str 我们要测试的文本
就是检测str文本是否符合我们写的正则表达式规范.

正则表达式的组成

一个正则表达式可以由简单的字符构成,比如 /abc/,也可以是简单和特殊字符的组合,比如 /ab*c/ 。其中特殊字符也被称为元字符,在正则表达式中是具有特殊意义的专用符号,如 ^ 、$ 、+ 等。

边界符

image.png

如果 ^ 和 $ 在一起,表示必须是精确匹配。

字符类

字符类表示有一系列字符可供选择,只要匹配其中一个就可以了。所有可供选择的字符都放在方括号内。

  1. [] 方括号
  1. /[abc]/.test('andy') // true

后面的字符串只要包含 abc 中任意一个字符,都返回 true 。

  1. [-] 方括号内部 范围符-
  1. /^[a-z]$/.test('c') // true
  1. [^] 方括号内部 取反符^
  1. /[^abc]/.test('andy') // false

注意和边界符 ^ 区别,边界符写到方括号外面。

量词符

量词符用来设定某个模式出现的次数。

image.png

预定义类

预定义类指的是某些常见模式的简写方式。

image.png

replace 替换

replace() 方法可以实现替换字符串操作,用来替换的参数可以是一个字符串或是一个正则表达式。

  1. stringObject.replace(regexp/substr,replacement)

第一个参数: 被替换的字符串 或者 正则表达式
第二个参数: 替换为的字符串
返回值是一个替换完毕的新字符串

正则表达式参数

  1. /表达式/[switch]

switch(也称为修饰符) 按照什么样的模式来匹配. 有三种值:
g:全局匹配
i:忽略大小写
gi:全局匹配 + 忽略大小写

ES6

let

  • let声明的变量只在所处于的块级有效
    注意:使用let关键字声明的变量才具有块级作用域,使用var声明的变量不具备块级作用域特性。
  • 不存在变量提升
  • 暂时性死区
    1. var tmp = 123;
    2. if (true) {
    3. tmp = 'abc';
    4. let tmp; //初始化之前不能用
    5. }

经典面试题:

  1. var arr = [];
  2. for (var i = 0; i < 2; i++) {
  3. arr[i] = function () {
  4. console.log(i);
  5. }
  6. }
  7. // i是全局的,注意arr[0]和arr[1]没有形参
  8. arr[0](); //2
  9. arr[1](); //2
  1. let arr = [];
  2. for (let i = 0; i < 2; i++) {
  3. arr[i] = function () {
  4. console.log(i);
  5. }
  6. }
  7. arr[0](); //0
  8. arr[1](); //1

此题的关键点在于每次循环都会产生一个块级作用域,每个块级作用域中的变量都是不同的,函数执行时输出的是自己上一级(循环产生的块级作用域)作用域下的i值.

const

作用:声明常量,常量就是值(内存地址)不能变化的量。

  • 具有块级作用域
  • 声明常量时必须赋值
  • 常量赋值后,值不能修改。

解构赋值

ES6中允许从数组中提取值,按照对应位置,对变量赋值。对象也可以实现解构。

数组解构

  1. let [a, b, c] = [1, 2, 3];
  2. console.log(a)
  3. console.log(b)
  4. console.log(c)

对象解构

  1. let person = { name: 'zhangsan', age: 20 };
  2. let { name, age } = person;
  3. console.log(name); // 'zhangsan'
  4. console.log(age); // 20
  5. let {name: myName, age: myAge} = person; // myName myAge 属于别名
  6. console.log(myName); // 'zhangsan'
  7. console.log(myAge); // 20

箭头函数

  1. () => {}
  2. const fn = () => {}

函数体中只有一句代码,且代码的执行结果就是返回值,可以省略大括号

  1. function sum(num1, num2) {
  2. return num1 + num2;
  3. }
  4. const sum = (num1, num2) => num1 + num2;

如果形参只有一个,可以省略小括号

  1. function fn (v) {
  2. return v;
  3. }
  4. const fn = v => v;

箭头函数不绑定this关键字,箭头函数中的this,指向的是函数定义位置的上下文this。

  1. const obj = { name: '张三'}
  2. function fn () {
  3. console.log(this);
  4. return () => {
  5. console.log(this)
  6. }
  7. }
  8. const resFn = fn.call(obj);
  9. resFn();

剩余参数

剩余参数语法允许我们将一个不定数量的参数表示为一个数组。

  1. function sum (first, ...args) {
  2. console.log(first); // 10
  3. console.log(args); // [20, 30]
  4. }
  5. sum(10, 20, 30)

剩余参数和解构配合使用

  1. let students = ['wangwu', 'zhangsan', 'lisi'];
  2. let [s1, ...s2] = students;
  3. console.log(s1); // 'wangwu'
  4. console.log(s2); // ['zhangsan', 'lisi']

ES6 的内置对象扩展

Array 的扩展方法

扩展运算符可以将数组或者对象转为用逗号分隔的参数序列。

  1. let ary = [1, 2, 3];
  2. ...ary // 1, 2, 3
  3. console.log(...ary); // 1 2 3
  4. console.log(1, 2, 3)

扩展运算符可以应用于合并数组。

  1. // 方法一
  2. let ary1 = [1, 2, 3];
  3. let ary2 = [3, 4, 5];
  4. let ary3 = [...ary1, ...ary2];
  5. // 方法二
  6. ary1.push(...ary2);

将类数组或可遍历对象转换为真正的数组:

  1. let oDivs = document.getElementsByTagName('div');
  2. oDivs = [...oDivs];

构造函数方法:Array.from() 将类数组或可遍历对象转换为真正的数组:

  1. let arrayLike = {
  2. '0': 'a',
  3. '1': 'b',
  4. '2': 'c',
  5. length: 3
  6. };
  7. let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']

方法还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。

  1. let arrayLike = {
  2. "0": 1,
  3. "1": 2,
  4. "length": 2
  5. }
  6. let newAry = Array.from(aryLike, item => item *2)

String 的扩展方法

模板字符串
  1. let name = `zhangsan`;

模板字符串中可以解析变量。

  1. let name = '张三';
  2. let sayHello = `hello,my name is ${name}`; // hello, my name is zhangsan

模板字符串中可以换行。

在模板字符串中可以调用函数。

startsWith() 和 endsWith()

startsWith():表示参数字符串是否在原字符串的头部,返回布尔值
endsWith():表示参数字符串是否在原字符串的尾部,返回布尔值

repeat()

repeat方法表示将原字符串重复n次,返回一个新字符串。

Set 数据结构

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值

Set本身是一个构造函数,用来生成 Set 数据结构。

Set函数可以接受一个数组作为参数,用来初始化。

  1. const set = new Set([1, 2, 3, 4, 4]);

实例方法:

add(value):添加某个值,返回 Set 结构本身
delete(value):删除某个值,返回一个布尔值,表示删除是否成功
has(value):返回一个布尔值,表示该值是否为 Set 的成员
clear():清除所有成员,没有返回值

Set 结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值。s.forEach(value => console.log(value))