一、概述

1.语言的发展

纸带机—-机器语言

汇编语言—-符号语言

现代语言—-高级语言

2.JavaScript起源

1995年,其出现主要用于处理网页中前端验证。所谓前端验证指检查用户输入内容是否符合一定的规则。

例如:用户名的长度,密码长度

3.简史

  • JavaScript是由网景公司发明的,最初起名为linkscript,后来由于sum公司的介入更名为JavaScript
  • 1996年,微软公司在其最新的IE3浏览器中引入了自己对JavaScript的实现JScript
  • 于是,市场上存在两个版本的JavaScript,一是网景公司的JavaScript,二是微软公司的JScript
  • 为了确保不同的浏览器上运行的JavaScript标准一致,所以几个公司共同制定了js的标准名命名为ECMAScript

4.JavaScript实现

  • ECMAScript
  • DOM:文档对象模型
  • BOM:浏览器对象模型

5.JavaScript特点

  • 解释性语言(不需要编译)
  • 类似于C和JAVA语法结构
  • 动态语言
  • 基于原型的面向对象

6.案例:HelloWorld

警告框:alert();

页面输出:document.write();

控制台输出:console.log();

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8" />
  5. <title>案例:HelloWorld</title>
  6. </head>
  7. <body>
  8. <script type="text/javascript">
  9. // 控制浏览器弹出一个警告框
  10. alert("HelloWorld");
  11. // 在页面中输出一个内容
  12. document.write("HelloWorld");
  13. // 在控制台输出内容
  14. console.log("HelloWorld");
  15. </script>
  16. </body>
  17. </html>

二、使用

1.JavaScript编写位置

js/new_file.js

alert("我是外部的js代码");
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>JavaScript编写位置</title>
        <!--
            链接外部的js代码
            优点:可以在不同的页面中同时引用
                利用浏览器的缓存机制
        -->
        <script type="text/javascript" src="js/new_file.js"></script>

        <!--将js代码写到JavaScript标签中-->
        <script type="text/javascript">
            alert("我是JavaScript标签中的js");
        </script>


    </head>
    <body>
        <!--
            作者:offline
            时间:2021-05-25
            描述:将js写在标签属性中
                onclick()点击事件,点击按钮,弹出警示框
                写在标签属性中,属于结构与行为耦合,不方便维护,不推荐使用
        -->
        <button onclick="alert('点击事件')">按钮</button>

        <!--
            作者:offline
            时间:2021-05-25
            描述:将js代码写在a标签的href属性中
                点击超链接,执行js代码
        -->
        <a href="javascript:alert('跳转');">我是超链接1</a>
        <a href="javascript:;">我是超链接2</a>

</html>

2.js基本语法

  1. js中严格区分大小写
  2. js中每一条语句以分号结尾
    如果不写分号,浏览器会自动添加,会消耗一些系统资源
    有时候,浏览器会加错分号,在开发中必须写分号
  3. js会忽略多个空格或换行

3.字面量和变量

字面量:都是一些不可改变的值
例:1,2,3,4,5
字面量可以直接使用
变量:变量可以用来保存字面量,而且变量的值可以任意改变

<!DOCTYPE html>
<html>

    <head>
        <meta charset="utf-8" />
        <title>字面量和变量</title>
    </head>

    <body>

        <script type="text/javascript">
//            声明变量

//            方法一:先声明,再赋值
            var a;
            a = 333;
            console.log(a);

//            方法二:声明并复制
            var b=666;
            console.log(b); 

        </script>
    </body>

</html>

4.标识符

  • 在js中所有的可以由我们自主命名的都可以称作标识符
  • 例如:变量名、函数名、属性名
  • 命名标识符的规则:
    标识符中可以包含字母、数字、_、$
    标识符不能以数字开头
    标识符不能是ES中的关键字和保留字
    标识符一般采用驼峰命名法
    首字母小写,每个单词的开头字符大写,其余小写
  • js底层保存标识符时实际上时采用的Unicode编码,理论上,所有utf-8 中含有的内容都可以作为标识符
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>标识符</title>
    </head>
    <body>
        <script type="text/javascript">
            var 锄禾日当午=888;
            console.log(锄禾日当午); 
        </script>
    </body>
</html>

5.数据类型

JavaScript - 图1

js中的变量都是保存在栈内存的

基本数据类型的值直接在栈内存中存储

值与值之间都是独立存在,修改一个变量不会影响其他的变量

5.1字符串String

  • 在js中字符串需要用引号引用起来(单引号和双引号都可以)
  • 引号不能嵌套

转义字符:用\进行转义
\ “表示”
\ ‘表示’
\n表示换行
\t制表符
\表示\

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>字符串String</title>
    </head>
    <body>
        <script type="text/javascript">
//            声明
            var s="今天是周六!";
            console.log(s); 

//            转义字符
            var z="今天\n多云转晴";
            console.log(z);
        </script>
    </body>
</html>

5.2数值Number

js中所有数值都是Number类型,包括整数和浮点数

typeof检查变量类型
语法:typeof 变量

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>数值Number</title>
    </head>
    <body>
        <script type="text/javascript">
//            数值
            var  a=123;
            console.log(a);

//            字符串
            var b="123";
            console.log(b);

//            typeof()检查变量类型
            console.log(typeof a);
            console.log(typeof b);
        </script>
    </body>
</html>
  • js中可以表示数字的最大值
    Number.MAX_VALUE
    1.7976931348623157e+308
    超过最大值会返回一个Infinity(正无穷)
  • js中可以表示数字的最小值
    Number.MIN_VALUE
    5e-324
    超过最小值会返回一个-Infinity(负无穷)
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>数值Number</title>
    </head>
    <body>
        <script type="text/javascript">

//            最大值
            console.log(Number.MAX_VALUE);

//            最小值
            console.log(Number.MIN_VALUE);

//            Infinity(正无穷)
            var c=Number.MAX_VALUE*Number.MAX_VALUE;
            console.log(c);
            console.log(typeof c);

//            -Infinity(负无穷)
            var e=Number.MIN_VALUE*Number.MIN_VALUE;
            console.log(e);
            console.log(typeof e);
        </script>
    </body>
</html>

NaN是一个特殊的数字,表示Not A Number

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>数值Number</title>
    </head>
    <body>
        <script type="text/javascript">

//            NaN
            var d="aaa"*"bbb";
            console.log(d);
            console.log(typeof d);

        </script>
    </body>
</html>

js中整数运算基本可以保持精确
浮点运算,可能得到一个不精确的结果

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>数值Number</title>
    </head>
    <body>
        <script type="text/javascript">

            //js中整数运算基本可以保持精确
            var f=333+666;
            console.log(f);
            //浮点运算,可能得到一个不精确的结果
            var g=0.1+0.2;
            console.log(g);

        </script>
    </body>
</html>

5.3布尔值Boolean

两个值,用来做逻辑判断
true真
false假

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>布尔值Boolean</title>
    </head>
    <body>
        <script type="text/javascript">
            var t=true;
            console.log(typeof t);
        </script>
    </body>
</html>

5.4空值Null

用typeof检查类型时返回Object

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>空值Null</title>
    </head>
    <body>
        <script type="text/javascript">
            var n=null;
            console.log(typeof n);
        </script>
    </body>
</html>

5.5未定义Undefined

声明一个变量,不赋值,返回Undefined
用typeof检查类型时返回Undefined

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>未定义Undefined</title>
    </head>
    <body>
        <script type="text/javascript">
            var s;
            console.log(s);

            var u=undefined;
            console.log(typeof u);
        </script>
    </body>
</html>

5.6对象Object

6.强制类型转换

指将一个数据类型强制转换成其他数据类型
类型转换指,将其他的数据类型转换为String、Number、Boolean

6.1.其他数据类型转换成String

方法一:
调用被转换数据类型的toString()方法
该方法不会影响到原变量,它会将转换的结果返回
null和undefined没有toString()方法,如果调用,会报错
“Uncaught TypeError: Cannot read property ‘toString’ of null”

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>强制类型转换</title>
    </head>
    <body>
        <script type="text/javascript">
                //6.1.其他数据类型转换成String
                //方法一:
                //调用被转换数据类型的toString()方法
                //该方法不会影响到原变量,它会将转换的结果返回
                //null和undefined没有toString()方法,如果调用,会报错

                var a=123;
                a=a.toString();
                console.log(typeof a); 

                var b=true;
                b=b.toString();
                console.log(typeof b); 

                var c=null;
                c=c.toString();
                console.log(typeof c); 
                // "Uncaught TypeError: Cannot read property 'toString' of null"

        </script>
    </body>
</html>

方法二:
调用String()函数,并将被转换的数据作为参数传递给函数
使用String()函数做强制类型转换时:
对于Number和Boolean实际上就是在调用toString()方法
对于null和undefined,不会调用toString()方法
它会将null直接转换为“null”,将undefined直接转换为“undefined”

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>强制类型转换</title>
    </head>
    <body>
        <script type="text/javascript">
            //方法二:
            //调用String()函数,并将被转换的数据作为参数传递给函数

            var d=123;
            d=String(d);
            console.log( typeof d);

            var e=null;
            e=String(e);
            console.log( typeof e); 

        </script>
    </body>
</html>

6.2.其他数据类型转换成Number

方法一:使用Number()函数

  • 字符串—->数字

    • 如果是纯数字的字符串,则直接转换成数字
    • 如果字符串有非数字的内容,则转换为NaN
    • 如果字符串是一个空串或是一个全是空格的字符串,则转换为0
  • 布尔—->数字

    • true 1
    • false 0
  • null—->数字 0
  • undefined—->数字 NaN

方式二:针对于字符串

parseInt() 把一个字符串的有效整数内容读出来,然后转换为Number

parseFloat() 把一个字符串的有效浮点数内容读出来,然后转换为Number

6.3 其他进制的数字

十六进制 ,以0x开头

八进制,以0开头

二进制,以0b开头(不是所有的浏览器都支持)

6.4 其他数据类型转换成boolean

调用Boolean()函数将a转换成布尔值

  • 数字—->布尔

    • 除了0和NaN,其余的都是true
  • 字符串—->布尔

    • 除了空串,其余都是true
  • null和undefined转换成false
  • 对象转换成true

7.运算符

运算符也叫操作符。

通过运算符可以对一个或多个值进行运算,并获取运算结果

7.1 typeof

typeof运算符值的类型以字符串形式返回

7.2 算术运算符

算术运算符 含义
+ 加法
- 减法
* 乘法
/ 除法
%
  • 对非number类型的值进行运算时,会将值转换为number类型进行运算

    • 任何值和NaN做运算,都得NaN
    • 任何值做- * / 运算时都会自动转换成Number
  • 加法

    • 字符串和字符串做加法运算,则会拼串
    • 任何的值和字符串做加法运算,都会转换成字符串,然后和字符串做拼串操作

7.3 一元运算符

只需要一个操作数

一元运算符 含义
+ 正号
- 负号

对于非number数值,他会转换成number,然后再运算

可以对一个其他的数据类型使用+,将其转换成number,与number()原理一样

7.4 自增自减运算符

  • 自增++

    • 通过自增可以使变量在自身的基础上加1
    • ++a先加再使用
    • a++先使用在加
  • 自减—

    • 通过自增可以使变量在自身的基础上减1
    • —a先减再使用
    • a—先使用在减

7.5 逻辑运算符

逻辑运算符 含义
$$
||
    • 如果对布尔值进行取反,则会将其转换为布尔值,然后再取反,可以利用这一特征,来将一个其他类型转换为布尔值
    • 两个值中,都是true,返回true
    • 只要有一个false,返回false
    • 短路法则:如果第一个值为false,不会看第二个值
    • 两个值中只要有一个true,就返回true
    • 两个都为false,才返回false
    • 短路法则:如果第一个为true,不会看第二个值;第一个为false,看第二个值
  • 非布尔值的与和或运算

    • 或将其先转换为布尔值,然后再运算,并且返回原值
    • 与运算:如果俩个值为true,返回后边的值;如果两个值有false,则返回靠前的false的值
    • 或运算:如果第一个值为true,则返回第一个值;如果第一个值为false,则返回第二个值

7.6 赋值运算符

赋值运算符 含义
= 赋值
+= a=a+2等价于a+=2
-= a=a-2等价于a-=2
*= a=a2等价于a * =2
/= a=a/2等价于a/=2
%= a=a%2等价于a%=2

7.7 关系运算符

通过关系运算符可以比较两个值的大小关系

如果关系成立返回true,不成立返回false

关系运算符 含义
> 大于
>= 大于等于
< 小于
<= 小于等于
  • 非数值的情况

    • 对于非数值比较时,会将其转换成数字然后再比较
    • 如果符号两边都是字符串,不会将其转换为数字比较,会分别比较字符串字符的Unicode码

      • 在字符串中使用转义字符输入Unicode:\u四位编码
      • 在网页中使用:&#编码;(编码为十进制)

7.8 相等运算符

相等运算符 含义
== 相等,会对变量进行自动类型转换
!= 不等,会对变量进行自动类型转换
=== 全等,不会对变量进行自动类型转换
!== 不全等,不会对变量进行自动类型转换
  • undefined衍生自null,这两个做相等判断时,会返回true
  • NaN不和任何值相等,包括他本身

    • isNaN()函数判断一个值是否是NaN

7.9 条件运算符(三元运算符)

条件表达式?语句1:语句2

首先对条件表达式进行求值

该值为true,执行语句1,并返回执行结果

该值为false,执行语句2,并返回执行结果

7.10 逗号运算符:

使用“,”可以分割多个语句,一般在声明多个变量时使用

7.11 优先级

JavaScript - 图2

8 代码块

使用{}为语句分组

要不都执行,要不都不执行

代码块的后边不需要写分号

代码块的内容,在外部是完全可见的

三、流程控制语句

从上到下一行一行执行

1.条件判断语句

1.1 if语句

if(条件表达式)
    语句;

if语句只能控制紧随其后的语句

如果希望if能控制多个语句,将多个语句放在代码块中

if(条件表达式){
    语句;
}else{
    语句;
}
if(条件表达式){
    语句;
}else if(条件表达式){
    语句;
}else{
    语句;
}

该语句中,只有一个代码块被执行,一旦执行了,则直接结束该语句

案例

prompt()函数 弹出一个带有文本的提示框

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <script type="text/javascript">
            var score=prompt("请输入成绩:");
            if (score==100) {
                alert("A!!");
            } else if(score>=90&&score<=99){
                alert("B!!");
            }else if(score>=80&&score<=89){
                alert("C!!");
            }else if(score>=70&&score<=79){
                alert("C!!");
            }else{
                alert("E!!!");
            }
        </script>
    </body>
</html>

2.条件分支语句(switch语句)

switch (条件表达式){
    case 表达式:
        语句;
        break;
    case 表达式:
        语句;
            break;
    default:
            语句;
            break;
}

break跳出当前switch语句

3.循环语句

while语句

初始化变量
while (条件表达式){
    语句;
    更新表达式(每次更新初始化变量);
}

条件表达式为true的循环时死循环(慎用)

可以使用break终止循环

do…while()语句

初始化变量
do{
    语句;
    更新表达式(每次更新初始化变量);
}while(条件表达式);

while与do…while的区别:

while先判断后执行

do...while先执行后判断,至少会执行一次

for语句

for (①初始化表達式,②条件表达式,④更新表达式) {
    ③语句;                
}

执行流程:

①执行初始化表达式,初始化变量(初始化表达式只会执行一次)

②执行条件表达式,判断是否执行

如果为true,则执行语句③

如果为false,终止循环

④执行更新表达式,更新表达式执行完毕后继续重复②

for循环中的三个部分都可以省略,也可以写在外部

var i=1;
for (;i<3;i++) {
    alert(i);
}

如果在for循环中不写任何表达式,只写两个;会变成死循环。

for (;;) {

}

关键字break

跳出当前语句

不能在if语句中使用break和continue

label:循环语句

使用break语句时,可以在break后跟一个label,这样break将会结束指定的循环,而不是最近的

关键字continue

跳过当前循环,

四、对象

在js中创建一个人的信息。如果使用基本数据类型,所创建的变量都是独立的,不能成为整体

对象属于一种复合的数据类型,在对象中可以保存多个不同数据类型的属性

1 对象的分类

1.1 内建对象

由ES标准中定义的对象,在任何的ES的实现中都可以引用

比如:Math、String、Number、Boolean、Function、Object

1.2 宿主对象

由js运行环境提供的对象,目前来讲主要指由浏览器提供的对象

比如:BOM、DOM

1.3 自定义对象

由开发人员自己创建的对象

2 对象的基本操作

2.1 创建对象及属性

2.1.1 创建对象

var obj=new Object();

使用new关键字调用的函数,是构造函数constructor

构造函数是专门用来创建对象的函数

对象.属性名=属性值;

2.1.2 设置属性

在对象中保存的值称为属性

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <script type="text/javascript">
             var obj=new Object();
             obj.name="张三";
             obj.age=18;
             console.log(obj);

        </script>
    </body>
</html>

JavaScript - 图3

属性

对象的属性名不强制要求遵守标识符的规范

如果使用特殊的属性名,不能采用“.”的方式,使用以下方式

对象[“属性名”]=属性值

使用==[]操作属性,更加灵活。[]==可以传递一个变量

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <script type="text/javascript">
             var obj=new Object();
             obj.name="张三";
             obj.age=18;
             obj["1213"]="学号";
             console.log(obj);     
        </script>
    </body>
</html>

JavaScript - 图4

属性值

js的属性值可以是任意的数据类型,也可以是对象,也可以是函数

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <script type="text/javascript">
             var obj=new Object();
             obj.name="张三";
             obj.age=18;

             var obj2=new Object();
             obj2.name="李四";

             obj.test=obj2;
             console.log(obj);     
        </script>
    </body>
</html>

JavaScript - 图5

in运算符

检查一个对象是否含有制定的属性

如果有则返回TRUE,没有则返回FALSE

语法:”属性名” in 对象

2.1.3 读取属性

对象.属性名

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <script type="text/javascript">
             var obj=new Object();
             obj.name="张三";
             obj.age=18;
             console.log(obj.age);     
        </script>
    </body>
</html>

如果读取对象中没有的属性,不会报错而是返回unidefined

2.1.4 修改属性

对象.属性名=新值;

2.1.5 删除属性

delete 对象.属性名

2.2 基本和引用数据类型

js中的变量都是保存到栈内存的

基本数据类型的值直接在栈内存中存储

值与值之间是独立存在的,修改一个变量不会影响其他的变量

对象是保存到堆内存的,每创建一个新的对象,就会在堆内存中开辟出一个新的空间,而变量保存的是对象的内存地址(对象的引用)

如果两个变量保存的是同一个对象引用,当一个通过一个变量修改属性时,另另一个也会受到影响

当比较两个基本数据类型的值时,就是比较值

比较两个引用数据类型时,他是比较的对象的内存地址,如果两个对象一模一样,但是地址不同,返回FALSE

2.3 对象字面量

//        使用字面量创建一个对象
        var obj1={};
        obj1.name="张三";
        console.log(obj1);

使用字面量创建对象时,可以直接制定对象中的属性

语法:{属性名:属性值,属性名:属性值}

//        使用字面量创建对象时,可以直接制定对象中的属性
var obj2={
    name:"张三",
    age:18
};
console.log(obj2);

对象字面量的属性名可以加引号也可以不加引号,建议不加

如果要使用一些特殊的名字,则必须加引号

3 函数function

函数function也是一个对象

函数中可以封装一些功能,在需要的时候可以执行这些功能

函数中可以保存一些代码在需要的时候调用

使用typeof检查一个函数对象时,会返回function

3.1 创建函数对象

3.1.1使用构造函数创建函数对象

//创建函数对象
var fun1=new Function("console.log('我是函数');");
//调用函数对象
fun1();

封装到函数中的代码不会立即执行

函数中的代码会在函数调用的时候按顺序执行

3.1.2使用函数声明创建函数对象

语法:

function 函数名([形参1,形参2…]){

}

多个形参之间使用”,”分割,声明形参就相当于在函数内部声明对应变量,但是不赋值

var fun2=function(a,b){
    console.log(a+b);
 };
 fun2(6,4);

调用函数值时,解析器不会检查实参的类型和数量,多余的实参不会被赋值

如果实参的数量少于形参的数量,则没有对应实参的形参将是undefined

3.2 函数返回值

可以使用return作为函数的返回值

返回值可以是任意数据类型,也可以是一个对象,也可以是一个函数

语法:return 值

function sum(a,b,c){
    var aa=a+b+c;
    return aa;
};
var re=sum(5,4,6);
console.log(re);

return后得语句不执行

如果return语句后面不跟任何值就相当于返回一个undefined

如果函数中不写return,则也会返回undefined

function sum(a,b,c){
    var aa=a+b+c;
    return aa;
};
console.log(sum);
console.log(sum(5,4,6));

sum:函数对象,相当于直接使用函数

sumsum(5,4,6):调用函数,相当于使用函数的返回值

JavaScript - 图6

3.3立即执行函数

立即执行函数:函数定义完,立即被调用。

立即执行函数往往只会执行一次

(function(){
    alert("我是立即执行函数");
})();

3.4调用对象的方法

函数也可以称为对象的属性

如果一个函数作为一个对象的属性保存,那么我们称这个函数是这个对象的方法

调用函数就说调用对象的方法

var obj=new Object();
    obj.name="张三";
    obj.age=18;
    obj.sayName=function(){
    console.log(obj.name+obj.age);
};

obj.sayName();

3.5.枚举对象中的属性

语法:
for(var 变量 in 对象){

}

for…in语句 对象中有几个属性,循环体就会执行几次
每次执行时,会将对象中的一个属性的名字赋值给变量

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script type="text/javascript">

            var obj = {
                        name:"孙悟空",
                        age:18,
                        gender:"男",
                        address:"花果山"
                     };

            for(var n in obj){
                console.log("属性名:"+n);
                console.log("属性值:"+obj[n]);
            }        
        </script>
    </head>
    <body>
    </body>
</html>

3.6 作用域(Scope)

  • 作用域指一个变量的作用的范围,在JS中一共有两种作用域:

    • 1.全局作用域

      • 直接编写在script标签中的JS代码,都在全局作用域
        
  - 

全局作用域在页面打开时创建,在页面关闭时销毁

  - 

在全局作用域中有一个全局对象window,它代表的是一个浏览器的窗口,它由浏览器创建我们可以直接使用

  - 

在全局作用域中:

     - 创建的变量都会作为window对象的属性保存
     - 
          创建的函数都会作为window对象的方法保存
  - 

全局作用域中的变量都是全局变量,在页面的任意的部分都可以访问的到

  • 2.函数作用域
    

3.7 函数的声明提前

  • 变量的声明提前

  • 使用var关键字声明的变量,会在所有的代码执行之前被声明(但是不会赋值)

  • 但是如果声明变量时不适用var关键字,则变量不会被声明提前

  • 函数的声明提前

    • 使用函数声明形式创建的函数 function 函数(){}
    • 它会在所有的代码执行之前就被创建,所以我们可以在函数声明前来调用函数
    • 使用函数表达式创建的函数,不会被声明提前,所以不能在声明前调用

3.8 函数作用域

  • 调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁
  • 每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的
  • 在函数作用域中可以访问到全局作用域的变量,在全局作用域中无法访问到函数作用域的变量
  • 当在函数作用域操作一个变量时,它会先在自身作用域中寻找,如果有就直接使用,如果没有则向上一级作用域中寻找,直到找到全局作用域,,如果全局作用域中依然没有找到,则会报错ReferenceError
  • 在函数中要访问全局变量可以使用window对象

  • 在函数作用域也有声明提前的特性,

    • 使用var关键字声明的变量,会在函数中所有的代码执行之前被声明
    • 函数声明也会在函数中所有的代码执行之前执行

3.9 this

解析器在调用函数每次都会向函数内部传递进一个隐含的参数,这个隐含的参数就是this,this指向的是一个对象,这个对象我们称为函数执行的 上下文对象,

根据函数的调用方式的不同,this会指向不同的对象
以函数的形式调用时,this永远都是window
以方法的形式调用时,this就是调用方法的那个对象

4 对象进阶

4.1使用工厂方法创建对象

通过该方法可以大批量的创建对象

使用工厂方法创建的对象,使用的构造函数都是Object
所以创建的对象都是Object这个类型,
就导致我们无法区分出多种不同类型的对象

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script type="text/javascript">

            /*
             * 创建一个对象
             */
            var obj = {
                    name:"孙悟空",
                    age:18,
                    gender:"男",
                    sayName:function(){
                        alert(this.name);
                    }
            };

            /*
             * 使用工厂方法创建对象
             *     通过该方法可以大批量的创建对象
             */
            function createPerson(name , age ,gender){
                //创建一个新的对象 
                var obj = new Object();
                //向对象中添加属性
                obj.name = name;
                obj.age = age;
                obj.gender = gender;
                obj.sayName = function(){
                    alert(this.name);
                };
                //将新的对象返回
                return obj;
            }

            /*
             * 用来创建狗的对象
             */
            function createDog(name , age){
                var obj = new Object();
                obj.name = name;
                obj.age = age;
                obj.sayHello = function(){
                    alert("汪汪~~");
                };

                return obj;
            }

            var obj2 = createPerson("猪八戒",28,"男");
            var obj3 = createPerson("白骨精",16,"女");
            var obj4 = createPerson("蜘蛛精",18,"女");

            //创建一个狗的对象
            var dog = createDog("旺财",3);

            console.log(dog);
            console.log(obj4);

        </script>
    </head>
    <body>
    </body>
</html>

4.2用构造函数创建对象

创建一个构造函数,专门用来创建Person对象的
构造函数就是一个普通的函数,创建方式和普通函数没有区别,
不同的是构造函数习惯上首字母大写

构造函数和普通函数的区别就是调用方式的不同

普通函数是直接调用,而构造函数需要使用new关键字来调用

构造函数的执行流程:

  • 1.立刻创建一个新的对象

  • 2.将新建的对象设置为函数中this,在构造函数中可以使用this来引用新建的对象

  • 3.逐行执行函数中的代码

  • 4.将新建的对象作为返回值返回

使用同一个构造函数创建的对象,我们称为一类对象,也将一个构造函数称为一个类。

我们将通过一个构造函数创建的对象,称为是该类的实例

this的情况:

  • 1.当以函数的形式调用时,this是window
  • 2.当以方法的形式调用时,谁调用方法this就是谁
  • 3.当以构造函数的形式调用时,this就是新创建的那个对象
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script type="text/javascript">
            function Person(name , age , gender){
                this.name = name;
                this.age = age;
                this.gender = gender;
                this.sayName = function(){
                    alert(this.name);
                };
            }

            function Dog(){

            }

            var per = new Person("孙悟空",18,"男");
            var per2 = new Person("玉兔精",16,"女");
            var per3 = new Person("奔波霸",38,"男");

            var dog = new Dog();

            console.log(per instanceof Person);
            console.log(dog instanceof Person);

            console.log(dog instanceof Object);

        </script>
    </head>
    <body>
    </body>
</html>

使用instanceof可以检查一个对象是否是一个类的实例

语法:对象 instanceof 构造函数

如果是,则返回true,否则返回false

所有的对象都是Object的后代,
所以任何对象和Object做instanceof检查时都会返回true

创建一个Person构造函数
在Person构造函数中,为每一个对象都添加了一个sayName方法,
目前我们的方法是在构造函数内部创建的,
也就是构造函数每执行一次就会创建一个新的sayName方法
也是所有实例的sayName都是唯一的。
这样就导致了构造函数执行一次就会创建一个新的方法,
执行10000次就会创建10000个新的方法,而10000个方法都是一摸一样的
这是完全没有必要,完全可以使所有的对象共享同一个方法

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script type="text/javascript">

            function Person(name , age , gender){
                this.name = name;
                this.age = age;
                this.gender = gender;
                //向对象中添加一个方法
                //this.sayName = fun;
            }

            将sayName方法在全局作用域中定义
             * 将函数定义在全局作用域,污染了全局作用域的命名空间
             *     而且定义在全局作用域中也很不安全
             */
            /*function fun(){
                alert("Hello大家好,我是:"+this.name);
            };*/

            //向原型中添加sayName方法
            Person.prototype.sayName = function(){
                alert("Hello大家好,我是:"+this.name);
            };

            //创建一个Person的实例
            var per = new Person("孙悟空",18,"男");
            var per2 = new Person("猪八戒",28,"男");
            per.sayName();
            per2.sayName();

        </script>
    </head>
    <body>
    </body>
</html>

4.3 原型 prototype

我们所创建的每一个函数,解析器都会向函数中添加一个属性prototype
这个属性对应着一个对象,这个对象就是我们所谓的原型对象

如果函数作为普通函数调用prototype没有任何作用
当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,我们可以通过proto来访问该属性

原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。

当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用

Object对象的原型没有原型,如果在Object原型中依然没有找到,则返回undefined

以后我们创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中,这样不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法了

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script type="text/javascript">

            function MyClass(){

            }

            //向MyClass的原型中添加属性a
            MyClass.prototype.a = 123;

            //向MyClass的原型中添加一个方法
            MyClass.prototype.sayHello = function(){
                alert("hello");
            };

            var mc = new MyClass();
            var mc2 = new MyClass();

            //向mc中添加a属性
            mc.a = "我是mc中的a";            
            mc.sayHello();

        </script>
    </head>
    <body>
    </body>
</html>

4.4 toString

当我们直接在页面中打印一个对象时,事件上是输出的对象的toString()方法的返回值
如果我们希望在输出对象时不输出[object Object],可以为对象添加一个toString()方法

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script type="text/javascript">

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

            //修改Person原型的toString
            Person.prototype.toString = function(){
                return "Person[name="+this.name+",age="+this.age+",gender="+this.gender+"]";
            };


            //创建一个Person实例
            var per = new Person("孙悟空",18,"男");
            var per2 = new Person("猪八戒",28,"男");

            var result = per.toString();
            console.log(per2);
            console.log(per);

        </script>
    </head>
    <body>
    </body>
</html>

4.5 垃圾回收(GC)

就像人生活的时间长了会产生垃圾一样,程序运行过程中也会产生垃圾
这些垃圾积攒过多以后,会导致程序运行的速度过慢,
所以我们需要一个垃圾回收的机制,来处理程序运行过程中产生垃圾

当一个对象没有任何的变量或属性对它进行引用,此时我们将永远无法操作该对象,此时这种对象就是一个垃圾,这种对象过多会占用大量的内存空间,导致程序运行变慢,所以这种垃圾必须进行清理。

在JS中拥有自动的垃圾回收机制,会自动将这些垃圾对象从内存中销毁,我们不需要也不能进行垃圾回收的操作,我们需要做的只是要将不再使用的对象设置null即可

五、JavaScript常用对象

5.1 数组对象array

5.1.1 概述

数组也是一个对象
它和我们普通对象功能类似,也是用来存储一些值的
不同的是普通对象是使用字符串作为属性名的,
而数组时使用数字来作为索引操作元素

索引:
从0开始的整数就是索引
数组的存储性能比普通对象要好,在开发中我们经常使用数组来存储一些数据

读取数组中的元素
语法:数组[索引]
如果读取不存在的索引,他不会报错而是返回undefined

获取数组的长度

可以使用length属性来获取数组的长度(元素的个数)

语法:数组.length

对于连续的数组,使用length可以获取到数组的长度(元素的个数)

对于非连续的数组,使用length会获取到数组的最大的索引+1

尽量不要创建非连续的数组

修改length

如果修改的length大于原长度,则多出部分会空出来

如果修改的length小于原长度,则多出的元素会被删除

向数组的最后一个位置添加元素
语法:数组[数组.length] = 值;

5.1.2 创建数组

使用字面量来创建数组
语法:[]

var arr = [];
var arr = [1,2,3,4,5,10];

使用构造函数创建数组时,也可以同时添加元素,将要添加的元素作文构造函数的参数传递,元素之间使用,隔开

var arr2 = new Array(10,20,30);

//数组中也可以放数组,如下这种数组我们称为二维数组
arr = [[1,2,3],[3,4,5],[5,6,7]];

5.1.3 数组方法

push()

该方法可以向数组的末尾添加一个或多个元素,并返回数组的新的长度
可以将要添加的元素作为方法的参数传递,这样这些元素将会自动添加到数组的末尾
该方法会将数组新的长度作为返回值返回

var arr = ["孙悟空","猪八戒","沙和尚"];
var result = arr.push("唐僧","蜘蛛精","白骨精","玉兔精");
console.log("result = "+result);

pop()

该方法可以删除数组的最后一个元素,并将被删除的元素作为返回值返回

var arr = ["孙悟空","猪八戒","沙和尚"];
result = arr.pop();
console.log(arr);

unshift()

向数组开头添加一个或多个元素,并返回新的数组长度
向前边插入元素以后,其他的元素索引会依次调整

var arr = ["孙悟空","猪八戒","沙和尚"];
arr.unshift("牛魔王","二郎神");    
console.log(arr);

shift()

可以删除数组的第一个元素,并将被删除的元素作为返回值返回

var arr = ["孙悟空","猪八戒","沙和尚"];
result = arr.shift();
console.log(arr);

slice()

可以用来从数组提取指定元素
该方法不会改变元素数组,而是将截取到的元素封装到一个新数组中返回

参数:
1.截取开始的位置的索引,包含开始索引
2.截取结束的位置的索引,不包含结束索引

  • 第二个参数可以省略不写,此时会截取从开始索引往后的所有元素
  • 索引可以传递一个负值,如果传递一个负值,则从后往前计算

    • -1 倒数第一个
    • -2 倒数第二个

splice()

可以用于删除数组中的指定元素
使用splice()会影响到原数组,会将指定元素从原数组中删除,并将被删除的元素作为返回值返回

参数:
第一个,表示开始位置的索引
第二个,表示删除的数量
第三个及以后。。
可以传递一些新的元素,这些元素将会自动插入到开始位置索引前边

arr = ["孙悟空","猪八戒","沙和尚","唐僧","白骨精"];
var result = arr.splice(3,0,"牛魔王","铁扇公主","红孩儿");
console.log(result);

concat()

可以连接两个或多个数组,并将新的数组返回

该方法不会对原数组产生影响

var arr = ["孙悟空","猪八戒","沙和尚"];
var arr2 = ["白骨精","玉兔精","蜘蛛精"];
var arr3 = ["二郎神","太上老君","玉皇大帝"];
var result = arr.concat(arr2,arr3,"牛魔王","铁扇公主");
console.log(result);

join()

该方法可以将数组转换为一个字符串
该方法不会对原数组产生影响,而是将转换后的字符串作为结果返回
在join()中可以指定一个字符串作为参数,这个字符串将会成为数组中元素的连接符
如果不指定连接符,则默认使用,作为连接符

arr = ["孙悟空","猪八戒","沙和尚","唐僧"];
result = arr.join("@-@");

reverse()

该方法用来反转数组(前边的去后边,后边的去前边)
该方法会直接修改原数组

sort()

可以用来对数组中的元素进行排序
也会影响原数组,默认会按照Unicode编码进行排序

即使对于纯数字的数组,使用sort()排序时,也会按照Unicode编码来排序,所以对数字进排序时,可能会得到错误的结果。

我们可以自己来指定排序的规则
我们可以在sort()添加一个回调函数,来指定排序规则,
回调函数中需要定义两个形参,
浏览器将会分别使用数组中的元素作为实参去调用回调函数
使用哪个元素调用不确定,但是肯定的是在数组中a一定在b前边

浏览器会根据回调函数的返回值来决定元素的顺序,
如果返回一个大于0的值,则元素会交换位置
如果返回一个小于0的值,则元素位置不变
如果返回一个0,则认为两个元素相等,也不交换位置

如果需要升序排列,则返回 a-b
如果需要降序排列,则返回b-a

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title></title>
        <script type="text/javascript">

            arr = [5,4,2,1,3,6,8,7];

            arr.sort(function(a,b){    
                //升序排列
                //return a - b;

                //降序排列
                return b - a;    
            });
            console.log(arr);

        </script>
    </head>
    <body>        
    </body>
</html>

5.1.4 数组的遍历

5.1.4.1 for循环

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script type="text/javascript">

            //创建一个数组
            var arr = ["孙悟空","猪八戒","沙和尚","唐僧","白骨精"];

            //所谓的遍历数组,就是将数组中所有的元素都取出来
            for(var i=0 ; i<arr.length ; i++){
                console.log(arr[i]);
            }
        </script>
    </head>
    <body>
    </body>
</html>

5.1.4.2 forEach()

这个方法只支持IE8以上的浏览器
IE8及以下的浏览器均不支持该方法,所以如果需要兼容IE8,则不要使用forEach

forEach()方法需要一个函数作为参数
像这种函数,由我们创建但是不由我们调用的,我们称为回调函数
数组中有几个元素函数就会执行几次,每次执行时,浏览器会将遍历到的元素,以实参的形式传递进来,我们可以来定义形参,来读取这些内容

浏览器会在回调函数中传递三个参数:
第一个参数,就是当前正在遍历的元素
第二个参数,就是当前正在遍历的元素的索引
第三个参数,就是正在遍历的数组

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script type="text/javascript">

            //创建一个数组
            var arr = ["孙悟空","猪八戒","沙和尚","唐僧","白骨精"];

            arr.forEach(function(value , index , obj){
                console.log(value);
            });

        </script>
    </head>
    <body>
    </body>
</html>

5.2 函数对象

5.2.1 call()和apply()

这两个方法都是函数对象的方法,需要通过函数对象来调用
当对函数调用call()和apply()都会调用函数执行
在调用call()和apply()可以将一个对象指定为第一个参数
此时这个对象将会成为函数执行时的this

call()方法可以将实参在对象之后依次传递
apply()方法需要将实参封装到一个数组中统一传递

this的情况:
1.以函数形式调用时,this永远都是window
2.以方法的形式调用时,this是调用方法的对象
3.以构造函数的形式调用时,this是新创建的那个对象
4.使用call和apply调用时,this是指定的那个对象

5.2.2 arguments

在调用函数时,浏览器每次都会传递进两个隐含的参数:
1.函数的上下文对象 this
2.封装实参的对象 arguments

arguments是一个类数组对象,它也可以通过索引来操作数据,也可以获取长度
在调用函数时,我们所传递的实参都会在arguments中保存

arguments.length可以用来获取实参的长度

即使不定义形参,也可以通过arguments来使用实参,只不过比较麻烦
arguments[0] 表示第一个实参
arguments[1] 表示第二个实参

它里边有一个属性叫做callee,
这个属性对应一个函数对象,就是当前正在指向的函数的对象

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script type="text/javascript">        
            function fun(a,b){
                console.log(arguments.callee == fun);
            }
            fun("hello",true);

        </script>
    </head>
    <body>
    </body>
</html>

5.3 日期函数date

5.3.1 创建Date对象

var d = new Date();

创建一个指定的时间对象
日期的格式 月份/日/年 时:分:秒

var d2 = new Date("2/18/2011 11:10:30");

5.3.2 方法

方法 含义
getDate() 获取当前日期对象是几日
getDay() 获取当前日期对象时周几(0 ~ 6)
getMonth() 获取当前时间对象的月份(0 ~ 11)
getFullYear() 获取当前日期对象的年份
getTime() 获取当前日期对象的时间戳,时间戳,指的是从格林威治标准时间的1970年1月1日,0时0分0秒,到当前日期所花费的毫秒数(1秒 = 1000毫秒)
getHours() 获取当前日期对象的小时(0 ~ 23)
getMinutes() 获取当前日期对象的分钟(0 ~ 59)
getSeconds() 获取当前日期对象的秒钟(0 ~ 59)
getMilliseconds() 获取当前日期对象的毫秒(0 ~ 999)

5.3 工具类math对象

Math和其他的对象不同,它不是一个构造函数,它属于一个工具类不用创建对象,它里边封装了数学运算相关的属性和方法

函数 含义
abs() 计算绝对值
Math.PI 圆周率
ceil() 向上取整,小数位只有有值就自动进1
floor() 向下取整,小数部分会被舍掉
round() 四舍五入取整
random() 生成一个0-1之间的随机数
max() 最大值
min() 最小值
pow(x,y) x的y次幂
sqrt() 开方运算

5.4 String对象

在JS中为我们提供了三个包装类,通过这三个包装类可以将基本数据类型的数据转换为对象

  • String()
    可以将基本数据类型字符串转换为String对象
  • Number()
    可以将基本数据类型的数字转换为Number对象
  • Boolean()
    可以将基本数据类型的布尔值转换为Boolean对象

但是注意:我们在实际应用中不会使用基本数据类型的对象,如果使用基本数据类型的对象,在做一些比较时可能会带来一些不可预期的结果

5.4.1 创建对象

var num = new Number(3);
var num2 = new Number(3);
var str = new String("hello");
var str2 = new String("hello");
var bool = new Boolean(true);
var bool2 = true;

5.4.2 添加属性

num.hello = "abcdefg";

方法和属性之能添加给对象,不能添加给基本数据类型
当我们对一些基本数据类型的值去调用属性和方法时,浏览器会临时使用包装类将其转换为对象,然后在调用对象的属性和方法。调用完以后,在将其转换为基本数据类型

constructor属性演示:返回创建字符串对象的原型函数

length属性演示:可以用来获取字符串的长度

5.4.3 方法

方法 含义
charAt() 可以返回字符串中指定位置的字符,根据索引获取指定的字符
charCodeAt() 获取指定位置字符的字符编码(Unicode编码)
formCharCode() 可以根据字符编码去获取字符
concat() 可以用来连接两个或多个字符串
indexof() 该方法可以检索一个字符串中是否含有指定内容。如果字符串中含有该内容,则会返回其第一次出现的索引。如果没有找到指定的内容,则返回-1。可以指定一个第二个参数,指定开始查找的位置
lastIndexOf() 也可以指定开始查找的位置。从后往前找
slice() 从字符串中截取指定的内容。不会影响原字符串,而是将截取到内容返回。
参数:第一个,开始位置的索引(包括开始位置)。第二个,结束位置的索引(不包括结束位置)。也可以传递一个负数作为参数,负数的话将会从后边计算
substring() 截取一个字符串
参数:第一个:开始截取位置的索引(包括开始位置)。 第二个:结束位置的索引(不包括结束位置)。这个方法不能接受负值作为参数。 而且他还自动调整参数的位置,如果第二个参数小于第一个,则自动交换
substr() 用来截取字符串
参数:1.截取开始位置的索引2.截取的长度
split() 可以将一个字符串拆分为一个数
toUpperCase() 将一个字符串转换为大写并返回
toLowerCase() 将一个字符串转换为小写并返回

5.5 正则表达式RegExp对象

正则表达式用于定义一些字符串的规则,计算机可以根据正则表达式,来检查一个字符串是否符合规则,获取将字符串中符合规则的内容提取出来

5.5.1 创建对象

语法:var 变量 = new RegExp(“正则表达式”,”匹配模式”);

使用typeof检查正则对象,会返回object

var reg = new RegExp("a"); 这个正则表达式可以来检查一个字符串中是否含有a

在构造函数中可以传递一个匹配模式作为第二个参数,
i 忽略大小写
g 全局匹配模式

使用字面量来创建正则表达式

语法:var 变量 = /正则表达式/匹配模式

var reg = /a/i;
符号 含义
| 或者
[]
[^ ] 除了
//创建一个正则表达式,检查一个字符串中是否有a或b
reg = /a|b|c/;

//任意字母
reg = /[A-z]/;

//检查一个字符串中是否含有 abc 或 adc 或 aec
reg = /a[bde]c/;

reg = /[^0-9]/;
console.log(reg.test("12a3456"));

5.5.2 方法

test()

使用这个方法可以用来检查一个字符串是否符合正则表达式的规则,
如果符合则返回true,否则返回false

var result = reg.test(str);
console.log(reg.test("Ac"));

5.5.3 字符串和正则相关的方法

split()

可以将一个字符串拆分为一个数组
方法中可以传递一个正则表达式作为参数,这样方法将会根据正则表达式去拆分字符串
这个方法即使不指定全局匹配,也会全都插分

/*
* 根据任意字母来将字符串拆分
*/
var result = str.split(/[A-z]/);
console.log(result);

search()

可以搜索字符串中是否含有指定内容
如果搜索到指定内容,则会返回第一次出现的索引,如果没有搜索到返回-1
它可以接受一个正则表达式作为参数,然后会根据正则表达式去检索字符串
serach()只会查找第一个,即使设置全局匹配也没用

/*
* 搜索字符串中是否含有abc 或 aec 或 afc
*/
str = "hello abc hello aec afc";
result = str.search(/a[bef]c/);
console.log(result);

match()

可以根据正则表达式,从一个字符串中将符合条件的内容提取出来
默认情况下我们的match只会找到第一个符合要求的内容,找到以后就停止检索
我们可以设置正则表达式为全局匹配模式,这样就会匹配到所有的内容
可以为一个正则表达式设置多个匹配模式,且顺序无所谓
match()会将匹配到的内容封装到一个数组中返回,即使只查询到一个结果

str = "1a2a3a4a5e6f7A8B9C";
result = str.match(/[a-z]/ig);
console.log(result);

replace()

可以将字符串中指定内容替换为新的内容

参数:
1.被替换的内容,可以接受一个正则表达式作为参数
2.新的内容
默认只会替换第一个

result = str.replace(/[a-z]/gi , "");
console.log(result);

5.5.4 量词

通过量词可以设置一个内容出现的次数
量词只对它前边的一个内容起作用

{n} 正好出现n次
{m,n} 出现m-n次
{m,} m次以上
+ 至少一个,相当于{1,}
* 0个或多个,相当于{0,}
? 0个或1个,相当于{0,1}

检查一个字符串中是否以a开头
^ 表示开头
reg = /^a/; //匹配开头的a

检查一个字符串中是否以a结尾
$ 表示结尾
reg = /a$/; //匹配结尾的a

检查一个字符串中是否含有 .
. 表示任意字符

\. 来表示.
\\  表示\

\w任意字母、数字、_  [A-z0-9_]
\W除了字母、数字、_  [^A-z0-9_]
\d任意的数字 [0-9]
\D除了数字 [^0-9]
\s空格
\S除了空格
\b单词边界
\B除了单词边界

六.DOM

6.1 概述

浏览器已经为我们提供 文档节点 对象这个对象是window属性。可以在页面中直接使用,文档节点代表的是整个网页

console.log(document);

我们可以在事件对应的属性中设置一些js代码,这样当事件被触发时,这些代码将会执行。这种写法我们称为结构和行为耦合,不方便维护,不推荐使用

事件,就是用户和浏览器之间的交互行为,
比如:点击按钮,鼠标移动、关闭窗口。。。

像这种为单击事件绑定的函数,我们称为单击响应函数

//获取按钮对象
var btn = document.getElementById("btn");
//绑定一个单击事件
//像这种为单击事件绑定的函数,我们称为单击响应函数
btn.onclick = function(){
    alert("你还点~~~");
};

6.2 文件加载

浏览器在加载一个页面时,是按照自上向下的顺序加载的,读取到一行就运行一行,如果将script标签写到页面的上边,在代码执行时,页面还没有加载,页面没有加载DOM对象也没有加载,会导致无法获取到DOM对象

onload事件会在整个页面加载完成之后才触发
为window绑定一个onload事件,该事件对应的响应函数将会在页面加载完成之后执行,这样可以确保我们的代码执行时所有的DOM对象已经加载完毕了

window.onload = function(){
    //获取id为btn的按钮
    var btn = document.getElementById("btn");
    //为按钮绑定一个单击响应函数
    btn.onclick = function(){
        alert("hello");
    };
};

6.3 DOM文档操作查

6.3.1 查找 HTML 元素

方法 描述
document.getElementById(id) 通过元素 id 来查找元素。
document.getElementsByTagName(name) 通过标签名来查找元素。
document.getElementsByClassName(name) 通过类名来查找元素。
document.querySelector(CSS选择器) 通过CSS选择器选择一个元素。
document.querySelectorAll(CSS选择器) 通过CSS选择器选择多个元素。
document.documentElement 保存的是html根标签
document.all 代表页面中所有的元素
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Untitled Document</title>
        <link rel="stylesheet" type="text/css" href="style/css.css" />
        <script type="text/javascript">

            /*
             * 定义一个函数,专门用来为指定元素绑定单击响应函数
             *     参数:
             *         idStr 要绑定单击响应函数的对象的id属性值
             *         fun 事件的回调函数,当单击元素时,该函数将会被触发
             */
            function myClick(idStr , fun){
                var btn = document.getElementById(idStr);
                btn.onclick = fun;
            }

            window.onload = function(){

                //为id为btn01的按钮绑定一个单击响应函数
                var btn01 = document.getElementById("btn01");
                btn01.onclick = function(){
                    //查找#bj节点
                    var bj = document.getElementById("bj");
                    //打印bj
                    //innerHTML 通过这个属性可以获取到元素内部的html代码
                    alert(bj.innerHTML);
                };


                //为id为btn02的按钮绑定一个单击响应函数
                var btn02 = document.getElementById("btn02");
                btn02.onclick = function(){
                    //查找所有li节点
                    //getElementsByTagName()可以根据标签名来获取一组元素节点对象
                    //这个方法会给我们返回一个类数组对象,所有查询到的元素都会封装到对象中
                    //即使查询到的元素只有一个,也会封装到数组中返回
                    var lis = document.getElementsByTagName("li");

                    //打印lis
                    //alert(lis.length);

                    //变量lis
                    for(var i=0 ; i<lis.length ; i++){
                        alert(lis[i].innerHTML);
                    }
                };


                //为id为btn03的按钮绑定一个单击响应函数
                var btn03 = document.getElementById("btn03");
                btn03.onclick = function(){
                    //查找name=gender的所有节点
                    var inputs = document.getElementsByName("gender");

                    //alert(inputs.length);

                    for(var i=0 ; i<inputs.length ; i++){
                        /*
                         * innerHTML用于获取元素内部的HTML代码的
                         *     对于自结束标签,这个属性没有意义
                         */
                        //alert(inputs[i].innerHTML);
                        /*
                         * 如果需要读取元素节点属性,
                         *     直接使用 元素.属性名
                         *         例子:元素.id 元素.name 元素.value
                         *         注意:class属性不能采用这种方式,
                         *             读取class属性时需要使用 元素.className
                         */
                        alert(inputs[i].className);
                    }
                };

                //为id为btn04的按钮绑定一个单击响应函数
                var btn04 = document.getElementById("btn04");
                btn04.onclick = function(){

                    //获取id为city的元素
                    var city = document.getElementById("city");

                    //查找#city下所有li节点
                    var lis = city.getElementsByTagName("li");

                    for(var i=0 ; i<lis.length ; i++){
                        alert(lis[i].innerHTML);
                    }

                };

                //为id为btn05的按钮绑定一个单击响应函数
                var btn05 = document.getElementById("btn05");
                btn05.onclick = function(){
                    //获取id为city的节点
                    var city = document.getElementById("city");
                    //返回#city的所有子节点
                    /*
                     * childNodes属性会获取包括文本节点在呢的所有节点
                     * 根据DOM标签标签间空白也会当成文本节点
                     * 注意:在IE8及以下的浏览器中,不会将空白文本当成子节点,
                     *     所以该属性在IE8中会返回4个子元素而其他浏览器是9个
                     */
                    var cns = city.childNodes;

                    //alert(cns.length);

                    /*for(var i=0 ; i<cns.length ; i++){
                        alert(cns[i]);
                    }*/

                    /*
                     * children属性可以获取当前元素的所有子元素
                     */
                    var cns2 = city.children;
                    alert(cns2.length);
                };

                //为id为btn06的按钮绑定一个单击响应函数
                var btn06 = document.getElementById("btn06");
                btn06.onclick = function(){
                    //获取id为phone的元素
                    var phone = document.getElementById("phone");
                    //返回#phone的第一个子节点
                    //phone.childNodes[0];
                    //firstChild可以获取到当前元素的第一个子节点(包括空白文本节点)
                    var fir = phone.firstChild;

                    //firstElementChild获取当前元素的第一个子元素
                    /*
                     * firstElementChild不支持IE8及以下的浏览器,
                     *     如果需要兼容他们尽量不要使用
                     */
                    //fir = phone.firstElementChild;

                    alert(fir);
                };

                //为id为btn07的按钮绑定一个单击响应函数
                myClick("btn07",function(){

                    //获取id为bj的节点
                    var bj = document.getElementById("bj");

                    //返回#bj的父节点
                    var pn = bj.parentNode;

                    alert(pn.innerHTML);

                    /*
                     * innerText
                     *     - 该属性可以获取到元素内部的文本内容
                     *     - 它和innerHTML类似,不同的是它会自动将html去除
                     */
                    //alert(pn.innerText);


                });


                //为id为btn08的按钮绑定一个单击响应函数
                myClick("btn08",function(){

                    //获取id为android的元素
                    var and = document.getElementById("android");

                    //返回#android的前一个兄弟节点(也可能获取到空白的文本)
                    var ps = and.previousSibling;

                    //previousElementSibling获取前一个兄弟元素,IE8及以下不支持
                    //var pe = and.previousElementSibling;

                    alert(ps);

                });

                //读取#username的value属性值
                myClick("btn09",function(){
                    //获取id为username的元素
                    var um = document.getElementById("username");
                    //读取um的value属性值
                    //文本框的value属性值,就是文本框中填写的内容
                    alert(um.value);
                });


                //设置#username的value属性值
                myClick("btn10",function(){
                    //获取id为username的元素
                    var um = document.getElementById("username");

                    um.value = "今天天气真不错~~~";
                });


                //返回#bj的文本值
                myClick("btn11",function(){

                    //获取id为bj的元素
                    var bj = document.getElementById("bj");

                    //alert(bj.innerHTML);
                    //alert(bj.innerText);

                    //获取bj中的文本节点
                    /*var fc = bj.firstChild;
                    alert(fc.nodeValue);*/

                    alert(bj.firstChild.nodeValue);


                });

            };


        </script>
    </head>
    <body>
        <div id="total">
            <div class="inner">
                <p>
                    你喜欢哪个城市?
                </p>

                <ul id="city">
                    <li id="bj">北京</li>
                    <li>上海</li>
                    <li>东京</li>
                    <li>首尔</li>
                </ul>

                <br>
                <br>

                <p>
                    你喜欢哪款单机游戏?
                </p>

                <ul id="game">
                    <li id="rl">红警</li>
                    <li>实况</li>
                    <li>极品飞车</li>
                    <li>魔兽</li>
                </ul>

                <br />
                <br />

                <p>
                    你手机的操作系统是?
                </p>

                <ul id="phone"><li>IOS</li> <li id="android">Android</li><li>Windows Phone</li></ul>
            </div>

            <div class="inner">
                gender:
                <input class="hello" type="radio" name="gender" value="male"/>
                Male
                <input class="hello" type="radio" name="gender" value="female"/>
                Female
                <br>
                <br>
                name:
                <input type="text" name="name" id="username" value="abcde"/>
            </div>
        </div>
        <div id="btnList">
            <div><button id="btn01">查找#bj节点</button></div>
            <div><button id="btn02">查找所有li节点</button></div>
            <div><button id="btn03">查找name=gender的所有节点</button></div>
            <div><button id="btn04">查找#city下所有li节点</button></div>
            <div><button id="btn05">返回#city的所有子节点</button></div>
            <div><button id="btn06">返回#phone的第一个子节点</button></div>
            <div><button id="btn07">返回#bj的父节点</button></div>
            <div><button id="btn08">返回#android的前一个兄弟节点</button></div>
            <div><button id="btn09">返回#username的value属性值</button></div>
            <div><button id="btn10">设置#username的value属性值</button></div>
            <div><button id="btn11">返回#bj的文本值</button></div>
        </div>
    </body>
</html>

6.3.2 获取 HTML 的值

方法 含义
元素节点.innerText 获取 HTML 元素的 inner Text。
元素节点.innerHTML 获取 HTML 元素的 inner HTML。
元素节点.属性 获取 HTML 元素的属性值。
元素节点.getAttribute(attribute) 获取 HTML 元素的属性值。
元素节点.style.样式 获取 HTML 元素的行内样式值。

6.3.3 改变 HTML 的值

方法 含义
元素节点.innerText = new text content 改变元素的 inner Text。
元素节点.innerText = new text content 改变元素的 inner HTML。
元素节点.属性 = *new value 改变 HTML 元素的属性值。
元素节点.setAttribute(attribute, value) 改变 HTML 元素的属性值。
元素节点.style.样式 = new style 改变 HTML 元素的行内样式值。

6.3.4 修改 HTML 元素

方法 含义
document.createElement(element) 创建 HTML 元素节点。
document.createAttribute(attribute) 创建 HTML 属性节点。
document.createTextNode(text) 创建 HTML 文本节点。
元素节点.removeChild(element) 删除 HTML 元素。
元素节点.appendChild(element) 添加 HTML 元素。
元素节点.replaceChild(element) 替换 HTML 元素。
元素节点.insertBefore(element) 在指定的子节点前面插入新的子节点。

6.3.5 查找 HTML 父子

方法 含义
元素节点.parentNode 返回元素的父节点。
元素节点.parentElement 返回元素的父元素。
元素节点.childNodes 返回元素的一个子节点的数组(包含空白文本Text节点)。
元素节点.children 返回元素的一个子元素的集合(不包含空白文本Text节点)。
元素节点.firstChild 返回元素的第一个子节点(包含空白文本Text节点)。
元素节点.firstElementChild 返回元素的第一个子元素(不包含空白文本Text节点)。
元素节点.lastChild 返回元素的最后一个子节点(包含空白文本Text节点)。
元素节点.lastElementChild 返回元素的最后一个子元素(不包含空白文本Text节点)。
元素节点.previousSibling 返回某个元素紧接之前节点(包含空白文本Text节点)。
元素节点.previousElementSibling 返回指定元素的前一个兄弟元素(相同节点树层中的前一个元素节点)
元素节点.nextSibling 返回某个元素紧接之后节点(包含空白文本Text节点)。
元素节点.nextElementSibling 返回指定元素的后一个兄弟元素(相同节点树层中的下一个元素节点)。

案例:轮播图

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style type="text/css">

            *{
                margin: 0;
                padding: 0;
            }

            #outer{
                width: 500px;
                margin: 50px auto;
                padding: 10px;
                background-color: greenyellow;
                /*设置文本居中*/
                text-align: center;
            }
        </style>

        <script type="text/javascript">

            window.onload = function(){

                /*
                 * 点击按钮切换图片
                 */

                //获取两个按钮
                var prev = document.getElementById("prev");
                var next = document.getElementById("next");

                /*
                 * 要切换图片就是要修改img标签的src属性
                 */

                //获取img标签
                var img = document.getElementsByTagName("img")[0];

                //创建一个数组,用来保存图片的路径
                var imgArr = ["img/1.jpg" , "img/2.jpg" , "img/3.jpg" , "img/4.jpg" ,"img/5.jpg"];

                //创建一个变量,来保存当前正在显示的图片的索引
                var index = 0;

                //获取id为info的p元素
                var info = document.getElementById("info");
                //设置提示文字
                info.innerHTML = "一共 "+imgArr.length+" 张图片,当前第 "+(index+1)+" 张";


                //分别为两个按钮绑定单击响应函数
                prev.onclick = function(){

                    /*
                     * 切换到上一张,索引自减
                     */
                    index--;

                    //判断index是否小于0
                    if(index < 0){
                        index = imgArr.length - 1;
                    }

                    img.src = imgArr[index];

                    //当点击按钮以后,重新设置信息
                    info.innerHTML = "一共 "+imgArr.length+" 张图片,当前第 "+(index+1)+" 张";
                };

                next.onclick = function(){

                    /*
                     * 切换到下一张是index自增
                     */
                    index++;

                    if(index > imgArr.length - 1){
                        index = 0;
                    }

                    //切换图片就是修改img的src属性
                    //要修改一个元素的属性 元素.属性 = 属性值
                    img.src = imgArr[index];

                    //当点击按钮以后,重新设置信息
                    info.innerHTML = "一共 "+imgArr.length+" 张图片,当前第 "+(index+1)+" 张";

                };


            };


        </script>
    </head>
    <body>
        <div id="outer">

            <p id="info"></p>

            <img src="img/1.jpg" alt="冰棍" />

            <button id="prev">上一张</button>
            <button id="next">下一张</button>

        </div>
    </body>
</html>

案例:增删改查

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Untitled Document</title>
        <link rel="stylesheet" type="text/css" href="style/css.css" />
        <script type="text/javascript">

            window.onload = function() {

                //创建一个"广州"节点,添加到#city下
                myClick("btn01",function(){
                    //创建广州节点 <li>广州</li>
                    //创建li元素节点
                    /*
                     * document.createElement()
                     *     可以用于创建一个元素节点对象,
                     *     它需要一个标签名作为参数,将会根据该标签名创建元素节点对象,
                     *     并将创建好的对象作为返回值返回
                     */
                    var li = document.createElement("li");

                    //创建广州文本节点
                    /*
                     * document.createTextNode()
                     *     可以用来创建一个文本节点对象
                     *  需要一个文本内容作为参数,将会根据该内容创建文本节点,并将新的节点返回
                     */
                    var gzText = document.createTextNode("广州");

                    //将gzText设置li的子节点
                    /*
                     * appendChild()
                     *      - 向一个父节点中添加一个新的子节点
                     *      - 用法:父节点.appendChild(子节点);
                     */
                    li.appendChild(gzText);

                    //获取id为city的节点
                    var city = document.getElementById("city");

                    //将广州添加到city下
                    city.appendChild(li);


                });

                //将"广州"节点插入到#bj前面
                myClick("btn02",function(){
                    //创建一个广州
                    var li = document.createElement("li");
                    var gzText = document.createTextNode("广州");
                    li.appendChild(gzText);

                    //获取id为bj的节点
                    var bj = document.getElementById("bj");

                    //获取city
                    var city = document.getElementById("city");

                    /*
                     * insertBefore()
                     *     - 可以在指定的子节点前插入新的子节点
                     *  - 语法:
                     *         父节点.insertBefore(新节点,旧节点);
                     */
                    city.insertBefore(li , bj);


                });


                //使用"广州"节点替换#bj节点
                myClick("btn03",function(){
                    //创建一个广州
                    var li = document.createElement("li");
                    var gzText = document.createTextNode("广州");
                    li.appendChild(gzText);

                    //获取id为bj的节点
                    var bj = document.getElementById("bj");

                    //获取city
                    var city = document.getElementById("city");

                    /*
                     * replaceChild()
                     *     - 可以使用指定的子节点替换已有的子节点
                     *     - 语法:父节点.replaceChild(新节点,旧节点);
                     */
                    city.replaceChild(li , bj);


                });

                //删除#bj节点
                myClick("btn04",function(){
                    //获取id为bj的节点
                    var bj = document.getElementById("bj");
                    //获取city
                    var city = document.getElementById("city");

                    /*
                     * removeChild()
                     *     - 可以删除一个子节点
                     *     - 语法:父节点.removeChild(子节点);
                     *         
                     *         子节点.parentNode.removeChild(子节点);
                     */
                    //city.removeChild(bj);

                    bj.parentNode.removeChild(bj);
                });


                //读取#city内的HTML代码
                myClick("btn05",function(){
                    //获取city
                    var city = document.getElementById("city");

                    alert(city.innerHTML);
                });

                //设置#bj内的HTML代码
                myClick("btn06" , function(){
                    //获取bj
                    var bj = document.getElementById("bj");
                    bj.innerHTML = "昌平";
                });

                myClick("btn07",function(){

                    //向city中添加广州
                    var city = document.getElementById("city");

                    /*
                     * 使用innerHTML也可以完成DOM的增删改的相关操作
                     * 一般我们会两种方式结合使用
                     */
                    //city.innerHTML += "<li>广州</li>";

                    //创建一个li
                    var li = document.createElement("li");
                    //向li中设置文本
                    li.innerHTML = "广州";
                    //将li添加到city中
                    city.appendChild(li);

                });


            };

            function myClick(idStr, fun) {
                var btn = document.getElementById(idStr);
                btn.onclick = fun;
            }


        </script>

    </head>
    <body>
        <div id="total">
            <div class="inner">
                <p>
                    你喜欢哪个城市?
                </p>

                <ul id="city">
                    <li id="bj">北京</li>
                    <li>上海</li>
                    <li>东京</li>
                    <li>首尔</li>
                </ul>

            </div>
        </div>
        <div id="btnList">
            <div><button id="btn01">创建一个"广州"节点,添加到#city下</button></div>
            <div><button id="btn02">将"广州"节点插入到#bj前面</button></div>
            <div><button id="btn03">使用"广州"节点替换#bj节点</button></div>
            <div><button id="btn04">删除#bj节点</button></div>
            <div><button id="btn05">读取#city内的HTML代码</button></div>
            <div><button id="btn06">设置#bj内的HTML代码</button></div>
            <div><button id="btn07">创建一个"广州"节点,添加到#city下</button></div>
        </div>
    </body>
</html>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>

    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>添加删除记录练习</title>
        <link rel="stylesheet" type="text/css" href="ex_2_style/css.css" />
        <script type="text/javascript">

            /*
             * 删除tr的响应函数
             */
            function delA() {

                //点击超链接以后需要删除超链接所在的那行
                //这里我们点击那个超链接this就是谁
                //获取当前tr
                var tr = this.parentNode.parentNode;

                //获取要删除的员工的名字
                //var name = tr.getElementsByTagName("td")[0].innerHTML;
                var name = tr.children[0].innerHTML;

                //删除之前弹出一个提示框
                /*
                 * confirm()用于弹出一个带有确认和取消按钮的提示框
                 *     需要一个字符串作为参数,该字符串将会作为提示文字显示出来
                 * 如果用户点击确认则会返回true,如果点击取消则返回false
                 */
                var flag = confirm("确认删除" + name + "吗?");

                //如果用户点击确认
                if(flag) {
                    //删除tr
                    tr.parentNode.removeChild(tr);
                }

                /*
                 * 点击超链接以后,超链接会跳转页面,这个是超链接的默认行为,
                 *     但是此时我们不希望出现默认行为,可以通过在响应函数的最后return false来取消默认行为
                 */
                return false;
            };

            window.onload = function() {

                /*
                 * 点击超链接以后,删除一个员工的信息
                 */

                //获取所有额超链接
                var allA = document.getElementsByTagName("a");

                //为每个超链接都绑定一个单击响应函数
                for(var i = 0; i < allA.length; i++) {
                    allA[i].onclick = delA;
                }

                /*
                 * 添加员工的功能
                 *     - 点击按钮以后,将员工的信息添加到表格中
                 */

                //为提交按钮绑定一个单击响应函数
                var addEmpButton = document.getElementById("addEmpButton");
                addEmpButton.onclick = function() {

                    //获取用户添加的员工信息
                    //获取员工的名字
                    var name = document.getElementById("empName").value;
                    //获取员工的email和salary
                    var email = document.getElementById("email").value;
                    var salary = document.getElementById("salary").value;

                    //alert(name+","+email+","+salary);
                    /*
                     *  <tr>
                            <td>Tom</td>
                            <td>tom@tom.com</td>
                            <td>5000</td>
                            <td><a href="javascript:;">Delete</a></td>
                        </tr>
                        需要将获取到的信息保存到tr中
                     */

                    //创建一个tr
                    var tr = document.createElement("tr");

                    //设置tr中的内容
                    tr.innerHTML = "<td>"+name+"</td>"+
                                    "<td>"+email+"</td>"+
                                    "<td>"+salary+"</td>"+
                                    "<td><a href='javascript:;'>Delete</a></td>";

                    //获取刚刚添加的a元素,并为其绑定单击响应函数                
                    var a = tr.getElementsByTagName("a")[0];
                    a.onclick = delA;

                    //获取table
                    var employeeTable = document.getElementById("employeeTable");
                    //获取employeeTable中的tbody
                    var tbody = employeeTable.getElementsByTagName("tbody")[0];
                    //将tr添加到tbodye中
                    tbody.appendChild(tr);
                    /*tbody.innerHTML += "<tr>"+

                    "<td>"+name+"</td>"+
                                    "<td>"+email+"</td>"+
                                    "<td>"+salary+"</td>"+
                                    "<td><a href='javascript:;'>Delete</a></td>"

                    +"</tr>";*/

                };

            };
        </script>
    </head>

    <body>

        <table id="employeeTable">
            <tr>
                <th>Name</th>
                <th>Email</th>
                <th>Salary</th>
                <th>&nbsp;</th>
            </tr>
            <tr>
                <td>Tom</td>
                <td>tom@tom.com</td>
                <td>5000</td>
                <td>
                    <a href="javascript:;">Delete</a>
                </td>
            </tr>
            <tr>
                <td>Jerry</td>
                <td>jerry@sohu.com</td>
                <td>8000</td>
                <td>
                    <a href="deleteEmp?id=002">Delete</a>
                </td>
            </tr>
            <tr>
                <td>Bob</td>
                <td>bob@tom.com</td>
                <td>10000</td>
                <td>
                    <a href="deleteEmp?id=003">Delete</a>
                </td>
            </tr>
        </table>

        <div id="formDiv">

            <h4>添加新员工</h4>

            <table>
                <tr>
                    <td class="word">name: </td>
                    <td class="inp">
                        <input type="text" name="empName" id="empName" />
                    </td>
                </tr>
                <tr>
                    <td class="word">email: </td>
                    <td class="inp">
                        <input type="text" name="email" id="email" />
                    </td>
                </tr>
                <tr>
                    <td class="word">salary: </td>
                    <td class="inp">
                        <input type="text" name="salary" id="salary" />
                    </td>
                </tr>
                <tr>
                    <td colspan="2" align="center">
                        <button id="addEmpButton">
                        Submit
                    </button>
                    </td>
                </tr>
            </table>

        </div>

    </body>

</html>

6.3.6 使用DOM操作CSS

通过JS修改元素的样式:
语法:元素.style.样式名 = 样式值

注意:如果CSS的样式名中含有-,这种名称在JS中是不合法的比如background-color.需要将这种样式名修改为驼峰命名法,去掉-,然后将-后的字母大写

我们通过style属性设置的样式都是内联样式,
而内联样式有较高的优先级,所以通过JS修改的样式往往会立即显示

但是如果在样式中写了!important,则此时样式会有最高的优先级,
即使通过JS也不能覆盖该样式,此时将会导致JS修改样式失效
所以尽量不要为样式添加!important

6.3.6.1 读取元素的样式

获取元素的当前显示的样式
语法:元素.currentStyle.样式名
它可以用来读取当前元素正在显示的样式
如果当前元素没有设置该样式,则获取它的默认值
currentStyle只有IE浏览器支持,其他的浏览器都不支持

在其他浏览器中可以使用
==getComputedStyle()==这个方法来获取元素当前的样式
这个方法是window的方法,可以直接使用
需要两个参数
第一个:要获取样式的元素
第二个:可以传递一个伪元素,一般都传null

该方法会返回一个对象,对象中封装了当前元素对应的样式

可以通过对象.样式名来读取样式

如果获取的样式没有设置,则会获取到真实的值,而不是默认值

比如:没有设置width,它不会获取到auto,而是一个长度

但是该方法不支持IE8及以下的浏览器

通过currentStyle和==getComputedStyle()==读取到的样式都是只读的,

不能修改,如果要修改必须通过style属性


/*
* 定义一个函数,用来获取指定元素的当前的样式
* 参数:
*         obj 要获取样式的元素
*         name 要获取的样式名
*/

function getStyle(obj , name){
    if(window.getComputedStyle){
        //正常浏览器的方式,具有getComputedStyle()方法
        return getComputedStyle(obj , null)[name];
    }else{
        //IE8的方式,没有getComputedStyle()方法
        return obj.currentStyle[name];
    }
}

6.3.6.2 其他样式操作的属性

clientWidth与clientHeight

这两个属性可以获取元素的可见宽度和高度

这些属性都是不带px的,返回都是一个数字,可以直接进行计算

会获取元素宽度和高度,包括内容区和内边距

这些属性都是只读的,不能修改

offsetWidth与offsetHeight

获取元素的整个的宽度和高度,包括内容区、内边距和边框

offsetParent

可以用来获取当前元素的定位父元素
会获取到离当前元素最近的开启了定位的祖先元素
如果所有的祖先元素都没有开启定位,则返回body

offsetLeft

当前元素相对于其定位父元素的水平偏移量

offsetTop

当前元素相对于其定位父元素的垂直偏移量

scrollWidth与scrollHeight

可以获取元素整个滚动区域的宽度和高度

scrollLeft

可以获取水平滚动条滚动的距离

scrollTop

可以获取垂直滚动条滚动的距离

注意:

当满足scrollHeight - scrollTop == clientHeight
说明垂直滚动条滚动到底了

当满足scrollWidth - scrollLeft == clientWidth
说明水平滚动条滚动到底

案例:注册说明
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style type="text/css">

            #info{
                width: 300px;
                height: 500px;
                background-color: #bfa;
                overflow: auto;
            }

        </style>
        <script type="text/javascript">
            window.onload = function(){

                /*
                 * 当垂直滚动条滚动到底时使表单项可用
                 * onscroll
                 *     - 该事件会在元素的滚动条滚动时触发
                 */

                //获取id为info的p元素
                var info = document.getElementById("info");
                //获取两个表单项
                var inputs = document.getElementsByTagName("input");
                //为info绑定一个滚动条滚动的事件
                info.onscroll = function(){

                    //检查垂直滚动条是否滚动到底
                    if(info.scrollHeight - info.scrollTop == info.clientHeight){
                        //滚动条滚动到底,使表单项可用
                        /*
                         * disabled属性可以设置一个元素是否禁用,
                         *     如果设置为true,则元素禁用
                         *     如果设置为false,则元素可用
                         */
                        inputs[0].disabled = false;
                        inputs[1].disabled = false;
                    }

                };

            };


        </script>
    </head>
    <body>
        <h3>欢迎亲爱的用户注册</h3>
        <p id="info">
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
            亲爱的用户,请仔细阅读以下协议,如果你不仔细阅读你就别注册
        </p>
        <!-- 如果为表单项添加disabled="disabled" 则表单项将变成不可用的状态 -->
        <input type="checkbox" disabled="disabled" />我已仔细阅读协议,一定遵守
        <input type="submit" value="注册" disabled="disabled" />
    </body>
</html>

6.3.7 DOM文档事件

6.3.7.1 窗口事件

属性 描述
onblur 当窗口失去焦点时运行脚本。
onfocus 当窗口获得焦点时运行脚本。
onload 当文档加载之后运行脚本。
onresize 当调整窗口大小时运行脚本。
onstorage 当 Web Storage 区域更新时(存储空间中的数据发生变化时)运行脚本。

6.3.7.2 表单事件

属性 描述
onblur 当元素失去焦点时运行脚本。
onfocus 当元素获得焦点时运行脚本。
onchange 当元素改变时运行脚本。
oninput 当元素获得用户输入时运行脚本。
oninvalid 当元素无效时运行脚本。
onselect 当选取元素时运行脚本。
onsubmit 当提交表单时运行脚本。

6.3.7.3 键盘事件

属性 描述
onkeydown 当按下按键时运行脚本。
onkeyup 当松开按键时运行脚本。
onkeypress 当按下并松开按键时运行脚本。
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script type="text/javascript">

            window.onload = function(){


                document.onkeydown = function(event){
                    event = event || window.event;

                    /*
                     * 可以通过keyCode来获取按键的编码
                     *     通过它可以判断哪个按键被按下
                     * 除了keyCode,事件对象中还提供了几个属性
                     *     altKey
                     *     ctrlKey
                     *     shiftKey
                     *         - 这个三个用来判断alt ctrl 和 shift是否被按下
                     *             如果按下则返回true,否则返回false
                     */


                    //判断一个y是否被按下
                    //判断y和ctrl是否同时被按下
                    if(event.keyCode === 89 && event.ctrlKey){
                        console.log("ctrl和y都被按下了");
                    }

                };

                //获取input
                var input = document.getElementsByTagName("input")[0];

                input.onkeydown = function(event){

                    event = event || window.event;

                    //console.log(event.keyCode);
                    //数字 48 - 57
                    //使文本框中不能输入数字
                    if(event.keyCode >= 48 && event.keyCode <= 57){
                        //在文本框中输入内容,属于onkeydown的默认行为
                        //如果在onkeydown中取消了默认行为,则输入的内容,不会出现在文本框中
                        return false;
                    }

                };
            };

        </script>
    </head>
    <body>

        <input type="text" />

    </body>
</html>

6.3.7.4 鼠标事件

属性 描述
onclick 当单击鼠标时运行脚本。
ondblclick 当双击鼠标时运行脚本。
onmousedown 当按下鼠标按钮时运行脚本。
onmouseup 当松开鼠标按钮时运行脚本。
onmousemove 当鼠标指针移动时运行脚本。
onmouseover 当鼠标指针移至元素之上时运行脚本,不可以阻止冒泡。
onmouseout 当鼠标指针移出元素时运行脚本,不可以阻止冒泡。
onmouseenter 当鼠标指针移至元素之上时运行脚本,可以阻止冒泡。
onmouseleave 当鼠标指针移出元素时运行脚本,可以阻止冒泡。
onmousewheel 当转动鼠标滚轮时运行脚本。
onscroll 当滚动元素的滚动条时运行脚本。

案例:获取坐标
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<style type="text/css">

    #areaDiv {
        border: 1px solid black;
        width: 300px;
        height: 50px;
        margin-bottom: 10px;
    }

    #showMsg {
        border: 1px solid black;
        width: 300px;
        height: 20px;
    }

</style>
<script type="text/javascript">

    window.onload = function(){
        /*
         * 当鼠标在areaDiv中移动时,在showMsg中来显示鼠标的坐标
         */
        //获取两个div
        var areaDiv = document.getElementById("areaDiv");
        var showMsg = document.getElementById("showMsg");

        /*
         * onmousemove
         *     - 该事件将会在鼠标在元素中移动时被触发
         * 
         * 事件对象
         *     - 当事件的响应函数被触发时,浏览器每次都会将一个事件对象作为实参传递进响应函数,
         *         在事件对象中封装了当前事件相关的一切信息,比如:鼠标的坐标  键盘哪个按键被按下  鼠标滚轮滚动的方向。。。
         */
        areaDiv.onmousemove = function(event){

            /*
             * 在IE8中,响应函数被处罚时,浏览器不会传递事件对象,
             *     在IE8及以下的浏览器中,是将事件对象作为window对象的属性保存的
             */
            /*if(!event){
                event = window.event;
            }*/

            //解决事件对象的兼容性问题
            event = event || window.event;

            /*
             * clientX可以获取鼠标指针的水平坐标
             * cilentY可以获取鼠标指针的垂直坐标
             */
            var x = event.clientX;
            var y = event.clientY;

            //alert("x = "+x + " , y = "+y);

            //在showMsg中显示鼠标的坐标
            showMsg.innerHTML = "x = "+x + " , y = "+y;

        };

    };

</script>
</head>
<body>

    <div id="areaDiv"></div>
    <div id="showMsg"></div>

</body>
</html>

案例:
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style type="text/css">
            #box1{
                width: 100px;
                height: 100px;
                background-color: red;
                /*
                 * 开启box1的绝对定位
                 */
                position: absolute;
            }

        </style>

        <script type="text/javascript">
            window.onload = function(){

                /*
                 * 使div可以跟随鼠标移动
                 */

                //获取box1
                var box1 = document.getElementById("box1");
                //绑定鼠标移动事件
                document.onmousemove = function(event){

                    //解决兼容问题
                    event = event || window.event;

                    //获取滚动条滚动的距离
                    /*
                     * chrome认为浏览器的滚动条是body的,可以通过body.scrollTop来获取
                     * 火狐等浏览器认为浏览器的滚动条是html的,
                     */
                    var st = document.body.scrollTop || document.documentElement.scrollTop;
                    var sl = document.body.scrollLeft || document.documentElement.scrollLeft;
                    //var st = document.documentElement.scrollTop;


                    //获取到鼠标的坐标
                    /*
                     * clientX和clientY
                     *     用于获取鼠标在当前的可见窗口的坐标
                     * div的偏移量,是相对于整个页面的
                     * 
                     * pageX和pageY可以获取鼠标相对于当前页面的坐标
                     *     但是这个两个属性在IE8中不支持,所以如果需要兼容IE8,则不要使用
                     */
                    var left = event.clientX;
                    var top = event.clientY;

                    //设置div的偏移量
                    box1.style.left = left + sl + "px";
                    box1.style.top = top + st + "px";

                };


            };


        </script>
    </head>
    <body style="height: 1000px;width: 2000px;">
        <div id="box1"></div>
    </body>
</html>

案例:拖拽
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style type="text/css">

            #box1{
                width: 100px;
                height: 100px;
                background-color: red;
                position: absolute;
            }

            #box2{
                width: 100px;
                height: 100px;
                background-color: yellow;
                position: absolute;

                left: 200px;
                top: 200px;
            }

        </style>

        <script type="text/javascript">

            window.onload = function(){
                /*
                 * 拖拽box1元素
                 *  - 拖拽的流程
                 *         1.当鼠标在被拖拽元素上按下时,开始拖拽  onmousedown
                 *         2.当鼠标移动时被拖拽元素跟随鼠标移动 onmousemove
                 *         3.当鼠标松开时,被拖拽元素固定在当前位置    onmouseup
                 */

                //获取box1
                var box1 = document.getElementById("box1");
                var box2 = document.getElementById("box2");
                var img1 = document.getElementById("img1");

                //开启box1的拖拽
                drag(box1);
                //开启box2的
                drag(box2);

                drag(img1);




            };

            /*
             * 提取一个专门用来设置拖拽的函数
             * 参数:开启拖拽的元素
             */
            function drag(obj){
                //当鼠标在被拖拽元素上按下时,开始拖拽  onmousedown
                obj.onmousedown = function(event){

                    //设置box1捕获所有鼠标按下的事件
                    /*
                     * setCapture()
                     *     - 只有IE支持,但是在火狐中调用时不会报错,
                     *         而如果使用chrome调用,会报错
                     */
                    /*if(box1.setCapture){
                        box1.setCapture();
                    }*/
                    obj.setCapture && obj.setCapture();


                    event = event || window.event;
                    //div的偏移量 鼠标.clentX - 元素.offsetLeft
                    //div的偏移量 鼠标.clentY - 元素.offsetTop
                    var ol = event.clientX - obj.offsetLeft;
                    var ot = event.clientY - obj.offsetTop;


                    //为document绑定一个onmousemove事件
                    document.onmousemove = function(event){
                        event = event || window.event;
                        //当鼠标移动时被拖拽元素跟随鼠标移动 onmousemove
                        //获取鼠标的坐标
                        var left = event.clientX - ol;
                        var top = event.clientY - ot;

                        //修改box1的位置
                        obj.style.left = left+"px";
                        obj.style.top = top+"px";

                    };

                    //为document绑定一个鼠标松开事件
                    document.onmouseup = function(){
                        //当鼠标松开时,被拖拽元素固定在当前位置    onmouseup
                        //取消document的onmousemove事件
                        document.onmousemove = null;
                        //取消document的onmouseup事件
                        document.onmouseup = null;
                        //当鼠标松开时,取消对事件的捕获
                        obj.releaseCapture && obj.releaseCapture();
                    };

                    /*
                     * 当我们拖拽一个网页中的内容时,浏览器会默认去搜索引擎中搜索内容,
                     *     此时会导致拖拽功能的异常,这个是浏览器提供的默认行为,
                     *     如果不希望发生这个行为,则可以通过return false来取消默认行为
                     * 
                     * 但是这招对IE8不起作用
                     */
                    return false;

                };
            }


        </script>
    </head>
    <body>

        我是一段文字

        <div id="box1"></div>

        <div id="box2"></div>

        <img src="img/an.jpg" id="img1" style="position: absolute;"/>
    </body>
</html>

案例:滚动时间
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style type="text/css">

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

        </style>
        <script type="text/javascript">

            window.onload = function(){


                //获取id为box1的div
                var box1 = document.getElementById("box1");

                //为box1绑定一个鼠标滚轮滚动的事件
                /*
                 * onmousewheel鼠标滚轮滚动的事件,会在滚轮滚动时触发,
                 *     但是火狐不支持该属性
                 * 
                 * 在火狐中需要使用 DOMMouseScroll 来绑定滚动事件
                 *     注意该事件需要通过addEventListener()函数来绑定
                 */


                box1.onmousewheel = function(event){

                    event = event || window.event;
                    /*
                     * 当鼠标滚轮向下滚动时,box1变长
                     *     当滚轮向上滚动时,box1变短
                     */
                    //判断鼠标滚轮滚动的方向
                    if(event.wheelDelta > 0 || event.detail < 0){
                        //向上滚,box1变短
                        box1.style.height = box1.clientHeight - 10 + "px";

                    }else{
                        //向下滚,box1变长
                        box1.style.height = box1.clientHeight + 10 + "px";
                    }

                    /*
                     * 使用addEventListener()方法绑定响应函数,取消默认行为时不能使用return false
                     * 需要使用event来取消默认行为event.preventDefault();
                     * 但是IE8不支持event.preventDefault();这个玩意,如果直接调用会报错
                     */
                    event.preventDefault && event.preventDefault();


                    /*
                     * 当滚轮滚动时,如果浏览器有滚动条,滚动条会随之滚动,
                     * 这是浏览器的默认行为,如果不希望发生,则可以取消默认行为
                     */
                    return false;

                };

                //为火狐绑定滚轮事件
                bind(box1,"DOMMouseScroll",box1.onmousewheel);        

            };


            function bind(obj , eventStr , callback){
                if(obj.addEventListener){
                    //大部分浏览器兼容的方式
                    obj.addEventListener(eventStr , callback , false);
                }else{
                    /*
                     * this是谁由调用方式决定
                     * callback.call(obj)
                     */
                    //IE8及以下
                    obj.attachEvent("on"+eventStr , function(){
                        //在匿名函数中调用回调函数
                        callback.call(obj);
                    });
                }
            }

        </script>
    </head>
    <body style="height: 2000px;">

        <div id="box1"></div>

    </body>
</html>

6.3.7.5媒体事件

属性 描述
oncanplay 当媒介能够开始播放但可能因缓冲而需要停止时运行脚本。
onabort 当发生中止事件时运行脚本。
oncanplaythrough 当媒介能够无需因缓冲而停止即可播放至结尾时运行脚本。
ondurationchange 当媒介长度改变时运行脚本。
onemptied 当媒介资源元素突然为空时(网络错误、加载错误等)运行脚本。
onended 当媒介已抵达结尾时运行脚本。
onerror 当在元素加载期间发生错误时运行脚本。
onloadeddata 当加载媒介数据时运行脚本。
onloadedmetadata 当媒介元素的持续时间以及其它媒介数据已加载时运行脚本。
onloadstart 当浏览器开始加载媒介数据时运行脚本。
onpause 当媒介数据暂停时运行脚本。
onplay 当媒介数据将要开始播放时运行脚本。
onplaying 当媒介数据已开始播放时运行脚本。
onprogress 当浏览器正在取媒介数据时运行脚本。
onratechange 当媒介数据的播放速率改变时运行脚本。

6.3.8 事件的冒泡(Bubble)

所谓的冒泡指的就是事件的向上传导,当后代元素上的事件被触发时,其祖先元素的相同事件也会被触发。在开发中大部分情况冒泡都是有用的,如果不希望发生事件冒泡可以通过事件对象来取消冒泡

取消冒泡:可以将事件对象的cancelBubble设置为true,即可取消冒泡

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style type="text/css">
            #box1{
                width: 200px;
                height: 200px;
                background-color: yellowgreen;
            }

            #s1{
                background-color: yellow;
            }


        </style>
        <script type="text/javascript">

            window.onload = function(){

                /*
                 * 事件的冒泡(Bubble)
                 *     - 所谓的冒泡指的就是事件的向上传导,当后代元素上的事件被触发时,其祖先元素的相同事件也会被触发
                 *     - 在开发中大部分情况冒泡都是有用的,如果不希望发生事件冒泡可以通过事件对象来取消冒泡
                 * 
                 */

                //为s1绑定一个单击响应函数
                var s1 = document.getElementById("s1");
                s1.onclick = function(event){
                    event = event || window.event;
                    alert("我是span的单击响应函数");

                    //取消冒泡
                    //可以将事件对象的cancelBubble设置为true,即可取消冒泡
                    event.cancelBubble = true;
                };

                //为box1绑定一个单击响应函数
                var box1 = document.getElementById("box1");
                box1.onclick = function(event){
                    event = event || window.event;
                    alert("我是div的单击响应函数");

                    event.cancelBubble = true;
                };

                //为body绑定一个单击响应函数
                document.body.onclick = function(){
                    alert("我是body的单击响应函数");
                };        
            };

        </script>
    </head>
    <body>        
        <div id="box1">
            我是box1
            <span id="s1">我是span</span>
        </div>    
    </body>
</html>

6.3.9 事件的委派

我们希望,只绑定一次事件,即可应用到多个的元素上,即使元素是后添加的
我们可以尝试将其绑定给元素的共同的祖先元素

事件的委派
指将事件统一绑定给元素的共同的祖先元素,这样当后代元素上的事件触发时,会一直冒泡到祖先元素,从而通过祖先元素的响应函数来处理事件。

事件委派是利用了冒泡,通过委派可以减少事件绑定的次数,提高程序的性能

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title></title>
        <script type="text/javascript">

            window.onload = function(){

                var u1 = document.getElementById("u1");

                //点击按钮以后添加超链接
                var btn01 = document.getElementById("btn01");
                btn01.onclick = function(){
                    //创建一个li
                    var li = document.createElement("li");
                    li.innerHTML = "<a href='javascript:;' class='link'>新建的超链接</a>";

                    //将li添加到ul中
                    u1.appendChild(li);
                };


                /*
                 * 为每一个超链接都绑定一个单击响应函数
                 * 这里我们为每一个超链接都绑定了一个单击响应函数,这种操作比较麻烦,
                 *     而且这些操作只能为已有的超链接设置事件,而新添加的超链接必须重新绑定
                 */
                //获取所有的a
                var allA = document.getElementsByTagName("a");
                //遍历
                /*for(var i=0 ; i<allA.length ; i++){
                    allA[i].onclick = function(){
                        alert("我是a的单击响应函数!!!");
                    };
                }*/


                //为ul绑定一个单击响应函数
                u1.onclick = function(event){
                    event = event || window.event;

                    /*
                     * target
                     *     - event中的target表示的触发事件的对象
                     */
                    //alert(event.target);


                    //如果触发事件的对象是我们期望的元素,则执行否则不执行
                    if(event.target.className == "link"){
                        alert("我是ul的单击响应函数");
                    }

                };

            };

        </script>
    </head>
    <body>
        <button id="btn01">添加超链接</button>

        <ul id="u1" style="background-color: #bfa;">
            <li>
                <p>我是p元素</p>
            </li>
            <li><a href="javascript:;" class="link">超链接一</a></li>
            <li><a href="javascript:;" class="link">超链接二</a></li>
            <li><a href="javascript:;" class="link">超链接三</a></li>
        </ul>

    </body>
</html>

6.3.10 事件的绑定

使用 对象.事件 = 函数 的形式绑定响应函数,
它只能同时为一个元素的一个事件绑定一个响应函数,
不能绑定多个,如果绑定了多个,则后边会覆盖掉前边的

addEventListener()
通过这个方法也可以为元素绑定响应函数
参数:
1.事件的字符串,不要on
2.回调函数,当事件触发时该函数会被调用
3.是否在捕获阶段触发事件,需要一个布尔值,一般都传false
使用addEventListener()可以同时为一个元素的相同事件同时绑定多个响应函数,
这样当事件被触发时,响应函数将会按照函数的绑定顺序执行

这个方法不支持IE8及以下的浏览器

attachEvent()

    • 在IE8中可以使用attachEvent()来绑定事件
    • 参数:
  • 1.事件的字符串,要on
  • 2.回调函数
    *
    • 这个方法也可以同时为一个事件绑定多个处理函数,
  • 不同的是它是后绑定先执行,执行顺序和addEventListener()相反
function bind(obj , eventStr , callback){
    if(obj.addEventListener){
        //大部分浏览器兼容的方式
        obj.addEventListener(eventStr , callback , false);
    }else{
        /*
        * this是谁由调用方式决定
        * callback.call(obj)
        */
        //IE8及以下
        obj.attachEvent("on"+eventStr , function(){
            //在匿名函数中调用回调函数
            callback.call(obj);
        });
    }
}

6.3.11事件的传播

关于事件的传播网景公司和微软公司有不同的理解
微软公司认为事件应该是由内向外传播,也就是当事件触发时,应该先触发当前元素上的事件,然后再向当前元素的祖先元素上传播,也就说事件应该在冒泡阶段执行。
网景公司认为事件应该是由外向内传播的,也就是当前事件触发时,应该先触发当前元素的最外层的祖先元素的事件,然后在向内传播给后代元素

W3C综合了两个公司的方案,将事件传播分成了三个阶段
1.捕获阶段
在捕获阶段时从最外层的祖先元素,向目标元素进行事件的捕获,但是默认此时不会触发事件
2.目标阶段
事件捕获到目标元素,捕获结束开始在目标元素上触发事件
3.冒泡阶段
事件从目标元素向他的祖先元素传递,依次触发祖先元素上的事件

如果希望在捕获阶段就触发事件,可以将addEventListener()的第三个参数设置为true
IE8及以下的浏览器中没有捕获阶段

function bind(obj , eventStr , callback){
    if(obj.addEventListener){
        //大部分浏览器兼容的方式
        obj.addEventListener(eventStr , callback , true);
    }else{
        /*
        * this是谁由调用方式决定
        * callback.call(obj)
        */
        //IE8及以下
        obj.attachEvent("on"+eventStr , function(){
            //在匿名函数中调用回调函数
            callback.call(obj);
        });
    }
}

第七章 BOM

7.1 概述

浏览器对象模型

BOM可以使我们通过JS来操作浏览器

在BOM中为我们提供了一组对象,用来完成对浏览器的操作

BOM对象

  • Window

    • 代表的是整个浏览器的窗口,同时window也是网页中的全局对象
      
  • Navigator

    • 代表的当前浏览器的信息,通过该对象可以来识别不同的浏览器
      
  • Location

    • 代表当前浏览器的地址栏信息,通过Location可以获取地址栏信息,或者操作浏览器跳转页面
      
  • History

    • 代表浏览器的历史记录,可以通过该对象来操作浏览器的历史记录
    •      由于隐私原因,该对象不能获取到具体的历史记录,只能操作浏览器向前或向后翻页
      
  •      而且该操作只在当次访问时有效
    
  • Screen

    • 代表用户的屏幕的信息,通过该对象可以获取到用户的显示器的相关的信息

7.1.1 Navigator

代表的当前浏览器的信息,通过该对象可以来识别不同的浏览器

由于历史原因,Navigator对象中的大部分属性都已经不能帮助我们识别浏览器了

一般我们只会使用userAgent来判断浏览器的信息,

userAgent是一个字符串,这个字符串中包含有用来描述浏览器信息的内容,

不同的浏览器会有不同的userAgent

对浏览器类型的判断:

var ua = navigator.userAgent;
console.log(ua);

if(/firefox/i.test(ua)){
    alert("你是火狐!!!");
}else if(/chrome/i.test(ua)){
    alert("你是Chrome");
}else if(/msie/i.test(ua)){
    alert("你是IE浏览器~~~");
}else if("ActiveXObject" in window){
    alert("你是IE11,枪毙了你~~~");
}

7.1.2 History

对象可以用来操作浏览器向前或向后翻页

length属性:可以获取到当成访问的链接数量
back():可以用来回退到上一个页面,作用和浏览器的回退按钮一样
forward():可以跳转下一个页面,作用和浏览器的前进按钮一样 go():可以用来跳转到指定的页面
它需要一个整数作为参数
1:表示向前跳转一个页面 相当于forward()
2:表示向前跳转两个页面
-1:表示向后跳转一个页面
-2:表示向后跳转两个页面

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script type="text/javascript">

            window.onload = function(){

                //获取按钮对象
                var btn = document.getElementById("btn");

                btn.onclick = function(){

                    alert(history.length);

                    history.back();

                    history.forward();

                    history.go(-2);
                };

            };

        </script>
    </head>
    <body>

        <button id="btn">点我一下</button>

        <h1>History</h1>

        <a href="01.BOM.html">去BOM</a>
    </body>
</html>

7.1.3 Location

该对象中封装了浏览器的地址栏的信息

如果直接打印location,则可以获取到地址栏的信息(当前页面的完整路径)

如果直接将location属性修改为一个完整的路径,或相对路径。则我们页面会自动跳转到该路径,并且会生成相应的历史记录

assign():用来跳转到其他的页面,作用和直接修改location一样

reload():用于重新加载当前页面,作用和刷新按钮一样。如果在方法中传递一个true,作为参数,则会强制清空缓存刷新页面

replace():可以使用一个新的页面替换当前页面,调用完毕也会跳转页面。不会生成历史记录,不能使用回退按钮回退

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script type="text/javascript">

            window.onload = function(){

                //获取按钮对象
                var btn = document.getElementById("btn");

                btn.onclick = function(){

                    //如果直接打印location,则可以获取到地址栏的信息(当前页面的完整路径)
                    alert(location);

                    /*
                     * 如果直接将location属性修改为一个完整的路径,或相对路径
                     *     则我们页面会自动跳转到该路径,并且会生成相应的历史记录
                     */
                    location = "http://www.baidu.com";

                    location.assign("http://www.baidu.com");

                    location.reload(true);

                    location.replace("01.BOM.html");

                };    
            };

        </script>
    </head>
    <body>

        <button id="btn">点我一下</button>

        <h1>Location</h1>

        <input type="text" />
        <a href="01.BOM.html">去BOM</a>
    </body>
</html>

7.2 定时

JS的程序的执行速度是非常非常快的。如果希望一段程序,可以每间隔一段时间执行一次,可以使用定时调用

setInterval()

定时调用
可以将一个函数,每隔一段时间执行一次
参数:
1.回调函数,该函数会每隔一段时间被调用一次
2.每次调用间隔的时间,单位是毫秒
返回值:
返回一个Number类型的数据
这个数字用来作为定时器的唯一标识

clearInterval()

方法中需要一个定时器的标识作为参数,这样将关闭标识对应的定时器

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script type="text/javascript">

            window.onload = function(){

                //获取count
                var count = document.getElementById("count");

                var num = 1;

                var timer = setInterval(function(){

                    count.innerHTML = num++;

                    if(num == 11){
                        //关闭定时器
                        clearInterval(timer);
                    }

                },1000);

                //console.log(timer);

                clearInterval(timer);
            };
        </script>
    </head>
    <body>
        <h1 id="count"></h1>
    </body>
</html>

案例:图片自动轮播

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script type="text/javascript">

            window.onload = function(){

                /*
                 * 使图片可以自动切换
                 */

                //获取img标签
                var img1 = document.getElementById("img1");

                //创建一个数组来保存图片的路径
                var imgArr = ["img/1.jpg","img/2.jpg","img/3.jpg","img/4.jpg","img/5.jpg"];

                //创建一个变量,用来保存当前图片的索引
                var index = 0;

                //定义一个变量,用来保存定时器的标识
                var timer;

                //为btn01绑定一个单击响应函数
                var btn01 = document.getElementById("btn01");
                btn01.onclick = function(){

                    /*
                     * 目前,我们每点击一次按钮,就会开启一个定时器,
                     *     点击多次就会开启多个定时器,这就导致图片的切换速度过快,
                     *     并且我们只能关闭最后一次开启的定时器
                     */

                    //在开启定时器之前,需要将当前元素上的其他定时器关闭
                    clearInterval(timer);

                    /*
                     * 开启一个定时器,来自动切换图片
                     */
                    timer = setInterval(function(){
                        //使索引自增
                        index++;
                        //判断索引是否超过最大索引
                        /*if(index >= imgArr.length){
                            //则将index设置为0
                            index = 0;
                        }*/
                        index %= imgArr.length;
                        //修改img1的src属性
                        img1.src = imgArr[index];

                    },1000);
                };

                //为btn02绑定一个单击响应函数
                var btn02 = document.getElementById("btn02");
                btn02.onclick = function(){
                    //点击按钮以后,停止图片的自动切换,关闭定时器
                    /*
                     * clearInterval()可以接收任意参数,
                     *     如果参数是一个有效的定时器的标识,则停止对应的定时器
                     *     如果参数不是一个有效的标识,则什么也不做
                     */
                    clearInterval(timer);

                };


            };

        </script>
    </head>
    <body>

        <img id="img1" src="img/1.jpg"/>
        <br /><br />
        <button id="btn01">开始</button>
        <button id="btn02">停止</button>
    </body>
</html>

延时调用

延时调用一个函数不马上执行,而是隔一段时间以后在执行,而且只会执行一次

延时调用和定时调用的区别,定时调用会执行多次,而延时调用只会执行一次
延时调用和定时调用实际上是可以互相代替的,在开发中可以根据自己需要去选择

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script type="text/javascript">

            var num = 1;

            var timer = setTimeout(function(){
                console.log(num++);
            },3000);

            //使用clearTimeout()来关闭一个延时调用
            clearTimeout(timer);

        </script>
    </head>
    <body>
    </body>
</html>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style type="text/css">

            *{
                margin: 0;
                padding: 0;
            }

            #box1{
                width: 100px;
                height: 100px;
                background-color: red;
                position: absolute;
                left: 0;
            }

        </style>

        <script type="text/javascript">

            window.onload = function(){

                //获取box1
                var box1 = document.getElementById("box1");
                //获取btn01
                var btn01 = document.getElementById("btn01");

                //获取btn02
                var btn02 = document.getElementById("btn02");


                //点击按钮以后,使box1向右移动(left值增大)
                btn01.onclick = function(){
                    move(box1 , 800 , 10);
                };


                //点击按钮以后,使box1向左移动(left值减小)
                btn02.onclick = function(){
                    move(box1 , 0 , 10);
                };

            };

            //定义一个变量,用来保存定时器的标识
            var timer;

            //尝试创建一个可以执行简单动画的函数
            /*
             * 参数:
             *     obj:要执行动画的对象
             *     target:执行动画的目标位置
             *     speed:移动的速度(正数向右移动,负数向左移动)
             */
            function move(obj , target ,speed){
                //关闭上一个定时器
                clearInterval(timer);

                //获取元素目前的位置
                var current = parseInt(getStyle(obj,"left"));

                //判断速度的正负值
                //如果从0 向 800移动,则speed为正
                //如果从800向0移动,则speed为负
                if(current > target){
                    //此时速度应为负值
                    speed = -speed;
                }

                //开启一个定时器,用来执行动画效果
                timer = setInterval(function(){

                    //获取box1的原来的left值
                    var oldValue = parseInt(getStyle(obj,"left"));

                    //在旧值的基础上增加
                    var newValue = oldValue + speed;

                    //判断newValue是否大于800
                    //从800 向 0移动
                    //向左移动时,需要判断newValue是否小于target
                    //向右移动时,需要判断newValue是否大于target
                    if((speed < 0 && newValue < target) || (speed > 0 && newValue > target)){
                        newValue = target;
                    }

                    //将新值设置给box1
                    obj.style.left = newValue + "px";

                    //当元素移动到0px时,使其停止执行动画
                    if(newValue == target){
                        //达到目标,关闭定时器
                        clearInterval(timer);
                    }


                },30);
            }


            /*
             * 定义一个函数,用来获取指定元素的当前的样式
             * 参数:
             *         obj 要获取样式的元素
             *         name 要获取的样式名
             */
            function getStyle(obj , name){

                if(window.getComputedStyle){
                    //正常浏览器的方式,具有getComputedStyle()方法
                    return getComputedStyle(obj , null)[name];
                }else{
                    //IE8的方式,没有getComputedStyle()方法
                    return obj.currentStyle[name];
                }

            }


        </script>
    </head>
    <body>

        <button id="btn01">点击按钮以后box1向右移动</button>
        <button id="btn02">点击按钮以后box1向左移动</button>

        <br /><br />

        <div id="box1"></div>

        <div style="width: 0; height: 1000px; border-left:1px black solid; position: absolute; left: 800px;top:0;"></div>

    </body>
</html>

案例:图片轮播

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>

        <style type="text/css">
            *{
                margin: 0;
                padding: 0;
            }

            /*
             * 设置outer的样式
             */
            #outer{
                /*设置宽和高*/
                width: 520px;
                height: 333px;
                /*居中*/
                margin: 50px auto;
                /*设置背景颜色*/
                background-color: greenyellow;
                /*设置padding*/
                padding: 10px 0;
                /*开启相对定位*/
                position: relative;
                /*裁剪溢出的内容*/
                overflow: hidden;
            }

            /*设置imgList*/
            #imgList{
                /*去除项目符号*/
                list-style: none;
                /*设置ul的宽度*/
                /*width: 2600px;*/
                /*开启绝对定位*/
                position: absolute;
                /*设置偏移量*/
                /*
                 * 每向左移动520px,就会显示到下一张图片
                 */
                left: 0px;
            }

            /*设置图片中的li*/
            #imgList li{
                /*设置浮动*/
                float: left;
                /*设置左右外边距*/
                margin: 0 10px;
            }

            /*设置导航按钮*/
            #navDiv{
                /*开启绝对定位*/
                position: absolute;
                /*设置位置*/
                bottom: 15px;
                /*设置left值
                     outer宽度  520
                     navDiv宽度 25*5 = 125
                         520 - 125 = 395/2 = 197.5
                 * */
                /*left: 197px;*/
            }

            #navDiv a{
                /*设置超链接浮动*/
                float: left;
                /*设置超链接的宽和高*/
                width: 15px;
                height: 15px;
                /*设置背景颜色*/
                background-color: red;
                /*设置左右外边距*/
                margin: 0 5px;
                /*设置透明*/
                opacity: 0.5;
                /*兼容IE8透明*/
                filter: alpha(opacity=50);
            }

            /*设置鼠标移入的效果*/
            #navDiv a:hover{
                background-color: black;
            }
        </style>

        <!--引用工具-->
        <script type="text/javascript" src="js/tools.js"></script>
        <script type="text/javascript">
            window.onload = function(){
                //获取imgList
                var imgList = document.getElementById("imgList");
                //获取页面中所有的img标签
                var imgArr = document.getElementsByTagName("img");
                //设置imgList的宽度
                imgList.style.width = 520*imgArr.length+"px";

                /*设置导航按钮居中*/
                //获取navDiv
                var navDiv = document.getElementById("navDiv");
                //获取outer
                var outer = document.getElementById("outer");
                //设置navDiv的left值
                navDiv.style.left = (outer.offsetWidth - navDiv.offsetWidth)/2 + "px";

                //默认显示图片的索引
                var index = 0;
                //获取所有的a
                var allA = document.getElementsByTagName("a");
                //设置默认选中的效果
                allA[index].style.backgroundColor = "black";

                /*
                     点击超链接切换到指定的图片
                         点击第一个超链接,显示第一个图片
                         点击第二个超链接,显示第二个图片
                 * */

                //为所有的超链接都绑定单击响应函数
                for(var i=0; i<allA.length ; i++){

                    //为每一个超链接都添加一个num属性
                    allA[i].num = i;

                    //为超链接绑定单击响应函数
                    allA[i].onclick = function(){

                        //获取点击超链接的索引,并将其设置为index
                        index = this.num;

                        //切换图片
                        /*
                         * 第一张  0 0
                         * 第二张  1 -520
                         * 第三张  2 -1040
                         */
                        //imgList.style.left = -520*index + "px";
                        //设置选中的a
                        setA();

                        //使用move函数来切换图片
                        move(imgList , "left" , -520*index , 20 , function(){

                        });

                    };
                }

                //创建一个方法用来设置选中的a
                function setA(){

                    //遍历所有a,并将它们的背景颜色设置为红色
                    for(var i=0 ; i<allA.length ; i++){
                        allA[i].style.backgroundColor = "";
                    }

                    //将选中的a设置为黑色
                    allA[index].style.backgroundColor = "black";
                };


            };

        </script>
    </head>
    <body>
        <!-- 创建一个外部的div,来作为大的容器 -->
        <div id="outer">
            <!-- 创建一个ul,用于放置图片 -->
            <ul id="imgList">
                <li><img src="img/1.jpg"/></li>
                <li><img src="img/2.jpg"/></li>
                <li><img src="img/3.jpg"/></li>
                <li><img src="img/4.jpg"/></li>
                <li><img src="img/5.jpg"/></li>
            </ul>
            <!--创建导航按钮-->
            <div id="navDiv">
                <a href="javascript:;"></a>
                <a href="javascript:;"></a>
                <a href="javascript:;"></a>
                <a href="javascript:;"></a>
                <a href="javascript:;"></a>
            </div>
        </div>
    </body>
</html>

案例:二级菜单

<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <title>二级菜单</title>
        <style type="text/css">
            * {
                margin: 0;
                padding: 0;
                list-style-type: none;
            }

            a,img {
                border: 0;
                text-decoration: none;
            }

            body {
                font: 12px/180% Arial, Helvetica, sans-serif, "新宋体";
            }
        </style>

        <link rel="stylesheet" type="text/css" href="css/sdmenu.css" />

        <script type="text/javascript" src="js/tools.js"></script>
        <script type="text/javascript">
            window.onload = function(){

                /*
                 * 我们的每一个菜单都是一个div
                 *     当div具有collapsed这个类时,div就是折叠的状态
                 *     当div没有这个类是,div就是展开的状态
                 */

                /*
                 * 点击菜单,切换菜单的显示状态
                 */
                //获取所有的class为menuSpan的元素
                var menuSpan = document.querySelectorAll(".menuSpan");

                //定义一个变量,来保存当前打开的菜单
                var openDiv = menuSpan[0].parentNode;

                //为span绑定单击响应函数
                for(var i=0 ; i<menuSpan.length ; i++){
                    menuSpan[i].onclick = function(){

                        //this代表我当前点击的span
                        //获取当前span的父元素
                        var parentDiv = this.parentNode;

                        //切换菜单的显示状态
                        toggleMenu(parentDiv);


                        //判断openDiv和parentDiv是否相同
                        if(openDiv != parentDiv  && !hasClass(openDiv , "collapsed")){
                            //打开菜单以后,应该关闭之前打开的菜单
                            //为了可以统一处理动画过渡效果,我们希望在这将addClass改为toggleClass
                            //addClass(openDiv , "collapsed");
                            //此处toggleClass()不需要有移除的功能
                            //toggleClass(openDiv , "collapsed");
                            //切换菜单的显示状态
                            toggleMenu(openDiv);
                        }

                        //修改openDiv为当前打开的菜单
                        openDiv = parentDiv;

                    };
                }

                /*
                 * 用来切换菜单折叠和显示状态
                 */
                function toggleMenu(obj){
                    //在切换类之前,获取元素的高度
                    var begin = obj.offsetHeight;

                    //切换parentDiv的显示
                    toggleClass(obj , "collapsed");

                    //在切换类之后获取一个高度
                    var end = obj.offsetHeight;

                    //console.log("begin = "+begin +" , end = "+end);
                    //动画效果就是将高度从begin向end过渡
                    //将元素的高度重置为begin
                    obj.style.height = begin + "px";

                    //执行动画,从bengin向end过渡
                    move(obj,"height",end,10,function(){
                        //动画执行完毕,内联样式已经没有存在的意义了,删除之
                        obj.style.height = "";
                    });

                }


            };


        </script>

    </head>

    <body>

        <div id="my_menu" class="sdmenu">
            <div>
                <span class="menuSpan">在线工具</span>
                <a href="#">图像优化</a>
                <a href="#">收藏夹图标生成器</a>
                <a href="#">邮件</a>
                <a href="#">htaccess密码</a>
                <a href="#">梯度图像</a>
                <a href="#">按钮生成器</a>
            </div>
            <div class="collapsed">
                <span class="menuSpan">支持我们</span>
                <a href="#">推荐我们</a>
                <a href="#">链接我们</a>
                <a href="#">网络资源</a>
            </div>
            <div class="collapsed">
                <span class="menuSpan">合作伙伴</span>
                <a href="#">JavaScript工具包</a>
                <a href="#">CSS驱动</a>
                <a href="#">CodingForums</a>
                <a href="#">CSS例子</a>
            </div>
            <div class="collapsed">
                <span class="menuSpan">测试电流</span>
                <a href="#">Current or not</a>
                <a href="#">Current or not</a>
                <a href="#">Current or not</a>
                <a href="#">Current or not</a>
            </div>
        </div>
    </body>
</html>

第八章 高级语法

8.1 JSON

JS中的对象只有JS自己认识,其他的语言都不认识

JSON就是一个特殊格式的字符串,这个字符串可以被任意的语言所识别并且可以转换为任意语言中的对象,JSON在开发中主要用来数据的交互

JSON

  • JavaScript Object Notation JS对象表示法

  • JSON和JS对象的格式一样,只不过JSON字符串中的属性名必须加双引号。其他的和JS语法一致

JSON分类:

  • 1.对象 {}

  • 2.数组 []

JSON中允许的值:

  •    1.字符串
    
  •    2.数值
    
  •    3.布尔值
    
  •    4.null
    
  •    5.对象
    
  •    6.数组
    

8.1.1 创建一个对象

var arr = '[1,2,3,"hello",true]';

var obj2 = '{"arr":[1,2,3]}';

var arr2 ='[{"name":"孙悟空","age":18,"gender":"男"},{"name":"孙悟空","age":18,"gender":"男"}]';

将JSON字符串转换为JS中的对象
在JS中,为我们提供了一个工具类,就叫JSON
这个对象可以帮助我们将一个JSON转换为JS对象,也可以将一个JS对象转换为JSON

var json = '{"name":"孙悟空","age":18,"gender":"男"}';

8.1.2 JSON.parse()

可以将以JSON字符串转换为js对象
它需要一个JSON字符串作为参数,会将该字符串转换为JS对象并返回

var o = JSON.parse(json);

8.1.3 JSON.stringify()

可以将一个JS对象转换为JSON字符串
需要一个js对象作为参数,会返回一个JSON字符串

var obj3 = {name:"猪八戒" , age:28 , gender:"男"};
var str = JSON.stringify(obj3);
console.log(str);

8.1.4 eval()

eval()

这个函数可以用来执行一段字符串形式的JS代码,并将执行结果返回

如果使用eval()执行的字符串中含有{},它会将{}当成是代码块

如果不希望将其当成代码块解析,则需要在字符串前后各加一个()

但是在开发中尽量不要使用,首先它的执行性能比较差,然后它还具有安全隐患

var str = '{"name":"孙悟空","age":18,"gender":"男"}';
var obj = eval("("+str+")");
console.log(obj);

8.2 基础总结深入

8.2.1 数据类型

8.2.1.1 分类(2大类)

  • 基本(值)类型

    • Number: 任意数值
    • String: 任意文本
    • Boolean: true/false
    • undefined: undefined
    • null: null
  • 对象(引用)类型

    • Object: 任意对象
    • Array: 特别的对象类型(下标/内部数据有序)
    • Function: 特别的对象类型(可执行)

8.2.1.2 判断

  • typeof:

    • 可以区别: 数值, 字符串, 布尔值, undefined, function
    • 不能区别: null与对象, 一般对象与数组
  • instanceof

    • 专门用来判断对象数据的类型: Object, Array与Function
  • ===

    • 可以判断: undefined和null

8.2.1.3 undefined与null的区别?

  • undefined代表没有赋值
  • null代表赋值了, 只是值为null

8.2.1.4 什么时候给变量赋值为null呢?

  • var a = null //a将指向一个对象, 但对象此时还没有确定
  • a = null //让a指向的对象成为垃圾对象

8.2.1.5 严格区别变量类型与数据类型?

  • js的变量本身是没有类型的, 变量的类型实际上是变量内存中数据的类型
  • 变量类型:

    • 基本类型: 保存基本类型数据的变量
    • 引用类型: 保存对象地址值的变量
  • 数据对象

    • 基本类型
    • 对象类型

8.2.2 数据 变量 内存

8.2.2.1 什么是数据?

  • 存储于内存中代表特定信息的’东东’, 本质就是0101二进制
  • 具有可读和可传递的基本特性
  • 万物(一切)皆数据, 函数也是数据
  • 程序中所有操作的目标: 数据

    • 算术运算
    • 逻辑运算
    • 赋值
    • 调用函数传参

8.2.2.2 什么是内存?

  • 内存条通电后产生的存储空间(临时的)
  • 产生和死亡: 内存条(集成电路板)>通电>产生一定容量的存储空间==>存储各种数据==>断电==>内存全部消失
  • 内存的空间是临时的, 而硬盘的空间是持久的
  • 一块内存包含2个数据

    • 内部存储的数据(一般数据/地址数据)
    • 内存地址值数据
  • 内存分类

    • 栈: 全局变量, 局部变量 (空间较小)
    • 堆: 对象 (空间较大)

8.2.2.3 什么是变量?

  • 值可以变化的量, 由变量名与变量值组成
  • 一个变量对应一块小内存, 变量名用来查找到内存, 变量值就是内存中保存的内容

8.2.2.4 内存,数据, 变量三者之间的关系

  • 内存是一个容器, 用来存储程序运行需要操作的数据
  • 变量是内存的标识, 我们通过变量找到对应的内存, 进而操作(读/写)内存中的数据

8.2.2.5 关于赋值和内存的问题

var a = xxx, a内存中到底保存的是什么?
xxx是基本数据, 保存的就是这个数据
xxx是对象, 保存的是对象的地址值
xxx是一个变量, 保存的xxx的内存内容(可能是基本数据, 也可能是地址值)

8.2.2.6 关于引用变量赋值问题

  • 2个引用变量指向同一个对象, 通过一个引用变量修改对象内部数据, 另一个引用变量也看得见
  • 2个引用变量指向同一个对象,让一个引用变量指向另一个对象, 另一个引用变量还是指向原来的对象

8.2.2.7 关于数据传递问题

问题: 在js调用函数时传递变量参数时, 是值传递还是引用传递

  • 只有值传递, 没有引用传递, 传递的都是变量的值, 只是这个值可能是基本数据, 也可能是地址(引用)数据
  • 如果后一种看成是引用传递, 那就值传递和引用传递都可以有

8.2.2.8 JS引擎如何管理内存?

  1. 内存生命周期
    1). 分配需要的内存
    2). 使用分配到的内存
    3). 不需要时将其释放/归还
  2. 释放内存
  • 为执行函数分配的栈空间内存: 函数执行完自动释放
  • 存储对象的堆空间内存: 当内存没有引用指向时, 对象成为垃圾对象, 垃圾回收器后面就会回收释放此内存

8.2.2.9 内存溢出与内存泄露

  1. 内存溢出
  • 一种程序运行出现的错误
  • 当程序运行需要的内存超过了剩余的内存时, 就出抛出内存溢出的错误
  1. 内存泄露
  • 占用的内存没有及时释放
  • 内存泄露积累多了就容易导致内存溢出
  • 常见的内存泄露:

    • 意外的全局变量
    • 没有及时清理的计时器或回调函数
    • 闭包

8.2.3 对象

8.2.3.1 什么是对象?

  • 代表现实中的某个事物, 是该事物在编程中的抽象
  • 多个数据的集合体(封装体)
  • 用于保存多个数据的容器

8.2.3.2 为什么要用对象?

  • 便于对多个数据进行统一管理

8.2.3.3 对象的组成

  • 属性

    • 代表现实事物的状态数据
    • 由属性名和属性值组成
    • 属性名都是字符串类型, 属性值是任意类型
  • 方法

    • 代表现实事物的行为数据
    • 是特别的属性==>属性值是函数

8.2.3.4 如何访问对象内部数据?

  • .属性名: 编码简单, 但有时不能用
  • [‘属性名’]: 编码麻烦, 但通用

8.2.3.5 什么时候必须使用[‘属性名’]的方式?

  • 属性名不是合法的标识名
  • 属性名不确定

8.2.4 函数

8.2.4.1 什么是函数?

  • 具有特定功能的n条语句的封装体
  • 只有函数是可执行的, 其它类型的数据是不可执行的
  • 函数也是对象

8.2.4.2 为什么要用函数?

  • 提高代码复用
  • 便于阅读和交流

8.2.4.3 如何定义函数?

  • 函数声明
  • 表达式
  function f1 () { // 函数声明
    console.log('f1()')
  }
  var f2 = function () { // 表达式
    console.log('f2()')
  }

8.2.4.4 如何调用(执行)函数?

  • test()
  • new test()
  • obj.test()
  • test.call/apply(obj)

8.2.4.5 回调函数

8.2.4.5.1 什么函数才是回调函数?
  • 你定义的
  • 你没有直接调用
  • 但最终它执行了(在特定条件或时刻)

8.2.4.5.2 常见的回调函数?
  • DOM事件函数

  • 定时器函数

  • ajax回调函数

  • 生命周期回调函数

  //1. DOM事件函数
  var btn = document.getElementById('btn')
  btn.onclick = function () {
    alert(this.innerHTML)
  }

  //2. 定时器函数
  setInterval(function () {
    alert('到点啦!')
  }, 2000)

8.2.4.6 IIEF

  • 全称: Immediately-Invoked Function Expression 立即调用函数表达式
  • 别名: 匿名函数自调用

8.2.4.6.1 作用
  • 隐藏内部实现
  • 不污染外部命名空间
(function (i) {
    var a = 4
    function fn() {
      console.log('fn ', i+a)
    }
    fn()
  })(3)

8.2.4.7 函数中的this

8.2.4.7.1 this是什么?
  • 一个关键字, 一个内置的引用变量
  • 在函数中都可以直接使用this
  • this代表调用函数的当前对象
  • 在定义函数时, this还没有确定, 只有在执行时才动态确定(绑定)的

8.1.4.7.2 如何确定this的值?
  • test()
  • obj.test()
  • new test()
  • test.call(obj)
    前置知识:
  • 本质上任何函数在执行时都是通过某个对象调用的

8.3 函数高级

8.3.1 原型与原型链

8.3.1.1 原型

8.3.1.1.1 函数的prototype属性(图)
  • 每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)
  • 原型对象中有一个属性constructor, 它指向函数对象

8.3.1.1.2 给原型对象添加属性(一般都是方法)
  • 作用: 函数的所有实例对象自动拥有原型中的属性(方法)
function F() {

  }
  F.prototype.age = 12 //添加属性
  F.prototype.setAge = function (age) { // 添加方法
    this.age = age
  }
  // 创建函数的实例对象
  var f = new F()
  console.log(f.age)
  f.setAge(23)
  console.log(f.age)

8.3.1.2 显式原型与隐式原型

  1. 每个函数function都有一个prototype,即显式原型
  2. 每个实例对象都有一个proto,可称为隐式原型
  3. 对象的隐式原型的值为其对应构造函数的显式原型的值
  4. 内存结构

JavaScript - 图7

  1. 总结:
  • 函数的prototype属性: 在定义函数时自动添加的, 默认值是一个空Object对象
  • 对象的proto属性: 创建对象时自动添加的, 默认值为构造函数的prototype属性值
  • 程序员能直接操作显式原型, 但不能直接操作隐式原型(ES6之前)

8.3.1.3 原型链

  1. 原型链
  • 访问一个对象的属性时,

    • 先在自身属性中查找,找到返回
    • 如果没有, 再沿着proto这条链向上查找, 找到返回
    • 如果最终没找到, 返回undefined
  • 别名: 隐式原型链
  • 作用: 查找对象的属性(方法)
  1. 构造函数/原型/实体对象的关系
  • 函数的显示原型指向的对象默认是空Object实例对象(但Object不满足)
  • 所有函数都是Function的实例(包含Function)
  • Object的原型对象是原型链尽头

8.3.1.4 原型链_属性问题

  1. 读取对象的属性值时: 会自动到原型链中查找
  2. 设置对象的属性值时: 不会查找原型链, 如果当前对象中没有此属性, 直接添加此属性并设置其值
  3. 方法一般定义在原型中, 属性一般通过构造函数定义在对象本身上

8.3.1.5 探索instanceof

  1. instanceof是如何判断的?
  • 表达式:A instanceof B
  • 如果B函数的显式原型对象在A对象的原型链上, 返回true, 否则返回false
  1. Function是通过new自己产生的实例

8.3.2 执行上下文与执行上下文栈

8.3.2.1 变量提升与函数提升

  1. 变量声明提升
  • 通过var定义(声明)的变量, 在定义语句之前就可以访问到
  • 值: undefined
  1. 函数声明提升
  • 通过function声明的函数, 在之前就可以直接调用
  • 值: 函数定义(对象)
  1. 问题: 变量提升和函数提升是如何产生的?

8.3.2.2 执行上下文

  1. 代码分类
  • 全局代码
  • 函数(局部)代码
  1. 全局执行上下文
  • 在执行全局代码前将window确定为全局执行上下文
  • 对全局数据进行预处理

    • var定义的全局变量==>undefined, 添加为window的属性
    • function声明的全局函数==>赋值(fun), 添加为window的方法
    • this==>赋值(window)
  • 开始执行全局代码
  1. 函数执行上下文
  • 在调用函数, 准备执行函数体之前, 创建对应的函数执行上下文对象(虚拟的, 存在于栈中)
  • 对局部数据进行预处理

    • 形参变量==>赋值(实参)==>添加为执行上下文的属性
    • arguments==>赋值(实参列表), 添加为执行上下文的属性
    • var定义的局部变量==>undefined, 添加为执行上下文的属性
    • function声明的函数 ==>赋值(fun), 添加为执行上下文的方法
    • this==>赋值(调用函数的对象)
  • 开始执行函数体代码

8.3.2.3 执行上下文栈

  1. 在全局代码执行前, JS引擎就会创建一个栈来存储管理所有的执行上下文对象
  2. 在全局执行上下文(window)确定后, 将其添加到栈中(压栈)
  3. 在函数执行上下文创建后, 将其添加到栈中(压栈)
  4. 在当前函数执行完后,将栈顶的对象移除(出栈)
  5. 当所有的代码执行完后, 栈中只剩下window

8.3.3 作用域与作用域链

8.3.3.1 作用域

  1. 理解
  • 就是一块”地盘”, 一个代码段所在的区域
  • 它是静态的(相对于上下文对象), 在编写代码时就确定了
  1. 分类
  • 全局作用域
  • 函数作用域
  • 没有块作用域(ES6有了)
  1. 作用
  • 隔离变量,不同作用域下同名变量不会有冲突

8.3.3.2 作用域与执行上下文

  1. 区别1
  • 全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了。而不是在函数调用时
  • 全局执行上下文环境是在全局作用域确定之后, js代码马上执行之前创建
  • 函数执行上下文是在调用函数时, 函数体代码执行之前创建
  1. 区别2
  • 作用域是静态的, 只要函数定义好了就一直存在, 且不会再变化
  • 执行上下文是动态的, 调用函数时创建, 函数调用结束时就会自动释放
  1. 联系
  • 执行上下文(对象)是从属于所在的作用域
  • 全局上下文环境==>全局作用域
  • 函数上下文环境==>对应的函数使用域

8.3.3.3 作用域链

  1. 理解
  • 多个上下级关系的作用域形成的链, 它的方向是从下向上的(从内到外)
  • 查找变量时就是沿着作用域链来查找的
  1. 查找一个变量的查找规则
  • 在当前作用域下的执行上下文中查找对应的属性, 如果有直接返回, 否则进入2
  • 在上一级作用域的执行上下文中查找对应的属性, 如果有直接返回, 否则进入3
  • 再次执行2的相同操作, 直到全局作用域, 如果还找不到就抛出找不到的异常
  var a = 1
  function fn1() {
    var b = 2
    function fn2() {
      var c = 3
      console.log(c)//3
      console.log(b)//2
      console.log(a)//1
      console.log(d)// Uncaught ReferenceError: d is not defined
    }
    fn2()
  }
  fn1()

8.3.4 闭包

8.3.4.1 理解闭包

  1. 如何产生闭包?
  • 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时, 就产生了闭包
  1. 闭包到底是什么?
  • 使用chrome调试查看
  • 理解一: 闭包是嵌套的内部函数(绝大部分人)
  • 理解二: 包含被引用变量(函数)的对象(极少数人)
  • 注意: 闭包存在于嵌套的内部函数中
  1. 产生闭包的条件?
  • 函数嵌套
  • 内部函数引用了外部函数的数据(变量/函数)

8.3.4.2 常见的闭包

  1. 将函数作为另一个函数的返回值
  2. 将函数作为实参传递给另一个函数调用
  // 1. 将函数作为另一个函数的返回值
  function fn1() {
    var a = 2
    function fn2() {
      a++
      console.log(a)
    }
    return fn2
  }
  var f = fn1()
  f() // 3
  f() // 4
  // 2. 将函数作为实参传递给另一个函数调用
  function showDelay(msg, time) {
    setTimeout(function () {
      alert(msg)
    }, time)
  }
  showDelay('atguigu', 2000)

8.3.4.3 闭包的作用

  1. 使用函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期)
  2. 让函数外部可以操作(读写)到函数内部的数据(变量/函数)

问题:

  1. 函数执行完后, 函数内部声明的局部变量是否还存在?
    一般是不存在, 存在于闭中的变量才可能存在

  2. 在函数外部能直接访问函数内部的局部变量吗?
    不能, 但我们可以通过闭包让外部操作它

8.3.4.4 闭包的生命周期

  1. 产生: 在嵌套内部函数定义执行完时就产生了(不是在调用)
  2. 死亡: 在嵌套的内部函数成为垃圾对象时
  function fn1() {
    //此时闭包就已经产生了(函数提升, 内部函数对象已经创建了)
    var a = 2
    function fn2 () {
      a++
      console.log(a)
    }
    return fn2
  }
  var f = fn1()
  f() // 3
  f() // 4
  f = null //闭包死亡(包含闭包的函数对象成为垃圾对象)

8.3.4.5 闭包的应用_自定义JS模块

  • 具有特定功能的js文件
  • 将所有的数据和功能都封装在一个函数内部(私有的)
  • 只向外暴露一个包信n个方法的对象或函数
  • 模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能

8.3.4.6 闭包的缺点及解决

  1. 缺点
  • 函数执行完后, 函数内的局部变量没有释放, 占用内存时间会变长
  • 容易造成内存泄露
  1. 解决
  • 能不用闭包就不用
  • 及时释放

8.4 对象高级

8.4.1 对象创建模式

8.4.1.1 Object构造函数模式

  • 套路: 先创建空Object对象, 再动态添加属性/方法
  • 适用场景: 起始时不确定对象内部数据
  • 问题: 语句太多
  /*
  一个人: name:"Tom", age: 12
   */
  // 先创建空Object对象
  var p = new Object()
  p = {} //此时内部数据是不确定的
  // 再动态添加属性/方法
  p.name = 'Tom'
  p.age = 12
  p.setName = function (name) {
    this.name = name
  }

  //测试
  console.log(p.name, p.age)
  p.setName('Bob')
  console.log(p.name, p.age)

8.4.1.2 对象字面量模式

  • 套路: 使用{}创建对象, 同时指定属性/方法
  • 适用场景: 起始时对象内部数据是确定的
  • 问题: 如果创建多个对象, 有重复代码
var p = {
    name: 'Tom',
    age: 12,
    setName: function (name) {
      this.name = name
    }
  }

  //测试
  console.log(p.name, p.age)
  p.setName('JACK')
  console.log(p.name, p.age)

  var p2 = {  //如果创建多个对象代码很重复
    name: 'Bob',
    age: 13,
    setName: function (name) {
      this.name = name
    }
  }

8.4.1.3 工厂模式

  • 套路: 通过工厂函数动态创建对象并返回
  • 适用场景: 需要创建多个对象
  • 问题: 对象没有一个具体的类型, 都是Object类型
function createPerson(name, age) { //返回一个对象的函数===>工厂函数
    var obj = {
      name: name,
      age: age,
      setName: function (name) {
        this.name = name
      }
    }

    return obj
  }

  // 创建2个人
  var p1 = createPerson('Tom', 12)
  var p2 = createPerson('Bob', 13)

  // p1/p2是Object类型

  function createStudent(name, price) {
    var obj = {
      name: name,
      price: price
    }
    return obj
  }
  var s = createStudent('张三', 12000)
  // s也是Object

8.4.1.4 自定义构造函数模式

  • 套路: 自定义构造函数, 通过new创建对象
  • 适用场景: 需要创建多个类型确定的对象
  • 问题: 每个对象都有相同的数据, 浪费内存
//定义类型
  function Person(name, age) {
    this.name = name
    this.age = age
    this.setName = function (name) {
      this.name = name
    }
  }
  var p1 = new Person('Tom', 12)
  p1.setName('Jack')
  console.log(p1.name, p1.age)
  console.log(p1 instanceof Person)

  function Student (name, price) {
    this.name = name
    this.price = price
  }
  var s = new Student('Bob', 13000)
  console.log(s instanceof Student)

  var p2 = new Person('JACK', 23)
  console.log(p1, p2)

8.4.1.5 构造函数+原型的组合模式

  • 套路: 自定义构造函数, 属性在函数中初始化, 方法添加到原型上
  • 适用场景: 需要创建多个类型确定的对象
function Person(name, age) { //在构造函数中只初始化一般函数
    this.name = name
    this.age = age
  }
  Person.prototype.setName = function (name) {
    this.name = name
  }

  var p1 = new Person('Tom', 23)
  var p2 = new Person('Jack', 24)
  console.log(p1, p2)

8.4.2 继承模式

8.4.2.1 原型链继承

  1. 套路

    • 定义父类型构造函数

    • 给父类型的原型添加方法

    • 定义子类型的构造函数

    • 创建父类型的对象赋值给子类型的原型

    • 将子类型原型的构造属性设置为子类型

    • 给子类型原型添加方法

    • 创建子类型的对象: 可以调用父类型的方法

  2. 关键

    • 子类型的原型为父类型的一个实例对象
cript type="text/javascript">
  //父类型
  function Supper() {
    this.supProp = 'Supper property'
  }
  Supper.prototype.showSupperProp = function () {
    console.log(this.supProp)
  }

  //子类型
  function Sub() {
    this.subProp = 'Sub property'
  }

  // 子类型的原型为父类型的一个实例对象
  Sub.prototype = new Supper()
  // 让子类型的原型的constructor指向子类型
  Sub.prototype.constructor = Sub
  Sub.prototype.showSubProp = function () {
    console.log(this.subProp)
  }

  var sub = new Sub()
  sub.showSupperProp()
  // sub.toString()
  sub.showSubProp()

  console.log(sub)  // Sub

8.4.2.2 借用构造函数继承

  1. 套路:

    • 定义父类型构造函数

    • 定义子类型构造函数

    • 在子类型构造函数中调用父类型构造

  2. 关键:

    • 在子类型构造函数中通用call()调用父类型构造函数
function Person(name, age) {
    this.name = name
    this.age = age
  }
  function Student(name, age, price) {
    Person.call(this, name, age)  // 相当于: this.Person(name, age)
    /*this.name = name
    this.age = age*/
    this.price = price
  }

  var s = new Student('Tom', 20, 14000)
  console.log(s.name, s.age, s.price)

8.4.2.3 组合继承

  1. 利用原型链实现对父类型对象的方法继承
  2. 利用super()借用父类型构建函数初始化相同属性
function Person(name, age) {
    this.name = name
    this.age = age
  }
  Person.prototype.setName = function (name) {
    this.name = name
  }

  function Student(name, age, price) {
    Person.call(this, name, age)  // 为了得到属性
    this.price = price
  }
  Student.prototype = new Person() // 为了能看到父类型的方法
  Student.prototype.constructor = Student //修正constructor属性
  Student.prototype.setPrice = function (price) {
    this.price = price
  }

  var s = new Student('Tom', 24, 15000)
  s.setName('Bob')
  s.setPrice(16000)
  console.log(s.name, s.age, s.price)

8.5 线程机制和事件机制

8.5.1 进程与线程

  1. 进程:程序的一次执行, 它占有一片独有的内存空间
  2. 线程: CPU的基本调度单位, 是程序执行的一个完整流程
  3. 进程与线程
  • 一个进程中一般至少有一个运行的线程: 主线程
  • 一个进程中也可以同时运行多个线程, 我们会说程序是多线程运行的
  • 一个进程内的数据可以供其中的多个线程直接共享
  • 多个进程之间的数据是不能直接共享的
  1. 浏览器运行是单进程还是多进程?
  • 有的是单进程

    • firefox
    • 老版IE
  • 有的是多进程

    • chrome
    • 新版IE
  1. 如何查看浏览器是否是多进程运行的呢?
  • 任务管理器==>进程
  1. 浏览器运行是单线程还是多线程?
  • 都是多线程运行的

8.5.2 浏览器内核

  1. 什么是浏览器内核?
  • 支持浏览器运行的最核心的程序
  1. 不同的浏览器可能不太一样
  • Chrome, Safari: webkit
  • firefox: Gecko
  • IE: Trident
  • 360,搜狗等国内浏览器: Trident + webkit
  1. 内核由很多模块组成
  • html,css文档解析模块 : 负责页面文本的解析

  • dom/css模块 : 负责dom/css在内存中的相关处理

  • 布局和渲染模块 : 负责页面的布局和效果的绘制

  • 布局和渲染模块 : 负责页面的布局和效果的绘制

  • 定时器模块 : 负责定时器的管理

  • 网络请求模块 : 负责服务器请求(常规/Ajax)

  • 事件响应模块 : 负责事件的管理

8.5.3 定时器引发的思考

  1. 定时器真是定时执行的吗?
  • 定时器并不能保证真正定时执行
  • 一般会延迟一丁点(可以接受), 也有可能延迟很长时间(不能接受)
  1. 定时器回调函数是在分线程执行的吗?
  • 在主线程执行的, js是单线程的
  1. 定时器是如何实现的?
  • 事件循环模型

8.5.4 JS是单线程的

  1. 如何证明js执行是单线程的?
  • setTimeout()的回调函数是在主线程执行的
  • 定时器回调函数只有在运行栈中的代码全部执行完后才有可能执行
  1. 为什么js要用单线程模式, 而不用多线程模式?
  • JavaScript的单线程,与它的用途有关。
  • 作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。
  • 这决定了它只能是单线程,否则会带来很复杂的同步问题
  1. 代码的分类:
  • 初始化代码
  • 回调代码
  1. js引擎执行代码的基本流程
  • 先执行初始化代码: 包含一些特别的代码 回调函数(异步执行)

    • 设置定时器
    • 绑定事件监听
    • 发送ajax请求
  • 后面在某个时刻才会执行回调代码

8.5.5 事件循环模型

  1. 所有代码分类
  • 初始化执行代码(同步代码): 包含绑定dom事件监听, 设置定时器, 发送ajax请求的代码
  • 回调执行代码(异步代码): 处理回调逻辑
  1. js引擎执行代码的基本流程:
  • 初始化代码===>回调代码
  1. 模型的2个重要组成部分:
  • 事件(定时器/DOM事件/Ajax)管理模块
  • 回调队列
  1. 模型的运转流程
  • 执行初始化代码, 将事件回调函数交给对应模块管理
  • 当事件发生时, 管理模块会将回调函数及其数据添加到回调列队中
  • 只有当初始化代码执行完后(可能要一定时间), 才会遍历读取回调队列中的回调函数执行
function fn1() {
    console.log('fn1()')
  }
  fn1()
  document.getElementById('btn').onclick = function () {
    console.log('点击了btn')
  }
  setTimeout(function () {
    console.log('定时器执行了')
  }, 2000)
  function fn2() {
    console.log('fn2()')
  }
  fn2()

8.5.6 Web Workers

  1. H5规范提供了js分线程的实现, 取名为: Web Workers
  2. 相关API
  • Worker: 构造函数, 加载分线程执行的js文件
  • Worker.prototype.onmessage: 用于接收另一个线程的回调函数
  • Worker.prototype.postMessage: 向另一个线程发送消息
  1. 不足
  • worker内代码不能操作DOM(更新UI)
  • 不能跨域加载JS
  • 不是每个浏览器都支持这个新特性