Javascript学习笔记

JavaScript

ECMAScript - JavaScript的核心

定义了JavaScript 的语法规范

JavaScript的核心,描述了语言的基本语法和数据类型,ECMAScript是一套标准,定义了一种语言的标准与具体实现无关

BOM - 浏览器对象模型

一套操作浏览器功能的API

通过BOM可以操作浏览器窗口,比如:弹出框、控制浏览器跳转、获取分辨率等

DOM - 文档对象模型

一套操作页面元素的API

DOM可以把HTML看做是文档树,通过DOM提供的API可以对树上的节点进行操作

ECMAScript

javascript 和 jscript遵守ECMAscript.

知识补充

计算机

​ 最初的目的:运算数据.

​ 摩尔定律:计算机的运算性能每隔18个月会翻一倍.

​ 最核心的内容是数据!

​ 编写程序来按照人的思想对数据进行加工处理.

​ 本质: 程序操作数据.

组成:

​ 输入 输出 运算 临时存储 永久存储

​ 键盘鼠标 显示器打印机 CPU(中央处理单元) 内存 硬盘

JavaScript

​ JavaScript,一种运行在客户端的脚本语言.

​ 脚本语言: 不需要编译,-运行过程中由js解释器(js引擎)逐行来进行解释 并执行

JavaScript的组成:

  • ECMAScript 是一套JS语言设计标准,描述 JavaScript 语言基本语法和数据类型,以及其它实现标准。

1995-2003: 第一个版本 3.0版本 JS是ECMAScript标准的具体实现

2003年: 4.0版本 太激进

2015年: ECMAScript 5.0 (ECMAScript 2015 , ES5)

2016年: ECMAScript 6.0 (ES6) 里程碑版本 (阮一峰的ES6)

http://es6.ruanyifeng.com

  • BOM: Browser Object Model ,浏览器对象模型,用来操作浏览器.

  • DOM: Document Object Model ,文档对象模型,用来操作HTML页面.

解释型语言和编译型语言

计算机不能直接理解任何除机器语言以外的语言,所以必须要把程序员所写的程序语言翻译成机器语言,计算机才能执行程序。程序语言翻译成机器语言的工具,被称为编译器

编译器翻译的方式有两种:一个是编译,另外一个是解释。两种方式之间的区别在于翻译时间点的不同

编译器是在代码执行之前进行编译,生成中间代码文件。

解释器是在运行时进行及时解释,并立即执行。(当编译器以解释方式运行的时候,也称之为解释器)

JS关键字、保留字、标识符

  • 标识符:就是指开发人员为 变量、属性、函数、参数 取的名字。
    标识符不能是 关键字保留字
  • 关键字:是指 JS本身已经使用了,不能再用它们充当变量名啊方法名啊什么的。
    包括:break、case、catch、continue、default、delete、do、else、finally、for、function、if、in、instanceof、new、return、switch、this、throw、try、typeof、var、void、while、with 等。
  • 保留字:实际上就是预留的“关键字”,意思是现在虽然现在还不是关键字,但是未来可能会成为关键字的,你一样是不能使用它们当变量名或方法名。
    包括:boolean、byte、char、class、const、debugger、double、enum、export、extends、fimal、float、goto、implements、import、int、interface、long、mative、package、private、protected、public、short、static、super、synchronized、throws、transient、volatile 等。
  • 注意:如果将保留字用作变量名或函数名,那么除非将来的浏览器实现了该保留字,否则很可能收不到任何错误消息。当浏览器将其实现后,该单词将被看做关键字,如此将出现关键字错误。
  • name 是 window 的自带全局属性, 值 = ‘’ ,string类型.

面向对象和面向过程

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

​ 封装:封装就是把处理数据的所有步骤封装到一个函数或其他结构中,方便代码的调用和管理,方便重用。

面向对象的编程: 面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。

​ 面向对象和面向过程的主要区别就是数据是单独存储还是与操作存储在一起

​ 对面向过程而言,数据是独立的。而在面向对象中,对象本身就提供了存储数据的空间(类的数据成员),这样就是函数的参数传递简单多了,而且提供了数据封装后,数据的访问也变可靠了。

---------------------------------------------------

两句话:

面向过程是一种自顶向下的编程.

面向对象是将事物高度抽象化,自下先建立抽象模型然后再使用模型 .

ES6新增

块级作用域 使用 let 声明的变量不会进行预解析(变量提升).

知识补充

Unix: 1970发布

​ Minix:

​ Linux: linus

C语言:1969发布

C++:70年代末

C#: C++++

linux:操作系统1990 1w行代码。

​ 2018年9月 2500w行代码。 新提交了4600w行代码,删除了2100万行代码。

JS书写位置

1.行内js(不推荐使用)

<input type="button" value="点我试试" onclick="alert('Hello World')" />  //写在标签内部 

2.内部js

<head>	
	<javascript>
		//代码书写的位置
	</javascript>
</head>

3.外部js(工作中推荐使用)

​ 写在外部的js文件中,使用script标签的src属性引入.

<script src="outdoor.js">script>

变量

1.用于存放数据的容器,通过变量名来获取数据,存放的数据可以修改.

//var uName : 声明变量名字,分配内存空间
var uName = "pink老师";

// age = 18 : 把18赋给age这个变量
var age = 18;

// 重新赋值81,18会被覆盖
age = 81;

// 控制台输出uName和age
console.log(uName,age);

2.报错信息

  • 已声明未赋值 undefiend
  • 未声明未赋值 报错
  • 未声明已赋值 语法宽松,可以使用,但尽量避免

3.变量命名规范

  • 由字母(A-Za-z)、数字(0-9)、下划线(_)、美元符号( $ )组成,如:`var usrAge, num01, _name
  • 不能以数字开头
  • 不能是关键字或保留字
  • 变量名严格区分大小写
  • 建议使用驼峰命名法 userName

4.交换两个变量

		var num1 = 10;
        var num2 = 20;
        console.log('交换前' + num1,num2);
        var temp = num1;
        num1 = num2;
        num2 = temp;
        console.log('交换后' + num1,num2);

数据类型

数据是有不同类型的,他们的存储和使用方式也不同.

JavaScript属于弱数据类型的语言.

​ Java 强数据类型

		int num = 10;  //必须声明数据类型
		float num2 = 1.1; 

​ JavaScript数据类型不固定,给什么值就是什么数据类型.


		var num = 10;  //数值类型
		num = "pink";  //文本类型

字面量

是一个固定值的表示方法, 从 = 右边看上去就知道它是什么数据类型.


		var num = 66;   //  66  数值型的字面量 

数据类型

Number 数值型

​ 1. js只有一种数值型,不区分整数和浮点数

​ 2. 不要用浮点数进行计算

​ 3. Infinity 正无穷大 (Number.MAX_VAKUE + 1)

4. NaN   not a number 不是一个数值
isNaN();  // 返回 不是数值 true| 是数值false
Boolean 布尔型

​ True (真) | False (假)

String 字符型

​ 带有引号的就是字符型.

​ 字符型里内容的引号,采用内双外单,外双内单.

​ 变量名一定不要加引号.

​ 转义字符

  • \n 换行
  • \ \ 斜杠\
  • \ ’ 单引号 ’
  • \’’ 双引号
  • \t Tab
  • \b 空格
  • \r 回车
字符串的长度
var str = 'abcdef';
// 输出字符串的长度
console.log(str.length);
字符串的拼接

只要有字符型的进行了相加,本质就是相连.

var str1 = 'pink老师';
var str2 = '18';
var str3 = '岁';
// 输出 'pink老师18岁'
console.log(str1 + str2 + str3);
undefined未定义型
	var num;   // undefined  声明变量未给值

判断数据类型

isNaN();  // 返回 不是数值 true| 是数值false
var num = 10;
console.log(typeof num); // 输出num的

数据类型转换

转换为字符型
  • toString(),可把一个逻辑值转换为字符串,并返回结果.
// 布尔类型的 toString() 只会输出 "true" 和 "false"

var num = 10;

console.log(num);  // 输出数值型的  10 

console.log(num.toString());  // 输出字符型的  '10'
  • String(),强制转换,有些值没有toString(),这个时候可以使用String()。
var  timer = null;

console.log(String(timer));  //  输出"false"
  • 加号拼接字符串(重点)

    当 + 两边 一个是 字符串类型,另一个是 其它类型 的时候,会先把 其它类型 转换成 字符串 再进行字符串拼接,最后返回字符串.

    alert(21 + "小白");  // 输出"21小白"
    
    alert(false + "小白");  // 输出"false小白"
    
转换为数值(重点)
  • parseInt(string)
    • 将 数值字符串 转换为 整数数值.
// 规则1.永远记住它是取整函数
var numLove = parseInt(18.08); // 18
var numLove = parseInt(18.88); // 18

// 规则2.如果第一个字符不是数字符号或者负号,返回NaN
var numLove = parseInt("aboard211"); // NaN

// 规则3.如果第一个字符是数字,则继续解析直至字符串解析完毕 或者 遇到一个非数字符号为止
var numLove = parseInt("520littlecat"); // 520
  • parseFloat(string)
    • 将 浮点数值字符串 转成 浮点数值.
var num = parseFloat("12.3abc"); // 12.3

//parseFloat函数如果用来转换 整型数值字符串,则也是返回 整型数值
var num = parseFloat("12"); // 12,而不是 12.0
  • Number()
    • 强制转换函数,要转换的字符串中不可以有字母.
      里面如果只要出现非数字字符或者undefined, 则就返回 NaN
    • 如果该值是空字符串、数字0、或null、false 则返回 0 如果是 true 则返回 1
  • 利用js隐式转换
    • 利用了js的弱类型的特点,进行算术运算,实现了字符串到数字的类型转换.
var   str= '123 ';  // - * /
var   x   =   str-0;  // 输出数值型 123
var   x   =   x*1;   // 输出数值型 123
转换为布尔值
  • Boolean()

    • 代表 空、否定的值 会被转换为 false 有五种 “ ‘’、0、NaN、null、undefined
    • 非空字符串 非0数字 true 任何对象都会被转换为 true
    var res = Boolean(''); // false
    res = Boolean(0); // false
    res = Boolean(NaN); // false
    res = Boolean(null); // false
    res = Boolean(undefined); // false
    
    var res2 = Boolean('小白'); // true
    var res2 = Boolean(12); // true
    
  • 布尔类型的隐式转换

	if('哈哈' && 12 )  等同于  if(true && true )

基础输入输出

1.弹出警示框

	alert();

2.输出到控制台

	console.log();

3.提示用户输入框,接收用户输入信息.

​ 用户输入的 任何内容 都是一个 字符串

	prompt("提示文字");  // 从键盘接收用户的输入

运算符

算数运算符

var res = 15 + 6;  // + 加  还可以字符串拼接
res = 21 - 15;  // - 减
res = 3 * 5;  // * 乘
res = 10 / 20;  // / 除
res = 9 % 2;  // 取余数(取模),返回除法的余数 9 % 2 = 1 先取余后加减乘除

一元运算符

​ 一些只需要一个操作数的运算符称为一元运算符

​ 比如 +15 -15 正负 ! 取反 还有 ++ 和 –

前置运算(理解)

  • ++num 前置自增 :先自加 后返回值
var num = 7;
alert(++num); // 8
alert(num);   // 8

后置运算(重点)

  • num++ 后置自增:先 返回原值 后 自加
var num = 7;
alert(num++); // 7
alert(num);   // 8

自增自减运算符 小结

  • ++-- 运算符目的可以简化代码的编写,让变量的值 + 1 或者 - 1

  • 只能用于变量;

  • 单独使用时,运行结果相同;

  • 与其他代码联用时,执行结果会不同

  • 后置:先 原值运算 后 自加 —先人后己

  • 前置:先自加 后运算

  • 开发时大多使用后置自增/减,并且代码独占一行,例如:num++; 或者 num--;

  • 开发时,和其他代码联用 —— 会降低代码的可读性

  • 开发的时候,我们更喜欢用 num++

关系(比较)运算符

​ 用来 两个数据 进行比较的运算符,会 返回 一个 布尔值(true / false),作为比较运算的结果.

运算符名称 说明 案例 结果
< 小于号 1 < 2 true
> 大于号 1 > 2 false
>= 大于等于号 (大于或者等于) 2 >= 2 true
<= 小于等于号 (小于或者等于) 3 <= 2 false
== 判等号(会隐式转换) 37 == ‘37’ true
!= 不等号 37 != 37 false
=== !== 全等于 要求值和 数据类型都一致 (不会隐式转换) 37 === ‘37’ false

=总结

符号 作用 用法
= 赋值 把右边给左边
== 判断 判断两边值是否相等(注意此时有隐式转换)
=== 全等 判断两边的值和数据类型是否完全相同

逻辑运算符

​ 用来进行若干个布尔值 运算的 运算符,返回值也是布尔值。

逻辑运算符 说明 案例
&& “逻辑与”,简称 “与” and exp1 && exp2
|| “逻辑或”,简称 “或” or exp1 || exp2
“逻辑非”,简称 “非” not ! exp1
  • 逻辑与 &&:** 两边都为 true才返回 true,否则返回 false
var res1 = 2 > 1 && 3 > 1; // 读作: 2 > 1 且 3 > 1
console.log(res1); // true

var res2 = 2 > 1 && 3 < 1; // 读作: 2 > 1 且 3 < 1
console.log(res2); // flase
  • 逻辑或 ||:** 两边都为 false才返回 false 否则都为true.
var res1 = 2 > 1 || 3 > 1; // 读作: 2 > 1 或 3 > 1
console.log(res1); // true

var res2 = 2 > 1 || 3 < 1; // 读作: 2 > 1 或 3 < 1
console.log(res2); // true

var res3 = 2 < 1 || 3 < 1; // 读作: 2 < 1 或 3 < 1
console.log(res3); // flase
  • 逻辑非 ! :** 也叫作 取反 符。用来取一个布尔值相反的值,如 true 的相反是 false.
var isOk = !true;
console.log(isOk); // false

赋值运算符

​ 用来把数据赋值给变量, 返回值 是 =右边的 .

var age = 10;  // 把10 赋值给变量 age  
赋值运算符 说明 案例
= 直接赋值 var usrName = ‘我是值’;
+=、-= 加、减 一个 数 后在赋值 var age = 10; age+=5; // 15 age = age + 5
*=、/=、%= 乘、除、取模 后在赋值 var age = 2; age*=5; // 10 age = age * 5
var age = 10;
age += 5;  // 相当于 age = age + 5;
age -= 5;  // 相当于 age = age - 5;
age *= 10; // 相当于 age = age * 10;

运算符优先级

优先级从高到底

1. ()  优先级最高 
2. 一元运算符  ++   --   !
3. 算数运算符  先*  /  %+   -
4. 关系运算符  >   >=   <   <=
5. 相等运算符   ==   !=    ===    !==
6. 逻辑运算符 先&&||
7. 赋值运算符

规律:
先一元   后  二元 
先 算数  后 关系

流程控制

顺序结构

概念: 由上至下的执行代码就是顺序结构。

分支结构

if…else…结构
  1. if 语法:
// 条件成立执行代码,否则什么也不做。
if (条件表达式) {
     
    // [如果] 条件成立执行的代码语句
}

// 判断年龄进入网吧
var age = prompt('请输入年龄');
if (age >= 18) {
     
    alert('您的年龄合法,欢迎来到七号网吧上网!');
}
  1. if-else 语法:
// if 如果  else 否则 
// 条件成立执行代码,否则执行另外的代码。
if (条件表达式) {
     
    // [如果] 条件成立执行的代码
} else {
     
    // [否则] 执行的代码
}

//判断是否为闰年 能被4整除且不能被100整除 或者 能被400整除
var year = prompt('请输入年份');
if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
     
    alert(year + '年是闰年!');
} else {
     
    alert(year + '年不是闰年!');
}
  1. if-else-if 语法:
//适合于检查多重条件。
if (条件1表达式) {
     
    // [如果] 条件1 成立执行的代码
} else if (条件2表达式)  {
     
    // [否则] [如果] 条件2 成立执行的代码
    // 注释:条件1 不成立,但是条件2 成立
} else if (条件3表达式)  {
     
    // [否则] [如果] 条件3 成立执行的代码
    // 注释:条件1 2 不成立,但是条件3 成立
} else {
     
    // 上述条件都不成立执行的代码
}

// 判断学生成绩
var score = prompt('请输入你的成绩');
if (score >= 90) {
     
    alert('优秀');
} else if (score >= 60) {
     
    alert('及格');
} else {
     
    alert('补考吧');
}
三元表达式 ? ?*

​ 如果表达式1为true ,则整个表达式的结果就是表达式2的,如果表达式false,则整个表达式的结果就是表达式3的值.

var a = 10;
var result = 0;
if(a > 7){
     
    result = 21;
}else{
     
    result = 12
}
alert(result); // 21

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

var a = 10;
var result = (a > 7) ? 21 : 12;  // 21
switch…case…结构

​ 使用结果表达式 的值 和 各个 case 中的值 进行相等比较.

​ switch 比较适合有限个值,并且值不连续的情况.

​ if 更适合值是在一个范围内的情况.

switch( 变量 ){
      
    case value1:
        //表达式结果 等于 value1 时 要执行的代码
        break;
    case value2:
        //表达式结果 等于 value2 时 要执行的代码
        break;
    // default 可以省略
    default:
        //表达式结果 不等于任何一个 value 时 要执行的代码
}

执行顺序:

  1. 先 从变量中 获取一个 ,随后 表达式的值 会与结构中的 case 的值 做比较。

  2. 如果存在匹配 **全等(===) 即( 变量值 === value 值) ** ,则与该 case 关联的 代码块 会被执行,

并在遇到 break 时停止,整个 switch 代码执行结束。

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

  2. 我们case 后面的值 通常都是一个常量。

循环结构

for循环

for循环小括号中的内容,是利用一个变量作为计数器控制循环次数.

for循环大括号的内容,是每次循环要执行的代码.

大括号中可以使用计数器变量的值.

//for 循环一般 用来 根据次数 循环 
for(初始化; 条件表达式; 自增表达式 ){
     
    //循环体
}
//如:
for(var i = 0; i < 10; i++){
     
    console.log('i='+i);
}

执行顺序

​ 1.初始化

​ 2.条件表达式 -> 3.循环体 -> 4.自增表达式

// 求1-100之间所有偶数的和
var sumNum = 0;
for(var i = 1;i <= 100; i++){
     
    if(i % 2 == 0){
      // 如果 i 取模2 等于0,则 i 为 偶数
        sumNum += i;
    }
}
console.log('1-100之间所有偶数的和 = ' + sumNum);
双重 for 循环
for (外循环的初始; 外循环的条件; 外循环的增量) {
     

for (内循环的初始; 内循环的条件; 内循环的增量) {
       

   需执行的代码;

   }

}
  1. 内层循环可以看做外出循环的语句。
  2. 内层循环执行的顺序也要遵循for循环的执行书序。
  3. 外层循环执行一次,则内层循环要执行全部次数(跑完毕)。
  • 使用 for循环 打印 三角形(9行 * 9列)
var str = '';
// 外层循环控制行数 1-9
for (var i = 1; i <= 9; i++) {
     
    // 内层循环控制每行的星星数  1:9-i(1) 2:9-i(2)  
    for (var j = 9; j >= i; j--) {
     
        str += '☆';
    }
    // 内层循环每执行完一次就换行,代表一行
    str += '\n';
}
console.log(str);
  • 使用 for循环 打印 九九乘法表
var str = '';
// 外层控制行数  1-9
for (var i = 1; i <= 9; i++) {
     
    // 内层控制列数  1-i  1:1-1 2:1-2 3:1-3
    for (var j = 1; j <= i; j++) {
     
        str += ( j + '*' + i + '=' + (i*j) + '\t');
    }
    // 每行执行完成后加换行
    str += ('\n');
}
console.log(str);
while 循环
//条件表达式为 true 时重复执行循环体代码
//条件表达式为 false 退出循环
while (条件表达式) {
     
    // 循环体代码 
}

条件表达式 的结果是一个 布尔值 ,为 true 时,执行循环体代码,为 false 时,退出循环,执行后面代码。

  • 执行顺序: 先判断,再执行循环体

    1.先执行 条件表达式 ,结果为 true,则 执行循环体代码,如果为 false,则退出 循环,进入后面代码执行

    2.然后执行 循环体代码 ,只要 条件表达式 为真,则会一直执行。

// 在控制台打印 10 遍 'Hi,有空吗?'
var numCount = 1; // 用来记录循环次数
while(numCount <= 10){
      // 判断循环的次数是否达到我们的条件
    console.log('Hi,有空吗?'); // 要执行的循环体 业务代码
    numCount++; // 做条件改变,否则 循环会出现【死循环】
}
  • while 语句就是当条件满足重复执行相同的代码
  • 无论是循环结构还是分支结构,在执行完成后,都会执行后续代码;
  • 在循环体内部,需要修改循环条件,否则会造成死循环
do…while 循环
do{
     
    // 循环体代码 - 条件表达式为 true 时重复执行循环体代码
} while(条件表达式);
  • 执行顺序 先执行一次循环体代码,再判断 循环条件

    1.先执行一次 循环体代码

    2.再执行 条件表达式 ,结果为 true,则 继续执行循环体代码
    ​ 如果为 false,则退出 循环,进入后面代码执行

// 输出你喜欢我吗?(y/n):",直到输入y退出,否则继续询问。
do {
     
   var ask = prompt('你喜欢我吗? y/n');
} while (ask !== 'y');
alert('我也喜欢你啊');
  • do…while 循环和 while 循环非常像,二者经常可以相互替代,但 do…while 的特点是不管条件成不成立,都会执行一次。
  • do…while 语句就是先执行一遍,后面才根据 条件是否满足 决定执行次数;
  • 和 while 循环一样,在循环体内部,需要修改循环条件,否则会造成死循环
continue 跳出本次循环
  • **概念:**立即跳出本次循环,继续下一次循环(本次循环体 continu 之后的 代码 就都少执行这一次)
  • 通俗: 吃5个包子,第3个有虫子,就扔掉第3个,继续吃第4个第5个包子。
break 退出整个循环
  • **概念:**理解跳出整个循环(循环结束),开始执行 循环后面的代码
  • 通俗: 吃5个包子,吃到第3个吃饱了,就不再吃后面的包子。

代码调试

  • alert()

  • console.log()

  • 断点调试

断点调试是指自己在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住,然后你可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码行即显示错误,停下。

断点调试步骤

浏览器中按F12-->sources-->找到需要调试的文件-->在程序的某一行设置断点
  • 调试中的相关操作
Watch: 监视,通过watch可以监视变量的值的变化,非常的常用。
F11: 程序单步执行,让程序一行一行的执行,这个时候,观察watch中变量的值的变化。
F8:跳到下一个断点处,如果后面没有断点了,则程序执行结束。

tips: 监视变量,不要监视表达式,因为监视了表达式,那么这个表达式也会执行。

  • 1.先到 console 控制台 tab 页 去看 是不是 有 报错!(JS语法错误)
  • 2.如果 没有语法错误,就应该 resource 资源 tab页 去页面js代码中设置断点,并 f11 逐句调试。

数组

  • 一组有序的数据

创建数组

​ 1. 通过new 方式 创建

    var  数组名  =   new Array()

​ 2. 通过字面量的形式

    var arrStus02 = ['小白','小黑','大黄','瑞奇'];

​ 3. 数组中可以存放任意类型的数据

	var arrStus03 = ['小白',12,true,28.9];
  • 元素:数组中每个空间里存放的数据

  • **下标 (索引) :**用来访问数组空间的 序号 (数组下标从 0 开始)

    数组可以通过 下标 来 访问、设置、修改 对应下标空间 里的元素。

    格式: 数组名[下标]

     var arrStus = [1,2,3];
     alert(arrStus[1]); // 2
    
  • 数组.length 用来访问数组里空间的数量(数组长度)

  • 遍历数组所有成员

    遍历**就是把每个元素从头到尾都访问一次 (类似我们每天早上学生的点名)

    通过for循环下标遍历

 for (var i = 0; i < arr.length; i++) {
     
            console.log(arr[i]);
}
  • 新增数组元素

    JS 里的数组可以通过直接 访问 下标来实现扩容的目的

    一定 要用 数组加下标的方式 追加数组元素, 不能直接给数组名赋值, 否则会覆盖掉以前的数据。

数组去重

var arr = [2, 0, 6, 1, 77, 0, 52, 0, 25, 7];
// 声明新的空数组
var newArr = [];
// 空数组的默认的长度为 0 
console.log(newArr.length);

// 定义一个变量 用来计算 新数组的索引号
for (var i = 0; i < arr.length; i++) {
     
    // 找出 大于 10 的数
    if (arr[i] != 0) {
     
        // 给新数组
        // 每次存入一个值, newArr长度都会 +1  
        newArr[newArr.length] = arr[i];
    }
}
console.log(newArr);

数组反转

var arr = ['red', 'green', 'blue', 'pink', 'purple'];
var newArr = [];
for (var i = 0; i < arr.length; i++) {
     
    // newArr 一定是接受方    arr 是 输送方
    newArr[i] = arr[arr.length - i - 1];
}
console.log(newArr);

冒泡排序

   var arr = [5, 4, 3, 2, 1];
        for (var i = 0; i < arr.length - 1; i++) {
     
            for (var j = 0; j < arr.length - i - 1; j++) {
     
                if (arr[j] > arr[j + 1]) {
     
                    var temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
 console.log(arr);

函数

声明

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

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

// 带参数的函数调用
函数名(实参1, 实参2, 实参3); 

参数

** 在函数内部某些值不能固定,只有在调用的时候才能确定这个值**

参数 定义
形参 **形式上的参数 函数定义的时候 传递的参数 当前并不知道是什么 **
实参 实际的参数 函数调用的时候传递的参数 这个参数我们知道 给形参赋值

返回值

** 函数执行完后,可以把执行的结果 通过 return 语法 返回给 调用者**

// 语法:
function 函数名(){
     
    return  需要返回的值;  // return后面的值不会被执行
}
// 函数调用
函数名();    //   此时 调用 函数就可以得到   函数体内return  后面的值  

arguments

JavaScript中,arguments对象是比较特别的一个对象,实际上是当前函数的一个内置属性。也就是说所有函数都内置了一个arguments对象,arguments对象中存储了传递的所有的实参。arguments是一个伪数组,因此及可以进行遍历

当我们不确定有多少个参数传递的时候,可以用arguments 来获取

//求任意个数数字的和

 function sumFun() {
     
            var sum = 0;
            for (var i = 0; i < arguments.length; i++) {
     
                sum += arguments[i];
            }
            return sum;
        }
console.log(sumFun(1, 3, 5));
console.log(sumFun(2, 4, 6));

函数声明的两种方式

  1. 函数声明方式

    会在预解析阶段,把函数声明提升到所在作用域的最前面.

    // 命名函数   有函数名 为  fn 
    function fn() {
            ....}
    // 调用  那个地方调用都可以
    fn()  
    
  2. 函数表达式方式

    在预解析阶段,只提升变量声明,不提升变量的赋值.

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

匿名函数

​ 没有名字的函数. 调用匿名函数时,必须放在函数声明语句之后 (与普通函数的区别).

​ 因为匿名函数声明之后,在预解析时,只会提升用于接收函数体的变量名的声明,不会把函数体赋值给变量名.

// 使用匿名函数表达式创建
// 将 fun () {} 赋值给 变量名
var fun = function () {
     
	// 函数体
}
// 输出fun会输出整个函数, 
cosole.log(fun);
// 
// fun() 会调用这个函数得到返回值
fun();

匿名函数起名的作用域问题 //一般不会这样干

// 函数表达式声明的函数 , b为函数标识符, 只在函数内部可以访, 并且匿名函数也用非要起个函数名.
var a = function b() {
     
	console.log(b);
}
a(); // 输出  function b() {console.log(b);}
b(); // b is not defined   b去哪了?

// 以下  正常函数声明, b为函数名, 是可以在函数外部正常访问的
function c() {
     
    console.log(c);
}
c();

自执行函数

​ 不需要调用,函数初始化时就会执行的函数.

// 自执行函数的三种写法
! function () {
     
	console.log('1. 使用!开头,结构清晰,不容易混乱,推荐使用');
}();

(function() {
     
	console.log('2. 能够将匿名函数与调用的()为一个整体,官方推荐使用');
}())

(function () {
     
	console.log('3. 无法表明函数与之后的()的整体性,不推荐使用');
})();

函数的数据类型

function fn() {
     
   console.log('11');
 }
 console.log(typeof fn); // 输出 function

函数作为参数

function getFn() {
     
    console.log('我也是一个函数')
 }
 // 声明函数
 function fn(fun) {
       // 此时 fun = getFn = function getFn() {console.log('我也是一个函数')}
     // 调用传递过来的 函数
     fun();  // 相当于调用getFn()
}
// 调用函数
fn(getFn);

函数作为返回值

function fn() {
     
    return function() {
       // 直接把函数作为返回值返回
        console.log('函数可以作为返回值');
    }
}

var ff = fn();  // fn函数返回了 function(){console.log(''函数可以作为返回值);}

ff();  // ff 里面是 fn 中的匿名函数  输出 '函数可以作为返回值'

作用域

JS中没有块级作用域(在ES5之前) if else for 等大括号

ES6 中是有块级作用域的,使用 let 声明,只在当前大括号中有效.

  • 全局作用域

    供所有代码执行的环境(整个script标签内部) 或者一个独立的js文件中

  • 局部作用域(私有作用域)

    在调用函数的时候,会形成一个执行函数内代码的新环境。

  • 全局变量

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

    • 在局部作用域下声明的变量叫做局部变量(在函数内部定义的变量)
    • 局部变量只能在该函数内部使用
  • var scope = 12; // 全局变量
    function demo() {
           
        var local = 1; // 局部变量
        console.log(scope) // 12
        console.log(local) // 1
    }
    console.log(scope) // 12
    console.log(local) // 报错 local is not defined
    
  • 函数的形参实际上就是局部变量

  • 局部变量当其所在的代码块被执行时,会被初始化,当代码块运行结束后,就被销毁了,节省内存空间。

  • 全局变量因为任何一个地方都可以使用,只有再浏览器关闭才会销毁,比较占内存。

作用域链

变量的一个查找的顺序.

就近原则, 当前有就用当前作用域逇变量,当前没有,就去上一级找.

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

预解析

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

在正式执行代码之前, 要对代码进行一次扫描,提升变量和函数,就是预解析.

为了解决函数的定义和调用的顺序问题.

  • 预解析过程

    • JavaScript解析器会在全局环境下查找 var、function关键字,变量只声明不赋值,函数声明不调用。
    • 预解析只发生在当前作用域下 函数作用域 ( let 大括号)
  • 预解析也叫做变量、函数提升 (函数的提升会在变量的前面)

    • 变量提升

      定义变量的时候,变量的声明会被提升到当前作用域的最上面,变量的赋值不会提升

    • 函数提升

      JavaScript解析器首先会把当前作用域的函数声明提前到整个作用域的最前面

  • 执行过程代码演示

    • 变量赋值、函数调用、表达式运算等等。
		var a = 1;
        function fn(a) {
     
            console.log(a);
            var a = 2;
            function a() {
     }
            console.log(a);
        }
        fn(1);
        // 等价于 
		var a = 1;
        function fn(a) {
       // 1
            var a;  // undefined
            function a() {
     }  // function a() {}
            console.log(a);  // 输出function a() {}
            a = 2;  // 2
            console.log(a);  // 输出 2
        }
        fn(1);
		var a = 3;
        function getA() {
     
            if (false) {
     
                var a = 1;
                this.a = 2;
            }
            console.log(a);
        }
        getA();
		// 等价于
		var a = 3;
        function getA() {
     
            var a; // undefined
            if (false) {
       // if条件为false,大括号里的代码不执行
                a = 1;
                this.a = 2;  // 如果执行,Wdindow.a = 2;
            }
            console.log(a);  // 输出 undefined
        }
        getA();

输出斐波那契数列

		// 斐波那契数列 1 1 2 3 5 8 13 21 34
		// 输出斐波那契数列 前9位
        var a = 1;
        var b = 1;
        var c;
        for (var i = 3; i <= 9; i++) {
     
            c = a + b;
            a = b;
            b = c;
        }
        console.log(c);

        // 输出斐波那契数列 第n位 的值
		// 递归写法  公式 fn(n-1)+fn(n-2)
        function fn(n) {
     
            if (n == 1 || n == 2) {
     
                return 1;
            }
            return fn(n - 1) + fn(n - 2);
        }
        console.log(fn(9));

判断质数

		// 质数,只能被1和自身整数的数

        function isZhi(num) {
     
        
            var count = 0;
            
            // 循环取余  只有因子是两个的才是素数
            for (var i = 1; i <= num; i++ ) {
     
                if (num % i == 0) {
     
                    count++;
                }
            }
            
            if(count == 2) {
     
                return '是素数';
            } else {
     
                return '不是素数';
            }
        }
        console.log(isZhi(10));

对象

对象

  • 对象 封装 相关的 属性 和 `方法

  • 因为可以为对象扩展属性和方法,我们现在 可以用对象把 一组相关的 变量 和 函数 关联 起来,访问和作为参数传递起来方便。

创建对象

对象就是一组 无序的 相关属性和方法集合

注意: 函数 用来按功能 封装代码对象 用来按功能 封装方法和属性,都起到复用代码和数据的作用

  • 对象字面量 方式创建
    • 对象字面量:**是封闭在花括号对 {} 中的一个对象的0个或多个 键:值 无序列表
    • **键:**相当于属性名
    • **值:**相当于属性值,可以是任意类型的值(数值类型、字符串类型、布尔类型,甚至 函数类型)
    		var stu1 = {
           
                name: '杜东轩',
                age: 20,
                gender : '男',
                study : function() {
           
                    console.log('学习');
                }
            };
    
  • new Object 创建对象

    • 跟我们前面学的 new Array() 一样。
    • Object() 是构造函数 第一个字母大写
    • new Object() 是调用构造函数 因为构造函数需要new 来调用 同时再内存中创建一个对象
    		var stu = new Object();
    		stu.name = '杜东轩';
            stu.age = 20;
            stu.gender = '男';
    		stu.study = function() {
           
        		console.log('学习');
    		}
    
对象调用
  • 对象里面的属性调用 : **对象.属性名 ** 这个小点 就理解为 的
  • 对象里面的属性另外调用方式 : **对象[‘属性名’] ** 注意 方括号里面的属性 必须加 引号 我们后面会用
  • 对象里面的方法调用: **对象.方法名() ** 注意这个方法名字后面一定加括号
console.log(stu.name)  // 调用 名字属性
console.log(stu.age)  // 调用 年龄属性
star.study();  // 调用 study 方法  

函数和方法的区别:

  • 函数是单独存在的, 调用的时候 函数名() 就可以了
  • 方法是在对象里面, 调用的时候,对象.方法名()
自定义构造函数

构造函数 ,是一种特殊的函数。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。

  1. 构造函数用于创建某一大类对象,首字母要大写
  2. 构造函数要和new一起使用才有意义。
		// 声明一个构造函数
		function Student(name, age, gender) {
     
            this.name = name;
            this.age = age;
            this.gender = gender;
            this.study = function() {
     
                console.log('学习');
            }
        }
        // 使用构造函数创建对象
		var stu = new Student('杜东轩',20,'男');
        var stu = new Student('杜慧妍',12,'女');

构造函数和对象

  1. Student() 是构造函数,主要是 初始化对象 用的. 泛指某一大类.
  2. new Student() 是 创建对象 用的. 特指某一个.
  3. 通过new 关键字创建对象的过程也叫 对象实例化 .
new关键字
		var stu = new Student('杜东轩',20,'男');
        var stu = new Student('杜慧妍',12,'女');

new在执行时会做四件事情

1. new会在内存中创建一个新的空对象
2. new 会让this指向这个新的对象
3. 执行构造函数里面的代码  目的:给这个新对象加属性和方法
4. new会返回这个新对象 (所以构造函数里面不需要return
this关键字
1. 函数在定义的时候this是不确定的,只有在调用的时候才可以确定
2. 一般函数直接执行,内部this指向全局window
3. 函数作为一个对象的方法,被该对象所调用,那么this指向的是该对象(谁调用指向谁)
4. 构造函数中的this  对象的实例  
// 1. 普通函数
function fn() {
     
    console.log(this); // this 指向 window
 }
fn();
 // 2 对象方法
var obj = {
     
    name: 'zs',
    dance: function() {
     
         console.log(this);
         that = this;
	}
}
obj.dance(); //  this 指向 obj (对象)
console.log(that === obj); // true
// 3 构造函数
function Fn() {
     
    this.age = '18';
    console.log(this)
    self = this;
}
var demo = new Fn(); // this  指向 demo (构造函数当前所创建的对象)
console.log(self === demo); // true
		// 面试题
        var x = 3;
        var foo = {
     
            x: 2,
            baz: {
     
                x: 1,
                bar: function () {
     
                    return this.x;
                }
            }
        }
        var go = foo.baz.bar;
        // 相当于
        var go = function () {
     
                // go是普通函数, this指向window   window.x = 3
                    return this.x;  
                }
        console.log(go()); // 3
        // 对象方法里的this  指向其对象  baz.x = 1
        console.log(foo.baz.bar()); // 1 
遍历对象的属性

for…in 语句用于对数组或者对象的属性进行循环操作。

for (变量 in 对象名字) {
     
    在此执行代码
}
// 创建一个对象
var stu = {
     
            name: '杜东轩',
            age: 20,
            gender : '男'
        };
// 遍历这个对象中的属性和方法
for (var k in stu) {
     
        console.log(k);
        console.log(stu1[k]);
		}
遍历 JSON格式

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。 易于人阅读和编写。

  1. JSON 数据的书写格式是:名称/值对。
  2. 里面的属性和值都要双引号 括起来
		// 创建JSON格式的数据
		var json = {
     
            "name" : "杜东轩",
            "age" : 20,
            "gender" : "男"
        };
        // 遍历JSON格式的数据
        for (var k in json) {
     
            console.log(k);
            console.log(json[k]);
        }

// 查看对象中的属性和值

console.dir(obj);

内置对象

Math对象

Math 是一个内置对象, 它具有数学常数和函数的属性和方法。不是一个函数对象。

Math常用方法
Math.PI						// 圆周率
Math.floor() 	              // 向下取整
Math.ceil()                   // 向上取整
Math.round()				// 四舍五入版 就近取整   注意 -3.5   结果是  -3 
Math.abs()					// 绝对值
Math.max()/Math.min()		 // 求最大和最小值
Math.sin()/Math.cos()		 // 正弦/余弦
Math.pow()/Math.sqrt()	 // 求指数次幂/求平方根
Math.random 生成随机数

随机返回一个小数 , 取值范围 是 范围**[0,1)** 左闭右开 0 <= x < 1

console.log(Math.random()); // 0.40645855054029756
  • 求10-20(包含10和20)之间的随机整数
function getRandom(min, max) {
   return Math.floor(Math.random() * (max - min + 1) + min);
}
console.log(getRandom(10, 20))
  • 随机生成颜色RGB
function getRandom(min, max) {
     
    return Math.floor(Math.random() * (max - min + 1) + min);
}

function getRGB(min, max) {
     
    var c1 = getRandom(min, max);
    var c2 = getRandom(min, max);
    var c3 = getRandom(min, max);
    return 'rgb(' + c1 + ', ' + c2 + ', ' + c3 + ')';
}
console.log(getRGB(0, 255));
  • 随机生成十六进制颜色
function getRandom(min, max) {
     
    return Math.floor(Math.random() * (max - min + 1) + min);
}

function getColor() {
     
	var arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
	var str = '#';
	for (var i = 0; i < 6; i++) {
     
    	str += arr[getRandom(0, 15)];
	}
	return str;
}
console.log(getColor());
  • 利用对象 模拟 max 和 min
var myMath = {
     
    PI: 3.141592653,
    max: function() {
     
        var max = arguments[0];
        for (var i = 0; i < arguments.length; i++) {
     
            if (max < arguments[i]) {
     
                max = arguments[i];
            }
        }
        return max;
    },
    min: function() {
     
        var min = arguments[0];
        for (var i = 0; i < arguments.length; i++) {
     
            if (min > arguments[i]) {
     
                min = arguments[i];
            }
        }
        return min;
    }
};
console.log(myMath.max(1, 6, 8, 9));
console.log(myMath.min(1, 6, 8, 9));
Date对象
Date() 的使用
// 创建日起对象,获取当前时间
var now = new Date();
console.log(now);	// 返回 Fri Mar 29 2019 13:12:16 GMT+0800 (中国标准时间)

Date构造函数的参数
// 括号里面时间 ,就返回 参数里面的时间
日期格式字符串 '2015-5-1'	 
new Date('2015-5-1')
new Date('2015/5/1') 
  • 如果Date()不写参数,就返回当前时间
  • 如果Date()里面写参数,就返回 括号里面输入的时间
获取日期的毫秒形式

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

// 创建一个日期对象
var now = new Date();
// 1. valueOf()
console.log(date.valueOf());

// 2. getTime()
console.log(date.getTime());

// 3. + new Date()
var now = + new Date();  // new Date()返回对象, + new Date() 返回1970年起的毫秒数

// 4. HTML5中提供的方法,有兼容性问题
var now = Date.now();
日期格式化方法
  • 获取日期指定部分
方法名 说明 代码
getFullYear() 获取当年 dObj.getFullYear()
getMonth() 获取当月(0-11)使用时 +1 dObj.getMonth()
getDate() 获取当天日期 dObj.getDate()
getDay() 获取星期几 (周日0 到周六6) // 使用时利用数组 dObj.getDay()
getHours() 获取当前小时 dObj.getHours()
getMinutes() 获取当前分钟 dObj.getMinutes()
getSeconds() 获取当前秒钟 dObj.getSeconds()

注意 月份 和星期 取值范围是从 0开始的。

  • 2019年3月24日 星期二 请写出这个格式
	function getMyDate() {
     
    	// 创建日起对象,声明存储年月日的变量
        var now = new Date();
        var year, month, day, zhou;
    
        year = now.getFullYear();
        month = now.getMonth() + 1;  // 月份从0开始 需要 +1
        day = now.getDate();
    
        var zhouArr = ['星期天', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
        zhou = zhouArr[now.getDay()];  // 星期数从0开始
        return  year + '年' + month + '月' + day + '日 ' + zhou;
	}

    console.log(getMyDate());  // 输出 2019年3月29日 星期五
  • 格式化日期对象,HH:mm:ss 的形式 比如 00:10:45
function getTimer() {
     
    var date = new Date();
    var h, m, s;
    
    h = date.getHours();
    m = date.getMinutes();
    s = date.getMinutes();
	// 如果时间小于 10 , 在前面补一个 0
    h = h < 10 ? '0' + h : h;
    m = m < 10 ? '0' + m : m;
    s = s < 10 ? '0' + s : s;
    
    return h + ':' + m + ':' + s;
}
console.log(getTimer());
  • 倒计时效果
function getCountTime(endTime) {
     
            var d, h, m, s;
            var countTime = parseInt((new Date(endTime) - new Date()) / 1000);

            d = parseInt(countTime / 60 / 60 / 24);  // 天数
            d = d < 9 ? '0' + d : d;
            h = parseInt(countTime / 60 / 60 % 24);  // 小时数
            h = h < 9 ? '0' + h : h;
            m = parseInt(countTime / 60 % 60);  // 分钟数
            m = m < 9 ? '0' + m : m;
            s = parseInt(countTime % 60);   // 秒数
            s = s < 9 ? '0' + s : s;
            
            return '还剩下' + d + '天' + h + '时' + m + '分' + s + '秒';
        }

        console.log(getCountTime('2019-3-30 8:00'));
基本包装类型

​ 为了方便操作基本数据类型,JavaScript还提供了三个特殊的引用类型:String/Number/Boolean

​ 基本包装类型就是 把简单数据类型包装成为复杂数据类型。 这样 基本数据类型就有了属性和方法.

// 
var str = 'andy';
console.log(str.length);
// 按道理 基本数据类型 是 没有属性和方法的 对象才有属性和方法的
// 这个原因是因为, js 会把 基本数据类型包装为复杂数据类型

//  等同于如下代码

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

​ 指的是里面的值不可变, 看上去可以改变内容,其实只是地址变了,新开辟了一个内存空间把新地址赋值给变量。

var str = 'abc';
str = 'hello';
// 当重新给str赋值的时候,常量'abc'不会被修改,依然在内存中
// 重新给字符串赋值,会重新在内存中开辟空间,这个特点就是字符串的不可变
// 由于字符串的不可变,在大量拼接字符串的时候会有效率问题
var str = '';
for (var i = 0; i < 100000; i++) {
     
    str += i;
}
console.log(str); // 这个结果需要花费大量时间 来 显示 因为需要不断的开辟新的空间
创建字符串对象
var str = 'andy';
console.log(str); // 看不到常见的属性和方法
var str1 = new String('andy');
console.log(str1); // 可以看到常见的属性和方法
// 但是字符串经过基本包装类, 是可以使用 常见的属性和方法

字符串对象的常用方法

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

根据位置获取字符
方法名 说明 使用
charAt(index) 返回指定位置的字符(index 字符串的索引号) str.charAt(0)
charCodeAt(index) 获取指定位置处字符的ASCII码 (index索引号) str.charCodeAt(0)
str[index] 获取指定位置处字符 HTML5,IE8+支持 和charAt()等效
var str = 'andy';
console.log(str.charAt(0));  // a

// 可以遍历的方法 得到所有的字符串
for (var i = 0; i < str.length; i++) {
     
    console.log(str[i]);
}
字符串拼接截取方法
方法名 说明
concat(str1,str2,str3…) concat() 方法用于连接两个或多个字符串。拼接字符串,等效于+,+更常用
substr(start,length) 截取字符串 从start位置开始(索引号) , length 取的个数 重点记住这个
slice(start, end) 从start位置开始,截取到end位置,end取不到 (他们俩都是索引号)
substring(start, end) 从start位置开始,截取到end位置,end取不到 基本和slice 相同 但是不接受负值
var str1 = 'andy';
var str2 = 'red';
console.log(str1.concat(str2));  // 输出 andyred

var s = "我爱中华人民共和国";
s = s.substr(2,2);  // 从索引第2个开始截取2个字符
console.log(s);  // 输出 '中华'
获取字符串位置方法
方法名 说明
indexOf(‘要查找的字符’, 开始的位置) 返回指定内容在元字符串中的位置, 如果找不到就返回 -1,开始的位置是index 索引号
lastIndexOf() 从后往前找,只找第一个匹配的

"abcoefoxyozzopp"查找字符串中所有o出现的位置

var str = 'abcoefoxyozzopp';
// 因为里面 index 要加1 所以这里是 -1
var index = -1;
do {
     
    // 这里判断是否能取到 o
    index = str.indexOf('o', index + 1);
    // 如果不是-1 就返回这个位置
    if (index != -1) {
     
        console.log(index);
    }
} while (index !== -1)
replace() 替换

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

// 把字符串中所有的 o 替换成 ! 
var s = 'abcoefoxyozzopp';
while (s.indexOf('o') !== -1) {
     
    s = s.replace('o', '!');
}
console.log(s);
转换大小写

toUpperCase() //转换大写
toLowerCase() //转换小写

var str = 'ANDY';
console.log(str.toLowerCase()); // andy
var str = 'andy';
console.log(str.toUpperCase()); // ANDY
split 切割字符串

​ 注意,切割完毕之后,返回的是一个新数组

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

数组常用方法

​ push()、shift()、unshift()、reverse()、sort()、splice()、indexOf()

数组添加删除方法
方法名 说明 返回值
push(参数1…) 修改原数组,末尾添加一个或多个元素 并返回新的长度
pop() 删除 数组的最后一个元素,把数组长度减 1 无参数 返回它删除的元素的值
unshift(参数1…) 向数组的开头添加一个或更多元素 并返回新的长度
shift() 把数组的第一个元素从其中删除,把数组长度减 1 无参数 并返回第一个元素的值
// 工资的数组[1500, 1200, 2000, 2100, 1800],把工资超过2000的删除

var arr = [1500, 1200, 2000, 2100, 1800];
var newArr = [];
for (var i = 0; i < arr.length; i++) {
     
    if (arr[i] < 2000) {
     
        newArr.push(arr[i]);  // 在末尾压入一个元素
    }
}
console.log(newArr);
数组排序方法
方法名 说明 是否修改原数组
reverse() 颠倒数组中元素的顺序,无参数 该方法会改变原来的数组
sort() 对数组的元素进行排序 该方法会改变原来的数组
// reverse() 反转数组
var arr = ['red', 'andy'];
console.log(arr.reverse()); // 返回翻转之后的数组
console.log(arr); // 原先数组也被修改

// 反转数组函数实现
function reverseArr(arr) {
     
 	var newArr = [];
 	for (var i = arr.length; i >= 0;i--) {
     
    	   newArr[newArr.length] = arr[i];
 	}
 	return newArr;
 }
console.log(reverseArr(['a', 'b', 'c']));

sort 如果调用该方法时没有使用参数,按照字符编码的顺序进行排序。

var arr = [1, 64, 9, 6];
arr.sort(function(a, b) {
     
    return a - b;  // 升序
    // return b - a;  // 降序
});
console.log(arr);


// 模拟 Sort() 方法
function mySort(arr, f) {
     
	for (var i = 0; i < arr.length - 1; i++) {
     
		for (var j = 0; j < arr.length - 1 - i; j++) {
     
		
			var x = f(arr[j], arr[j + 1]);
            // 当x大于0时,说明第一个参数比第二个参数大
            // 当x等于0时,说明相等
            // 当x小于0时,说明第一个参数比第二个参数小

            if (x > 0) {
      // 排序中最关键的一句话,判断谁先谁后
            	var tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
            }
         }
    }
    return arr;
 }

// 比较数值大小排序
var arr = [5, 4, 3, 2, 1];
function numberSort(a, b) {
     
	return a - b;
}
mySort(arr, numberSort);
console.log(arr);

// 比较字符串长度排序
var arr1 = ["pink", "abc", "po", "push1"];
function stringLengthSort(a, b) {
     
	return a.length - b.length;
}
mySort(arr1, stringLengthSort);
console.log(arr1);

// 比较 stu对象 的年龄 排序
var stu1 = new Student('zs', 20);
var stu2 = new Student('ls', 18);
var stu3 = new Student('ww', 30);
var arr2 = [stu1, stu2, stu3];
function Student(name, age) {
     
	this.name = name;
	this.age = age;
}
mySort(arr2, studentAgeSort);
console.log(arr2);
数组拼接截取方法
方法名 说明 返回值
concat() 连接两个或多个数组 返回一个新的数组
slice() 数组截取slice(begin, end) 返回被截取项目的新数组
splice() 数组删除splice(第几个开始,要删除个数) 返回被删除项目的新数组 注意,这个会影响原数组
清空数组的三个方法
// 1. 赋一个空数组  
arr = [];

// 2. 数组长度 = 0 
arr.length = 0;

// 3. 从 0 开始删除数组 , arr.length 可以不写
arr.splice(0, arr.length);
数组位置方法
方法名 说明 返回值
indexOf() 连接两个或多个数组 返回一个新的数组
lastIndexOf() 如果没找到返回-1 返回被截取项目的新数组

数组去重

var arr = ['red', 'green', 'blue', 'pink','red'];
// 判断数组中的元素在新数组中有没有出现过,有就添加,否则不添加
function deleteRepeat(arr) {
     
	var newArr = [];
	for (var i = 0; i < arr.length; i++) {
     
		if (newArr.indexOf(arr[i]) == -1) {
     
			newArr.push(arr[i]);
		}
	}
	return newArr;
}
console.log(deleteRepeat(arr));
join连接成字符串
方法名 说明 返回值
join(‘分隔符’) 方法用于把数组中的所有元素转换为一个字符串。 返回一个字符串
var arr = ['red', 'green', 'blue', 'pink'];
console.log(arr.join()); // 跟toString() 一样, 转换为字符串
console.log(arr.join('-')); //red-green-blue-pink
filter迭代方法
方法名 说明 返回值
filter() 按照条件筛选数组
语法 : arr.filter(callback[, thisArg])
// 使用 filter 创建了一个新数组,该数组的元素由原数组中值大于 10 的元素组成。
function isBigEnough(element) {
     
  return element >= 10;
}
var arr = [12, 5, 8, 130, 44];
var filtered = arr.filter(isBigEnough);
// filtered is [12, 130, 44]

综合案例 分割字符串

var url = ‘http://www.itheima.com/login?name=zs&age=18’;

function getParams(url) {
     
    // 1. 首先把 网址分为2部分  用 ? 分割 
    // 2. 得到 ?+ 1 的索引位置
    var index = url.indexOf('?') + 1;
    // 3. 得到 ? 后面的字符串
    var params = url.substr(index);
    console.log(params); // name=zs&age=18
    // 4. 把 得到 这串字符 继续用 & 分隔开
    var arr = params.split('&');
    // console.log(arr);
    var o = {
     };
    // 5. 把 数组里面的每一项,继续用 = 分割
    for (var i = 0; i < arr.length; i++) {
     
        var newArr = arr[i].split('=');
        // console.log(newArr);
        // 完成赋值 操作
        o[newArr[0]] = newArr[1];
    }
    return o;
}
console.log(getParams(url));

简单类型和复杂类型

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

值类型:简单数据类型/基本数据类型,在存储时,变量中存储的是值本身,因此叫做值类型。

引用类型:复杂数据类型,在存储是,变量中存储的仅仅是地址(引用),因此叫做引用数据类型。

堆 和 栈

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

值类型内存分配
  • 值类型(简单数据类型): string ,number,boolean,undefined,null
  • 值类型变量 的 数据 直接存放在变量(栈空间)中
引用类型内存分配
  • 引用类型(复杂数据类型):通过 new 关键字创建的对象(系统对象、自定义对象)

  • 引用类型变量(栈空间)里存放的是地址,真正的对象实例存放在堆空间中

  • 我们通过变量 usrObj 访问 Object对象实例 里的内容时,实际是通过 栈空间里存放的堆地址来找到对象实例,再调用对象实例里的成员。

值类型传参
function fn(a) {
     
    a++;
    console.log(a);  // 11
}
var x = 10;
fn(x);
console.log(x)// 10
  • 结论:函数的形参也可以看做是一个变量,当我们把一个值类型变量作为参数传给函数的形参时,其实是把变量在栈空间里的值 复制 了一份给形参,那么在方法内部对形参做任何修改,都不会影响到的外部变量
引用类型传参
function Person(name) {
     
  this.name = name;
}
function f1(x) {
     
  x.name = "ls";
}
var p = new Person("zs");
console.log(p.name);  // 'zs'
f1(p);
console.log(p.name);  // 'ls'
  • 结论:函数的形参也可以看做是一个变量,当我们把 引用类型变量 传给 形参时,其实是把 变量在栈空间里保存的 堆地址 复制给了 形参,形参和实参其实保存的是同一个堆地址,所以操作的是同一个对象

Web API

Web API介绍

API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。

  • 任何开发语言都有自己的API
  • API的特征输入和输出(I/O)
    • var max = Math.max(1, 2, 3);
  • API的使用方法(console.log(‘adf’))

Web API的概念

浏览器提供的一套操作浏览器功能和页面元素的API(BOM和DOM)

此处的Web API特指浏览器提供的API(一组方法),Web API在后面的课程中有其它含义

DOM

DOM的概念

文档对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展标记语言的标准编程接口。它是一种与平台和语言无关的应用程序接口(API),它可以动态地访问程序和脚本,更新其内容、结构和www文档的风格(目前,HTML和XML文档是通过说明部分定义的)。文档可以进一步被处理,处理的结果可以加入到当前的页面。DOM是一种基于树的API文档,它要求在处理过程中整个文档都表示在存储器中。

DOM又称为文档树模型

  • 文档:一个网页可以称为文档
  • 节点:网页中的所有内容都是节点(标签、属性、文本、注释等)
  • 元素:网页中的标签
  • 属性:标签的属性

DOM经常进行的操作

  • 获取元素
  • 对元素进行操作(设置其属性或调用其方法)
  • 动态创建元素
  • 事件(什么时机做相应的操作)

获取页面元素

根据id获取元素

var div = document.getElementById('main');
console.log(div);
// 如果没有查询到,返回一个null
// 获取到的数据类型 HTMLDivElement,对象都是有类型的

根据标签名获取元素

var divs = document.getElementsByTagName('div');
for (var i = 0; i < divs.length; i++) {
     
  var div = divs[i];
  console.log(div);
} 
// 根据标签名获取一个元素的伪数组。
// 如果没有查询到,返回的是一个空数组!
// 元素集合是动态的:体现在在对页面元素进行操作(添加、删除、替换)伪数组中的元素对象会随之改变。

根据name获取元素*

var inputs = document.getElementsByName('hobby');
for (var i = 0; i < inputs.length; i++) {
     
  var input = inputs[i];
  console.log(input);
}

根据类名获取元素*

var mains = document.getElementsByClassName('main');
for (var i = 0; i < mains.length; i++) {
     
  var main = mains[i];
  console.log(main);
}

根据选择器获取元素*

var text = document.querySelector('#text');
console.log(text);

var boxes = document.querySelectorAll('.box');
for (var i = 0; i < boxes.length; i++) {
     
  var box = boxes[i];
  console.log(box);
}

控制台打印元素所有属性

var div1 = document.getElementById(‘main’);

console.dir(div1);

事件

​ 事件:触发-响应机制

​ 程序的执行是依赖于事件的发生(点击)才进行执行。这种编写代码的思路就叫做事件驱动。

事件三要素

  • 事件源:触发(被)事件的元素,就是发生事件的那个东西.
  • 事件处理程序(监听器):事件触发后要执行的代码(函数形式),就是当事件发生之后,处理事件的程序.
  • 事件: click 点击事件,就是发生的那件事.

开发时间驱动的程序的流程:

  1. 创建一个事件源
  2. 编写一个事件处理程序(监听器)
  3. 绑定事件源和监听器

事件的基本使用

var box = document.getElementById('box');
box.onclick = function() {
     
  console.log('代码会在box被点击后执行');  
};
  • 点击按钮弹出提示框
	var btn = document.getElementById('btn');
    btn.onclick = function () {
     
      alert('点到我了');
    }
  • 点击按钮切换图片
   var btn = document.getElementById('btn');
   var photo = document.getElementById('photo');
   var flag = true;
   btn.onclick = function () {
     
     if(flag) {
     
      photo.src = 'images/b.jpg';
      flag = false;
     } else {
     
       photo.src = 'images/a.jpg';
       flag = true;
     }
   }

通用属性

id 元素的ID、

className 元素的属性名,为元素设置class样式、

name

属性操作

非表单元素的属性

a : href、title

img : src

innerHTML

innerText

// a 标签
var link = document.getElementById('link');
console.log(link.href);
console.log(link.title);
// img 标签
var pic = document.getElementById('pic');
console.log(pic.src);

案例:

点击按钮显示隐藏div

		// 获取元素
        var btn = document.getElementById('btn');
        var div = document.getElementById('div');
        // 声明一个flag进行标识
        var flag = true;
        // 给按钮注册事件
        btn.onclick = function () {
     
            if (flag) {
     
                div.style.display = 'none';
                btn.value = '点击显示';
        		// this.value = '点击显示';
                flag = false;
            } else {
     
                div.style.display = 'block';
                btn.value = '点击隐藏';
        		// this.value = '点击隐藏';
                flag = true;
            }
        }

		// 或者使用 对象的属性名className 赋值
        .hidden {
     
            display:none;
        }
		var btn = document.getElementById('btn');
        var div = document.getElementById('div');
		btn.onclick = function () {
     
            // 设置div的class属性为hidden
            // 因为标签的属性class在js中是关键字,所以对象的属性名是 className .
    		div.className = 'hidden';
		}
		

取消 a 链接 的跳转功能

<a id = 'link' href="https://www.baidu.com/">百度一下</a>

var link = document.getElementById('link');
link.onclick = function () {
     
	alert('点到我了');
    
	// 取消 a 链接 的跳转功能
	return false;
}

美女相册

<div id="main">
	<a href="../代码/images/1.jpg" title = '美女A'>
		<img src="../代码/images/1-small.jpg" alt="美女A">
	</a>
	<a href="../代码/images/2.jpg" title = '美女B'>
		<img src="../代码/images/2-small.jpg" alt="美女B">
	</a>
	<a href="../代码/images/3.jpg" title = '美女C'>
		<img src="../代码/images/3-small.jpg" alt="美女C">
	</a>
	<a href="../代码/images/4.jpg" title = '美女D'>
		<img src="../代码/images/4-small.jpg" alt="美女D">
	</a>
</div>
<img id="image" src="../代码images/placeholder.png" alt="" width="450px" />
<br>
<p id = 'des'>选择一张图片</p>
    
	// 业务代码
    <script>
        // 获取元素
        var main = document.getElementById('main');
        // 获取所有main下的a链接对象
        var links = main.getElementsByTagName('a');
        
        for (var i = 0; i < links.length; i++) {
     
            // 循环遍历a链接对象
            var link = links[i];
            // 为当前点击的a链接注册事件
            link.onclick = function () {
     
                var img = document.getElementById('image');
                // this.href 当前点击的a链接
                img.src = this.href;
                
				// 设置p标签显示a链接的title属性值
                var des = document.getElementById('des');
                des.innerHTML = this.title;

                // 取消a链接的跳转
                return false;
            }
        }
    </script>
innerHTML和innerText

​ innerHTML , 获取内容的时候,如果内容中有标签,会把标签获取到,解析成HTML形式展示

​ innerText , 获取内容的时候,如果内容中有标签,会把标签过滤掉,解析成Text形式展示

var box = document.getElementById('box');
box.innerHTML = '我是文本

我会生成为标签

'
; // 输出 我是文本 // 我会生成标签 console.log(box.innerHTML); box.innerText = '我是文本

我不会生成为标签

'
; // 输出 我是文本

我不会生成为标签

console.log(box.innerText);
  • innerText的兼容性处理

    一些浏览器有这个属性,另外一些没有.

    判断有没有这个属性,如果没有,就用另外的方式.

    如果一个对象没有这个属性,直接获取这个属性的值,获取的就是undefined.

    typeof element.innerText === ‘string’

    老版本firefox不支持innerText

    2016年的时候 innerText在Dom中规定为正式的标准属性

    innerContent 输出原本的样式

	// 如何知道。浏览器是否支持元素的某个属性
    var box = document.getElementById('box');
    // 当属性不存在的时候返回的是  undefined
    console.log(typeof box.a);
    // 当属性存在的时候返回的是 该属性的类型
    console.log(typeof box.id);

    var box = document.getElementById('box');
    console.log(getInnerText(box));

    // 处理innerText的兼容性问题
    function getInnerText(element) {
     
      // 判断当前浏览器 是否支持元素的innerText属性,支持innerText 使用element.innerText获取内容
      // 如果不支持innerText属性,使用element.textContent获取内容
      
      if (typeof element.innerText === 'string') {
     
        return element.innerText;
      } else {
     
        return element.textContent;
      }
    }

    // 设置内容的时候
    // innerText(textContent)       当设置不含标签的内容的时候应该使用innerText,效率高
    // innerHTML 
  • HTML转义符
"		&quot;
'		&apos;
&		&amp;
<		&lt;   // less than  小于
>		&gt;   // greater than  大于
空格	   &nbsp;
©		&copy;
表单元素属性
  • value 用于大部分表单元素的内容获取(option除外)

给文本框赋值

  • type 可以获取input标签的类型(输入框或复选框等)

布尔类型的值

  • disabled 禁用属性 true:禁用 false:未禁用
<input type="text"  id="txt" value='123'>
console.log(txt.disabled);
txt.disabled = true;  // 禁用用户输入
  • checked 复选框选中属性
  • selected 下拉菜单选中属性
自定义属性操作
  • element.getAttribute(‘属性名’) 获取标签行内属性 //返回指定属性名的属性值
  • element.setAttribute(‘属性名’,‘属性值’) 设置标签行内属性
  • element.removeAttribute(‘属性名’) 移除标签行内属性
  • 与element.属性的区别: 上述三个方法用于获取任意的行内属性
		<div id="box" css='s'></div>

        // 获取:元素对象.getAttribute('属性名');  返回指定属性名的属性值。
        console.log(document.getElementById('box').getAttribute('id'));
        
        // 设置:元素对象.setAttribute('属性名','属性值');
        document.getElementById('box').setAttribute('id','1');

        // 移除:元素对象:removeAttribute('属性名');
        document.getElementsByTagName('div')[0].removeAttribute('css');
样式操作
  • 使用style方式设置的样式显示在标签行内
        .red {
     
            background-color: red;
        }

		<div id="box"></div>

        // 根据id获取元素,封装成函数 my$(id)
        function my$(id) {
     
            return document.getElementById(id);
        }
        // 使用类样式 my$('div1').className = '类样式名';
        my$('box').className = 'red';
        // 使用style my$('div1').style.backgroundColor = 'red';
        my$('box').style.backgroundColor = 'green';
        // 当设置类型比价少的时候,用style比较方便

注意

通过样式属性设置宽高、位置的属性类型是字符串,需要加上px

类名操作
  • 修改标签的className属性相当于直接修改标签的类名
var box = document.getElementById('box');
box.className = 'show';

创建元素的三种方式

// 动态创建元素

​ // 第1种方式 document.write()

​ document.write(‘hello?

World!

’); // 在事件中使用会把页面之前的内容覆盖掉,很少使用

​ // 第2种方式 innerHTML

​ box.innerHtml = ‘hello?

World!

’; // 解析成为HTML格式 简单,但不灵活

​ // 第3种方式 document.createElement()

​ document.createElement(‘p’); // 在内存中创建一个DOM p标签对象 操作的是对象 灵活,但不简单

​ p.innerHtml = ‘Hello World’; // 设置p标签对象的属性

​ box.appendChild§; // 把p元素对象,追加到box中,最后面的子元素

节点操作

节点属性
  • nodeType:节点的类型

    1----元素,

    2—属性,

    3—文本

  • nodeName:节点的名字

    元素节点—大写的元素名字,

    属性节点—小写的属性名字,

    文本节点----#text

  • nodeValue:节点的值

    元素节点—null,

    属性节点—属性值,

    文本节点—文本内容

节点层级

获取节点

  • 父子节点

    节点 : 属性节点/元素节点/文本节点/注释节点

  1. parentNode 获取父节点,只有一个

  2. childNodes 获取所所有的子节点,有很多个 是一个属性,不需要()

  3. 遍历所有子节点,判断子节点.nodeType==1,是元素

    元素

    1. children 获取所有子元素,有很多个
  • 兄弟节点

​ box.nextSibling 获取下一个兄弟节点

​ box.previousSibling 获取上一个兄弟节点

​ box.nextElementSibling 获取下一个兄弟节点

​ box.previousElementSibling 获取上一个兄弟节点

插入节点

appendChild(要插入的元素) 追加元素到调用对象元素的末尾,如果已存在,会先移除,后插入 如需保存原始节点,需先克隆节点

insertBefore(新插入的元素,参考的元素) 把元素插入到页面的指定位置,参考元素之前

​ // 1. 创建一个元素

​ // 2. 插入元素到指定位置

repleaceChild(新的元素,要替换的元素) 把当前元素的标签进行替换,返回被替换的节点

事件详解

事件的注册方式

​ 1.

​ 元素对象.onclick = function() {}

​ 元素对象.onclick = null;

​ 2.

​ 元素对象.addEventListener(‘事件名’, 函数, false ); 添加事件 时间类型去掉on

​ 元素对象.removeEventListener(‘事件名’, 函数, false); 删除事件

事件的三个阶段

​ 事件发生的时候,事件传播的三个不同的阶段

​ 事件的捕获阶段 : 发生在事件到达目标元素对象之前,可以阻止事件的发生

​ 事件的执行阶段 : 发生在事件到达目标元素对象

​ 事件的冒泡阶段 : 子元素发生某个事件后,会把发生的时间向父级元素传播,父级元素也可以收到相同的事件信息.

​ 冒泡阶段研究的最多:

​ addEventListener 如果不指定第三个参数,研究的就是冒泡阶段

​ onclick , 也是绑定的冒泡阶段

​ 方向是从事件源对象,向外层进行扩散.

​ 在冒泡阶段,事件时机已经发生了.

事件委托

​ 利用了事件的冒泡, 捕获子元素发生的事件, 只需要在父元素上注册一次事件, 而不需要在子元素上反复注册. 通过给父元素添加处理函数来处理子元素发生的事件.

​ 事件的委托依赖于两个要点,1.事件的冒泡,2.事件对象 event .

事件对象

​ 当事件发生时, 浏览器将时间的相关信息封装成一个对象, 以实参的方式传递给事件处理函数(监听器), 在事件处理函数中就可以直接使用事件对象. 记录了事件发生过程中相关信息的对象

​ Dom 标准中,是给事件处理函数一个参数

​ event就是事件对象

属性

​ event.eventPhase; 事件的阶段 : 1. 捕获阶段 2. 目标阶段 3. 冒泡阶段 了解

​ event.target; 真正触发事件的对象 事件源

​ event.currentTarget; 事件处理函数所属的对象 this

​ event.type; 事件的类型 ‘click’ ‘onmouseover’ ‘onmouseout’ ,可以判断当前事件对象发生事件的类型

​ event.preventDefault() 取消默认行为 return false;

​ event.stopProgration() 停止事件传播的方法

事件处理函数绑定的元素对象的区别:

​ 绑定在事件源上

​ eventPhase值为2,意思是事件的执行阶段

​ target 就是当前的事件源

​ currentTarget 也是当前对象

​ this 也是当前对象

​ 绑定在事件源的父元素上

​ eventPhase值为3,意思是事件的冒泡阶段

​ target 就是实际上发生事件的元素对象(子元素对象)

​ currentTarget 就是当前的父元素对象

​ this 也是当前的父元素对象

常用的鼠标和键盘事件

  • onmouseup 鼠标按键放开时触发
  • onmousedown 鼠标按键按下触发
  • onmousemove 鼠标移动触发
  • onkeyup 键盘按键按下触发
  • onkeydown 键盘按键抬起触发

鼠标的位置信息

​ clientX / clientY : 发生鼠标事件时,相对于当前可视区域的位置

​ pageX / pageY : 发生鼠标事件时,相对于整个页面的位置

​ pageY = clientY + 页面滚动出去的距离

​ document.body.scroolTop : 获取页面纵向滚动的距离

​ offsetLeft : 获取盒子左边距, 只读属性

​ offsetRight : 获取盒子右边距, 只读属性

和位置相关的三组属性

​ offset 偏移量, 相对于有定位的父元素, 返回数值类型

​ 元素.offsetLeft; 相对于定位父元素的左边距

​ 元素.offsetTop;

​ 元素.offsetWidth; 包括边框和padding

​ 元素.offsetHeight;

​ 元素.offsetParent; 获取最近的有定位的父元素

​ client 边框宽度

​ 元素.clientLeft; 自身边框宽度

​ 元素.clientTop;

​ 元素.clientWith; 包括padding不包括边框

​ 元素.clientHeight;

​ scrool 滚动距离

​ 元素.scroolLeft;

​ 元素.scroolTop; 向上滚动出去的距离

​ 元素.scroolWidth;

​ 元素.scroolHeight; 包括padding和未显示内容的高度

​ onscrool 当拖动滚动条的时候触发事件

BOM

​ BOM(Browser Object Model) 是指浏览器对象模型,浏览器对象模型提供了独立于内容的、可以与浏览器窗口进行互动的对象结构。BOM由多个对象组成,其中代表浏览器窗口的Window对象是BOM的顶层对象,其他对象都是该对象的子对象。

我们在浏览器中的一些操作都可以使用BOM的方式进行编程处理,

比如:刷新浏览器、后退、前进、在浏览器中输入URL等

window对象

​ 所有的对象和成员都是window对象的属性或方法

​ 当用window成员的时候,window可以省略 (如果在函数内部不适用var声明, 那么这个变量就是全局变量)

​ name, top 等是window的成员 ,只能获取不能赋值 .

​ window 有很多的子属性, document 也是 winodw 的子属性

onload页面加载事件

​ 在页面加载完成之后执行 , 加载完成指页面上所有的元素创建完毕, 并且引用的外部资源下载完毕(js, css, 图片)

​ 比直接写在底部的方式加载效率略低

​ onunload 页面卸载事件

​ 在页面卸载完成之后执行

​ 在onunload中, 所有的对话框都无法使用, window对象被冻结, 不能使用alert等方法

​ f5 刷新 1. 先卸载页面 2. 重新加载页面

定时器

​ setTimeout(fn,3000) 设置定时器 定时炸弹 隔一段时间执行,并且只会执行一次

​ 第一个参数 函数

​ 第二个参数 间隔的时间 单位是毫秒

​ 返回值 是一个整数,是定时器的标示 var timerId = setTimeout(fn,3000)

​ clearTimerout(timerId) 清除定时器

​ 参数是 要取消的定时器的标示

​ setInterval(fn, 3000) 闹钟 隔一段时间执行, 并且会重复执行

​ 第一次执行也要等待三秒钟

​ clearTimerval(定时器标识) 清除定时器

location对象

获取或设置浏览器地址栏的url

​ href : 获取和设置浏览器地址 最常用

​ assign() : 委派,和href的作用一样,可以让页面跳转到指定页面, 可以后退

​ repleace() : 替换掉地址栏的地址, 不记录历史, 不可以后退

​ refresh() : 参数 true 强制从服务器获取页面(F5刷新) false 如果浏览器有缓存的话, 直接从缓存中获取(Ctrl+F5强制刷新)

​ URL 统一资源定位符(网址)

​ 中文: 协议://主机名或地地址:端口号/资源路径?参数#锚点

​ 英文: scheme://host:port/path?query#fragment

​ location属性: protocal://hostname:port/pathname?search#hash

​ 实例: http://www.baidu.com:80/a/b/index.html?name=zs&age=18#bottom

​ scheme : 通信协议

​ host : 主机

​ port : 端口号 http默认80

​ path : 路径

​ query : 参数

​ fragment : 锚点

history对象

历史访问 前进后退 用的不多

​ history.forword();

​ history.go(1); 0刷新当前页面,关不掉了… 1前进 2前进两个 -1后退

navigator对象

​ navigator.userAgent 判断用户浏览器的类型

​ platform 判断浏览器所在的系统平台类型.

jQuery

jquery : Write Less, Do More.

框架库和jQuery介绍

​ javaScript框架库: 就是一个普通的js文件, 封装了很多的函数, 封装了很多兼容的代码.

​ jQuery : 是javaScript框架库中的一种.

​ 好处: 可以解决js兼容问题, 体积小, 链式编程, 隐式迭代, 插件丰富, 开源, 免费.

​ jQuery文件使用

​ 引入文件就行了

​ jQuery中顶级对象

​ Dom document , Bom window

​ jQuery 简化写法 $

页面加载事件

1、window.onload=function(){
     
	// js原生的事件
    // 在页面所有资源加载完后执行,如果有多个定义则只执行最后一个 (包括图片/外链等)
}

2、$(function(){
     
    // JQuery的方法
    // 在DOM加载完成时运行的代码,如果有多个定义则依次执行 (不包括图片)
});

等同于 

    $(document).ready(function(){
     
        // 在DOM加载完毕后执行了ready()方法
    }); 

也等同与 
    jQuery(function(){
     
        // $ 是 jQuery 的缩写
    });

$的常用作用

  1. 在页面基本标签加载完成时执行代码
	$(function() {
     });
  1. 将原生的js对象转换为jQuery对象.
	$(原生的js对象);  注意: $(this)

原生js对象包括 : 内置对象/自定义对象/DOM对象/BOM对象

注意: 实际处理函数中的this是一个原生js对象, 需要使用$(this)转换为jQuery对象

jQuery对象[0] 或者 jQuery对象.get(0) 来得到js对象

  1. $(‘选择器’)
	$('#btn');
  1. 接收一个html字符串,可以把html字符串转换为jQuery对象
	// 第一种方式
	var linkObj = $('百度');
	// 添加到文档中
	$('#dv').append(linkObj);

	// 第二种方式
	$('#dv').html('百度');

常用的选择器

基本选择器

            id选择器 : #box{}

            标签选择器 : div{}

            类选择器 : .cls{}

            并集选择器 : div, p{}

            通配符选择器 : *{}

层级选择器

            直接子元素 : div p{}

            所有子元素 : div>p{}

            下一个兄弟元素 : div+p{}

            所有的兄弟元素 : div~p{}

筛选选择器

            偶数选择器 : #box:even{}

            奇数选择器 : #box:odd{}

            选择索引为4 : #box>li:eq(4)

            选择索引大于4 : #box>li:gt(4)

            选择索引小于4 : #box>li:lt(4)
            
筛选状态:
			筛选选中的单选 :checked
			筛选选中的多选 :selected

常用的方法

HTML代码/文本/html()  innerHtml

		text()  innerText

		val()  表单元素的value属性一样
        val('zhi')  设置zhi
		// 下拉列表框中的val方法的返回值, 如果选中的option有value属性就返回value属性的值, 否则返回option标签中间的内容
        
属性       
  attr() 和 prop 的区别 :
	prop()设置和获取的是dom对象中的原生的属性 (如className, checked)
    attr()设置和获取的是标签中的属性. (可以是原生的, 也可以是自定义的) 如果获取的属性, 在标签上没有设置, 则返回undefined.
    自定义属性,就用attr;原生属性,就用prop.
    
        attr('属性')  // 获取该属性的值
        attr('属性','属性值') // 设置自定义属性 参数 1属性 2属性值
		removeAttr('属性')  // 移除属性

		prop('属性')  // 获取元素选中状态
		prop('属性','属性值')  // 设置元素选中状态

		width()  // 设置和获取宽
		height()  // 设置和获取高

		offset().left  // 获取左偏移
		offset().top  // 获取右偏移
		offset({
     left:100, top:100})  // 设置偏移量

		position()
		scrollTop();  // 获取纵向滚动的距离 --> 数字类型
		scrollLeft();  // 获取横向滚动的距离

		bind('事件 事件',函数) 
		delegate('要绑定的元素', '要绑定的事件的名字',函数 )

		$('#btn').get(0)  // 获取第一个元素
		$('#btn')[0]  // 获取第一个数组

		index()  获取元素下标

		find(x)  查找x下的所有元素
            
		children()  直接子元素

		siblings()  所有的兄弟元素
        
        // jQuery对象就是一个维数组, 其中存的元素就是js对象, each()遍历方法
        each(function (index, element){
     })  遍历对象
		// index  索引
		// element 当前对象

设置元素样式

	.css()写法
    	css('width')  // 获取元素的css的属性值, 字符串类型
		

      	css('width':'200px', 'height':'200px');

	   \1. 普通

            \2. 链式编程

                对象.方法().方法().方法()......

                就是方法在执行结束时, 将调用方法的对象再原样返回

                .end 恢复到断链之前

            \3. 键值对

操作类样式

	css()  获取或设置css的样式, 用来取代style属性

	addClass('cls cls2')  添加类样式, 多个用空格隔开

	removeClass('cls')  移除类样式cls, ()中什么也不写:移除所有类样式

	hasClass('cls')  判断元素是否拥有某个元素,返回T|F

	toggleClass('cls') 切换类样式,没有就添加,有就移除

获得兄弟元素

	siblings() : 所有兄弟元素

	next() : 下一个兄弟元素

	nextAll() : 后面的所有兄弟元素

	prev() : 前一个兄弟元素

	prevAll : 前面的所有兄弟元素

操作动画效果

	hide(动画执行时间, 回调函数)  // 隐藏元素 

	show(动画执行时间, 回调函数)  // 显示元素

	sideDown()  // 滑下来

	slideUp()  // 滑上去

	slideToggle()  // 切换滑动状态

	fadeln()  // 淡入

	fadeOut()  // 淡出

	fadeToggle()  // 切换淡入淡出

	fadeTo()  // 透明度变化
    
    animate({
     css样式}, 时间, 回调函数);  // 把元素从当前的css样式, 变换到指定的css样式

	stop()  // 用来停止动画,停止当前元素上排队的动画

		属性

	动画执行的时间 : 数值类型--毫秒值, 字符串类型 : 'slow'--'normal'正常  'fast'--快

	匿名函数 : 在动画执行结束后, 会自动调用回调函数arguments.callee 相当于递归


操作元素(创建/添加…)

DOM中创建元素

​ 1.document.write(“标签代码”);缺陷:页面加载后创建元素,把页面中原有的内容全部的干掉

​ 2.innerHTML, 每次设置值, 都会重新渲染页面, 效率比较低; innerHTML添加的元素, 比较难以绑定事件.

​ 3.document.createElement(“标签的名字”), 直接可以获取元素对象, 可以方便的绑定事件.

jQuery中创建元素

​ 1.$(“HTML的字符串”);

// 点击按钮, 创建一个超链接
$(function () {
     
    $('#btn').click(function () {
     
      // 创建了一个jQuery对象
      var linkObj = $('百度');
      // 添加到文档中  父元素.append(子元素) || 子元素.appendTo(父元素);
      $('#dv').append(linkObj);
      });
});

​ 2.对象.html(“HTML的字符串”);

// 相当于innerHtml
$(function () {
     
    $('#dv').html('百度');
});
jQuery中添加元素

内部插入:

​ append() : 添加一个子元素, 追加到最后

​ appendTo()

​ prepend() : 添加一个子元素, 追加到最前

​ prependTo()

// append方法把元素添加到另一个元素中的时候,有剪切的效果
// 父元素.append(子元素);
$("div").append($("p"));
// appendTo方法 子元素主动添加到父元素中
// 子元素.appendTo(父元素);
$("p").appendTo("div");

外部插入:

​ after() : 在每个匹配的元素之后插入内容

​ insertAfter()

​ before() : 在每个匹配的元素之前插入内容

​ insertBefore()

jQuery中移除元素
// 清空div中的内容
// 第一种方式 清空 
$('#dv').html(''); 
// 第二种方式 清空
$('#dv').empty();
// 低三种方式 移除
$('#dv').remove();
jQuery中替换元素
jQuery中克隆元素
// 克隆元素
$('#div').clone();

元素绑定多个事件

​ 原生的js中, 可以为同一个对象添加多个不同的事件, 但不能添加多个同一事件.

​ jQuery中, 可以为一个独享添加多个同一个事件.

绑定事件的方法

​ 1.jQuery对象调用事件方法绑定事件

$('#btn').click(function () {
     });

​ 2.使用bind方法绑定事件

$('#btn').bind('事件名称不带on', function () {
     });

​ 3.使用delegate方法绑定事件

​ 父元素给子元素绑定事件

​ 用途 : 可以为当前不存在的子元素添加事件!

父元素对象.delegate('子元素', '事件名称不带on', function () {
     });
$('#dv').delegate('p', 'click', function () {
     });

​ 委托机制, 利用了事件冒泡(父元素可以接受到子元素发生的事件), 可以再父元素中获得事件对象, 从而判断是哪个子元素 (event.target) 发生了事件.

​ 依赖于两个条件: 1. 冒泡事件 2. 事件对象中的 target 属性

​ 4.使用on方法绑定事件 :

取代bind的用法:
	jQuery对象.on('事件名称不带on', funciton () {
     })
	jQuery对象.on({
     '事件名称不带on':function () {
     }, '事件2':处理函数2})

取代delegate的用法:
	父元素对象.on('事件名称不带on', '子元素选择器', function () {
     })

解绑事件的方法

​ 1. 使用on绑定的事件

解除绑定在当前元素上的所有当前事件的处理函数
	元素对象.off("事件名称不带on")
	 可以解绑绑定给自己的事件。使用方法名绑定的事件、解除bind绑定的事件、解除使用on第一种方式绑定的事件。
	 可以解绑当前元素的子元素绑定的事件,通过delegate方法和on的第二种方式给子元素添加的事件,可以被解除

​ 2. 使用bind绑定的事件

元素对象.unbind("事件名称不带on")

​ 3. 使用delegate绑定的事件

父元素对象.undelegate("子元素", "事件名称");
<div>
	<p></p>
</div>
// 情况1
$("div").click(function(){
     }) // 无
$("p").click(function(){
     })  // 有
$("div").off("click");
// 情况2
$("div").click(function(){
     })    // 无
$("div").delegate("p", "click", function(){
     })  // 无
$("div").off("click");


给div绑定
$("div").click(function(){
     })
	$("div").off("click");
	$("div").off()
$("div").bind("click", function(){
     })
	$("div").unbind("click");
	$("div").off("click");
	$("div").off()
$("div").on("click", function(){
     })
	$("div").off("click");
	$("div").off()
给p绑定
$("p").click(function(){
     })
	$("p").off("click");
	// 不可以这么用$("div").off("click")
$("p").bind("click", function(){
     })
	$("p").unbind("click")
	$("p").off("click")
	// 不可以这么用$("div").off("click")
$("p").on("click", function(){
     })
	$("p").off("click");
$("div").delegate("p", "click", function(){
     })
	$("div").undelegate("p", "click")
	$("div").off("click", "p")
	$("div").off("click")
	$("div").off("click", "**")
	$("div").off()
$("div").on("click", "p", function(){
     })
	$("div").off("click", "p")
	$("div").off("click")
	$("div").off("click", "**")
	$("div").off()

事件触发的3种方式

// 1.常用方法
	$("#btn1").click();

// 2. trigget()方法
	$("#btn1").trigger("click");  // 触发事件  

// 3.triggerHandler()方法
	$("#btn1").triggerHandler("click");//触发事件

区别:
	第一种和第二种触发事件的方式是相同的,都会触发浏览器默认的事件(光标在文本框中闪烁)
	第三种触发事件的方式不会触发浏览器的默认事件

事件对象

$(function () {
     
    $('#dv').on('click', 'input', function (event) {
     
        event.delegateTarget;  // 调用deletage或on方法为子元素绑定事件的那个父元素
        event.currentTarget;  // 执行事件的元素
        event.target;  // 真正触发事件的元素
    });
});

事件冒泡

​ 元素中有元素, 这些元素都有相同的事件, 一旦最里面的元素的事件触发了, 外面的所有的元素的相同的事件都会被触发

​ 例 : 元素A中有一个元素B, A和B都有点击事件, B点击事件触发, A点击事件自动触发

取消事件冒泡

​ jQuery中 return false

each()方法

// jQuery对象就是一个维数组, 其中存的元素就是js对象, each()遍历方法
each(function (index, element){}) 遍历对象
// index 索引
// element 当前对象// jQuery对象就是一个维数组, 其中存的元素就是js对象, each()遍历方法
each(function (index, element){}) 遍历对象
// index 索引
// element 当前对象

jQuery插件

​ 就是实现一些功能的封装.

​ 插件 : js代码, css代码, 页面的结构

封装自己的插件:

// changeColor.css
.cls{
     
    width: 200px;
    height: 100px;
    background-color: pink;
    margin-top: 30px;
    margin-right: 20px;
    float: left;
}

// changeColor.js
// 固定语法, 为$添加一个方法
$.fn.changeBackgroundColor = function (color) {
     
    $('.cls').css('backgroundColor', color);
}

// index
<script>
        $(function () {
     
            //点击每个按钮改变每个div的背景颜色
            $("input[type=button]").click(function () {
     
                $(".cls").changeBackgrounColor($(this).val());
            });
        });
</script>

<body>
<input type="button" value="green"/>
<input type="button" value="red"/>
<input type="button" value="blue"/>
<div id="dv">
    <div class="cls"></div>
    <div class="cls"></div>
    <div class="cls"></div>
    <div class="cls"></div>
    <div class="cls"></div>
</div>
</body>

// 插件的使用
首先引入外部的css文件
//代码如下:
// 
请复制下面的js的代码
// 
请复制下面的js的代码
// 
请复制下面的代码
// $(function () {
     
//     //点击每个按钮改变每个div的背景颜色
//     $("input[type=button]").click(function () {
     
//         $(".cls").changeBackgrounColor($(this).val());
//     });
// });
请复制下面的代码到html中的body标签中
// 
//     
//     
//     
//
//
//
//
//
//

jQueryUI

  1. 引入jQueryUI的样式文件
  2. 引入jQuery文件
  3. 引入jQueryUI的js文件
  4. 使用jQueryUI功能

JavaScript高级

JS回顾

重新介绍javaScript

javaScript是什么
  • 解析执行:轻量级解释型的

  • 语言特点:动态,头等函数 (First-class Function)

    • 又称函数是 JavaScript 中的一等公民
  • 执行环境:在宿主环境(host environment)下运行,浏览器是最常见的 JavaScript 宿主环境

    • 但是在很多非浏览器环境中也使用 JavaScript ,例如 node.js

    MDN-JavaScript

javaScript的组成
  • ECMAScript - 语法规范
    • 变量、数据类型、类型转换、操作符
    • 流程控制语句:判断、循环语句
    • 数组、函数、作用域、预解析
    • 对象、属性、方法、简单类型和复杂类型的区别
    • 内置对象:Math、Date、Array,基本包装类型String、Number、Boolean
  • Web APIs
    • BOM
      • onload页面加载事件,window顶级对象
      • 定时器
      • location、history
    • DOM
      • 获取页面元素,注册事件
      • 属性操作,样式操作
      • 节点属性,节点层级
      • 动态创建元素
      • 事件:注册事件的方式、事件的三个阶段、事件对象
JavaScript 可以做什么

阿特伍德定律:

Any application that can be written in JavaScript, will eventually be written in JavaScript.

任何可以用JavaScript来写的应用,最终都将用JavaScript来写

阿特伍德 stackoverflow的创始人之一

  • 知乎 - JavaScript 能做什么,该做什么?
  • 最流行的编程语言 JavaScript 能做什么?

浏览器是如何工作的

参考链接

User Interface  用户界面,我们所看到的浏览器
Browser engine  浏览器引擎,用来查询和操作渲染引擎
*Rendering engine 用来显示请求的内容,负责解析HTML、CSS,并把解析的内容显示出来
Networking   网络,负责发送网络请求
*JavaScript Interpreter(解析者)   JavaScript解析器,负责执行JavaScript的代码
UI Backend   UI后端,用来绘制类似组合框和弹出窗口
Data Persistence(持久化)  数据持久化,数据存储  cookie、HTML5中的sessionStorage

JavaScript 执行过程

JavaScript 运行分为两个阶段:

  • 预解析
    • 全局预解析(所有变量和函数声明都会提前;同名的函数和变量函数的优先级高)
    • 函数内部预解析(所有的变量、函数和形参都会参与预解析)
      • 函数
      • 形参
      • 普通变量
  • 执行

先预解析全局作用域,然后执行全局作用域中的代码,
在执行全局代码的过程中遇到函数调用就会先进行函数预解析,然后再执行函数内代码。

面向对象编程

面向对象介绍

什么是对象

Everything is object (万物皆对象)

内存的一片区域, 包含数据和代码, 数据成为属性, 代码成为方法.

(1) 对象是单个事物的抽象。

(2) 对象是一个容器,封装了属性(property)和方法(method)。

ECMAScript-262 把对象定义为:无序属性的集合,其属性可以包含基本值、对象或者函数

提示:每个对象都是基于一个引用类型创建的,这些类型可以是系统内置的原生类型,也可以是开发人员自定义的类型。

什么是面向对象

面向对象不是新的东西,它只是过程式代码的一种高度封装,目的在于提高代码的开发效率和可维 护性。

​ 面向对象编程 —— Object Oriented Programming,简称 OOP ,是一种编程开发思想。
​ 它将真实世界各种复杂的关系,抽象为一个个对象,然后由对象之间的分工与合作,完成对真实世界的模拟。

​ 在面向对象程序开发思想中,每一个对象都是功能中心,具有明确分工,可以完成接受信息、处理数据、发出信息等任务。
因此,面向对象编程具有灵活、代码可复用、高度模块化等特点,容易维护和开发,比起由一系列函数或指令组成的传统的过程式编程(procedural programming),更适合多人合作的大型软件项目。

面向对象与面向过程的区别

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

​ 封装:封装就是把处理数据的所有步骤封装到一个函数或其他结构中,方便代码的调用和管理,方便重用。

面向对象的编程: 面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。

​ 面向对象和面向过程的主要区别就是数据是单独存储还是与操作存储在一起

​ 对面向过程而言,数据是独立的。而在面向对象中,对象本身就提供了存储数据的空间(类的数据成员),这样就是函数的参数传递简单多了,而且提供了数据封装后,数据的访问也变可靠了。

扩展阅读:

  • 维基百科 - 面向对象程序设计
  • 知乎:如何用一句话说明什么是面向对象思想?
  • 知乎:什么是面向对象编程思想?

创建对象

简单方式

我们可以直接通过 new Object() 创建:

var person = {
     
  name: 'Jack',
  age: 18,
  sayName: function () {
     
    console.log(this.name)
  }
}
工厂函数

我们可以写一个函数,解决代码重复问题:

function createPerson (name, age) {
     
  return {
     
    name: name,
    age: age,
    sayName: function () {
     
      console.log(this.name)
    }
  }
}

然后生成实例对象:

var p1 = createPerson('Jack', 18)
var p2 = createPerson('Mike', 18)

这样封装确实爽多了,通过工厂模式我们解决了创建多个相似对象代码冗余的问题,
但却没有解决对象识别的问题(即怎样知道一个对象的类型)。

静态成员和实例成员
静态成员 : 属于构造函数对象的属性或方法
	构造函数.属性;  // 
实例成员 : 属于实例对象的属性或方法
	对象.属性;  // 

构造函数

更优雅的工厂函数:构造函数

一种更优雅的工厂函数就是下面这样,构造函数:

function Person (name, age) {
     
  this.name = name
  this.age = age
  this.sayName = function () {
     
    console.log(this.name)
  }
}

var p1 = new Person('Jack', 18)
p1.sayName() // => Jack

var p2 = new Person('Mike', 23)
p2.sayName() // => Mike
构造函数的执行

要创建 Person 实例,则必须使用 new操作符。
以这种方式调用构造函数会经历以下 4 个步骤:

  1. 在内存中创建一个新对象
  2. 设置构造函数的this, 让this只想刚刚创建的对象
  3. 执行构造函数中的代码
  4. 返回新对象
下面是具体的伪代码:
function Person (name, age) {
     
  this.name = name
  this.age = age
  this.sayName = function () {
     
    console.log(this.name)
  }
}
构造函数和实例对象的关系

在每一个实例对象中同时有一个 constructor 属性,该属性指向创建该实例的构造函数:

console.log(p1.constructor === Person) // => true
console.log(p2.constructor === Person) // => true
console.log(p1.constructor === p2.constructor) // => true

对象的 constructor 属性最初是用来标识对象类型的,
如果要检测对象的类型,还是使用 instanceof 操作符更可靠一些:

console.log(p1 instanceof Person) // => true
console.log(p2 instanceof Person) // => true
构造函数的问题

使用构造函数带来的最大的好处就是创建对象更方便了,但是其本身也存在一个浪费内存的问题:

function Person (name, age) {
     
  this.name = name
  this.age = age
  this.type = 'human'
  this.sayHello = function () {
     
    console.log('hello ' + this.name)
  }
}

var p1 = new Person('Tom', 18)
var p2 = new Person('Jack', 16)

在该示例中,从表面上好像没什么问题,但是实际上这样做,有一个很大的弊端。
那就是对于每一个实例对象,typesayHello 都是一模一样的内容,
每一次生成一个实例,都必须为重复的内容,多占用一些内存,如果实例对象很多,会造成极大的内存浪费。

console.log(p1.sayHello === p2.sayHello) // => false 两个sayHello是不一样的

对于这种问题我们可以把需要共享的函数定义到构造函数外部

你肯定想到了可以把多个函数放到一个对象中用来避免全局命名空间冲突的问题:

var fns = {
     
  sayHello: function () {
     
    console.log('hello ' + this.name)
  },
  sayAge: function () {
     
    console.log(this.age)
  }
}

function Person (name, age) {
     
  this.name = name
  this.age = age
  this.type = 'human'
  this.sayHello = fns.sayHello
  this.sayAge = fns.sayAge
}

var p1 = new Person('lpz', 18)
var p2 = new Person('Jack', 16)

console.log(p1.sayHello === p2.sayHello) // => true
console.log(p1.sayAge === p2.sayAge) // => true

至此,我们利用自己的方式基本上解决了构造函数的内存浪费问题。
但是代码看起来还是那么的格格不入,那有没有更好的方式呢?

原型对象

prototype
目的 : 

	为了减少代码冗余(占用内存), 可以把共同的代码保存到一个构造函数创建出来的所有对象都可以访问的空间, 这个空间称之为原型对象.

具体的步骤:

	1. js解释器, 在碰到构造函数时, 会自动开辟一块空间, 并给构造函数对象添加一个属性 prototype, 可以吧所有的对象共用的代码存出来这片空间中, 这个空间就称之为原型对象.
	
	2. 在使用new关键字创建对象时, js解释器会给新创建出来的对象默认添加一个隐含的属性__proto__, 这个属性中存储的就是构造函数中原型对象的地址, 通过这个地址, 每个实例对象都可以访问构造函数的原型对象中的属性和方法.
	
	3. 当我们使用实例对象.方法名或属性名访问对象的属性或方法时, 会先在实例对象本身的空间进行查找, 如果找不到, 回到原型对象中进行查找.

原型对象语法

function Person (name, age) {
     
  this.name = name;
  this.age = age;
}

// 给Person原型对象增加一个属性
Person.prototype.text = '随便写的文本内容';

// 将 Person.prototype 重置到一个新的对象
Person.prototype = {
     
    // 保持 constructor 的指向正确
  constructor: Person, // 手动将 constructor 指向正确的构造函数
  type: 'human',
  sayHello: function () {
     
    console.log('我叫' + this.name + ',我今年' + this.age + '岁了')
  }
}

// 在原型链中查找或添加/修改
	1. 查找属性或方法时, 自身有就找到自己的, 自己没有就找原型对象
	2. 添加属性或方法时, 自身有就在自身改变, 自己没有就在自身添加, 不改变原型对象
三角关系

![图像 030](C:\Users\NElK\Pictures\sss\图像 030.png)

原型链

![图像 031](C:\Users\NElK\Pictures\sss\图像 031.png)

	作用:

		为了代码的重用。把相同的方法,只保存一份!

	A和B对象具有同样的方法,发现某些方法跟C和D对象的方法也具有同样的代码

案例:随机方块


面向对象:贪吃蛇

案例介绍

游戏演示

演示:贪吃蛇

案例目标

游戏的目的是用来体会js高级语法的使用 不需要具备抽象对象的能力,使用面向对象的方式分析问题,需要一个漫长的过程。

功能实现

搭建页面

放一个容器盛放游戏场景 div#map,设置样式

#map {
     
  width: 800px;
  height: 600px;
  background-color: #ccc;
  position: relative;
}
分析对象
  • 游戏对象
  • 蛇对象
  • 食物对象
创建食物对象
  • Food
    • 属性
      • x
      • y
      • width
      • height
      • color
    • 方法
      • render 随机创建一个食物对象,并输出到map上
  • 创建Food的构造函数,并设置属性
var position = 'absolute';
var elements = [];
function Food(x, y, width, height, color) {
     
  this.x = x || 0;
  this.y = y || 0;
  // 食物的宽度和高度(像素)
  this.width = width || 20;
  this.height = height || 20;
  // 食物的颜色
  this.color = color || 'green';
}
  • 通过原型设置render方法,实现随机产生食物对象,并渲染到map上
Food.prototype.render = function (map) {
     
  // 随机食物的位置,map.宽度/food.宽度,总共有多少分food的宽度,随机一下。然后再乘以food的宽度
  this.x = parseInt(Math.random() * map.offsetWidth / this.width) * this.width;
  this.y = parseInt(Math.random() * map.offsetHeight / this.height) * this.height;

  // 动态创建食物对应的div
  var div = document.createElement('div');
  map.appendChild(div);
  div.style.position = position;
  div.style.left = this.x + 'px';
  div.style.top = this.y + 'px';
  div.style.width = this.width + 'px';
  div.style.height = this.height + 'px';
  div.style.backgroundColor = this.color;
  elements.push(div);
}
  • 通过自调用函数,进行封装,通过window暴露Food对象
window.Food = Food;
创建蛇对象
  • Snake
  • 属性
    • width 蛇节的宽度 默认20
    • height 蛇节的高度 默认20
    • body 数组,蛇的头部和身体,第一个位置是蛇头
    • direction 蛇运动的方向 默认right 可以是 left top bottom
  • 方法
    • render 把蛇渲染到map上
  • Snake构造函数
var position = 'absolute';
var elements = [];
function Snake(width, height, direction) {
     
  // 设置每一个蛇节的宽度
  this.width = width || 20;
  this.height = height || 20;
  // 蛇的每一部分, 第一部分是蛇头
  this.body = [
    {
     x: 3, y: 2, color: 'red'},
    {
     x: 2, y: 2, color: 'red'},
    {
     x: 1, y: 2, color: 'red'}
  ];
  this.direction = direction || 'right';
}
  • render方法
Snake.prototype.render = function(map) {
     
  for(var i = 0; i < this.body.length; i++) {
     
    var obj = this.body[i];
    var div = document.createElement('div');
    map.appendChild(div);
    div.style.left = obj.x * this.width + 'px';
    div.style.top = obj.y * this.height + 'px';
    div.style.position = position;
    div.style.backgroundColor = obj.color;
    div.style.width = this.width + 'px';
    div.style.height = this.height + 'px';
  }
}
  • 在自调用函数中暴露Snake对象
window.Snake = Snake;
创建游戏对象

游戏对象,用来管理游戏中的所有对象和开始游戏

  • Game

    • 属性
      • food
      • snake
      • map
    • 方法
      • start 开始游戏(绘制所有游戏对象)
  • 构造函数

function Game(map) {
     
  this.food = new Food();
  this.snake = new Snake();
  this.map = map;
}
  • 开始游戏,渲染食物对象和蛇对象
Game.prototype.start = function () {
     
  this.food.render(this.map);
  this.snake.render(this.map);
}

游戏的逻辑

写蛇的move方法
  • 在蛇对象(snake.js)中,在Snake的原型上新增move方法
  1. 让蛇移动起来,把蛇身体的每一部分往前移动一下
  2. 蛇头部分根据不同的方向决定 往哪里移动
Snake.prototype.move = function (food, map) {
     
  // 让蛇身体的每一部分往前移动一下
  var i = this.body.length - 1;
  for(; i > 0; i--) {
     
    this.body[i].x = this.body[i - 1].x;
    this.body[i].y = this.body[i - 1].y;
  }
  // 根据移动的方向,决定蛇头如何处理
  switch(this.direction) {
     
    case 'left': 
      this.body[0].x -= 1;
      break;
    case 'right':
      this.body[0].x += 1;
      break;
    case 'top':
      this.body[0].y -= 1;
      break;
    case 'bottom':
      this.body[0].y += 1;
      break;
  }
}
  • 在game中测试
this.snake.move(this.food, this.map);
this.snake.render(this.map);
让蛇自己动起来
  • 私有方法

    什么是私有方法?
      不能被外部访问的方法
    如何创建私有方法?
      使用自调用函数包裹
    
  • 在game.js中 添加runSnake的私有方法,开启定时器调用蛇的move和render方法,让蛇动起来

  • 判断蛇是否撞墙

function runSnake() {
     
  var timerId = setInterval(function() {
     
    this.snake.move(this.food, this.map);
    // 在渲染前,删除之前的蛇
    this.snake.render(this.map);

    // 判断蛇是否撞墙
    var maxX = this.map.offsetWidth / this.snake.width;
    var maxY = this.map.offsetHeight / this.snake.height;
    var headX = this.snake.body[0].x;
    var headY = this.snake.body[0].y;
    if (headX < 0 || headX >= maxX) {
     
      clearInterval(timerId);
      alert('Game Over');
    }

    if (headY < 0 || headY >= maxY) {
     
      clearInterval(timerId);
      alert('Game Over');
    }

  }.bind(that), 150);
}
  • 在snake中添加删除蛇的私有方法,在render中调用
function remove() {
     
  // 删除渲染的蛇
  var i = elements.length - 1;
  for(; i >= 0; i--) {
     
    // 删除页面上渲染的蛇
    elements[i].parentNode.removeChild(elements[i]);
    // 删除elements数组中的元素
    elements.splice(i, 1);
  }
}
  • 在game中通过键盘控制蛇的移动方向
function bindKey() {
     
  document.addEventListener('keydown', function(e) {
     
    switch (e.keyCode) {
     
      case 37:
        // left
        this.snake.direction = 'left';
        break;
      case 38:
        // top
        this.snake.direction = 'top';
        break;
      case 39:
        // right
        this.snake.direction = 'right';
        break;
      case 40:
        // bottom
        this.snake.direction = 'bottom';
        break;
    }
  }.bind(that), false);
}
  • 在start方法中调用
bindKey();
判断蛇是否吃到食物
// 在Snake的move方法中

// 在移动的过程中判断蛇是否吃到食物
// 如果蛇头和食物的位置重合代表吃到食物
// 食物的坐标是像素,蛇的坐标是几个宽度,进行转换
var headX = this.body[0].x * this.width;
var headY = this.body[0].y * this.height;
if (headX === food.x && headY === food.y) {
     
  // 吃到食物,往蛇节的最后加一节
  var last = this.body[this.body.length - 1];
  this.body.push({
     
    x: last.x,
    y: last.y,
    color: last.color
  })
  // 把现在的食物对象删除,并重新随机渲染一个食物对象
  food.render(map);
}

其它处理

把html中的js代码放到index.js中

避免html中出现js代码

自调用函数的参数
(function (window, undefined) {
     
  var document = window.document;

}(window, undefined))
  • 传入window对象

将来代码压缩的时候,可以吧 function (window) 压缩成 function (w)

  • 传入undefined

在将来会看到别人写的代码中会把undefined作为函数的参数(当前案例没有使用)
因为在有的老版本的浏览器中 undefined可以被重新赋值,防止undefined 被重新赋值

整理代码

现在的代码结构清晰,谁出问题就找到对应的js文件即可。
通过自调用函数,已经防止了变量命名污染的问题

但是,由于js文件数较多,需要在页面上引用,会产生文件依赖的问题(先引入那个js,再引入哪个js)
将来通过工具把js文件合并并压缩。现在手工合并js文件演示

  • 问题1
// 如果存在多个自调用函数要用分号分割,否则语法错误
// 下面代码会报错
(function () {
     
}())

(function () {
     
}())
// 所以代码规范中会建议在自调用函数之前加上分号
// 下面代码没有问题
;(function () {
     
}())

;(function () {
     
}())
  • 问题2
// 当自调用函数 前面有函数声明时,会把自调用函数作为参数
// 所以建议自调用函数前,加上;
var a = function () {
     
  alert('11');
}
    
(function () {
     
  alert('22');
}())

继承

什么是继承

  • 现实生活中的继承
  • 程序中的继承

构造函数的属性继承:借用构造函数

function Person (name, age) {
     
  this.type = 'human'
  this.name = name
  this.age = age
}

function Student (name, age) {
     
  // 借用构造函数继承属性成员
  Person.call(this, name, age)
}

var s1 = Student('张三', 18)
console.log(s1.type, s1.name, s1.age) // => human 张三 18

构造函数的原型方法继承:拷贝继承(for-in)

function Person (name, age) {
     
  this.type = 'human'
  this.name = name
  this.age = age
}

Person.prototype.sayName = function () {
     
  console.log('hello ' + this.name)
}

function Student (name, age) {
     
  Person.call(this, name, age)
}

// 原型对象拷贝继承原型对象成员
for(var key in Person.prototype) {
     
  Student.prototype[key] = Person.prototype[key]
}

var s1 = Student('张三', 18)

s1.sayName() // => hello 张三

另一种继承方式:原型继承

function Person (name, age) {
     
  this.type = 'human'
  this.name = name
  this.age = age
}

Person.prototype.sayName = function () {
     
  console.log('hello ' + this.name)
}

function Student (name, age) {
     
  Person.call(this, name, age)
}

// 利用原型的特性实现继承
Student.prototype = new Person()

var s1 = Student('张三', 18)

console.log(s1.type) // => human

s1.sayName() // => hello 张三

函数进阶

函数的定义方式

  • 函数声明
  • 函数表达式
  • new Function
函数声明
function foo () {
     

}
函数表达式
var foo = function () {
     

}
函数声明与函数表达式的区别
  • 函数声明必须有名字
  • 函数声明会函数提升,在预解析阶段就已创建,声明前后都可以调用
  • 函数表达式类似于变量赋值
  • 函数表达式可以没有名字,例如匿名函数
  • 函数表达式没有变量提升,在执行阶段创建,必须在表达式执行之后才可以调用

下面是一个根据条件定义函数的例子:

if (true) {
     
  function f () {
     
    console.log(1)
  }
} else {
     
  function f () {
     
    console.log(2)
  }
}

以上代码执行结果在不同浏览器中结果不一致。

不过我们可以使用函数表达式解决上面的问题:

var f

if (true) {
     
  f = function () {
     
    console.log(1)
  }
} else {
     
  f = function () {
     
    console.log(2)
  }
}

函数的调用方式

  • 普通函数
  • 构造函数
  • 对象方法

函数内 this 指向的不同场景

函数的调用方式决定了 this 指向的不同:

调用方式 非严格模式 备注
普通函数调用 window 严格模式下是 undefined
构造函数调用 实例对象 原型方法中 this 也是实例对象
对象方法调用 该方法所属对象 紧挨着的对象
事件绑定方法 绑定事件对象
定时器函数 window

这就是对函数内部 this 指向的基本整理,写代码写多了自然而然就熟悉了。

函数也是对象

  • 所有函数都是 Function 的实例

call、apply、bind

那了解了函数 this 指向的不同场景之后,我们知道有些情况下我们为了使用某种特定环境的 this 引用,
这时候时候我们就需要采用一些特殊手段来处理了,例如我们经常在定时器外部备份 this 引用,然后在定时器函数内部使用外部 this 的引用。
然而实际上对于这种做法我们的 JavaScript 为我们专门提供了一些函数方法用来帮我们更优雅的处理函数内部 this 指向问题。
这就是接下来我们要学习的 call、apply、bind 三个函数方法。

call

call() 方法调用一个函数, 其具有一个指定的 this 值和分别地提供的参数(参数的列表)。

注意:该方法的作用和 `apply()` 方法类似,只有一个区别,就是 `call()` 方法接受的是若干个参数的列表,而 `apply()` 方法接受的是一个包含多个参数的数组。

语法:

fun.call(thisArg[, arg1[, arg2[, ...]]])

参数:

  • thisArg
    • 在 fun 函数运行时指定的 this 值
    • 如果指定了 null 或者 undefined 则内部 this 指向 window
  • arg1, arg2, ...
    • 指定的参数列表
apply

apply() 方法调用一个函数, 其具有一个指定的 this 值,以及作为一个数组(或类似数组的对象)提供的参数。

注意:该方法的作用和 `call()` 方法类似,只有一个区别,就是 `call()` 方法接受的是若干个参数的列表,而 `apply()` 方法接受的是一个包含多个参数的数组。

语法:

fun.apply(thisArg, [argsArray])

参数:

  • thisArg
  • argsArray

apply()call() 非常相似,不同之处在于提供参数的方式。
apply() 使用参数数组而不是一组参数列表。例如:

fun.apply(this, ['eat', 'bananas'])
bind

bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的call属性)。
当目标函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也接受预设的参数提供给原函数。
一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。

语法:

fun.bind(thisArg[, arg1[, arg2[, ...]]])

参数:

  • thisArg
    • 当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用new 操作符调用绑定函数时,该参数无效。
  • arg1, arg2, …
    • 当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。

返回值:

返回由指定的this值和初始化参数改造的原函数拷贝。

示例1:

this.x = 9; 
var module = {
     
  x: 81,
  getX: function() {
      return this.x; }
};

module.getX(); // 返回 81

var retrieveX = module.getX;
retrieveX(); // 返回 9, 在这种情况下,"this"指向全局作用域

// 创建一个新函数,将"this"绑定到module对象
// 新手可能会被全局的x变量和module里的属性x所迷惑
var boundGetX = retrieveX.bind(module);
boundGetX(); // 返回 81

示例2:

function LateBloomer() {
     
  this.petalCount = Math.ceil(Math.random() * 12) + 1;
}

// Declare bloom after a delay of 1 second
LateBloomer.prototype.bloom = function() {
     
  window.setTimeout(this.declare.bind(this), 1000);
};

LateBloomer.prototype.declare = function() {
     
  console.log('I am a beautiful flower with ' +
    this.petalCount + ' petals!');
};

var flower = new LateBloomer();
flower.bloom();  // 一秒钟后, 调用'declare'方法
小结
  • call 和 apply 特性一样
    • 都是用来调用函数,而且是立即调用
    • 但是可以在调用函数的同时,通过第一个参数指定函数内部 this 的指向
    • call 调用的时候,参数必须以参数列表的形式进行传递,也就是以逗号分隔的方式依次传递即可
    • apply 调用的时候,参数必须是一个数组,然后在执行的时候,会将数组内部的元素一个一个拿出来,与形参一一对应进行传递
    • 如果第一个参数指定了 null 或者 undefined 则内部 this 指向 window
  • bind
    • 可以用来指定内部 this 的指向,然后生成一个改变了 this 指向的新的函数
    • 它和 call、apply 最大的区别是:bind 不会调用
    • bind 支持传递参数,它的传参方式比较特殊,一共有两个位置可以传递
        1. 在 bind 的同时,以参数列表的形式进行传递
        1. 在调用的时候,以参数列表的形式进行传递
      • 那到底以谁 bind 的时候传递的参数为准呢还是以调用的时候传递的参数为准
      • 两者合并:bind 的时候传递的参数和调用的时候传递的参数会合并到一起,传递到函数内部

函数的其它成员

  • arguments
    • 实参集合
  • caller
    • 函数的调用者
  • length
    • 形参的个数
  • name
    • 函数的名称
function fn(x, y, z) {
     
  console.log(fn.length) // => 形参的个数
  console.log(arguments) // 伪数组实参参数集合
  console.log(arguments.callee === fn) // 函数本身
  console.log(fn.caller) // 函数的调用者
  console.log(fn.name) // => 函数的名字
}

function f() {
     
  fn(10, 20, 30)
}

f()

高阶函数

  • 函数可以作为参数
  • 函数可以作为返回值
作为参数
function eat (callback) {
     
  setTimeout(function () {
     
    console.log('吃完了')
    callback()
  }, 1000)
}

eat(function () {
     
  console.log('去唱歌')
})
作为返回值
function genFun (type) {
     
  return function (obj) {
     
    return Object.prototype.toString.call(obj) === type
  }
}

var isArray = genFun('[object Array]')
var isObject = genFun('[object Object]')

console.log(isArray([])) // => true
console.log(isArray({
     })) // => true

函数闭包

function fn () {
     
  var count = 0
  return {
     
    getCount: function () {
     
      console.log(count)
    },
    setCount: function () {
     
      count++
    }
  }
}

var fns = fn()

fns.getCount() // => 0
fns.setCount()
fns.getCount() // => 1
作用域、作用域链、预解析
  • 全局作用域
  • 函数作用域
  • 没有块级作用域
{
     
  var foo = 'bar'
}

console.log(foo)

if (true) {
     
  var a = 123
}
console.log(a)

作用域链示例代码:

var a = 10

function fn () {
     
  var b = 20

  function fn1 () {
     
    var c = 30
    console.log(a + b + c)
  }

  function fn2 () {
     
    var d = 40
    console.log(c + d)
  }

  fn1()
  fn2()
}
  • 内层作用域可以访问外层作用域,反之不行
什么是闭包

闭包就是能够读取其他函数内部变量的函数,
由于在 Javascript 语言中,只有函数内部的子函数才能读取局部变量,
因此可以把闭包简单理解成 “定义在一个函数内部的函数”。
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

闭包的用途:

  • 可以在函数外部读取函数内部成员
  • 让函数内成员始终存活在内存中
一些关于闭包的例子

示例1:

var arr = [10, 20, 30]
for(var i = 0; i < arr.length; i++) {
     
  arr[i] = function () {
     
    console.log(i)
  }
}

示例2:

console.log(111)

for(var i = 0; i < 3; i++) {
     
  setTimeout(function () {
     
    console.log(i)
  }, 0)
}
console.log(222)

示例3:投票

示例4:判断类型

示例5:沙箱模式

闭包的思考题

思考题 1:

var name = "The Window";
var object = {
     
  name: "My Object",
  getNameFunc: function () {
     
    return function () {
     
      return this.name;
    };
  }
};

console.log(object.getNameFunc()())

思考题 2:

var name = "The Window";  
var object = {
         
  name: "My Object",
  getNameFunc: function () {
     
    var that = this;
    return function () {
     
      return that.name;
    };
  }
};
console.log(object.getNameFunc()())
小结

函数递归

递归执行模型
function fn1 () {
     
  console.log(111)
  fn2()
  console.log('fn1')
}

function fn2 () {
     
  console.log(222)
  fn3()
  console.log('fn2')
}

function fn3 () {
     
  console.log(333)
  fn4()
  console.log('fn3')
}

function fn4 () {
     
  console.log(444)
  console.log('fn4')
}

fn1()
举个栗子:计算阶乘的递归函数
function factorial (num) {
     
  if (num <= 1) {
     
    return 1
  } else {
     
    return num * factorial(num - 1)
  }
}
递归应用场景
  • 深拷贝
  • 菜单树
  • 遍历 DOM 树

正则表达式

  • 了解正则表达式基本语法
  • 能够使用JavaScript的正则对象

正则表达式简介

什么是正则表达式

正则表达式:用于匹配规律规则的表达式,正则表达式最初是科学家对人类神经系统的工作原理的早期研究,现在在编程语言中有广泛的应用。正则表通常被用来检索、替换那些符合某个模式(规则)的文本。
正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。

正则表达式的作用
  1. 给定的字符串是否符合正则表达式的过滤逻辑(匹配)
  2. 可以通过正则表达式,从字符串中获取我们想要的特定部分(提取)
  3. 强大的字符串替换能力(替换)
正则表达式的特点
  1. 灵活性、逻辑性和功能性非常的强
  2. 可以迅速地用极简单的方式达到字符串的复杂控制
  3. 对于刚接触的人来说,比较晦涩难懂

正则表达式的测试

  • 在线测试正则
  • 工具中使用正则表达式
    • sublime/vscode/word
    • 演示替换所有的数字

正则表达式的组成

  • 普通字符
  • 特殊字符(元字符):正则表达式中有特殊意义的字符

示例演示:

  • \d 匹配数字
  • ab\d 匹配 ab1、ab2

元字符串

通过测试工具演示下面元字符的使用

常用元字符串
元字符 说明
\d 匹配数字
\D 匹配任意非数字的字符
\w 匹配字母或数字或下划线
\W 匹配任意不是字母,数字,下划线
\s 匹配任意的空白符
\S 匹配任意不是空白符的字符
. 匹配除换行符以外的任意单个字符
^ 表示匹配行首的文本(以谁开始)
$ 表示匹配行尾的文本(以谁结束)
限定符
限定符 说明
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次
其它
[] 字符串用中括号括起来,表示匹配其中的任一字符,相当于或的意思
[^]  匹配除中括号以内的内容
\ 转义符
| 或者,选择两者中的一个。注意|将左右两边分为两部分,而不管左右两边有多长多乱
() 从两个直接量中选择一个,分组
   eg:gr(a|e)y匹配gray和grey
[\u4e00-\u9fa5]  匹配汉字

案例

验证手机号:

^\d{
     11}$

验证邮编:

^\d{
     6}$

验证日期 2012-5-01

^\d{
     4}-\d{
     1,2}-\d{
     1,2}$

验证邮箱 [email protected]

^\w+@\w+\.\w+$

验证IP地址 192.168.1.10

^\d{
     1,3}\(.\d{
     1,3}){
     3}$

JavaScript 中使用正则表达式

创建正则对象

方式1:

var reg = new Regex('\d', 'i');
var reg = new Regex('\d', 'gi');

方式2:

var reg = /\d/i;
var reg = /\d/gi;
参数
标志 说明
i 忽略大小写
g 全局匹配
gi 全局匹配+忽略大小写

正则匹配

// 匹配日期
var dateStr = '2015-10-10';
var reg = /^\d{4}-\d{1,2}-\d{1,2}$/
console.log(reg.test(dateStr));

正则提取

// 1. 提取工资
var str = "张三:1000,李四:5000,王五:8000。";
var array = str.match(/\d+/g);
console.log(array);

// 2. 提取email地址
var str = "[email protected],[email protected] [email protected] 2、[email protected] [email protected]...";
var array = str.match(/\w+@\w+\.\w+(\.\w+)?/g);
console.log(array);

// 3. 分组提取  
// 3. 提取日期中的年部分  2015-5-10
var dateStr = '2016-1-5';
// 正则表达式中的()作为分组来使用,获取分组匹配到的结果用Regex.$1 $2 $3....来获取
var reg = /(\d{4})-\d{1,2}-\d{1,2}/;
if (reg.test(dateStr)) {
     
  console.log(RegExp.$1);
}

// 4. 提取邮件中的每一部分
var reg = /(\w+)@(\w+)\.(\w+)(\.\w+)?/;
var str = "[email protected]";
if (reg.test(str)) {
     
  console.log(RegExp.$1);
  console.log(RegExp.$2);
  console.log(RegExp.$3);
}

正则替换

// 1. 替换所有空白
var str = "   123AD  asadf   asadfasf  adf ";
str = str.replace(/\s/g,"xx");
console.log(str);

// 2. 替换所有,|,
var str = "abc,efg,123,abc,123,a";
str = str.replace(/,|,/g, ".");
console.log(str);

案例:表单验证

QQ号:<input type="text" id="txtQQ"><span>span><br>
邮箱:<input type="text" id="txtEMail"><span>span><br>
手机:<input type="text" id="txtPhone"><span>span><br>
生日:<input type="text" id="txtBirthday"><span>span><br>
姓名:<input type="text" id="txtName"><span>span><br>
//获取文本框
var txtQQ = document.getElementById("txtQQ");
var txtEMail = document.getElementById("txtEMail");
var txtPhone = document.getElementById("txtPhone");
var txtBirthday = document.getElementById("txtBirthday");
var txtName = document.getElementById("txtName");

//
txtQQ.onblur = function () {
     
  //获取当前文本框对应的span
  var span = this.nextElementSibling;
  var reg = /^\d{5,12}$/;
  //判断验证是否成功
  if(!reg.test(this.value) ){
     
    //验证不成功
    span.innerText = "请输入正确的QQ号";
    span.style.color = "red";
  }else{
     
    //验证成功
    span.innerText = "";
    span.style.color = "";
  }
};

//txtEMail
txtEMail.onblur = function () {
     
  //获取当前文本框对应的span
  var span = this.nextElementSibling;
  var reg = /^\w+@\w+\.\w+(\.\w+)?$/;
  //判断验证是否成功
  if(!reg.test(this.value) ){
     
    //验证不成功
    span.innerText = "请输入正确的EMail地址";
    span.style.color = "red";
  }else{
     
    //验证成功
    span.innerText = "";
    span.style.color = "";
  }
};

表单验证部分,封装成函数:

var regBirthday = /^\d{4}-\d{1,2}-\d{1,2}$/;
addCheck(txtBirthday, regBirthday, "请输入正确的出生日期");
//给文本框添加验证
function addCheck(element, reg, tip) {
     
  element.onblur = function () {
     
    //获取当前文本框对应的span
    var span = this.nextElementSibling;
    //判断验证是否成功
    if(!reg.test(this.value) ){
     
      //验证不成功
      span.innerText = tip;
      span.style.color = "red";
    }else{
     
      //验证成功
      span.innerText = "";
      span.style.color = "";
    }
  };
}

通过给元素增加自定义验证属性对表单进行验证:

<form id="frm">
  QQ号:<input type="text" name="txtQQ" data-rule="qq"><span>span><br>
  邮箱:<input type="text" name="txtEMail" data-rule="email"><span>span><br>
  手机:<input type="text" name="txtPhone" data-rule="phone"><span>span><br>
  生日:<input type="text" name="txtBirthday" data-rule="date"><span>span><br>
  姓名:<input type="text" name="txtName" data-rule="cn"><span>span><br>
form>
// 所有的验证规则
var rules = [
  {
     
    name: 'qq',
    reg: /^\d{5,12}$/,
    tip: "请输入正确的QQ"
  },
  {
     
    name: 'email',
    reg: /^\w+@\w+\.\w+(\.\w+)?$/,
    tip: "请输入正确的邮箱地址"
  },
  {
     
    name: 'phone',
    reg: /^\d{11}$/,
    tip: "请输入正确的手机号码"
  },
  {
     
    name: 'date',
    reg: /^\d{4}-\d{1,2}-\d{1,2}$/,
    tip: "请输入正确的出生日期"
  },
  {
     
    name: 'cn',
    reg: /^[\u4e00-\u9fa5]{2,4}$/,
    tip: "请输入正确的姓名"
  }];

addCheck('frm');


//给文本框添加验证
function addCheck(formId) {
     
  var i = 0,
      len = 0,
      frm =document.getElementById(formId);
  len = frm.children.length;
  for (; i < len; i++) {
     
    var element = frm.children[i];
    // 表单元素中有name属性的元素添加验证
    if (element.name) {
     
      element.onblur = function () {
     
        // 使用dataset获取data-自定义属性的值
        var ruleName = this.dataset.rule;
        var rule =getRuleByRuleName(rules, ruleName);

        var span = this.nextElementSibling;
        //判断验证是否成功
        if(!rule.reg.test(this.value) ){
     
          //验证不成功
          span.innerText = rule.tip;
          span.style.color = "red";
        }else{
     
          //验证成功
          span.innerText = "";
          span.style.color = "";
        }
      }
    }
  }
}

// 根据规则的名称获取规则对象
function getRuleByRuleName(rules, ruleName) {
     
  var i = 0,
      len = rules.length;
  var rule = null;
  for (; i < len; i++) {
     
    if (rules[i].name == ruleName) {
     
      rule = rules[i];
      break;
    }
  }
  return rule;
}

附录

A 代码规范

代码风格
  • JavaScript Standard Style
  • Airbnb JavaScript Style Guide() {
校验工具
  • JSLint
  • JSHint
  • ESLint

B Chrome 开发者工具

C 文档相关工具

  • 电子文档制作工具: docute
  • 流程图工具:[DiagramDesigner](

你可能感兴趣的:(javascript,javascript,jQuery,WebApi)