# javascript基础
## 一、javascript的介绍
### (一)javascript的诞生
>1994 年,网景公司(NetScape)发布了Navigator浏览器0.9版, 这是历史上第一个比较成熟的浏览器,引起了广泛关注。但是,这个版本的浏览器只能用来浏览,不具备与访问者互动的能力。。。。网景公司急需要一门网页脚本语言,使得浏览器可以与网页进行互动。
1995年4月,网景公司录用了34岁的系统程序员Brendan Eich, 他只用10天时间就把Javascript设计出来。布兰登·艾奇
### (二)javascript的特点
+ javascript是一个轻量级的语言
+ javascript是可以插入HTML页面的编程代码
+ javascript支持目前所有的浏览器
+ 解释执行不需要编译
+ 基于对象,内置大量的现成对象,编写少量的程序就可以完成目标
### (三)javascript的组成
+ ECMAScript javascript的语法标准
+ DOM(Document Object Model)文档对象模型: javascript 操作网页元素的API
+ BOM(Browser Object Model) javascript 操作浏览器部分功能的API
### (四)javascript 和 Html css 的关系
+ Html 用来编写网页的结构
+ css 美化网页添加样式
+ javacript 实现网页和客户之间的沟通,让网页有活力
## 二、javascript的书写位置
### 1、在html标签中使用 **行内式**
```
<button onclick="alert('wsfsf ')">按钮</button>
```
### 2、在HTML内部使用
`<script>脚本内容</script>`标签,可以放在任意的地方 **页内式**
+ head标签中使用
```
<script>
window.onload = function () {
alert('wwwww')
}
</script>
```
+ body标签中使用
### 3、外部调用
javascript在script标签的src属性中设置 `<script src="脚本的路径"></script>`在外部的脚本中不能包括script标签。 **外链式**
### 4、**js代码书写需要注意的问题:**
```
1. 在一对script标签中,出现错误,那么这对script标签中的后面的代码则不会再往下执行;
2. 如果有一对的script标签中出现错误,不会影响其他script标签对的执行;
3. script中可以写什么内容:type="text/javascript"是标准的写法,也可以写language="javascript",但是在目前的html中,type和language则可以省略,因为html是遵循h5的标准。
4. 有时兼容性的问题,type和language会同时出现;
5. script可以在页面中出现多对;
6. 如果script是引入外部的js文件,则标签内不能再写js代码,要写则需要重新写一行script包裹;
```
## 三、javascript显示数据的方式
### 1、使用 window.alert() 弹出警告框
```
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>text</title>
</head>
<body>
<h1>我的第一个页面</h1>
<p>我的第一个段落</p>
<script type="text/javascript">
window.alert("我的第一个弹窗");
</script>
</body>
</html>
```
### 2、使用document.write() 方法将内容写到html文档中
```
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>text</title>
</head>
<body>
<h1>第一个页面</h1>
<p>第一个段落</p>
<script type="text/javascript">
document.write(Date());
</script>-
</body>
```
```
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>text</title>
</head>
<body>
<button onclick="myfunction()">来点我呀,com on</button>
<script type="text/javascript">
function myfunction(){
document.write(Date())
}
</script>
</body>
</html>
```
### 3、使用innerHTML写入到HTML元素。
```
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>text</title>
</head>
<body>;
<h1>我的第一个页面</h1>
<p id="demo">我的第一个段落</p>
<script type="text/javascript">
document.getElementById("demo").innerHTML = "段落被修改了";
</script>
</body>
</html>
```
### 4、使用console.log()写入到浏览器的控制台。
F12 启用调试模式, 在调试窗口中点击 "Console" 菜单。`console.warn("警告输出")`,`console.error('这是一个错误')`
```
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<script type="text/javascript">
var a = 5;
var b = 6;
var c = a + b;
console.log(c);
</script>
</body>
</html>
```
### 5、用户输入 prompt()语句
prompt()语句就是专门用来弹出能够让用户输入的对话框。用一个变量来接收值,且不论用户输入的是什么都是一个字符串。
```
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<script type="text/javascript">
var a = prompt('今天的天气如何');
console.log(a);
</script>
</body>
</html>
```
### 6、confirm
在网页中弹出提示框,显示信息,一般和if 判断来配合使用,相比alert多了一个取消的按钮。
```
<script>
comfirm("Hello,JavaScript!");
</script>
```
### 7、javascript显示方式的说明和对比
#### (1) console.log()和alert相比
```
console.log()的用处主要是方便你调式javascript用的, 你可以看到你在页面中输出的内容。
相比alert他的优点是:他能看到结构化的东西,如果是alert,弹出一个对象就是[object object],但是console能看到对象的内容。console不会打断你页面的操作,如果用alert弹出来内容,那么页面就死了,但是console输出内容后你页面还可以正常操作。
```
#### (2) document.write 和 innerHTML
```
1、document.write是直接写入到页面的内容流,如果在写之前没有调用document.open, 浏览器会自动调用open。每次写完关闭之后重新调用该函数,会导致页面被重写。
2、innerHTML则是DOM页面元素的一个属性,代表该元素的html内容。你可以精确到某一个具体的元素来进行更改。如果想修改document的内容,则需要修改document.documentElement.innerElement。
3、innerHTML很多情况下都优于document.write,其原因在于其允许更精确的控制要刷新页面的那一个部分。
```
#### (3) alert和prompt的区别
```
alert("我很帅"); 直接使用不需要变量
var a = prompt(“请输入一个数字”) 需要用一个变量来接收用户的输入,且这个值一定是一个字符串
```
## 四、javascript的语法
### **js 基本的代码规范:**
+ javascript对换行、缩进、空格不敏感。
+ 每条语句的末尾都要加上分号,除了if、for、function语句结尾处不加分号,如果不加分号,压缩之后将不能运行。
+ 所有的符号都是英文的。
+ js中的字符串可以使用单引号,也可以使用双引号,一般使用双引号;
+ 区分大小写
+ 变量是弱类型的,定义变量时要用var运算符声明变量,可以将它初始化为任意值。因此,可以随时改变变量所存数据的类型(尽量避免这样做)。
+ javascript的注释
+ 单行 // 单行注释可以嵌套单行、多行注释
```
// 单行注释
var name;
```
+ 多行 /* 内容 */ 多行注释可以嵌套单行注释,但是 **不能嵌套多行注释**
```
/*多行注释*/
var name;
```
+ 括号表示代码块,代码块表示一系列应该按顺序执行的语句,这些语句被封装在左括号({)和右括号(})之间。
## 五、javascript的字面量
“直接量”即常量,就是一些不可改变的值,也称为“字面量”,看见什么就是什么。
字面量的种类
+ 数字(number)字面量,可以是整数或者是小数,或者是科学计数(e)。
```
3.14
1001
123e5 #12300000
123e-5 #0.00123
```
+ 字符串(String)字面量 可以使用单引号或双引号
```
"John Doe"
'John Doe'
```
+ 表达式字面量 用于计算
```
5 + 6
5 * 10
```
+ 数组(Array)字面量 定义一个数组
```
[40, 100, 1, 5, 25, 10]
```
+ 对象(Object)字面量 定义一个对象
```
{firstName:"John", lastName:"Doe", age:50, eyeColor:"blue"}
```
+ 函数(Function)字面量 定义一个函数
```
function myFunction(a, b) { return a * b;}
```
## 六、变量
变量表示一些可以变化的数据,当一个数据的值经常变化或是不确定时,就应该用变量来表示。
变量的作用:用来操作数据的(可以读取,可以存储)
### (一)声明变量
+ 变量使用前必须用var运算符进行声明,且不需要声明变量的类型。
```
var test = "hi";
var test1 = 1;
```
+ 可以用一个var声明多个变量,变量之间用逗号分隔。
```
var test1 = 1, test2 = "string";
```
+ 可以用同一个 var 语句声明的变量不必具有相同的类型。
```
var test = "hi", age = 25;
```
+ var声明的变量并不一定要初始化一个值。
```
var test; // 这样也是有效的
```
+ 变量可以存放不同类型的值。这是弱类型变量的优势,如可以把变量初始化为字符串类型的值,之后把它设置为数字值。
```
var test = "hi";
alert(test);
test = 55;
alert(test);
```
+ 变量声明不是必须的。
```
var sTest = "hello ";
sTest2 = sTest + "world";
alert(sTest2);
```
首先,sTest 被声明为字符串类型的值 "hello"。接下来的一行,用变量 sTest2 把 sTest 与字符串 "world" 连在一起。变量 sTest2并没有用var运算符定义,这里只是插入了它,就像已经声明过它一样。
ECMAScript的解释程序遇到未声明过的标识符时,用该变量名创建一个**全局变量**,并将其初始化为指定的值。这是该语言的便利之处,不过如果不能紧密跟踪变量,这样做也很危险。最好的习惯是像使用其他程序设计语言一样,总是声明所有变量。
### (二)变量的命名规范
变量名需要遵守两条简单的规则:
+ 第一个字符必须是字母、下划线(_)或美元符号($),不能以数字开头。
+ 余下的字符可以是下划线、美元符号或任何字母或数字字符;
+ 变量名一般都是小写;
下面的变量都是合法的:
```
var test;
var $test;
var $1;
var _$te$t2;
```
+ 保留字不能用作变量名
```
abstract、boolean、byte、char、class、const、debugger、double、enum、export、extends、final、float、goto
implements、import、int、interface、long、native、package、private、protected、public、short、static、super、synchronized、throws、transient、volatile
```
+ 几种常见的命名方式:
1、Camel 标记法:首字母是小写的,接下来的字母都以大写字符开头。例如:
```
var myTestValue = 0, mySecondValue = "hi";
```
2、Pascal 标记法 首字母是大写的,接下来的字母都以大写字符开头。例如:
```
var MyTestValue = 0, MySecondValue = "hi";
```
3、匈牙利类型标记法:
在以Pascal标记法命名的变量前附加一个小写字母(或小写字母序列),说明该变量的类型。例如,i 表示整数,s 表示字符串,如下所示“
```
var iMyTestValue = 0, sMySecondValue = "hi";
```
### (三)变量值交换
#### 1、第一种方法:利用临时变量进行交换
```
<script type="text/javascript">
var num1 = 10;
var num2 = 20;
var temp = num1;
num1 = num2;
num2 = temp;
console.log(num1, num2); // 20 10
</script>
```
#### 2、第二种方法: 一般用于数字交换
```
<script type="text/javascript">
var num1 = 10;
var num2 = 20;
num1 = num1 + num2;
num2 = num1 - num2;
num1 = num1 - num2;
console.log(num1, num2); // 20 10
</script>
```
#### 3、第三种方法: 位运算交换
```
<script type="text/javascript">
var num1 = 10;
var num2 = 20;
num1 = num1 ^ num2;
num2 = num1 ^ num2;
num1 = num1 ^ num2;
console.log(num1, num2); // 20 10
</script>
```
### (四)变量值的分类
在 ECMAScript 中,变量可以存在两种类型的值,即原始值和引用值。又可以称为基本数据类型(简单类型和值类型)和复杂类型,
原始数据类型有:number string null undefined boolean object
原始值(基本数据类型):number string null undefined boolean
引用值(复杂数据类型):object
**原始值(基本数据类型)**
存储在栈(stack)中的简单数据段,也就是说,它们的值直接存储在变量访问的位置。
**引用值(复杂数据类型)**
存储在堆(heap)中的对象,也就是说,存储在变量处的值是一个指针(point),指向存储对象的内存处。
为变量赋值时,ECMAScript 的解释程序必须判断该值是原始类型,还是引用类型。要实现这一点,解释程序则需尝试判断该值是否为 ECMAScript 的原始类型之一,即 Undefined、Null、Boolean、Number 和 String 型。由于这些原始类型占据的空间是固定的,所以可将他们存储在较小的内存区域 - 栈中。这样存储便于迅速查寻变量的值。
在许多语言中,字符串都被看作引用类型,而非原始类型,因为字符串的长度是可变的。ECMAScript 打破了这一传统。
如果一个值是引用类型的,那么它的存储空间将从堆中分配。由于引用值的大小会改变,所以不能把它放在栈中,否则会降低变量查寻的速度。相反,放在变量的栈空间中的值是该对象存储在堆中的地址。地址的大小是固定的,所以把它存储在栈中对变量性能无任何负面影响。如下图所示:
![栈堆](media/6.png)
#### 原始类型(基本数据类型)
ECMAScript 有 5 种原始类型(primitive type),即 Undefined、Null、Boolean、Number 和 String。
#### typeof运算符查看变量值的类型
typeof 运算符有一个参数,即要检查的变量或值。例如:
```
var sTemp = "test string";
alert (typeof sTemp); //输出 "string"
```
---
```
<script type="text/javascript">
var a = 1;
var b = '我是谁';
var c = 1.3242424;
var d = 3e7;
var e;
var f = "";
console.log(typeof a, typeof b, typeof c, typeof d);
console.log(typeof e);
console.log(typeof f);
console.log(typeof bb);
</script>
```
输出结果:
![输出结果](media/1.png)
**对变量或值调用 typeof 运算符将返回下列值之一:**
+ undefined - 如果变量是 Undefined 类型的
+ boolean - 如果变量是 Boolean 类型的
+ number - 如果变量是 Number 类型的
+ string - 如果变量是 String 类型的
+ object - 如果变量是一种引用类型或 Null 类型的
+ function-函数
**typeof 运算符对于 null 值会返回 "Object"。这实际上是 JavaScript 最初实现中的一个错误,然后被 ECMAScript 沿用了。现在,null 被认为是对象的占位符,从而解释了这一矛盾,但从技术上来说,它仍然是原始值。**
#### 各个原始类型(基本数据类型)详细介绍
##### 1、Undefined 类型
Undefined 类型只有一个值,即 undefined。当声明的变量未初始化时,该变量的默认值是 undefined。
```
var oTemp;
```
**值 undefined 并不同于未定义的值。但是,typeof 运算符并不真正区分这两种值。**
```
console.log(typeof e); //e 被定义了,但是没有给值进行初始化。也是输出undefined
console.log(typeof bb); //bb没有被定义过 也是输出undefined
```
如果对bb使用除typeof之外的其他运算符的话,会引起错误,因为其他运算符只能用于已声明的变量上。
**当函数无明确返回值时,返回的也是值 "undefined"**
```
function testFunc() {
}
alert(testFunc() == undefined); //输出 "true"
```
**调用函数时,应该提供的参数没有提供,该参数等于 undefined**
```javascript
function f(x) {
return x;
}
f() // undefined
```
**对象没有赋值的属性**
```javascript
// 对象没有赋值的属性
var o = new Object();
o.p // undefined
```
##### 2、Null 类型
它只有一个专用值 null,即它的字面量。值 undefined 实际上是从值 null 派生来的,因此 ECMAScript 把它们定义为相等的。
```
alert(null == undefined); //输出 "true"
```
尽管这两个值相等,但它们的含义不同。undefined 是声明了变量但未对其初始化时赋予该变量的值,null则用于表示尚未存在的对象。如果函数或方法要返回的是对象,那么找不到该对象时,返回的通常是 null。想让一个变量的值是Null时,必须手动指定 `var a = Null;`
##### 3、Boolean 类型
它有两个值 true 和 false (即两个 Boolean 字面量)。即使 false 不等于 0,0 也可以在必要时被转换成 false,这样在 Boolean 语句中使用两者都是安全的
> 任何的非0数值都是true,包括正负无穷,只有0和NaN是false
> 任何的非空字符串都是true,只有空字符串是false
> 任何的对象都是true,除了Null和 undefined是false,空数组[],和空对象{}是true
##### 4、Number 类型
ECMA-262 中定义的最特殊的类型是 Number 类型。这种类型既可以表示 32 位的整数,还可以表示 64位的浮点数。直接输入的(而不是从另一个变量访问的)任何数字都被看做 Number 类型的字面量。
* javascript 可正常计算的范围:小数点前16位,后16位。
+ 八进制数和十六进制数
js 可以表示不同进制的数字,得看浏览器的支持
整数也可以被表示为八进制(以8为底)或十六进制(以16为底)的字面量。八进制字面量的首数字必须是 0,其后的数字可以是任何八进制数字(0-7),
```
var iNum = 070; //070 等于十进制的 56
```
要创建十六进制的字面量,首位数字必须为 0,后面接字母 x,然后是任意的十六进制数字(0 到 9 和 A 到 F)。这些字母可以是大写的,也可以是小写的。
```
var iNum = 0x1f; //0x1f 等于十进制的 31
var iNum = 0xAB; //0xAB 等于十进制的 171
```
尽管所有整数都可以表示为八进制或十六进制的字面量,但所有数学运算返回的都是十进制结果。
+ 浮点数
要定义浮点值,必须包括小数点和小数点后的一位数字(例如,用 1.0 而不是 1)。这被看作浮点数字面量。
```
var fNum = 5.0;
```
对于浮点字面量的有趣之处在于,用它进行计算前,真正存储的是字符串
+ 科学计数法
对于非常大或非常小的数,可以用科学计数法表示浮点数,可以把一个数表示为数字(包括十进制数字)加 e(或 E),后面加乘以 10 的倍数。
```
var fNum = 5.618e7
```
该符号表示的是数 56180000。把科学计数法转化成计算式就可以得到该值:5.618 x 107。
+ 特殊的 Number 值
前两个是 Number.MAX_VALUE 和 Number.MIN_VALUE,它们定义了 Number 值集合的外边界。所有ECMAScript数都必须在这两个值之间。不过计算生成的数值结果可以不落在这两个值之间。
最后一个特殊值是 NaN,表示非数(NotaNumber)。NaN是个奇怪的特殊值。一般说来,这 种情况发生在类型(String、Boolean 等)转换失败时。例如,要把单词 blue 转换成数值就会失败,因为没有与之等价的数值。与无穷大一样,NaN也不能用于算术计算, 是一个非法的数字,当对数值进行计算,没有结果返回,则返回NaN,NaN 的另一个奇特之处在于,它与自身不相等,这意味着NaN也不等于NaN。
* NaN 属性是代表非数字值的特殊值。
含义:表示非数字, 主要用在将字符串解析成数字出错的场合。
```javascript
5 - 'x' // NaN
```
一些数学函数的运算结果也会出现NAN
```javascript
Math.acos(2) // NaN
Math.log(-1) // NaN
Math.sqrt(-1) // NaN
```
0除0也会是NaN 1/0是Infinity
运算规则:
NaN不等于任何值,包括它本身`NaN === NaN // false`
Nan在布尔运算时被当作是false `Boolean(NaN) // false`
NaN与任何数(包括它自己)的运算,得到的都是NaN
```javascript
NaN + 32 // NaN
NaN - 32 // NaN
NaN * 32 // NaN
NaN / 32 // NaN
```
###### **小结:**
(1)无论是小数还是整数都是数字类型
(2)不要用小数去验证小数
```
var x=0.1;
var y=0.2;
var sum=x+y;
console.log(sum==0.3); // false
```
(3)不要用NaN判断是不是Nan,而是用isNan(值或者是变量句)
```
判断结果不是一个数字可以使用isNaN(变量名)
var sum=num+10;//NaN
console.log(sum);
console.log(isNaN(sum));//不是数字为true,是数字结果为false
```
(4)想表示十进制就是正常的数字
(5)想表示八进制以0开头
(6)想表示十六进制以0x开头
(7)像010这样的字符串,有些浏览器会当成是8进制,有些则会当成是10进制,为了保证准确性,在进行转换时,parseInt()时,用第二个参数明确具体的进制数。
##### 5、String 类型
String 类型的独特之处在于,它是唯一没有固定大小的原始类型。可以用字符串存储 0 或更多的 Unicode 字符,
字符串字面量是由双引号(")或单引号(')声明的。而Java则是用双引号声明字符串,用单引号声明字符。但是由于 ECMAScript 没有字符类型,所以可使用这两种表示法中的任何一种。
```
var sColor1 = "red";
var sColor2 = 'red';
```
**只要有一个是字符串,其他的是数字,那么结果也是拼接,不是相加**
```
console.log("我" + "爱" + "你"); //连字符,把三个独立的汉字,连接在一起了
console.log("我+爱+你"); //原样输出
console.log(1+2+3); //输出6
```
**字符串 - 或 * 数值 = 数值 如果有一个是字符串,另一个不是字符串,使用 - 或 * 号,此时会发生计算**
```
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<script type="text/javascript">
var a = "54";
var b = 99;
// 浏览器会自动把字符串类型的数字转换为数字类型,这叫隐式转换。
var c = b - a;
console.log(typeof c, c);
</script>
</body>
</html>
```
输出结果:
![输出结果](media/2.png)
如果把a改为字母 var a = 'e'; 则输出结果为:
![输出结果](media/3.png)
如果把-换成*乘号,var c = b * a; 则输出结果为:
![输出结果](media/4.png)
#### 类型转换
##### 1、 转换成字符串
+ toString()方法
```
<script type="text/javascript">
// 数字转字符串
var num1 = 123;
var str1 = num1.toString();
console.log(str1, typeof str1) //123 string
// Boolean 转字符串
var bool = true;
var str2 = bool.toString();
console.log(str2, typeof str2); // true string
</script>
```
**需要注意的地方:**
> null 和 undefined 没有toString()的方法,强行调用会报错
> toString 不会改变原变量的值,它只会将转化的结果返回
+ String() 函数
有时候有些值没有toString()的方法,比如null和undefined
> 如果Number 和 Boolean 用String函数进行字符串的转换,实际上是调用了toString的方法
> 对于null和undefined来说,没有toString的方法,会在内部产生一个新的字符串
```
<script type="text/javascript">
// 数字转字符串
var num1 = 123;
var str1 = String(num1);
console.log(str1, typeof str1); // 123 string
// Boolean 转字符串
var bool = true;
var str2 = String(bool);
console.log(str2, typeof str2); // true string
// null转字符串
var nu = null;
var str3 = String(nu);
console.log(str3, typeof str3); // null string
var unde = undefined;
var str4 = String(unde);
console.log(str4, typeof str4) // undefined string
</script>
```
+ 任何数据和+连接在一起都会转为字符串,其内部的原理和String一样
```
<script>
var num1 = 123;
var str1 = num1 + '';
console.log(str1, typeof str1); // 123 string
var bool = true;
var str2 = bool + '';
console.log(str2, typeof str2); // true string
var nul = null;
var str3 = nul + '';
console.log(str3, typeof str3); // null string
var unde = undefined;
var str4 = unde + '';
console.log(str4, typeof str4); // undefined string
</script>
```
##### 2、转换成数字
parseInt() parseFloat() 和 Number()。
+ 注意的地方
+ parseInt()把值转换成整数,parseFloat()把值转换成浮点数。只有对String类型调用这些方法,它们才能正确运行;对其他类型都是先转化成字符串(string()方法)后再进行转换。他的返回值只有两种,一种是十进制的整数,一种是NaN。
+ Number()函数中无论混合字符串中是否存在有效的整数,都会返回NaN,利用parseInt()和parseFloat()可以提取字符串中的有效整数。
+ parseInt()和parseFloat()的区别是,前者可以提取有效的整数,后者可以提取有效的小数。
+ 对非String使用parseInt()或者parseFloat(),会将其转换成String然后再操作
```
var str11 = true;
var res13 = parseInt(str11); // 这里相当于parseInt("true");
console.log(res13); // NaN
var res14 = Number(str11);
console.log(res14); // 1
```
+ Number()函数
1、字符串转数字
**如果是纯数字字符串,则直接将其转换为数字**
```
<script>
var str1 = "123";
var num1 = Number(str1);
console.log(num1, typeof num1); // 123 "number"
</script>
```
**如果字符串中含有非数字的内容,则转换为NaN**
```
<script>
var str1 = "abc123";
var num1 = Number(str1);
console.log(num1, typeof num1); // NaN "number"
</script>
```
**如果字符串是一个空串,或是一个全部是空格的字符串,则转换为0 **
```
<script>
var str1 = ""; // 空串
var str2 = " "; // 全部是空格
var str3 = "123 " // 最后含有两个空格
var str4 = " 123" // 开头含有两个空格
var num1 = Number(str1);
var num2 = Number(str2);
var num3 = Number(str3);
var num4 = Number(str4);
console.log(num1, typeof num1); // 0 "number"
console.log(num2, typeof num2); // 0 "number"
console.log(num3, typeof num3); // 123 "number"
console.log(num4, typeof num4); // 123 "number"
</script>
```
2、undefined 转数字 NaN
```
<script>
// undefined转数字
var unde = undefined;
var num1 = Number(unde);
console.log(num1, typeof num1); // NaN "number"
</script>
```
3、null转数字 0
```
<script>
// null转数字
var nu = null;
var num1 = Number(nu);
console.log(num1, typeof num1) // 0 "number"
</script>
```
4、布尔转数字 true转成1 false转成0
```
<script>
// 布尔值转数字
var bool = true;
var num1 = Number(bool);
console.log(num1, typeof num1) // 1 "number"
var bool1 = false;
var num2 = Number(bool1);
console.log(num2, typeof num2); // 0 "number"
</script>
```
+ parseInt() 把值转换成整数
1. 带有自动净化的功能;只保留字符串最开头的数字,后面的中文自动消失
parseInt()方法首先查看位置0处的字符,判断它是否是个有效数字;如果不是,该方法将返回 NaN,不再继续执行其他操作。但如果该字符是有效数字,该方法将查看位置 1 处的字符,进行同样的测试。这一过程将持续到发现非有效数字的字符为止,此时 parseInt() 将把该字符之前的字符串转换成数字。
2. 自动带有截断小数的功能:取整,不四舍五入
字符串中包含的数字字面量会被正确转换为数字,比如 "0xA" 会被正确转换为数字 10。不过,字符串 "22.5" 将被转换成 22,因为对于整数来说,小数点是无效字符。
3. parseInt()方法还有基模式,可以把二进制、八进制、十六进制或其他任何进制的字符串转换成整数。
```
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<script type="text/javascript">
var a = '123adfsfw'; // 数字开头的
var b = 'afsfsf234'; // 字母开头的
var c = '1sf23iwr'; // 开头1个数字,混全的
var d = '1334北京!!';
var f = '1.23'; // 小于1.5的
var f1 = '1.98'; //大于1.5的
var aa = 'AF';
var bb = '010';
console.log(parseInt(a))
console.log(parseInt(b))
console.log(parseInt(c))
console.log(parseInt(d))
console.log(parseInt(f))
console.log(parseInt(f1))
console.log(parseInt(aa, 16))
console.log(parseInt(bb, 8))
</script>
</body>
</html>
```
输出结果:
![输出结果](media/5.png)
+ parseFloat() 把值转换成浮点数
+ 会解析第一个,遇到第二个或者非数字结束;
+ 如果第一位不是有效数字,什么也提取不到;
+ 不支持第二个参数,只能解析10进制数;
+ 如果解析内容里只有整数,解析成整数;
```
var fNum1 = parseFloat("12345red"); //返回 12345
var fNum2 = parseFloat("0xA"); //返回 NaN
var fNum3 = parseFloat("11.2"); //返回 11.2
var fNum4 = parseFloat("11.22.33"); //返回 11.22
var fNum5 = parseFloat("0102"); //返回 102
var fNum1 = parseFloat("red"); //返回 NaN
```
## 七、运算符
+ **作用:**
运算符是告诉程序执行特定算术或逻辑操作的符号, 例如告诉程序, 某两个数相加, 相减等
+ 分类
+ 按照功能分:算术、位、关系、逻辑
+ 按照操作个数分:
+ 单目运算 只有一个操作数:如i++
+ 双目运算 有两个操作数:如a+b
+ 三目运算 也称为问号表达式 如:a > b ? 1:0
+ 结合性
JavaScript中各种运算符的结合性分为两种: 左结合性(自左至右) 和 右结合性(自右至左)
+ 自左至右,即先左后右
例如表达式: x - y + z;
则y 应先与“-”号结合,执行 x-y 运算,然后再执行+z 的运算。
这种自左至右的结合 方向就称为“左结合性”。
+ 自右至左,即先右后左
例如:如x = y = z = 10
由于“=”的 右结合性,应先执行z = 10; 再执行y = z 再执行x = y运算。
### (一)加法运算符 +
+ 非Number类型的值,除字符串外,都会先转为Number类型的值后再进行计算
```
var sun = true + 2;
console.log(sun, typeof sun); // 3 "number"
var sun1 = true + false;
console.log(sun1, typeof sun1); // 1 "number"
var sun3 = 3 + null; // null 转为0
console.log(sun3, typeof sun3); // 3 “number"
```
+ 任何值和NaN进行运算时,结果都是NaN
```
var sun2 = NaN + 3;
console.log(sun2, typeof sun2); // NaN "number"
var sun4 = undefined + 3;
console.log(sun4, typeof sun4); // NaN "number"
```
+ 任何值和字符串进行加法运算,都会先转为字符串,然后和字符串进行拼接
```
var sun5 = 3 + "3";
console.log(sun5, typeof sun5); // 33 string
var sun6 = null + "3";
console.log(sun6, typeof sun6); // null3 string
var sun7 = undefined + "3";
console.log(sun7, typeof sun7); // undefined3 string
var sun8 = true + '3';
console.log(sun8, typeof sun8); // true3 string
```
### (二)减法运算符 -
+ 所有的非Number类型(包括字符串)的数据类型,都会将非Number数据类型先转为Number后再进行计算
```
var difference = 10 - 2;
console.log(difference, typeof difference); // 8 "number"
var difference1 = 10 - '2';
console.log(difference1, typeof difference1); // 8 "number"
var difference2 = 10 - true;
console.log(difference2, typeof difference2); // 9 "number"
var difference3 = 10 - null;
console.log(difference3, typeof difference3); // 10 "number"
```
+ 任何数据类型和NaN进行减法运算时,结果都是NaN
```
var difference4 = 10 - undefined;
console.log(difference4, typeof difference4); // NaN "number"
var difference5 = 10 - NaN;
console.log(difference5, typeof difference5); // NaN "number"
```
### (三) 乘法运算 *
规律和减法一样
### (四)除法运算符 /
规律和减法一样
### (五)取余运算符 %
> m % n 求余 相当于 m / n 取余数
+ m 等于0 返回 0
```
var remainder = 0 % '4';
console.log(remainder, typeof remainder); // 0 "number"
```
+ n 等于0 返回 NaN
```
// 当n为0时
var remainder1 = '4' % 0;
console.log(remainder1, typeof remainder1); // NaN "number"
```
+ m > n 正常取余
```
// 当m>n时
var remainder2 = 5 % 2;
console.log(remainder2, typeof remainder2); // 1 "number"
```
+ m < n 结果是m
```
// 当m<n时
var remainder3 = 2 % 5;
console.log(remainder3, typeof remainder3); // 2 "number"
```
+ 其他的规则和减法一样
### (六)一元运算符
只有一个操作数的运算符 + -
+ +号不会对数字产生任何的影响,类似与数字中的正号
+ 对非Number类型的值,会调用Number()函数先进行转换,然后再运算
```
// + 对数字不会产生影响,对与非Number类型的数值,会先调用Number()函数进行转换
var num1 = + "6";
console.log(num1, typeof num1); // 6 "number"
var num2 = + null;
console.log(num2, typeof num2); // 0 "number"
var num3 = + undefined;
console.log(num3, typeof num3); // NaN "number"
var num4 = + true;
console.log(num4, typeof num4); // 1 "number"
```
+ -号相当于数字中的负数,对数字进行取反操作
```
var num5 = - - '6'; // 负负得正
console.log(num5, typeof num5); // 6 "number"
var num6 = 1 - -"3" + 3;
console.log(num6, typeof num6); // 7 "number"
var num7 = - "123abc"; // 转换的时候调用的Number()函数,不是调用的parseInt()函数。
console.log(num7, typeof num7); // NaN "number"
```
### (七)赋值运算符
赋值运算符可以分为简单赋值运算符和复合赋值运算符
+ 简单赋值运算符
+ 格式:变量名 = 数据
+ =的左边只能是变量
+ 多个赋值运算符可以组成赋值表达式,具有右结合性
```
// 从右往左进行赋值
// 先将10赋值给c,然后变量c中储存的值再赋值给b,变量b中储存的值再赋值给a,最后a b c 储存的值都是10
a = b = c = 10;
```
+ 复合赋值运算符
```
+= 加后赋值 如: a += 1 相当于 a = a + 1
-= 减后赋值 如:a -= 1 相当于 a = a -1
*= 乘后赋值 如:a *= 1 相当于 a = a * 1
/= 除后赋值 如:a /= 1 相当于 a = a / 1
%= 取模后赋值 如:a %= 1 相当于 a = a % 1
```
+ 复合赋值表达示运算
+ 格式:a *= 1 + 2
+ 由于赋值运算符是右结合性,所以会先计算=右边的,然后再进行计算 相当于 a = a * (1 + 2)
```
var a = 10
a *= 100 + 30;
console.log(a) // 1300 a = 10 * (100+30)
```
### (八) 自增自减运算符
### 1、介绍
javascript提供了 ++ -- 这两种自增和自减的运算符来简化 i = i + 1, i = i - 1 这样的操作
### 2、自增、自减 求值的过程
+ 无论运算符在前面还是后面(前缀还是后缀)变量自身都会发生改变
+ 后缀表达式 i ++ , i -- 先用i的值做为整个表达式的值,再进行加1或减1的操作,即:先用后变
```
// a ++ a-- 先用后变
var a = 10, b;
b = a ++; // 先用i的值做为赋值运算右边的值,把a的值赋值给b,然后a再自身加1
console.log(a); // 11
console.log(b); // 10
var num = 10;
sun = num++ + 10;
console.log(sun); // 20 先完成num + 10 后,再自身加1
console.log(num); // 11
```
+ 前缀表达式 ++ i, -- i 先自身加1或减1,然后再将i的值做为表达式的值,即:先变后 用
```
// ++a --a 先变后用
var a1 = 10 , b;
b1 = ++ a1; // a1自身先加上1,然后把值赋值给b1
console.log(a1); // 11
console.log(b1); // 11
var num1 = 10;
sun1 = ++num1 + 10;
console.log(sun1); // 21 先自身加1 后,再进行 + 10 的运算
console.log(num1); // 11
```
### (九)关系运算符
关系运算符的返回值,只有两个,要么是真(true)要么是假(false)
+ javascript 支持的关系运算符
![关系](media/关系.png)
+ 对于非数值类型进行比较时,会转换为数值再进行比较
```
var a = 1;
console.log(a == true); // true
console.log( a === true); // false
console.log(a > '0'); // ture
```
+ 如果两侧都是字符串,不会转换为数字进行比较,而是分别比较字符串的Unicode编码的大小
+ 比较编码时是从左到右一位一位进行比较
+ 如果两位相同,则会比较下一位,可以用来做英文的排序
+ 比较中文没有意义
```
console.log('a' < 'b'); // true
console.log('abc' < 'abd'); // true
console.log('你' > "我"); // false
```
+ null、 undefined、 NaN比较
```
console.log(null == 0); // false
console.log(undefined == 0); // false
// 永远不要判断两个NaN是否相等
console.log(NaN == NaN); // false
/*
* 可以通过isNaN()函数来判断一个值是否是NaN
* 如果该值是NaN(不是一个数字)则返回true,否则返回false
*/
var num = NaN;
console.log(isNaN(num)); // true
console.log(isNaN(1)); // false
console.log(isNaN(null)); // false
console.log(isNaN(undefined)); // true
console.log(isNaN('abc')); // true
// undefined 衍生自 null, 所以返回true
console.log(null == undefined); // true;
console.log(null === undefined); // false;
// == 判断值是否相等
// == 会进行数据类型转换
console.log("123" == 123); // true
// === 判断值和类型时候同时相等
// === 不会进行数据类型转换
console.log("123" === 123); // false
```
+ 注意的地方
比较两个字符串类型的数字,结果可能有偏差,所以需要提前转型;
```
console.log("1111123" < "124" ); // true
console.log("1111123" < 124 ); // false
```
### (十)逻辑运算符
#### 1、&& (与运算)
+ 运行结果 条件A && 条件B
先去判断条件A成不成立,如果A成立了才去判断B是否成立,如果A不成立则不会去判断B
两个条件为真才为真,有一个为假就为假
+ 短路测试
条件 && 表达式 条件为真时,后面的表达式执行
```
console.log(7 > 3 && console.log('执行了')) // 执行了
```
+ 注意的地方
+ 对于非Boolean类型的数值,逻辑与会自动将其转换为Boolean类型来判断;
+ 如果条件A成立,不管条件B成立与不成立都会返回条件B;
+ 如果条件A不成立,则返回条件A本身;
```
// 如果条件A不成立,则返回a的本身
var result = null && 7 > 3;
console.log(result); // null
// 如果条件A成立,不管B成不成立,都将返回B
var result1 = 123 && 'abc';
console.log(result1) // abc
var result2 = 123 && null;
console.log(result2); // null
```
#### 2、 || (或运算)
+ 运行结果 条件A || 条件B
有一个为真就为真,两个为假才为假
如果A成立了,则不会去判断条件B,只有在A不成立的时候才会去判断条件B
+ 短路测试
条件 && 表达式 条件为假时,后面的表达式执行
```
console.log(7 < 3 && console.log('执行了')) // 执行了
```
+ 注意的地方
+ 对于非Boolean类型的值,逻辑或会将其自动转为布尔类型的值来判断;
+ 如果条件A不成立,则不管条件B成不成立都会返回条件B本身的数值;
+ 如果条件A成立,则返回条件A本身的数值;
#### 3、 !(非运算)
+ 运行结果 !条件A
对条件A进行取反 比如:条件A成立true,取反后为false
+ 注意的地方
+ 对一个值进行两次取反,它不会发生变化;
+ 对非布尔值进行操作,则先将其转换为Boolean,然后再进行取反,所以将一个数值转换为Boolean,除了Boolean()函数外,还可以使用 !!数值, 实现的原理和Boolean(数值)是一样的;
### (十一)逗号运算符
逗号表达式就是把多个表达式连接起来,组成一个表达式。
+ 结果
从左往右计算,一直到最后一个,整个表达式的值也是最后一个表达式的值。
```
var a, b;
b = (a=3, --a, a*5);
console.log(b) // 10
```
+ 使用注意事项
+ 程序中使用逗号表达式,通常是求表达式内各个表达式的值,并不一定要求,整个逗号表达式的值;
+ 并不是所有出现逗号的地方都组成逗号表达式,例如在变量说明中,函数参数表中的逗号只是分隔作用;
```
var a, b, c; //这里的逗号只是分隔符
function sendMessage(num, content) { //这里的逗号只是分隔符
console.log(num, content);
}
```
### (十二)三目运算符
+ 格式:
条件表达式 ?语句1:语句2;
+ 求值规则
条件表达式为真时,执行语句1;为假时执行语句2.
```
// 如果条件表达式为真,则执行语句1,为假则执行语句2
true?alert("语句1"):alert("语句2"); // 语句1
false?alert('语句1'):alert('语句2') // 语句2
```
一个案例:
```
<script>
// 接收用户输入的三个数,找出最大的那个
var num1, num2, num3, maxNum;
num1 = Number(prompt("请输入数字1"));
num2 = Number(prompt("请输入数字2"));
num3 = Number(prompt('请输入数字3'));
maxNum = num1 > num2 ? num1:num2;
maxNum = maxNum > num3 ? maxNum:num3;
console.log(maxNum);
</script>
```
+ 注意的地方
+ 条件运算符?:是一对运算符,不能分开单独使用
+ 如果条件表达式的求值结果不是一个Boolean,则会转换为Boolean值后再运算
### (十三)运算符的优先级
+ 运算符的优先级共分为15级,1最高,15最低;
+ 计算规则
+ 先计算优先级高的;
+ 如果优先级一样,则谁在左边先算谁
+ 可以使用()来改变优先级
+ 优先级图示
![优先级](media/优先级.png)
## 八、流程控制
### (一)if 语句
一个分支,要么执行,要么不执行
+ 语法
```
if (条件){
代码块;
}
```
+ 执行过程
如果条件是true,则执行代码块,如果是false,不会执行代码块
### (二)if else 语句
两个分支,只能执行一个分支,像这种情况也可以使用三元表达式
+ 语法
```
if(表达式){
代码块1;
}else{
代码块2;
}
```
+ 执行过程
如果表达式的结果是true,执行代码块1,如果结果是false,则执行代码块2
### (三)if else if else if .. else 语句
多个分支,最终也执行一个
+ 语法
```
if(表达式1){
代码块1;
}else if(表达式2){
代码块2;
.....
}else{
代码块3;
}
else if 可以写多个,具体看需要
else 可以不用写,具体看需求
```
+ 执行过程
先判断表达式1的结果,如果是true,就执行代码块1,后面的不会执行,如果结果是false,则会判断表达式2的结果,如果是true,就执行代码块2,后面的不执行,如果是false,则会继续判断下一个表达式,依此类推,最终,如果else if的表达式结果都是false,则执行最后的else代码块的语句。
### (四) switch case 语句
多个分支,最终也执行一个(最终执行几个由break决定)
+ 语法
```
switch(表达式){
case 值1:代码1;
break;
case 值2:代码2;
break;
case 值3:代码3;
break;
case 值4:代码4;
break;
.....
default:代码5;
break;
}
default 后的break可以省略
default 也可以省略
case 值和表达式的值进行比较的时候是严格模式,相当是===的比较;
break,如果不写,会从case匹配到的值开始,一直执行代码块,直到遇到break跳出
```
+ 执行过程
获取表达式的值,和值1进行比较,如果相同,则执行代码1,遇到break跳出,后面的代码不执行;如果和值1不相同,则和值2进行比较,依此类推,如果表达式的值和case的值都不一样,就执行default代码5的,跳出。
break的使用
```
<script>
// 每个月有多少天
var month = Number(prompt("月份"));
if (!isNaN(month)) {
switch (month) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
console.log("有31天")
break;
case 4:
case 6:
case 9:
case 11:
console.log("有30天")
break;
case 2:
console.log("有28天")
break;
}
}else{
console.log("输入有误")
}
</script>
```
## 九、循环
### (一)while 循环
+ 语法
```
while(循环条件){
循环体;
计数器++;
}
```
+ 执行过程
先判断循环条件是true还是false,如果条件是true,则执行循环体和计数器,再次判断循环条件,如果条件成立,就执行循环体,否则就退出循环。
### (二)do while 循环
+ 语法
```
do {
循环体;
} while(条件);
```
+ 执行过程
先执行一次循环体,然后判断条件是true还是false,如果是false,则跳出循环,如果是true,则继续执行循环体,至到条件不满足,跳出循环体,
+ while 和 do while的区别
+ while 是先判断,后循环,有可能一次循环都不执行;
+ do-while 是先循环,后判断,至少执行一次循环;
### (三)for 循环
+ 语法
```
for(表达式1;表达式2;表达式3){
循环体;
}
```
+ 执行过程
先执行一次表达式1,然后判断表达式2的条件成不成立。
+ 如果成立,则执行一次循环体,然后执行表达式3,再判断表达式2成不成立,依此类推,至到表达式2不成立。跳出循环;
+ 如果不成立,则跳出循环;
```
// 斐波那契数列
var num1=1, num2=1, sun=0;
for (var i=3; i<=12; i++) {
sun = num1 + num2;
num1 = num2;
num2 = sun;
}
console.log(sun);
```
### (四) break 语句
如果在循环中,遇到了break,则立即跳出当前所在的循环(注意是当前的循环,如果还有外层循环,是不会跳出的)
```
<script>
// break 跳出
for(var i=0; i<5; i++){
while (true) {
console.log("哈哈哈");
break; // 只跳出当前这层循环
}
}
</script>
```
```
// break 找100-200以内被7整除的第2个数
for (var i=100, j=0; i<=200; i++) {
if (i%7==0){
if(j==1){
console.log(i);
break;
}
j++;
}
}
```
### (五)continue 跳出本次循环
在循环中遇到continue关键字,直接跳出本次循环,进入一下次循环。
```
// 求100-200之间所有的奇数的和 continue
for (var i=100, sun=0; i<=200; i++) {
if (i%2==0) {
continue;
}
sun += i;
}
console.log(sun);
```
```
// 求整数100-200的累加值,要求跳过所有个位数为3的数
// 个数数为3,要取出来,就是10取余后,余数是3的
for (var sun=0, i=100; i<=200; i++) {
if (i%10==3) {
/* 说明个位数是3,不要 */
continue;
}
sun += i;
}
console.log(sun);
```
## 十、数组
+ 数组:变量只能存储一个值,所谓的数组就是将多个元素(通常是同一类型)按一定顺序排列放到一个集合中,那么这个集合我们就称为数组。
+ 数组的作用:可以一次性存储多个数据;
+ 数组的所有方法来源于:`Array.prototype`
```javascript
// 自己写数组的push方法
Array.prototype.push = function () {
for (var i=0; i<arguments.length; i++) {
this[this.length] = arguments[i];
}
return this.length;
};
var arr = [1, 2]
```
+ 数组的定义
1、通过构造函数创建数组;
```
语法:
var 数组名=new Array(长度);
var array=new Array();//没有数据,空数组
数组的名字如果直接输出,那么直接就可以把数组中的数据显示出来,如果没有数据,就看不到数据。
var array=new Array(5); // 如果数组中没有数据,但是有长度,数组中的每个值就是undefined
var arr = new Array(10.2) // 会报错,第一位传小数会报错
```
2、通过字面量的方式创建数组;
```
var 数组名=[]; //空数组
```
3、数组的读和写
arr[num] 不可以溢出读,结果是undefind,不会报错
arr[num] = xxx 可以溢出写,会把数组的长度撑长到num,前面的数据是undefind。
+ 数组元素:数组中存储的每个数据,都可以叫数组的元素,比如:存储了3个数据,数组中3个元素
+ 数组的长度:就是数组的元素的个数,比如有3个元素,就说这个数组的长度是3;
+ 数组索引(下标):用来存储或者访问数组中的数据的;数组名[下标]
+ 注意点:
+ 无论是构造函数的方式还是字面量的方式定义的数组,如果有长度,那么默认的是undefined;
+ 构造函数的方式创建数组的时候,如果在Array(一个数字)————>数组的长度(数组元素的个数);
+ 如果在Array(多个值),这个数组中就有数据了,数组的长度就是这些数据的个数;
+ 数组的索引和数组长度的关系:就是长度减1,就是最大的索引;
```
// for 循环遍历数组
var array = new Array(1, 2, 'mjc', undefined, true, null, new Object);
for (var i=0; i<array.length; i++) {
console.log(array[i]);
}
```
```
// 求数组里的最大值
var array = new Array(1, 6, 9000, 120, 900, 1000, 0);
for (var max=array[0], i=0; i<array.length; i++) {
if (max < array[i]) {
max = array[i];
}
}
console.log(max);
```
```
// 数组的倒序
var array = new Array(1, 2, 3, 4, 5, 6);
for (var i=array.length-1; i>=0; i--) {
console.log(array[i]);
}
```
```
// 把数组中的每个元素用|拼接到一起产生一个字符串输出
var array = new Array(1, 2, 3, 4, 5, 6);
for (var i=0, str=''; i<array.length-1; i++) { /* 这里的length-1,为了不输出最后一个 */
str += array[i] +'|';
}
console.log(str+array[array.length-1]); /* 输出的最后再把数组的最后一个加上 */
// 1|2|3|4|5|6
```
```
// 去掉数组中重复的0, 把其他的元素放在一个新的数组中
var array = new Array(1, 0, 4, 9, 10, 0, 23);
var newArray=[];
for (var i=0; i<array.length; i++) {
if (array[i] != 0) {
newArray[newArray.length] = array[i]; // 把新数组的长度作为下标
}
}
console.log(newArray); // [1, 4, 9, 10, 23]
```
```
// 翻转数组 位置调换
// 思路:
// 1、交换的次数是长度的一半,4个元素,交换2次,5个元素也交换2次
// 2、利用第三方变量临时存储交换的值
/* 3、循环交换时:第一次:索引位置是0的和索引位置是length-1-0的进行交换;第二次:索引位置是1的和索引位置是length-1-1的进行交换;
第三次:索引位置是2的和索引位置是length-1-2的进行交换。。。依此类推 */
var array = new Array(1, 2, 3, 4, 5, 6, 7);
for (var i=0; i<array.length/2; i++) {
var temp = array[i];
array[i] = array[array.length-1-i];
array[array.length-1-i] = temp;
}
console.log(array);
```
冒泡排序:
![冒泡排序](media/冒泡排序.png)
```
// 冒泡排序
/* 思路:
1、拿一个数字和其他的元素进行对比,每一个元素都要跟其他的元素对比一次;
2、比较的轮数是长度-1;
3、每一轮比较的次数:length-1-i;
4、用第三方交换变量; */
// 从小到大排序
var array = new Array(1, 3, 5, 9, 2, 6, 4);
for (var i=0; i<array.length-1; i++) {
for (var j=0;j<array.length-1-i; j++) {
var temp;
if (array[j] < array[j+1]) {
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
}
console.log(array); // [9, 6, 5, 4, 3, 2, 1]
```
### 伪数组
arguments对象是所有(非箭头)函数中都可用的局部变量。你可以使用arguments对象在函数中引用函数的参数。arguments对象不是一个 Array 。它类似于Array,但除了length属性和索引元素之外没有任何Array属性。例如,它没有 pop 方法。
arguments.length 可以获取函数在调用时,传入了几个参数,还可以使用arguments对象可以获取传入的每个参数的值(数组形式)函数名.length:可以获取形参的个数;
类数组的构成
```javascript
// 类数组的构成
var obj = {
0:'a',
1:'b',
2:'c',
length:3,
push:Array.prototype.push,
splice:Array.prototype.splice // 给一个对象加上splice,就长的像数组一样。
}
obj.push('d')
console.log(obj) // {0: "a", 1: "b", 2: "c", 3: "d", length: 4, push: ƒ} 会发现增加了一个3:'d',并且长度增加了
```
上面的就叫一个类数组,必须有几个组成部分
1、属性要为索引(数字)必须有length属性,最好加上push。
2、好处,可以像数组一样用,也可以像对象一样用,DOM生成的类数组的东西,全是类数组。
```javascript
var obj = {
0:'a',
1:'b',
2:'c',
name:'ww',
age:123,
length:3,
push:Array.prototype.push,
splice:Array.prototype.splice
}
console.log(obj); // Object(3) ["a", "b", "c", name: "ww", age: 123, push: ƒ, splice: ƒ]
console.log(obj.name); // ww
```
## 十一、函数
函数:把一坨重复的代码封装,在需要的时候直接调用即可
函数的作用:代码的重用。
```
语法:
1、函数的定义:
function 函数名字(){
函数体-->一坨重复的代码
}
2、函数的调用:函数名();
3、函数的参数:在函数定义的时候,函数名子后面的小括号里的变量就是参数,目的是函数在调用的时候,用户传进来的值操作;此时函数定义的时候后面的小括号里的变量叫参数;在函数调用的时候,按照提示的方式,给变量赋值-->就叫传值,把这个值就传到了变量(参数)中;
4、形参:函数在定义的时候,小括号里的变量叫形参;
5、实参:函数在调用的时候,小括号里传入的值叫实参,实参可以是变量也可以是值;
函数的返回值:在函数内部有return关键字,并且在关键字后面有内容,这个内容被返回了,当函数调用之后,需要这个返回值,那么就定义变量接收,即可;
6、命名函数:函数如果有名子,就是命名函数,是一种弱类型的声明方式,在预解析完,执行的时候,会被var声明的覆盖。
alert(a) 如果是在预解析的时候,函数名会被提升
var a = 1;
function a() {
alert(1)
}
alert(a) 会弹出1
==========================================================================
7、匿名函数:函数如果没有名子,就是匿名函数;
8、函数的另一种定义方式:函数表达式,把一个匿名函数或者把一个函数给一个变量;强类型的声明,先声明,后调用
var f1 = function () {
console.log("我是一个函数");
};
console.log(f1.name); // f1
var f1 = function abc () { // 注意这里的abc充当了一个表达式就不能充当一个函数体了
console.log("我是一个函数");
}
abc(); // 报错
console.log(f1.name) // abc
如果是函数表达式,那么此时前面的变量中存储的就是一个函数,而这个变量就相当于是一个函数,就可以直接加小括号调用了。
9、函数的自调用,没有名字,调用--声明的同时,加小括号直接用, 一次性的,一般用于初始化数据
(function(){console.log("自调用");})(); 声明函数不能自调用,但是在声明函数前加上-/+/!/(括号)就变成了函数表达式了,能够自调用了,但是函数名就不起作用了;
10、回调函数:函数作为参数使用,如果一个函数作为参数,那么我们说这个参数(函数)可以叫做回调函数,只要是看到了一个函数作为参数使用了,那么就是回调函数;
11、块级作用域:一对大括号就可以看作是一块,在这块区域中定义的变量,只能在这个区域中使用,但是在js中在这个块级作用域中定义的变量,外面也能使用。说明,js中没有块级作用域;
12、全局变量:声明的变量,是使用var声明的,那么这个变量就是全局变量,全局变量可以在页面的任何位置使用,如果页面不关闭,那么就不会释放,就会占空间,消耗内存;一切声明的全局变量,都是window的属性。如 var a = 123; console.log(window.a) // 123
13、局部变量:在函数内部定义的变量,是局部变量,外面不能使用,目前除了函数内部定义的变量是局部变量,其他地方定义的变量是全局变量。
14、全局作用域:全局变量的使用范围;
15、局部作用域:局部变量的使用范围;
16、在一个函数中使用一个变量,先在该函数中搜索这个变量,找到了则使用,找不到则继续向外找这个变量,一直找到全局使用域,找不到则是undefined;
17、隐式全局变量(暗式全局变量):声明的变量没有var,就叫隐式全局变量;函数内的隐式全局变量,在函数外也可以访问,但是隐式全局变量不会预解析进行变量的提升。
18、js的执行过过程:1、语法分析,先过一遍看有没有语法错误;2、预编译;3、解释执行代码;
预编译也叫变量提升,不管函数在那定义的,都会把函数整体(整个函数,包括声明,但是不是函数表达式 )提升到逻辑的最前面,变量声明提升,只提升声明,不会赋值。
注意点:
1、函数要先定义,才能使用;
2、函数的名字和变量一样,要遵循命名法;
3、函数声明重名,后面的会把前面的覆盖,不管在那个位置;函数表达式
4、形参的个数和实参的个数可以不一到(这一点和python不一样,python里必须要求一致,否则会报错);
5、函数没有返回值(没有return)或者是没有明确的返回值(return后面没有内容)但是在调用的时候接收了,那么结果就是undefined(变量声明了,没有赋值也是undefined);
6、return就是结束函数,后面的代码不会被执行;
7、函数名不加括号,打印出来是函数块的代码(python打印出来的是函数的内存地址);
8、函数之间可以相互调用;
9、函数表达式后面,赋值结束后,必须要加分号;
10、函数是有数据类型的,是function类型的;
11、函数可以作为参数(回调函数),也可以作为返回值;
12、全局变量是不能被delete删除的,隐式全局变量是可以被删除的,也就是说使用var是不会被删除的,没有var是可以删除的;
13、预解析中,变量声名的提升,只会在当前的作用域中提升,提升到当前的作用域的最上面,函数中的变量只会提前到局部作用域的最前面,不会成为全局的;
14、预解析会分段(多对的script标签中函数重名,预解析的时候不会冲突)
```
### arguments.callee
指向的是函数的引用,也就是函数本身的代码,在那个函数里就指代那个函数本身
```javascript
function test() {
console.log(arguments.callee); // ƒ test() {console.log(arguments.callee);}
}
test()
============================
var num = (function (n) {
if (n == 1) {
return 1
}
return n * arguments.callee(n-1) // return n * 函数(n -1)
}(5))
console.log(num); // 120
```
### `function.caller`
函数自己的属性,在那个环境里调用,就是谁
```javascript
function f1() {
f2()
}
function f2() {
console.log(f2.caller); // 在f1里被调用了,那么就是f1本身,ƒ f1() {f2()}
}
f1()
```
![作用域](media/作用域.jpg)
```
// 三个数中最大的值
function maxNum(x, y, z) {
return x>y?(x>z?x:z):(y>z?y:z);
}
console.log(maxNum(3, 5, 9));
```
```
// 判断一个数是不是素数
function primeNumber(x) {
for (var i=2; i<x; i++) {
if (x%i == 0) {
return false;
}
return true;
}
}
console.log(primeNumber(5)?"是质数":"不是质数");
```
```
// 求一组数字中最大的值
function maxNum(array) {
var max = array[0]; // 注意这个变量应该是定义在循环外边
for (var i=0; i<array.length; i++) {
if (max < array[i]) {
max = array[i];
}
}
return max;
}
console.log(maxNum([1, 3, 4, 56, 998, 233, 833]));
```
```
// 输入,年、月、日、获取这个日期是这一年的多少天
// 定义判断闰年的函数
function isLeapYear(year) {
// 闰年:能被4整除,但是不能被100整除,或者能被400整除
return year%4==0&&year%100!=0||year%400==0;
}
// 定义计算天数的函数
function getDays(year, month, day) {
var days = day;
if (month==1) { // 如果用户输入的是1月份,没必要再向后算天数,直接返回天数
return day;
}
// 定义一个数组,存储每个月份的天数
var months = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
// 小于的是月份-1,而不是数组长度-1
for (var i=0; i<month-1; i++) {
days += months[i];
}
// 需要判断这个年份是不是闰年
if (isLeapYear(year) && month>2) {
days ++;
}
return days;
}
console.log(getDays(2000, 3, 2))
```
```
/* arguments 的应用 计算N个数字的和
定义一个函数,如果不确定用户是否传入了参数,或者说不知道用户传了几个参数,没办法计算,但是如果在函数中知道了参数的个数,也知道了每个参数的值,可以 */
function f1() {
var sun = 0;
for (var i=0; i<arguments.length; i++) {
sun += arguments[i];
}
return sun;
}
console.log(f1(20, 30, 10, 50));
```
```
// 声名函数 重名 后面的会覆盖,不管在那个位置调用
function f1() {
console.log("小明好帅")
}
f1(); // 小猪好帅哦
function f1() {
console.log("小猪好帅哦")
}
f1(); // 小猪好帅哦
// 函数表达式 重名后,不会覆盖,调用时会根据位置的不同产生不同的结果。
var f2 = function () {
console.log("星期天真好")
}; // 函数表达式这个分号不能少
f2(); // 星期天真好
f2 = function () {
console.log("星期一真痛苦")
}
f2(); // 星期一真痛苦
// 函数的自调用,因为加小括号就是函数的调用,函数名不加括号输出就是函数的代码,自调用就是匿名函数代码直接加小括号,且匿名函数之间不影响;
(function () {
console.log("macbook真香");
})(); // macbook真香
(function () {
console.log("Thinkpad也还行"
);
})(); // macbook真香
```
预解析代码:
```
// 预编译(变量的提升)
/*
* 预编译发生在函数执行的前一刻,预编译的四部曲
* 1、创建Ao对象(相当于作用域或是执行期上下文)
* AO = {
*
* }
* 2、找形参和变量声明,将变量和形参名作为ao属性名,值为undefined, 只找有var的
* AO = {
* a:undefined,
* b:undefined
* }
* 3、将实参值和形参值统一
* AO = {
* a:1,
* b:undefined
* }
* 4、在函数体里面找函数声明,将函数名作为AO属性,值是函数体,注意是函数声明,不是函数表达式
* AO = {
* a:function a() {},
* b:undefined // b叫函数表达式,不叫函数声明
*
* }
* 预编译结束开始执行函数
* */
function fn (a) {
console.log(a); // function a() {}
var a = 123; // 预编译已经执行过var a; 这一步只执行 a = 123;
console.log(a); // 123
function a() {} // 预编译时已经执行过了不再执行
console.log(a); // 123
var b = function () {}; // 预编译的时候已经执行过var b; 只执行b=function () {}
console.log(b); // function () {}
}
fn(1);
/*
* 全局预编译
* 1、创建GO对象,
* 2、找到变量声明,只找var的,将变量名作为GO的属性,值为undefined;
* 3、找到函数声明,将函数名作为GO属性,值是函数体,注意是函数声明,不是函数表达式
* */
```
```
// 预解析
// 一般变量的书写顺序是先提前声明变量,输出在后,但是预解析会把变量的声名提前到最前面
// 正常书写
var num = 11;
console.log(num); // 11
// 先输出,后声明
console.log(num1); // undefined;
var num1 = 12;
// 正常书写函数
function f1() {
console.log("我是一个函数");
}
f1(); // 我是一个函数
// 先调用,后声明
f2(); // 预解析
function f2() {
console.log("预解析");
}
// 例3;
f3(); // undefined 调用函数的时候,会把函数的声明提升到作用域的上面
var num3 = 20 // 这个变量会被提升到变量使用之前,相当于在f3()之前,var num3; 但是没有赋值。
function f3() {
console.log(num3);
}
// 例4
var a = 25;
function abc() {
/* 相当于是
alert(a); // undefined ,预解析只会在局部变量里提升 var a=10 的声名,不会到全局的a
/* 相当于是
a= 20 */
var a = 10;
}
abc();
console.log(a) // 25 全局变量
// 例5
console.log(a); // 函数的代码 因为提升的是函数的声明
function a() {
console.log("aaaa");
}
var a = 1;
console.log(a); // 1
// 上面的代码相当于:
function a() {
console.log("aaaa");
}
console.log(a);
var a = 1;
console.log(a);
// 例6
function f4() {
console.log(num4); // undefined
var num4 = 20;
}
console.log(num4); // 会报错,因为,预解析只会提升到当前作用域的最上面。不会提升到全局作用域的
// 例7:注意坑
f5();
console.log(c); // 9
console.log(b); // 9
console.log(a); // 报错
function f5() {
var a=b=c=9;
console.log(a); // 9
console.log(b); // 9
console.log(c); // 9
}
// 原因:相当于是下面的代码
function f5() {
var a;
a = 9;
// b和c是没有var的隐式全局变量
b = 9;
c = 9;
console.log(a); // 9
console.log(b); // 9
console.log(c); // 9
}
f5();
console.log(c); // 9
console.log(b); // 9
console.log(a); // 报错
// 例8
f6(); // 报错
var f6 = function () {
console.log(a);
var a = 10;
};
// 原因:因为这个函数是函数表达式,是通过赋值的形式,在预解析的时候,提升声名,只会在f6();前面声明var f6;并没有函数的代码,所以报错;
```
### 递归
找规律(公式),找出口(已知的条件)
阶乘的公式:n*(n-1)
斐波那契数列:(n-1)+(n-2);
```
// 递归
function f(n) {
if (n == 1|| n==0){
return 1;
}
return n * f(n-1);
}
// 斐波那契数列
function f(n) {
if (n == 1||n == 2) {
return 1;
}
return f(n-1) + f(n-2);
}
```
## 十二、面向对象
+ 编程思想:把一些生活中做事的经验融入到程序中;
+ 面向过程:凡事都要亲力亲为,每件事的具体过程都要知道,注重的是过程;
+ 面向对象:根据需求找对象,所有的事都用对象来做,注重的是结果;
+ 面对对象的三大特性:封装、多态、继承;
+ js不是面对对象的语言,是一门基于对象的语言,但是可以模拟面向对象的思想;
+ 什么是对象:看的见,摸的到,具体特指的某个东西,比如:汽车就是不是一个对象,是一个类别,而挂甘A10011的就是对象。有属性和方法,具体特指的某一个事物;
### 创建对象的三种方式
1、调用系统的构造函数创建对象
```javascript
创建对象:
var obj = new Object();
添加属性:对象.名子 = 值;
obj.name = "小明";
obj.age = 18;
添加方法:对象.名子 = 函数;
obj.eat = function () {
console.log("我喜欢吃大米");
};
调用属性:对象.名子;
console.log(obj.name);
调用方法:对象.名子();
obj.eat();
删除属性: delete 对象.属性名
虽然全局变量var num = 123 是window的属性,但是一但经历了var操作,所得出的属性,这种属性叫做不可配置的属性,不可配置的属性,delete是删不掉的。但是隐式全局变量没有经历var操作,就可以删掉
```
2、工厂模式创建对象
```
// 工厂模式创建,解决了创建多个相似对象的问题,相比于自定义的构造函数,但是不能解决对象识别的问题
function createObject(name, age) {
var obj = new Object();
obj.name = name;
obj.age = age;
obj.fangFa = function () {
console.log("工厂模式的方法");
};
return obj;
}
var person = createObject("小明", 18);
person.fangFa();
```
3、自定义构造函数创建对象(结合第一种和需求通过工厂模式创建对象)
```javascript
// 2、自定义构造函数创建(工厂模式创建对象)
// 一次性可以创建多个对象,把创建对象的代码封装在一个函数中
// 对象可以分辨出是属于什么类型
// 函数和构造函数的区别:名字的首字母是不是大写;
// 自定义构造函数
function Person(name, age) {
this.name = name;
this.age = age;
this.eat = function () {
console.log(this.name + "今年" + this.age + "岁" + "想吃大米");
};
}
var pers1 = new Person("小明", 18); // 自定义构造函数创建对象 当这行代码执行的时候发生了4件事
//第一件事:在内存中开辟空间,存储创建的新对象;
//第二件事:把this设置为当前的对象,并且在这个对象中添加__proto__:Person.prototype(原型)的属性
//第三件事:设置对象的属性和方法的值
//第四件事:把this这个对象返回
pers1.eat();
console.log(pers1 instanceof Person);
自定义构造函数相比工厂模式:
1、没有显式的创建对象;
2、直接将属性和方法赋给了this对象;
3、没有return;
// instanceof
// 如何获取该变量(对象)是不是属于什么类型的?
// 语法:变量 instanceof 类型的名字 ----> 布尔类型
```
4、字面量的方式创建对象;
```
// 3、字面量创建对象
// 注意里面的属性方法与值之间用冒号,相互之间用逗号隔开,最后一个不加
// 缺陷:一次性的对象,不对传值
var pers2 = {
name:"小明",
age:18,
eat:function () {
console.log(this.name + this.age + "爱吃辣条");
}
};
pers2.eat();
```
### 访问对象属性方法的另一种写法
obj.prop与obj["prop"]
当执行obj.prop的时候,内部会隐式的转化成obj["prop"]的方式来执行,obj[]的这种形式,方括号里的必须是字符串
```javascript
// 访问对象属性的另一种方法["名子"] 注意双引号,是以字符串的形式
function Dog (name, age) {
this.name = name;
this.age = age;
this.play = function () {
console.log(this.name + "愉快的玩要");
};
}
var dog = new Dog("阿黄", 3);
dog["play"](); // 调用方法
console.log(dog["name"]); // 调用属性
console.log(dog["age"]);
```
属性名是字符串拼接的情况
```javascript
// 属性的字符串拼接
var den = {
wife1: {name:'xiaoliu'},
wife2: {name:'xiaozhang'},
wife3: {name:'xiaowang'},
sayWife: function (num) {
return this['wife'+num]
}
}
console.log(den.sayWife(1)); // {name: "xiaoliu"}
===================================================
var obj = {
name:'www',
age:12,
sex: 'wrwr'
}
for (var key in obj){
console.log(obj.key) // 打印3次undefined 为什么呢?因为执行obj.key相当于是==>obj["key"],这个字符串的key属性是对象没有的,所以是undefined
console.log(obj[key]) // 这样才是对的
}
```
### 知识点
+ 对象是一组无序属性的集合,属性的值可以是任意的数据类型
```
function Person(name, age, bool) {
this.name = name; // 字符串
this.age = age; // 数字
this.dog = {name:"阿花", age:18}; // 对象
this.sex = bool; // 布尔值
this.play = function () { // 函数
console.log("喜欢玩游戏")
};
}
var xiaoMing = new Person("小明", 18, true);
console.log(xiaoMing.sex?"男":"女"); // 男
console.log(xiaoMing.dog.name); // 阿花
============================================
```
+ json格式的数据:一般都是成对的,是键值对,json也是一个对象,数据都是成对的,一般json格式的数据无论是键还是值都是用双引号括起来的。
JSON.parse():string —> json
JSON.stringify():json —>string
```
var json = {
"name":"小明",
"age":18,
"sex":"男"
}
// 遍历对象,不单是json,所有的对象都适用
for (var key in json) {
// console.log(json.key) // 这种是错误的,key 是个变量,不是值,在属性里没有,会是undefined,点语法,如果没有属性值,会是undefind
console.log(key + "====" + json[key]);
}
```
+ 判断一个对象里有没有指定属性
```
// 判断一个对象里有没有指定的属性
var obj = {
sex:"男",
age:18
};
if (obj["sex"]) {
console.log("有");
}else {
console.log("没有");
}
// 有
```
```
// 判断一个对象里没有指定的属性
var obj = {
sex:"男",
age:18
};
function check(key, obj) {
return obj[key]?true:false;
}
console.log(check("sex", obj)); // true
```
+ 在对象中一个方法调用另一个方法
```
// 在对象中一个方法调用另一个方法
obj = {
sayHi:function () {
console.log("sayhi方法");
this.eat(); // 此时的this就是obj
},
eat:function () {
console.log("eat方法");
}
};
```
### 对象的枚举
#### `for in `
会延伸到原型链上的属性,但是不会打印Object上的东西。
hasOwnProperty(属性名) ,判断一个属性是自己的还是原型链上的,是自己的为true
```javascript
var obj = {
name:'www',
age:12,
sex: 'wrwr',
__proto__: {
lastName:'邓'
}
}
for (var key in obj){
// console.log(obj.key) // 打印3次undefined 为什么呢?因为执行obj.key相当于是==>obj["key"],这个字符串的key属性是对象没有的,所以是undefined
if (obj.hasOwnProperty(key)) { // 判断obj自身有没有这循环出来的这些属性,有了才打印,for in 会延展到原型链上,如果不加判断则会打印出邓
console.log(obj[key]) // 这样才是对的
}
}
```
#### `in`
判断整个原型链上有没有这个属性,有就是true,
`"lastName" in obj`则为true
#### `instanceof`
`A instanceof B` A对象是不是B构造函数构造出来的,实际上是看A的原型链上有没有B的原型
```javascript
person instanceof Person // true
person instanceof Object // true
[] instanceof Array // true
[] instanceof Object // true
```
#### 判断数据的类型
```javascript
// 第一种判断数组和对象的方法
if ([].constructor === Array){
console.log('是一个数组');
}
var obj = {};
if (obj.constructor === Object) {
console.log('是一个对象');
}
// 第二种区分数组和对象的方法
console.log([] instanceof Array ? '是数组':'是对象');
console.log(obj instanceof Array ? '是数组':'是对象');
// toString, 因为函数重写了toString方法
// Object.prototype.toString = function () {
// // 识别this
// // 返回相应的结果
// }
console.log(Object.prototype.toString.call([])); // [object Array]
console.log(Object.prototype.toString.call(123)); // [object Number]
console.log(Object.prototype.toString.call({})); // [object Object]
一般用的时候用toString,因为在页面中有iframe子页面的时候,比如子域里的数组到父域[] instanceof Array 会是false
```
### 对象的深浅拷贝
#### 浅拷贝
总结起来,浅拷贝就是只能拷贝原始值,只是拷贝了引用值的指向,所以引用值一个改变也会影响另一个的改变。
```javascript
obj = {
name:'abc',
age:123,
sex:"female",
card:['visa', 'unionpay']
};
function clone(origin, target) {
var target = target || {};
for (var key in origin) {
target[key] = origin[key];
}
return target
}
var obj1 = {};
clone(obj, obj1);
console.log(obj1);
obj1.card.push('mm') // 修改引用值
console.log(obj); // 另一个对象中的引用值也会跟着改变
obj1.name = 'bb'; // 修改原始值
console.log(obj) // 另一个对象中的原始值不会发生改变。
```
#### 深拷贝
```javascript
var obj = {
name: "abc",
age: 123,
card: ["visa", "master"],
wife: {
name: "bcd",
son: {
name: "aaa"
}
}
};
// 1.判断是不是原始值
// 2.判断引用值是数组还是对象
// 3. 建立相应的数组或对象
// 4.递归
function deepClone (origin, target) {
var target = target || {},
toStr = Object.prototype.toString,
arrStr = '[object Array]';
for (var key in origin) {
if (origin.hasOwnProperty(key)) { // 只拷贝自身的,而不是原型链上的
if (origin[key] !== "null" && typeof(origin[key]) == "object") {
// 开始判断是数组还是对象
target[key] = toStr.call(origin[key]) == arrStr ? []:{};
deepClone(origin[key], target[key])
}else { // 是原始值就直接拷贝
target[key] = origin[key];
}
}
}
return target;
}
var obj1 = {};
deepClone(obj, obj1)
```
## 十三、内置对象
对象分为三种:内置对象、自定义对象、浏览器对象
内置对象:系统提供的
自定义对象:自己写的对象
浏览器对象:浏览器的
实例对象,通过构造函数创建出来的,实例化的对象;
静态对象,不需要创建,直接就是一个对象,方法(静态方法),直接通过这个对象名子调用。
实例方法必须通过实例化后的对象调用的;对象.方法();
静态方法必须通过大写的构造函数这样的调用的方式调用的;构造函数.方法()
### (一)Math
Math 是一个内置对象,具有数学常数和函数的属性和方法,不是一个构造函数,所有的属性和方法都是静态的。
#### Math 对象属性
| 属性 | 描述 |
| :----------------------------------------------------------- | :------------------------------------------------ |
| [E](http://www.w3school.com.cn/jsref/jsref_e.asp) | 返回算术常量 e,即自然对数的底数(约等于2.718)。 |
| [LN2](http://www.w3school.com.cn/jsref/jsref_ln2.asp) | 返回 2 的自然对数(约等于0.693)。 |
| [LN10](http://www.w3school.com.cn/jsref/jsref_ln10.asp) | 返回 10 的自然对数(约等于2.302)。 |
| [LOG2E](http://www.w3school.com.cn/jsref/jsref_log2e.asp) | 返回以 2 为底的 e 的对数(约等于 1.414)。 |
| [LOG10E](http://www.w3school.com.cn/jsref/jsref_log10e.asp) | 返回以 10 为底的 e 的对数(约等于0.434)。 |
| [PI](http://www.w3school.com.cn/jsref/jsref_pi.asp) | 返回圆周率(约等于3.14159)。 |
| [SQRT1_2](http://www.w3school.com.cn/jsref/jsref_sqrt1_2.asp) | 返回返回 2 的平方根的倒数(约等于 0.707)。 |
| [SQRT2](http://www.w3school.com.cn/jsref/jsref_sqrt2.asp) | 返回 2 的平方根(约等于 1.414)。 |
#### Math 对象方法
| 方法 | 描述 |
| :----------------------------------------------------------- | :----------------------------------------------------------- |
| [abs(x)](http://www.w3school.com.cn/jsref/jsref_abs.asp) | 返回数的绝对值。 |
| [acos(x)](http://www.w3school.com.cn/jsref/jsref_acos.asp) | 返回数的反余弦值。 |
| [asin(x)](http://www.w3school.com.cn/jsref/jsref_asin.asp) | 返回数的反正弦值。 |
| [atan(x)](http://www.w3school.com.cn/jsref/jsref_atan.asp) | 以介于 -PI/2 与 PI/2 弧度之间的数值来返回 x 的反正切值。 |
| [atan2(y,x)](http://www.w3school.com.cn/jsref/jsref_atan2.asp) | 返回从 x 轴到点 (x,y) 的角度(介于 -PI/2 与 PI/2 弧度之间)。 |
| [ceil(x)](http://www.w3school.com.cn/jsref/jsref_ceil.asp) | 对数进行上舍入。 |
| [cos(x)](http://www.w3school.com.cn/jsref/jsref_cos.asp) | 返回数的余弦。 |
| [exp(x)](http://www.w3school.com.cn/jsref/jsref_exp.asp) | 返回 e 的指数。 |
| [floor(x)](http://www.w3school.com.cn/jsref/jsref_floor.asp) | 对数进行下舍入。 |
| [log(x)](http://www.w3school.com.cn/jsref/jsref_log.asp) | 返回数的自然对数(底为e)。 |
| [max(x,y)](http://www.w3school.com.cn/jsref/jsref_max.asp) | 返回 x 和 y 中的最高值。 |
| [min(x,y)](http://www.w3school.com.cn/jsref/jsref_min.asp) | 返回 x 和 y 中的最低值。 |
| [pow(x,y)](http://www.w3school.com.cn/jsref/jsref_pow.asp) | 返回 x 的 y 次幂。 |
| [random()](http://www.w3school.com.cn/jsref/jsref_random.asp) | 返回 0 ~ 1 之间的随机数。 |
| [round(x)](http://www.w3school.com.cn/jsref/jsref_round.asp) | 把数四舍五入为最接近的整数。 |
| [sin(x)](http://www.w3school.com.cn/jsref/jsref_sin.asp) | 返回数的正弦。 |
| [sqrt(x)](http://www.w3school.com.cn/jsref/jsref_sqrt.asp) | 返回数的平方根。 |
| [tan(x)](http://www.w3school.com.cn/jsref/jsref_tan.asp) | 返回角的正切。 |
| [toSource()](http://www.w3school.com.cn/jsref/jsref_tosource_math.asp) | 返回该对象的源代码。 |
| [valueOf()](http://www.w3school.com.cn/jsref/jsref_valueof_math.asp) | 返回 Math 对象的原始值。 |
### (二)Date
```
// 创建一个Date的实例化对象(不传参)
var date = new Date();
console.log(date);
// 获取时间戳
console.log(date.getTime())
// 传入参数
var date1 = new Date("2000-5-4")
console.log(date1);
// 获取年份
console.log(date.getFullYear());
// 获取月份
console.log(date.getMonth() + 1);//是从0开始的,真实的需要加1
// 获取日期
console.log(date.getDate());
// 获取时
console.log(date.getHours());
// 获取分
console.log(date.getMinutes());
// 获取秒
console.log(date.getSeconds());
// 获取星期
console.log(date.getDay()); // 周日是0,依此类推
// 英文的日期的字符串
console.log(date.toDateString());
// 数字的日期的字符串
console.log(date.toLocaleDateString());
// 英文的时间的字符串
console.log(date.toTimeString());
// 中文的时间的字符串
console.log(date.toLocaleTimeString());
```
```
// 格式化日期输出“2018年11月15日 11:23:23”
/** 检查月、日、时、分、秒是不是大于0
* @param num 日期的数字
* @returns {string} 返回的是清洗后的数字
*/
function checkDate(num) {
return num>10?num:"0"+num;
}
/** 返回指定格式的日期字符串
* @param date 日期对象
* @returns {string} 返回的是字符串
*/
function formatDate(date) {
// var date = new Date(); // 可以直接向函数传一个对象的参数
//年
var y = date.getFullYear();
//月
var m = checkDate(date.getMonth() + 1); // 月份是从0开始的,所以要加1
//日
var d = checkDate(date.getDate());
//时
var h = checkDate(date.getHours());
//分
var M = checkDate(date.getMinutes());
//秒
var s = checkDate(date.getSeconds());
var str = y+"年"+ m +"月"+d+"日" + h +":"+M+":"+ s;
return str;
}
console.log(formatDate(new Date())); // 可以直接向函数传一个对象的参数
```
```
// 毫秒值转成日期对象
var date = new Date();
console.log(date.valueOf()) // 得到毫秒数 1546436676284
var dt = new Date(date.valueOf());
console.log(dt);
```
### (三)String
String是一个对象,字符串可以看成是字符组成的数组,但是JS中没有字符类型,字符是一个一个的,在别的语言中是用单引号括起来的,在JS中字符串可以使用单引号或者双引号,因为字符串可以看成是数组,所以能用for循环进行遍历。
字符串的特性:不可变性。单独写是基本的数据类型(栈)通过new构造函数的方式创建的是引用类型(堆 栈)javascript会将基本的数据类型的字符串转化为字符串对象,来使用字符串对象的方法。
字符串的重新赋值,看起来是字符串发生改变了,实际上是指向改变了,并不是值改变了,原来的值还在内存中。
```
// 创建一个字符串的对象,两种方法:一是字面量,第二种系统构造函数创建对象
var str = "小明好帅abc";
var str1 = new String('今天是个好天气');
// 字符串对象的属性
var len = str.length;
console.log(len); // 7
// **** 1、字符方法
// charAt() 返回指定位置的字符
console.log(str.charAt(2)); // 好
// charCodeAT() 返回指定位置的unicode编码
console.log(str.charCodeAt(4));
// **** 2、字符串的操作方法
// concat 和字符串的拼接+ 一样,比+更常用
console.log(str.concat(str1, "哈哈")); // 小明好帅abc今天是个好天气哈哈
// slice 从strat位置截取字符串的片断,直到end end 截取不到,一个参数一直截取到最后,不支持反截取
console.log(str.slice(2)); // 好帅abc
console.log(str.slice(2, 4)); // 好帅
console.log(str.slice(2, -2)); // 好帅a
console.log(str.slice(4, 2)); // 空 截取不到
// substring() 功能和slice一样,不一样的是不支持负数索引值,但是支持反截取
console.log(str.substring(2, -2)); // 小明 不支持负数索引,会从0位置截取
console.log(str.substring(2, 4)); // 好帅
console.log(str.substring(4, 2)); // 好帅
// substr() 从开始的位置截取长度为几个的字符串 第一个参数是开始的位置,第二个参数为截取的长度,只写一个参数,从开始一直到结束
console.log(str.substr(3, 1)); // 帅
// **** 3、位置方法
// indexOf() 返回指定内容在字符串中的位置,找不到则返回-1,第二个参数是从第几个索引开始找
console.log(str.indexOf("帅")); // 3
console.log(str.indexOf("帅", 5)); // -1
// lasttIndexOf() 从右往左找返回指定内容在字符串中的位置,找不到返回-1,第二个参数无意义
console.log(str.lastIndexOf("帅")); // 3
console.log(str.lastIndexOf("帅", 5)); // 3
// **** 4、去除空白
// trim() 只能是去除字符串两端的空格,不能去除中间的
console.log(" abess ".trim());
// **** 5、大小写转换方法
// 小写转大写
console.log(str.toUpperCase()); // 小明好帅ABC
console.log(str.toLocaleUpperCase()); // 小明好帅ABC
// 大写转小写
console.log("SFSFS".toLocaleLowerCase()); // sfsfs
console.log("SAADAD".toLowerCase()); // saadad
// **** 6、与正则相关的
// split() 把一个字符串分隔成字符串数组 第一个参数,正则表达式或者是分隔符,第二个参数分隔后数组的长度
console.log("wsf|sfsw|sfwrr|wwrw|www".split('|')); // ["wsf", "sfsw", "sfwrr", "wwrw", "www"]
// replace 替换 第一个参数是被替换的内容或是regexp 正则对象, 第二个参数是新的内容
console.log(str.replace("帅", "丑")); // 小明好丑abc
console.log("123,321,456".replace(',', '')); // 123321,456 只会替换第一个不是全局的
console.log("123,321,456".replace(/,/g, '')); // 123321456
// search() 查找指定内容或正则表达式的索引,找不到返回-1,
console.log(str.search("帅")); // 3
```
案例:
```
// 截取指定的字符串
var str = "我喜欢村里的小芳";
var key = "小芳";
var index = str.indexOf(key);
str = str.slice(index, index+key.length);
console.log(str);
```
```
// 找到字符串中所有o的位置
var str = "wrwrowrowrsfsfowrsfsfwosfsfosfwosfwwowrwo";
var key = "o";
var index = 0;
while ((index = str.indexOf(key, index))!= -1) { /* 先算等号右边的,如果index不等于-1,
就循环查找,本次查找到的索引值作为下一次查找的开始位置 */
console.log(index);
index += key.length; // 不加上查找字符串的长度,会成为死循环
};
// 将上述代码封装成函数
function finedStr(str, key) {
var index = 0;
while ((index = str.indexOf(key, index)) != -1) {
console.log(index);
index += key.length;
}
}
var str1 = "wsfpsfwpsfwpsfwpsfsp"
console.log(finedStr(str1, "p"));
```
```
// 找出字符串的每个字母出现了多少次
var str = "ooouwrosiQWiadWooiuQWWueyrREwq";
// 第一步先将字符串全部变为大写或小写
str = str.toLowerCase();
// 创建一个空对象,目的为了存储每个字母的次数
var obj = {};
// 循环遍历字符串,获取每个字母
for (var i=0; i<str.length; i++) {
// 得到每个字母,把字母作为对象的键
var key = str[i];
// 判断对象里没有这个键
if (obj[key]){
obj[key] ++; // 有的话就加1
}else {
obj[key] = 1; // 没有的话就把这个字母作为键值为1,存到对象中
}
}
// 循环对象,得到每个字母的次数
for (var key in obj) {
console.log(key+"出现了"+ obj[key] +"次");
}
// 将上面的代码封装成函数
function finedCount(str) {
var obj = {};
// 先转换为小写
str = str.toLowerCase();
// 循环遍历字符串取到每个字母
for (var i=0; i<str.length; i++) {
key = str[i]; // 得到每个字母
obj[key]?obj[key]++:obj[key]=1;
}
return obj;
}
function ouptCount(obj) {
for (var key in obj) {
console.log(key+"出现了"+obj[key]+"次");
}
}
var str="sfwwrpqpruqeiwpruw";
var obj = finedCount(str);
ouptCount(obj);
```
### (四)Array
+ 判断变量是不是数组类型两种方式
1、instanceof
2、使用数组的静态方法Array.isArray()方法
```
/* 判断变量是不是一个数组
1、instanceof */
var obj = [];
console.log(obj instanceof Array); // true
// 2、使用数组的静态方法Array.isArray()
console.log(Array.isArray(obj)); // true
```
+ 常用的一些方法
+ 改变原数组的:push, pop, shift, unshift, sort, reverse, splice
+ 不改变原数组:concat, (join —可逆> split),toString, slice
```javascript
// 数组的方法
var arr1 = [1, 2, 3, 45, 6];
// 1、Array.from()将一个可迭代对象,转换为数组, 不改变原有的,返回一个新的数组
console.log(Array.from(arr1)); // [1, 2, 3, 45, 6]
console.log(Array.from("abcedf")); // ["a", "b", "c", "e", "d", "f"]
// 2、concat() 合并多个数组,不修改原来的,返回一个新的数组,不会去重
console.log(arr1.concat([7, 8, 9])); // [1, 2, 3, 45, 6, 7, 8, 9]
console.log(arr1.concat([1, 2, 3, 7])); // [1, 2, 3, 45, 6, 1, 2, 3, 7]
// 2、every(callback 回调函数) 测试数组中的所有的元素是否通过了指定函数的测试
// 函数作为参数使用,整体返回boolean值是and的关系,函数中有三个参数,第一个是元素的值,第二个
// 参数是元素的索引值,第三个参数是调用方法的数组(一般没用) 不用可以不写
var flage = arr1.every(function (ele, index) {
return ele > 2;
});
console.log(flage); // false
// 3、filter(函数) 参数是一个回调函数,通过回调函数,过滤数组中的每个元素,把符合条件的元素,组成一个新的数组,返回
// 函数有三个参数,第一个是元素的值,第二个是元素的索引,第三个是调用方法的数组,不用不以不写
var newArry = arr1.filter(function (ele) {
return ele > 5;
});
console.log(newArry); // [45, 6]
// 4、forEach() 遍历数组,相当于for 循环,数组的每一个元素都执行一次回调函数
// 函数有三个参数,第一个是元素的值,第二个是元素的索引,第三个是调用方法的数组,不用不以不写
var arr1 = [1, 2, 3, 45, 6];
arr1.forEach(function (ele, index) {
console.log(ele + "====" + index);
});
/*
1 ====0
2 ====1
3 ====2
45 ====3
6 ====4
*/
// 5、map(函数) 数据中的每个元素都要执行一次回调函数,把新的值组成一个新的数组,并返回
// 函数有三个参数,第一个是元素的值,第二个是元素的索引,第三个是调用方法的数组,不用不以不写
var arr1 = [1, 2, 3, 45, 6];
var newArr = arr1.map(function (ele, index) {
return ele += 3;
});
console.log(newArr); // [4, 5, 6, 48, 9]
// 6、push() 追加元素 原地修改 返回新数组长度,不去重
var arr2 = [1, 2, 3];
console.log(arr2.push(3)); // 4
console.log(arr2); // [1, 2, 3, 3]
// 7、unshift() 向数组的第一个元素前面插入一个新的元素 原地修改,返回新数组长度,不去重
var arr2 = [1, 2, 3];
console.log(arr2.unshift(3)); // 4
console.log(arr2); // [3, 1, 2, 3]
// 8、pop() 删除最后一个元素或者指定的元素, 原地修改,返回被删除的元素,
var arr2 = [1, 2, 3, 'aa'];
console.log(arr2.pop("aa")); // aa
console.log(arr2); // [1, 2, 3]
console.log(arr2.pop()); // 3
console.log(arr2); // [1, 2]
// 9、shift() 删除数组中的第一个元素,不能删除指定的元素,原地修改,返回被删除的元素
var arr2 = [1, 2, 3, 'aa'];
console.log(arr2.shift("aa")); // 1 删除指定的元素,失败
console.log(arr2); // [2, 3, "aa"]
console.log(arr2.shift()); // 2
console.log(arr2); // [3, "aa"]
// 10、reverse 翻转数组,原地修改,返回排序后的数据
var arr2 = [1, 2, 3, 'aa'];
console.log(arr2.reverse()); // ["aa", 3, 2, 1]
console.log(arr2); // ["aa", 3, 2, 1]
// 11、sort(排序函数) 数组排序 原地修改,返回排序后的数据
// 不写函数不稳定,回调函数的第一个参数相当于arr[j], 第二个参数相当于arr[j+1]
// 规则: 1、必须写两个参数 2、看返回值:(1)当返回值为负数时,那么前面的数放在前面;(2)为正数,那么后面的数
// 在前;(3)为0,不动。
var arr2 = [3, 4, 2, 8, 9, 10, -1];
console.log(arr2.sort(function (a, b) {
if (a > b) {
return 1;
} else if (a == b) {
return 0;
} else {
return -1;
}
// 简化版的
// return a-b
})); // 不写回调函数结果是[-1, 10, 2, 3, 4, 8, 9] ,写了回调函数 [-1, 2, 3, 4, 8, 9, 10]
console.log(arr2); // 不写回调函数结果是[-1, 10, 2, 3, 4, 8, 9] 写了回调函数 [-1, 2, 3, 4, 8, 9, 10]
// 12、indexOf() 返回指定元素的索引,第二参数,查找的起始位置,找不到返回-1
var arr = [1, 2, 2, 2, 3, 4];
console.log(arr.indexOf(2)); // 1
console.log(arr.indexOf(2, 3)); // 3
console.log(arr.indexOf(500)); // -1
// 13、lastIndexOf 从右往左,返回指定元素的索引,找不到返回-1
var arr = [1, 2, 2, 2, 3, 4];
console.log(arr.lastIndexOf(2)); // 3
console.log(arr.lastIndexOf(2, 2)); // 2
console.log(arr.lastIndexOf(500)); // -1
// 14、join 将数组按指定的元素组成一个新的字符串,返回
var arr = [1, 2, 3, 4, 5];
console.log(arr.join(":")); // 1:2:3:4:5
// 15、selice() 浅拷贝从参数(开始索引到结束索引之间的元素,不包括结束索引),组成一个新的数据 返回,原数组不变,不传参数就是拷贝整个数组
var arr = [1, 2, 3, 4, 5];
console.log(arr.slice(1, 3)); // [2, 3]
console.log(arr.slice(3)); // [4, 5]
console.log(arr); // [1, 2, 3, 4, 5]
// 16、splice(开始的位置,要删除的个数, 替换的元素值) 一般用于删除数组中的元素,或者是替换元素,或者是插入元素
// 替换返回的是[]空列表,删除返回的是删除的元素组成的列表
var arr = [1, 2, 3, 4, 5];
console.log(arr.splice(3, 0, 'deue')); // [] 在任意位置插入元素
console.log(arr); // [1, 2, 3, "deue", 4, 5]
var arr = [1, 2, 3, 4, 5];
console.log(arr.splice(2, 2)); // [3, 4]
console.log(arr); // [1, 2, 5]
```
技巧:
```
// 清空一个数组
var arr = [1, 2, 3, 4, 5];
arr.length = 0;
console.log(arr); // []
```
### (五)包装类
基本包装类型
+ 普通变量不能直接调用属性或者方法;
+ 对象可以直接调用属性和方法;
**基本包装类型:本身是基本类型,但是在执行代码的过程中,如果这种类型的变量调用了属性或者是方法,那么这种类型就不再是基本类型了,而是基本包装类型,这个变量也不再是普通的变量了,而是基本包装类型对象 **
string number boolean 是基本类型,也是基本包装类型
```javascript
var str = "hello";
str = str.replace("ll", "HH"); // 基本数据类型调用了方法
console.log(str); // heHHo 这个时候,str就再是基本数据类型了,而是基本包装类型对象
var num = 10; // 原来是基本数据类型
console.log(num.toString()); // 10 调用了方法,那么现在就是基本包装类型了
规律:
如果是一个对象&&true,那么结果是true;
如果 是一个true&&对象,那么结果是对象;
var flag = new Boolean(false);
var result = flag && true;
console.log(result); // true
var flag = new Boolean(false);
var result = true && flag;
console.log(result); // Boolean
var num = 10;
var num2 = Number("10") // 类型转换,还是基本数据类型,没有new
var num3 = new Number("10"); // 基本包装类型
var str = "hello";
str.li = "ee";
console.log(str.li); // undefined; 因为str在调用属性的时候,相当于是调用了new String("hello") 包装成了引用类型,然后添加了属性,当执行log的时候,又相当是再次调用了new String("hello")的方法,而这个新的对象里是没有li的属性的。
```
# Web API
## 一、API相关的概念
** 什么是API **
API是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。
+ 任何开发语言都有自己的api
+ API的特征输入和输出
+ API的使用方法(console.log())
** 什么是WEB API **
浏览器提供一套操作浏览器功能和页面的API(BOM和DOM)
## 二、DOM
DOM 就是把页面看成是一个文档,学习DOM就是学习操作文档里的元素。DOM又称为文档树模型
DOM树:把html页面或者是xml文件看成是一个文档,文档就是一个对象,这个文档中所有的标签都是元素,元素也可以看成是对象,标签(元素、对象)有很多,还有嵌套关系组成的这种层次结构,可以模拟成树形结构图,简称:树状图,就是dom树
DOM对象:通过DOM方式获取的元素得到的对象
文档 一个网页可以称为文档
节点(node):页面中所有的内容都是节点(包括标签、属性、文本(文字、换行、空格、回车)、注释等)
元素(elementt):页面中所有的标签都是元素,元素可以看成是对象。
根元素:html标签
页面中的顶级对象:document
属性 标签的属性
HTML文件是用来展示信息,展示数据的
XML:侧重于存储数据
HTML 文件看成是一个文档(document),把这个文档也可以看成是一个对象,文档中所有的标签都可以看成是一个对象
### DOM结构树(继承关系)
![dom结构树](/Users/mengjuncheng/Documents/notes/前端/javascript笔记/media/dom结构树.png)
DOM结构树上相关联的有这个继承的关系,例如在`HTMLElement.abc=123`,那么在它的分支下的任何一个拥有继承关系的分支上,都会有这个属性。比如在页面中选中一个元素,那么这个元素身上也有abc=123这个属性。
`document.__proto__` ==> `HTMLDocument` `HTMLDocument.__proto__` ==>`Document` `Document.__proto__` ==> `Node` `Node.__proto__`==> `EventTarget` `EventTarget.__proto__` ==>`Object`
## 三、DOM 常见的操作
+ 获取元素
+ 动态创建元素
+ 对元素进行操作(设置其属性或调用其方法)
+ 事件(什么时机做相应的操作)
1、`getElementByid`方法定义在`Document.prototype`上,即`Element`节点上不能使用。
2、`getElementsByName`方法定义在`HTMLDocument.prototype`上,即非`html`中的`document`不能使用(`xml documentElement`)
3、`getElementsByTagName`方法定义在`Document.prototype`和`Element.prototype`上,换句话说就是获取到的元素可以继续调用`getElementsByTagName`这个方法。
4、`HTMLDocument.prototype`定义了一些常用的属性,`body.head`分别指代`HTML`文档中的`<body><head>`标签
5、`Document.prototype`上定义了`documentElement`属性,指代文档的根元素,在`HTML`文档中,他总是指代 `<html>`元素。
6、`getELementsByClassName`、`querySelectorAll`、`querySelector`在`Docuement.prototype`,`Element.prototype`类中均有定义。
### 获取页面元素的方法
除了id,其他的选择器取出来的都是一组的,基本上全部是类数组
1、根据id属性获取元素 返回的是一个元素对象 getElementById("id")
2、根据标签名获取元素 返回的是元素对象组成的伪数组 getElementsByTagName("标签的名子")
下面的几个,有的浏览器不支持
3、根据表单的name属性获取元素,返回的是元素对象组成的一个伪数组
getElementsByName("name属性")
```
<body>
<input type="button" value="更改value值" id="btn">
<input type="text" name="name1" value="我很好"> <br/>
<input type="text" name="name2" value="我很好"> <br/>
<input type="text" name="name1" value="我很好"> <br/>
<input type="text" name="name3" value="我很好"> <br/>
<input type="text" name="name3" value="我很好"> <br/>
<script >
// 根据表单的name属性获取元素 把name是1的获取出来
document.getElementById("btn").onclick = function () {
// document.getElementsByName("name属性值")是根据表单的name属性获取元素,返回的也是一个伪数组
var nameList = document.getElementsByName("name1");
for (var i=0; nameList.length; i++) {
nameList[i].value = "你好呀";
}
};
</script>
</body>
```
4、根据类样式的名子来获取元素,返回的是元素对象组成的伪数组 getElementsByClassName("类名子")
```
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
div {
100px;
height: 100px;
background-color: #f40;
}
span {
display: block;
100px;
height: 100px;
background-color: yellow;
}
</style>
</head>
<body>
<div class="cls">这是第一个div</div>
<div class="cls">这是第二个div</div>
<span class="cls">span</span>
<input type="button" value="变颜色" id="btn">
<script src="./common.js"></script>
<script >
// 根据类样式获取元素 注意ie8及以下都不支持
my$("btn").onclick = function () {
// document.getElementsByClassName("类样式名子") 根据类样式获取元素,返回的也是一个伪数组
var classList = document.getElementsByClassName("cls");
for (var i=0; i<classList.length; i++) {
classList[i].style.backgroundColor="pink";
}
};
</script>
</body>
```
5、根据css选择器获取元素,返回来的是一个元素对象 querySelector("选择器名字"),并不常用,选择出来的东西不是时事的
6、根据css选择器获取元素,返回的是元素对象组成的伪数组 querySelectorAll("选择器的名字")
## 四、事件
事件的三要素
+ 事件源:被触发事件的元素
+ 事件类型:事件的触发方法(如鼠标经过、鼠标点击)
* 鼠标事件
click(点击)、mousedown(鼠标按下),mouseup(鼠标抬起),一个鼠标的click点击事件分开来看就是是先down,再up,形成一个click事件。
mousemove:鼠标移动事件
contextmenu:鼠标右键产生菜单事件。
mouseover进入,mouseout离开,mouseenter、mouseleave
- mouseover和mouseout在父元素和其子元素都可以触发,当鼠标穿过一个元素时,触发次数得依子元素数量而言。
- mouseenter和mouseleave只在父元素触发,当鼠标穿过一个元素时,只会触发一次。
- mouseover和mouseout比mouseenter和mouseleave先触发
因此一般mouseover和mouseout一起使用,mouseenter和mouseleave一起使用,
必须是事件mousedown和mouseup事件来判断,用事件参数对象button的属性值来区分鼠标的左右键:0/1/2,左/中键/右键,click事件只能监听左键。
* 键盘事件
* 和鼠标的click事件不一样,keydown+ keyup != keypress,
* keydown>keypress>keyup
* keydown和keypress的区别
* keydown的事件参数参数对象的charCode属性值为0,而keypress的charCode是每个字母的ASCII码,而两个的which都有值,对应的是每个键的值,但是大小写也是一样的,所以字符用keypress,而操作类的用keydown.
* keydown可以响应任意键盘按键,keypress只可响应字符类键盘按键
* keypress返回ASCII码,可以转换成相应字符。
* 文本操作事件
* input(输入一次触发一次),change(鼠标聚焦,与失去聚点,两次input的value值发生改变时才触发)
* focus获得焦点触发, blur失去焦点后触发,
* 窗休操作类(window上的事件)
* scroll 页面滚动条发生滚动触发
* load 整个文档解析完毕后才触发,效率低。
+ 事件的外理程序:事件触发后要执行的代码(函数形式)
点击操作: 就是一个事件。
事件:就是一件事,比如 按键被点击,弹出对话框,点击就是事件的名子
触发:被点了,就是触发了
响应:弹框了就是响应
事件源:按键就是事件源
事件绑定的特性 一但绑定 这个元素 就一直有这个事件了,即使事件中修改了这个元素的id,事件也会继续有这个事件。
事件源对象:event.target 火狐只有这个,event.srcElement ie只有这个,这两个chrome都有
`var target = event.target || event.srcElement;`
事件参数对象:window.event和e都是事件参数对象,一个是Ie的标准,一个是火狐的标准,事件参数e在ie8的浏览器中是不存在,此时用window.event来代替。`var event = e ||window.event;`
事件委托:通过事件的冒泡把子元素的事件传传递给父元素,父元素再通过事件源对象操作每个子元素
优点:1、性能不需要循环所有的元素一个个绑定事件
2、灵活,当有新的子元素时不需要重新绑定事件。
```javascript
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
</ul>
<script>
var ul = document.getElementsBytagName('ul')[0];
ul.onclick = function (e) {
var event = e || window.event;
var target = event.target || event.srcElement;
console.log(target.innerText);
}
</script>
```
(一)点击事件 onclick
```
<!--第一种方法弹框--没有把html和js分离-->
<input type="button" value="按键1" onclick="alert('弹出1')">
<!--第二种方法弹框--只是把js代码封装在函数内,还是没有真正意义上的分离-->
<!--标签内属性的值是由引号包裹的,代表点击元素时,执行引号里的代码,会把引号里的代码拿出来跑,如果函数不加括号,函数就不会执行,所以这里的函数要加括号-->
<input type="button" value="按键2" onclick="f1()">
<!--第三种方法-->
<input type="button" value="按键3" id="btn">
<!--最终版-->
<input type="button" value="按键4" id="btn2">
<script >
// 第二种方法js
function f1() {
alert("弹出2");
}
</script>
<script >
// 第三种方法js
function f2() {
alert("弹出3");
}
/* html标签中的id属性中存储的值是唯一的,id属性就像人的身份证号一样,不能重复,页面中唯一的标识,从文档中找到id值为btn的这个标签(元素)
* document.getElementById("id属性的值") 返回的是一个元素对象,如果出现在了html代码的前面,会找不到而报错。
* */
// 根据id属性的值从整个文档中获取这个元素(标签)
var btnObj = document.getElementById("btn");
// 为当前找到的这个按钮元素(对象)注册点击事件 ,添加事件处理函数
/*这里的函数不能加括号,加上括号是执行的意思,页面加载到这个地方会直接执行了函数,添加事件的回调函数应该就是给相应的事件属性赋值,而很明显需要把一个函数赋值给这个事件属性,而不是函数的调用结果。所以在js中的绑定是直接赋值。*/
btnObj.onclick = f2; // 这个方法有个缺陷,就是如果从别的文件中引入的js文件中正好有个f2文件出现了重名的情况,后面的会把前面的覆盖了
</script>
<script >
// 最终版js
// 根据id属性的值从整个文档中获取这个元素(标签)
var btn1Obj = document.getElementById("btn2");
// 为当前的这个按钮(元素)注册点击事件,添加事件处理函数
btn1Obj.onclick = function () { // 这是匿名函数就不会出现名子冲突的情况了
// 响应做的事
alert("最终版的弹框");
}
</script>
```
(二)鼠标进入和移出事件 onmouseover进入 onmouseout 移出
案例一:鼠标进入离开列表高亮显示
```
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
ul {
list-style: none;
}
</style>
</head>
<body>
<ul id="uu">
<li>五菱神车</li>
<li>五菱神车</li>
<li>五菱神车</li>
<li>五菱神车</li>
<li>五菱神车</li>
<li>五菱神车</li>
<li>五菱神车</li>
<li>五菱神车</li>
</ul>
<script src="./common.js"></script>
<script >
// 鼠标进入li高亮显示,离开后恢复原样
// 鼠标进入和鼠标离开是两个事件
// 先通过id 获取到ul元素,再通过标签把li选中,放到一个伪数组内
var liList = my$("uu").getElementsByTagName("li");
// 循环这个列表
for (var i=0; i<liList.length; i++) {
// 注册鼠标进入事件
liList[i].onmouseover = function () {
this.style.backgroundColor = "red";
};
// 注册鼠标离开事件
liList[i].onmouseout = function () {
this.style.backgroundColor = "";
};
}
</script>
</body>
```
案例二:鼠标进入和移出显示二维码
事件响应代码中添加和移除类名的方式,显示或隐藏,注意多个类名的时候,要把之前的也要重新写上
```
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
.nodeSmall {
50px;
height: 50px;
background: url(../images/bgs.png) no-repeat -159px -51px;
position: fixed;
right: 10px;
top: 40%;
}
.erweima {
position: absolute;
top: 0;
left: -150px;
}
.nodeSmall a {
display: block;
50px;
height: 50px;
}
.hide {
display: none;
}
.show {
display: block;
}
</style>
</head>
<body>
<div class="nodeSmall" id="node_small">
<a href="#"></a><!--锚定-->
<div class="erweima hide" id="er">
<img src="../images/456.png" alt=""/>
</div>
</div>
<script src="./common.js"></script>
<script >
// 鼠标进入时a标签时,让id是er的类样式改为show,移出为hide
// 先根据id 获取元素div,然后根据标签名获取a
var aObj = my$("node_small").getElementsByTagName("a")[0];
// 为元素a注册鼠标进入事件 添加事件处理函数
aObj.onmouseover = function () {
my$("er").className = "erweima show";
};
// 为元素a注册鼠标移出事件 添加事件处理函数
aObj.onmouseout = function () {
my$("er").className = "erweima hide";
};
</script>
</body>
```
案例三:鼠标经过高亮显示边框
注意,由于border会增加盒子的大小,鼠标经过时会显示跳,影响下面的布局,所以先在css里设置好边框大小,颜色和背景一样,鼠标经过时只改边框颜色就可以了
```
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
div {
900px;
margin: 20px auto;
text-align: center;
}
ul>li {
list-style: none;
150px;
height: 150px;
background-color: #ccc;
float: left;
margin-left: 10px;
border: 2px solid #ccc;
}
</style>
</head>
<body>
<div>
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
<script >
// 根据标签名获取所有li元素伪数组
var tagList = document.getElementsByTagName("li")
// 循环数组,拿到每个li元素,为每个元素注册鼠标经过事件
for (var i=0; i<tagList.length; i++) {
// 鼠标进入事件
tagList[i].onmouseover = function () {
this.style.borderColor = "#f40";
};
// 鼠标离开事件
tagList[i].onmouseout = function () {
this.style.borderColor = "#ccc";
};
}
</script>
</body>
```
(三)焦点事件
进入焦点:onfocus 失去焦点:onblur
案例一:模拟搜索框焦点事件
```
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
input {
200px;
height: 30px;
color: #ccc;
outline: none;
}
</style>
</head>
<body>
<input type="text" value="请输入内容">
<script >
// 根据标签名获取到input元素
var inpuObj = document.getElementsByTagName("input")[0];
// 注册获取焦点事件 onfocus
inpuObj.onfocus = function () {
// 判断输入框是否是默认值
if (this.value == "请输入内容") {
this.value="";
this.style.color = "black"
}
}
// 注册失去焦点事件 onblur
inpuObj.onblur = function () {
// 判断用户有没有输入,这里也可以写this.value == "",但是效率没有数字的高
if (this.value.length == 0) {
this.value = "请输入内容";
this.style.color = "#ccc";
}
};
</script>
</body>
```
案例二:验证密码长度
```
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
input {
200px;
height: 20px;
outline: none;
/*border: 1px solid ;*/
}
</style>
</head>
<body>
<input type="text" value="">
<script >
// 根据标签名获取到元素input, 注册失去焦点事件,添加事件处理函数
document.getElementsByTagName("input")[0].onblur = function () {
// 判断密码长度大于等于6小于等于10
if (this.value.length >= 6 && this.value.length <= 10) {
this.style.border = "2px solid green"
} else {
this.style.border = "2px solid red";
}
};
</script>
</body>
```
## 五、操作属性
能对属性进行赋值,也可以通过属性拿对对应的值
### (一)非表单元素的属性
href、 title 、id、 src、 className
案例一:点击按钮显示图片
```
<body>
<input type="button" value="显示图片" id="btn">
<img src="" alt="" id="img">
<script >
// 点击按钮显示图片:点击按钮的时候,设置img的src属性来显示图片
// 1、获取按钮的id
var btnObj = document.getElementById("btn");
// 2、为按钮注册点击事件,添加事件处理函数
btnObj.onclick = function () {
// 3、获取图片的id获取图片元素(标签)
var imgObj = document.getElementById("img");
// 为图片标签设置src属性
imgObj.src = "1.jpg";
// 为图片标签添加width属性
imgObj.width = '300'; // 不加px,因为这是img标签里的属性,只有在css里时才要加px
}
</script>
</body>
```
案例二:点击修改a标签的地址和热点文字
```
<body>
<input type="button" value="显示图片" id="btn">
<img src="" alt="" id="img">
<script >
// 点击按钮显示图片:点击按钮的时候,设置img的src属性来显示图片
// 1、获取按钮的id
var btnObj = document.getElementById("btn");
// 2、为按钮注册点击事件,添加事件处理函数
btnObj.onclick = function () {
// 3、获取图片的id获取图片元素(标签)
var imgObj = document.getElementById("img");
// 为图片标签设置src属性
imgObj.src = "1.jpg";
// 为图片标签添加width属性
imgObj.width = '300'; // 不加px,因为这是img标签里的属性,只有在css里时才要加px
}
</script>
</body>
```
案例三 点击修改P标签的显示内容
```
<body>
<input type="button" value="设置p标签的内容" id="btn"/>
<p id="p1">这是一个p标签</p>
<script >
// 案例:点击按钮修改p标签的显示内容
// 先根据id获取按钮,为按钮注册点击事件,添加事件处理函数,在事件处理函数里改写p标签的内容
// 凡是成对的标签,中间文本内容,设置的时候,都使用innerText这个属性的方式
var btnObj = document.getElementById("btn");
btnObj.onclick = function () {
// 获取id为p1标签并设置其innerText属性
document.getElementById("p1").innerText = "哈哈哈";
}
</script>
</body>
```
案例四 点击修改多个P标签的文字
```
<body>
<input type="button" id="btn" value="更换多个P标签">
<p>王者归来</p>
<p>王者归来</p>
<p>王者归来</p>
<p>王者归来</p>
<p>王者归来</p>
<p>王者归来</p>
<!--如果有两个div,里面有P标签,只想改d1里的,另一个不动,需要在标签选择的时候,进行区别:
加上document代表的是从整个文档里找,下面这句代码的意思是,先找到d1,然后再找d1下面的p标签
document.getElementById("d1").getElementsByTagName("p")-->
<div id="d1">
<p>哈哈,变帅了</p>
<p>哈哈,变帅了</p>
<p>哈哈,变帅了</p>
<p>哈哈,变帅了</p>
<p>哈哈,变帅了</p>
</div>
<div id="d2">
<p>啦啦,变丑了</p>
<p>啦啦,变丑了</p>
<p>啦啦,变丑了</p>
<p>啦啦,变丑了</p>
<p>啦啦,变丑了</p>
</div>
<script>
// 案例:点击按键修改多个标签的文字内容
// 1、根据id获取按钮,注册点击事件,添加事件处理函数
document.getElementById("btn").onclick = function () {
// document.getElementsByTanName("标签的名子"),不管找到的元素是几个,返回的都是一个伪数组
// var pObjs = document.getElementsByTagName("p"); 找整个文档的p标签
var pObjs = document.getElementById('d1').getElementsByTagName("p"); // 只找id为d1下面的p标签
// 循环这个伪数组
for (var i=0; i<pObjs.length; i++) {
pObjs[i].innerText = "王者荣耀";
}
};
</script>
</body>
```
案例5 点击修改图片的alt title
```
<body>
<img src="1.jpg" alt="" title="">
<input type="button" value="变一下">
<script >
// 因为根据标签名获取到的元素是一个伪数组,所以,要想进行属性的设置,要么用下标获取单个的元素,要么循环获取每个元素
document.getElementsByTagName("input")[0].onclick = function () {
var imgObj = document.getElementsByTagName("img")[0];
imgObj.alt = "图片丢失后显示的";
imgObj.title = "鼠标悬停时显示的";
}
</script>
</body>
```
案例6:点击修改所有文本框的值
```
<body>
<input type="button" value="变一下" id="btn"> <br/>
<input type="text" value=""> <br/>
<input type="text" value=""> <br/>
<input type="text" value=""> <br/>
<input type="text" value=""> <br/>
<input type="text" value=""> <br/>
<input type="text" value=""> <br/>
<input type="text" value=""> <br/>
<input type="text" value=""> <br/>
<input type="text" value=""> <br/>
<input type="text" value=""> <br/>
<script >
// 根据id获取到标签,为标签注册事件,添加事件处理函数
document.getElementById("btn").onclick = function () {
// 根据标签名获取元素,循环伪数组,排除type是button的那个,为其他的input标签改value的值
var inpObjs = document.getElementsByTagName("input");
for (var i=0; i<inpObjs.length; i++) {
if (inpObjs[i].type != "button") {
inpObjs[i].value = "无声无息";
}
}
};
</script>
</body>
```
案例7 为每一张图片添加点击事件
```
<body>
<img src="1.jpg" alt="">
<img src="1.jpg" alt="">
<img src="1.jpg" alt="">
<img src="1.jpg" alt="">
<img src="1.jpg" alt="">
<script >
// 找到页面中的所有img标签,循环,在循环里面给每一个标签注册事件,添加事件处理函数
var imgObjs = document.getElementsByTagName("img");
for (var i=0; i<imgObjs.length; i++) {
imgObjs[i].onclick = function () {
alert("被点了");
};
}
</script>
</body>
```
案例8:点击按键修改按钮本身的value属性
```
<body>
<input type="button" value="按钮" id="btn"/>
<script >
// 在某个元素的事件中,自己的事件中的this,就是当前的这个元素对象
var btnObj = document.getElementById("btn");
btnObj.onclick = function () {
this.value = "我变了";
this.type = "text";
this.id = "btn2";
};
</script>
</body>
```
案例9:点击图片修改自身的宽高
```
body>
<img src="1.jpg" alt="">
<script >
// 点击图片修改自身的大小
// 根据标签名获取到标签伪数组,取第一个,给自身注册一个事件,添加事件处理函数
var imgObj = document.getElementsByTagName("img")[0];
imgObj.onclick = function () {
this.width = "100";
this.height = "80";
};
</script>
</body>
```
案例10 按钮的排他功能
```
<body>
<input type="button" value="没怀孕">
<input type="button" value="没怀孕">
<input type="button" value="没怀孕">
<input type="button" value="没怀孕">
<input type="button" value="没怀孕">
<input type="button" value="没怀孕">
<script >
// 按钮的排他功能的思路,点击选中的按钮,并不是先将本次选中的改变,再把之前选中的再变回去,而是在本次改变之前,先将所有的恢复成默认的,再将选中的
// 发生改变
// 根据标签名获取所有的input标签
var inpObjs = document.getElementsByTagName("input");
// 循环inpObjs这个伪数组对象
for (var i=0; i<inpObjs.length; i++) {
// 为每一个元素注册点击事件,添加事件处理函数
inpObjs[i].onclick = function () {
// 先进行一次循环将所有的标签恢复成默认的
for (var j=0; j<inpObjs.length; j++) {
inpObjs[j].value = "没怀孕";
}
// 再把当前点击的设成怀孕了
this.value= "怀孕了"; // 因为已经给每个元素都注册了事件,这里的this,代表,谁被点击了就是谁
// console.log(i); // 6
// inpObjs[i].value = "怀孕了"; 这里不这样写,因为,页面一加载完毕,for循环给每个元素注册了点击事件,也就循环完毕了,而点击事件,
// 要等到点击后才能触发,此时i这个变量,已经变成了数组的长度6,所以会报错,找不到。
};
}
</script>
</body>
```
案例11 点击超连接切换图片
```
<body>
<a href="images/1.jpg" id="ak">
<img src="images/1-small.jpg" alt="" id="im">
</a>
<script >
// 点击小图的时候,把大图的图片地址给小图
document.getElementById("im").onclick = function () {
this.src = document.getElementById("ak").href;
return false;
};
</script>
</body>
```
案例12 点击按钮切换图片-通过封装document.getElementById()的方式
```
// common.js
/**
* 根据id值,返回对应的标签元素。
* @param id id 属性的值,string类型
* @returns {HTMLElement} 元素对象
*/
function my$ (id) {
return document.getElementById(id);
}
```
```
<body>
<input type="button" value="显示大图" id="btn">
<img src="images/1-small.jpg" alt="" id="im">
<script >
// 因为每次都要写document.getElementById(id)有重复的代码,所以要封装成函数
function my$ (id) {
return document.getElementById(id);
}
// 把id传给封装后的函数,获取到元素,注册事件,添加事件处理函数
my$("btn").onclick = function () {
// 获取到响应的元素,改变其src属性
my$("im").src = "images/1.jpg";
};
</script>
</body>
```
### (二)表单元素属性
+ value 用于大部分表单元素的内容获取(option除外)
+ type 可以获取input标签的类型(输入框或复选框等)
+ disabled 禁用属性
+ checked 复选框选中属性
+ selected 下拉菜单选中属性
+ name
+ readonly 只读
案例1:点击修改性别和兴趣
```
<body>
<div id="d1">
<input type="button" value="修改性别" id="btn">
<input type="radio" value="1" name="sex">男
<input type="radio" value="2" name="sex" id="ip1">女
<input type="radio" value="3" name="sex">保密
</div>
<br/>
<div id="d2">
<input type="button" value="修改兴趣" id="btn2">
<input type="checkbox" value="1" name="xinqu">吃饭
<input type="checkbox" value="2" name="xinqu" id="ip2">睡觉
<input type="checkbox" value="3" name="xinqu">打豆豆
<input type="checkbox" value="4" name="xinqu" id="ip3">保蓝球
<input type="checkbox" value="5" name="xinqu">踢足球
</div>
<script >
// 规律:在表单的标签中,如果属性和值只有一个,并且值是这个属性本身(如checked),那么写js代码,DOM操作的时候,这个属性值,是布尔类型就可以了。
// 先封装一个函数
function my$ (id) {
return document.getElementById(id);
}
// 获取id为btn的元素,注册事件,添加事件处理函数
my$("btn").onclick = function () {
my$("ip1").checked = true;
};
my$("btn2").onclick = function () {
my$("ip2").checked = true;
my$("ip3").checked = true;
};
</script>
</body>
```
案例2:点击按钮选择菜单
```
<body>
<input type="button" id="btn" value="点菜">
<select name="" id="ss">
<option value="1">油炸榴莲</option>
<option value="2">油炸杜松</option>
<option value="3">爆炒臭豆腐</option>
<option value="4" id="op1">清蒸助教</option>
<option value="5">凉拌班主任</option>
<option value="6">红烧三丝</option>
</select>
<script src="common.js"></script>
<script >
// 根据id获取元素,注册事件,添加事件处理函数
my$("btn").onclick = function () {
// 事件响应
my$("op1").selected = true;
};
</script>
</body>
```
案例3:点击修改文本域
```
<body>
<!--readonly="readonly"只读 disabled="disable" 禁用-->
<textarea name="" id="tt" cols="30" rows="10">
anchor() 创建 HTML 锚。
big() 用大号字体显示字符串。
blink() 显示闪动字符串。
bold() 使用粗体显示字符串。
charAt() 返回在指定位置的字符。
charCodeAt() 返回在指定的位置的字符的 Unicode 编码。
concat() 连接字符串。
fixed() 以打字机文本显示字符串。
fontcolor() 使用指定的颜色来显示字符串
</textarea>
<input type="button" id="btn" value="点我">
<script src="common.js"></script>
<script >
// 根据id值获取元素,注册事件,添加事件处理函数
my$("btn").onclick = function () {
// 推荐用value,form表单的标签
// my$("tt").value = "哈哈";
my$("tt").innerText = "吹牛逼";
};
</script>
</body>
```
### (三)样式操作
#### 1、使用style方式设置的样式显示在标签行内
对象.style.属性 = 值;设置标签的
对象.style.classText = 值
获取:getComputedStyle,因为对象.style.属性只能拿到行间样式,拿不到css类里的属性。
window.getComputedStyle(ele, null).属性。计算样式只读,返回计算样式的值都是绝对值,没有相对单位,ie8及ie8以下不兼容。第二个参数是用来获取伪元素的,把伪元素名子放到第二个参数上。
在ie8及ie8以下,使用ele.currentStyle[属性]来获取只读属性,返回的计算样式的值不是经过转换的绝对值,
案例1:点击修改div的宽高背景颜色
```
<body>
<div id="dv"></div>
<input type="button" id="btn" value="变了变了">
<script src="common.js"></script>
<script >
/*凡是在css中属性是多个单词组成的比如:background-color 在js操作DOM的时候,把—干掉,后面单词的首字母大写即可,可读写行间样式,没有兼容性的疸,碰到float这样的保留字属性,前面应加css,float->cssFloat,写入的值必须是字符串的格式。
比如backgroundColor*/
// 获取id获取元素,注册事件,添加事件处理函数
my$("btn").onclick = function () {
// 事件响应
my$("dv").style.width = "300px";
my$("dv").style.height = "200px";
my$("dv").style.backgroundColor = "deeppink";
}
</script>
</body>
```
案例2 点击隐藏或显示div
```
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
div {
300px;
height: 300px;
background-color: #f40;
}
</style>
</head>
<body>
<input type="button" id="btn" value="隐藏">
<input type="button" id="btn2" value="显示">
<div id="dv"></div>
<script src="common.js"></script>
<script >
// 根据id获取元素 注册事件 添加事件处理函数
my$("btn").onclick = function () {
// 响应事件
my$("dv").style.display = "none";
};
my$("btn2").onclick = function () {
// 响应事件
my$("dv").style.display = "block";
};
</script>
</body>
```
案例3 一个按钮解决div的隐藏与显示
```
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
div {
300px;
height: 300px;
background-color: #f40;
}
</style>
</head>
<body>
<input type="button" id="btn" value="隐藏">
<div id="dv"></div>
<script src="common.js"></script>
<script >
// 通过设置元素的style属性的display是none,还是block来显示或是隐藏,根据btn的value属性值来判断显示的状态
// 根据id获取元素 注册事件 添加事件处理函数
my$("btn").onclick = function () {
// 根据value值来判断
if (this.value=="隐藏") {
my$("dv").style.display = "none";
this.value = "显示";
}else if (this.value == "显示") {
my$("dv").style.display = "block";
this.value = "隐藏";
}
};
</script>
</body>
```
#### 2、类名操作
修改标签的className属性相当于直接修改标签的类名
对象.className = 值
案例1 通过类样式,点击设置div样式
```
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.cls {
300px;
height: 300px;
background-color: #f40;
border: 1px solid pink;
}
</style>
</head>
<body>
<div id="dv"></div>
<input type="button" id="btn" value="设置div">
<script src="common.js"></script>
<script >
my$("btn").onclick = function () {
// 在js代码操作Dom时,设置元素的类样式,不用clsss关键字,应该使用className
// 下面这种方式不好,一般超过2个样式,采用css样式,给元素设置类样式属性值是之前设置过的css类名的方式
// my$("dv").style="300px;height:300px;border:2px solid pink";
my$("dv").className = "cls";
};
</script>
</body>
```
案例2 通过类样式显示隐藏div
```
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
div {
300px;
height: 300px;
background-color: #f40;
}
.cls {
display: none;
}
</style>
</head>
<body>
<input type="button" id="btn" value="隐藏">
<div id="dv"></div>
<script src="common.js"></script>
<script >
// 通过给一个标签添加类样式,来控制该元素的显示与隐藏,
my$("btn").onclick = function () {
// 判断元素的类里面有没有cls这个类样式,如果没有,就设置类样式为cls并设置btn的value属性值为显示
if (my$("dv").className != "cls") {
my$("dv").className = "cls";
this.value = "显示";
} else {
// 否则,就设置这个元素的类样式属性为空,并且把btn的value属性设置为隐藏
my$("dv").className = "";
this.value = "隐藏";
}
};
</script>
</body>
```
案例3 关灯效果
```
<body>
<input type="button" value="关灯" id="btn" />
<script src="common.js"></script>
<script >
my$("btn").onclick = function () {
/*直接给body标签添加类样式,改变背景色
两种方式,一是可以用document.getElementById()获取body,一种是直接document.body*/
// document.body.className != "cls"?document.body.className="cls":document.body.className="";
document.body.className=document.body.className != "cls"?"cls":"";
};
</script>
</body>
```
### (四)阻止a链接浏览器默认跳转和其他的默认事件
+ return false;
+ 事件参数对象.preventDefault()方法;
+ href="javacript:void(0);"
```javascript
<body>
<!--阻止超连接的跳转,就是要在事件响应中return false-->
<!--第一种写法-->
<!--加onclick对浏览器默认的跳转事件没有影响, 阻止超连接的默认跳转:return false-->
<a href="http://www.baidu.com" onclick="alert('哎呀我被点了');return false">百度一下</a>
<!--第二种写法-->
<script >
function f1() {
alert("弹窗");
return false;
}
</script>
<!--onclick=f1()因为这个函数的执行结果是false,与第一种结果相对比,没有return,所以还是阻止不了-->
<!--<a href="http://www.baidu.com" onclick="f1()">百度一下</a>-->
<a href="http://www.baidu.com" onclick="return f1()">百度一下</a>
<!--第三种写法-->
<a href="http://www.baidu.com" id="aa">百度一下</a>
<script >
document.getElementById("aa").onclick = function () {
alert("弹框2");
return false;
};
</script>
</body>
//还有一种写法
function cancelHandler(evernt){
if (event.preventDefault) {
event.preventDefault();
}else {
event.returnValue = false;
}
}
```
案例一:点击小图切换大图
```
<body>
<a href="../images/1.jpg" id="aa"><img src="../images/1-small.jpg" alt=""></a>
<img src="" alt="" id="big">
<script src="./common.js"></script>
<script >
// 点击小图显示大图
// 因为小图是在a连接里面,所以直接点击a超连接就可以了
// 根据Id获取a连接元素对象,注册事件,添加事件处理函数
my$("aa").onclick = function () {
my$("big").src = this.href;
return false;
};
</script>
</body>
```
案例二:美女相册
```
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style type="text/css">
body {
font-family: "Helvetica", "Arial", serif;
color: #333;
background-color: #ccc;
margin: 1em 10%;
}
h1 {
color: #333;
background-color: transparent;
}
a {
color: #c60;
background-color: transparent;
font-weight: bold;
text-decoration: none;
}
ul {
padding: 0;
}
li {
float: left;
padding: 1em;
list-style: none;
}
#imagegallery {
list-style: none;
}
#imagegallery li {
margin: 0px 20px 20px 0px;
padding: 0px;
display: inline;
}
#imagegallery li a img {
border: 0;
}
</style>
</head>
<body>
<h2>
美女画廊
</h2>
<ul id="imagegallery">
<li><a href="../images/1.jpg" title="美女A">
<img src="../images/1-small.jpg" width="100" alt="美女1"/>
</a></li>
<li><a href="../images/2.jpg" title="美女B">
<img src="../images/2-small.jpg" width="100" alt="美女2"/>
</a></li>
<li><a href="../images/3.jpg" title="美女C">
<img src="../images/3-small.jpg" width="100" alt="美女3"/>
</a></li>
<li><a href="../images/4.jpg" title="美女D">
<img src="../images/4-small.jpg" width="100" alt="美女4"/>
</a></li>
</ul>
<div style="clear:both"></div>
<!--显示大图的-->
<img id="image" src="../images/placeholder.png" alt="" width="450"/>
<p id="des">选择一个图片</p>
<script src="./common.js"></script>
<script >
// 点击a标签把a标签href属性赋值给id为img的src,同时把a标签的title属性赋值给id为des的文本属性
var aObjs = my$("imagegallery").getElementsByTagName("a"); // 先根据Id获取到ul元素,再根据标签名获取到a元素组成的伪数组
// 循环a元素组成的伪数组对象,为每个a元素注册事件,添加事件处理函数
for (var i=0; i<aObjs.length; i++) {
// 为每个a添加事件
aObjs[i].onclick = function () {
// 把a 的href属性赋值给id为image的src
my$("image").src = this.href;
// 把a 的title属性赋值给des的innerText
my$("des").innerText = this.title;
// 阻止浏览器的默认跳转事件
return false;
};
}
</script>
</body>
```
### (五) innerText和textContent兼容性
+ 设置标签中的文本内容,应该使用textContent属性,谷歌、火狐支持,ie8不支持
+ 设置标签中的文本内容,应该使用innerText属性,谷歌、ie8支持,高版本的火狐支持,低版本的不支持
+ 如果这个属性在浏览器中不支持,那么这个属性的类型是undefined,判断这个属性的类型是不是undefined,就知道浏览器支不支持。
```
<body>
<input type="button" value="设置" id="btn">
<input type="button" value="获取" id="btn1">
<div id="dv">螺蛳粉就算了</div>
<script src="./common.js"></script>
<script >
// 设置标签中的文本内容,应该使用textContent属性,谷歌、火狐支持,ie8不支持
// 设置标签中的文本内容,应该使用innerText属性,谷歌、ie8支持,高版本的火狐支持,低版本的不支持
// 如果这个属性在浏览器中不支持,那么这个属性的类型是undefined,判断这个属性的类型是不是undefined,就知道浏览器支不支持。
// ** 写解决兼容性的函数
// 1、设置标签中的文本内容
function setInnerText (element, text) {
if (typeof element.textContent=="undefined") {//说明不支持
element.innerText = text;
}else {
element.textContent = text;
}
}
// 2、获取标签中的文本内容
function getInnerText (element) {
if (typeof element.textContent == "undefined") {
return element.innerText;
} else {
return element.textContent;
}
}
// 测试设置
my$("btn").onclick = function () {
// 设置div中的文本内容
setInnerText(my$("dv"), "老司机服务");
};
// 测试获取
my$("btn1").onclick = function () {
// 获取div中的内容
var out = getInnerText(my$("dv"));
console.log(out);
};
</script>
</body>
```
### (六)innerText和innerHTML区别
+ innerText主要是设置文本的,设置标签内容,是没有标签的效果的,同时会把标签内的所有内容(包括包含的标签)都替换为新的内容 文本的形式
+ innerHTML 可以设置文本内容,主要作用是在标签中设置新的html标签内容,是有标签的效果的,同时把标签内的所有内容(包括包含的标签)都替换成
+ 新的内容 解析成html代码的形式
+ innerText 获取的时候,会把标签内的所有的文本内容到取到。包括嵌套的标签里的内容,文本的形式
+ innerHTML 获取的时候,会把标签内的所有内容,原样获取到,html代码的形式
```
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
div {
300px;
height: 300px;
border: 2px solid red;
}
</style>
</head>
<body>
<input type="button" value="设置" id="btn">
<input type="button" value="获取" id="btn1">
<div id="dv">酸辣粉叫我
<span>哈哈哈</span>
</div>
<script src="./common.js"></script>
<script >
/*总结:1、innerText主要是设置文本的,设置标签内容,是没有标签的效果的,同时会把标签内的所有内容(包括包含的标签)都替换为新的内容 文本的形式
* 2、innerHTML 可以设置文本内容,主要作用是在标签中设置新的html标签内容,是有标签的效果的,同时把标签内的所有内容(包括包含的标签)都替换成
* 新的内容 解析成html代码的形式
* 3、innerText 获取的时候,会把标签内的所有的文本内容到取到。包括嵌套的标签里的内容,文本的形式
* 4、innerHTML 获取的时候,会把标签内的所有内容,原样获取到,html代码的形式
*/
// 设置
my$("btn").onclick = function () {
// my$("dv").innerText = "哈哈" // 设置文本
// my$("dv").innerText = "<p>没看到</p>" // 设置html 代码 ,没有标签的效果
// my$("dv").innerHTML = "<p>没看到</p>" // 设置html代码,有标签的效果。
};
// 获取
my$("btn1").onclick = function () {
console.log(my$("dv").innerText); // 酸辣粉叫我 哈哈哈
console.log(my$("dv").innerHTML); //酸辣粉叫我 <span>哈哈哈</span>
};
</script>
</body>
```
### (七)自定义属性
+ 自定义属性:也叫隐式属性,本身html标签中没有这个属性,是自己添加的,用来存储一些数据。setAttribute(属性名,属性值)
+ 在html标签中添加的自定义属性,是无法通过对象.属性获取的,因为自定义属性在标签中,并不在DOM对象中,所以需要使用getAttribute("自定义属性名子")来获取的,也可以拿到显示属性。
+ 在html标签中设置自定义属性,是无法通过对象.属性=值来设置的,因为这种方法自定义属性在DOM对象上,不在标签中
+ 删除自定义属性:removeAttribute("属性的名子") 用于移除自定义属性和标签的属性
+ 判断有没有这个属性 hasAttribute(属性名) 返回布尔值。
案例一:自定义属性的获取
```
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
li {
list-style: none;
cursor: pointer;
}
</style>
</head>
<body>
<ul id="uu">
<li score="10">小明的成绩</li>
<li score="20">小花的成绩</li>
<li score="30">小李的成绩</li>
</ul>
<script src="./common.js"></script>
<script >
/*
* 自定义属性:本身html标签中没有这个属性,是自己添加的,用来存储一些数据
* 在html标签中添加的自定义属性,是无法通过对象.属性获取的,因为自定义属性在标签中,并不在DOM对象中,所以需要使用getAttribute("自定义属性
* 名子")来获取的
* */
var list = my$("uu").getElementsByTagName("li");
for (var i=0; i<list.length; i++) {
list[i].onclick = function () {
// alert(this.score); // undefined
alert(this.getAttribute("score"));
};
}
</script>
</body>
```
案例二:自定义属性的设置与获取
```
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
li {
list-style: none;
cursor: pointer;
}
</style>
</head>
<body>
<ul id="uu">
<li>小明的成绩</li>
<li>小明的成绩</li>
<li>小明的成绩</li>
<li>小明的成绩</li>
</ul>
<script src="./common.js"></script>
<script >
// 根据id名获取ul元素并且获取下面的所有li标签
var list = my$("uu").getElementsByTagName("li");
// 循环
for (var i=0; i<list.length; i++) {
// 先为每个li添加自定义的属性
// list[i].score = (i+1)*10; // 因为此方法的自定义属性在DOM对象上,不在标签中
list[i].setAttribute("score", (i+1)*10);
// 为每个li注册点击事件,显示对应的自定义属性
list[i].onclick = function () {
alert(this.getAttribute("score"));
};
}
</script>
</body>
```
案例三:删除自定义属性和标签属性
```
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
div {
200px;
height: 200px;
background-color: #ff4400;
}
.cls {
background-color: yellow;
}
</style>
</head>
<body>
<input type="button" value="删除自定义属性" id="btn">
<input type="button" value="删除标签属性" id="btn1">
<div id="dv" class="cls" score="30"></div>
<script src="./common.js"></script>
<script >
/*
* 点击后移动自定义的属性和标签的属性class 注册区分标签属性class和DOM属性className
* removeAttribute("属性的名子") 用于移除自定义属性和标签的属性
* */
my$("btn").onclick = function () {
my$("dv").removeAttribute("score");
};
my$("btn1").onclick = function () {
// my$("dv").className = ""; // 这种方式值没了,但是属性还在
my$("dv").removeAttribute("class"); // 删除标签的自带属性
};
</script>
</body>
```
案例四:tab切换案例的实现 排他思想
排他的步骤:1、先删除所有(恢复默认)2、再重新定义
```
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
* {
margin: 0;
padding: 0;
}
ul {
list-style-type: none;
}
.box {
400px;
height: 300px;
border: 1px solid #ccc;
margin: 100px auto;
overflow: hidden;
}
.hd {
height: 45px;
}
.hd span {
display: inline-block;
90px;
background-color: pink;
line-height: 45px;
text-align: center;
cursor: pointer;
}
.hd span.current {
background-color: purple;
}
.bd li {
height: 255px;
background-color: purple;
display: none;
}
.bd li.current {
display: block;
}
</style>
</head>
<body>
<div class="box" id="box">
<div class="hd">
<span class="current">体育</span>
<span>娱乐</span>
<span>新闻</span>
<span>综合</span>
</div>
<div class="bd">
<ul>
<li class="current">我是体育模块</li>
<li>我是娱乐模块</li>
<li>我是新闻模块</li>
<li>我是综合模块</li>
</ul>
</div>
</div>
<script src="./common.js"></script>
<script>
// 获取最外面的盒子
var boxObj = my$("box");
// 获取hd盒子
var hdObj = boxObj.getElementsByClassName("hd")[0];
// 获取bd盒子
var bdObj = boxObj.getElementsByClassName("bd")[0];
// 获取hd下的所有span
var spanObjs = hdObj.getElementsByTagName("span");
// 获取bd下所有的li
var liObjs = bdObj.getElementsByTagName("li");
// 循环span元素组成的伪数组
for (var i = 0; i < spanObjs.length; i++) {
// 为span添加自定义属性index
spanObjs[i].setAttribute("index", i);
// 注册点击事件
spanObjs[i].onclick = function () {
// hd排他,先移动所有的,再添加自己的
for (var j = 0; j < spanObjs.length; j++) {
spanObjs[j].removeAttribute("class");
}
this.className = "current";
// bd排他 先移动所有,再添加自己的
for (var k = 0; k < liObjs.length; k++) {
liObjs[k].removeAttribute("class");
}
liObjs[this.getAttribute("index")].className = "current";
};
}
</script>
</body>
</html>
```
## 六、节点
### (一)节点的介绍
页面中的所有内容都是节点,包括标签、文本(文字、空格、换行等)、注释、属性
### (三)节点的属性
可以使用标签.点出来,可以使用属性节点.出来,文本节点.点出来
+ nodeType: 节点的类型 1 -- 标签,2--属性,3—文本,注释-8 , document-9
+ nodeName:节点的名子:标签节点--大写的标签名子,属性节点--小写的属性名子,文本节点--#text
+ nodeValue: 节点的值:标签节点--null, 属性节点--属性值,文本节点--文本内容
+ attributes:元素节点的属性集合
```
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="dv">
<span>这是div中的第一个span标签</span>
<p>这是div中的第二个元素,第一个P标签</p>
<ul id="uu">
<li>这是ul中的第一个元素li</li>
<li>这是ul中的第二个元素li</li>
<li>这是ul中的第三个元素li</li>
</ul>
</div>
<script src="./common.js"></script>
<script >
var dvObj = my$("dv");
var child = dvObj.childNodes;
// 获取里面的每个子节点
for (var i=0; i<child.length; i++) {
var node = child[i];
/*
* nodeType--节点的类型 1--标签、2--属性、3--文本;
* nodeName--节点的名子 大写--标签名子、小写--属性名子、#text--文本
* nodeValue--节点的值 null--标签、属性值--属性、 文本内容--文本
* */
console.log(node.nodeType+"==="+node.nodeName+"==="+node.nodeValue);
}
/*
* 3===#text===
1===SPAN===null
1===P===null
1===UL===null
3===#text===*/
// 获取的是属性节点 getAttributeNode("属性名子")
var node = dvObj.getAttributeNode("id");
console.log(node.nodeType+"==="+node.nodeName+"==="+node.nodeValue); // 2===id===dv
</script>
</body>
</html>
```
### (四)节点的层级
在页面中只有标签能做为父节点(或者叫父元素)
父节点obj.parentNode=(父元素)obj.parentElement;
元素层级中,只有children兼容ie9以下的浏览器
```
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="dv">
<span>这是div中的第一个span标签</span>
<p>这是div中的第二个元素,第一个P标签</p>
<ul id="uu">
<li>这是ul中的第一个元素li</li>
<li>这是ul中的第二个元素li</li>
<li>这是ul中的第三个元素li</li>
</ul>
</div>
<script src="./common.js"></script>
<script >
var ulObj = my$("uu");
// ul 标签的父级节点
console.log(ulObj.parentNode); //div
// ul 标签的父级元素
console.log(ulObj.parentElement); //div
// ul 标签的父节点的父节点
console.log(ulObj.parentNode.parentNode); //body
// 再上一层
console.log(ulObj.parentNode.parentNode.parentNode); // html
// 再上一层
console.log(ulObj.parentNode.parentNode.parentNode.parentNode); // #document
// 再上一层
console.log(ulObj.parentNode.parentNode.parentNode.parentNode.parentNode); // null
// 获取节点的类型、名子、值
console.log(ulObj.nodeType); // 1 标签的节点类型是1
console.log(ulObj.nodeName); // UL 标签的节点名子是大写的标签名
console.log(ulObj.nodeValue); // null 标签节点的值是null
// 获取子节点、节元素
var dvObj = my$("dv");
console.log(dvObj.childNodes); // NodeList(7) [text, span, text, p, text, ul#uu, text] 7个子节点 这是的text是换行
console.log(dvObj.children); // HTMLCollection(3) [span, p, ul#uu, uu: ul#uu] 3个子元素
</script>
</body>
</html>
```
#### 节点层级的十二行代码
```
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="dv">哈哈
<span>这是div中的第一个span标签</span>
<p>这是div中的第二个元素,第一个P标签</p>
<ul id="uu">
<li>这是ul中的第一个元素li</li>
<li id="mid">这是ul中的第二个元素li</li>
<li>这是ul中的第三个元素li</li>
</ul>啧啧
</div>å
<script src="./common.js"></script>
<script >
var dvObj = my$("dv");
var liObj = my$("mid");
// 父节点
console.log(dvObj.parentNode);
// 父元素
console.log(dvObj.parentElement);
// 子节点
console.log(dvObj.childNodes);
// 子元素
console.log(dvObj.children);
//============以下方法在谷歌和火狐里是正常的,但是在ie8,子节点的结果变成了子元素的结果,子元素则是undefined
// 第一个子节点
console.log(dvObj.firstChild); // ie8中是第一个字元素
// 第一个子元素
console.log(dvObj.firstElementChild); // ie8中是undefined
// 最后一个子节点
console.log(dvObj.lastChild);
// 最后一个子元素
console.log(dvObj.lastElementChild);
// 某元素的前一个兄弟节点
console.log(liObj.previousSibling);
// 某元素的前一个兄弟元素
console.log(liObj.previousElementSibling);
// 某元素的后一个兄弟节点
console.log(liObj.nextSibling);
// 某元素的后一个兄弟元素
console.log(liObj.nextElementSibling);
/*
* 总结:凡是获取节点的代码在谷歌和火狐得到的都是相关的节点
* 凡是获取元素的代码在谷歌和火狐得到的都是相关的元素
* 从子节点和兄弟节点开始,凡是获取节点的代码在ie8中得到的都是元素,获取元素的相关代码,在ie8中得到的都是undefined,一句话,就是元素的代码,ie不支持
* */
</script>
</body>
</html>
```
案例一:利用节点 点击更改p标签的背景颜色
```
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
div {
300px;
height: 450px;
border:1px solid #f40;
}
</style>
</head>
<body>
<input type="button" value="变色" id="btn">
<div id="dv">
<span>这是span</span>
<p>这是p</p>
<span>这是span</span>
<p>这是p</p>
<span>这是span</span>
<p>这是p</p>
<span>这是span</span>
<p>这是p</p>
<span>这是span</span>
<p>这是p</p>
<a href="">百度</a>
</div>
<script src="./common.js"></script>
<script >
// 点击按键,设置p变色--节点的方式做
my$("btn").onclick = function () {
// 获取dv里的子节点
var dvChild = my$("dv").childNodes;
// 循环数组
for (var i=0; i<dvChild.length; i++) {
// 判断是不是p标签
// if (dvChild[i].nodeType==1&&dvChild[i].nodeName=="P") { // 正常取p标签
if (dvChild[i].nodeType==1&&dvChild[i].nodeName!="P") { // 取反得到除P处的所有标签元素
dvChild[i].style.backgroundColor="red";
}
}
};
</script>
</body>
</html>
```
案例二:隔行变色 节点的方式
```
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input type="button" value="变色" id="btn">
<ul id="uu">
<li>五菱宏光</li>
<li>五菱宏光</li>
<li>五菱宏光</li>
<li>五菱宏光</li>
<li>五菱宏光</li>
</ul>
<script src="./common.js"></script>
<script >
// 隔行变色-通过节点的方式获取liq
my$("btn").onclick = function () {
var liObjs = my$("uu").childNodes;
var content = 0;
for (var i=0; i<liObjs.length; i++) {
// 判断是不是li标签
if (liObjs[i].nodeType==1&&liObjs[i].nodeName=="LI") {
liObjs[i].style.backgroundColor=i%2==0?"red":"yellow"; // 不能用i因为子节点的数和li的数不一致
content ++;
liObjs[i].style.backgroundColor=content%2==0?"red":"yellow";
}
}
};
</script>
</body>
</html>
```
#### 节点的兼容代码
```
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul id="uu">
<li>第一个</li>
<li>第二个</li>
<li>第三个</li>
<li>第四个</li>
<li>第五个</li>
</ul>
<script src="./common.js"></script>
<script >
var ulObj = my$("uu")
// 第一个节点和第一个元素的获取代码在Ie8中和火狐谷歌的不一样
// ulObj.firstChild 在谷歌和火狐中获取的是第一个子节点而在Ie8中是第一个子元素
// ulObj.firstElementChild 在谷歌和火狐中获取的是第一个子元素,而在ie8中是undefined
// 封装第一个节点的兼容函数,获取任意一个父级元素的第一个子元素
function getFirstElementChild (element) { // 不是ie8
if (typeof element.firstElementChild != "undefined") { // (element.firstElementChild)有值就是true,undefined就是false
return element.firstElementChild;
} else { // 是ie8,要拿到第一个子元素,就得是firstCHild
// return element.firstChild; // 如果单是ie返回就行了,但是如果有浏览器不支持上边的,firstChild就是第一个子节点,直接返回就不行了
var node = element.firstChild; // 第一个节点
while (node&&node.nodeType!=1) { // node 存在并且不是标签才循环,因为子节点会算上标签前面的换行,主要是排除第一是子节点,但是不是第一个子元素。再往下面找一下,看是不是子元素。
node = element.nextSibling; // 将下一个兄弟节点赋值给node,再进行判断
}
return node; // 如果node没有找到或者是子元素了就返回
}
}
// 封装最后一个节点的兼容函数 获取任意一个父级元素的最后一个子元素
function getLaseElementChild (element) {
if (typeof element.lastElementChild != "undefined") {
return element.lastElementChild;
} else {
var node = element.lastChild; // 最后一个节点
while (node&&node.nodeType!=1) {
node = element.previousSibling;
}
return node;
}
}
// 测试
console.log(getFirstElementChild(ulObj).innerText)
console.log(getLaseElementChild(ulObj).innerText);
</script>
</body>
</html>
```
案例三:切换背景图片
背景图片变量的拼接
```
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>哈哈,我又变帅了</title>
<style>
* {
margin: 0px;
padding: 0px;
}
body {
background-image: url("images/1.jpg");
}
#mask {
background-color: rgba(255, 255, 255, 0.3);
height: 200px;
text-align: center;
}
#mask img {
200px;
margin-top: 35px;
cursor: pointer;
}
</style>
</head>
<body id="bd">
<div id="mask">
<img src="images/1.jpg" alt="">
<img src="images/2.jpg" alt="">
<img src="images/3.jpg" alt="">
</div>
<script src="./common.js"></script>
<script >
// 获取所有的img元素 循环注册点击事件 将body的背景图地址设置为对应的img的src , 注意的地方就是body背景图片地址的拼接
var imgs = my$("mask").children;
for (var i=0; i<imgs.length; i++) {
imgs[i].onclick = function () {
my$("bd").style.backgroundImage = "url("+this.src+")";
};
}
</script>
</body>
</html>
```
案例四:全选和全不选逻辑
```
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
* {
padding: 0;
margin: 0;
}
.wrap {
300px;
margin: 100px auto 0;
}
table {
border-collapse: collapse;
border-spacing: 0;
border: 1px solid #c0c0c0;
300px;
}
th,
td {
border: 1px solid #d0d0d0;
color: #404060;
padding: 10px;
}
th {
background-color: #09c;
font: bold 16px "微软雅黑";
color: #fff;
}
td {
font: 14px "微软雅黑";
}
tbody tr {
background-color: #f0f0f0;
}
tbody tr:hover {
cursor: pointer;
background-color: #fafafa;
}
</style>
</head>
<body>
<div class="wrap">
<table>
<thead>
<tr>
<th>
<input type="checkbox" id="j_cbAll"/>
</th>
<th>菜名</th>
<th>饭店</th>
</tr>
</thead>
<tbody id="j_tb">
<tr>
<td>
<input type="checkbox"/>
</td>
<td>红烧肉</td>
<td>田老师</td>
</tr>
<tr>
<td>
<input type="checkbox"/>
</td>
<td>西红柿鸡蛋</td>
<td>田老师</td>
</tr>
<tr>
<td>
<input type="checkbox"/>
</td>
<td>油炸榴莲</td>
<td>田老师</td>
</tr>
<tr>
<td>
<input type="checkbox"/>
</td>
<td>清蒸助教</td>
<td>田老师</td>
</tr>
</tbody>
</table>
</div>
<script src="./common.js"></script>
<script >
// 先获取全先的这个复选框
var ckAll = my$("j_cbAll");
// 获取tbody下的所有复选框
var cks = my$("j_tb").getElementsByTagName("input");
// 点击全选框,获取当前的状态,然后设置tbody复选框的状态
ckAll.onclick = function () {
// 循环tbody中的复选框,让每个的checked的状态等于ckAll的状态
for (var i=0; i<cks.length; i++) {
cks[i].checked = this.checked;
}
};
// 为tbody中的input添加点击事件
for (var i=0; i<cks.length; i++) {
cks[i].onclick = function () {
// 检查其他的input的选中状态
var flag = true;
for (var j=0; j<cks.length; j++) {
if (!cks[j].checked) { // 当有任何一个没有选中的时候,就更改标记为false
flag = false;
break;
}
}
ckAll.checked = flag; // 让ckAll的checked的状态和标记同步
};
}
</script>
</body>
</html>
```
## 七、元素
为了提高用户的体验,
元素创建的三种方式:
+ document.write("标签的代码及内容");
+ 对象.innerHTML="标签及代码";
+ document.createElement("标签的名子");
### (一)document.write()
创建元素有缺陷,如果是在页面**加载完毕后**,此时通过这种方式创建元素,那么页面上存在的所有的内容被干掉了。
### (二)对象.innerHTML = "内容"
innerHTML 返回dom的内容,如果 = 就是赋予dom内容
outerHTML 会返回 dom本身+自己的内容,如果 = 就是连自己也给替换了
innerText 返回dom的文本内容 如果=需要注意,这个dom内如果有其他的标签和内容,就会一同覆盖掉
```
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
* {
padding: 0;
margin: 0;
}
.wrap {
300px;
margin: 100px auto 0;
}
table {
border-collapse: collapse;
border-spacing: 0;
border: 1px solid #c0c0c0;
300px;
}
th,
td {
border: 1px solid #d0d0d0;
color: #404060;
padding: 10px;
}
th {
background-color: #09c;
font: bold 16px "微软雅黑";
color: #fff;
}
td {
font: 14px "微软雅黑";
}
tbody tr {
background-color: #f0f0f0;
}
tbody tr:hover {
cursor: pointer;
background-color: #fafafa;
}
</style>
</head>
<body>
<div class="wrap">
<table>
<thead>
<tr>
<th>
<input type="checkbox" id="j_cbAll"/>
</th>
<th>菜名</th>
<th>饭店</th>
</tr>
</thead>
<tbody id="j_tb">
<tr>
<td>
<input type="checkbox"/>
</td>
<td>红烧肉</td>
<td>田老师</td>
</tr>
<tr>
<td>
<input type="checkbox"/>
</td>
<td>西红柿鸡蛋</td>
<td>田老师</td>
</tr>
<tr>
<td>
<input type="checkbox"/>
</td>
<td>油炸榴莲</td>
<td>田老师</td>
</tr>
<tr>
<td>
<input type="checkbox"/>
</td>
<td>清蒸助教</td>
<td>田老师</td>
</tr>
</tbody>
</table>
</div>
<script src="./common.js"></script>
<script >
// 先获取全先的这个复选框
var ckAll = my$("j_cbAll");
// 获取tbody下的所有复选框
var cks = my$("j_tb").getElementsByTagName("input");
// 点击全选框,获取当前的状态,然后设置tbody复选框的状态
ckAll.onclick = function () {
// 循环tbody中的复选框,让每个的checked的状态等于ckAll的状态
for (var i=0; i<cks.length; i++) {
cks[i].checked = this.checked;
}
};
// 为tbody中的input添加点击事件
for (var i=0; i<cks.length; i++) {
cks[i].onclick = function () {
// 检查其他的input的选中状态
var flag = true;
for (var j=0; j<cks.length; j++) {
if (!cks[j].checked) { // 当有任何一个没有选中的时候,就更改标记为false
flag = false;
break;
}
}
ckAll.checked = flag; // 让ckAll的checked的状态和标记同步
};
}
</script>
</body>
</html>
```
### (三)创建元素
document.createElement('标签名') 创建元素节点
document.createTextNode('文本') 创建文本节点
document.createComment('注释内容') 创建注释节点
1、先创建对象
document.createElement("标签名子") 返回的是一个对象
2、追加到父元素
父元素.appendChild(对象) 追加到父元素的最后
父元素.insertBefore(对象,节点),在父元素内那个子元素前插入
案例一:点击动态创建列表
```
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
div {
300px;
height: 500px;
border: 1px solid #ff4400;
}
</style>
</head>
<body>
<input type="button" id="btn" value="添加列表">
<div id="dv"></div>
<script src="./common.js"></script>
<script >
// 动态的创建li列表,把列表加入到div中
var kunfu = ["天真", '爱你爱你', '回信息', '精华露', '遴选', '迷你地图'];
my$("btn").onclick = function () {
// 创建ul把ul加入到div中
var ulObj = document.createElement("ul");
my$("dv").appendChild(ulObj);
// 动态的创建li,加入到ul中
for (var i=0; i<kunfu.length; i++) {
// 创建Li对象
var liObj = document.createElement("li");
// 为创建好的li对象添加属性
liObj.innerHTML = kunfu[i];
// 追加到父元素ul
ulObj.appendChild(liObj);
// 为创建好的li添加鼠标事件,在循环中添加事件,要使用命名函数
liObj.onmouseover = mouseoverHandle; // 进入事件
liObj.onmouseout = mouseoutHandle; // 移出事件
}
function mouseoverHandle () {
this.style.backgroundColor = "red";
}
function mouseoutHandle () {
this.style.backgroundColor = "";
}
};
</script>
</body>
</html>
```
案例二:动态创建表格
```
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
div {
300px;
height: 500px;
border: 1px solid red;
}
</style>
</head>
<body>
<input type="button" id="btn" value="添加表格">
<div id="dv"></div>
<script src="./common.js"></script>
<script>
var tabList = [
{name: "土豆", href: "http://www.baidu.com"},
{name: "优酷", href: "http://www.baidu.com"},
{name: "京东", href: "http://www.baidu.com"},
{name: "天猫", href: "http://www.baidu.com"},
{name: "爱奇艺", href: "http://www.baidu.com"},
{name: "新浪", href: "http://www.baidu.com"}
];
// 点击创建表格
my$("btn").onclick = function () {
// 创建table对象
var tableObj = document.createElement("table");
tableObj.border = "1";
tableObj.cellPadding = "0";
tableObj.cellSpacing = "0";
my$("dv").appendChild(tableObj);
// 动态的创建tr对象
for (var i = 0; i < tabList.length; i++) {
var dtObj = tabList[i];
var trObj = document.createElement("tr");
tableObj.appendChild(trObj);
// 创建第一列对象
var td01 = document.createElement("td");
td01.innerText = dtObj.name;
trObj.appendChild(td01);
// 创建第二列对象
var td02 = document.createElement("td");
td02.innerHTML = "<a href="+dtObj.href+">"+dtObj.name+"</a>"; // 拼接第二行的a标签里的内容
trObj.appendChild(td02);
}
};
</script>
</body>
</html>
```
### (四)元素相关的操作方法
+ 追加子元素 父元素对象.appendChild("子元素");
+ 插入子元素 父元素对象.insertBefore(新元素,在那个元素之前插入);
+ 替换子元素 父元素对象.replaceChild(新元素,被替换的元素); 返回被替换的元素
+ 删除子元素 父元素对象.removeChild(删除的元素); 返回值就是删除的元素
+ 删除自身:子元素对象.remove(); 没有返回值
```
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
div {
300px;
height: 500px;
border: 1px solid red;
}
</style>
</head>
<body>
<input type="button" id="btn" value="添加一个">
<input type="button" id="btn2" value="替换一个">
<input type="button" id="btn3" value="删除一个">
<input type="button" id="btn4" value="删除全部">
<div id="dv"></div>
<script src="./common.js"></script>
<script >
var num = 0;
my$("btn").onclick = function () {
num ++;
var bObj = document.createElement("input");
bObj.type = "button";
bObj.value = "按钮" + num;
// my$("dv").appendChild(bObj);
// 把新元素插入到第一个子元素前面
my$("dv").insertBefore(bObj, my$("dv").firstElementChild);
};
my$("btn2").onclick = function () {
// 用第一个子元素替换最后一个子元素
my$("dv").replaceChild(my$("dv").firstElementChild, my$("dv").lastElementChild);
};
// 删除一个
my$("btn3").onclick = function () {
// 删除最后一个子元素
my$("dv").removeChild(my$("dv").lastElementChild);
};
// 删除全部
my$("btn4").onclick = function () {
// 判断父级有没有第一个子元素,如果删除完了,就为false
while (my$("dv").firstElementChild) {
my$("dv").removeChild(my$("dv").firstElementChild);
}
};
</script>
</body>
</html>
```
### (五)为同一个元素绑定多个相同的事件
由于 ele.onXXXX=function(){}一个元素上只能绑定一个事件,后面的会把前面的覆盖。这时this指向的是dom元素本身
#### 1、addEventListener()
程序this指向的是dom元素本身
```
<script >
/*
* 同一个元素有多个点击事件,如果用对象.onxx只会执行一个,其他的都被覆盖了
* 对象.addEventListener(参数1,参数2, 参数3) 火狐、谷歌支持,但是ie8不支持
* 参数1:事件的类型 事件的名子,字符串,没有on
* 参数2:事件处理函数
* 参数3:布尔类型 false 冒泡阶段 true 捕获阶段
* */
// 为同一个元素绑定多个相同的事件
my$("btn").addEventListener("click", function () {
console.log("小明很帅");
}, false);
my$("btn").addEventListener("click", function () {
console.log("小明很帅");
}, false);
my$("btn").addEventListener("click", function () {
console.log("小明很帅");
}, false);
my$("btn").addEventListener("click", function () {
console.log("小明很帅");
}, false);
</script>
```
#### 2、attachEvent()
程序指向是window
```
/*
* 对象.attachEvent(参数1, 参数2) 只有ie8可以用
* 参数1:事件类型 事件的名子 有on
* 参数2:事件处理函数
* */
my$("btn").attachEvent("onclick", function () {
console.log("小明很帅");
});
my$("btn").attachEvent("onclick", function () {
console.log("小明很帅");
});
my$("btn").attachEvent("onclick", function () {
console.log("小明很帅");
});
```
#### 3、兼容性代码
```
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input type="button" value="测试" id="btn">
<script src="./common.js"></script>
<script >
function addEventListener (element, type, fn) {
// 如果对象里有这个方法就为真,注意不加括号,加了括号是判断返回值是不是真
if (element.addEventListener) {
element.addEventListener(type, fn, false);
} else if (element.attachEvent) {
element.attachEvent("on"+type, fun); // attachEvent的type是带on的,所以要加上on
}else {
element["on"+ type] = fn;
}
}
// 测试
addEventListener(my$("btn"), "click", function () {
console.log("小明好帅");
});
</script>
</body>
</html>
```
#### 4、绑定事件的区别
总结绑定事件的区别:
addEventListener();
attachEvent();
+ 相同点:
1、都可以为元素绑定多个事件;
+ 不同点
1、方法名不一样;
2、参数的个数不一样,addEventListener三个参数,attachEvent两个参数;
3、浏览器的兼容性不一样,addEventListener 谷歌、火狐、ie11支持,ie8不支持;attachEvent 谷歌、火狐不支持,ie11不支持,ie8不支持;
4、this不同,addEventListener中的this是当前绑定事件的对象,attachEvent中的this是window;
5、addEventListener中的事件的类型(事件的名子)没有On,attachEvent中的事件的类型(事件的名子)有on;
### (六)为元素解绑事件
1、对象.onclick = function ----> 对象.onclick = null;
2、对象.addEventListener("事件名子不带On", 命名函数,false)----> 对象.removeEventListener("事件名子不带On", 命名函数,false)
3、对象.attachEvent("on事件名子",命名函数) ----> 对象.detachEvent("on事件名子",命名函数)
**注意点:**
1、用什么方式绑定事件,就应该用对应的方式解绑事件,不能互换。
对象.on事件名子 = 事件处理函数 (绑定事件)
对象.on事件名子 = null;
2、如果需要解绑,就要在绑定的时候使用命名函数,不能使用匿名函数;
**解绑的兼容性代码 **
```
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<input type="button" id="btn1" value="绑定">
<input type="button" id="btn2" value="解绑">
<body>
<script src="./common.js"></script>
<script >
// 绑定事件的兼容性代码
function addEventListener (element, name, fn) {
if (element.addEventListener) {
element.addEventListener(name, fn, false);
} else if (element.attachEvent) {
element.attachEvent("on"+name, fn);
} else {
element["on"+name] = fn;
}
}
// 解绑事件的兼容性代码
function removeEvent (element, name, fn) {
if (element.removeEventListener) {
element.removeEventListener(name, fn, false);
}else if (element.detachEvent) {
element.detachEvent("on"+name, fn);
} else {
element["on"+name] = null;
}
}
// 测试
function f1 () {
console.log("小明好帅");
}
function f2 () {
console.log("小花好漂亮");
}
addEventListener(my$("btn1"), "click", f1);
addEventListener(my$("btn1"), "click", f2);
// 注意,点击btn2时,是删除btn1的绑定事件
my$("btn2").onclick = function () {
removeEvent(my$("btn1"), "click", f1);
};
</script>
</body>
</html>
```
## 八、事件冒泡
事件冒泡:多个元素嵌套,有层次关系,这些元素都注册了相同的事件,如果里面的元素的事件触发了,外面元素的该事件也自动触发了。 父级的事件子级都继承,取消冒泡防止父级的事件贯穿到子级
阻止事件冒泡
1、window.event.cancelBubble = true; ie特有的,谷歌支持,火狐不支持
```javascript
<script >
onload = function () {
div1.onclick = function () { // 父元素
alert('div1')
}
div2.onclick = function (e) { // 子元素
console.log(e); // MouseEvent对象
var e = e || event
e.cancelBubble = true
alert('div2')
}
}
</script>
```
2、事件参数.stopPropagation(); 谷歌和火狐支持,ie8+;
```javascript
<script >
onload = function () {
div1.onclick = function () { // 父元素
alert('div1')
}
div2.onclick = function (e) { // 子元素
console.log(e); // MouseEvent对象
var ev = e || event
ev.stopPropagation()
alert('div2')
}
}
</script>
```
兼容代码:
事件的三个阶段:
1、事件捕获阶段:从外向内
2、事件目标阶段:最开始选择的目标
3、事件冒泡阶段:从里向外
4、触发顺序:先捕获后冒泡
5、focus、blur、change、submit、reset、select等事件不冒泡
事件参数.eventPhase 查看事件阶段
为元素绑定事件:
addEventListener("没有on的事件类型",事件处理函数,控制事件阶段的),事件触发的过程中,可能会出现 事件冒泡的效果,为了阻止事件冒泡:
window.event.cancelBubble=true;谷歌、ie8支持,火狐不支持;window.event就是一个对象,是ie的标准。
e.stopPropagation();阻止事件冒泡 ==>谷歌和火狐支持
window.event和e都是事件参数对象,一个是Ie的标准,一个是火狐的标准,事件参数e在ie8的浏览器中是不存在,此时用window.event来代替。`var event = e ||window.event;`
事件的阶段有三个,通过e.eventPhase这个属性可以知道当前的事件是什么阶段的,如果 这个属性的值是:1--捕获阶段 2--目标阶段 3--冒泡阶段
一般默认都是冒泡阶段,很少用捕获阶段
```
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input type="button" id="btn" value="按钮">
<script src="./common.js"></script>
<script >
// 为同一个元素绑定多个不同事件指向同一个事件处理函数
my$("btn").onclick = f1;
my$("btn").onmouseover = f1;
my$("btn").onmouseout = f1;
function f1 (event) {
switch (event.type) {
case "click":
alert("弹个框");
break;
case "mouseover":
this.style.backgroundColor="red";
break;
case "mouseout":
this.style.backgroundColor="green";
break;
}
}
</script>
</body>
</html>
=============取消冒泡兼容代码===============
function stopBubble(event){
if (event.stopPropagation){
event.stopPropagation();
}else {
event.cancelBubble = true;
}
}
```
## BOM 相关操作
### 一、BOM介绍
浏览器的顶级对象:window
页面中的顶级对象:document
页面中所有的内容都是属于浏览器的,页面中的内容也是window。因为页面中所有的内容都是window的,window可以省略。
### 二、BOM的对话框
window.alert("内容") 弹框 每个浏览器的样式不一样,只是在测试的时候用
window.prompt("请输入帐号") 让用户输入 每个浏览器的样式不一样,只是在测试的时候用
var ret = window.confirm("你确定退出吗") 点确定的时候有返回值 true false 每个浏览器的样式不一样,只是在测试的时候用
### 三、加载事件
只要页面加载完毕,这个事件就会触发----页面所有的内容,标签、属性、文本、包括外部引入的js文件。
```
window.onload = function () {
}; // window 可以省略,但是最好把js文件都放在最后
```
页面关闭后才触发的事件 谷歌不支持
```
window.onunload = function () {
};
```
页面关闭之前触发的 谷歌不支持
```
window.onbeforeunload = function () {
}
```
### 四、window 中的对象
#### (一)location对象
```
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<input type="button" value="显示效果" id="btn">
<body>
<script >
/*
* location对象 既是window的属性,又是对象
* */
// 属性
// 返回地址栏里#及后面的内容,超链接 锚点
console.log(location.hash);
// 返回主机名及端口号
console.log(location.host);
// 返回主机名
console.log(location.hostname);
// 返回文件的相对路径
console.log(location.pathname);
// 返回端口号
console.log(location.port);
// 返回协议类型
console.log(location.protocol);
// ?及后面的内容 搜索的内容
console.log(location.search);
// 方法
window.onload = function () {
document.getElementById("btn").onclick = function () {
// 设置跳转的页面的地址 能返回说明有历史记录
location.href = "http://www.baidu.com"; // 属性 比较重要
location.assign("http://www.baidu.com"); // 方法
// 重新加载 刷新
location.reload();
// 把地址栏的地址替换,进行跳转 没有后退,没有历史记录
location.replace("http://www.baidu.com")
};
};
</script>
</body>
</html>
```
#### (二)history对象
```
document.getElementById("btn2").onclick = function () {
window.history.forward(); // 相当于浏览器的前进 得有历史记录
};
document.getElementById("btn1").onclick = function () {
window.history.back(); // 相当于浏览器上的后退 得有历史记录
};
```
#### (三)navigator对象
```
// navigator 对象
// 通过platform 可以判断浏览器所在的系统平台类型
console.log(window.navigator.platform); // MacIntel
// 判断浏览器的类型
console.log(window.navigator.userAgent);
```
### 五、定时器
#### (一)setInterval() 反复
setInterval(参数1, 参数2)
参数1:函数
参数2:时间 毫秒 1000毫秒=1秒
返回值:就是定时器的ID;
执行过程:页面加载完毕后,过了1秒,执行一次函数的代码,又过了1秒,再执行函数,注意的是页面加载完毕,过了间隔的秒后,才执行定时器。
停止定时器:window.clearInterval(参数) 参数:要清理定时器id的值,所以内部的this指向的是window
```
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input type="button" value="停止定时器" id="btn">
<script >
// setInteval 每隔多少毫秒执行一次函数 参数1:函数 参数2:间隔时间,返回值是Inteval的id;
var timeId = setInterval(function () {
console.log(111);
}, 1000);
// clearInterval停止定时器,参数是定时器的Id;
document.getElementById("btn").onclick = function () {
clearInterval(timeId);
};
</script>
</body>
</html>
```
案例一:最简单的轮播图
```
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
* {
margin: 0;
padding: 0
}
ul {
list-style: none
}
img {
vertical-align: top
}
.box {
730px;
height: 454px;
margin: 100px auto;
padding: 5px;
border: 1px solid #ccc;
}
.inner {
730px;
height: 454px;
background-color: pink;
overflow: hidden;
position: relative;
}
.inner ul {
1000%;
position: absolute;
top: 0;
left: 0;
}
.inner li {
float: left;
}
.square {
position: absolute;
right: 10px;
bottom: 10px;
}
.square span {
display: inline-block;
16px;
height: 16px;
background-color: #fff;
text-align: center;
line-height: 16px;
cursor: pointer;
}
.square span.current {
background-color: orangered;
color: #fff;
}
</style>
</head>
<body>
<div class="box" id="box">
<div class="inner"><!--相框-->
<ul>
<li><a href="#"><img src="images/1.jpg" alt=""/></a></li>
<li><a href="#"><img src="images/2.jpg" alt=""/></a></li>
<li><a href="#"><img src="images/3.jpg" alt=""/></a></li>
<li><a href="#"><img src="images/4.jpg" alt=""/></a></li>
<li><a href="#"><img src="images/5.jpg" alt=""/></a></li>
<li><a href="#"><img src="images/6.jpg" alt=""/></a></li>
</ul>
<div class="square">
<span class="current">1</span>
<span>2</span>
<span>3</span>
<span>4</span>
<span>5</span>
<span>6</span>
</div>
</div>
</div>
<script >
// 1、获取最外面的div
var boxObj = document.getElementById("box");
// 2、获取相框和相框的宽度
var photoFrame = boxObj.firstElementChild;
var photoWidth = photoFrame.offsetWidth;
// 3、获取ul
var ulObj = photoFrame.firstElementChild;
// 4、获取所有的span
var spanObjs = photoFrame.lastElementChild.children;
// 5、循环所有的span标签,注册进入事件
for (var i=0; i<spanObjs.length; i++) {
// 添加自定义属性,用来解决事件发生时,i已经循环完毕,无法拿到索引值的问题
spanObjs[i].setAttribute("index", i);
// 为每个span注册鼠标进入事件
spanObjs[i].addEventListener("mouseover", f1, false);
}
// 鼠标进入事件的函数
function f1 () {
// 先干掉所有span的class类
for (var i=0; i<spanObjs.length; i++) {
spanObjs[i].removeAttribute("class");
}
// 再为当前的鼠标设置类
this.className = "current";
// 移动ul的位置 负的每张图片的宽度*索引
animation(ulObj, -this.getAttribute("index")*photoWidth);
}
function animation (element, goal) {
// 为了只产生一个定时器,先清理,再创建,还有一种办法就是加锁
clearInterval(element.intervalId);
// 为了防止点一按钮开辟一块内存空间,直接把变量当作对象的属性,因为对象相同的属性只有一个
// var intervalId = setInterval(function () {
element.intervalId = setInterval(function () {
// 获取div的当前位置
var current = element.offsetLeft; // offsetleft得到的是数字类型没有px;
// div每次移动多少
var step = 9;
step = goal>current?step:-step; // 目标大于当前就是正数的step 否则为负数的step
// 每次移动后的距离
current += step;
// 设置div的目标位置
if (Math.abs(goal-current)>Math.abs(step)) { //如果目标减去当前的绝对值大于移动位置的绝对值
element.style.left = current + "px"; // 设置移动的距离为增加或减少后的值
} else {
element.style.left = goal + "px"; // 否则让最后那点距离等于目标值。如 当前位置396 目标400 之间的差4小于step9,定时器就停了
// 而位置却没到,就直接让等于目标值就行了。
clearInterval(element.intervalId);
}
}, 20);
}
</script>
</body>
</html>
```
案例二 完整的轮播图
```
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style type="text/css">
* {
padding: 0;
margin: 0;
list-style: none;
border: 0;
}
.all {
500px;
height: 200px;
padding: 7px;
border: 1px solid #ccc;
margin: 100px auto;
position: relative;
}
.screen {
500px;
height: 200px;
overflow: hidden;
position: relative;
}
.screen li {
500px;
height: 200px;
overflow: hidden;
float: left;
}
.screen ul {
position: absolute;
left: 0;
top: 0px;
3000px;
}
.all ol {
position: absolute;
right: 10px;
bottom: 10px;
line-height: 20px;
text-align: center;
}
.all ol li {
float: left;
20px;
height: 20px;
background: #fff;
border: 1px solid #ccc;
margin-left: 10px;
cursor: pointer;
}
.all ol li.current {
background: #DB192A;
}
#arr {
display: none;
}
#arr span {
40px;
height: 40px;
position: absolute;
left: 5px;
top: 50%;
margin-top: -20px;
background: #000;
cursor: pointer;
line-height: 40px;
text-align: center;
font-weight: bold;
font-family: '黑体';
font-size: 30px;
color: #fff;
opacity: 0.3;
border: 1px solid #fff;
}
#arr #right {
right: 5px;
left: auto;
}
</style>
</head>
<body>
<div class="all" id='box'>
<div class="screen"><!--相框-->
<ul>
<li><img src="images/1.jpg" width="500" height="200"/></li>
<li><img src="images/2.jpg" width="500" height="200"/></li>
<li><img src="images/3.jpg" width="500" height="200"/></li>
<li><img src="images/4.jpg" width="500" height="200"/></li>
<li><img src="images/5.jpg" width="500" height="200"/></li>
</ul>
<ol>
</ol>
</div>
<div id="arr"><span id="left"><</span><span id="right">></span></div>
</div>
<script >
// 移动动画函数
function animation (element, goai) {
// 每次点击之前都先清除一下定时器
clearInterval(element.intervalId);
// 为了防止点一次产生一个定时器,让定时器做为element的属性,因为属性只有一个
element.intervalId = setInterval(function () {
// 获取当前的位置
var current = element.offsetLeft; // 得到是一个数字类型的,没有px
// 设置步长
var step = 9;
step = goai>current?step:-step; // 判断向左移动还是向右移动
// 移动后的位置
current += step;
// 设置left值 判断到没到位置,只需要判断,目标和当前位置的差是不是大于步长
if (Math.abs(goai-current)>Math.abs(step)) {
element.style.left = current + "px";
} else {
element.style.left = goai + "px"; // 因为步长的关系,还有一点距离,这个时候,让他直接等于目标的值就可以了
clearInterval(element.intervalId); // 清除定时器
}
}, 10);
}
// 第一步获取所有需要用的元素
var boxObj = document.getElementById("box"); // box
var phoneObj = boxObj.children[0]; // 相框
var phoneWidth = phoneObj.offsetWidth; // 相框的宽度
var ulObj = phoneObj.children[0]; // ul
var liObjs = ulObj.children; // li
var olObj = phoneObj.children[1]; // ol
var arrObj = document.getElementById("arr"); // 按钮
var leftObj = document.getElementById("left"); //左按钮
var rightObj = document.getElementById("right"); // 右按钮
var index = 0; // 创建一个全局的变量,用于ol中的索引和左右两边的按钮同步
// 第二步创建小按钮,根据li的个数 同时注册鼠标进入事件
for (var i=0; i<liObjs.length; i++) {
// 根据循环的次数创建ol中的li的个数
var olLiObj = document.createElement("li");
// 在每个li中添加自定义的属性,存储index值
olLiObj.setAttribute("index", i);
olObj.appendChild(olLiObj);
// Ol中的数字
olLiObj.innerHTML = i + 1; // 因为i是从0开始的
// 注册鼠标进入事件,两件事,一是改变背景颜色,二是移动图片
olLiObj.onmouseover = function () {
// 先干掉ol中li所有的背景
for (var j=0; j<olObj.children.length; j++) {
olObj.children[j].removeAttribute("class");
}
// 设置当前鼠标进来的li的背景颜色
this.className = "current";
// 获取鼠标进入后当前的li的索引值
index = this.getAttribute("index");
// 移动ul 负的索引值*相框的宽度,就是移动的位置
animation(ulObj, -index*phoneWidth);
};
// 注册鼠标进入事件
olLiObj.onmouseout = function () {
};
}
// 默认给第一个增加背景
olObj.children[0].className = "current";
// 克隆一个ul中的第一个li,加入到ul的最后
var copyLi = liObjs[0].cloneNode(true); // 谁调用这个方法就克隆谁
ulObj.appendChild(copyLi);
var intervalId = setInterval(rightClick, 2000); // 自动播放
// 鼠标进入box 显示左右的按钮
boxObj.onmouseover = function () {
clearInterval(intervalId);
arrObj.style.display = "block";
};
// 鼠标离开box 隐藏左右的按钮
boxObj.onmouseout = function () {
intervalId = setInterval(rightClick, 2000);
arrObj.style.display = "none";
};
// 右边按钮的点击
/*
* 当点击到最后一张图片的时候,也就是index=5的时候,直接让left的值等于0,并且把index值也设为0。
* */
function rightClick () {
if (index==liObjs.length-1) {
index = 0;
ulObj.style.left = 0 + "px";
}
index ++;
animation(ulObj, -index*phoneWidth);
// 如果index ==5的时候,此时显示第6张图片,内容是第一张的,第一小按钮有颜色
if (index == liObjs.length-1) {
// 第一个按钮的颜色设置上
olObj.children[0].className = "current";
// 第五个按纽的颜色干掉
olObj.children[olObj.children.length-1].removeAttribute("class");
} else {
// 其他地方就是排他功能
// 删除所有的
for (var i=0; i<olObj.children.length; i++) {
olObj.children[i].removeAttribute("class");
}
// 添加当前的
olObj.children[index].className = "current";
}
}
rightObj.onclick = rightClick;
// 左边按钮的点击
leftObj.onclick = function () {
// 当index是0的时候,直接跳到第六张图,同时把index也改成第六张图的
if (index == 0) {
index = liObjs.length-1;
ulObj.style.left = -index*phoneWidth + "px";
}
index --;
animation(ulObj, -index*phoneWidth);
// 设置小按钮的颜色
// 所有的先干掉
for (var i=0; i<olObj.children.length; i++) {
olObj.children[i].removeAttribute("class");
}
// 添加当前的
olObj.children[index].className = "current";
};
</script>
</body>
</html>
```
变速动画封装
```
<script >
/*
* zIndex的值一步到位就行,opacity:获取的时候,放大100倍,目标也放大100倍,赋值的时候缩小100倍,方便计算
* */
/**
* 获取任意对象属性
* @param element 对象
* @param attr 属性
* @returns {string} 属性的值
*/
function getStyle(element, attr) {
return window.getComputedStyle?window.getComputedStyle(element, null)[attr]:element.currentStyle[attr];
}
/**
* 能够根据json对象里的属性值发生变速动画的函数
* @param element 对象
* @param json 属性值的json对象
* @param fn 回调函数
*/
function moreAnimation (element, json, fn) {
clearInterval(element.intervalId);
element.intervalId = setInterval(function () {
var flage = true;
for (var attr in json) {
if (attr == "zIndex") { // 是不是zindex
element.style[attr] = json[attr];
}else if (attr == "opacity") { // 是不是opacitty
// 获取当前的透明度,扩大100倍
var current = getStyle(element, attr) *100; // 这里不用加parseInt,因为字符串*数字是按数字计算的不是拼接
// 获取目标透明度,扩大100倍
var target = json[attr] * 100;
// 步长
var step = (target-current)/10;
step = step>0?Math.ceil(step):Math.floor(step);
// 变化后的值
current += step;
// 设置 再缩小100倍
element.style[attr] = current / 100;
if (current != target) {
flage = false;
}
} else { // 普通属性
// 获取当前的位置
var current = parseInt(getStyle(element, attr));
// 获取目标的值
var target = parseInt(json[attr]);
// 步长
var step = (target-current)/10;
step = step>0?Math.ceil(step):Math.floor(step);
// 移动后的距离
current += step;
// 设置
element.style[attr] = current + "px";
if (current != target) {
flage = false;
}
}
}
if (flage) {
clearInterval(element.intervalId);
if (fn) {
fn();
}
}
}, 20)
}
// 测试
var btnObj = document.getElementById("btn");
var dvObj = document.getElementById("dv");
btnObj.onclick = function () {
moreAnimation(dvObj, {"width":400, "height":500, "left":500, "top":300, "zIndex":1000, "opacity":0.2}, function () {
moreAnimation(dvObj, {"width":40, "height":50, "left":50, "top":30, "zIndex":1000, "opacity":0.8})
})
}
</script>
```
#### (二)setTimeout()一次性
setTimeout(函数,时间);时间还是毫秒 返回值:还是该定时器的ID
执行过程:一次性的定时器
停止定时器:window.clearTimeoutl(参数) 参数:要清理定时器id的值,所以内部的this指向的是window
### (三)直接通过document获取属性
```
//获取body
console.log(document.body);//获取的是元素--标签
//获取title
console.log(document.title);//标签中的值
document.title="嘎嘎去"; // 设置
//获取html
console.log(document.documentElement);
```
## 九、一些属性和方法
### (一)offset系列中的属性
值都是数字类型,元素的样式属性是无法直接通过:对象.style,属性来获取的(因为样式在style标签中设置的,只有在style属性中设置的才能获取)
+ offsetLeft:距离左边位置的值,忽略自身是不是定位元素,如果父级是定位元素,那么距离是到父级的距离,如果不是定位元素,则是到外边框的距离。
+ offsetTop:距离上边位置的值 规则同上
+ offsetWidth:元素本身的宽,不管内容 元素的尺寸
+ offfsetheight:元素的高,不管内容 元素的尺寸
+ `offsetParent`返回最近的有定位的父级,如果没有,返回body,body.offsetParent返回null
#### 1、没有脱离标准的文档流
offsetLeft:父级元素margin+父级元素padding+父级元素的border+自己的margin
#### 2、脱离了标准文档流
主要是自己的left和自己的margin
### (二)scroll系列:卷曲--滚出去
+ scrollWidth 元素中内容的实际宽度,如果没有内容,就是元素的宽度
+ scrollHeight 元素中内容的实际的高,如果没有内容,就是元素的高
+ scrollTop:向上卷曲出去的距离,有了滚动条之后,从可视区无法看到部分的高度,也就是超出部分的高度
+ scrollLeft:向左卷曲出去的距离,有了滚动条之后,从可视区无法看到部分的宽度,也就是超出部分的高度
+ 兼容性的问题:(都是查看滚动条的滚动距离)
1. window.pageXOffset或者window.pageYOffset IE8及IE8以下不兼容;
2. document.body.scrollLeft/Top
document.documentElement.scrollLeft/Top (ie8和ie8以下的浏览器兼容,但是具体那个好使是没有定论的,有可能ie7上面的好使,也有可能下面的好使,有一个规律,在一个浏览器上只要上面的有值,那么下面的一定为零,反之下面的有值,上面的就没值,相加之后就可以兼容ie8和ie8以下的浏览器了)
```
获取向上卷曲出去的距离的兼容代码
var scrollTop = window.pageYoffset || document.documentElement.scrollTop ||document.body.scrollTop || 0;
也可以这样:
var scrollTop = window.pageYoffset || document.documentElement.scrollTop + document.body.scrollTop || 0;
获取向左卷曲出去的距离的兼容代码
var scrollLeft = window.pageXoffset || document.documentElement.scrollLeft ||document.body.scrollLeft || 0;
也可以这样:
var scrollLeft = window.pageXoffset || document.documentElement.scrollLeft + document.body.scrollLeft || 0;
```
### (三)client系列:可视区
+ clientWidth:可视区域的宽(没有边框)边框内部的宽度
+ clientHeight:可视区域的高(没有边框)边框内部的高度
+ clientLeft:左边边框的宽度
+ clientTop:顶部边框的宽度
+ clientX:可视区域的橫坐标
+ clientY:可视区域的纵坐标
查看可视区窗口的尺寸(可以看到的html代码,能看到的部分,不包括浏览器的导航栏,控制台等,页面的缩放会影响到可视区窗口的尺寸)
1. `window.innerWith/innerHeight`w3c标准的。 ie8及ie8以下不兼容
2. ie8及ie8以下兼容
`document.documentElement.clientWidth/clientHeight` 标准模式下(有`<!DOCTYPE html>`这行的就是标准模式,没有就是怪异模式/混杂模式 向后兼容),任意浏览器都兼容
`document.body.clientWidth/clientHeight` 适用于怪异模式下的浏览器
案例图片跟着鼠标 兼容代码的封装过程
```
var imgObj = document.getElementById("im");
// 火狐 谷歌
document.onmousemove = function (e) {
imgObj.style.left = e.clientX + "px";
imgObj.style.top = e.clientY + "px";
};
// 事件参数对象e只有谷歌和火狐有,ie8没有,ie8是window.event来代替e。但是在火狐里不支持window.event,谷歌支持
document.onmousemove = function () {
imgObj.style.left = window.event.clientX + "px";
imgObj.style.top = window.event.clientY + "px";
};
// 当有了滚动条之后,鼠标超过了可视区,如:y坐标(pageX/pageY)=滚动出去的距离+clientY;图标就不会跟鼠标了,而会找clientY/X;
document.onmousemove = function (e) { // 谷歌支持,火狐支持 ie8不支持window.enevt.pageX和window.event.pageY这两个属性
imgObj.style.left = e.pageX + "px";
imgObj.style.top = e.pageY + "px";
};
// ie8 支持
function getScroll () { // scroll兼容代码
return {
left:window.pageXOffset||document.body.scrollLeft||document.documentElement.scrollLeft||0,
top:window.pageYOffset||document.body.scrollTop||document.documentElement.scrollTop||0
}
}
document.onmousemove = function () {
imgObj.style.left = window.event.clientX + getScroll().left + "px";
imgObj.style.top = window.event.clientY + getScroll().top + "px";
};
===============================================================================
// 终极兼容版
// 用到了对象的一个方法调用另一个方法
obj = {
sayHi:function () {
console.log("sayHi方法");
this.eat(); // 调用eat方法
},
eat:function () {
console.log("eat方法");
}
};
/*
* 兼容了那些:
* 1、window.event和事件参数对象e的兼容
* 2、 clientX和clientY单独的使用的兼容代码
* 3、scrollleft和scrolltop的兼容代码
* 4、pageX,pageY和clientX+scrollLeft和clientY+scrollTop
* 封装在对象中
* */
var evt = {
// window.event和e的兼容
getEvent:function (evt) {
return window.event || evt; // 相当于var a = 0 || 1; a是1;如果有window.event,就用window.event,没有就用evt,
},
// 可视区域横坐标的兼容代码
getClientX:function (evt) {
return this.getEvent(evt).clientX;
},
// 可视区域纵坐标的兼容代码
getClientY:function (evt) {
return this.getEvent(evt).clientY;
},
// scrollLeft和scrollTop的兼容代码
/*页面向左卷曲出去的横坐标*/
getScrollLeft:function () {
return window.pageXOffset||document.body.scrollLeft||document.documentElement.scrollLeft||0;
},
/*页面向上卷曲出去的纵坐标*/
getscrollTop:function () {
return window.pageYOffset||document.body.scrollTop||document.documentElement.scrollTop||0;
},
// 相对于页面的横坐标(pagex或者是clientX+scrollLeft)
getPageX:function (evt) {
return this.getEvent(evt).pageX?this.getEvent(evt).pageX:this.getScrollLeft()+this.getClientX(evt);
},
// 相对于页面的纵坐标(pagey或者是clientY+scrollTop
getPageY:function (evt) {
return this.getEvent(evt).pageY?this.getEvent(evt).pageY:this.getscrollTop()+this.getClientY(evt);
}
};
// 测试
var imObj = document.getElementById("im");
document.onmousemove = function (e) {
imObj.style.top = evt.getPageY(e) + "px";
imObj.style.left = evt.getPageX(e) + "px";
};
```
### (四)查看元素的几何尺寸
`dom元素.getBoundingClientRect()`得到一个对象包,里面有所选择元素的几乎一切信息,和offset系列功能一样,没offset系列好用。
1. 兼容性很好;
2. 该方法返回一个对象,对象里面有left, top, right, bottom等属性。left和top代表该元素左上角的X和Y坐标,right和bottom代表元素右下角的X和Y坐标。
3. height和width属性老版本IE并未实现。
4. 返回的结果不是实时的;
### (五)让滚动条滚动
1. `window.scroll()`
2. `window.scrollTo()`
3. window.scrollBy()
4. 三个方法功能类似,用法都是将x,y坐标传入,即实现让滚动轮滚动到当前位置。
5. 区别:scrollBy()会在之前的数据基础之上做累加
## 十、闭包
### (一) [[scope]]
每个javascript的函数都是一个对象,对象中有些属性可以访问,但是有些不可以,这些属性仅供javascript引擎存取,scope就是其中一个,scope就是我们所说的作用域,其中存储了运行期上下文的集合,就是ao和go
### (二) 作用域链
scope中存储的运行期上下文对象的集合,这个集合呈链式链接,把这个链式连接叫做作用域链
### (三) 运行期上下文
当函数执行时,会创建一个称为执行期上下文的内部对象,(AO/GO),一个执行期上下文定义了一个函数的执行环境,函数执行时,对应的执行上下文都是独一无二的,多次调用函数,会创建多个执行上下文,当函数执行完毕,所产生的执行上下文被销毁
查找变量:从作用域链的顶端依次向下查找
当一个函数a被定义的时候 [[scope]]存储的第0位的是GO
当一个函数a被执行的时候 [[scope]]存储的第0位就变成了AO,第一位就变成了GO。
![](media/scope1.png)
![](media/scope2.png)
### (四) 闭包
当内部函数被保存到外部时,将会生成闭包,闭包会导致原有作用域不被释放,造成内存泄露
两个函数嵌套,把里面的函数保存到了外部全局,就行成了装饰,它保存着父级函数的作用域AO
给一个标签绑定一个事件,相当于是将事件外理函数保存在了js之外的标签属性上,那么也就行成了闭包,如果正好这个事件外理函数在一个循环的函数中,那么他就会保存着父级函数的AO,AO对象存储着变量的值,这个值会随着循环更新,停在循环最后一个值上,如果想要拿到循环时候的每个值,就需要在事件函数的外面加一个立即执行函数,这样事件函数就会存储立即执行函数的AO对象,而这个AO里的变量属性是每次跟着循环得到的值,这个值是不会变的
```
function createLi (n) {
var divObj = document.createElement("div");
var div = document.body.appendChild(divObj);
var ulObj = document.createElement("ul");
div.appendChild(ulObj);
for (var i=0;i<n;i++) {
var liObj = document.createElement("li");
ulObj.appendChild(liObj);
liObj.style.backgroundColor=i%2==0?"red":"yellow";
(function (j) {
liObj.onclick = function () {
alert(j);
}
}(i));
}
}
```
只要是运行期的上下文被保存到了函数的外部,都行成了闭包
```javascript
var obj = {};
function a () {
var aa = 123;
function b () {
console.log(aa)
}
obj.fun = b; // 这个b被保存到了函数外部,obj对象里,就形成了闭包
}
```
### (五)闭包的作用
* 实现共有变量
* 函数累加器
* 可以作缓存(存储结构)
* 实现封装,属性私有化
* 模块化开发,防止污染全局变量
```javascript
<script >
var name = 'bcd';
var init = (function () {
var name = 'abc';
function callName () {
console.log(name);
}
return function () {
callName();
}
}())
console.log(name); // bcd
init() // abc
</script>
```
## 十一、原型
定义:原型是function对象的一个属性,它定义了构造函数制造出的对象的公共祖先,通过该构造函数产生的对象,可以继承该原型的属性和方法,原型也是对象,没有属性和方法之前就是一个只有constructor属性值是函数本身的一个对象。
+ 利用原型的特点和概念可以提取公共属性。
```javascript
<script >
Car.prototype.height = 1400;
Car.prototype.lang = 4900;
Car.prototype.carName = "BMW";
function Car (color, owner) {
this.owner = owner;
this.color = color;
// this.height = 1400; // 可以把这些共有的属性提取到原型对象上,不必要每次创建一个对象,就添加一次
// this.lang = 4900;
// this.carName = "BMW";
}
var car1 = new Car('red', 'ww')
var car2 = new Car('green', 'bb')
</script>
```
+ 对象如何查看原型: 隐式属性`__proto__`
+ 对象如何查看对象的构造函数 constructor
+ 访问一个对象的属性时,先在基本属性中查找,如果没有,再沿着`__proto__`这条链向上找,这就是原型链。
+ 如何区分一个属性到底是基本的还是从原型中找到的呢?——hasOwnProperty,特别是在for…in…循环中,一定要注意
![原型](media/原型.png)
+ 原型的修改和删除,不能通过对象.属性的方式修改和删除,必须用`构造函数.prototype.属性`的方式修改,删除也不能使用`delete 对象.属性`的方式删除,要使用`delete 构造函数.prototype.属性`的方式删除。
```javascript
<script >
// 原型属性的修改
Person.prototype.lastName = 'uu';
function Person(name) {
this.name = name
}
var person = new Person('ww');
// 不能这样修改原型的属性
person.lastName = 'bb'; // 这样会在对象上增加一个属性lastName
Person.prototype.lastName = 'cc'; // 这样才会修改原型对象的属性
</script>
```
+ 另一种写法, 这种方式和函数.prototype.属性 = 值 修改对象,一种是直接修改引用值。
```javascript
构造函数.prototype = {
name="kk",
age=18
}
```
+ 绝大多数的对象最终都会继承自object.prototype,但是有一个例外,object.create(原型/null),使用null参数创建的对象是个例外。
总结:
- 任何函数都具有一个 `prototype` 属性,prototype里有`constructor`属性
- 构造函数的 `prototype` 对象默认都有一个 `constructor` 属性,指向 `prototype` 对象所在函数,可以修改
```javascript
<script >
function Person() {
}
function NewPerson() {
}
Person.prototype = {
constructor: NewPerson
};
var person = new Person()
console.log(person.constructor); //ƒ NewPerson() {}
</script>
```
- 通过构造函数得到的实例对象内部会包含一个指向构造函数的 `prototype` 对象的指针 `__proto__`
```javascript
<script >
Grand.prototype.lastName = 'grandProto'
function Grand() {
}
var grand = new Grand();
Father.prototype = grand
function Father(name) {
this.name = name
this.fourth = {
card1: 'vasia' // 对于引用值,可以使用son.fourth.card2 = 'jianshe' 这样进行修改
}
this.num = 100 // 不是引用类型的,通过son.num ++ ,这样的操作,不能修改,只能增加到自身
}
var father = new Father('father')
Son.prototype = father
function Son(hobbit) {
this.hobbit = hobbit
}
var son = new Son('somke')
</script>
```
- 所有实例都直接或间接继承了原型对象的成员
- Object.create(原型)
```javascript
<script >
Person.prototype.lastName = 'cc'
function Person() {
this.name = 'ww'
}
var bb = Object.create(Person.prototype) // 创建一个对象出来,创建出来的这个对象的原型就是括号里面写的对象。
console.log(bb.lastName);
</script>
========================
var demo = {
lastName: "小花"
};
var obj = Object.create(demo);
// 而这个obj对象是个空对象,但是他里面有个属性__proto__值是demo,他自身没有lastName属性的时候,就会去原型上去找。
// obj = {__proto__: demo}
```
- undefined,null没有原型
- call/apply 改变this指向 区别后面传的参数的形式不同。借用别人的函数,实现自己的功能
函数()=函数.call(参数1,参数2...) 参数1,改变this指向的对象,参数2以后正常的函数参数
```javascript
<script >
function Person(name, age) {
this.name = name;
this.age = age;
}
obj = {};
Person.call(obj, 'ww', 18);
console.log(obj); // {name: "ww", age: 18}
</script>
```
函数()=函数.apply(参数1,[参数2...]) 参数1,改变this指向的对象,函数的正常参数要放在一个数组中。
```javascript
function Person(name, age) {
this.name = name;
this.age = age;
}
obj = {};
Person.apply(obj, ['ww', 18]);
console.log(obj);
```
区别:
call:需要把实参按形参的个数传进去
applay:需要传一个arguments
应用:
```
// call、apply
function Father (name, age) {
this.name = name;
this.age = age;
}
function Son (name, age, phone, email) {
Father.call(this, name, age);
this.phone = phone;
this.email = email;
}
var son = new Son("小花", 13, 1443322323, "wlrjwr@163.com")
```
## 十二、继承
### 传统形式:原型链继承
过多的继承了没用的属性
```javascript
<script >
Grand.prototype.lastName = 'ji';
function Grand(){}
var grand = new Grand();
Father.prototype = grand;
function Father () {
this.name = 'hehe'
}
var father = new Father();
Son.prototype = father;
function Son () {}
var son = new Son()
</script>
```
### 借用构造函数
不能继承借用构造函数的原型
每次构造函数都要多走一个函数
```javascript
function Person (name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
function Student (name, age, sex, grade) {
Person.call(this, name, age, sex);
this.grade = grade;
}
var student = new Student('ww', 12, '女', 'bb')
```
### 共享原型
不能随便改动自己的原型
```javascript
Father.prototype.lastName = '张';
function Father () {}
function Son () {}
Son.prototype = Father.prototype
var son = new Son()
```
当Son的原型想增加东西的时候,势必造成Father的原型上也增加了东西
### 圣杯继承
```javascript
// 圣杯模式 普通版
Father.prototype.lastname = "eee";
function Father (name) {
this.name = name;
}
var father = new Father("mjc");
function Son (age) {
this.age = age;
}
function Inherit (Target,Origin) {
function F() {};
F.prototype = Target.prototype;
Origin.prototype = new F(); // 注意这行不能在F的原型改变之前,一定要在后面。原因是new的时候的原型一定是要改变后的原型
Origin.prototype.constructor = Origin; // 改变原型的constructor指向自身的构造函数,不改变的话,构造函数会指向父类
Origin.prototype.uber = Origin.prototype; // 找到超类,究竟继承自谁,为了信息的一个存储
}
Inherit(Father, Son);
var son = new Son(18);
// 圣杯模式 雅虎版
Father.prototype.lastname = "eee";
function Father (name) {
this.name = name;
}
var father = new Father("mjc");
function Son (age) {
this.age = age;
}
var inherit = (function () {
var F = function () {}; // 闭包的属性私有化,不想让外界访问这个属性
return function (Target,Origin) {
F.prototype = Target.prototype;
Origin.prototype = new F();
Origin.prototype.constructor = Origin;
Origin.prototype.uber = Origin.prototype;
}
}());
inherit(Father, Son);
var son = new Son(18);
```
## 十一、this指向
全局作用域里this指向window
call/apply可以改变函数运行时的this指向
```javascript
function test() {
console.log(this)
}
test.call({name:'小花'});
// 相当于是
test() -- > AO {
arguments: {},
this: window --> {name:'小花'}
}
1、预编译: this --> window
2、谁调用的this 指向谁
var name = 'window';
var obj = {
name: 'obj',
say: function (){
console.log(this.name)
}
}
obj.say() // obj
obj.say.call(window); // window
var fun = obj.say
fun() // window
fun.call(obj) // obj
3、call apply 改变指向
4、全局this --> window
```
普通函数中的this:window 相当于是window.函数(),(函数预编译过程中,this指向window)
对象方法中的this: 当前的实例对象;
定时器方法中的this:也是window
构造函数中的this:当前的实例对象
原型对象方法中的this:当前的实例对象;
jquery为什么能连续的点方法或属性,因为return this。
当new一个函数时,相当于是在函数里创建了一条下面的语句
`var this = Object.create(函数.prototype)`准确的来说是在这个对象里添加了this对象,而这个this对象里添加了`__proto__:函数.prototype`。
## 十二、try catch
不抛出错误,后续代码继续执行(try里的代码出错的那行以下不会再执行),把错误放到catch的参数e里。
```javascript
try{
console.log('a');
console.log(b); // 出错行
console.log('c'); // 不执行
} catch(e){
console.log(e.name); // 错误名称
console.log(e.message)
}
console.log('d') // 会执行
```
e.name 的六种对应的信息:
1、EvalError:eval()的使用与定义不一致;
2、RangeError:数值越界
3、ReferenceError: 非法或者不能识别的引用数值 ,变量、函数 没声明就使用,
4、SyntaxError:发生语法解析错误;
5、TypeError:操作类型错误;
6、uRLError:URL处理函数使用不当;
## 十三、es5严格模式
不使用严格模式的情况下是:基于es3的+es5的新增方法,使用的,出现冲突,以es3的方法为主。
严格模式就是出现冲突的时候以es5的方法为主
"use strict" 写在逻辑的最顶端。全局的。局部的,写在函数代码的最顶端
不再兼容e3的一些不规范的语法,使用全新的es5规范
两种用法:
1、全局严格模式
2、局部函数内严格模式(推荐)
就是一行字符串,不会对不兼容严格模式的浏览器产生影响。
严格模式下,不支持with(改变作用域链,改变AO), arguments.callee, func.caller 变量赋值前必须声明,(隐式全局变量不能用了)局部this必须被赋值(Person.call(null/undefined,赋什么就是什么),拒绝重复属性和参数。
```javascript
var obj = {
name: "obj"
}
var name = "window";
function test() {
var age = 123;
var name = 'scope';
with (obj){
console.log(name); // obj
console.log(age); // 123
}
}
==================
"use strict"
function Test() {
console.log(this) // 也就是预编译的时候,不再指向window而是指向了空
}
Test() // undefined
new Test() // test{}
Test.call({}) // {}
```
## 十三、小技巧工具方法
### 1、实现jquery连续调用方法
```javascript
<script >
// 方法的连续调用
var fanfa = {
smoke:function () {
console.log("抽烟有害健康");
return this;
},
drink:function () {
console.log('喝酒有害健康');
return this;
}
}
fanfa.smoke().drink();
</script>
```
## 2、自定义的typeof
```javascript
function myType(target) {
var ret = typeof(target);
template = {
"[object Array]": "arrery",
"[object Object]": "object",
"[object Number]": "number-object",
"[object Boolean]": "boolean-object",
"[object String]": "string-object"
};
if (target === null) {
return 'null';
}else if(ret == 'object') {
var str = Object.prototype.toString.call(target);
return template[str];
} else {
return ret;
}
}
```
### 自定义的数组去重方法
```javascript
// 数组的去重,思路:把数组的每一个元素放到对象中去,给一个随意的值,因为对象中的每个属性是不重复的,达到去重的效果
Array.prototype.unique = function () {
var temp = {},
arr = [],
len = this.length;
for (var i=1;i<len;i++) {
if (! temp[this[i]]) {
temp[this[i]] = 'abc';
arr.push(this[i]);
}
}
return arr;
}
```
## 性能
循环里添加匿名函数效率低,要改成命名函数,放在循环的外面,如果不是循环的方式添加事件,推荐使用匿名函数
异步加载js
js加载的缺点:加载工具方法没必要阻塞文档,js加载会影响页面效率,一旦网速不好,那么整个网站将等待js加载而不进行后续的渲染等工作。
有些工具方法需要按需加载,用到再加载,不用不加载
异步加载的三种方案:
1、defer异步加载,但是要等到dom文档全部解析完才会被执行,只有ie能用,也可以将代码写到内部
`script type="text/javascript" src="tools.js" defer="defer"></script>`
2、async w3c标准,异步加载,加载完就行,async只能加载外部脚本,不能把js写在script标签里。
`script type="text/javascript" src="tools.js" aysnc="aysnc"></script>`
3、执行时也不阻塞页面。
4、创建script,插入到dom中,加载完毕后callBack。
```javascript
demo.js
function test(){
console.log(a)
}
<script type="text/javascript">
var script = document.createElement('script');
script.type = "text/javascript";
script.src = "demo.js";
// 由于ie没有onload事件,但是是他有一个状态码的触发事件
script.onreadystatechange = function (){
if (script.readState == "complete" || script.readState == "loaded") {
test();
}
} else {
// safari chrome firefox opera
script.onload = function () {
test();
}
}
document.head.appendchild(script);
=========== 封装成函数============
function loadScript(url, callback){
var script = document.createElement('script');
script.type = "text/javascript";
if(script.readyState){
script.onreadystatechange = function (){
if (script.readState == "complete" || script.readState == "loaded") {
callback();
}
}
}else {
script.onload = function () {
callback();
}
}
script.src = url;
document.head.appendchild(script);
}
loadScript('demo.js', function (){
test();
})
```
JavaScript之js加载时间
在js加载开始的时候,浏览器会记录js执行的这段过程。
1.创建Document对象,开始解析web页面,解析HTML元素和他们的文本内容后添加Element对象和Text节点到文档中。这个阶段Document。readyState = "loading"。
2.遇到link外部css,创建线程加载,并继续解析文档。
3.遇到script外部js,并且没有设置async , defer ,浏览器加载,并阻塞,等待js加载完成并执行该脚本,然后继续解析文档
4.遇到script外部js,并且设置有async,defer 浏览器创建线程加载,并继续解析文档,对于async属性的脚本,脚本加载完成后立即执行(异步禁止使用docuemnt.write())。
5.遇到img标签等,先正常解析dom结构,然后浏览器异步加载src,并继续解析文档
6.当文档解析完成,document.readyState = "interactive";
7.文档解析完成后,所有设置有defer的脚本会按照顺序执行。
8..当文档解析完成之后,document对象触发DOMContentLoaded事件,这也标志着程序执行从同步脚本执行阶段,转化为事件驱动阶段
9.当所有saync的脚本加载完成并执行后,img等加载完成后,document.readyState = "complete" window对象触发load事件
10.从此,页面以异步响应方式处理用户输入,网络事件等。
```javascript
console.log(document.readyState);
document.onreadystatechange = function(){
console.log(document.readyState);
}
```
在这里我们说一下两个事件的区别:DOMContentLoaded和load
load事件我们知道,在window上有这个事件window.onload 这个事件是在dom文档完全渲染完毕之后才会触发,那么如果网速不好的时候,有那么一丁点的数据没有加载完成,这个事件就不会执行,这是一个非常脱浏览器后腿的事件。
而DOMContentLoaded则不同了,这个事件表示的是当Dom解析完毕之后执行,没有浪费浏览器运行效率,但是这个事件只能用addEventListener绑定。但这也不是个问题,所以,当我们在head标签里面写Javascript语句的时候用这个事件,千万不要用load了。