JavaScript学习笔记--【黑马程序员】

目录

一、编程语言

1.1 编程

 1.2 计算机语言

 1.3 编程语言

1.4 翻译器 

 1.5 编程语言和标记语言区别

 总结

二、计算机基础

2.1 计算机组成

2.2.数据存储 

2.3 数据存储单位 

2.4 程序运行 

三、HTML中的JavaScript

1.元素

1.1 标签的位置

 1.2 延迟脚本--defer

1.3 异步脚本--async

 1.4 XHML中的变化

2.嵌入代码和外部文件

 3.元素

 4.小结

 四、初始JavaScript

1.1 Javascript是什么

1.2JavaScript的作用 

1.3 HTML/CSS/JS的关系 

1.4 浏览器执行JS简介 

1.5 JS组成

​编辑 1.6 JS初体验

1.6.1 行内式JS

1.6.2 内嵌JS

1.6.3 外部JS文件 

四、JavaScript注释

1.单行注释

2.多行注释 

五、Javascript输入输出语句

 六、变量

1.变量的概述

1.1 什么是变量

1.2 变量在内存中的存储

2.变量的使用

2.1 声明变量

2.2.赋值

2.3 变量的初始化

2.4 【案例】变量的使用

3.变量语法扩展

3.1 更新变量

3.2 同时声明多个变量

 3.3 声明变量特殊情况

4.变量命名规范

5.标识符

6.var 、 let、 const

6.1 var关键字--声明的是函数作用域

6.2 let关键字--声明的是块作用域

6.3 const声明

7.交换变量案例

七、数据类型

1.数据类型简介

1.1 为什么需要数据类型

1.2 变量的数据类型

1.3 数据类型的分类

2.简单数据类型(Number,String,Boolean,Undefined,Null)

2.1 数字型--Number

​编辑 2.2 字符串类型--String

 2.3 布尔值--Boolean

 2.4 Undefined和Null

3.获取变量数据类型(Object)

3.1 获取检测变量的数据类型--typeof 

3.2 字面量

4.数据类型转换

4.1 什么是数据类型转换

4.2 转换为字符串

4.3 转换为数字型(重点) 

​编辑

【案例】计算年龄

【简单加法器】

 4.4转换为布尔值

八、标识符,关键字,保留字

1.标识符

2.关键字

3.保留字

 九、JavaScript 运算符

1.运算符

2.算数运算符

2.1 算术运算符概述

 2.2 浮点数的精度问题

2.3 表达式和返回值

3.递增和递减运算符(一元运算符)

3.1 递增和递减运算符概述

3.2 递增运算符

  3.3 前置递增和后置递增小结

4.比较运算符

4.1 比较运算符概述

4.2 小结

5.逻辑运算符

5.1 逻辑运算符概述

5.2 逻辑与 &&

5.3 逻辑或 ||

5.4 逻辑非 ! 

5.5 短路运算(逻辑中断)

6.赋值运算符

7.运算符优先级

 8.练习

十、JavaScript 流程控制--分支

1.流程控制

2.顺序流程控制

3.分支流程控制if语句

3.1 分支结构

3.2 if语句

  3.3 if else语句(双分支语句)

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

4.三元表达式

4.1 语法结构

 4.2 执行思路

5.分支流程控制switch语句

5.1 语法结构

5.2 switch 语句和 if else if 语句的区别

十一、JavaScript 流程控制--循环

1.循环

2.for循环

2.1 语法结构

2.2 for循环重复相同的代码

2.3 for循环重复不相同的代码

3.双重for循环

3.1 双重for循环概述

 3.2 双重for循环语法

3.5 for循环小结

4.while循环

5.do while 循环

6.continue break

6.1 continue关键字

6.2 break关键字

十二、数组

1.数组的概念

2.创建数组

2.1 利用new创建数组--new Array()

 2.2 利用数组字面量创建数组

 2.4 数组元素的类型

3.获取数组中的元素

3.1 数组的索引

4.遍历数组

 4.1 数组的长度

​编辑

5.数组中新增元素

5.1 通过修改length长度新增数组元素

5.2 通过修改数组索引新增数组元素

【案例-数组新增元素】

【案例-筛选数组】

6.数组案例

十二、JavaScript 函数

1.函数的概念

2.函数的使用

2.1 声明函数

2.2 调用函数

2.3 函数的封装

3.函数的参数

3.1形参和实参

3.2 函数参数的传递过程

 3.3 函数形参和实参个数不匹配问题

3.4 小结

4.函数的返回值

4.1 return 语句

4.2 return终止函数

​编辑 4.3 return  的返回值

4.4 函数没有return 返回 undefined

4.5 break,continue,return 的区别

4.6 return 注意事项

5.arguments(伪数组)的使用

6.函数案例

【案例--利用函数封装方法,翻转任意一个数组】

【案例--利用函数封装方法,队数组排序--冒泡排序】

【案例--判断闰年】

函数可以调用另外一个函数

【案例-用户输入年份,输出当前年份2月份的天数】

7.函数的两种声明方式

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

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

十三、Javascript 作用域

1.作用域

2.变量的作用域

2.1 全局变量

2.2 局部变量

2.3 Javascript没有块级作用域

3.作用域链

​编辑

【案例】

十四、预解析

1.预解析

2.变量预解析和函数预解析

3.预解析案例

十五、Javascript 对象

1.对象

1.1 什么是对象

1.2 为什么需要对象

2.创建对象的三种方式

2.1 利用字面量创建对象

​编辑

2.2 利用new Object 创建对象

2.3 利用构造函数创建对象

​编辑

2.4 构造函数和对象

3.new关键字

4.遍历对象属性(for...in)--for( var k in obj) 

十六、JavaScript 内置对象

1.内置对象

​编辑

2.查文档

2.1 MDN

2.2 如何学习对象中的方法

3.Math对象

3.1 Math概述

3.2 随机数方法random()--[0,1)

4.日期对象

4.1 Date概述

 4.2 Date()方法的使用

4.3 日期格式化

4.4 获取日期的总的毫秒形式

【案例:倒计时效果】

5.数组对象

5.1 数组对象的创建

5.2 检测是否为数组----instanceof、Array.isArray()

 5.3 添加删除数组元素的方法

5.4 数组排序

5.5 数组索引方法

5.6 数组转换为字符串

5.7 其他

6.字符串对象

6.1 基本包装类型

6.2 字符串的不可变

6.3 根据字符返回位置

6.4 根据位置返回字符

6.5 字符串操作方法

6.6replace()方法

6.7 split()方法

6.8 其他

十七、JavaScript 简单类型与复杂类型

1.简单类型和复杂类型

​编辑

2.堆和栈

3.简单类型的内存分配

​编辑

4.复杂类型的内存分配

​编辑

5.简单类型传参

6.复杂类型传参

十八、Web APIs

1. Web APIs 和 JS 基础关联性

1.1 JS的组成

 1.2 JS基础阶段以及Web APIs阶段

2.API(接口)和Web API(浏览器接口)

2.3 API和Web API总结

 2.4 什么是DOM

2.5 DOM树

2.6 DOM对象

十九、DOM元素的获取

1.根据CSS选择器来获取DOM元素(必须是字符串,也就是必须加引号;如果是类选择器前面加“.";如果是id选择器前面加"#")

        1.1 选择匹配的第一个元素--document.querySelector('CSS选择器')

         1.2 选择匹配的多个元素(得到的是一个伪数组)--document.querySelectorAll('CSS选择器')

 2.其他获取DOM元素方法

        根据id获取一个元素-->document.getElementById('nav')

        根据标签获取一类元素 获取页面 所有div(返回的是伪数组) ---->document.getElementsByTagName('div')

        根据类名获取元素 获取页面 所有类名为w的 --->document.getElementByClassName('w')

 获取某一个元素(父元素)内部所有指定标签的子元素--element(父元素).getElementsByTagName('标签名')

3. 获取HTML和Body标签

二十、事件基础

1. 事件基础

2.事件三要素

1. 事件源 (谁)2. 事件类型 (什么事件)3. 事件处理程序 (做啥)

3.执行事件的步骤

1. 获取事件源2. 注册事件(绑定事件)3. 添加事件处理程序(采取函数赋值形式)

4.常见的鼠标事件

二十一、设置/修改DOM元素内容

1. document.write()方法

2.对象.innerText属性

3.对象.innerHTML属性

4.三种方法的总结和区别

​编辑

5.常见元素的属性操作

 6.案例

 【分时显示不同图片,显示不同问候语】

 7.表单元素的属性操作

【案例-- 仿京东显示密码】

  8. 样式属性操作

【 案例: 淘宝点击关闭二维码】

 【案例: 循环精灵图背景】

 【案例:显示隐藏文本框内容】

【案例: 密码框格式提示错误信息】

 9.操作元素总结

10.排他思想

 【案例:百度换肤】

 【案例:表格隔行变色】

 【案例:表单全选取消全选案例】

 11.自定义属性的操作

1. 获取属性值

2.设置属性值

3.移除属性

 12.H5自定义属性--只能通过getAttriubute

12.1 设置H5自定义属性----data-index="2"/div.setAttribute('data-time', 20)

12.2 获取H5自定义属性 ---element.getAttrubute('data-index')/element.dataset.index/element.dataset['index']

二十二、节点操作

1.为什么学节点操作

2.节点概述

3.节点层级

3.1 父级节点--node.parentNode

3.2 子节点(总结:无论用什么都是“父节点.children【】”)

3.3 兄弟节点

  3.4 创建节点

4.4 添加节点

4.5 删除节点

4.6 复制节点(克隆节点)--node.cloneNode(false)-->不复制内容/node.cloneNode(true)-->复制内容

4.7 三种动态创建元素区别

二十二、DOM重点核心

1.创建

 1. document.write2. innerHTML3. createElement

2.增

1. appendChild2. insertBefore

3.删

1.. removeChild

4.改

1. 修改元素属性: src、href、title等2. 修改普通元素内容: innerHTML 、innerText3. 修改表单元素: value、type、disabled等4. 修改元素样式: style、className

5.查

6.属性操作

7.事件操作

 7.1 注册事件两种方式

二十三、PC端网页特效

 1. 元素偏移量offset系列

1.1 offset概述

​编辑

1.2 offset 与 style 区别

2.元素可视区client系列

3.元素滚动scroll系列

3.1  元素scroll系列属性

3.2 页面被卷去的头部

3.3 页面被卷去的头部兼容性解决方案 

4.offset、client、scroll总结

5.mouseenter 和mouseover的区别

6.动画函数封装

6.1 动画实现原理

 6.2 动画函数简单封装

6.3 动画函数给不同元素记录不同定时器

6.4 缓动效果原理

6.5 动画函数多个目标值之间移动

6.6 动画函数添加回调函数

 6.7 动画函数封装到单独JS文件里面

6.常见网页特效案例

【案例:网页轮播图】

6.1 节流阈

二十四、事件高级

1.注册事件(绑定事件)

1.1 注册事件概述:传统方式   VS  方法监听注册事件 

1.2 addEventListener事件监听方式

 1.3 attachEvent 注册事件 事件监听方式

注意点:

1.4 注册事件兼容性解决方案

2删除事件(解绑事件)

2.1 删除事件的方式

2.2 删除事件兼容性解决方案

3.DOM事件流

4.事件对象

4.1 什么是事件对象

4.2 事件对象的使用语法

4.3 事件对象的兼容性方案

4.4 事件对象的常见属性和方法

5.阻止事件冒泡

5.1 阻止事件冒泡的两种方式

5.2 阻止事件冒泡的兼容性解决方案

6.事件委托(代理,委派)

7.常用的鼠标事件

7.1 常用的鼠标事件

7.2 鼠标事件对象

8.常用的键盘事件

8.1 常用键盘事件

 8.2 键盘事件对象

 8.3 ASCII 表


一、编程语言

1.1 编程

 1.2 计算机语言

 1.3 编程语言

1.4 翻译器 

 1.5 编程语言和标记语言区别

 总结

二、计算机基础

2.1 计算机组成

2.2.数据存储 

2.3 数据存储单位 

2.4 程序运行 

三、HTML中的JavaScript

1.<script.>元素

 

1.1 标签的位置

 script元素应该在body里面,这样可以加快打开速度

 1.2 延迟脚本--defer

立即下载,但是先将整个页面解析完,才开始运行。 

在XHTML文档中,要把defer属性设置为defer="defer"

1.3 异步脚本--async

在XHTML文档中,要把async属性设置为async="async"

 1.4 XHML中的变化

XHTML中使用JavaScript必须指定type属性且值为text/javascript

 

 

2.嵌入代码和外部文件

 3.<noscript>元素

这个元素可以包含能够出现在文档<body>中的任何HTML元素--->script>元素除外

 4.小结

 四、初始JavaScript

1.1 Javascript是什么

1.2JavaScript的作用 

1.3 HTML/CSS/JS的关系 

1.4 浏览器执行JS简介 

1.5 JS组成

 

  

 1.6 JS初体验

1.6.1 行内式JS

1.6.2 内嵌JS

1.6.3 外部JS文件 

四、JavaScript注释

1.单行注释

2.多行注释 

五、Javascript输入输出语句

    <script>
        // 1.document 文档的意思    write 写
        document.write('我愿意')
        document.write('<h1>我愿意</h1>')
        // 2. alert 警示
        // alert('黑马')
        // 3. 控制台输出语法  程序员看的
        console.log('我是用来测试的')
        // 4. 输入语句 prompt
        prompt('您今年多大了?')
    </script>

 六、变量

1.变量的概述

1.1 什么是变量

1.2 变量在内存中的存储

 

2.变量的使用

2.1 声明变量

2.2.赋值

2.3 变量的初始化

 

    <script>
        // let声明变量   age 变量名
        // let age
        // // 赋值  =  
        // age = 18
        // age = 19
        // console.log(age)

        // 2变量的 初始化  声明变量的时候直接赋值
        // let age = 18
        // let age = 19
        // console.log(age)

        let age = 19, uname = 'pink老师'
        console.log(age)
        console.log(uname)
    </script>

2.4 【案例】变量的使用

    <script>
        let uname = prompt('请您输入姓名:')
        document.write(uname)
    </script>

3.变量语法扩展

3.1 更新变量

3.2 同时声明多个变量

 3.3 声明变量特殊情况

4.变量命名规范

5.标识符

6.var 、 let、 const

 var 在 ECMAScript 的所有版本中都可以使用,而 const 和 let 只能在 ECMAScript 6 及更晚的版本中使用。

6.1 var关键字--声明的是函数作用域

        (1) var声明作用域

        (2)var声明提升

 

6.2 let关键字--声明的是块作用域

 块作用域是函数作用域的子集

let 也不允许同一个块作用域中出现冗余声明

    <script>
        var name = 'Nicholas';
        console.log(name); // 'Nicholas'
        if (true) {
        var name = 'Matt';
        console.log(name); // 'Matt'
        }
        let age = 30;
        console.log(age); // 30
        if (true) {
        let age = 26;
        console.log(age); // 26
        } 
    </script>

         (1)暂时性死区

let 与 var 的另一个重要的区别,就是 let 声明的变量不会在作用域中被提升

// name 会被提升
console.log(name); // undefined
var name = 'Matt';
// age 不会被提升
console.log(age); // ReferenceError:age 没有定义
let age = 26;

在解析代码时,JavaScript 引擎也会注意出现在块后面的 let 声明,只不过在此之前不能以任何方 式来引用未声明的变量。在 let 声明之前的执行瞬间被称为“暂时性死区”(temporal dead zone),在此 阶段引用任何后面才声明的变量都会抛出 ReferenceError。

        (2)全局声明

与 var 关键字不同,使用 let 在全局作用域中声明的变量不会成为 window 对象的属性(var 声 明的变量则会)。

var name = 'Matt';
console.log(window.name); // 'Matt'
let age = 26;
console.log(window.age); // undefined 

        (3)条件声明

在使用 var 声明变量时,由于声明会被提升,JavaScript 引擎会自动将多余的声明在作用域顶部合 并为一个声明。因为 let 的作用域是块,所以不可能检查前面是否已经使用 let 声明过同名变量,同 时也就不可能在没有声明的情况下声明它。

<script>
 var name = 'Nicholas';
 let age = 26;
</script>
<script>
 // 假设脚本不确定页面中是否已经声明了同名变量
 // 那它可以假设还没有声明过
 var name = 'Matt';
 // 这里没问题,因为可以被作为一个提升声明来处理
 // 不需要检查之前是否声明过同名变量
 let age = 36;
 // 如果 age 之前声明过,这里会报错
</script> 

使用 try/catch 语句或 typeof 操作符也不能解决,因为条件块中 let 声明的作用域仅限于该块。

    <script>
        let name = 'Nicholas';
        let age = 36;
   </script>
   <script>
        // 假设脚本不确定页面中是否已经声明了同名变量
        // 那它可以假设还没有声明过
        if (typeof name === 'undefined') {
        let name;
        }
        // name 被限制在 if {} 块的作用域内
        // 因此这个赋值形同全局赋值
        name = 'Matt';
        try {
        console.log(age); // 如果 age 没有声明过,则会报错
        }
        catch(error) {
        let age; 
    }
        // age 被限制在 catch {}块的作用域内
        // 因此这个赋值形同全局赋值
        age = 26;
   </script> 

        (4)for 循环中的 let 声明

    <script>
       for (var i = 0; i < 5; ++i) {
            setTimeout(() => console.log(i), 0)
        } //5,5,5,5,5
        
        /*之所以会这样,是因为在退出循环时,迭代变量保存的是导致循环退出的值:5。在之后执行超时
逻辑时,所有的 i 都是同一个变量,因而输出的都是同一个最终值。
而在使用 let 声明迭代变量时,JavaScript 引擎在后台会为每个迭代循环声明一个新的迭代变量。
每个 setTimeout 引用的都是不同的变量实例,所以 console.log 输出的是我们期望的值,也就是循
环执行过程中每个迭代变量的值。*/

        for(let i=0;i<5;i++){
            setTimeout(()=>console.log(i),0)
        }//0,1,2,3,4
   </script> 

这种每次迭代声明一个独立变量实例的行为适用于所有风格的 for 循环,包括 for-in 和 for-of 循环。

6.3 const声明

const 的行为与 let 基本相同,唯一一个重要的区别是用它声明变量时必须同时初始化变量,且 尝试修改 const 声明的变量会导致运行时错误。

    <script>
        const age = 26;
        age = 36; // TypeError: 给常量赋值

        // const 也不允许重复声明
        const name = 'Matt';
        const name = 'Nicholas'; // SyntaxError
        
        // const 声明的作用域也是块
        const name = 'Matt';
        if (true) {
        const name = 'Nicholas';
        }
        console.log(name); // Matt
   </script> 

const 声明的限制只适用于它指向的变量的引用。换句话说,如果 const 变量引用的是一个对象, 那么修改这个对象内部的属性并不违反 const 的限制。

const person = {};
person.name = 'Matt'; // ok 

JavaScript 引擎会为 for 循环中的 let 声明分别创建独立的变量实例,虽然 const 变量跟 let 变 量很相似,但是不能用 const 来声明迭代变量(因为迭代变量会自增):

for (const i = 0; i < 10; ++i) {} // TypeError:给常量赋值

如果你只想用 const 声明一个不会被修改的 for 循环变量,那也是可以的。也就是说,每 次迭代只是创建一个新变量。这对 for-of 和 for-in 循环特别有意义

    <script>
        let i = 0;
        for (const j = 7; i < 5; ++i) {
        console.log(j);
        }
        // 7, 7, 7, 7, 7
        for (const key in {a: 1, b: 2}) {
        console.log(key);
        }
        // a, b
        for (const value of [1,2,3,4,5]) {
        console.log(value);
        }
        // 1, 2, 3, 4, 5 
   </script> 

7.交换变量案例

    <script>
        let num1 = '刘德华'
        let num2 = 'pink老师'
        let temp
        temp = num1
        num1 = num2
        num2 = temp
        console.log(num1, num2)

    //    let  myFirstName 
    //    let myfirstname 
    </script>

七、数据类型

1.数据类型简介

1.1 为什么需要数据类型

1.2 变量的数据类型

 

1.3 数据类型的分类

 

2.简单数据类型(Number,String,Boolean,Undefined,Null,Symbol)

2.1 数字型--Number

(1)数值进制

八进制前面加0,十六进制前面加0x

如果字面量中包含的数字超出了应 有的范围,就会忽略前缀的零,后面的数字序列会被当成十进制数,

 (2)数字类型范围

(3)数值类型的三个特殊值

(4)isNaN()

console.log(0/0); // NaN 
console.log(-0/+0); // NaN 
//如果分子是非 0 值,分母是有符号 0 或无符号 0,则会返回 Infinity 或-Infinity:
console.log(5/0); // Infinity 
console.log(5/-0); // -Infinity

任何涉及 NaN 的操作始终返回 NaN(如 NaN/10)

NaN 不等于包括 NaN 在内的任何值

console.log(NaN == NaN); // false 

ECMAScript 提供了 isNaN()函数。该函数接收一个参数,可以是任意数据类型,然后判断 这个参数是否“不是数值”。把一个值传给 isNaN()后,该函数会尝试把它转换为数值。某些非数值的 值可以直接转换成数值,如字符串"10"或布尔值。任何不能转换为数值的值都会导致这个函数返回 true。

console.log(isNaN(NaN)); // true 
console.log(isNaN(10)); // false,10 是数值
console.log(isNaN("10")); // false,可以转换为数值 10 
console.log(isNaN("blue")); // true,不可以转换为数值
console.log(isNaN(true)); // false,可以转换为数值 1 

(5)浮点值

要定义浮点值,数值中必须包含小数点,而且小数点后面必须至少有一个数字。虽然小数点前面不 是必须有整数,但推荐加上。

let floatNum1 = 1.1; 
let floatNum2 = 0.1; 
let floatNum3 = .1; // 有效,但不推荐

如果数值本身就是整数,只是小 数点后面跟着 0(如 1.0),那它也会被转换为整数。

对于非常大或非常小的数值,浮点值可以用科学记数法来表示。科学记数法用于表示一个应该乘以 10 的给定次幂的数值。ECMAScript 中科学记数法的格式要求是一个数值(整数或浮点数)后跟一个大 写或小写的字母 e,再加上一个要乘的 10 的多少次幂。

if (a + b == 0.3) { // 别这么干! 
     console.log("You got 0.3."); 
} 

 (6)数值转换

有 3 个函数可以将非数值转换为数值:Number()、parseInt()和 parseFloat()。

Number()--可以用于任何数据类型

let num1 = Number("Hello world!"); // NaN 
let num2 = Number(""); // 0 
let num3 = Number("000011"); // 11 
let num4 = Number(true); // 1 

parseInt()--字符串转换为数值

let num1 = parseInt("1234blue"); // 1234 
let num2 = parseInt(""); // NaN 
let num3 = parseInt("0xA"); // 10,解释为十六进制整数
let num4 = parseInt(22.5); // 22 
let num5 = parseInt("70"); // 70,解释为十进制值
let num6 = parseInt("0xf"); // 15,解释为十六进制整数

不同的数值格式很容易混淆,因此 parseInt()也接收第二个参数,用于指定底数(进制数)。

let num1 = parseInt("10", 2); // 2,按二进制解析
let num2 = parseInt("10", 8); // 8,按八进制解析
let num3 = parseInt("10", 10); // 10,按十进制解析
let num4 = parseInt("10", 16); // 16,按十六进制解析

parseFloat()--字符串转换为数值

  • 它始终忽略字符串开头的零
  • 十六进制数值始终会返回 0
  • 因为 parseFloat()只解析十进制值,因此不能指定底数。最后,如果字符串表示整数(没有小数点或者小 数点后面只有一个零),则 parseFloat()返回整数
let num1 = parseFloat("1234blue"); // 1234,按整数解析
let num2 = parseFloat("0xA"); // 0 
let num3 = parseFloat("22.5"); // 22.5 
let num4 = parseFloat("22.34.5"); // 22.34 
let num5 = parseFloat("0908.5"); // 908.5 
let num6 = parseFloat("3.125e7"); // 31250000

2.2 字符串类型--String

 字符串要用双引号或者单引号括起来

 (1)字符串引号嵌套

 

(2)字符串转义符

 (3)字符串长度

 (4)字符串拼接--只要有字符串则最后结果都是字符串

<script>
        // 利用 + 做拼接
        // console.log('我是' + 'pink老师')

        // console.log('我今年年龄是:' + 18)

        // let age = 20
        // console.log('我今年年龄是:' + age)
        // 用户输入名字
        // 页面输出的是: 我的名字是xxx
        let uname = prompt('请输入您的名字')
        document.write('我的名字是:' + uname)
</script>

有两种方式把一个值转换为字符串。首先是使用几乎所有值都有的 toString()方法。这个方法唯 一的用途就是返回当前值的字符串等价物。toString()方法可见于数值、布尔值、对象和字符串值。(没错,字符串值也有 toString()方法, 该方法只是简单地返回自身的一个副本。)null 和 undefined 值没有 toString()方法

let age = 11; 
let ageAsString = age.toString(); // 字符串"11" 
let found = true; 
let foundAsString = found.toString(); // 字符串"true" 

多数情况下,toString()不接收任何参数。不过,在对数值调用这个方法时,toString()可以接收一个底数参数,即以什么底数来输出数值的字符串表示。默认情况下,toString()返回数值的十 进制字符串表示。而通过传入参数,可以得到数值的二进制、八进制、十六进制,或者其他任何有效基 数的字符串表示

let num = 10; 
console.log(num.toString()); // "10" 
console.log(num.toString(2)); // "1010" 
console.log(num.toString(8)); // "12" 
console.log(num.toString(10)); // "10" 
console.log(num.toString(16)); // "a" 

let value1 = 10; 
let value2 = true; 
let value3 = null; 
let value4; 
console.log(String(value1)); // "10" 
console.log(String(value2)); // "true" 
console.log(String(value3)); // "null" 
console.log(String(value4)); // "undefined" 

 (5)字符串拼接加强 

 变量是不能添加引号的,因为如果加了就变成字符串

<script>
        let num = 123
        let str = '123'
        console.log(num)
        console.log(str)
        let str1 = `pink老师`
        console.log(str1)
        console.log('num')  // 输出结果是啥?
        // 外单内双  外双内单
        console.log('我是"pink德华"')
        console.log("我是'pink德华'")
        // console.log('我是\'pink德华\'') 了解
</script>

(6)字符串的特点

ECMAScript 中的字符串是不可变的(immutable),意思是一旦创建,它们的值就不能变了。要修改 某个变量中的字符串值,必须先销毁原始的字符串,然后将包含新值的另一个字符串保存到该变量

 (7)模板字面量

ECMAScript 6 新增了使用模板字面量定义字符串的能力。与使用单引号或双引号不同,模板字面量 保留换行字符,可以跨行定义字符串

let myMultiLineString = 'first line\nsecond line'; 
let myMultiLineTemplateLiteral = `first line 
second line`; 
console.log(myMultiLineString); 
// first line 
// second line" 
console.log(myMultiLineTemplateLiteral); 
// first line 
// second line 
console.log(myMultiLineString === myMultiLinetemplateLiteral); // true 

由于模板字面量会保持反引号内部的空格,因此在使用时要格外注意。格式正确的模板字符串看起 来可能会缩进不当

// 这个模板字面量在换行符之后有 25 个空格符
let myTemplateLiteral = `first line 
 second line`; 
console.log(myTemplateLiteral.length); // 47 
// 这个模板字面量以一个换行符开头
let secondTemplateLiteral = ` 
first line 
second line`; 
console.log(secondTemplateLiteral[0] === '\n'); // true 
// 这个模板字面量没有意料之外的字符
let thirdTemplateLiteral = `first line 
second line`; 
console.log(thirdTemplateLiteral); 
// first line 
// second line 

 (8)字符串插值

模板字面量最常用的一个特性是支持字符串插值,也就是可以在一个连续定义中插入一个或多个 值。技术上讲,模板字面量不是字符串,而是一种特殊的 JavaScript 句法表达式,只不过求值后得到的 是字符串。

let value = 5; 
let exponent = 'second'; 
// 以前,字符串插值是这样实现的:
let interpolatedString = 
 value + ' to the ' + exponent + ' power is ' + (value * value); 
// 现在,可以用模板字面量这样实现:
let interpolatedTemplateLiteral = 
 `${ value } to the ${ exponent } power is ${ value * value }`; 
console.log(interpolatedString); // 5 to the second power is 25 
console.log(interpolatedTemplateLiteral); // 5 to the second power is 25

模板也可以插入自己之前的值

let value = ''; 
function append() { 
 value = `${value}abc` 
 console.log(value); 
} 
append(); // abc 
append(); // abcabc 
append(); // abcabcabc 

【案例】显示年龄

<script>
    var age=prompt('请输入您的年龄:')
    alert('您今年'+age+'岁了')
</script>

 2.3 布尔值--Boolean

默认值是false

Boolean()转型函数可以在任意类型的数据上调用,而且始终返回一个布尔值。什么值能转换为 true 或 false 的规则取决于数据类型和实际的值。

 2.4 Undefined

当使用 var 或 let 声明了变量但没有初始 化时,就相当于给变量赋予了 undefined 值

var variable;
console.log(variable); // undefined
console.log('你好' + variable); // 你好undefined
console.log(11 + variable); // NaN
console.log(true + variable); // Na

包含 undefined 值的变量跟未定义变量是有区别的

let message; // 这个变量被声明了,只是值为 undefined 
// 确保没有声明过这个变量
// let age 
console.log(message); // "undefined" 
console.log(age); // 报错

无论是声明还是未声明,typeof 返回的都是字符串"undefined"。

let message; // 这个变量被声明了,只是值为 undefined 
// 确保没有声明过这个变量
// let age 
console.log(typeof message); // "undefined"  
console.log(typeof age); // "undefined"

2.5 Null

一个声明变量给 null 值,里面存的值为空(学习对象时,我们继续研究null)

var vari = null;
console.log('你好' + vari); // 你好null
console.log(11 + vari); // 11
console.log(true + vari); // 1

如前所述,永远不必显式地将 变量值设置为 undefined。但 null 不是这样的。任何时候,只要变量要保存对象,而当时又没有那个 对象可保存,就要用 null 来填充该变量。这样就可以保持 null 是空对象指针的语义,并进一步将其 与 undefined 区分开来

let message = null; 
let age; //默认为undefined,所以不为空
if (message) { 
     // 这个块不会执行
} 
if (!message) { 
     // 这个块会执行
} 
if (age) { 
     // 这个块不会执行
} 
if (!age) { //因为为undefined,所以age在这里进入
     // 这个块会执行
} 

2.6 Symbol

符号是原始值,且符号实例是唯一、不可变的。 符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险。

(1)符号的基本用法

符号需要使用 Symbol()函数初始化。因为符号本身是原始类型,所以 typeof 操作符对符号返回 symbol

let sym = Symbol(); 
console.log(typeof sym); // symbol

调用 Symbol()函数时,也可以传入一个字符串参数作为对符号的描述(description),将来可以通 过这个字符串来调试代码。但是,这个字符串参数与符号定义或标识完全无关

let genericSymbol = Symbol(); 
let otherGenericSymbol = Symbol(); 
let fooSymbol = Symbol('foo'); 
let otherFooSymbol = Symbol('foo'); 
console.log(genericSymbol == otherGenericSymbol); // false
console.log(fooSymbol == otherFooSymbol); // false

符号没有字面量语法,这也是它们发挥作用的关键。按照规范,你只要创建 Symbol()实例并将其 用作对象的新属性,就可以保证它不会覆盖已有的对象属性,无论是符号属性还是字符串属性

let genericSymbol = Symbol(); 
console.log(genericSymbol); // Symbol() 
let fooSymbol = Symbol('foo'); 
console.log(fooSymbol); // Symbol(foo);

Symbol()函数不能与 new 关键字一起作为构造函数使用

let myBoolean = new Boolean(); 
console.log(typeof myBoolean); // "object" 
let myString = new String(); 
console.log(typeof myString); // "object" 
let myNumber = new Number(); 
console.log(typeof myNumber); // "object" 
let mySymbol = new Symbol(); // TypeError: Symbol is not a constructor

Symbol 不可以添加属性

let hd = Symbol();
hd.name = "后盾人";
console.log(hd.name);

(2.)Symbol.for()

根据描述获取Symbol,如果不存在则新建一个Symbol

  • 使用Symbol.for会在系统中将Symbol登记
  • 使用Symbol则不会登记

如果运行时的不同部分需要共享和重用符号实例,那么可以用一个字符串作为键,在全局符号注册 表中创建并重用符号。 需要使用 Symbol.for()方法

let fooGlobalSymbol = Symbol.for('foo'); 
console.log(typeof fooGlobalSymbol); // symbol

第一次使用某个字符串调用时,它会检查全局运 行时注册表,发现不存在对应的符号,于是就会生成一个新符号实例并添加到注册表中。后续使用相同 字符串的调用同样会检查注册表,发现存在与该字符串对应的符号,然后就会返回该符号实例。

let fooGlobalSymbol = Symbol.for('foo'); // 创建新符号
let otherFooGlobalSymbol = Symbol.for('foo'); // 重用已有符号
console.log(fooGlobalSymbol === otherFooGlobalSymbol); // true 
即使采用相同的符号描述,在全局注册表中定义的符号跟使用 Symbol()定义的符号也并不等同:
let localSymbol = Symbol('foo'); 
let globalSymbol = Symbol.for('foo'); 
console.log(localSymbol === globalSymbol); // false


使用description可以获取传入的描述参数
 

let hd = Symbol("小林");
console.log(hd.description); //小林

(3)Symbol.keyFor()

还可以使用 Symbol.keyFor()来查询全局注册表Symbol.keyFor 根据使用Symbol.for登记的Symbol返回描述,如果找不到返回undefined

// 创建全局符号
let s = Symbol.for('foo'); 
console.log(Symbol.keyFor(s)); // foo 
// 创建普通符号
let s2 = Symbol('bar'); 
console.log(Symbol.keyFor(s2)); // undefined 
如果传给 Symbol.keyFor()的不是符号,则该方法抛出 TypeError:
Symbol.keyFor(123); // TypeError: 123 is not a symbol

(4)对象属性

Symbol 是独一无二的所以可以保证对象属性的唯一。

  • Symbol 声明和访问使用 [](变量)形式操作

  • 也不能使用 . 语法因为 .语法是操作字符串属性的。

下面写法是错误的,会将symbol 当成字符串symbol处理

let symbol = Symbol("小林");
let obj = {
  symbol: "hdcms.com"
};
console.log(obj);

正确写法是以[] 变量形式声明和访问

let symbol = Symbol("小林");
let obj = {
  [symbol]: "abc"
};
console.log(obj[symbol]); //abc

(5)遍历属性

Symbol 不能使用 for/infor/of 遍历操作

let symbol = Symbol("小林");
let obj = {
  name: "abc",
  [symbol]: "abc"
};

for (const key in obj) {
  console.log(key); //name
}

for (const key of Object.keys(obj)) {
  console.log(key); //name
}

也可以使用 Reflect.ownKeys(obj) 获取所有属性包括Symbol

for (const key of Reflect.ownKeys(obj)) {
  console.log(key);
}

3.获取变量数据类型(Object)

let o = new Object();

//如果没有参数,如上面的例子所示,那么完全可以省略括号(不推荐):
let o = new Object; // 合法,但不推荐

3.1 Object实例有如下属性和方法

3.2 获取检测变量的数据类型--typeof 

注意typeof在某些情况下返回的结果可能会让人费解,但技术上讲还是正确的。比如,调用typeof null 返回的是"object"。这是因为特殊值 null 被认为是一个对空对象的引用。

<script>
        // 返回的什么类型  string  number Boolean null
        console.log(typeof 123)//number
        console.log(typeof '123')//string
        console.log(typeof true)//boolean
        console.log(typeof undefined)//undefined
        console.log(typeof null)//object
        let num = 10
        console.log(typeof num + '11')//number11
         console.log('number' + '11')//number11
</script>

3.2 字面量

3.3 Object类型

对象通过 new 操作符后跟对象类型的名称 来创建。开发者可以通过创建 Object 类型的实例来创建自己的对象,然后再给对象添加属性和方法

let o = new Object(); 
let o = new Object; // 合法,但不推

4.数据类型转换

4.1 什么是数据类型转换

<script>
        // let num = prompt('请输入一个数字')
        // console.log(num, typeof num)
        // 内部悄悄的把 18 转换为了字符串的 '18'
        console.log('pink老师' + 18)//pink老师18
        console.log(10 + '10')  //  1010
        // - *  / 把 字符串的 '10' 转换为 数字型 10
        console.log(10 - '10') // 0
        // 小技巧
        let num = '10'
        console.log(num)//10
        console.log(+num)//10
        // console.log(-num)
        console.log(10 + +'10')
</script>

4.2 转换为字符串

 

<script>
        console.log(String(10))
        let age = 10
        console.log(age.toString())//10
        console.log(typeof age);//number
        // 括号里面如果是2 转换为 二进制
        console.log(age.toString(4))//22
</script>

4.3 转换为数字型(重点) 

<script>
        // let num = '10'  
        // Number(数据)
        console.log(Number('10.01'))//10.01
        console.log(typeof num)//undefined
        // 转换为数字型,只保留整数,没有四舍五入
        console.log(typeof parseInt('10'))//number
        console.log(parseInt('10.111'))//10
        console.log(parseInt('10.999px'))//10
        // 转换为数字型,会保留小数
        console.log(parseFloat('10.999'))//10.999

        // 区别:
        // 1.Number() 只能放数字类型的字符,不能放abc这样的
        // 否则返回的是 NaN   not A number
        console.log(Number('10.01abc'))//NaN
        // parseFloat 经常用于过滤px单位
        console.log(parseFloat('10.01abc'))//10.01
        console.log(parseFloat('100px'))//100
        console.log(parseFloat('px100'))//NaN
</script>

【案例】计算年龄

 

<script>
    var year=prompt('请输入您的出生年份')
    var newYear=2023-year
    alert('您的年龄为:'+newYear)
</script>

【简单加法器】

<script>
    // 1. 先弹出第一个输入框,提示用户输入第一个值
    var num1 = prompt('请输入第一个值:');
    // 2. 再弹出第二个框,提示用户输入第二个值
    var num2 = prompt('请输入第二个值:');
    // 3. 将输入的值转换为数字型后,把这两个值相加,并将结果赋给新的变量 v
    var result = parseFloat(num1) + parseFloat(num2);
    // 4. 弹出结果
    alert('结果是:' + result);
</script>

 4.4转换为布尔值

console.log(Boolean('')); // false
console.log(Boolean(0)); // false
console.log(Boolean(NaN)); // false
console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false
console.log(Boolean('小白')); // true
console.log(Boolean(12)); // true

八、标识符,关键字,保留字

1.标识符

2.关键字

3.保留字

 九、JavaScript 运算符

1.运算符

2.算数运算符

2.1 算术运算符概述

 2.2 浮点数的精度问题

不要直接判断两个浮点数是否相等

 

2.3 表达式和返回值

 

<script>
        console.log(4 / 2)//2
        console.log(4 % 2)//0
        console.log(2 % 4)//2
        console.log(5 % 8)//5
</script>

3.递增和递减运算符(一元运算符)

3.1 递增和递减运算符概述

3.2 递增运算符

        (1)前置递增运算符 

        (2)后置递增运算符

<script>
        var a = 10;
        ++a; 
        console.log(a);//11
        var b = ++a + 2;//12+2
        console.log(b);//14
        var c = 10;
        c++;
        console.log(c);//11
        var d = c++ + 2;// 11+2
        console.log(d);//13
        var e = 10;
        var f = e++ + ++e;//10+(11+1)=22
        console.log(f)//22
</script>

  3.3 前置递增和后置递增小结

<script>
        // let num = 10
        // 一元运算符
        // ++num    等价于  num += 1
        // num++
        // console.log(num)

        // 前置自增
        // let i = 1
        // 先自加 再使用
        // console.log(++i + 2)  // 4
        // 后置自增
        let i = 1
        // 先使用 后自加
        console.log(i++ + 2) // 
        console.log(i)
</script>
<script>
        let i = 1
        console.log(i++ + ++i + i)
        //i++ --> i=2 ,但是输出i=1
        //++i --> i=2+1=3
        //i++ + ++i + i=1 + 3 + 3=7
</script>

let s1 = "2"; 
let s2 = "z"; 
let b = false; 
let f = 1.1; 
let o = { 
 valueOf() { 
 return -1; 
 } 
}; 
s1++; // 值变成数值 3 
s2++; // 值变成 NaN 
b++; // 值变成数值 1 
f--; // 值变成 0.10000000000000009(因为浮点数不精确)
o--; // 值变成-2 

3.4 一元加和减

如果将一元加应用到非数值,则会执行与使用 Number()转型函数一样的类型转换:布尔值 false 和 true 转换为 0 和 1,字符串根据特殊规则进行解析,对象会调用它们的 valueOf()和/或 toString() 方法以得到可以转换的值

let s1 = "01"; 
let s2 = "1.1";
let s3 = "z"; 
let b = false; 
let f = 1.1; 
let o = { 
 valueOf() { 
 return -1; 
 } 
}; 
s1 = +s1; // 值变成数值 1 
s2 = +s2; // 值变成数值 1.1 
s3 = +s3; // 值变成 NaN 
b = +b; // 值变成数值 0 
f = +f; // 不变,还是 1.1 
o = +o; // 值变成数值-1  

一元减由一个减号(-)表示,放在变量前头,主要用于把数值变成负值

let s1 = "01"; 
let s2 = "1.1"; 
let s3 = "z"; 
let b = false; 
let f = 1.1; 
let o = { 
 valueOf() { 
 return -1; 
 } 
}; 
s1 = -s1; // 值变成数值-1 
s2 = -s2; // 值变成数值-1.1 
s3 = -s3; // 值变成 NaN 
b = -b; // 值变成数值 0 
f = -f; // 变成-1.1 
o = -o; // 值变成数值 1

4.比较运算符

4.1 比较运算符概述

 只要是数值和字符串 比较,字符串就会先被转换为数值,然后进行数值比较。

//因为两个操作数都是字符串,所以会逐个比较它们的字符编码(字符"2"的编码是 50,而字符"3"的编码是 51)。
let result = "23" < "3"; // true 


//这次会将字符串"23"转换为数值 23,然后再跟 3 比较
let result = "23" < 3; // false


//因为字符"a"不能转换成任何有意义的数值,所以只能转换为 NaN
let result = "a" < 3; // 因为"a"会转换为 NaN,所以结果是 false

即任何关系 操作符在涉及比较 NaN 时都返回 false。

let result1 = NaN < 3; // false 
let result2 = NaN >= 3; // false 
//在大多数比较的场景中,如果一个值不小于另一个值,那就一定大于或等于它。但在比较 NaN 时,
无论是小于还是大于等于,比较的结果都会返回 false

4.2 小结

 

<script>
        console.log(3 > 5) // false
        console.log(5 >= 5) //  true
        // console.log(5 = 5)
        console.log(5 == 5)//true
        // == 只要值一样就是true  不管数据类型
        console.log(5 == '5')//true
        console.log(5 == 'pink')//false
        // === 以后判断要用  ===  开发常用   要求值和数据类型都一样
        console.log(5 === 5)//true
        console.log(5 === '5')//false
        // 特殊情况
        console.log('pink' > 'red')//false
        console.log('pink' > 'pin')//true
        console.log(1 === NaN)//false
        console.log(NaN === NaN)//false
        //浮点数相加会有精度问题
        console.log(0.1 + 0.2 === 0.3)//false
        console.log(0.1 + 0.2)//0.30000000000000004
        console.log(3 > '2')//true
</script>
<script>
    var num1 = 10;
    var num2 = 100;
    var res1 = num1 > num2; 
    console.log(res1);//false
    var res2 = num1 == 11; 
    console.log(res2);//false
    var res3 = num1 != num2;
    //"==="全等
    var test1=100;
    var test2=100;
    console.log(test1===test2);//true
</script>

5.逻辑运算符

5.1 逻辑运算符概述

5.2 逻辑与 &&

两边都是true才返回true,否则返回false

5.3 逻辑或 ||

 两边都为 false 才返回 false,否则都为true

5.4 逻辑非 ! 

 

<script>
    var num = 7;
    var str = "我爱你~中国~";
    console.log(num > 5 && str.length >= num);//true && true==true
    console.log(num < 5 && str.length >= num);//false && true==false
    console.log(!(num < 10));//!(true)==false
    console.log(!(num < 10 || str.length == num));//!(true || true)==!(true)==false
</script>

5.5 短路运算(逻辑中断)

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

         (1)逻辑与--表达式1 && 表达式2

  • 如果第一个表达式的值为真,则返回表达式2
  • 如果第一个表达式的值为假,则返回表达式1
<script>
    //逻辑与
    //如果为真返回表达式2,如果为假返回表达式2
        console.log( 123 && 456 ); // 456
        console.log( 0 && 456 ); // 0
        console.log( 123 && 456&& 789 ); // 789
</script>

        (2)逻辑或--表达式1 || 表达式2

  • 如果第一个表达式的值为真,则返回表达式1
  • 如果第一个表达式的值为假,则返回表达式2
<script>
    console.log( 123 || 456 ); // 123
    console.log( 0 || 456 ); // 456
    console.log( 123 || 456 || 789 ); // 123
</script>

        (3)逻辑中断(短路操作)

<script>
        var num = 0;
        console.log(123 || num++);//123
        console.log(num); //0
</script>
<script>
        // 逻辑与 一假则假
        console.log(true && true)//true
        console.log(false && true)//false
        // 逻辑或  一真则真
        console.log(false || true)//true
        console.log(false || false)//false
        // 逻辑非  取反
        console.log(!true)//false
        console.log(!false)//true

        // 有5个值是当 false 来看的   其余是真的
        //  false   数字0   ''  undefined   null   


        // function fun(x, y) {
        //     x = x || 0
        //     y = y || 0

        //     x + y
        // }
        // fun()
</script>

6.赋值运算符

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

var age = 10;
age += 5; // 相当于 age = age + 5;
age -= 5; // 相当于 age = age - 5;
age *= 10; // 相当于 age = age * 10;

7.位操作符

   

  (1) 按位非-- 波浪符(~)表示,它的作用是返回数值的一补数按位非的最终效果是对 数值取反并减 1】

let num1 = 25; // 二进制 00000000000000000000000000011001 
let num2 = ~num1; // 二进制 11111111111111111111111111100110 
console.log(num2); // -26

(2)按位与--和号(&)

按位与操作在两个位都是 1 时返回 1,在任何一位是 0 时返回 0

(3)按位或--操作符用管道符(|)表示

 按位或操作在至少一位是 1 时返回 1,两位都是 0 时返回 0。

(4)按位异或--用脱字符(^)表示

 

  按位异或与按位或的区别是,它只在一位上是 1 的时候返回 1(两位都是 1 或 0,则返回 0)。

(5)左移--用两个小于号(<<)表示

let oldValue = 2; // 等于二进制 10 
let newValue = oldValue << 5; // 等于二进制 1000000,即十进制 64

 

(6)有符号右 移--两个大于号(>>)表示,会将数值的所有 32 位都向右移,同时保留符号(正或负)

let oldValue = 64; // 等于二进制 1000000 
let newValue = oldValue >> 5; // 等于二进制 10,即十进制 2 

(7)无符号右移--3 个大于号表示(>>>)

 对于正数,无符号右移与 有符号右移结果相同。

let oldValue = 64; // 等于二进制 1000000 
let newValue = oldValue >>> 5; // 等于二进制 10,即十进制 2 

无符号右移操作符将负数的二进制表示当成正数的二进制表示来处理

let oldValue = -64; // 等于二进制 11111111111111111111111111000000 
let newValue = oldValue >>> 5; // 等于十进制 134217726

8.布尔操作符

1. 逻辑非--一个叹号(!)表示

逻辑非操作符首先将操作数转换为布尔值,然后再对其取反。

console.log(!false); // true 
console.log(!"blue"); // false 
console.log(!0); // true 
console.log(!NaN); // true 
console.log(!""); // true 
console.log(!12345); // false

 逻辑非操作符也可以用于把任意值转换为布尔值。同时使用两个叹号(!!),相当于调用了转型函 数 Boolean()。无论操作数是什么类型,第一个叹号总会返回布尔值。第二个叹号对该布尔值取反, 从而给出变量真正对应的布尔值。结果与对同一个值使用 Boolean()函数是一样的

console.log(!!"blue"); // true 
console.log(!!0); // false 
console.log(!!NaN); // false 
console.log(!!""); // false 
console.log(!!12345); // true 

2. 逻辑与--由两个和号(&&)表示

 逻辑与操作符是一种短路操作符,意思就是如果第一个操作数决定了结果,那么永远不会对第二个 操作数求值。对逻辑与操作符来说,如果第一个操作数是 false,那么无论第二个操作数是什么值,结 果也不可能等于 true。

 

3.逻辑或---两个管道符(||)表示

 第一个操作数求值为 true,第二个操作数就不会再被求值了

let found = true; 
let result = (found || someUndeclaredVariable); // 不会出错
console.log(result); // 会执行
跟前面的例子一样,变量 someUndeclaredVariable 也没有定义。但是,因为变量 found 的值
为 true,所以逻辑或操作符不会对变量 someUndeclaredVariable 求值,而直接返回 true。假如把
found 的值改为 false,那就会报错了:
let found = false; 
let result = (found || someUndeclaredVariable); // 这里会出错
console.log(result); // 不会执行这一行

 9.乘性操作符

(1)乘法操作符--由一个星号(*)表示,

(2)除法操作符--符由一个斜杠(/)表示

 

 (3) 取模操作符---由一个百分比符号(%)表示

10.指数操作符

 ECMAScript 7 新增了指数操作符,Math.pow()现在有了自己的操作符**

console.log(Math.pow(3, 2); // 9 
console.log(3 ** 2); // 9 
console.log(Math.pow(16, 0.5); // 4 
console.log(16** 0.5); // 4 

指数操作符也有自己的指数赋值操作符**=

let squared = 3; 
squared **= 2; 
console.log(squared); // 9
let sqrt = 16; 
sqrt **= 0.5; 
console.log(sqrt); // 4

11.加性操作符

1. 加法操作符

let result1 = 5 + 5; // 两个数值
console.log(result1); // 10 
let result2 = 5 + "5"; // 一个数值和一个字符串
console.log(result2); // "55"
let num1 = 5; 
let num2 = 10; 
let message = "The sum of 5 and 10 is " + num1 + num2; 
console.log(message); // "The sum of 5 and 10 is 510"

如果想真正执行数学计算,然后把结果追加到字符串末尾,只要使用一对括号即可

let num1 = 5; 
let num2 = 10; 
let message = "The sum of 5 and 10 is " + (num1 + num2); 
console.log(message); // "The sum of 5 and 10 is 15"

2. 减法操作符

let result1 = 5 - true; // true 被转换为 1,所以结果是 4 
let result2 = NaN - 1; // NaN 
let result3 = 5 - 3; // 2 
let result4 = 5 - ""; // ""被转换为 0,所以结果是 5 
let result5 = 5 - "2"; // "2"被转换为 2,所以结果是 3 
let result6 = 5 - null; // null 被转换为 0,所以结果是 5 

12.相等操作符

1. 等于和不等于---两个等于号(==/!=)表示

 2. 全等和不全等--3 个等于号(===/!==)表示

let result1 = ("55" == 55); // true,转换后相等
let result2 = ("55" === 55); // false,不相等,因为数据类型不同

let result1 = ("55" != 55); // false,转换后相等
let result2 = ("55" !== 55); // true,不相等,因为数据类型不同

null===undefined(false)

 

 13.条件操作符

variable = boolean_expression ? true_value : false_value;

上面的代码执行了条件赋值操作,即根据条件表达式 boolean_expression 的值决定将哪个值赋 给变量 variable 。如果 boolean_expression 是 true ,则赋值 true_value ;如果 boolean_expression 是 false,则赋值 false_value。

14.逗号操作符

在一条语句中同时声明多个变量是逗号操作符最常用的场景。不过,也可以使用逗号操作符来辅助 赋值。在赋值时使用逗号操作符分隔值,最终会返回表达式中最后一个值

let num = (5, 1, 4, 8, 0); // num 的值为 0 

10.运算符优先级

  • 一元运算符里面的逻辑非优先级很高
  • 逻辑与比逻辑或优先级高(&& >> ||)

 9.练习

<script>
    console.log( 4 >= 6 || '人' != '阿凡达' && !(12 * 2 == 144) && true)
    //(false || true && !(false) && true)==(true && true && true)==true
    var num = 10;
    console.log( 5 == num / 2 && (2 + 2 * num).toString() === '22')
    //(true &&  true)==true
</script>
<script>
        var a = 3 > 5 && 2 < 7 && 3 == 4; //false && true && false
        console.log(a); //false
        var b = 3 <= 4 || 3 > 1 || 3 != 2; //true || true true
        console.log(b); //true
        var c = 2 === "2"; //false
        console.log(c); //false
        var d = !c || b && a ;
        /*注意点:优先级 ---- && >> ||*/
        //!(false) || true && false==true || true && false=true || false==true
        console.log(d);//true
</script>

十、JavaScript 流程控制--分支

1.流程控制

2.顺序流程控制

3.分支流程控制if语句

3.1 分支结构

3.2 if语句

        (1)语法结构

 

        (2)执行流程 

 【案例】

var usrAge = prompt('请输入您的年龄:');
if(usrAge >= 18){
 alert('您的年龄合法,欢迎来天际网吧享受学习的乐趣!');
}

  3.3 if else语句(双分支语句)

        (1)语法结构

         (2)执行流程

【案例】

if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
 alert("这个年份是闰年");
 } else { // 剩下的是平年
 alert("这个年份是平年");
 }

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

        (1)语法结构

        (2)执行流程 

【案例】

var score = prompt('请您输入分数:');
if (score >= 90) {
alert('宝贝,你是我的骄傲');
} else if (score >= 80) {
alert('宝贝,你已经很出色了');
} else if (score >= 70) {
alert('你要继续加油喽');
} else if (score >= 60) {
alert('孩子,你很危险');
} else {
alert('熊孩子,我不想和你说话,我只想用鞭子和你说话');

4.三元表达式

4.1 语法结构

 4.2 执行思路

【案例】

var time = prompt('请您输入一个 0 ~ 59 之间的一个数字');
// 三元表达式 表达式 ? 表达式1 :表达式2 
var result = time < 10 ? '0' + time : time; // 把返回值赋值给一个变量
alert(result);

5.分支流程控制switch语句

5.1 语法结构

    <script>
        switch (2) {
            case 1:
                alert(1)
                break
            case 2:
                alert(2)
                break
            case 3:
                alert(3)
                break
            default:
                alert('没有数据')

        }

    </script>

 【案例】

var fruit = prompt('请您输入查询的水果:');
switch (fruit) {
case '苹果':
alert('苹果的价格是 3.5/斤');
break;
case '榴莲':
alert('榴莲的价格是 35/斤');
break;
default:
alert('没有此水果');
}

5.2 switch 语句和 if else if 语句的区别

    <script>
        // 循环必须有3要素
        // 变量的起始值
        let i = 1
        // 终止条件
        while (i <= 3) {
            document.write(`月薪过万 <br>`)
            // 变量变化
            i++
        }
    </script>

【案例-ATM取款机】

<script>
    // 1. 不断的弹出对话框
    // 3. 金额的变量
    let money = 100
    while (true) {
        let str = prompt(`请您选择操作:
            1. 存钱
            2. 取钱
            3. 查看余额
            4. 退出
        `)

        // 2. 除非输入的是4 则退出循环  break
        if (str === '4') {
            break
        }

        // 4. switch 加减操作
        switch (str) {
            case '1':
                let cun = +prompt('请您输入存钱的金额:')
                // money = money + cun 
                money += cun
                break
            case '2':
                let qu = +prompt('请您输入取钱的金额:')
                money -= qu
                break
            case '3':
                alert(`您卡上的余额是${money}元`)
                break
        }

    }

</script>

十一、JavaScript 流程控制--循环

1.循环

  

2.for循环

2.1 语法结构

2.2 for循环重复相同的代码

不过,初始化定义的迭代器变 量在循环执行完成后几乎不可能再用到了。因此,最清晰的写法是使用 let 声明迭代器变量,这样就可 以将这个变量的作用域限定在循环中。

 

let count = 10; 
for (let i = 0; i < count; i++) { 
 console.log(i); 
} 
//以上代码在循环开始前定义了变量 i 的初始值为 0。然后求值条件表达式,如果求值结果
//为 true(i < count),则执行循环体。因此循环体也可能不会被执行。如果循环体被执
//行了,则循环后表达式也会执行,以便递增变量 i。for 循环跟下面的 while 循环是一样的:
let count = 10; 
let i = 0; 
while (i < count) { 
 console.log(i); 
 i++

2.3 for循环重复不相同的代码

 

 【案例:求1-100之间所有整数的累加和】

var sum = 0;
for(var i = 1;i <= 100; i++){
 sumNum += i;
}
console.log('1-100之间整数的和 = ' + sum);

 【案例-求学生成绩】

var num = prompt('请输入班级总的人数:'); // num 班级总的人数
var sum = 0; // 总成绩
var average = 0; // 平均成绩
for (var i = 1; i <= num; i++) {
 var score = prompt('请输入第' + i + '个学生的成绩');
 sum = sum + parseFloat(score); 
}
average = sum / num;
alert('班级总的成绩是:' + sum);
alert('班级总的平均成绩是:' + average);

【案例-一行打印五个星星】

var star = '';
for (var i = 1; i <= 5; i++) {
 star += '☆'
 }
console.log(star);

3.双重for循环

3.1 双重for循环概述

 3.2 双重for循环语法

【案例--打印五行五列星星】

 

var star = '';
for (var j = 1; j <= 5; j++) {
 for (var i = 1; i <= 5; i++) {
 star += '☆'
 }
 // 每次满 5个星星 就 加一次换行
 star += '\n'
}
console.log(star);

【案例--打印n行n列星星】

var row = prompt('请输入您打印几行星星:');
var col = prompt('请输入您打印几列星星:');
var str = '';
for (var i = 1; i <= row; i++) {
 for (j = 1; j <= col; j++) {
 str += '☆';
 }
 str += '\n';
}
 console.log(str);

【案例--打印倒三角】

var row = prompt('请输入您打印几行星星:');
var col = prompt('请输入您打印几列星星:');
var str = '';
for (var i = 1; i <= row; i++) {
 for (j = 1; j <= col; j++) {
 str += '☆';
 }
 str += '\n';
}
 console.log(str);

【案例--九九乘法表】

 

<script>
var str = ''
    for (var i = 1; i <= 9; i++) { // 外层for控制 行数 9行
    for (var j = 1; j <= i; j++) { // j 控制列数 列数和行数是一样的 j <= i 
    str += j + " × " + i + " = " + i * j + '\t';
    }
    str += '\n';
    }
    console.log(str);

</script>

3.3 for-in语句

for-in 语句是一种严格的迭代语句,用于枚举对象中的非符号键属性

for (const propName in window) { 
 document.write(propName); 
} 

这个例子使用 for-in 循环显示了 BOM 对象 window 的所有属性。每次执行循环,都会给变量 propName 赋予一个 window 对象的属性作为值,直到 window 的所有属性都被枚举一遍。与 for 循环 一样,这里控制语句中的 const 也不是必需的。但为了确保这个局部变量不被修改,推荐使用 const。

1.所有可枚举的属性都会返回一次,但返回的顺序可能会因浏览器而异。

2.如果 for-in 循环要迭代的变量是 null 或 undefined,则不执行循环体。

var obj = {a:1, b:2, c:3};

for (var prop in obj) {
  console.log("obj." + prop + " = " + obj[prop]);
}

// Output:
// "obj.a = 1"
// "obj.b = 2"
// "obj.c = 3"

 3.4 for-of语句

for-of 语句是一种严格的迭代语句,用于遍历可迭代对象的元素

for (const el of [2,4,6,8]) { 
 document.write(el); 
} 

与 for 循环一样,这里控制语句中的 const 也不是必需的。但为了确保 这个局部变量不被修改,推荐使用 const。

3.5 for-in和for-of的区别

    1.for-in只是获取数组的索引;而for-of会获取数组的值
    2.for-in会遍历对象的整个原型链,性能差;而for-of只遍历当前对象,不会遍历原型链
    3.对于数组的遍历,for-in会返回数组中所有可枚举的属性(包括原型链上可枚举的属性);for-of只返回数组的下标对应的属性值
    4.for-of适用遍历数组/字符串/map/set等有迭代器对象的集合,但是不能遍历普通对象(obj is not iterable)

for-in

let arr = [1, 2, 3, 4, 5]
Array.prototype.id = 123
arr.name = 'Lisa'
for (let index in arr) {
	if (index == 2) {
	}
	console.log(index, arr[index]);//遍历[1,2,3,4,5,Lisa,123]
}

for-of

let arr = [1, 2, 3, 4, 5]
Array.prototype.id = 321
arr.name = 'Lisa'
for (let value of arr) {
	console.log(value);//只能拿到[1,2,3,4,5]
}

3.6  for循环小结

4.while循环

5.do while 循环

【案例】

do {
 var love = prompt('你爱我吗?');
} while (love != '我爱你')
alert('登录成功');

 

6.continue break

6.1 continue关键字

跳出本次循环,继续下一次循环 

6.2 break关键字

跳出整个循环结构

    <script>
        // 我们要打印吃包子
        let i = 1
        while (i <= 6) {
            if (i === 3) {
                i++
                // continue 结束本次循环 继续下一次循环
                // continue
                // 退出循环
                break
            }
            document.write(`我要吃第${i}个包子 <br>`)
            i++
        }
    </script>

 6.3.break和continue语句

break 语句用于立即退 出循环,强制执行循环后的下一条语句。(退出当前循环)

continue 语句也用于立即退出循环,但会再次从循环顶部 开始执行

let num = 0; 
for (let i = 1; i < 10; i++) { 
 if (i % 5 == 0) { 
 break;
 } 
 num++; 
} 
console.log(num); // 4 

而在循环体内,有一个 if 语句用于检查 i 能否被 5 整除(使用取模操作符)。如果是,则执行 break 语句,退出循环。变量 num 的初始值为 0, 表示循环在退出前执行了多少次。当 break 语句执行后,下一行执行的代码是 console.log(num), 显示 4。之所以循环执行了 4 次,是因为当 i 等于 5 时,break 语句会导致循环退出,该次循环不会执 行递增 num 的代码。

let num = 0; 
for (let i = 1; i < 10; i++) { 
 if (i % 5 == 0) { 
 continue; 
 } 
 num++; 
} 
console.log(num); // 8 

7.with函数

with 语句的用途是将代码作用域设置为特定的对象

with 语句的原本用意是为逐级的对象访问提供命名空间式的速写方式. 也就是在指定的代码区域, 直接通过节点名称调用对象。

with 通常被当做重复引用同一个对象中的多个属性的快捷方式,可以不需要重复引用对象本身。

比如,目前现在有一个这样的对象:

var obj = {
    a: 1,
    b: 2,
    c: 3
};


如果想要改变 obj 中每一项的值,一般写法可能会是这样:

// 重复写了3次的“obj”
obj.a = 2;
obj.b = 3;
obj.c = 4;


而用了 with 的写法,会有一个简单的快捷方式

with (obj) {
    a = 3;
    b = 4;
    c = 5;
}

8.switch语句

switch (expression) { 
 case value1: 
 statement
 break; 
 case value2: 
 statement 
 break; 
 case value3: 
 statement 
 break; 
 case value4: 
 statement 
 break; 
 default: 
 statement 
} 

这里的每个 case(条件/分支)相当于:“如果表达式等于后面的值,则执行下面的语句。”break 关键字会导致代码执行跳出 switch 语句。如果没有 break,则代码会继续匹配下一个条件。default 关键字用于在任何条件都没有满足时指定默认执行的语句(相当于 else 语句)。

1.switch 语句可以用于所有数据类型(在很多语言中,它只能用于数值),因此可以使用字符串甚至对象。

2.条件的值不需要是常量,也可以是变量或表达式。

switch ("hello world") { 
 case "hello" + " world": 
 console.log("Greeting was found."); 
 break; 
 case "goodbye": 
 console.log("Closing was found."); 
 break; 
 default: 
 console.log("Unexpected message was found."); 
} 

switch 语句在比较每个条件的值时会使用全等操作符,因此不会强制转换数据类 型(比如,字符串"10"不等于数值 10)

十二、数组

1.数组的概念

2.创建数组

2.1 利用new创建数组--new Array()

 2.2 利用数组字面量创建数组

 2.4 数组元素的类型

  

    <script>
        // 1.数组(Array) :就是一组数据的集合 存储在单个变量下的优雅方式 
        // 2. 利用new 创建数组
        var arr = new Array(); // 创建了一个空的数组
        // 3. 利用数组字面量创建数组 []
        var arr = []; // 创建了一个空的数组
        var arr1 = [1, 2, 'pink老师', true];
        // 4. 我们数组里面的数据一定用逗号分隔
    </script>

3.获取数组中的元素

3.1 数组的索引

<script>
        var arr1 = [1, 2, 'pink老师', true];

        // 5. 数组里面的数据 比如1,2, 我们称为数组元素
        // 6. 获取数组元素  格式 数组名[索引号]  索引号从 0开始 
        console.log(arr1);
        console.log(arr1[2]); // pink老师
        console.log(arr1[3]); // true
        var arr2 = ['迪丽热巴', '古丽扎娜', '佟丽丫丫'];
        console.log(arr2[0]);
        console.log(arr2[1]);
        console.log(arr2[2]);
        console.log(arr2[3]); // 因为没有这个数组元素 所以输出的结果是 undefined

</script>

4.遍历数组

    <script>
        // 遍历数组:就是把数组的元素从头到尾访问一次
        var arr = ['red', 'green', 'blue'];
        for (var i = 0; i < 3; i++) {
            console.log(arr[i]);
        }
        // 1. 因为我们的数组索引号从0开始 ,所以 i 必须从 0开始  i < 3
        // 2. 输出的时候 arr[i]  i 计数器当索引号来用
    </script>

 4.1 数组的长度

    <script>
        // 数组长度 数组名.length
        var arr = ['关羽', '张飞', '马超', '赵云', '黄忠', '刘备', '姜维', 'pink'];
        for (var i = 0; i < 7; i++) {
            console.log(arr[i]);
        }
        console.log(arr.length);
        for (var i = 0; i < arr.length; i++) {
            console.log(arr[i]);
        }
        // 1. 数组的长度是元素个数  不要跟索引号混淆
        // 2. arr.length 动态监测数组元素的个数
    </script>

【案例-数组求和及平均值】

    <script>
        // 1. 求数组 [2,6,1,7, 4] 里面所有元素的和以及平均值。
        // (1)声明一个求和变量 sum。
        // (2)遍历这个数组,把里面每个数组元素加到 sum 里面。
        // (3)用求和变量 sum 除以数组的长度就可以得到数组的平均值。
        var arr = [2, 6, 1, 7, 4];
        var sum = 0;
        var average = 0;
        for (var i = 0; i < arr.length; i++) {
            sum += arr[i]; // 我们加的是数组元素 arr[i] 不是计数器 i
        }
        average = sum / arr.length;
        console.log(sum, average); // 想要输出多个变量,用逗号分隔即可
    </script>

 【案例-数组的最大值】

    <script>
        // 求数组[2,6,1,77,52,25,7]中的最大值
        // 声明一个保存最大元素的变量 max。
        // 默认最大值可以取数组中的第一个元素。
        // 遍历这个数组,把里面每个数组元素和 max 相比较。
        // 如果这个数组元素大于max 就把这个数组元素存到 max 里面,否则继续下一轮比较。
        // 最后输出这个 max
        var arr = [2, 6, 1, 77, 52, 25, 7, 99];
        var max = arr[0];
        for (var i = 1; i < arr.length; i++) {
            if (arr[i] > max) {
                max = arr[i];
            }
        }
        console.log('该数组里面的最大值是:' + max);
    </script>

 【案例-数组转换为分割字符串】

    <script>
        // 将数组 ['red', 'green', 'blue', 'pink'] 转换为字符串,并且用 | 或其他符号分割
        // 1.需要一个新变量用于存放转换完的字符串 str。
        // 2.遍历原来的数组,分别把里面数据取出来,加到字符串里面。
        // 3.同时在后面多加一个分隔符
        var arr = ['red', 'green', 'blue', 'pink'];
        var str = '';
        var sep = '*';
        for (var i = 0; i < arr.length; i++) {
            str += arr[i] + sep;
        }
        console.log(str);
    </script>

5.数组中新增元素

5.1 通过修改length长度新增数组元素

        // 1. 新增数组元素 修改length长度 
        var arr = ['red', 'green', 'blue'];
        console.log(arr.length);
        arr.length = 5; // 把我们数组的长度修改为了 5  里面应该有5个元素 
        console.log(arr);
        console.log(arr[3]); // undefined
        console.log(arr[4]); // undefined

5.2 通过修改数组索引新增数组元素

        // 2. 新增数组元素 修改索引号 追加数组元素
        var arr1 = ['red', 'green', 'blue'];
        arr1[3] = 'pink';
        console.log(arr1);
        arr1[4] = 'hotpink';
        console.log(arr1);
        arr1[0] = 'yellow'; // 这里是替换原来的数组元素
        console.log(arr1);
        arr1 = '有点意思';
        console.log(arr1); // 不要直接给 数组名赋值 否则里面的数组元素都没有了

【案例-数组新增元素】

   <script>
        // 新建一个数组,里面存放10个整数( 1~10)
        // 核心原理:使用循环来追加数组。
        // 1、声明一个空数组 arr。
        // 2、循环中的计数器 i  可以作为数组元素存入。
        // 3、由于数组的索引号是从0开始的, 因此计数器从 0 开始更合适,存入的数组元素要+1。
        var arr = [];
        for (var i = 0; i < 100; i++) {
            // arr = i; 不要直接给数组名赋值 否则以前的元素都没了
            arr[i] = i + 1;
        }
        console.log(arr);
    </script>

【案例-筛选数组】

    <script>
        // 将数组 [2, 0, 6, 1, 77, 0, 52, 0, 25, 7] 中大于等于 10 的元素选出来,放入新数组。
        // 1、声明一个新的数组用于存放新数据newArr。
        // 2、遍历原来的旧数组, 找出大于等于 10 的元素。
        // 3、依次追加给新数组 newArr。
        // 方法1
        var arr = [2, 0, 6, 1, 77, 0, 52, 0, 25, 7];
        var newArr = [];
        var j = 0;
        for (var i = 0; i < arr.length; i++) {
            if (arr[i] >= 10) {
                // 新数组索引号应该从0开始 依次递增
                newArr[j] = arr[i];
                j++;
            }
        }
        console.log(newArr);
        // 方法2 
        var arr = [2, 0, 6, 1, 77, 0, 52, 0, 25, 7];
        var newArr = [];
        // 刚开始 newArr.length 就是 0
        for (var i = 0; i < arr.length; i++) {
            if (arr[i] >= 10) {
                // 新数组索引号应该从0开始 依次递增
                newArr[newArr.length] = arr[i];
            }
        }
        console.log(newArr);
    </script>

6.数组案例

【案例-删除指定数组元素】

    <script>
        // 将数组[2, 0, 6, 1, 77, 0, 52, 0, 25, 7]中的 0 去掉后,形成一个不包含 0 的新数组。
        // 1、需要一个新数组用于存放筛选之后的数据。
        // 2、遍历原来的数组, 把不是 0 的数据添加到新数组里面(此时要注意采用数组名 + 索引的格式接收数据)。
        // 3、新数组里面的个数, 用 length 不断累加。
        var arr = [2, 0, 6, 1, 77, 0, 52, 0, 25, 7];
        var newArr = [];
        for (var i = 0; i < arr.length; i++) {
            if (arr[i] != 0) {
                newArr[newArr.length] = arr[i];
            }
        }
        console.log(newArr);
    </script>

【案例-翻转数组】

    <script>
        // 将数组 ['red', 'green', 'blue', 'pink', 'purple'] 的内容反过来存放
        // 1、声明一个新数组 newArr
        // 2、把旧数组索引号第4个取过来(arr.length - 1),给新数组索引号第0个元素 (newArr.length)
        // 3、我们采取 递减的方式  i--
        var arr = ['red', 'green', 'blue', 'pink', 'purple', 'hotpink'];
        var newArr = [];
        for (var i = arr.length - 1; i >= 0; i--) {
            newArr[newArr.length] = arr[i]
        }
        console.log(newArr);
    </script>

【复习交换两个变量】

    <script>
        // 交换两个变量
        var num1 = 'pink';
        var num2 = 'yellow';
        var temp;
        temp = num1;
        num1 = num2;
        num2 = temp;
        console.log(num1, num2);
    </script>

【案例--冒泡排序】

    <script>
        // 冒泡排序
        // var arr = [5, 4, 3, 2, 1];
        var arr = [4, 1, 2, 3, 5];
        for (var i = 0; i <= arr.length - 1; i++) { // 外层循环管趟数 
            for (var j = 0; j <= arr.length - i - 1; j++) { // 里面的循环管 每一趟的交换次数
                // 内部交换2个变量的值 前一个和后面一个数组元素相比较
                if (arr[j] < arr[j + 1]) {
                    var temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }

            }
        }
        console.log(arr);
    </script>

十二、JavaScript 函数

1.函数的概念

    <script>
        // 1. 求 1~100的累加和
        var sum = 0;
        for (var i = 1; i <= 100; i++) {
            sum += i;
        }
        console.log(sum);

        // 2. 求 10~50的累加和
        var sum = 0;
        for (var i = 10; i <= 50; i++) {
            sum += i;
        }
        console.log(sum);

        // 3. 函数就是封装了一段可以被重复执行调用的代码块 目的: 就是让大量代码重复使用
        function getSum(num1, num2) {
            var sum = 0;
            for (var i = num1; i <= num2; i++) {
                sum += i;
            }
            console.log(sum);
        }
        getSum(1, 100);
        getSum(10, 50);
        getSum(1, 1000);
    </script>

2.函数的使用

2.1 声明函数

2.2 调用函数

 

    <script>
        // 函数使用分为两步: 声明函数 和 调用函数
        // 1. 声明函数
        // function 函数名() {
        //     // 函数体
        // }
        function sayHi() {
            console.log('hi~~');

        }
        // (1) function 声明函数的关键字 全部小写
        // (2) 函数是做某件事情,函数名一般是动词 sayHi 
        // (3) 函数不调用自己不执行
        // 2. 调用函数
        // 函数名();
        sayHi();
        // 调用函数的时候千万不要忘记加小括号
    </script>

2.3 函数的封装

 

【案例--利用函数计算1-100之间累加和】

    <script>
        // 利用函数计算1-100之间的累加和 
        // 1. 声明函数
        function getSum() {
            var sum = 0;
            for (var i = 1; i <= 100; i++) {
                sum += i;
            }
            console.log(sum);

        }
        // 2. 调用函数
        getSum();
        getSum();
    </script>

3.函数的参数

3.1形参和实参

    <script>
        // 1. 函数可以重复相同的代码
        // function cook() {
        //     console.log('酸辣土豆丝');

        // }
        // cook();
        // cook();
        // 2. 我们可以利用函数的参数实现函数重复不同的代码
        // function 函数名(形参1,形参2...) { // 在声明函数的小括号里面是 形参 (形式上的参数)

        // }
        // 函数名(实参1,实参2...); // 在函数调用的小括号里面是实参(实际的参数)
        // 3. 形参和实参的执行过程
        function cook(aru) { // 形参是接受实参的  aru = '酸辣土豆丝' 形参类似于一个变量
            console.log(aru);

        }
        cook('酸辣土豆丝');
        cook('大肘子');
        // 4. 函数的参数可以有,也可以没有个数不限
    </script>

 【案例--利用函数求任意两个数的和】

    <script>
        // 1. 利用函数求任意两个数的和
        function getSum(num1, num2) {
            console.log(num1 + num2);

        }
        getSum(1, 3);
        getSum(3, 8);
        // 2. 利用函数求任意两个数之间的和
        function getSums(start, end) {
            var sum = 0;
            for (var i = start; i <= end; i++) {
                sum += i;
            }
            console.log(sum);

        }
        getSums(1, 100);
        getSums(1, 10);
        // 3. 注意点
        // (1) 多个参数之间用逗号隔开
        // (2) 形参可以看做是不用声明的变量
    </script>

3.2 函数参数的传递过程

 3.3 函数形参和实参个数不匹配问题

<script>
    // 函数形参实参个数匹配
    function getSum(num1, num2) {
        console.log(num1 + num2);

    }
    // 1. 如果实参的个数和形参的个数一致 则正常输出结果
    getSum(1, 2);//1+2=3
    // 2. 如果实参的个数多于形参的个数  会取到形参的个数 
    getSum(1, 2, 3);//1+2=3
    // 3. 如果实参的个数小于形参的个数  多于的形参定义为undefined  最终的结果就是 NaN
    // 形参可以看做是不用声明的变量  num2 是一个变量但是没有接受值  结果就是undefined 
    getSum(1); // NaN
    // 建议 我们尽量让实参的个数和形参相匹配
</script>

3.4 小结

 

4.函数的返回值

4.1 return 语句

【案例-利用函数求任意两个数的最大值】

<script>
        function getMax(num1, num2) {
        return num1 > num2 ? num1 : num2;
        }
        console.log(getMax(1, 2));//2
        console.log(getMax(11, 2));//11
</script>

 【案例--利用函数求任意一个数组中的最大值】

<script>
        //定义一个获取数组中最大数的函数
        function getMaxFromArr(numArray){
        var maxNum = 0;
        for(var i =0;i < numArray.length;i++){
        if(numArray[i] > maxNum){
        maxNum = numArray[i];
        }
        }
        return maxNum;
        }
        var arrNum = [5,2,99,101,67,77];
        var maxN = getMaxFromArr(arrNum); // 这个实参是个数组
        alert('最大值为:'+ maxN);//101
</script>

4.2 return终止函数


 4.3 return  的返回值

<script>
    // 1.函数是做某件事或者实现某种功能
    function cook(aru) {
        console.log(aru);

    }
    cook('大肘子');//大肘子
    // 2. 函数的返回值格式
    // function 函数名() {
    //     return 需要返回的结果;
    // }
    // 函数名();
    // (1) 我们函数只是实现某种功能,最终的结果需要返回给函数的调用者函数名() 通过return 实现的
    // (2) 只要函数遇到return 就把后面的结果 返回给函数的调用者  函数名() = return后面的结果
    // 3. 代码验证
    function getResult() {
        return 666;
    }
    getResult(); // getResult()   = 666
    console.log(getResult());

    function cook(aru) {
        return aru;
    }
    console.log(cook('大肘子'));
    // 4. 求任意两个数的和
    function getSum(num1, num2) {
        return num1 + num2;
    }
    console.log(getSum(1, 2));//3
</script>

 【案例--创建一个函数,实现两个数之间的加减乘除运算,并将结果返回】

<script>
        var a = parseFloat(prompt('请输入第一个数'));
        var b = parseFloat(prompt('请输入第二个数'));
        function count(a, b) {
        var arr = [a + b, a - b, a * b, a / b];
        return arr;
        }
        var result = count(a, b);
        console.log(result);
</script>

4.4 函数没有return 返回 undefined

    <script>
        // 函数返回值注意事项
        // 1. return 终止函数
        function getSum(num1, num2) {
            return num1 + num2; // return 后面的代码不会被执行
            alert('我是不会被执行的哦!')
        }
        console.log(getSum(1, 2));
        // 2. return 只能返回一个值
        function fn(num1, num2) {
            return num1, num2; // 返回的结果是最后一个值
        }
        console.log(fn(1, 2));

        // 3.  我们求任意两个数的 加减乘数结果
        function getResult(num1, num2) {
            return [num1 + num2, num1 - num2, num1 * num2, num1 / num2];
        }
        var re = getResult(1, 2); // 返回的是一个数组
        console.log(re);
        // 4. 我们的函数如果有return 则返回的是 return 后面的值,如果函数么有 return 则返回undefined
        function fun1() {
            return 666;
        }
        console.log(fun1()); // 返回 666
        function fun2() {

        }
        console.log(fun2()); // 函数返回的结果是 undefined
    </script>

4.5 break,continue,return 的区别

 

4.6 return 注意事项

1.return是将要返回的东西进行返回,return后面的句子不会执行

2.return只能返回一个值,只能返回最后一个

3.我们的函数如果有return 则返回的是 return 后面的值,如果函数么有 return 则返回undefined

5.arguments(伪数组)的使用

 当不确定传入多少个参数的时候,就使用arguments

只有函数才可以使用arguments,而且是每个函数都内置好了这个arguments

<script>
    // arguments 的使用  只有函数才有 arguments对象  而且是每个函数都内置好了这个arguments
    function fn() {
        console.log(arguments); // 里面存储了所有传递过来的实参  arguments = [1,2,3]
        console.log(arguments.length);
        console.log(arguments[2]);
        // 我们可以按照数组的方式遍历arguments
        for (var i = 0; i < arguments.length; i++) {
            console.log(arguments[i]);

        }
    }
    fn(1, 2, 3);
    fn(1, 2, 3, 4, 5);
    // 伪数组 并不是真正意义上的数组
    // 1. 具有数组的 length 属性
    // 2. 按照索引的方式进行存储的
    // 3. 它没有真正数组的一些方法 pop()  push() 等等
</script>

 【案例--利用函数求任意个数的最大值】

    <script>
        // 利用函数求任意个数的最大值
        function getMax() { // arguments = [1,2,3]
            var max = arguments[0];
            for (var i = 1; i < arguments.length; i++) {
                if (arguments[i] > max) {
                    max = arguments[i];
                }
            }
            return max;
        }
        console.log(getMax(1, 2, 3));
        console.log(getMax(1, 2, 3, 4, 5));
        console.log(getMax(11, 2, 34, 444, 5, 100));
    </script>

6.函数案例

【案例--利用函数封装方法,翻转任意一个数组】

    <script>
        // 利用函数翻转任意数组 reverse 翻转
        function reverse(arr) {
            var newArr = [];
            for (var i = arr.length - 1; i >= 0; i--) {
                newArr[newArr.length] = arr[i];
            }
            return newArr;
        }
        var arr1 = reverse([1, 3, 4, 6, 9]);
        console.log(arr1);
        var arr2 = reverse(['red', 'pink', 'blue']);
        console.log(arr2);
    </script>

【案例--利用函数封装方法,队数组排序--冒泡排序】

    <script>
        // 利用函数冒泡排序 sort 排序
        function sort(arr) {
            for (var i = 0; i < arr.length - 1; i++) {
                for (var j = 0; j < arr.length - i - 1; j++) {
                    if (arr[j] > arr[j + 1]) {
                        var temp = arr[j];
                        arr[j] = arr[j + 1];
                        arr[j + 1] = temp;
                    }
                }
            }
            return arr;
        }
        var arr1 = sort([1, 4, 2, 9]);
        console.log(arr1);
        var arr2 = sort([11, 7, 22, 999]);
        console.log(arr2);
    </script>

【案例--判断闰年】

    <script>
        // 利用函数判断闰年
        function isRunYear(year) {
            // 如果是闰年我们返回 true  否则 返回 false 
            var flag = false;
            if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
                flag = true;
            }
            return flag;
        }
        console.log(isRunYear(2000));
        console.log(isRunYear(1999));
    </script>

函数可以调用另外一个函数

<script>
    // 函数是可以相互调用的
    // function fn1() {
    //     console.log(11);
    //     fn2(); // 在fn1 函数里面调用了 fn2 函数
    // }
    // fn1();

    // function fn2() {
    //     console.log(22);

    // }

    function fn1() {
        console.log(111);
        fn2();
        console.log('fn1');
    }

    function fn2() {
        console.log(222);
        console.log('fn2');
    }
    //调用了fn1()
    fn1();//111 222 fn2 fn1
</script>

【案例-用户输入年份,输出当前年份2月份的天数】

    <script>
        // 用户输入年份,输出当前年份2月份的天数
        function backDay() {
            var year = prompt('请您输入年份:');
            if (isRunYear(year)) { // 调用函数需要加小括号
                alert('当前年份是闰年2月份有29天');
            } else {
                alert('当前年份是平年2月份有28天');
            }
        }
        backDay();


        // 判断是否为闰年的函数
        function isRunYear(year) {
            // 如果是闰年我们返回 true  否则 返回 false 
            var flag = false;
            if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
                flag = true;
            }
            return flag;
        }
    </script>

7.函数的两种声明方式

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

调用函数的代码和声明函数的代码顺序无所谓

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

 调用函数的代码只能在声明函数的代码后面

    <script>
        // 函数的2中声明方式
        // 1. 利用函数关键字自定义函数(命名函数)
        function fn() {

        }
        fn();
        // 2. 函数表达式(匿名函数) 
        // var 变量名 = function() {};
        var fun = function(aru) {
            console.log('我是函数表达式');
            console.log(aru);

        }
        fun('pink老师');
        // (1) fun是变量名 不是函数名  
        // (2) 函数表达式声明方式跟声明变量差不多,只不过变量里面存的是值 而 函数表达式里面存的是函数
        // (3) 函数表达式也可以进行传递参数
    </script>

十三、Javascript 作用域

1.作用域

    <script>
        // 1.JavaScript作用域 : 就是代码名字(变量)在某个范围内起作用和效果 目的是为了提高程序的可靠性更重要的是减少命名冲突
        // 2. js的作用域(es6)之前 : 全局作用域   局部作用域 
        // 3. 全局作用域: 整个script标签 或者是一个单独的js文件
        var num = 10;
        var num = 30;
        console.log(num);//30

        // 4. 局部作用域(函数作用域) 在函数内部就是局部作用域 这个代码的名字只在函数内部起效果和作用
        function fn() {
            // 局部作用域
            var num = 20;
            console.log(num);

        }
        fn();//20
    </script>

2.变量的作用域

1.注意 如果在函数内部 没有声明直接赋值的变量也属于全局变量

2.函数的形参也可以看做是局部变量

3. 从执行效率来看全局变量和局部变量
         (1) 全局变量只有浏览器关闭的时候才会销毁,比较占内存资源
         (2) 局部变量 当我们程序执行完毕就会销毁, 比较节约内存资源

2.1 全局变量

2.2 局部变量

 

    <script>
        // 变量的作用域: 根据作用域的不同我们变量分为全局变量和局部变量
        // 1. 全局变量: 在全局作用域下的变量 在全局下都可以使用
        // 注意 如果在函数内部 没有声明直接赋值的变量也属于全局变量
        var num = 10; // num就是一个全局变量
        console.log(num);//10

        function fn() {
            console.log(num);

        }
        fn();
        // console.log(aru);

        // 2. 局部变量   在局部作用域下的变量   后者在函数内部的变量就是 局部变量
        // 注意: 函数的形参也可以看做是局部变量
        function fun(aru) {
            var num1 = 10; // num1就是局部变量 只能在函数内部使用
            num2 = 20;
        }
        fun();
        // console.log(num1);//报错
        // console.log(num2);//20
        // 3. 从执行效率来看全局变量和局部变量
        // (1) 全局变量只有浏览器关闭的时候才会销毁,比较占内存资源
        // (2) 局部变量 当我们程序执行完毕就会销毁, 比较节约内存资源
    </script>

2.3 Javascript没有块级作用域

    <script>
        // js中没有块级作用域  js的作用域: 全局作用域  局部作用域  现阶段我们js 没有 块级作用域
        // 我们js 也是在 es6 的时候新增的块级作用域
        // 块级作用域 {}   if {}  for {}
        // java 
        // if(xx) {
        //     int num = 10;
        // }
        // 外面的是不能调用num的
        if (3 < 5) {
            var num = 10;
        }
        console.log(num);
    </script>

3.作用域链

    <script>
        // 作用域链  : 内部函数访问外部函数的变量,采取的是链式查找的方式来决定取那个值 这种结构我们称为作用域链   就近原则
        var num = 10;

        function fn() { // 外部函数
            var num = 20;

            function fun() { // 内部函数
                console.log(num);//20

            }
            fun();
        }
        fn();
    </script>

【案例】

   <script>
        // 案例1 : 结果是几?
        function f1() {
            var num = 123;

            function f2() {
                var num = 0;
                console.log(num);//0  // 站在目标出发,一层一层的往外查找
            }
            f2();
        }
        var num = 456;
        f1();
        // 案例2 :结果是几?
        var a = 1;

        function fn1() {
            var a = 2;
            var b = '22';
            fn2();

            function fn2() {
                var a = 3;
                fn3();

                function fn3() {
                    var a = 4;
                    console.log(a); //4  //a的值 ?
                    console.log(b); // 22  //b的值 ?
                }
            }
        }
        fn1();
    </script>

十四、预解析

1.预解析

    <script>
        // 1问  
        console.log(num);



        // 2问
        console.log(num); // undefined  坑 1
        var num = 10;
        // 相当于执行了以下代码
        // var num;
        // console.log(num);
        // num = 10;



        // 3问  
        function fn() {
            console.log(11);
        }
        fn();




        // 4问
        fun(); // 报错  坑2 
        var fun = function() {
                console.log(22);

            }
            // 函数表达式 调用必须写在函数表达式的下面
            // 相当于执行了以下代码
            // var fun;
            // fun();
            // fun = function() {
            //         console.log(22);

        //     }

        // 1. 我们js引擎运行js 分为两步:  预解析  代码执行
        // (1). 预解析 js引擎会把js 里面所有的 var  还有 function 提升到当前作用域的最前面
        // (2). 代码执行  按照代码书写的顺序从上往下执行
        // 2. 预解析分为 变量预解析(变量提升) 和 函数预解析(函数提升)
        // (1) 变量提升 就是把所有的变量声明提升到当前的作用域最前面  不提升赋值操作
        // (2) 函数提升 就是把所有的函数声明提升到当前作用域的最前面  不调用函数
    </script>

2.变量预解析和函数预解析

1. 我们js引擎运行js 分为两步:  预解析  代码执行
        // (1). 预解析 js引擎会把js 里面所有的 var  还有 function 提升到当前作用域的最前面
        // (2). 代码执行  按照代码书写的顺序从上往下执行
2. 预解析分为 变量预解析(变量提升) 和 函数预解析(函数提升)
        // (1) 变量提升 就是把所有的变量声明提升到当前的作用域最前面  不提升赋值操作
        // (2) 函数提升 就是把所有的函数声明提升到当前作用域的最前面  不调用函数

3.预解析案例

    <script>
        // 预解析案例
        // 案例1
        // var num = 10;
        // fun();

        // function fun() {
        //     console.log(num);
        //     var num = 20;
        // }
        // // 相当于执行了以下操作
        // // var num;

        // // function fun() {
        // //     var num;
        // //     console.log(num);//undefined
        // //     num = 20;
        // // }
        // // num = 10;
        // // fun();


        // // 案例2
        // var num = 10;

        // function fn() {
        //     console.log(num);
        //     var num = 20;
        //     console.log(num);
        // }
        // fn();
        // // 相当于以下代码
        // // var num;

        // // function fn() {
        // //     var num;
        // //     console.log(num);//undefined
        // //     num = 20;
        // //     console.log(num);//20
        // // }
        // // num = 10;
        // // fn();


        // // 案例3
        // var a = 18;
        // f1();

        // function f1() {
        //     var b = 9;
        //     console.log(a);
        //     console.log(b);
        //     var a = '123';
        // }
        // 相当于以下代码
        // var a;

        // function f1() {
        //     var b;
        //     var a;
        //     b = 9;
        //     console.log(a);//undefined
        //     console.log(b);//9
        //     a = '123';
        // }
        // a = 18;
        // f1();


        // 案例4
        f1();
        console.log(c);
        console.log(b);
        console.log(a);

        function f1() {
            var a = b = c = 9;
            console.log(a);
            console.log(b);
            console.log(c);
        }
        // 以下代码
        // function f1() {
        //     var a;
        //     a = b = c = 9;
        //     // 相当于 var  a  = 9; b = 9; c = 9; b 和 c 直接赋值 没有var 声明 当 全局变量看
        //     // 集体声明  var a = 9, b = 9, c = 9;-->相当于 var a=9,var b=9,var c=9;
        //     console.log(a);//9
        //     console.log(b);//9
        //     console.log(c);//9
        // }
        // f1();
        // console.log(c);//9
        // console.log(b);//9
        // console.log(a);//报错
    </script>

十五、Javascript 对象

1.对象

1.1 什么是对象

1.2 为什么需要对象

 

2.创建对象的三种方式

2.1 利用字面量创建对象

<script>
        var star = {
            name : 'pink',
            age : 18,
            sex : '男',
            sayHi : function(){
            alert('大家好啊~');
            }
        };
</script>

<script>
    // 1.利用对象字面量创建对象 {}
    // var obj = {};  // 创建了一个空的对象 
    var obj = {
            uname: '张三疯',
            age: 18,
            sex: '男',
            sayHi: function() {
                console.log('hi~');

            }
        }
        // (1) 里面的属性或者方法我们采取键值对的形式  键 属性名 : 值  属性值 
        // (2) 多个属性或者方法中间用逗号隔开的
        // (3) 方法冒号后面跟的是一个匿名函数
        // 2. 使用对象
        // (1). 调用对象的属性 我们采取 对象名.属性名 . 我们理解为 的
    console.log(obj.uname);
    // (2). 调用属性还有一种方法 对象名['属性名']
    console.log(obj['age']);
    // (3) 调用对象的方法 sayHi   对象名.方法名() 千万别忘记添加小括号
    obj.sayHi();
</script>

变量、属性、函数、方法总结

 

    <script>
        // 变量、属性、函数、方法的区别
        // 1.变量和属性的相同点 他们都是用来存储数据的 
        var num = 10;
        var obj = {
            age: 18,
            fn: function() {

            }
        }

        function fn() {

        }
        console.log(obj.age);
        // console.log(age);

        // 变量 单独声明并赋值  使用的时候直接写变量名 单独存在
        // 属性 在对象里面的不需要声明的 使用的时候必须是 对象.属性
        // 2. 函数和方法的相同点 都是实现某种功能  做某件事
        // 函数是单独声明 并且调用的 函数名() 单独存在的
        // 方法 在对象里面 调用的时候 对象.方法()
    </script>
    <script>
        // 我们为什么需要使用构造函数
        // 就是因我们前面两种创建对象的方式一次只能创建一个对象
        var ldh = {
            uname: '刘德华',
            age: 55,
            sing: function() {
                console.log('冰雨');

            }
        }
        var zxy = {
                uname: '张学友',
                age: 58,
                sing: function() {
                    console.log('李香兰');

                }
            }
            // 因为我们一次创建一个对象,里面很多的属性和方法是大量相同的 我们只能复制 
            // 因此我们可以利用函数的方法 重复这些相同的代码 我们就把这个函数称为 构造函数
            // 又因为这个函数不一样,里面封装的不是普通代码,而是 对象  
            // 构造函数 就是把我们对象里面一些相同的属性和方法抽象出来封装到函数里面
    </script>

2.2 利用new Object 创建对象

 

    <script>
        // 利用 new Object 创建对象
        var obj = new Object(); // 创建了一个空的对象
        obj.uname = '张三疯';
        obj.age = 18;
        obj.sex = '男';
        obj.sayHi = function() {
                console.log('hi~');

            }
            // (1) 我们是利用 等号 = 赋值的方法 添加对象的属性和方法
            // (2) 每个属性和方法之间用 分号结束
        console.log(obj.uname);
        console.log(obj['sex']);
        obj.sayHi();
    </script>

2.3 利用构造函数创建对象

 

<script>
        function Person(name, age, sex) {
            this.name = name;
            this.age = age;
            this.sex = sex;
            this.sayHi = function() {
            alert('我的名字叫:' + this.name + ',年龄:' + this.age + ',性别:' + this.sex);
            }
        }
        var bigbai = new Person('大白', 100, '男');
        var smallbai = new Person('小白', 21, '男');
        console.log(bigbai.name);
        console.log(smallbai.name);
</script>

    <script>
        // 利用构造函数创建对象
        // 我们需要创建四大天王的对象  相同的属性: 名字 年龄 性别  相同的方法: 唱歌
        // 构造函数的语法格式
        // function 构造函数名() {
        //     this.属性 = 值;
        //     this.方法 = function() {}
        // }
        // new 构造函数名();
        function Star(uname, age, sex) {
            this.name = uname;
            this.age = age;
            this.sex = sex;
            this.sing = function(sang) {
                console.log(sang);

            }
        }
        var ldh = new Star('刘德华', 18, '男'); // 调用函数返回的是一个对象
        // console.log(typeof ldh);
        console.log(ldh.name);
        console.log(ldh['sex']);
        ldh.sing('冰雨');
        var zxy = new Star('张学友', 19, '男');
        console.log(zxy.name);
        console.log(zxy.age);
        zxy.sing('李香兰')



        // 1. 构造函数名字首字母要大写
        // 2. 我们构造函数不需要return 就可以返回结果
        // 3. 我们调用构造函数 必须使用 new
        // 4. 我们只要new Star() 调用函数就创建一个对象 ldh  {}
        // 5. 我们的属性和方法前面必须添加 this
    </script>

2.4 构造函数和对象

 

    <script>
        // 构造函数和对象
        // 1. 构造函数  明星 泛指的某一大类  它类似于 java 语言里面的  类(class)
        function Star(uname, age, sex) {
            this.name = uname;
            this.age = age;
            this.sex = sex;
            this.sing = function(sang) {
                console.log(sang);

            }
        }
        // 2. 对象 特指 是一个具体的事物 刘德华 ==  {name: "刘德华", age: 18, sex: "男", sing: ƒ}
        var ldh = new Star('刘德华', 18, '男'); // 调用函数返回的是一个对象
        console.log(ldh);
        // 3. 我们利用构造函数创建对象的过程我们也称为对象的实例化
    </script>

3.new关键字

    <script>
        // new关键字执行过程
        // 1. new 构造函数可以在内存中创建了一个空的对象 
        // 2. this 就会指向刚才创建的空对象
        // 3. 执行构造函数里面的代码 给这个空对象添加属性和方法
        // 4. 返回这个对象
        function Star(uname, age, sex) {
            this.name = uname;
            this.age = age;
            this.sex = sex;
            this.sing = function(sang) {
                console.log(sang);

            }
        }
        var ldh = new Star('刘德华', 18, '男');
    </script>

4.遍历对象属性(for...in)--for( var k in obj) 

    <script>
        // 遍历对象 
        var obj = {
                name: 'pink老师',
                age: 18,
                sex: '男',
                fn: function() {}
            }
            // console.log(obj.name);
            // console.log(obj.age);
            // console.log(obj.sex);
            // for in 遍历我们的对象
            // for (变量 in 对象) {

        // }
        for (var k in obj) {
            console.log(k); // k 变量 输出  得到的是 属性名
            console.log(obj[k]); // obj[k] 得到是 属性值

        }
        // 我们使用 for in 里面的变量 我们喜欢写 k  或者  key
    </script>

十六、JavaScript 内置对象

1.内置对象

2.查文档

2.1 MDN

MDN Web Docs (mozilla.org)

2.2 如何学习对象中的方法

3.Math对象

3.1 Math概述

<script>
    // Math数学对象 不是一个构造函数 ,所以我们不需要new 来调用 而是直接使用里面的属性和方法即可
    console.log(Math.PI); // 一个属性 圆周率
    console.log(Math.max(1, 99, 3)); // 99
    console.log(Math.max(-1, -10)); // -1
    console.log(Math.max(1, 99, 'pink老师')); // NaN
    console.log(Math.max()); // -Infinity
</script>
    <script>
        // 1.绝对值方法
        console.log(Math.abs(1)); // 1
        console.log(Math.abs(-1)); // 1
        console.log(Math.abs('-1')); // 隐式转换 会把字符串型 -1 转换为数字型
        console.log(Math.abs('pink')); // NaN 

        // 2.三个取整方法
        // (1) Math.floor()   地板 向下取整  往最小了取值
        console.log(Math.floor(1.1)); // 1
        console.log(Math.floor(1.9)); // 1
        // (2) Math.ceil()   ceil 天花板 向上取整  往最大了取值
        console.log(Math.ceil(1.1)); // 2
        console.log(Math.ceil(1.9)); // 2
        // (3) Math.round()   四舍五入  其他数字都是四舍五入,但是 .5 特殊 它往大了取  
        console.log(Math.round(1.1)); // 1
        console.log(Math.round(1.5)); // 2
        console.log(Math.round(1.9)); // 2
        console.log(Math.round(-1.1)); // -1
        console.log(Math.round(-1.5)); // 这个结果是 -1
    </script>

【案例-封装自己的数学对象】

<script>
    // 利用对象封装自己的数学对象  里面有 PI 最大值和最小值
    var myMath = {
        PI: 3.141592653,
        max: function() {
            var max = arguments[0];
            for (var i = 1; i < arguments.length; i++) {
                if (arguments[i] > max) {
                    max = arguments[i];
                }
            }
            return max;
        },
        min: function() {
            var min = arguments[0];
            for (var i = 1; i < arguments.length; i++) {
                if (arguments[i] < min) {
                    min = arguments[i];
                }
            }
            return min;
        }
    }
    console.log(myMath.PI);
    console.log(myMath.max(1, 5, 9));
    console.log(myMath.min(1, 5, 9));
</script>

3.2 随机数方法random()--[0,1)

 

    <script>
        // 1.Math对象随机数方法   random() 返回一个随机的小数  0 =< x < 1
        // 2. 这个方法里面不跟参数
        // 3. 代码验证 
        console.log(Math.random());
        // 4. 我们想要得到两个数之间的随机整数 并且 包含这2个整数
        // Math.floor(Math.random() * (max - min + 1)) + min;
        function getRandom(min, max) {
            return Math.floor(Math.random() * (max - min + 1)) + min;
        }
        console.log(getRandom(1, 10));
        // 5. 随机点名  
        var arr = ['张三', '张三丰', '张三疯子', '李四', '李思思', 'pink老师'];
        // console.log(arr[0]);
        console.log(arr[getRandom(0, arr.length - 1)]);
    </script>

 【案例--猜数字游戏】

    <script>
        // 猜数字游戏
        // 1.随机生成一个1~10 的整数  我们需要用到 Math.random() 方法。
        // 2.需要一直猜到正确为止,所以需要一直循环。
        // 3.while 循环更简单
        // 4.核心算法:使用 if  else if 多分支语句来判断大于、小于、等于。
        function getRandom(min, max) {
            return Math.floor(Math.random() * (max - min + 1)) + min;
        }
        var random = getRandom(1, 10);
        while (true) { // 死循环
            var num = prompt('你来猜? 输入1~10之间的一个数字');
            if (num > random) {
                alert('你猜大了');
            } else if (num < random) {
                alert('你猜小了');
            } else {
                alert('你好帅哦,猜对了');
                break; // 退出整个循环结束程序
            }

        }
        // 要求用户猜 1~50之间的一个数字 但是只有 10次猜的机会
    </script>

4.日期对象

4.1 Date概述

使用时需要先实例化(先new 一个对象才可以使用)

 4.2 Date()方法的使用

    <script>
        // Date() 日期对象  是一个构造函数 必须使用new 来调用创建我们的日期对象
        var arr = new Array(); // 创建一个数组对象
        var obj = new Object(); // 创建了一个对象实例
        // 1. 使用Date  如果没有参数 返回当前系统的当前时间
        var date = new Date();
        console.log(date);
        // 2. 参数常用的写法  数字型  2019, 10, 01  或者是 字符串型 '2019-10-1 8:8:8'
        var date1 = new Date(2019, 10, 1);
        console.log(date1); // 返回的是 11月 不是 10月 
        var date2 = new Date('2019-10-1 8:8:8');
        console.log(date2);
    </script>

创建的对象将保存当前日期和时间:ECMAScript 为此提供了两个辅助方法:Date.parse()和 Date.UTC()

//比如,要创建一个表示“2019 年 5 月 23 日”的日期对象,可以使用以下代码:
let someDate = new Date(Date.parse("May 23, 2019"));
//如果直接把表示日期的字
//符串传给 Date 构造函数,那么 Date 会在后台调用 Date.parse()。
//与上面是等价的:
let someDate = new Date("May 23, 2019");

ECMAScript 还提供了 Date.now()方法,返回表示方法执行时日期和时间的毫秒数。

// 起始时间
let start = Date.now(); 
// 调用函数
doSomething(); 
// 结束时间
let stop = Date.now(), 
result = stop - start;

4.3 继承的方法

Date 类型重写了 toLocaleString()、toString()和 valueOf()方法。

Date 类型的 valueOf()方法根本就不返回字符串,这个方法被重写后返回的是日期的毫秒表示。

let date1 = new Date(2019, 0, 1); // 2019 年 1 月 1 日
let date2 = new Date(2019, 1, 1); // 2019 年 2 月 1 日
console.log(date1 < date2); // true 
console.log(date1 > date2); // false

4.4 日期格式化

 

    <script>
        // 格式化日期 年月日 
        var date = new Date();
        console.log(date.getFullYear()); // 返回当前日期的年  2019
        console.log(date.getMonth() + 1); // 月份 返回的月份小1个月   记得月份+1 呦
        console.log(date.getDate()); // 返回的是 几号
        console.log(date.getDay()); // 3  周一返回的是 1 周六返回的是 6 但是 周日返回的是 0
        // 我们写一个 2019年 5月 1日 星期三
        var year = date.getFullYear();
        var month = date.getMonth() + 1;
        var dates = date.getDate();
        var arr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
        var day = date.getDay();
        console.log('今天是:' + year + '年' + month + '月' + dates + '日 ' + arr[day]);
    </script>
    <script>
        // 格式化日期 时分秒
        var date = new Date();
        console.log(date.getHours()); // 时
        console.log(date.getMinutes()); // 分
        console.log(date.getSeconds()); // 秒
        // 要求封装一个函数返回当前的时分秒 格式 08:08:08
        function getTimer() {
            var time = new Date();
            var h = time.getHours();
            h = h < 10 ? '0' + h : h;
            var m = time.getMinutes();
            m = m < 10 ? '0' + m : m;
            var s = time.getSeconds();
            s = s < 10 ? '0' + s : s;
            return h + ':' + m + ':' + s;
        }
        console.log(getTimer());
    </script>

4.4 获取日期的总的毫秒形式

<script>
        // 实例化Date对象
        var now = new Date('');
        // 1. 用于获取对象的原始值
        console.log(date.valueOf())
        console.log(date.getTime())
        // 2. 简单写可以这么做
        var now = + new Date();
        // 3. HTML5中提供的方法,有兼容性问题
        var now = Date.now()
</script>
    <script>
        // 获得Date总的毫秒数(时间戳)  不是当前时间的毫秒数 而是距离1970年1月1号过了多少毫秒数
        // 1. 通过 valueOf()  getTime()
        var date = new Date();
        console.log(date.valueOf()); // 就是 我们现在时间 距离1970.1.1 总的毫秒数
        console.log(date.getTime());
        // 2. 简单的写法 (最常用的写法)
        var date1 = +new Date(); // +new Date()  返回的就是总的毫秒数
        console.log(date1);
        // 3. H5 新增的 获得总的毫秒数
        console.log(Date.now());
    </script>

【案例:倒计时效果】

<script>
    // 倒计时效果
    // 1.核心算法:输入的时间减去现在的时间就是剩余的时间,即倒计时 ,但是不能拿着时分秒相减,比如 05 分减去25分,结果会是负数的。
    // 2.用时间戳来做。用户输入时间总的毫秒数减去现在时间的总的毫秒数,得到的就是剩余时间的毫秒数。
    // 3.把剩余时间总的毫秒数转换为天、时、分、秒 (时间戳转换为时分秒)
    // 转换公式如下: 
    //  d = parseInt(总秒数/ 60/60 /24);    //  计算天数
    //  h = parseInt(总秒数/ 60/60 %24)   //   计算小时
    //  m = parseInt(总秒数 /60 %60 );     //   计算分数
    //  s = parseInt(总秒数%60);            //   计算当前秒数
    function countDown(time) {
        var nowTime = +new Date(); // 返回的是当前时间总的毫秒数
        var inputTime = +new Date(time); // 返回的是用户输入时间总的毫秒数
        var times = (inputTime - nowTime) / 1000; // times是剩余时间总的秒数 
        var d = parseInt(times / 60 / 60 / 24); // 天
        d = d < 10 ? '0' + d : d;
        var h = parseInt(times / 60 / 60 % 24); //时
        h = h < 10 ? '0' + h : h;
        var m = parseInt(times / 60 % 60); // 分
        m = m < 10 ? '0' + m : m;
        var s = parseInt(times % 60); // 当前的秒
        s = s < 10 ? '0' + s : s;
        return d + '天' + h + '时' + m + '分' + s + '秒';
    }
    console.log(countDown('2019-5-1 18:00:00'));
    var date = new Date();
    console.log(date);
</script>

5.数组对象

5.1 数组对象的创建

    <script>
        // 创建数组的两种方式
        // 1. 利用数组字面量
        var arr = [1, 2, 3];
        console.log(arr[0]);

        // 2. 利用new Array()
        // var arr1 = new Array();  // 创建了一个空的数组
        // var arr1 = new Array(2);  // 这个2 表示 数组的长度为 2  里面有2个空的数组元素 
        var arr1 = new Array(2, 3); // 等价于 [2,3]  这样写表示 里面有2个数组元素 是 2和3
        console.log(arr1);
    </script>

5.2 检测是否为数组----instanceof、Array.isArray()

    <script>
        // 翻转数组
        function reverse(arr) {
            // if (arr instanceof Array) {
            if (Array.isArray(arr)) {
                var newArr = [];
                for (var i = arr.length - 1; i >= 0; i--) {
                    newArr[newArr.length] = arr[i];

                }
                return newArr;
            } else {
                return 'error 这个参数要求必须是数组格式 [1,2,3]'
            }
        }
        console.log(reverse([1, 2, 3]));
        console.log(reverse(1, 2, 3));
        // 检测是否为数组
        // (1) instanceof  运算符 它可以用来检测是否为数组
        var arr = [];
        var obj = {};
        console.log(arr instanceof Array);
        console.log(obj instanceof Array);
        // (2) Array.isArray(参数);  H5新增的方法  ie9以上版本支持
        console.log(Array.isArray(arr));
        console.log(Array.isArray(obj));
    </script>

 5.3 添加删除数组元素的方法

添加元素--push()、unshift()

删除元素--pop()、shift()

<script>
    // 添加删除数组元素方法
    // 1. push() 在我们数组的末尾 添加一个或者多个数组元素   push  推
    var arr = [1, 2, 3];
    // arr.push(4, 'pink');
    console.log(arr.push(4, 'pink'));

    console.log(arr);//5--[1,2,3,4,'pink']
    // (1) push 是可以给数组追加新的元素
    // (2) push() 参数直接写 数组元素就可以了
    // (3) push完毕之后,返回的结果是 新数组的长度 
    // (4) 原数组也会发生变化

    // 2. unshift 在我们数组的开头 添加一个或者多个数组元素
    console.log(arr.unshift('red', 'purple'));
    console.log(arr);//7--['red','purple',1,2,3,4,'pink']
    // (1) unshift是可以给数组前面追加新的元素
    // (2) unshift() 参数直接写 数组元素就可以了
    // (3) unshift完毕之后,返回的结果是 新数组的长度 
    // (4) 原数组也会发生变化

    // 3. pop() 它可以删除数组的最后一个元素  
    console.log(arr.pop());
    console.log(arr);//6--['red','purple',1,2,3,4]
    // (1) pop是可以删除数组的最后一个元素 记住一次只能删除一个元素
    // (2) pop() 没有参数
    // (3) pop完毕之后,返回的结果是 删除的那个元素 
    // (4) 原数组也会发生变化
    
    // 4. shift() 它可以删除数组的第一个元素  
    console.log(arr.shift());
    console.log(arr);
    // (1) shift是可以删除数组的第一个元素 记住一次只能删除一个元素
    // (2) shift() 没有参数
    // (3) shift完毕之后,返回的结果是 删除的那个元素 
    // (4) 原数组也会发生变化
</script>

 【案例: 筛选数组】

    <script>
        // 有一个包含工资的数组[1500, 1200, 2000, 2100, 1800],要求把数组中工资超过2000的删除,剩余的放到新数组里面
        var arr = [1500, 1200, 2000, 2100, 1800];
        var newArr = [];
        for (var i = 0; i < arr.length; i++) {
            if (arr[i] < 2000) {
                // newArr[newArr.length] = arr[i];
                newArr.push(arr[i]);
            }
        }
        console.log(newArr);
    </script>

5.4 数组排序

 

    <script>
        // 数组排序
        // 1. 翻转数组
        var arr = ['pink', 'red', 'blue'];
        arr.reverse();
        console.log(arr);

        // 2. 数组排序(冒泡排序)
        var arr1 = [13, 4, 77, 1, 7];
        arr1.sort(function(a, b) {
            //  return a - b; 升序的顺序排列
            return b - a; // 降序的顺序排列
        });
        console.log(arr1);
    </script>

5.5 数组索引方法

 

<script>
    // 返回数组元素索引号方法  indexOf(数组元素)  作用就是返回该数组元素的索引号 从前面开始查找
    // 它只返回第一个满足条件的索引号 
    // 它如果在该数组里面找不到元素,则返回的是 -1  
    // var arr = ['red', 'green', 'blue', 'pink', 'blue'];
    var arr = ['red', 'green', 'pink'];
    console.log(arr.indexOf('blue'));//-1
    // 返回数组元素索引号方法  lastIndexOf(数组元素)  作用就是返回该数组元素的索引号 从后面开始查找
    var arr = ['red', 'green', 'blue', 'pink', 'blue'];

    console.log(arr.lastIndexOf('blue')); // 4
</script>

 【案例: 数组去重】

<script>
    // 数组去重 ['c', 'a', 'z', 'a', 'x', 'a', 'x', 'c', 'b'] 要求去除数组中重复的元素。
    // 1.目标: 把旧数组里面不重复的元素选取出来放到新数组中, 重复的元素只保留一个, 放到新数组中去重。
    // 2.核心算法: 我们遍历旧数组, 然后拿着旧数组元素去查询新数组, 如果该元素在新数组里面没有出现过, 我们就添加, 否则不添加。
    // 3.我们怎么知道该元素没有存在? 利用 新数组.indexOf(数组元素) 如果返回时 - 1 就说明 新数组里面没有改元素
    // 封装一个 去重的函数 unique 独一无二的 
    function unique(arr) {
        var newArr = [];
        for (var i = 0; i < arr.length; i++) {
            if (newArr.indexOf(arr[i]) === -1) {
                newArr.push(arr[i]);
            }
        }
        return newArr;
    }
    // var demo = unique(['c', 'a', 'z', 'a', 'x', 'a', 'x', 'c', 'b'])
    var demo = unique(['blue', 'green', 'blue'])
    console.log(demo);
</script>

5.6 数组转换为字符串

 

    <script>
        // 数组转换为字符串 
        // 1. toString() 将我们的数组转换为字符串
        var arr = [1, 2, 3];
        console.log(arr.toString()); // 1,2,3
        // 2. join(分隔符) 
        var arr1 = ['green', 'blue', 'pink'];
        console.log(arr1.join()); // green,blue,pink
        console.log(arr1.join('-')); // green-blue-pink
        console.log(arr1.join('&')); // green&blue&pink
    </script>

5.7 其他

 

6.字符串对象

6.1 基本包装类型

    <script>
        // 基本包装类型
        var str = 'andy';
        console.log(str.length);
        // 对象 才有 属性和方法   复杂数据类型才有 属性和方法 
        // 简单数据类型为什么会有length 属性呢? 
        // 基本包装类型:  就是把简单数据类型 包装成为了 复杂数据类型 
        // (1) 把简单数据类型包装为复杂数据类型 
        var temp = new String('andy');
        // (2) 把临时变量的值 给 str
        str = temp;
        // (3) 销毁这个临时变量
        temp = null;
    </script>

6.2 字符串的不可变

 

    <script>
        // 字符串的不可变性
        var str = 'andy';
        console.log(str);
        str = 'red';
        console.log(str);
        // 因为我们字符串的不可变所以不要大量的拼接字符串
        var str = '';
        for (var i = 1; i <= 1000000000; i++) {
            str += i;
        }
        console.log(str);
    </script>

6.3 根据字符返回位置

 

 【案例:返回字符位置】

    <script>
        // 字符串对象  根据字符返回位置  str.indexOf('要查找的字符', [起始的位置])
        var str = '改革春风吹满地,春天来了';
        console.log(str.indexOf('春'));
        console.log(str.indexOf('春', 3)); // 从索引号是 3的位置开始往后查找
    </script>

6.4 根据位置返回字符

<script>
        // 根据位置返回字符
        // 1. charAt(index) 根据位置返回字符
        var str = 'andy';
        console.log(str.charAt(3));//y
        // 遍历所有的字符
        for (var i = 0; i < str.length; i++) {
            console.log(str.charAt(i));
        }
        // 2. charCodeAt(index)  返回相应索引号的字符ASCII值 目的: 判断用户按下了那个键 
        console.log(str.charCodeAt(0)); // 97
        // 3. str[index] H5 新增的
        console.log(str[0]); // a
</script>

【案例-统计出现最大的字符和次数】

    <script>
        // 有一个对象 来判断是否有该属性 对象['属性名']
        var o = {
            age: 18
        }
        if (o['sex']) {
            console.log('里面有该属性');

        } else {
            console.log('没有该属性');

        }

        //  判断一个字符串 'abcoefoxyozzopp' 中出现次数最多的字符,并统计其次数。
        // o.a = 1
        // o.b = 1
        // o.c = 1
        // o.o = 4
        // 核心算法:利用 charAt() 遍历这个字符串
        // 把每个字符都存储给对象, 如果对象没有该属性,就为1,如果存在了就 +1
        // 遍历对象,得到最大值和该字符
        var str = 'abcoefoxyozzopp';
        //创建一个对象存放
        var o = {};
        for (var i = 0; i < str.length; i++) {
            var chars = str.charAt(i); // chars 是 字符串的每一个字符
            if (o[chars]) { // o[chars] 得到的是属性值
                o[chars]++;
            } else {
                o[chars] = 1;
            }
        }
        console.log(o);
        // 2. 遍历对象
        var max = 0;
        var ch = '';
        for (var k in o) {
            // k 得到是 属性名
            // o[k] 得到的是属性值
            if (o[k] > max) {
                max = o[k];
                //将出现次数最多的字母赋值给ch
                ch = k;
            }
        }
        console.log(max);
        console.log('最多的字符是' + ch);
    </script>

6.5 字符串操作方法

    <script>
        // 字符串操作方法
        // 1. concat('字符串1','字符串2'....)
        var str = 'andy';
        console.log(str.concat('red'));

        // 2. substr('截取的起始位置', '截取几个字符');
        var str1 = '改革春风吹满地';
        console.log(str1.substr(2, 2)); // 第一个2 是索引号的2 从第几个开始  第二个2 是取几个字符
    </script>

6.6replace()方法

 

        // 1. 替换字符 replace('被替换的字符', '替换为的字符')  它只会替换第一个字符
        var str = 'andyandy';
        console.log(str.replace('a', 'b'));
        // 有一个字符串 'abcoefoxyozzopp'  要求把里面所有的 o 替换为 *
        var str1 = 'abcoefoxyozzopp';
        while (str1.indexOf('o') !== -1) {
            str1 = str1.replace('o', '*');
        }
        console.log(str1);

6.7 split()方法

 

<script>
        // 2. 字符转换为数组 split('分隔符')    前面我们学过 join 把数组转换为字符串
        var str2 = 'red, pink, blue';
        console.log(str2.split(','));//['red','pink','blue']
        var str3 = 'red&pink&blue';
        console.log(str3.split('&'));//['red','pink','blue']
        var str4 = 'red&pink&blue';
        console.log(str3.split('*'));//['red'*'pink'*'blue']
</script>

6.8 其他

 

7.RegExp

通过 RegExp 类型支持正则表达式:let expression = /pattern/flags;

  匹配模式

 g:全局模式,表示查找字符串的全部内容,而不是找到第一个匹配的内容就结束。

 i:不区分大小写,表示在查找匹配时忽略 pattern 和字符串的大小写。

 m:多行模式,表示查找到一行文本末尾时会继续查找。

 y:粘附模式,表示只查找从 lastIndex 开始及之后的字符串。

 u:Unicode 模式,启用 Unicode 匹配。

 s:dotAll 模式,表示元字符.匹配任何字符(包括\n 或\r)。

let text = "this has been a short summer"; 
let pattern = /(.)hort/g; 
if (pattern.test(text)) { 
 console.log(RegExp.input); // this has been a short summer 
 console.log(RegExp.leftContext); // this has been a 
 console.log(RegExp.rightContext); // summer 
 console.log(RegExp.lastMatch); // short 
 console.log(RegExp.lastParen); // s 
} 

[112]

十七、JavaScript 简单类型与复杂类型、作用域与内存

1.简单类型和复杂类型

2.堆和栈

<script>
    // 简单数据类型 null  返回的是一个空的对象  object 
    var timer = null;
    console.log(typeof timer);
    // 如果有个变量我们以后打算存储为对象,暂时没想好放啥, 这个时候就给 null 
    // 1. 简单数据类型 是存放在栈里面 里面直接开辟一个空间存放的是值
    // 2. 复杂数据类型 首先在栈里面存放地址 十六进制表示  然后这个地址指向堆里面的数据
</script>

3.简单类型的内存分配

4.复杂类型的内存分配

5.简单类型传参

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

    <script>
        // 简单数据类型传参
        function fn(a) {
            a++;
            console.log(a);
        }
        var x = 10;
        fn(x);
        console.log(x);
    </script>

6.复杂类型传参

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

<script>
    // 复杂数据类型传参
    function Person(name) {
        this.name = name;
    }

    function f1(x) { // x = p
        console.log(x.name); // 2. 这个输出什么 ?  刘德华   
        x.name = "张学友";
        console.log(x.name); // 3. 这个输出什么 ?   张学友
    }
    var p = new Person("刘德华");
    console.log(p.name); // 1. 这个输出什么 ?   刘德华 
    f1(p);
    console.log(p.name); // 4. 这个输出什么 ?   张学友
</script>

7.原始值和引用值

ECMAScript 变量可以包含两种不同类型的数据:原始值和引用值。

原始值(primitive value)就是 最简单的数据

----->保存原始值的变量是按值(by value)访问的,因为我们操作的就是存储在变量中的实际值

引用值(reference value)则是由多个值构成的对象

--->引用值是保存在内存中的对象。与其他语言不同,JavaScript 不允许直接访问内存位置,因此也就 不能直接操作对象所在的内存空间。在操作对象时,实际上操作的是对该对象的引用(reference)而非 实际的对象本身。为此,保存引用值的变量是按引用(by reference)访问的。

7.1 动态属性

对于引用值而言,可以随时添加、修改和删除其属性 和方法。

let person = new Object(); 
person.name = "Nicholas"; 
console.log(person.name); // "Nicholas" 

原始值不能有属性,尽管尝试给原始值添加属性不会报错。

let name = "Nicholas"; 
name.age = 27; 
console.log(name.age); // undefined 

7.2 复制值

原始值:两个变量可以独立使用,互不干扰

在通过变量把一个原始值赋值 到另一个变量时,原始值会被复制到新变量的位置。

let num1 = 5; 
let num2 = num1; 
//这个值跟存储在num1 中的 5 是完全独立的,因为它是那个值的副本。

 引用值:两个变量实际 上指向同一个对象

在把引用值从一个变量赋给另一个变量时,存储在变量中的值也会被复制到新变量所在的位置。区 别在于,这里复制的值实际上是一个指针,它指向存储在堆内存中的对象。

let obj1 = new Object(); 
let obj2 = obj1; 
obj1.name = "Nicholas"; 
console.log(obj2.name); // "Nicholas" 

变量 obj1 保存了一个新对象的实例。然后,这个值被复制到 obj2,此时两个变 量都指向了同一个对象。在给 obj1 创建属性 name 并赋值后,通过 obj2 也可以访问这个属性,因为 它们都指向同一个对象。

7.3 传递参数:ECMAScript 中所有函数的参数都是按值传递的

 函数外的值会被复制到函数内部的参数 中,就像从一个变量复制到另一个变量一样。如果是原始值,那么就跟原始值变量的复制一样,如果是 引用值,那么就跟引用值变量的复制一样。

按值传递参数时,值会被复制到一个局部变量(即一个命名参数,或者用 ECMAScript 的话说, 就是 arguments 对象中的一个槽位)。

function addTen(num) { 
 num += 10; 
 return num; 
} 
let count = 20; 
let result = addTen(count); 
console.log(count); // 20,没有变化
console.log(result); // 30 

按引用传递参数时,值在内存中的位置会被保存在一个局部变 量,这意味着对本地变量的修改会反映到函数外部。(这在 ECMAScript 中是不可能的。)

function setName(obj) { 
 obj.name = "Nicholas"; 
} 
let person = new Object(); 
setName(person); 
console.log(person.name); // "Nicholas" 

对象并把它保存在变量 person 中。然后,这个对象被传给 setName() 方法,并被复制到参数 obj 中。在函数内部,obj 和 person 都指向同一个对象。结果就是,即使对象 是按值传进函数的,obj 也会通过引用访问对象。当函数内部给 obj 设置了 name 属性时,函数外部的 对象也会反映这个变化,因为 obj 指向的对象保存在全局作用域的堆内存上。

function setName(obj) { 
 obj.name = "Nicholas"; 
 obj = new Object(); 
 obj.name = "Greg"; 
} 
let person = new Object(); 
setName(person); 
console.log(person.name); // "Nicholas"

7.4 确定类型

如果值是对象或 null,那么 typeof  返回"object",

let s = "Nicholas"; 
let b = true; 
let i = 22; 
let u; 
let n = null; 
let o = new Object(); 
console.log(typeof s); // string 
console.log(typeof i); // number 
console.log(typeof b); // boolean 
console.log(typeof u); // undefined 
console.log(typeof n); // object 
console.log(typeof o); // object 

所有引用值都是 Object 的实例,因此通过 instanceof 操作符检测任何引用值和 Object 构造函数都会返回 true。类似地,如果用 instanceof 检测原始值,则始终会返回 false, 因为原始值不是对象

8.执行上下文与作用域

在浏览器中,全局上下文就是我们常说的 window 对象,因此所有通过 var 定 义的全局变量和函数都会成为 window 对象的属性和方法。使用 let 和 const 的顶级声明不会定义在全变 局上下文中,但在作用域链解析上效果是一样的。

var color = "blue"; 
function changeColor() { 
 let anotherColor = "red"; 
 function swapColors() { 
 let tempColor = anotherColor; 
 anotherColor = color; 
 color = tempColor; 
 // 这里可以访问 color、anotherColor 和 tempColor 
 } 
 // 这里可以访问 color 和 anotherColor,但访问不到 tempColor 
 swapColors(); 
} 
// 这里只能访问 color 
changeColor(); 

以上代码涉及 3 个上下文:全局上下文、changeColor()的局部上下文和 swapColors()的局部 上下文。全局上下文中有一个变量 color 和一个函数 changeColor()。changeColor()的局部上下文中 有一个变量 anotherColor 和一个函数 swapColors(),但在这里可以访问全局上下文中的变量 color。 swapColors()的局部上下文中有一个变量 tempColor,只能在这个上下文中访问到。全局上下文和 changeColor()的局部上下文都无法访问到 tempColor。而在 swapColors()中则可以访问另外两个 上下文中的变量,因为它们都是父上下文。

 8.1 变量声明

        1.使用var的函数作用域声明

function add(num1, num2) { 
 var sum = num1 + num2; 
 return sum; 
} 
let result = add(10, 20); // 30 
console.log(sum); // 报错:sum 在这里不是有效变量

如果变量未经声明就被初始化了, 那么它就会自动被添加到全局上下文

function add(num1, num2) { 
 sum = num1 + num2; 
 return sum; 
} 
let result = add(10, 20); // 30 
console.log(sum); // 30 

var 声明会被拿到函数或全局作用域的顶部,位于作用域中所有代码之前。这个现象叫作“提升” (hoisting)。

var name = "Jake"; 
// 等价于:
name = 'Jake'; 
var name; 
下面是两个等价的函数:
function fn1() { 
 var name = 'Jake'; 
} 
// 等价于:
function fn2() { 
 var name; 
 name = 'Jake'; 
} 

通过在声明之前打印变量,可以验证变量会被提升。声明的提升意味着会输出 undefined 而不是 Reference Error:

console.log(name); // undefined 
var name = 'Jake'; 
function() { 
 console.log(name); // undefined 
 var name = 'Jake'; 
} 

        2. 使用 let 的块级作用域声明

块 级作用域由最近的一对包含花括号{}界定。换句话说,if 块、while 块、function 块,甚至连单独 的块也是 let 声明变量的作用域

if (true) { 
 let a; 
} 
console.log(a); // ReferenceError: a 没有定义
while (true) { 
 let b; 
} 
console.log(b); // ReferenceError: b 没有定义
function foo() { 
 let c; 
} 
console.log(c); // ReferenceError: c 没有定义
 // 这没什么可奇怪的
 // var 声明也会导致报错
// 这不是对象字面量,而是一个独立的块
// JavaScript 解释器会根据其中内容识别出它来
{ 
 let d; 
} 
console.log(d); // ReferenceError: d 没有定义

let 与 var 的另一个不同之处是在同一作用域内不能声明两次。重复的 var 声明会被忽略,而重 复的 let 声明会抛出 SyntaxError。

var a; 
var a; 
// 不会报错
{ 
 let b; 
 let b; 
} 
// SyntaxError: 标识符 b 已经声明过了

let 的行为非常适合在循环中声明迭代变量。使用 var 声明的迭代变量会泄漏到循环外部,这种情 况应该避免。

for (var i = 0; i < 10; ++i) {} 
console.log(i); // 10 
for (let j = 0; j < 10; ++j) {} 
console.log(j); // ReferenceError: j 没有定义

        3. 使用 const 的常量声明

使用 const 声明的变量必须同时初始化为某个值。 一经声明,在其生命周期的任何时候都不能再重新赋予新值。

const a; // SyntaxError: 常量声明时没有初始化
const b = 3; 
console.log(b); // 3 
b = 4; // TypeError: 给常量赋值

const 除了要遵循以上规则,其他方面与 let 声明是一样的:

if (true) { 
 const a = 0; 
} 
console.log(a); // ReferenceError: a 没有定义
while (true) { 
 const b = 1; 
} 
console.log(b); // ReferenceError: b 没有定义
function foo() { 
 const c = 2; 
} 
console.log(c); // ReferenceError: c 没有定义
{ 
 const d = 3; 
} 
console.log(d); // ReferenceError: d 没有定义

const 声明只应用到顶级原语或者对象。换句话说,赋值为对象的 const 变量不能再被重新赋值 为其他引用值,但对象的键则不受限制。

const o1 = {}; 
o1 = {}; // TypeError: 给常量赋值
const o2 = {}; 
o2.name = 'Jake'; 
console.log(o2.name); // 'Jake' 

十八、Web APIs

1. Web APIs 和 JS 基础关联性

1.1 JS的组成

 1.2 JS基础阶段以及Web APIs阶段

2.API(接口)和Web API(浏览器接口)

 

2.3 API和Web API总结

 

 2.4 什么是DOM

2.5 DOM树

 

2.6 DOM对象

 

<body>
    <!-- HTML叫标签 -->
    <button>点击</button>
    <script>
      var btn=document.querySelector('button');
      console.log(typeof btn);//object
      //将按钮里面的文字改为“唐伯虎”
      btn.innerHTML='唐伯虎'
      // js btn 对象 DOM对象
    </script>
  </body>

十九、DOM元素的获取

1.根据CSS选择器来获取DOM元素(必须是字符串,也就是必须加引号;如果是类选择器前面加“.";如果是id选择器前面加"#")

        1.1 选择匹配的第一个元素--document.querySelector('CSS选择器')

         1.2 选择匹配的多个元素(得到的是一个伪数组)--document.querySelectorAll('CSS选择器')

<body>
    <div class="box">盒子1</div>
    <div class="box">盒子2</div>
    <div id="nav">
        <ul>
            <li>首页</li>
            <li>产品</li>
        </ul>
    </div>
    <script>
        // 2. querySelector 返回指定选择器的第一个元素对象  切记 里面的选择器需要加符号 .box  #nav

        //因为是类选择器所以要在类名前加“.”
        var firstBox = document.querySelector('.box');
        console.log(firstBox);

        //因为是id选择器所以前面要加“#”
        var nav = document.querySelector('#nav');
        console.log(nav);
        
        var li = document.querySelector('li');
        console.log(li);
        // 3. querySelectorAll()返回指定选择器的所有元素对象集合
        var allBox = document.querySelectorAll('.box');
        console.log(allBox);
        var lis = document.querySelectorAll('li');
        console.log(lis);
    </script>
</body>

 2.其他获取DOM元素方法

        根据id获取一个元素-->document.getElementById('nav')

<body>
    <div id="time">2019-9-9</div>
    <script>
        // 1. 因为我们文档页面从上往下加载,所以先得有标签 所以我们script写到标签的下面
        // 2. get 获得 element 元素 by 通过 驼峰命名法 
        // 3. 参数 id是大小写敏感的字符串
        // 4. 返回的是一个元素对象
        var timer = document.getElementById('time');
        console.log(timer);
        console.log(typeof timer);//object
        // 5. console.dir 打印我们返回的元素对象 更好的查看里面的属性和方法
        console.dir(timer);//div#time
    </script>
</body>

        根据标签获取一类元素 获取页面 所有div(返回的是伪数组) ---->document.getElementsByTagName('div')

<body>
    <ul>
        <li>知否知否,应是等你好久11</li>
        <li>知否知否,应是等你好久11</li>
        <li>知否知否,应是等你好久11</li>
        <li>知否知否,应是等你好久11</li>

    </ul>
    <ol id="ol">
        <li>生僻字</li>
        <li>生僻字</li>
        <li>生僻字</li>
        <li>生僻字</li>

    </ol>

    <script>
        // 1.返回的是 获取过来元素对象的集合 以伪数组的形式存储的
        var lis = document.getElementsByTagName('li');
        console.log(lis);
        console.log(lis[0]);
        // 2. 我们想要依次打印里面的元素对象我们可以采取遍历的方式
        for (var i = 0; i < lis.length; i++) {
            console.log(lis[i]);

        }
        // 3. 如果页面中只有一个li 返回的还是伪数组的形式 
        // 4. 如果页面中没有这个元素 返回的是空的伪数组的形式
        // 5. element.getElementsByTagName('标签名'); 父元素必须是指定的单个元素
        // var ol = document.getElementsByTagName('ol'); // [ol]
        // console.log(ol[0].getElementsByTagName('li'));
        var ol = document.getElementById('ol');
        console.log(ol.getElementsByTagName('li'));
    </script>
</body>

        根据类名获取元素 获取页面 所有类名为w的 --->document.getElementByClassName('w')

<body>
    <div class="box">盒子1</div>
    <div class="box">盒子2</div>
    <div id="nav">
        <ul>
            <li>首页</li>
            <li>产品</li>
        </ul>
    </div>
    <script>
        // 1. getElementsByClassName 根据类名获得某些元素集合
        var boxs = document.getElementsByClassName('box');
        console.log(boxs);
    </script>
</body>

 获取某一个元素(父元素)内部所有指定标签的子元素--element(父元素).getElementsByTagName('标签名')

<body>
    <ol>
        <li>生僻字</li>
        <li>生僻字</li>
        <li>生僻字</li>
        <li>生僻字</li>

    </ol>
    <script>
        // //错误写法
        var ol=document.getElementsByTagName('ol')//获取到的是一个伪数组[ol]
        console.log(ol.getElementsByTagName('li'));

        //正确写法
        var ol1=document.getElementsByTagName('ol')
        console.log(ol1[0].getElementsByTagName('li'));
    </script>
</body>

3. 获取HTML和Body标签

<body>
    <script>
        // 1.获取body 元素
        var bodyEle = document.body;
        console.log(bodyEle);
        console.dir(bodyEle);
        // 2.获取html 元素
        // var htmlEle = document.html;
        var htmlEle = document.documentElement;
        console.log(htmlEle);
    </script>
</body>

二十、事件基础

1. 事件基础

<body>
    <button id="btn">唐伯虎</button>
    <script>
        // 点击一个按钮,弹出对话框
        // 1. 事件是有三部分组成  事件源  事件类型  事件处理程序   我们也称为事件三要素
        //(1) 事件源 事件被触发的对象   谁  按钮
        var btn = document.getElementById('btn');
        //(2) 事件类型  如何触发 什么事件 比如鼠标点击(onclick) 还是鼠标经过 还是键盘按下
        //(3) 事件处理程序  通过一个函数赋值的方式 完成
        btn.onclick = function() {
            alert('点秋香');
        }
    </script>
</body>

2.事件三要素

1. 事件源 (谁)
2. 事件类型 (什么事件)
3. 事件处理程序 (做啥)

<body>
    <div>123</div>
    <script>
        // 执行事件步骤
        // 点击div 控制台输出 我被选中了
        // 1. 获取事件源
        var div = document.querySelector('div');
        // 2.绑定事件 注册事件
        // div.onclick 
        // 3.添加事件处理程序 
        div.onclick = function() {
            console.log('我被选中了');

        }
    </script>
</body>

【案例: 点击按钮弹出警示框】

var btn = document.getElementById('btn');
btn.onclick = function() {
alert('你好吗');
};

3.执行事件的步骤

1. 获取事件源
2. 注册事件(绑定事件)
3. 添加事件处理程序(采取函数赋值形式)

<style>
    div,
    p {
        width: 300px;
        height: 30px;
        line-height: 30px;
        color: #fff;
        background-color: pink;
    }
</style>
</head>

<body>
<button>显示当前系统时间</button>
<div>某个时间</div>
<p>1123</p>
<script>
    // 当我们点击了按钮,  div里面的文字会发生变化
    // 1. 获取元素 
    var btn = document.querySelector('button');
    var div = document.querySelector('div');
    // 2.注册事件
    btn.onclick = function() {
        // div.innerText = '2019-6-6';
        div.innerHTML = getDate();
    }

    function getDate() {
        var date = new Date();
        // 我们写一个 2019年 5月 1日 星期三
        var year = date.getFullYear();
        var month = date.getMonth() + 1;
        var dates = date.getDate();
        var arr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
        var day = date.getDay();
        return '今天是:' + year + '年' + month + '月' + dates + '日 ' + arr[day];
    }
    // 我们元素可以不用添加事件  打开页面就自动显示时间
    var p = document.querySelector('p');
    p.innerHTML = getDate();
</script>
</body>

4.常见的鼠标事件

  

二十一、设置/修改DOM元素内容

1. document.write()方法

  1. 只能将文本内容追加到</body>前面的位置
  2. 文本中包含的标签会被解析
//永远都只能追加操作,且只能位置</body>前
document.write('Hello World')
document.write('<h3>你好</h3>')

2.对象.innerText属性

  1. 将文本内容添加/更新到任意标签位置
  2. 文本中包含的标签不会被解析
  3. innerText 不识别html标签 非标准 去除空格和换行
    <script>
        //innerText将文本内容添加/更新到任意标签位置
        let info=document.getElementById('info');
        //intro.innerText='嗨'
        info.innerText='<h4>嗨</h4>'
    </script>

3.对象.innerHTML属性

  1.   将文本内容添加/更新到任意标签位置
  2. 文本中包含的标签会被解析
  3. innerHTML 识别html标签 W3C标准 保留空格和换行的
//innerHTML属性
box.innerHTML='<h3>前端程序员</h3>'

4.三种方法的总结和区别

<body>
    <div></div>
    <p>
        我是文字
        <span>123</span>
    </p>
    <script>
        // innerText 和 innerHTML的区别 
        // 1. innerText 不识别html标签 非标准  去除空格和换行
        var div = document.querySelector('div');
        div.innerText = '<strong>今天是:</strong> 2019';
        //输出---<strong>今天是:</strong> 2019

        // 2. innerHTML 识别html标签 W3C标准 保留空格和换行的
        div.innerHTML = '<strong>今天是:</strong> 2019';
        //输出---今天是: 2019

        // 这两个属性是可读写的  可以获取元素里面的内容
        var p = document.querySelector('p');
        console.log(p.innerText);
        //输出---我是文字 123
        console.log(p.innerHTML);
        //输出---我是文字   <span>123</span>
    </script>
</body>

5.常见元素的属性操作

<body>
    <button id="ldh">刘德华</button>
    <button id="zxy">张学友</button> <br>
    <img src="images/ldh.jpg" alt="" title="刘德华">

    <script>
        // 修改元素属性  src
        // 1. 获取元素
        var ldh = document.getElementById('ldh');
        var zxy = document.getElementById('zxy');
        var img = document.querySelector('img');
        // 2. 注册事件  处理程序
        zxy.onclick = function() {
            img.src = 'images/zxy.jpg';
            //当鼠标在图片上的时候,显示的文字
            img.title = '张学友思密达';
        }
        ldh.onclick = function() {
            img.src = 'images/ldh.jpg';
            img.title = '刘德华';
        }
    </script>
</body>

 6.案例

 【分时显示不同图片,显示不同问候语】

    <style>
        img {
            width: 300px;
        }
    </style>
</head>

<body>
    <img src="images/s.gif" alt="">
    <div>上午好</div>
    <script>
        // 根据系统不同时间来判断,所以需要用到日期内置对象
        // 利用多分支语句来设置不同的图片
        // 需要一个图片,并且根据时间修改图片,就需要用到操作元素src属性
        // 需要一个div元素,显示不同问候语,修改元素内容即可
        // 1.获取元素
        var img = document.querySelector('img');
        var div = document.querySelector('div');
        // 2. 得到当前的小时数
        var date = new Date();
        var h = date.getHours();
        // 3. 判断小时数改变图片和文字信息
        if (h < 12) {
            img.src = 'images/s.gif';
            div.innerHTML = '亲,上午好,好好写代码';
        } else if (h < 18) {
            img.src = 'images/x.gif';
            div.innerHTML = '亲,下午好,好好写代码';
        } else {
            img.src = 'images/w.gif';
            div.innerHTML = '亲,晚上好,好好写代码';

        }
    </script>
</body>

 7.表单元素的属性操作

<body>
    <button>按钮</button>
    <input type="text" value="输入内容">
    <script>
        // 1. 获取元素
        var btn = document.querySelector('button');
        var input = document.querySelector('input');
        // 2. 注册事件 处理程序
        btn.onclick = function() {
            // input.innerHTML = '点击了';  这个是 普通盒子 比如 div 标签里面的内容
            // 表单里面的值 文字内容是通过 value 来修改的
            input.value = '被点击了';
            // 如果想要某个表单被禁用 不能再点击 disabled  我们想要这个按钮 button禁用
            // btn.disabled = true;
            this.disabled = true;
            // this 指向的是事件函数的调用者 btn
        }
    </script>
</body>

【案例-- 仿京东显示密码】

    <style>
        .box {
            position: relative;
            width: 400px;
            border-bottom: 1px solid #ccc;
            margin: 100px auto;
        }
        
        .box input {
            width: 370px;
            height: 30px;
            border: 0;
            outline: none;
        }
        
        .box img {
            position: absolute;
            top: 2px;
            right: 2px;
            width: 24px;
        }
    </style>
</head>

<body>
    <div class="box">
        <label for="">
            <img src="images/close.png" alt="" id="eye">
        </label>
        <input type="password" name="" id="pwd">
    </div>
    <script>
        // 1. 获取元素
        var eye = document.getElementById('eye');
        var pwd = document.getElementById('pwd');
        // 2. 注册事件 处理程序
        var flag = 0;
        eye.onclick = function() {
            // 点击一次之后, flag 一定要变化
            if (flag == 0) {
                //将原来的密码框修改为文本框
                pwd.type = 'text';
                eye.src = 'images/open.png';
                flag = 1; // 赋值操作
            } else {
                pwd.type = 'password';
                eye.src = 'images/close.png';
                flag = 0;
            }

        }
    </script>
</body>

  8. 样式属性操作

我们可以通过 JS 修改元素的大小、颜色、位置等样式。

  • element.style---行内样式操作
  •     <style>
            div {
                width: 200px;
                height: 200px;
                background-color: pink;
            }
        </style>
    </head>
    
    <body>
        <div></div>
        <script>
            // 1. 获取元素
            var div = document.querySelector('div');
            // 2. 注册事件 处理程序
            div.onclick = function() {
                // div.style里面的属性 采取驼峰命名法 
                this.style.backgroundColor = 'purple';
                this.style.width = '250px';
            }
        </script>
    </body>
  • element.className---类名样式操作 
  1. 1.如果样式修改较多,可以采用操作类名方式更改元素样式
  2. 2.class因为是个保留字,因此使用className来操作元素类名属性
  3. className直接更改元素的类名,会覆盖原来的类名
  4. 如果想要保留原先的类名,我们可以这么做 多类名选择器(原来的类名 新的类名)
    <style>
        div {
            width: 100px;
            height: 100px;
            background-color: pink;
        }
        
        .change {
            background-color: purple;
            color: #fff;
            font-size: 25px;
            margin-top: 100px;
        }
    </style>
</head>


<body>
    <div class="first">文本</div>
    <script>
        // 1. 使用 element.style 获得修改元素样式  如果样式比较少 或者 功能简单的情况下使用
        var test = document.querySelector('div');
        test.onclick = function() {
            // this.style.backgroundColor = 'purple';
            // this.style.color = '#fff';
            // this.style.fontSize = '25px';
            // this.style.marginTop = '100px';

            // 2. 我们可以通过 修改元素的className更改元素的样式 适合于样式较多或者功能复杂的情况
            // 3. 如果想要保留原先的类名,我们可以这么做 多类名选择器
            // 让我们当前元素的类名改为了 change
            // this.className = 'change';
            this.className = 'first change';
        }
    </script>
</body>

【 案例: 淘宝点击关闭二维码】

    <style>
        .box {
            position: relative;
            width: 74px;
            height: 88px;
            border: 1px solid #ccc;
            margin: 100px auto;
            font-size: 12px;
            text-align: center;
            color: #f40;
            /* display: block; */
        }
        
        .box img {
            width: 60px;
            margin-top: 5px;
        }
        
        .close-btn {
            position: absolute;
            top: -1px;
            left: -16px;
            width: 14px;
            height: 14px;
            border: 1px solid #ccc;
            line-height: 14px;
            font-family: Arial, Helvetica, sans-serif;
            cursor: pointer;
        }
    </style>
</head>

<body>
    <div class="box">
        淘宝二维码
        <img src="images/tao.png" alt="">
        <i class="close-btn">×</i>
    </div>
    <script>
        // 1. 获取元素 
        var btn = document.querySelector('.close-btn');
        var box = document.querySelector('.box');
        // 2.注册事件 程序处理
        btn.onclick = function() {
            box.style.display = 'none';
        }
    </script>
</body>

 【案例: 循环精灵图背景】

    <style>
        * {
            margin: 0;
            padding: 0;
        }
        
        li {
            list-style-type: none;
        }
        
        .box {
            width: 250px;
            margin: 100px auto;
        }
        
        .box li {
            float: left;
            width: 24px;
            height: 24px;
            background-color: pink;
            margin: 15px;
            background: url(images/sprite.png) no-repeat;
        }
    </style>
</head>

<body>
    <div class="box">
        <ul>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
        </ul>
    </div>
    <script>
        // 1. 获取元素 所有的小li 
        var lis = document.querySelectorAll('li');
        for (var i = 0; i < lis.length; i++) {
            // 让索引号 乘以 44 就是每个li 的背景y坐标  index就是我们的y坐标
            var index = i * 44;
            lis[i].style.backgroundPosition = '0 -' + index + 'px';
        }
    </script>
</body>

 【案例:显示隐藏文本框内容】

    <input type="text" value="手机">
    <script>
        // 1.获取元素
        var text = document.querySelector('input');
        // 2.注册事件 获得焦点事件 onfocus 
        text.onfocus = function() {
                // console.log('得到了焦点');
                if (this.value === '手机') {
                    this.value = '';
                }
                // 获得焦点需要把文本框里面的文字颜色变黑
                this.style.color = '#333';
            }
            // 3. 注册事件 失去焦点事件 onblur
        text.onblur = function() {
            // console.log('失去了焦点');
            if (this.value === '') {
                this.value = '手机';
            }
            // 失去焦点需要把文本框里面的文字颜色变浅色
            this.style.color = '#999';
        }
    </script>

</body>

【案例: 密码框格式提示错误信息】

<head>   
 <style>
        div {
            width: 600px;
            margin: 100px auto;
        }
        
        .message {
            display: inline-block;
            font-size: 12px;
            color: #999;
            background: url(images/mess.png) no-repeat left center;
            padding-left: 20px;
        }
        
        .wrong {
            color: red;
            background-image: url(images/wrong.png);
        }
        
        .right {
            color: green;
            background-image: url(images/right.png);
        }
    </style>
</head>

<body>
    <div class="register">
        <input type="password" class="ipt">
        <p class="message">请输入6~16位密码</p>
    </div>
    <script>
        // 首先判断的事件是表单失去焦点 onblur
        // 如果输入正确则提示正确的信息颜色为绿色小图标变化
        // 如果输入不是6到16位,则提示错误信息颜色为红色 小图标变化
        // 因为里面变化样式较多,我们采取className修改样式
        // 1.获取元素
        var ipt = document.querySelector('.ipt');
        var message = document.querySelector('.message');
        //2. 注册事件 失去焦点
        ipt.onblur = function() {
            // 根据表单里面值的长度 ipt.value.length
            if (this.value.length < 6 || this.value.length > 16) {
                // console.log('错误');
                //注意此类名必须要message wrong 如果就只有wrong就错误了
                message.className = 'message wrong';
                message.innerHTML = '您输入的位数不对要求6~16位';
            } else {
                message.className = 'message right';
                message.innerHTML = '您输入的正确';
            }
        }
    </script>
</body>

 9.操作元素总结

10.排他思想

 

<body>
    <button>按钮1</button>
    <button>按钮2</button>
    <button>按钮3</button>
    <button>按钮4</button>
    <button>按钮5</button>
    <script>
        // 1. 获取所有按钮元素
        var btns = document.getElementsByTagName('button');
        // btns得到的是伪数组  里面的每一个元素 btns[i]
        for (var i = 0; i < btns.length; i++) {
            btns[i].onclick = function() {
                // (1) 我们先把所有的按钮背景颜色去掉  干掉所有人
                for (var i = 0; i < btns.length; i++) {
                    //将背景颜色清空
                    btns[i].style.backgroundColor = '';
                }
                // (2) 然后才让当前的元素背景颜色为pink 留下我自己
                this.style.backgroundColor = 'pink';

            }
        }
        //2. 首先先排除其他人,然后才设置自己的样式 这种排除其他人的思想我们成为排他思想
    </script>
</body>

 【案例:百度换肤】

<head>  
  <style>
        * {
            margin: 0;
            padding: 0;
        }
        
        body {
            background: url(images/1.jpg) no-repeat center top;
        }
        
        li {
            list-style: none;
        }
        
        .baidu {
            overflow: hidden;
            margin: 100px auto;
            background-color: #fff;
            width: 410px;
            padding-top: 3px;
        }
        
        .baidu li {
            float: left;
            margin: 0 1px;
            cursor: pointer;
        }
        
        .baidu img {
            width: 100px;
        }
    </style>
</head>

<body>
    <ul class="baidu">
        <li><img src="images/1.jpg"></li>
        <li><img src="images/2.jpg"></li>
        <li><img src="images/3.jpg"></li>
        <li><img src="images/4.jpg"></li>
    </ul>
    <script>
        // 1. 获取元素 
        var imgs = document.querySelector('.baidu').querySelectorAll('img');
        // console.log(imgs);
        // 2. 循环注册事件 
        for (var i = 0; i < imgs.length; i++) {
            imgs[i].onclick = function() {
                // this.src 就是我们点击图片的路径   images/2.jpg
                // console.log(this.src);
                // 把这个路径 this.src 给body 就可以了
                document.body.style.backgroundImage = 'url(' + this.src + ')';
            }
        }
    </script>
</body>

 【案例:表格隔行变色】

    <style>
        table {
            width: 800px;
            margin: 100px auto;
            text-align: center;
            border-collapse: collapse;
            font-size: 14px;
        }
        
        thead tr {
            height: 30px;
            background-color: skyblue;
        }
        
        tbody tr {
            height: 30px;
        }
        
        tbody td {
            border-bottom: 1px solid #d7d7d7;
            font-size: 12px;
            color: blue;
        }
        
        .bg {
            background-color: pink;
        }
    </style>
</head>

<body>
    <table>
        <thead>
            <tr>
                <th>代码</th>
                <th>名称</th>
                <th>最新公布净值</th>
                <th>累计净值</th>
                <th>前单位净值</th>
                <th>净值增长率</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>003526</td>
                <td>农银金穗3个月定期开放债券</td>
                <td>1.075</td>
                <td>1.079</td>
                <td>1.074</td>
                <td>+0.047%</td>
            </tr>
            <tr>
                <td>003526</td>
                <td>农银金穗3个月定期开放债券</td>
                <td>1.075</td>
                <td>1.079</td>
                <td>1.074</td>
                <td>+0.047%</td>
            </tr>
            <tr>
                <td>003526</td>
                <td>农银金穗3个月定期开放债券</td>
                <td>1.075</td>
                <td>1.079</td>
                <td>1.074</td>
                <td>+0.047%</td>
            </tr>
            <tr>
                <td>003526</td>
                <td>农银金穗3个月定期开放债券</td>
                <td>1.075</td>
                <td>1.079</td>
                <td>1.074</td>
                <td>+0.047%</td>
            </tr>
            <tr>
                <td>003526</td>
                <td>农银金穗3个月定期开放债券</td>
                <td>1.075</td>
                <td>1.079</td>
                <td>1.074</td>
                <td>+0.047%</td>
            </tr>
            <tr>
                <td>003526</td>
                <td>农银金穗3个月定期开放债券</td>
                <td>1.075</td>
                <td>1.079</td>
                <td>1.074</td>
                <td>+0.047%</td>
            </tr>
        </tbody>
    </table>
    <script>
        // 1.获取元素 获取的是 tbody 里面所有的行
        var trs = document.querySelector('tbody').querySelectorAll('tr');
        // 2. 利用循环绑定注册事件
        for (var i = 0; i < trs.length; i++) {
            // 3. 鼠标经过事件 onmouseover
            trs[i].onmouseover = function() {
                    // console.log(11);
                    this.className = 'bg';
                }
                // 4. 鼠标离开事件 onmouseout
            trs[i].onmouseout = function() {
                this.className = '';
            }
        }
    </script>
</body>

 【案例:表单全选取消全选案例】

<head> 
   <style>
        * {
            padding: 0;
            margin: 0;
        }
        
        .wrap {
            width: 300px;
            margin: 100px auto 0;
        }
        
        table {
            border-collapse: collapse;
            border-spacing: 0;
            border: 1px solid #c0c0c0;
            width: 300px;
        }
        
        th,
        td {
            border: 1px solid #d0d0d0;
            color: #404060;
            padding: 10px;
        }
        
        th {
            background-color: #09c;
            font: bold 16px "微软雅黑";
            color: #fff;
        }
        
        td {
            font: 14px "微软雅黑";
        }
        
        tbody tr {
            background-color: #f0f0f0;
        }
        
        tbody tr:hover {
            cursor: pointer;
            background-color: #fafafa;
        }
    </style>

</head>

<body>
    <div class="wrap">
        <table>
            <thead>
                <tr>
                    <th>
                        <input type="checkbox" id="j_cbAll" />
                    </th>
                    <th>商品</th>
                    <th>价钱</th>
                </tr>
            </thead>
            <tbody id="j_tb">
                <tr>
                    <td>
                        <input type="checkbox" />
                    </td>
                    <td>iPhone8</td>
                    <td>8000</td>
                </tr>
                <tr>
                    <td>
                        <input type="checkbox" />
                    </td>
                    <td>iPad Pro</td>
                    <td>5000</td>
                </tr>
                <tr>
                    <td>
                        <input type="checkbox" />
                    </td>
                    <td>iPad Air</td>
                    <td>2000</td>
                </tr>
                <tr>
                    <td>
                        <input type="checkbox" />
                    </td>
                    <td>Apple Watch</td>
                    <td>2000</td>
                </tr>

            </tbody>
        </table>
    </div>
    <script>
        // 1. 全选和取消全选做法:  让下面所有复选框的checked属性(选中状态) 跟随 全选按钮即可
        // 获取元素
        var j_cbAll = document.getElementById('j_cbAll'); // 全选按钮
        var j_tbs = document.getElementById('j_tb').getElementsByTagName('input'); // 下面所有的复选框
        // 注册事件
        j_cbAll.onclick = function() {
                // this.checked 它可以得到当前复选框的选中状态如果是true 就是选中,如果是false 就是未选中
                console.log(this.checked);
                //全选和取消全选
                for (var i = 0; i < j_tbs.length; i++) {
                    j_tbs[i].checked = this.checked;
                }
            }
            // 2. 下面复选框需要全部选中, 上面全选才能选中做法: 给下面所有复选框绑定点击事件,每次点击,都要循环查看下面所有的复选框是否有没选中的,如果有一个没选中的, 上面全选就不选中。
        for (var i = 0; i < j_tbs.length; i++) {
            j_tbs[i].onclick = function() {
                // flag 控制全选按钮是否选中
                var flag = true;
                // 每次点击下面的复选框都要循环检查者4个小按钮是否全被选中
                for (var i = 0; i < j_tbs.length; i++) {
                    //当有一个未被选中的时候,则进入if
                    if (!j_tbs[i].checked) {
                        flag = false;
                        break; // 退出for循环 这样可以提高执行效率 因为只要有一个没有选中,剩下的就无需循环判断了
                    }
                }
                j_cbAll.checked = flag;
            }
        }
    </script>
</body>

 11.自定义属性的操作

1. 获取属性值

  1. element.属性
  2. element.getAttribute('自定义属性')--最主要用于获取自定义属性

<body>
    <div id="demo" index="1"></div>
    <script>
        var div = document.querySelector('div');
        // 1. 获取元素的属性值
        // (1) element.属性
        console.log(div.id);
        //(2) element.getAttribute('属性')  get得到获取 attribute 属性的意思 我们程序员自己添加的属性我们称为自定义属性 index
        console.log(div.getAttribute('id'));
        console.log(div.getAttribute('index'));
    </script>
</body>

2.设置属性值

  1.  element.属性='值'
  2. element.setAttribute('属性‘,’值')--常用于自定义

<body>
    <div id="demo" index="1" class="nav"></div>
    <script>
        // 2. 设置元素属性值
        // (1) element.属性= '值'
        div.id = 'test';
        div.className = 'navs';
        // (2) element.setAttribute('属性', '值');  主要针对于自定义属性
        div.setAttribute('index', 2);
        div.setAttribute('class', 'footer'); // class 特殊  这里面写的就是class 不是className
    </script>
</body>

3.移除属性

  1. element.removeAttrubute('属性')

 

<body>
    <div id="demo" index="1" class="nav"></div>
    <script>
            // 3 移除属性 removeAttribute(属性)    
            div.removeAttribute('index');
    </script>
</body>

【 案例:tab 栏切换(重点案例)】

    <style>
        * {
            margin: 0;
            padding: 0;
        }
        
        li {
            list-style-type: none;
        }
        
        .tab {
            width: 978px;
            margin: 100px auto;
        }
        
        .tab_list {
            height: 39px;
            border: 1px solid #ccc;
            background-color: #f1f1f1;
        }
        
        .tab_list li {
            float: left;
            height: 39px;
            line-height: 39px;
            padding: 0 20px;
            text-align: center;
            cursor: pointer;
        }
        
        .tab_list .current {
            background-color: #c81623;
            color: #fff;
        }
        
        .item_info {
            padding: 20px 0 0 20px;
        }
        
        .item {
            display: none;
        }
    </style>
</head>

<body>
    <div class="tab">
        <div class="tab_list">
            <ul>
                <li class="current">商品介绍</li>
                <li>规格与包装</li>
                <li>售后保障</li>
                <li>商品评价(50000)</li>
                <li>手机社区</li>
            </ul>
        </div>
        <div class="tab_con">
            <div class="item" style="display: block;">
                商品介绍模块内容
            </div>
            <div class="item">
                规格与包装模块内容
            </div>
            <div class="item">
                售后保障模块内容
            </div>
            <div class="item">
                商品评价(50000)模块内容
            </div>
            <div class="item">
                手机社区模块内容
            </div>

        </div>
    </div>
    <script>
        // 获取元素
        var tab_list = document.querySelector('.tab_list');
        var lis = tab_list.querySelectorAll('li');
        var items = document.querySelectorAll('.item');
        // for循环绑定点击事件
        for (var i = 0; i < lis.length; i++) {
            // 开始给5个小li 设置索引号 
            lis[i].setAttribute('index', i);
            lis[i].onclick = function() {
                // 1. 上的模块选项卡,点击某一个,当前这一个底色会是红色,其余不变(排他思想) 修改类名的方式

                // 干掉所有人 其余的li清除 class 这个类
                for (var i = 0; i < lis.length; i++) {
                    //将其他Li去除这个类名
                    lis[i].className = '';
                }
                // 留下我自己 
                this.className = 'current';


                // 2. 下面的显示内容模块
                var index = this.getAttribute('index');
                console.log(index);
                // 干掉所有人 让其余的item 这些div 隐藏
                for (var i = 0; i < items.length; i++) {
                    items[i].style.display = 'none';
                }
                // 留下我自己 让对应的item 显示出来
                items[index].style.display = 'block';
            }
        }
    </script>
</body>

 12.H5自定义属性--只能通过getAttriubute

12.1 设置H5自定义属性----data-index="2"/div.setAttribute('data-time', 20)

<body>
    <div getTime="20" data-index="2" data-list-name="andy"></div>
    <script>
        var div = document.querySelector('div');
        // console.log(div.getTime);
        console.log(div.getAttribute('getTime'));
        div.setAttribute('data-time', 20);
        console.log(div.getAttribute('data-index'));
        console.log(div.getAttribute('data-list-name'));
    </script>
</body>

12.2 获取H5自定义属性 ---element.getAttrubute('data-index')/element.dataset.index/element.dataset['index']

  • h5新增的获取自定义属性的方法 它只能获取data-开头的
  • dataset 是一个集合里面存放了所有以data开头的自定义属性
  • 如果自定义属性里面有多个-链接的单词,我们获取的时候采取 驼峰命名法

<body>
    <div getTime="20" data-index="2" data-list-name="andy"></div>
    <script>
        // h5新增的获取自定义属性的方法 它只能获取data-开头的
        // dataset 是一个集合里面存放了所有以data开头的自定义属性
        console.log(div.dataset);
        console.log(div.dataset.index);
        console.log(div.dataset['index']);
        // 如果自定义属性里面有多个-链接的单词,我们获取的时候采取 驼峰命名法
        console.log(div.dataset.listName);
        console.log(div.dataset['listName']);
    </script>
</body>

二十二、节点操作

1.为什么学节点操作

2.节点概述

 

<body>
    <!-- 节点的优点 -->
    <div>我是div</div>
    <span>我是span</span>
    <ul>
        <li>我是li</li>
        <li>我是li</li>
        <li>我是li</li>
        <li>我是li</li>
    </ul>
    <div class="box">
        <span class="erweima">×</span>
    </div>
    <script>
        var box = document.querySelector('.box');
        console.dir(box);
    </script>
</body>

3.节点层级

 

3.1 父级节点--node.parentNode

 

<body>
    <!-- 节点的优点 -->
    <div>我是div</div>
    <span>我是span</span>
    <ul>
        <li>我是li</li>
        <li>我是li</li>
        <li>我是li</li>
        <li>我是li</li>
    </ul>
    <div class="demo">
        <div class="box">
            <span class="erweima">×</span>
        </div>
    </div>

    <script>
        // 1. 父节点 parentNode
        var erweima = document.querySelector('.erweima');
        // var box = document.querySelector('.box');
        // 得到的是离元素最近的父级节点(亲爸爸) 如果找不到父节点就返回为 null
        console.log(erweima.parentNode);//返回的是(box)
    </script>
</body>

3.2 子节点(总结:无论用什么都是“父节点.children【】”)

        (1)parentNode.childNodes(标准)--一般不用这个(因为包括其他元素)

  • 注意点:
  • 返回值包含了所有的子节点,包括元素节点,文本节点等

         (2)parentNode.children(非标准)--主要用这个(因为直接获得相要的元素)

<body>
    <!-- 节点的优点 -->
    <div>我是div</div>
    <span>我是span</span>
    <ul>
        <li>我是li</li>
        <li>我是li</li>
        <li>我是li</li>
        <li>我是li</li>

    </ul>
    <ol>
        <li>我是li</li>
        <li>我是li</li>
        <li>我是li</li>
        <li>我是li</li>
    </ol>

    <div class="demo">
        <div class="box">
            <span class="erweima">×</span>
        </div>
    </div>

    <script>
        // DOM 提供的方法(API)获取
        var ul = document.querySelector('ul');
        var lis = ul.querySelectorAll('li');
        // 1. 子节点  childNodes 所有的子节点 包含 元素节点 文本节点等等(包括换行等)
        console.log(ul.childNodes);
        console.log(ul.childNodes[0].nodeType);
        console.log(ul.childNodes[1].nodeType);
        // 2. children 获取所有的子元素节点 也是我们实际开发常用的
        console.log(ul.children);
    </script>
</body>

         (3)parentNode.firstChild--一般不用这个(因为包括其他元素)

  firstChild 返回第一个子节点,找不到则返回null。同样,也是包含所有的节点。

        (4)parentNode.lastChild--一般不用这个(因为包括其他元素)

lastChild 返回最后一个子节点,找不到则返回null。同样,也是包含所有的节点。

        (5). parentNode.firstElementChild--一般不用(因为i9才支持)
firstElementChild 返回第一个子元素节点,找不到则返回null。


        (6). parentNode.lastElementChild--一般不用(因为i9才支持)
lastElementChild 返回最后一个子元素节点,找不到则返回null。

    (7) 实际开发中, 既没有兼容性问题又返回第一个子元素
 

        console.log(ol.children[0]);
        console.log(ol.children[ol.children.length - 1]);

<body>
    <ol>
        <li>我是li1</li>
        <li>我是li2</li>
        <li>我是li3</li>
        <li>我是li4</li>
        <li>我是li5</li>
    </ol>
    <script>
        var ol = document.querySelector('ol');
        // 1. firstChild 第一个子节点 不管是文本节点还是元素节点
        console.log(ol.firstChild);
        console.log(ol.lastChild);
        // 2. firstElementChild 返回第一个子元素节点 ie9才支持
        console.log(ol.firstElementChild);
        console.log(ol.lastElementChild);
        // 3. 实际开发的写法  既没有兼容性问题又返回第一个子元素
        console.log(ol.children[0]);
        console.log(ol.children[ol.children.length - 1]);
    </script>
</body>

【案例:下拉菜单】

 

<head> 
   <style>
        * {
            margin: 0;
            padding: 0;
        }
        
        li {
            list-style-type: none;
        }
        
        a {
            text-decoration: none;
            font-size: 14px;
        }
        
        .nav {
            margin: 100px;
        }
        
        .nav>li {
            position: relative;
            float: left;
            width: 80px;
            height: 41px;
            text-align: center;
        }
        
        .nav li a {
            display: block;
            width: 100%;
            height: 100%;
            line-height: 41px;
            color: #333;
        }
        
        .nav>li>a:hover {
            background-color: #eee;
        }
        
        .nav ul {
            display: none;
            position: absolute;
            top: 41px;
            left: 0;
            width: 100%;
            border-left: 1px solid #FECC5B;
            border-right: 1px solid #FECC5B;
        }
        
        .nav ul li {
            border-bottom: 1px solid #FECC5B;
        }
        
        .nav ul li a:hover {
            background-color: #FFF5DA;
        }
    </style>
</head>

<body>
    <ul class="nav">
        <li>
            <a href="#">微博</a>
            <ul>
                <li>
                    <a href="">私信</a>
                </li>
                <li>
                    <a href="">评论</a>
                </li>
                <li>
                    <a href="">@我</a>
                </li>
            </ul>
        </li>
        <li>
            <a href="#">微博</a>
            <ul>
                <li>
                    <a href="">私信</a>
                </li>
                <li>
                    <a href="">评论</a>
                </li>
                <li>
                    <a href="">@我</a>
                </li>
            </ul>
        </li>
        <li>
            <a href="#">微博</a>
            <ul>
                <li>
                    <a href="">私信</a>
                </li>
                <li>
                    <a href="">评论</a>
                </li>
                <li>
                    <a href="">@我</a>
                </li>
            </ul>
        </li>
        <li>
            <a href="#">微博</a>
            <ul>
                <li>
                    <a href="">私信</a>
                </li>
                <li>
                    <a href="">评论</a>
                </li>
                <li>
                    <a href="">@我</a>
                </li>
            </ul>
        </li>
    </ul>
    <script>
        // 1. 获取元素
        var nav = document.querySelector('.nav');
        var lis = nav.children; // 得到4个小li
        // 2.循环注册事件
        for (var i = 0; i < lis.length; i++) {
            lis[i].onmouseover = function() {
                this.children[1].style.display = 'block';
            }
            lis[i].onmouseout = function() {
                this.children[1].style.display = 'none';
            }
        }
    </script>
</body>

3.3 兄弟节点

1. node.nextSibling--一般不用这个(因为包括其他元素)
nextSibling 返回当前元素的下一个兄弟元素节点,找不到则返回null。同样,也是包含所有的节点。
2. node.previousSibling--一般不用这个(因为包括其他元素)
previousSibling 返回当前元素上一个兄弟元素节点,找不到则返回null。同样,也是包含所有的节点

3. node.nextElementSibling--主要用这个(因为直接获得相要的元素)
nextElementSibling 返回当前元素下一个兄弟元素节点,找不到则返回null。
4. node.previousElementSibling--主要用这个(因为直接获得相要的元素)
previousElementSibling 返回当前元素上一个兄弟节点,找不到则返回null

<body>
    <div>我是div</div>
    <span>我是span</span>
    <script>
        var div = document.querySelector('div');
        // 1.nextSibling 下一个兄弟节点 包含元素节点或者 文本节点等等
        console.log(div.nextSibling);
        console.log(div.previousSibling);
        // 2. nextElementSibling 得到下一个兄弟元素节点
        console.log(div.nextElementSibling);
        console.log(div.previousElementSibling);
    </script>
</body>

  3.4 创建节点

document.createElement('tagName')
document.createElement() 方法创建由 tagName 指定的 HTML 元素。因为这些元素原先不存在,
是根据我们的需求动态生成的,所以我们也称为动态创建元素节点。

4.4 添加节点

1. node(父亲).appendChild(child)
node.appendChild() 方法将一个节点添加到指定父节点的子节点列表末尾。类似于 CSS 里面的
after 伪元素。
2. node.insertBefore(child, 指定元素)
node.insertBefore() 方法将一个节点添加到父节点的指定子节点前面。类似于 CSS 里面的 before
伪元素

<body>
    <ul>
        <li>123</li>
    </ul>
    <script>
        // 1. 创建节点元素节点
        var li = document.createElement('li');
        // 2. 添加节点 node.appendChild(child)  node 父级  child 是子级 后面追加元素  类似于数组中的push
        var ul = document.querySelector('ul');
        ul.appendChild(li);
        // 3. 添加节点 node.insertBefore(child, 指定元素);
        var lili = document.createElement('li');
        //放在第一个元素的前面
        ul.insertBefore(lili, ul.children[0]);
        // 4. 我们想要页面添加一个新的元素 : 1. 创建元素 2. 添加元素
    </script>
</body>

【案例:简单版发布留言案例】

    <style>
        * {
            margin: 0;
            padding: 0;
        }
        
        body {
            padding: 100px;
        }
        
        textarea {
            width: 200px;
            height: 100px;
            border: 1px solid pink;
            outline: none;
            resize: none;
        }
        
        ul {
            margin-top: 50px;
        }
        
        li {
            width: 300px;
            padding: 5px;
            background-color: rgb(245, 209, 243);
            color: red;
            font-size: 14px;
            margin: 15px 0;
        }
    </style>
</head>

<body>
    <textarea name="" id=""></textarea>
    <button>发布</button>
    <ul>

    </ul>
    <script>
        // 1. 获取元素
        var btn = document.querySelector('button');
        var text = document.querySelector('textarea');
        var ul = document.querySelector('ul');
        // 2. 注册事件
        btn.onclick = function() {
            if (text.value == '') {
                alert('您没有输入内容');
                return false;
            } else {
                // console.log(text.value);
                // (1) 创建元素
                var li = document.createElement('li');
                // 先有li 才能赋值
                li.innerHTML = text.value;
                // (2) 添加元素
                // ul.appendChild(li);
                //放在第一个位置
                ul.insertBefore(li, ul.children[0]);
            }
        }
    </script>
</body>

4.5 删除节点

node.removeChild(child)
node.removeChild() 方法从 DOM 中删除一个子节点,返回删除的节点。

<body>
    <button>删除</button>
    <ul>
        <li>熊大</li>
        <li>熊二</li>
        <li>光头强</li>
    </ul>
    <script>
        // 1.获取元素
        var ul = document.querySelector('ul');
        var btn = document.querySelector('button');
        // 2. 删除元素  node.removeChild(child)
        // ul.removeChild(ul.children[0]);
        // 3. 点击按钮依次删除里面的孩子
        btn.onclick = function() {
            if (ul.children.length == 0) {
                this.disabled = true;
            } else {
                ul.removeChild(ul.children[0]);
            }
        }
    </script>
</body>

【案例:删除留言案例】

阻止链接跳转:<a href='javascript:;'>删除</a>

    <style>
        * {
            margin: 0;
            padding: 0;
        }
        
        body {
            padding: 100px;
        }
        
        textarea {
            width: 200px;
            height: 100px;
            border: 1px solid pink;
            outline: none;
            resize: none;
        }
        
        ul {
            margin-top: 50px;
        }
        
        li {
            width: 300px;
            padding: 5px;
            background-color: rgb(245, 209, 243);
            color: red;
            font-size: 14px;
            margin: 15px 0;
        }
        
        li a {
            float: right;
        }
    </style>
</head>

<body>
    <textarea name="" id=""></textarea>
    <button>发布</button>
    <ul>

    </ul>
    <script>
        // 1. 获取元素
        var btn = document.querySelector('button');
        var text = document.querySelector('textarea');
        var ul = document.querySelector('ul');
        // 2. 注册事件
        btn.onclick = function() {
            if (text.value == '') {
                alert('您没有输入内容');
                return false;
            } else {
                // console.log(text.value);
                // (1) 创建元素
                var li = document.createElement('li');
                // 先有li 才能赋值
                li.innerHTML = text.value + "<a href='javascript:;'>删除</a>";
                // (2) 添加元素
                // ul.appendChild(li);
                ul.insertBefore(li, ul.children[0]);
                // (3) 删除元素 删除的是当前链接的li  它的父亲
                var as = document.querySelectorAll('a');
                for (var i = 0; i < as.length; i++) {
                    as[i].onclick = function() {
                        // node(父亲节点).removeChild(child); 删除的是 li 当前a所在的li  this.parentNode==li;
                        ul.removeChild(this.parentNode);
                    }
                }
            }
        }
    </script>
</body>

4.6 复制节点(克隆节点)--node.cloneNode(false)-->不复制内容/node.cloneNode(true)-->复制内容

node.cloneNode()
node.cloneNode() 方法返回调用该方法的节点的一个副本。 也称为克隆节点/拷贝节点
5.6 复制节点(克隆节点)
注意:
1. 如果括号参数为空或者为 false ,则是浅拷贝,即只克隆复制节点本身,不克隆里面的子节点(不复制内容)。
2.
如果括号参数为 true ,则是深度拷贝,会复制节点本身以及里面所有的子节点

<body>
    <ul>
        <li>1111</li>
        <li>2</li>
        <li>3</li>
    </ul>
    <script>
        var ul = document.querySelector('ul');
        // 1. node.cloneNode(); 括号为空或者里面是false 浅拷贝 只复制标签不复制里面的内容
        // 2. node.cloneNode(true); 括号为true 深拷贝 复制标签复制里面的内容
        var lili = ul.children[0].cloneNode(true);
        ul.appendChild(lili);
    </script>
</body>

【案例:动态生成表格】

 

<head>   
 <style>
        table {
            width: 500px;
            margin: 100px auto;
            border-collapse: collapse;
            text-align: center;
        }
        
        td,
        th {
            border: 1px solid #333;
        }
        
        thead tr {
            height: 40px;
            background-color: #ccc;
        }
    </style>
</head>

<body>
    <table cellspacing="0">
        <thead>
            <tr>
                <th>姓名</th>
                <th>科目</th>
                <th>成绩</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody>

        </tbody>
    </table>
    <script>
        // 1.先去准备好学生的数据,使用数组
        var datas = [{
            name: '魏璎珞',
            subject: 'JavaScript',
            score: 100
        }, {
            name: '弘历',
            subject: 'JavaScript',
            score: 98
        }, {
            name: '傅恒',
            subject: 'JavaScript',
            score: 99
        }, {
            name: '明玉',
            subject: 'JavaScript',
            score: 88
        }, {
            name: '大猪蹄子',
            subject: 'JavaScript',
            score: 0
        }];
        // 2. 往tbody 里面创建行: 有几个人(通过数组的长度)我们就创建几行
        var tbody = document.querySelector('tbody');
        for (var i = 0; i < datas.length; i++) { // 外面的for循环管行 tr
            // 1. 创建 tr行
            var tr = document.createElement('tr');
            tbody.appendChild(tr);
            // 2. 行里面创建单元格(跟数据有关系的3个单元格) td 单元格的数量取决于每个对象里面的属性个数  for循环遍历对象 datas[i]
            //遍历对象
            for (var k in datas[i]) { // 里面的for循环管列 td
                // 创建单元格 
                var td = document.createElement('td');
                // 把对象里面的属性值 datas[i][k] 给 td  
                // 打印属性值:console.log(datas[i][k]);
                td.innerHTML = datas[i][k];
                tr.appendChild(td);
            }
            // 3. 创建有删除2个字的单元格 
            var td = document.createElement('td');
            td.innerHTML = '<a href="javascript:;">删除 </a>';
            tr.appendChild(td);

        }
        // 4. 删除操作 开始 
        var as = document.querySelectorAll('a');
        for (var i = 0; i < as.length; i++) {
            as[i].onclick = function() {
                   //链接的爸爸是它所在的单元格,单元格的父亲是行
                // 点击a 删除 当前a 所在的行(链接的爸爸的爸爸)  node.removeChild(child)  
                tbody.removeChild(this.parentNode.parentNode)
            }
        }
        // for(var k in obj) {
        //     k 得到的是属性名
        //     obj[k] 得到是属性值
        // }
    </script>
</body>

4.7 三种动态创建元素区别

 document.write()--如果页面文档流加载完毕,再调用这句话会导致页面重绘

        1. document.write() 创建元素  如果页面文档流加载完毕,再调用这句话会导致页面重绘
         var btn = document.querySelector('button');
         btn.onclick = function() {
            document.write('<div>123</div>');
         }

element.innerHTML

document.creatElement()

注意点:

  • 1. document.write 是直接将内容写入页面的内容流,但是文档流执行完毕,则它会导致页面全部重绘
  • 2. innerHTML 是将内容写入某个 DOM 节点,不会导致页面全部重绘
  • 3. innerHTML 创建多个元素效率更高(不要拼接字符串,采取数组形式拼接),结构稍微复杂
  • 4. createElement() 创建多个元素效率稍低一点点,但是结构更清晰
  • 总结:不同浏览器下,innerHTML 效率要比 creatElement 高

<body>
    <button>点击</button>
    <p>abc</p>
    <div class="inner"></div>
    <div class="create"></div>
    <script>
        // 2. innerHTML 创建元素
        var inner = document.querySelector('.inner');
        // for (var i = 0; i <= 100; i++) {
        //     inner.innerHTML += '<a href="#">百度</a>'
        // }
        var arr = [];
        for (var i = 0; i <= 100; i++) {
            arr.push('<a href="#">百度</a>');
        }
        //join()---将数组转换成字符串
        inner.innerHTML = arr.join('');
        // 3. document.createElement() 创建元素
        var create = document.querySelector('.create');
        for (var i = 0; i <= 100; i++) {
            var a = document.createElement('a');
            create.appendChild(a);
        }
    </script>
</body>

element.innerHTML   与   document.creatElement()的效率对比

<head>
    <meta charset="UTF-8">
    <title>效率测试</title>
</head>

<body>
</body>
<script>
    function fn() {
        var d1 = +new Date();

        var str = '';
        for (var i = 0; i < 1000; i++) {
            document.body.innerHTML += '<div style="width:100px; height:2px; border:1px solid blue;"></div>';
        }
        var d2 = +new Date();
        console.log(d2 - d1);
    }
    fn();
</script>
<script>
    function fn() {
        var d1 = +new Date();
        var array = [];
        for (var i = 0; i < 1000; i++) {
            array.push('<div style="width:100px; height:2px; border:1px solid blue;"></div>');
        }
        document.body.innerHTML = array.join('');
        var d2 = +new Date();
        console.log(d2 - d1);
    }
    fn();
</script>
<script>
    function fn() {
        var d1 = +new Date();

        for (var i = 0; i < 1000; i++) {
            var div = document.createElement('div');
            div.style.width = '100px';
            div.style.height = '2px';
            div.style.border = '1px solid red';
            document.body.appendChild(div);
        }
        var d2 = +new Date();
        console.log(d2 - d1);
    }
    fn();
</script>

二十二、DOM重点核心

1.创建

 1. document.write
2. innerHTML

3. createElement

2.增

1. appendChild
2. insertBefore

3.删

1.. removeChild

4.改

1. 修改元素属性: src、href、title等
2. 修改普通元素内容: innerHTML 、innerText
3. 修改表单元素: value、type、disabled等
4. 修改元素样式: style、className

5.查

1. DOM提供的API 方法: getElementById、getElementsByTagName 古老用法 不太推荐
2. H5提供的新方法: querySelector、querySelectorAll 提倡
3. 利用节点操作获取元素: 父(parentNode)、子(children)、兄(previousElementSibling、
nextElementSibling)
提倡

6.属性操作

1. setAttribute:设置dom的属性值
2. getAttribute:得到dom的属性值
3. removeAttribute移除属性

7.事件操作

给元素注册事件, 采取 事件源.事件类型 = 事件处理程序

 7.1 注册事件两种方式

二十三、PC端网页特效

 1. 元素偏移量offset系列(包含边框的)

1.1 offset概述

offset 翻译过来就是偏移量, 我们使用 offset 系列相关属性可以动态的得到该元素的位置(偏移)、大小等。

  •  获得元素距离带有定位父元素的位置
  •  获得元素自身的大小(宽度高度)
  •  注意: 返回的数值都不带单位

    <style>
        * {
            margin: 0;
            padding: 0;
        }
        
        .father {
            /* position: relative; */
            width: 200px;
            height: 200px;
            background-color: pink;
            margin: 150px;
        }
        
        .son {
            width: 100px;
            height: 100px;
            background-color: purple;
            margin-left: 45px;
        }
        
        .w {
            height: 200px;
            background-color: skyblue;
            margin: 0 auto 200px;
            padding: 10px;
            border: 15px solid red;
        }
    </style>
</head>

<body>
    <div class="father">
        <div class="son"></div>
    </div>
    <div class="w"></div>
    <script>
        // offset 系列
        var father = document.querySelector('.father');
        var son = document.querySelector('.son');
        // 1.可以得到元素的偏移 位置 返回的不带单位的数值  
        console.log(father.offsetTop);
        console.log(father.offsetLeft);
        // 它以带有定位的父亲为准  如果么有父亲或者父亲没有定位 则以 body 为准
        console.log(son.offsetLeft);
        var w = document.querySelector('.w');
        // 2.可以得到元素的大小 宽度和高度 是包含padding + border + width 
        console.log(w.offsetWidth);
        console.log(w.offsetHeight);
        // 3. 返回带有定位的父亲 否则返回的是body
        console.log(son.offsetParent); // 返回带有定位的父亲 否则返回的是body
        console.log(son.parentNode); // 返回父亲 是最近一级的父亲 亲爸爸 不管父亲有没有定位
    </script>
</body>

1.2 offset 与 style 区别

    <style>
        .box {
            width: 200px;
            height: 200px;
            background-color: pink;
            padding: 10px;
        }
    </style>
</head>

<body>
    <div class="box" style="width: 200px;"></div>
    <script>
        // offset与style的区别
        var box = document.querySelector('.box');
        console.log(box.offsetWidth);
        console.log(box.style.width);
        // box.offsetWidth = '300px';
        box.style.width = '300px';
    </script>
</body>

 【案例:获取鼠标在盒子内的坐标】

    <style>
        .box {
            width: 300px;
            height: 300px;
            background-color: pink;
            margin: 200px;
        }
    </style>
</head>

<body>
    <div class="box"></div>
    <script>
        // 我们在盒子内点击, 想要得到鼠标距离盒子左右的距离。
        // 首先得到鼠标在页面中的坐标( e.pageX, e.pageY)
        // 其次得到盒子在页面中的距离(box.offsetLeft, box.offsetTop)
        // 用鼠标距离页面的坐标减去盒子在页面中的距离, 得到 鼠标在盒子内的坐标
        var box = document.querySelector('.box');
        box.addEventListener('mousemove', function(e) {
            // console.log(e.pageX);//鼠标距离页面的长度
            // console.log(e.pageY);//鼠标距离页面的长度
            // console.log(box.offsetLeft);
            var x = e.pageX - this.offsetLeft;
            var y = e.pageY - this.offsetTop;
            this.innerHTML = 'x坐标是' + x + ' y坐标是' + y;
        })
    </script>
</body>

【案例:模态框拖拽】

<!DOCTYPE html>
<html lang="en">
</head>
<html>
    <style>
        .father {
            overflow: hidden;
            width: 300px;
            height: 300px;
            margin: 100px auto;
            background-color: pink;
            text-align: center;
        }
        
        .son {
            width: 200px;
            height: 200px;
            margin: 50px;
            background-color: purple;
            line-height: 200px;
            color: #fff;
        }
    </style>
</head>

<head>  
    <style>
          div {
              margin: 200px;
          }
          
          span {
              display: inline-block;
              width: 40px;
              height: 40px;
              background-color: #333;
              font-size: 20px;
              color: #fff;
              text-align: center;
              line-height: 40px;
          }
      </style>
  </head>
  
  <style>
    .login-header {
        width: 100%;
        text-align: center;
        height: 30px;
        font-size: 24px;
        line-height: 30px;
    }
    
    ul,
    li,
    ol,
    dl,
    dt,
    dd,
    div,
    p,
    span,
    h1,
    h2,
    h3,
    h4,
    h5,
    h6,
    a {
        padding: 0px;
        margin: 0px;
    }
    
    .login {
        display: none;
        width: 512px;
        height: 280px;
        position: fixed;
        border: #ebebeb solid 1px;
        left: 50%;
        top: 50%;
        background: #ffffff;
        box-shadow: 0px 0px 20px #ddd;
        z-index: 9999;
        transform: translate(-50%, -50%);
    }
    
    .login-title {
        width: 100%;
        margin: 10px 0px 0px 0px;
        text-align: center;
        line-height: 40px;
        height: 40px;
        font-size: 18px;
        position: relative;
        cursor: move;
    }
    
    .login-input-content {
        margin-top: 20px;
    }
    
    .login-button {
        width: 50%;
        margin: 30px auto 0px auto;
        line-height: 40px;
        font-size: 14px;
        border: #ebebeb 1px solid;
        text-align: center;
    }
    
    .login-bg {
        display: none;
        width: 100%;
        height: 100%;
        position: fixed;
        top: 0px;
        left: 0px;
        background: rgba(0, 0, 0, .3);
    }
    
    a {
        text-decoration: none;
        color: #000000;
    }
    
    .login-button a {
        display: block;
    }
    
    .login-input input.list-input {
        float: left;
        line-height: 35px;
        height: 35px;
        width: 350px;
        border: #ebebeb 1px solid;
        text-indent: 5px;
    }
    
    .login-input {
        overflow: hidden;
        margin: 0px 0px 20px 0px;
    }
    
    .login-input label {
        float: left;
        width: 90px;
        padding-right: 10px;
        text-align: right;
        line-height: 35px;
        height: 35px;
        font-size: 14px;
    }
    
    .login-title span {
        position: absolute;
        font-size: 12px;
        right: -20px;
        top: -30px;
        background: #ffffff;
        border: #ebebeb solid 1px;
        width: 40px;
        height: 40px;
        border-radius: 20px;
    }
</style>
</head>

<body>
<div class="login-header"><a id="link" href="javascript:;">点击,弹出登录框</a></div>
<div id="login" class="login">
    <div id="title" class="login-title">登录会员
        <span><a id="closeBtn" href="javascript:void(0);" class="close-login">关闭</a></span>
    </div>
    <div class="login-input-content">
        <div class="login-input">
            <label>用户名:</label>
            <input type="text" placeholder="请输入用户名" name="info[username]" id="username" class="list-input">
        </div>
        <div class="login-input">
            <label>登录密码:</label>
            <input type="password" placeholder="请输入登录密码" name="info[password]" id="password" class="list-input">
        </div>
    </div>
    <div id="loginBtn" class="login-button"><a href="javascript:void(0);" id="login-button-submit">登录会员</a></div>
</div>
<!-- 遮盖层 -->
<div id="bg" class="login-bg"></div>
<script>
    // 1. 获取元素
    var login = document.querySelector('.login');
    var mask = document.querySelector('.login-bg');
    var link = document.querySelector('#link');
    var closeBtn = document.querySelector('#closeBtn');
    var title = document.querySelector('#title');
    // 2. 点击弹出层这个链接 link  让mask 和login 显示出来
    link.addEventListener('click', function() {
            mask.style.display = 'block';
            login.style.display = 'block';
        })
        // 3. 点击 closeBtn 就隐藏 mask 和 login 
    closeBtn.addEventListener('click', function() {
            mask.style.display = 'none';
            login.style.display = 'none';
        })
        // 4. 开始拖拽
        // (1) 当我们鼠标按下, 就获得鼠标在盒子内的坐标
    title.addEventListener('mousedown', function(e) {
        var x = e.pageX - login.offsetLeft;//获取鼠标在盒子的横坐标
        var y = e.pageY - login.offsetTop;//获取鼠标在盒子的纵坐标
        // (2) 鼠标移动的时候,把鼠标在页面中的坐标,减去 鼠标在盒子内的坐标就是模态框的left和top值
        document.addEventListener('mousemove', move)

        //因为后面的移除事件监听要使用到此函数,所以要单独写出来
        function move(e) {
            login.style.left = e.pageX - x + 'px';//获取盒子距离页面的横距离
            login.style.top = e.pageY - y + 'px';//获取盒子距离页面的纵距离
        }
        // (3) 鼠标弹起,就让鼠标移动事件移除
        document.addEventListener('mouseup', function() {
            document.removeEventListener('mousemove', move);
        })
    })
</script>
</body>


</html>

 【案例:仿京东放大镜】

html

        <!-- 产品介绍模块 -->
        <div class="product_intro clearfix">
            <!-- 预览区域 -->
            <div class="preview_wrap fl">
                <div class="preview_img">
                    <img src="upload/s3.png" alt="">
                    <div class="mask"></div>
                    <div class="big">
                        <img src="upload/big.jpg" alt="" class="bigImg">
                    </div>
                </div>

                <div class="preview_list">
                    <a href="#" class="arrow_prev"></a>
                    <a href="#" class="arrow_next"></a>
                    <ul class="list_item">
                        <li>
                            <img src="upload/pre.jpg" alt="">
                        </li>
                        <li class="current">
                            <img src="upload/pre.jpg" alt="">
                        </li>
                        <li>
                            <img src="upload/pre.jpg" alt="">
                        </li>
                        <li>
                            <img src="upload/pre.jpg" alt="">
                        </li>
                        <li>
                            <img src="upload/pre.jpg" alt="">
                        </li>
                    </ul>
                </div>
            </div>

css

.preview_img {
    position: relative;
    height: 398px;
    border: 1px solid #ccc;
}

.mask {
    display: none;
    position: absolute;
    top: 0;
    left: 0;
    width: 300px;
    height: 300px;
    background: #FEDE4F;
    opacity: .5;
    border: 1px solid #ccc;
    cursor: move;
}

.big {
    display: none;
    position: absolute;
    left: 410px;
    top: 0;
    width: 500px;
    height: 500px;
    background-color: pink;
    z-index: 999;
    border: 1px solid #ccc;
    overflow: hidden;
}

window.addEventListener('load', function() {
    var preview_img = document.querySelector('.preview_img');
    var mask = document.querySelector('.mask');
    var big = document.querySelector('.big');
    // 1. 当我们鼠标经过 preview_img 就显示和隐藏 mask 遮挡层 和 big 大盒子
    preview_img.addEventListener('mouseover', function() {
        mask.style.display = 'block';
        big.style.display = 'block';
    })
    preview_img.addEventListener('mouseout', function() {
            mask.style.display = 'none';
            big.style.display = 'none';
        })
})

window.addEventListener('load', function() {
    var preview_img = document.querySelector('.preview_img');
    var mask = document.querySelector('.mask');
    var big = document.querySelector('.big');
    // 1. 当我们鼠标经过 preview_img 就显示和隐藏 mask 遮挡层 和 big 大盒子
    preview_img.addEventListener('mouseover', function() {
        mask.style.display = 'block';
        big.style.display = 'block';
    })
    preview_img.addEventListener('mouseout', function() {
            mask.style.display = 'none';
            big.style.display = 'none';
        })
        // 2. 鼠标移动的时候,让黄色的盒子跟着鼠标来走
    preview_img.addEventListener('mousemove', function(e) {
        // (1). 先计算出鼠标在盒子内的坐标
        //鼠标与盒子的距离==鼠标与页面的距离--盒子和页面的距离
        var x = e.pageX - this.offsetLeft;
        var y = e.pageY - this.offsetTop;
        // console.log(x, y);
        // (2) 减去盒子高度 300的一半 是 150 就是我们mask 的最终 left 和top值了(要让鼠标在黄色遮罩层的中间)
        // (3) 我们mask 移动的距离
        var maskX = x - mask.offsetWidth / 2;
        var maskY = y - mask.offsetHeight / 2;

    })

})

 

window.addEventListener('load', function() {
    var preview_img = document.querySelector('.preview_img');
    var mask = document.querySelector('.mask');
    var big = document.querySelector('.big');
    // 1. 当我们鼠标经过 preview_img 就显示和隐藏 mask 遮挡层 和 big 大盒子
    preview_img.addEventListener('mouseover', function() {
        mask.style.display = 'block';
        big.style.display = 'block';
    })
    preview_img.addEventListener('mouseout', function() {
            mask.style.display = 'none';
            big.style.display = 'none';
        })
        // 2. 鼠标移动的时候,让黄色的盒子跟着鼠标来走
    preview_img.addEventListener('mousemove', function(e) {
        // (1). 先计算出鼠标在盒子内的坐标
        var x = e.pageX - this.offsetLeft;
        var y = e.pageY - this.offsetTop;
        // console.log(x, y);
        // (2) 减去盒子高度 300的一半 是 150 就是我们mask 的最终 left 和top值了
        // (3) 我们mask 移动的距离
        var maskX = x - mask.offsetWidth / 2;
        var maskY = y - mask.offsetHeight / 2;
        // (4) 如果x 坐标小于了0 就让他停在0 的位置
        // 遮挡层的最大移动距离===最大盒子的宽度--黄色遮罩层的大小
        var maskMax = preview_img.offsetWidth - mask.offsetWidth;
        if (maskX <= 0) {
            maskX = 0;
        } else if (maskX >= maskMax) {
            maskX = maskMax;
        }
        if (maskY <= 0) {
            maskY = 0;
        } else if (maskY >= maskMax) {
            maskY = maskMax;
        }
        mask.style.left = maskX + 'px';
        mask.style.top = maskY + 'px';

    })

})

window.addEventListener('load', function() {
    var preview_img = document.querySelector('.preview_img');
    var mask = document.querySelector('.mask');
    var big = document.querySelector('.big');
    // 1. 当我们鼠标经过 preview_img 就显示和隐藏 mask 遮挡层 和 big 大盒子
    preview_img.addEventListener('mouseover', function() {
        mask.style.display = 'block';
        big.style.display = 'block';
    })
    preview_img.addEventListener('mouseout', function() {
            mask.style.display = 'none';
            big.style.display = 'none';
        })
        // 2. 鼠标移动的时候,让黄色的盒子跟着鼠标来走
    preview_img.addEventListener('mousemove', function(e) {
        // (1). 先计算出鼠标在盒子内的坐标
        var x = e.pageX - this.offsetLeft;
        var y = e.pageY - this.offsetTop;
        // console.log(x, y);
        // (2) 减去盒子高度 300的一半 是 150 就是我们mask 的最终 left 和top值了
        // (3) 我们mask 移动的距离
        var maskX = x - mask.offsetWidth / 2;
        var maskY = y - mask.offsetHeight / 2;
        // (4) 如果x 坐标小于了0 就让他停在0 的位置
        // 遮挡层的最大移动距离
        var maskMax = preview_img.offsetWidth - mask.offsetWidth;
        if (maskX <= 0) {
            maskX = 0;
        } else if (maskX >= maskMax) {
            maskX = maskMax;
        }
        if (maskY <= 0) {
            maskY = 0;
        } else if (maskY >= maskMax) {
            maskY = maskMax;
        }
        mask.style.left = maskX + 'px';
        mask.style.top = maskY + 'px';
        // 3. 大图片的移动距离 = 遮挡层移动距离 * 大图片最大移动距离 / 遮挡层的最大移动距离
        // 大图
        var bigIMg = document.querySelector('.bigImg');
        // 大图片最大移动距离
        var bigMax = bigIMg.offsetWidth - big.offsetWidth;
        // 大图片的移动距离 X Y
        var bigX = maskX * bigMax / maskMax;
        var bigY = maskY * bigMax / maskMax;
        //记得给图片加上定位
        //大图和遮罩层是反方向的
        bigIMg.style.left = -bigX + 'px';
        bigIMg.style.top = -bigY + 'px';

    })

})

2.元素可视区client系列(不包含边框的)

client 翻译过来就是客户端,我们使用 client 系列的相关属性来获取元素可视区的相关信息。通过 client 系列的相关属性可以动态的得到该元素的边框大小、元素大小等。

 

    <style>
        div {
            width: 200px;
            height: 200px;
            background-color: pink;
            border: 10px solid red;
            padding: 10px;
        }
        .one {
            width: 200px;
            height: 200px;
            background-color: pink;
            border: 10px solid red;
            /* 添加的是左右上下边框 */
            padding: 20px;
        }
    </style>
</head>
<body>
    <div></div>
    <div class="one"></div>
    <script>
        // client 宽度 和我们offsetWidth 最大的区别就是 不包含边框
        var div = document.querySelector('div');
        console.log(div.clientWidth);//200
        var one=document.querySelector('.one')
        console.log(one.clientWidth);//240
    </script>
</body>

 立即执行函数

如果有多个立即执行函数则每一个函数之间要用分号隔开

    <script>
        // 1.立即执行函数: 不需要调用,立马能够自己执行的函数
        function fn() {
            console.log(1);

        }
        fn();
        // 2. 写法 也可以传递参数进来
        // 1.(function() {})()    或者  2. (function(){}());
        (function(a, b) {
            console.log(a + b);
            var num = 10;
        })(1, 2); // 第二个小括号可以看做是调用函数
        (function sum(a, b) {
            console.log(a + b);
            var num = 10; // 局部变量
        }(2, 3));
        // 3. 立即执行函数最大的作用就是 独立创建了一个作用域, 里面所有的变量都是局部变量 不会有命名冲突的情况
    </script>

【案例: 淘宝 flexible.js 源码分析】

(function flexible(window, document) {
    // 获取的html 的根元素
    var docEl = document.documentElement
        // dpr 物理像素比
    var dpr = window.devicePixelRatio || 1

    // adjust body font size  设置我们body 的字体大小
    function setBodyFontSize() {
        // 如果页面中有body 这个元素 就设置body的字体大小
        if (document.body) {
            document.body.style.fontSize = (12 * dpr) + 'px'
        } else {
            // 如果页面中没有body 这个元素,则等着 我们页面主要的DOM元素加载完毕再去设置body
            // 的字体大小
            document.addEventListener('DOMContentLoaded', setBodyFontSize)
        }
    }
    setBodyFontSize();

    // set 1rem = viewWidth / 10    设置我们html 元素的文字大小
    function setRemUnit() {
        var rem = docEl.clientWidth / 10
        docEl.style.fontSize = rem + 'px'
    }

    setRemUnit()

    // reset rem unit on page resize  当我们页面尺寸大小发生变化的时候,要重新设置下rem 的大小
    window.addEventListener('resize', setRemUnit)
        // pageshow 是我们重新加载页面触发的事件
    window.addEventListener('pageshow', function(e) {
        // e.persisted 返回的是true 就是说如果这个页面是从缓存取过来的页面,也需要从新计算一下rem 的大小
        if (e.persisted) {
            setRemUnit()
        }
    })

    // detect 0.5px supports  有些移动端的浏览器不支持0.5像素的写法
    if (dpr >= 2) {
        var fakeBody = document.createElement('body')
        var testElement = document.createElement('div')
        testElement.style.border = '.5px solid transparent'
        fakeBody.appendChild(testElement)
        docEl.appendChild(fakeBody)
        if (testElement.offsetHeight === 1) {
            docEl.classList.add('hairlines')
        }
        docEl.removeChild(fakeBody)
    }
}(window, document))

3.元素滚动scroll系列

3.1  元素scroll系列属性

scroll 翻译过来就是滚动的,我们使用 scroll 系列的相关属性可以动态的得到该元素的大小、滚动距离等。

元素被卷去的头部是 element.scrollTop , 如果是页面被卷去的头部 则是 window.pageYOffset

    <style>
        div {
            width: 200px;
            height: 200px;
            background-color: pink;
            border: 10px solid red;
            padding: 20px;
            /* 添加滚动条 */
            overflow: auto;
        }
    </style>
</head>

<body>
    <div>
        我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容
    </div>
    <script>
        // scroll 系列--得到的是实际内容大小
        //不包含border   但是包括padding
        var div = document.querySelector('div');
        console.log(div.scrollHeight);//240==200+20+20
        console.log(div.clientHeight);//只显示文本框中出现的大小
        // scroll滚动事件当我们滚动条发生变化会触发的事件
        div.addEventListener('scroll', function() {
            console.log(div.scrollTop);

        })
    </script>
</body>

3.2 页面被卷去的头部

 如果浏览器的高(或宽)度不足以显示整个页面时,会自动出现滚动条。当滚动条向下滚动时,页面上面被隐藏掉的高度,我们就称为页面被卷去的头部。滚动条在滚动时会触发 onscroll 事件。

【案例:仿淘宝固定右侧侧边栏】

 

    <style>
        .slider-bar {
            position: absolute;
            left: 50%;
            top: 300px;
            margin-left: 600px;
            width: 45px;
            height: 130px;
            background-color: pink;
        }
        
        .w {
            width: 1200px;
            margin: 10px auto;
        }
        
        .header {
            height: 150px;
            background-color: purple;
        }
        
        .banner {
            height: 250px;
            background-color: skyblue;
        }
        
        .main {
            height: 1000px;
            background-color: yellowgreen;
        }
        
        span {
            display: none;
            position: absolute;
            bottom: 0;
        }
    </style>
</head>

<body>
    <div class="slider-bar">
        <span class="goBack">返回顶部</span>
    </div>
    <div class="header w">头部区域</div>
    <div class="banner w">banner区域</div>
    <div class="main w">主体部分</div>
    <script>
        //1. 获取元素
        var sliderbar = document.querySelector('.slider-bar');
        var banner = document.querySelector('.banner');
        // banner.offestTop 就是被卷去头部的大小 一定要写到滚动的外面
        var bannerTop = banner.offsetTop
            // 当我们侧边栏固定定位之后应该变化的数值
        var sliderbarTop = sliderbar.offsetTop - bannerTop;
        // 获取main 主体元素
        var main = document.querySelector('.main');
        var goBack = document.querySelector('.goBack');
        var mainTop = main.offsetTop;
        // 2. 页面滚动事件 scroll
        document.addEventListener('scroll', function() {
            // console.log(11);
            // window.pageYOffset 页面被卷去的头部
            // console.log(window.pageYOffset);
            // 3 .当我们页面被卷去的头部大于等于了 172 此时 侧边栏就要改为固定定位
            if (window.pageYOffset >= bannerTop) {
                //将绝对定位改为固定定位
                sliderbar.style.position = 'fixed';
                sliderbar.style.top = sliderbarTop + 'px';
            } else {
                //当我们页面被卷去的头部小于了 172 此时 侧边栏就要改为绝对定位
                sliderbar.style.position = 'absolute';
                sliderbar.style.top = '300px';
            }
            // 4. 当我们页面滚动到main盒子,就显示 goback模块
            if (window.pageYOffset >= mainTop) {
                goBack.style.display = 'block';
            } else {
                goBack.style.display = 'none';
            }

        })
    </script>
</body>

3.3 页面被卷去的头部兼容性解决方案 

需要注意的是,页面被卷去的头部,有兼容性问题,因此被卷去的头部通常有如下几种写法:
1. 声明了 DTD,使用 document.documentElement.scrollTop
2. 未声明 DTD,使用 document.body.scrollTop
3. 新方法 window.pageYOffset 和 window.pageXOffset,IE9 开始支持

4.offset、client、scroll总结

 

1. offset系列 经常用于获得元素位置 offsetLeft offsetTop
2. client 经常用于获取元素大小 clientWidth clientHeight
3. scroll 经常用于获取滚动距离 scrollTop scrollLeft


4. 注意页面滚动的距离通过 window.pageXOffset 获得

5.mouseenter 【一般跟mouseleave】(不会冒泡)mouseover(会冒泡)的区别

 当鼠标移动到元素上时就会触发 mouseenter 事件
 类似 mouseover,它们两者之间的差别是
 mouseover 鼠标经过自身盒子会触发,经过子盒子还会触发。 mouseenter 只会经过自身盒子触发
 之所以这样,就是因为mouseenter不会冒泡
跟mouseenter搭配 鼠标离开 mouseleave 同样不会冒泡

    <style>
        .father {
            width: 300px;
            height: 300px;
            background-color: pink;
            margin: 100px auto;
        }
        
        .son {
            width: 200px;
            height: 200px;
            background-color: purple;
        }
    </style>
</head>

<body>
    <div class="father">
        <div class="son"></div>
    </div>
    <script>
        var father = document.querySelector('.father');
        var son = document.querySelector('.son');
        father.addEventListener('mouseenter', function() {
            console.log(11);//只要经过自己的时候才会触发

        })

        father.addEventListener('mouseover', function() {
            console.log(11);//经过子孩子的时候也会触发

        })
    </script>
</body>

6.动画函数封装

6.1 动画实现原理:通过定时器 setInterval() 不断移动盒子位置

    <style>
        div {
            position: absolute;
            left: 0;
            width: 100px;
            height: 100px;
            background-color: pink;
        }
    </style>
</head>

<body>
    <div></div>
    <script>
        // 动画原理
        // 1. 获得盒子当前位置  offsetLeft
        // 2. 让盒子在当前位置加上1个移动距离  因为offsetLeft不能修改,所以只能重新赋值一个新值
        // 3. 利用定时器不断重复这个操作
        // 4. 加一个结束定时器的条件
        // 5. 注意此元素需要添加定位(position:absolute), 才能使用element.style.left
        var div = document.querySelector('div');
        //因为要不断的进行所以setInterval
        var timer = setInterval(function() {
            if (div.offsetLeft >= 400) {
                // 停止动画 本质是停止定时器
                clearInterval(timer);
            }
            div.style.left = div.offsetLeft + 1 + 'px';
        }, 30);
    </script>
</body>

6.2 动画函数简单封装==动画对象+移动到的距离

注意函数需要传递2个参数,动画对象和移动到的距离

    <style>
        div {
            position: absolute;
            left: 0;
            width: 100px;
            height: 100px;
            background-color: pink;
        }
        
        span {
            position: absolute;
            left: 0;
            top: 200px;
            display: block;
            width: 150px;
            height: 150px;
            background-color: purple;
        }
    </style>
</head>

<body>
    <div></div>
    <span>夏雨荷</span>
    <script>
        // 简单动画函数封装obj目标对象 target 目标位置
        function animate(obj, target) {
            var timer = setInterval(function() {
                if (obj.offsetLeft >= target) {
                    // 停止动画 本质是停止定时器
                    clearInterval(timer);
                }
                obj.style.left = obj.offsetLeft + 1 + 'px';

            }, 30);
        }

        var div = document.querySelector('div');
        var span = document.querySelector('span');
        // 调用函数
        animate(div, 300);
        animate(span, 200);
    </script>
</body>

6.3 动画函数给不同元素记录不同定时器

    <style>
        div {
            position: absolute;
            left: 0;
            width: 100px;
            height: 100px;
            background-color: pink;
        }
        
        span {
            position: absolute;
            left: 0;
            top: 200px;
            display: block;
            width: 150px;
            height: 150px;
            background-color: purple;
        }
    </style>
</head>

<body>
    <button>点击夏雨荷才走</button>
    <div></div>
    <span>夏雨荷</span>
    <script>
        // var obj = {};
        // obj.name = 'andy';//给obj这个对象添加属性和属性值



        // 简单动画函数封装obj目标对象 target 目标位置
        // 给不同的元素指定了不同的定时器
        function animate(obj, target) {
            // 当我们不断的点击按钮,这个元素的速度会越来越快,因为开启了太多的定时器
            // 解决方案就是 让我们元素只有一个定时器执行
            // 先清除以前的定时器,只保留当前的一个定时器执行
            clearInterval(obj.timer);
            obj.timer = setInterval(function() {// // 给不同的元素指定了不同的定时器
                if (obj.offsetLeft >= target) {
                    // 停止动画 本质是停止定时器
                    clearInterval(obj.timer);
                }
                obj.style.left = obj.offsetLeft + 1 + 'px';

            }, 30);
        }

        var div = document.querySelector('div');
        var span = document.querySelector('span');
        var btn = document.querySelector('button');
        // 调用函数
        animate(div, 300);
        btn.addEventListener('click', function() {
            animate(span, 200);
        })
    </script>
</body>

6.4 缓动效果原理:步长公式:(目标值 - 现在的位置) / 10

 

    <style>
        div {
            position: absolute;
            left: 0;
            width: 100px;
            height: 100px;
            background-color: pink;
        }
        
        span {
            position: absolute;
            left: 0;
            top: 200px;
            display: block;
            width: 150px;
            height: 150px;
            background-color: purple;
        }
    </style>
</head>

<body>
    <button>点击夏雨荷才走</button>
    <span>夏雨荷</span>
    <script>
        // 缓动动画函数封装obj目标对象 target 目标位置
        // 思路:
        // 1. 让盒子每次移动的距离慢慢变小, 速度就会慢慢落下来。
        // 2. 核心算法:(目标值 - 现在的位置) / 10 做为每次移动的距离 步长
        // 3. 停止的条件是: 让当前盒子位置等于目标位置就停止定时器
        function animate(obj, target) {
            // 先清除以前的定时器,只保留当前的一个定时器执行
            clearInterval(obj.timer);
            obj.timer = setInterval(function() {
                // 步长值写到定时器的里面
                var step = (target - obj.offsetLeft) / 10;
                if (obj.offsetLeft == target) {
                    // 停止动画 本质是停止定时器
                    clearInterval(obj.timer);
                }
                // 把每次加1 这个步长值改为一个慢慢变小的值  步长公式:(目标值 - 现在的位置) / 10
                obj.style.left = obj.offsetLeft + step + 'px';

            }, 15);
        }
        var span = document.querySelector('span');
        var btn = document.querySelector('button');

        btn.addEventListener('click', function() {
                // 调用函数
                animate(span, 500);
            })
            // 匀速动画 就是 盒子是当前的位置 +  固定的值 10 
            // 缓动动画就是  盒子当前的位置 + 变化的值(目标值 - 现在的位置) / 10)
    </script>
</body>

6.5 动画函数多个目标值之间移动

 

<body>
    <button class="btn500">点击夏雨荷到500</button>
    <button class="btn800">点击夏雨荷到800</button>
    <span>夏雨荷</span>
    <script>
        // 缓动动画函数封装obj目标对象 target 目标位置
        // 思路:
        // 1. 让盒子每次移动的距离慢慢变小, 速度就会慢慢落下来。
        // 2. 核心算法:(目标值 - 现在的位置) / 10 做为每次移动的距离 步长
        // 3. 停止的条件是: 让当前盒子位置等于目标位置就停止定时器
        function animate(obj, target) {
            // 先清除以前的定时器,只保留当前的一个定时器执行
            clearInterval(obj.timer);
            obj.timer = setInterval(function() {
                // 步长值写到定时器的里面
                // 把我们步长值改为整数(向上取整) 不要出现小数的问题
                // var step = Math.ceil((target - obj.offsetLeft) / 10);
                var step = (target - obj.offsetLeft) / 10;
                //因为可能是来回移动 所以可能向上取(往前走)    向下取(往回走)
                step = step > 0 ? Math.ceil(step) : Math.floor(step);
                if (obj.offsetLeft == target) {
                    // 停止动画 本质是停止定时器
                    clearInterval(obj.timer);
                }
                // 把每次加1 这个步长值改为一个慢慢变小的值  步长公式:(目标值 - 现在的位置) / 10
                obj.style.left = obj.offsetLeft + step + 'px';

            }, 15);
        }
        var span = document.querySelector('span');
        var btn500 = document.querySelector('.btn500');
        var btn800 = document.querySelector('.btn800');

        btn500.addEventListener('click', function() {
            // 调用函数
            animate(span, 500);
        })
        btn800.addEventListener('click', function() {
                // 调用函数
                animate(span, 800);
            })
            // 匀速动画 就是 盒子是当前的位置 +  固定的值 10 
            // 缓动动画就是  盒子当前的位置 + 变化的值(目标值 - 现在的位置) / 10)
    </script>
</body>

6.6 动画函数添加回调函数(写在定时器结束的位置)

 

    <style>
        div {
            position: absolute;
            left: 0;
            width: 100px;
            height: 100px;
            background-color: pink;
        }
        
        span {
            position: absolute;
            left: 0;
            top: 200px;
            display: block;
            width: 150px;
            height: 150px;
            background-color: purple;
        }
    </style>
</head>

<body>
    <button class="btn500">点击夏雨荷到500</button>
    <button class="btn800">点击夏雨荷到800</button>
    <span>夏雨荷</span>
    <script>
        // 缓动动画函数封装obj目标对象 target 目标位置
        // 思路:
        // 1. 让盒子每次移动的距离慢慢变小, 速度就会慢慢落下来。
        // 2. 核心算法:(目标值 - 现在的位置) / 10 做为每次移动的距离 步长
        // 3. 停止的条件是: 让当前盒子位置等于目标位置就停止定时器
        function animate(obj, target, callback) {
            // console.log(callback);  callback = function() {}  调用的时候 callback()

            // 先清除以前的定时器,只保留当前的一个定时器执行
            clearInterval(obj.timer);
            obj.timer = setInterval(function() {
                // 步长值写到定时器的里面
                // 把我们步长值改为整数 不要出现小数的问题
                // var step = Math.ceil((target - obj.offsetLeft) / 10);
                var step = (target - obj.offsetLeft) / 10;
                step = step > 0 ? Math.ceil(step) : Math.floor(step);
                if (obj.offsetLeft == target) {
                    // 停止动画 本质是停止定时器
                    clearInterval(obj.timer);
                    // 回调函数写到定时器结束里面
                    if (callback) {
                        // 调用函数
                        callback();
                    }
                }
                // 把每次加1 这个步长值改为一个慢慢变小的值  步长公式:(目标值 - 现在的位置) / 10
                obj.style.left = obj.offsetLeft + step + 'px';

            }, 15);
        }
        var span = document.querySelector('span');
        var btn500 = document.querySelector('.btn500');
        var btn800 = document.querySelector('.btn800');

        btn500.addEventListener('click', function() {
            // 调用函数
            animate(span, 500);
        })
        btn800.addEventListener('click', function() {
                // 调用函数
                animate(span, 800, function() {
                    // alert('你好吗');
                    span.style.backgroundColor = 'red';
                });
            })
            // 匀速动画 就是 盒子是当前的位置 +  固定的值 10 
            // 缓动动画就是  盒子当前的位置 + 变化的值(目标值 - 现在的位置) / 10)
    </script>
</body>

 6.7 动画函数封装到单独JS文件里面

    <style>
        .sliderbar {
            position: fixed;
            right: 0;
            bottom: 100px;
            width: 40px;
            height: 40px;
            text-align: center;
            line-height: 40px;
            cursor: pointer;
            color: #fff;
        }
        
        .con {
            position: absolute;
            left: 0;
            top: 0;
            width: 200px;
            height: 40px;
            background-color: purple;
            z-index: -1;
        }
    </style>
    <script src="animate.js"></script>
</head>

<body>
    <div class="sliderbar">
        <span>←</span>
        <div class="con">问题反馈</div>
    </div>

    <script>
        // 1. 获取元素
        var sliderbar = document.querySelector('.sliderbar');
        var con = document.querySelector('.con');
        // 当我们鼠标经过 sliderbar 就会让 con这个盒子滑动到左侧
        // 当我们鼠标离开 sliderbar 就会让 con这个盒子滑动到右侧
        sliderbar.addEventListener('mouseenter', function() {
            // animate(obj, target, callback);
            animate(con, -160, function() {
                // 当我们动画执行完毕,就把 ← 改为 →
                sliderbar.children[0].innerHTML = '→';
            });

        })
        sliderbar.addEventListener('mouseleave', function() {
            // animate(obj, target, callback);
            animate(con, 0, function() {
                sliderbar.children[0].innerHTML = '←';
            });

        })
    </script>
</body>

6.常见网页特效案例

【案例:网页轮播图】

 左右两边的按钮

// 先让其他文件加载完在执行js
window.addEventListener('load', function() {
    // 1. 获取元素
    var arrow_l = document.querySelector('.arrow-l');
    var arrow_r = document.querySelector('.arrow-r');
    var focus = document.querySelector('.focus');
    // 2. 鼠标经过focus 就显示隐藏左右按钮
    focus.addEventListener('mouseenter', function() {
        arrow_l.style.display = 'block';
        arrow_r.style.display = 'block';
    });
    focus.addEventListener('mouseleave', function() {
        arrow_l.style.display = 'none';
        arrow_r.style.display = 'none';
    })
})


 动态生成小圆点

    //3.动态生成小圆点  有几张图片,我就生成几个小圆点
    //因为ul和ol很多 加上focus可以缩小范围
    var ul=focus.querySelector('ul')
    var ol=focus.querySelector('.circle')
    //先求出ul中有多少个li
    for(var i=0;i<ul.children.length;i++){
        //创建一个小li
        var li=document.createElement('li');
        //把小li插入到ol里面
        ol.appendChild(li);
    }

    //把ol里面的第一个小li设置类名位current
    ol.children[0].className='current';
})

    //3.动态生成小圆点  有几张图片,我就生成几个小圆点
    //因为ul和ol很多 加上focus可以缩小范围
    var ul=focus.querySelector('ul')
    var ol=focus.querySelector('.circle')
    //先求出ul中有多少个li
    for(var i=0;i<ul.children.length;i++){
        //创建一个小li
        var li=document.createElement('li');
        //把小li插入到ol里面
        ol.appendChild(li);
        //4.小圆圈的排他思想  我们可以直接在生成小圆圈的同时直接绑定点击事件
        li.addEventListener('click',function(){
            //干掉所有人 把所有的小li 清除current类名
            for(var i=0;i<ol.children.length;i++){
                ol.children[i].className='';
            }
            //留下自己  当前的li 设置current类名
            this.className='current';
        })
    }

    //把ol里面的第一个小li设置类名位current
    ol.children[0].className='current';

 点击小圆圈实现图片滚动

// 先让其他文件加载完在执行js
window.addEventListener('load', function() {
    // 1. 获取元素
    var arrow_l = document.querySelector('.arrow-l');
    var arrow_r = document.querySelector('.arrow-r');
    var focus = document.querySelector('.focus');
    // 2. 鼠标经过focus 就显示隐藏左右按钮
    focus.addEventListener('mouseenter', function() {
        arrow_l.style.display = 'block';
        arrow_r.style.display = 'block';
    });
    focus.addEventListener('mouseleave', function() {
        arrow_l.style.display = 'none';
        arrow_r.style.display = 'none';
    })


    //3.动态生成小圆点  有几张图片,我就生成几个小圆点
    //因为ul和ol很多 加上focus可以缩小范围
    var ul=focus.querySelector('ul')
    var ol=focus.querySelector('.circle')
    //先求出ul中有多少个li
    for(var i=0;i<ul.children.length;i++){
        //创建一个小li
        var li=document.createElement('li');
        //记录当前小圆圈的索引号  通过自定义属性来做
        li.setAttribute('index',i);
        //把小li插入到ol里面
        ol.appendChild(li);
        //4.小圆圈的排他思想  我们可以直接在生成小圆圈的同时直接绑定点击事件
        li.addEventListener('click',function(){
            //干掉所有人 把所有的小li 清除current类名
            for(var i=0;i<ol.children.length;i++){
                ol.children[i].className='';
            }
            //留下自己  当前的li 设置current类名
            this.className='current';

            //5.点击小圆圈 移动图片 当然是移动ul
            // animate(obj,target,callback)
            //ul的移动距离 小圆圈的索引号 乘以 图片的宽度 注意是负值
            //当我们点击了某一个小li 就拿到当前小li 的索引号
            var index=this.getAttribute('index');
            var focusWidth=focus.offsetWidth;
            console.log(focusWidth);
            console.log(index);

            animate(ul,-index*focusWidth)

        })
    }

    //把ol里面的第一个小li设置类名位current
    ol.children[0].className='current';
})


 图片的无缝滚动(将第一张图片复制到最后)

 

// 先让其他文件加载完在执行js
window.addEventListener('load', function() {
    // 1. 获取元素
    var arrow_l = document.querySelector('.arrow-l');
    var arrow_r = document.querySelector('.arrow-r');
    var focus = document.querySelector('.focus');
    //图片的宽度 因为要经常用到 所以当中全局变量
    var focusWidth=focus.offsetWidth;
    // 2. 鼠标经过focus 就显示隐藏左右按钮
    focus.addEventListener('mouseenter', function() {
        arrow_l.style.display = 'block';
        arrow_r.style.display = 'block';
    });
    focus.addEventListener('mouseleave', function() {
        arrow_l.style.display = 'none';
        arrow_r.style.display = 'none';
    })


    //3.动态生成小圆点  有几张图片,我就生成几个小圆点
    //因为ul和ol很多 加上focus可以缩小范围
    var ul=focus.querySelector('ul')
    var ol=focus.querySelector('.circle')
    //先求出ul中有多少个li
    for(var i=0;i<ul.children.length;i++){
        //创建一个小li
        var li=document.createElement('li');
        //记录当前小圆圈的索引号  通过自定义属性来做
        li.setAttribute('index',i);
        //把小li插入到ol里面
        ol.appendChild(li);
        //4.小圆圈的排他思想  我们可以直接在生成小圆圈的同时直接绑定点击事件
        li.addEventListener('click',function(){
            //干掉所有人 把所有的小li 清除current类名
            for(var i=0;i<ol.children.length;i++){
                ol.children[i].className='';
            }
            //留下自己  当前的li 设置current类名
            this.className='current';
            //5.点击小圆圈 移动图片 当然是移动ul
            // animate(obj,target,callback)
            //ul的移动距离 小圆圈的索引号 乘以 图片的宽度 注意是负值
            //当我们点击了某一个小li 就拿到当前小li 的索引号
            var index=this.getAttribute('index');
            console.log(focusWidth);
            console.log(index);

            animate(ul,-index*focusWidth)

        })
    }

    //把ol里面的第一个小li设置类名位current
    ol.children[0].className='current';
})

克隆图片---并使得不添加小圆圈 

// 先让其他文件加载完在执行js
window.addEventListener('load', function() {
    // 1. 获取元素
    var arrow_l = document.querySelector('.arrow-l');
    var arrow_r = document.querySelector('.arrow-r');
    var focus = document.querySelector('.focus');
    //图片的宽度 因为要经常用到 所以当中全局变量
    var focusWidth=focus.offsetWidth;
    // 2. 鼠标经过focus 就显示隐藏左右按钮
    focus.addEventListener('mouseenter', function() {
        arrow_l.style.display = 'block';
        arrow_r.style.display = 'block';
    });
    focus.addEventListener('mouseleave', function() {
        arrow_l.style.display = 'none';
        arrow_r.style.display = 'none';
    })


    //3.动态生成小圆点  有几张图片,我就生成几个小圆点
    //因为ul和ol很多 加上focus可以缩小范围
    var ul=focus.querySelector('ul')
    var ol=focus.querySelector('.circle')
    //先求出ul中有多少个li
    for(var i=0;i<ul.children.length;i++){
        //创建一个小li
        var li=document.createElement('li');
        //记录当前小圆圈的索引号  通过自定义属性来做
        li.setAttribute('index',i);
        //把小li插入到ol里面
        ol.appendChild(li);
        //4.小圆圈的排他思想  我们可以直接在生成小圆圈的同时直接绑定点击事件
        li.addEventListener('click',function(){
            //干掉所有人 把所有的小li 清除current类名
            for(var i=0;i<ol.children.length;i++){
                ol.children[i].className='';
            }
            //留下自己  当前的li 设置current类名
            this.className='current';
            //5.点击小圆圈 移动图片 当然是移动ul
            // animate(obj,target,callback)
            //ul的移动距离 小圆圈的索引号 乘以 图片的宽度 注意是负值
            //当我们点击了某一个小li 就拿到当前小li 的索引号
            var index=this.getAttribute('index');
            console.log(focusWidth);
            console.log(index);

            animate(ul,-index*focusWidth)

        })
    }
    //6.克隆第一张图片(li)放到ul的最后
    var first=ul.children[0].cloneNode(true);
    ul.appendChild(first);

    //7.点击右侧按钮 ,图片滚动一张
    var num=0;
    arrow_r.addEventListener('click',function(){
        //如果走到最后复制的一张图片 此时 我们的ul 要快速复原left 改为0
        if(num==4){
            //复制操作
            ul.style.left=0;
            num=0;
        }
        num++;
        //移动的是ul不是li
        animate(ul,-num*focusWidth)
    })

    //把ol里面的第一个小li设置类名位current
    ol.children[0].className='current';
})

 小圆圈跟随左按钮

            //【问题:当我们点击小圆圈的第二个的时候 点击右侧按钮的时候 图片却出现上一张照片】
            //当我们点击某一个小li的时候 就拿到当前的li的索引号
            num=index;
            //当我们点击某一个小li的时候 就拿到当前的li的索引号
            num=circle;

 左侧按钮

// 先让其他文件加载完在执行js
window.addEventListener('load', function() {
    // 1. 获取元素
    var arrow_l = document.querySelector('.arrow-l');
    var arrow_r = document.querySelector('.arrow-r');
    var focus = document.querySelector('.focus');
    //图片的宽度 因为要经常用到 所以当中全局变量
    var focusWidth=focus.offsetWidth;
    // 2. 鼠标经过focus 就显示隐藏左右按钮
    focus.addEventListener('mouseenter', function() {
        arrow_l.style.display = 'block';
        arrow_r.style.display = 'block';
    });
    focus.addEventListener('mouseleave', function() {
        arrow_l.style.display = 'none';
        arrow_r.style.display = 'none';
    })


    //3.动态生成小圆点  有几张图片,我就生成几个小圆点
    //因为ul和ol很多 加上focus可以缩小范围
    var ul=focus.querySelector('ul')
    var ol=focus.querySelector('.circle')
    //先求出ul中有多少个li
    for(var i=0;i<ul.children.length;i++){
        //创建一个小li
        var li=document.createElement('li');
        //记录当前小圆圈的索引号  通过自定义属性来做
        li.setAttribute('index',i);
        //把小li插入到ol里面
        ol.appendChild(li);
        //4.小圆圈的排他思想  我们可以直接在生成小圆圈的同时直接绑定点击事件
        li.addEventListener('click',function(){
            //干掉所有人 把所有的小li 清除current类名
            for(var i=0;i<ol.children.length;i++){
                ol.children[i].className='';
            }
            //留下自己  当前的li 设置current类名
            this.className='current';
            //5.点击小圆圈 移动图片 当然是移动ul
            // animate(obj,target,callback)
            //ul的移动距离 小圆圈的索引号 乘以 图片的宽度 注意是负值
            //当我们点击了某一个小li 就拿到当前小li 的索引号
            var index=this.getAttribute('index');
            //【问题:当我们点击小圆圈的第二个的时候 点击右侧按钮的时候 图片却出现上一张照片】
            //当我们点击某一个小li的时候 就拿到当前的li的索引号
            num=index;
            //当我们点击某一个小li的时候 就拿到当前的li的索引号
            num=circle;

            console.log(focusWidth);
            console.log(index);

            animate(ul,-index*focusWidth)

        })
    }
    //6.克隆第一张图片(li)放到ul的最后
    var first=ul.children[0].cloneNode(true);
    ul.appendChild(first);

    //7.点击右侧按钮 ,图片滚动一张
    var num=0;
    //circle  控制小圆圈的播放
    var circle=0;
    arrow_r.addEventListener('click',function(){
        //如果走到最后复制的一张图片 此时 我们的ul 要快速复原left 改为0
        if(num==ul.children.length-1){
            //复制操作
            ul.style.left=0;
            num=0;
        }
        num++;
        //移动的是ul不是li
        animate(ul,-num*focusWidth)

        //8.点击右侧按钮  小圆圈跟随一起变化  可以在声明一个变量控制小圆圈播放
        circle++;
        //如果circle==4  说明走到最后我们克隆的这张照片 我们就要复原
        if(circle==ol.children.length){
            circle=0;
        }
        circleChange();
    })

    //把ol里面的第一个小li设置类名位current
    ol.children[0].className='current';




    //9.左侧按钮做法
    arrow_l.addEventListener('click',function(){
        //如果走到最后复制的一张图片 此时 我们的ul 要快速复原left 改为0
        if(num==ul.children.length-1){
            //复制操作
            ul.style.left=-(ul.children.length-1)*focusWidth;
            num=ul.children.length-1;
        }
        num--;
        //移动的是ul不是li
        animate(ul,-num*focusWidth)

        //8.点击右侧按钮  小圆圈跟随一起变化  可以在声明一个变量控制小圆圈播放
        circle--;
        //如果circle<0说明第一张图片 则小圆圈要改为第4个小圆圈
        if(circle<0){
            circle=ol.children.length-1;
        }
        circleChange();
    });


    //封装一个函数 改变小圆圈的样式
    function circleChange(){
                //先清除其余小圆圈的current
                for(var i=0;i<ol.children.length;i++){
                    ol.children[i].className='';
                }
                //留下当前的小圆圈的current类
                ol.children[circle].className='current'
    }
})

自动播放功能【添加和删除定时器】 

window.addEventListener('load', function() {
    // 1. 获取元素
    var arrow_l = document.querySelector('.arrow-l');
    var arrow_r = document.querySelector('.arrow-r');
    var focus = document.querySelector('.focus');
    var focusWidth = focus.offsetWidth;
    // 2. 鼠标经过focus 就显示隐藏左右按钮
    focus.addEventListener('mouseenter', function() {
        arrow_l.style.display = 'block';
        arrow_r.style.display = 'block';
        //当鼠标在图片上面的时候 自动播放功能要停止
        clearInterval(timer);
        timer = null; // 清除定时器变量
    });
    focus.addEventListener('mouseleave', function() {
        arrow_l.style.display = 'none';
        arrow_r.style.display = 'none';
        //当鼠标移开的时候 自动播放功能要继续开启
        timer = setInterval(function() {
            //手动调用点击事件
            arrow_r.click();
        }, 2000);
    });
    // 3. 动态生成小圆圈  有几张图片,我就生成几个小圆圈
    var ul = focus.querySelector('ul');
    var ol = focus.querySelector('.circle');
    // console.log(ul.children.length);
    for (var i = 0; i < ul.children.length; i++) {
        // 创建一个小li 
        var li = document.createElement('li');
        // 记录当前小圆圈的索引号 通过自定义属性来做 
        li.setAttribute('index', i);
        // 把小li插入到ol 里面
        ol.appendChild(li);
        // 4. 小圆圈的排他思想 我们可以直接在生成小圆圈的同时直接绑定点击事件
        li.addEventListener('click', function() {
            // 干掉所有人 把所有的小li 清除 current 类名
            for (var i = 0; i < ol.children.length; i++) {
                ol.children[i].className = '';
            }
            // 留下我自己  当前的小li 设置current 类名
            this.className = 'current';
            // 5. 点击小圆圈,移动图片 当然移动的是 ul 
            // ul 的移动距离 小圆圈的索引号 乘以 图片的宽度 注意是负值
            // 当我们点击了某个小li 就拿到当前小li 的索引号
            var index = this.getAttribute('index');
            // 当我们点击了某个小li 就要把这个li 的索引号给 num  
            num = index;
            // 当我们点击了某个小li 就要把这个li 的索引号给 circle  
            circle = index;
            // num = circle = index;
            console.log(focusWidth);
            console.log(index);

            animate(ul, -index * focusWidth);
        })
    }
    // 把ol里面的第一个小li设置类名为 current
    ol.children[0].className = 'current';
    // 6. 克隆第一张图片(li)放到ul 最后面
    var first = ul.children[0].cloneNode(true);
    ul.appendChild(first);
    // 7. 点击右侧按钮, 图片滚动一张
    var num = 0;
    // circle 控制小圆圈的播放
    var circle = 0;
    // flag 节流阀
    var flag = true;
    arrow_r.addEventListener('click', function() {
        if (flag) {
            flag = false; // 关闭节流阀
            // 如果走到了最后复制的一张图片,此时 我们的ul 要快速复原 left 改为 0
            if (num == ul.children.length - 1) {
                ul.style.left = 0;
                num = 0;
            }
            num++;
            animate(ul, -num * focusWidth, function() {
                flag = true; // 打开节流阀
            });
            // 8. 点击右侧按钮,小圆圈跟随一起变化 可以再声明一个变量控制小圆圈的播放
            circle++;
            // 如果circle == 4 说明走到最后我们克隆的这张图片了 我们就复原
            if (circle == ol.children.length) {
                circle = 0;
            }
            // 调用函数
            circleChange();
        }
    });

    // 9. 左侧按钮做法
    arrow_l.addEventListener('click', function() {
        if (flag) {
            flag = false;
            if (num == 0) {
                num = ul.children.length - 1;
                ul.style.left = -num * focusWidth + 'px';

            }
            num--;
            animate(ul, -num * focusWidth, function() {
                flag = true;
            });
            // 点击左侧按钮,小圆圈跟随一起变化 可以再声明一个变量控制小圆圈的播放
            circle--;
            // 如果circle < 0  说明第一张图片,则小圆圈要改为第4个小圆圈(3)
            // if (circle < 0) {
            //     circle = ol.children.length - 1;
            // }
            circle = circle < 0 ? ol.children.length - 1 : circle;
            // 调用函数
            circleChange();
        }
    });

    function circleChange() {
        // 先清除其余小圆圈的current类名
        for (var i = 0; i < ol.children.length; i++) {
            ol.children[i].className = '';
        }
        // 留下当前的小圆圈的current类名
        ol.children[circle].className = 'current';
    }
    // 10. 自动播放轮播图
    var timer = setInterval(function() {
        //手动调用点击事件
        arrow_r.click();
    }, 2000);

})

6.1 节流阈(防止动画过快--【使用回调函数】

 开始设置一个变量 var flag = true;
If(flag) {flag = false; do something} 关闭水龙头
利用回调函数 动画执行完毕, flag = true 打开水龙头
【案例:返回顶部】

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .slider-bar {
            position: absolute;
            left: 50%;
            top: 300px;
            margin-left: 600px;
            width: 45px;
            height: 130px;
            background-color: pink;
        }
        
        .w {
            width: 1200px;
            margin: 10px auto;
        }
        
        .header {
            height: 150px;
            background-color: purple;
        }
        
        .banner {
            height: 250px;
            background-color: skyblue;
        }
        
        .main {
            height: 1000px;
            background-color: yellowgreen;
        }
        
        span {
            display: none;
            position: absolute;
            bottom: 0;
        }
    </style>
</head>

<body>
    <div class="slider-bar">
        <span class="goBack">返回顶部</span>
    </div>
    <div class="header w">头部区域</div>
    <div class="banner w">banner区域</div>
    <div class="main w">主体部分</div>
    <script>
        //1. 获取元素
        var sliderbar = document.querySelector('.slider-bar');
        var banner = document.querySelector('.banner');
        // banner.offestTop 就是被卷去头部的大小 一定要写到滚动的外面
        var bannerTop = banner.offsetTop
            // 当我们侧边栏固定定位之后应该变化的数值
        var sliderbarTop = sliderbar.offsetTop - bannerTop;
        // 获取main 主体元素
        var main = document.querySelector('.main');
        var goBack = document.querySelector('.goBack');
        var mainTop = main.offsetTop;
        // 2. 页面滚动事件 scroll
        document.addEventListener('scroll', function() {
                // console.log(11);
                // window.pageYOffset 页面被卷去的头部
                // console.log(window.pageYOffset);
                // 3 .当我们页面被卷去的头部大于等于了 172 此时 侧边栏就要改为固定定位
                if (window.pageYOffset >= bannerTop) {
                    sliderbar.style.position = 'fixed';
                    sliderbar.style.top = sliderbarTop + 'px';
                } else {
                    sliderbar.style.position = 'absolute';
                    sliderbar.style.top = '300px';
                }
                // 4. 当我们页面滚动到main盒子,就显示 goback模块
                if (window.pageYOffset >= mainTop) {
                    goBack.style.display = 'block';
                } else {
                    goBack.style.display = 'none';
                }

            })
            // 3. 当我们点击了返回顶部模块,就让窗口滚动的页面的最上方
        goBack.addEventListener('click', function() {
            // 里面的x和y 不跟单位的 直接写数字即可
            // window.scroll(0, 0);
            // 因为是窗口滚动 所以对象是window
            animate(window, 0);
        });
        // 动画函数
        function animate(obj, target, callback) {
            // console.log(callback);  callback = function() {}  调用的时候 callback()

            // 先清除以前的定时器,只保留当前的一个定时器执行
            clearInterval(obj.timer);
            obj.timer = setInterval(function() {
                // 步长值写到定时器的里面
                // 把我们步长值改为整数 不要出现小数的问题
                // var step = Math.ceil((target - obj.offsetLeft) / 10);
                var step = (target - window.pageYOffset) / 10;
                step = step > 0 ? Math.ceil(step) : Math.floor(step);
                if (window.pageYOffset == target) {
                    // 停止动画 本质是停止定时器
                    clearInterval(obj.timer);
                    // 回调函数写到定时器结束里面
                    // if (callback) {
                    //     // 调用函数
                    //     callback();
                    // }
                    callback && callback();
                }
                // 把每次加1 这个步长值改为一个慢慢变小的值  步长公式:(目标值 - 现在的位置) / 10
                // obj.style.left = window.pageYOffset + step + 'px';
                window.scroll(0, window.pageYOffset + step);
            }, 15);
        }
    </script>
</body>

</html>

 【案例:筋头云案例】

<!DOCTYPE html>
<html>

<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <style>
        * {
            margin: 0;
            padding: 0
        }
        
        ul {
            list-style: none;
        }
        
        body {
            background-color: black;
        }
        
        .c-nav {
            width: 900px;
            height: 42px;
            background: #fff url(images/rss.png) no-repeat right center;
            margin: 100px auto;
            border-radius: 5px;
            position: relative;
        }
        
        .c-nav ul {
            position: absolute;
        }
        
        .c-nav li {
            float: left;
            width: 83px;
            text-align: center;
            line-height: 42px;
        }
        
        .c-nav li a {
            color: #333;
            text-decoration: none;
            display: inline-block;
            height: 42px;
        }
        
        .c-nav li a:hover {
            color: white;
        }
        
        .c-nav li.current a {
            color: #0dff1d;
        }
        
        .cloud {
            position: absolute;
            left: 0;
            top: 0;
            width: 83px;
            height: 42px;
            background: url(images/cloud.gif) no-repeat;
        }
    </style>
    <script src="animate.js"></script>
    <script>
        window.addEventListener('load', function() {
            // 1. 获取元素
            var cloud = document.querySelector('.cloud');
            var c_nav = document.querySelector('.c-nav');
            var lis = c_nav.querySelectorAll('li');
            // 2. 给所有的小li绑定事件 
            // 这个current 做为筋斗云的起始位置
            var current = 0;
            for (var i = 0; i < lis.length; i++) {
                // (1) 鼠标经过把当前小li 的位置做为目标值
                lis[i].addEventListener('mouseenter', function() {
                    animate(cloud, this.offsetLeft);
                });
                // (2) 鼠标离开就回到起始的位置 
                lis[i].addEventListener('mouseleave', function() {
                    animate(cloud, current);
                });
                // (3) 当我们鼠标点击,就把当前位置做为目标值
                lis[i].addEventListener('click', function() {
                    current = this.offsetLeft;
                });
            }
        })
    </script>
</head>

<body>
    <div id="c_nav" class="c-nav">
        <span class="cloud"></span>
        <ul>
            <li class="current"><a href="#">首页新闻</a></li>
            <li><a href="#">师资力量</a></li>
            <li><a href="#">活动策划</a></li>
            <li><a href="#">企业文化</a></li>
            <li><a href="#">招聘信息</a></li>
            <li><a href="#">公司简介</a></li>
            <li><a href="#">我是佩奇</a></li>
            <li><a href="#">啥是佩奇</a></li>
        </ul>
    </div>
</body>

</html>

二十四、事件高级

1.注册事件(绑定事件)

1.1 注册事件概述:传统方式   VS  方法监听注册事件 

  btns[0].onclick = function() {}

使用时后面会将前面的进行覆盖

<body>
    <button>传统注册事件</button>
    <button>方法监听注册事件</button>
    <script>
        var btns = document.querySelectorAll('button');
        // 1. 传统方式注册事件
        btns[0].onclick = function() {
            alert('hi');
        }
        btns[0].onclick = function() {
                alert('hao a u');
            }
    </script>
</body>

1.2 addEventListener事件监听方式

eventTarget.addEventListener('事件类型', listener[, useCapture])

  •  (1) 里面的事件类型是字符串 必定加引号 而且不带on
  •  (2) 同一个元素 同一个事件可以添加多个侦听器(事件处理程序)
<body>
    <button>传统注册事件</button>
    <button>方法监听注册事件</button>
    <script>
        var btns=document.querySelectorAll('button')
           // 2. 事件侦听注册事件 addEventListener 
            // (1) 里面的事件类型是字符串 必定加引号 而且不带on
            // (2) 同一个元素 同一个事件可以添加多个侦听器(事件处理程序)
            btns[1].addEventListener('click', function() {
            alert(22);
        })
        btns[1].addEventListener('click', function() {
                alert(33);
            })
    </script>
</body>

 1.3 attachEvent 注册事件 事件监听方式

eventTarget.attachEvent(eventNameWithOn, callback)

注意点:

  1. 事件类型是字符串,且要加上on
<body>
    <button>传统注册事件</button>
    <button>方法监听注册事件</button>
    <button>ie9 attachEvent</button>
    <script>
        var btns=document.querySelectorAll('button')
        // 3. attachEvent ie9以前的版本支持
            btns[2].attachEvent('onclick', function() {
                alert(11);
            })
    </script>

1.4 注册事件兼容性解决方案

 兼容性处理的原则: 首先照顾大多数浏览器,再处理特殊浏览器

    <script>
            function addEventListener(element, eventName, fn) {
            // 判断当前浏览器是否支持 addEventListener 方法
            if (element.addEventListener) {
            element.addEventListener(eventName, fn); // 第三个参数 默认是false
            } else if (element.attachEvent) {
            element.attachEvent('on' + eventName, fn);
            } else {
            // 相当于 element.onclick = fn;
            element['on' + eventName] = fn;
            }
    </script>

2删除事件(解绑事件)

2.1 删除事件的方式

        1. 传统注册方式
eventTarget.onclick = null;

    <style>
        div {
            width: 100px;
            height: 100px;
            background-color: pink;
        }
    </style>
<body>
    <div>1</div>
    <div>2</div>
    <div>3</div>
    <script>
        var divs = document.querySelectorAll('div');
        divs[0].onclick = function() {
                alert(11);
                // 1. 传统方式删除事件
                divs[0].onclick = null;
            }
    </script>
</body>


        2. 方法监听注册方式
① eventTarget.removeEventListener(type, listener[, useCapture]);

    <style>
        div {
            width: 100px;
            height: 100px;
            background-color: pink;
        }
    </style>
<body>
    <div>1</div>
    <div>2</div>
    <div>3</div>
    <script>
            var divs = document.querySelectorAll('div');

            //错误写法
            /*divs[1].addEventListener('click',function(){
                alert('22);
                divs[1].removeEventListener('click',function);*/

            // 2. removeEventListener 删除事件
            divs[1].addEventListener('click', fn) // 里面的fn 不需要调用加小括号

            function fn() {
                alert(22);
                divs[1].removeEventListener('click', fn);
            }
    </script>
</body>


② eventTarget.detachEvent(eventNameWithOn, callback);

    <style>
        div {
            width: 100px;
            height: 100px;
            background-color: pink;
        }
    </style>
<body>
    <div>1</div>
    <div>2</div>
    <div>3</div>
    <script>
        // 3. detachEvent
        divs[2].attachEvent('onclick', fn1);

        function fn1() {
            alert(33);
            divs[2].detachEvent('onclick', fn1);
        }
    </script>
</body>

2.2 删除事件兼容性解决方案

    <script>
        function removeEventListener(element, eventName, fn) {
        // 判断当前浏览器是否支持 removeEventListener 方法
        if (element.removeEventListener) {
        element.removeEventListener(eventName, fn); // 第三个参数 默认是false
        } else if (element.detachEvent) {
        element.detachEvent('on' + eventName, fn);
        } else {
        element['on' + eventName] = null;
        }
    </script>

3.DOM事件流

事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即 DOM 事件流。 

注意
1. JS 代码中只能执行捕获或者冒泡其中的一个阶段。
2. onclick 和 attachEvent 只能得到冒泡阶段。
3.
addEventListener(type, listener[, useCapture])第三个参数如果是 true,表示在事件捕
获阶段调用事件处理程序;如果是 false(不写默认就是false),表示在事件冒泡阶段调用事件处理程序

4. 实际开发中我们很少使用事件捕获,我们更关注事件冒泡。
5. 有些事件是没有冒泡的,比如 onblur、onfocus、onmouseenter、onmouseleave
6. 事件冒泡有时候会带来麻烦,有时候又会帮助很巧妙的做某些事件,我们后面讲解。

<body>
    <div class="father">
        <div class="son">son盒子</div>
    </div>
    <script>
        // dom 事件流 三个阶段
        // 1. JS 代码中只能执行捕获或者冒泡其中的一个阶段。
        // 2. onclick 和 attachEvent(ie) 只能得到冒泡阶段。
        // 3. 捕获阶段 如果addEventListener 第三个参数是 true 那么则处于捕获阶段  document -> html -> body -> father -> son

        //输出顺序是father-->son
        var son = document.querySelector('.son');
        son.addEventListener('click', function() {
            alert('son');
        }, true);
        var father = document.querySelector('.father');
        father.addEventListener('click', function() {
            alert('father');
        }, true);

        // 4. 冒泡阶段 如果addEventListener 第三个参数是 false 或者 省略 那么则处于冒泡阶段  son -> father ->body -> html -> document

        //输出顺序是son-->father-->document
        var son = document.querySelector('.son');
        son.addEventListener('click', function() {
            alert('son');
        }, false);
        var father = document.querySelector('.father');
        father.addEventListener('click', function() {
            alert('father');
        }, false);
        document.addEventListener('click', function() {
            alert('document');
        })
    </script>
</body>

4.事件对象

4.1 什么是事件对象

    <style>
        div {
            width: 100px;
            height: 100px;
            background-color: pink;
        }
    </style>
</head>

<body>
    <div>123</div>
    <script>
        // 事件对象
        var div = document.querySelector('div');
        div.onclick = function(e) {
                // console.log(e);
                // console.log(window.event);
                 e = e || window.event;
                console.log(e);


            }
            // div.addEventListener('click', function(e) {
            //         console.log(e);

        //     })
        // 1. event 就是一个事件对象 写到我们侦听函数的 小括号里面 当形参来看
        // 2. 事件对象只有有了事件才会存在,它是系统给我们自动创建的,不需要我们传递参数
        // 3. 事件对象 是 我们事件的一系列相关数据的集合 跟事件相关的 比如鼠标点击里面就包含了鼠标的相关信息,鼠标坐标啊,如果是键盘事件里面就包含的键盘事件的信息 比如 判断用户按下了那个键
        // 4. 这个事件对象我们可以自己命名 比如 event 、 evt、 e
        // 5. 事件对象也有兼容性问题 ie678 通过 window.event 兼容性的写法  e = e || window.event;
    </script>
</body>

4.2 事件对象的使用语法

    <script>
        eventTarget.onclick = function(event) {
        // 这个 event 就是事件对象,我们还喜欢的写成 e 或者 evt
        }
        eventTarget.addEventListener('click', function(event) {
        // 这个 event 就是事件对象,我们还喜欢的写成 e 或者 evt
        }
    </script>

 这个 event 是个形参,系统帮我们设定为事件对象,不需要传递实参过去。
当我们注册事件时, event 对象就会被系统自动创建,并依次传递给事件监听器(事件处理函数)

4.3 事件对象的兼容性方案

4.4 事件对象的常见属性和方法

 e.target 点击了那个元素,就返回那个元素

this (currentTarget)那个元素绑定了这个点击事件,那么就返回谁

    <style>
        div {
            width: 100px;
            height: 100px;
            background-color: pink;
        }
    </style>
</head>

<body>
    <div>123</div>
    <ul>
        <li>abc</li>
        <li>abc</li>
        <li>abc</li>
    </ul>
    <script>
        // 常见事件对象的属性和方法
        // 1. e.target 返回的是触发事件的对象(元素)  this 返回的是绑定事件的对象(元素)
        // 区别 : e.target 点击了那个元素,就返回那个元素 this 那个元素绑定了这个点击事件,那么就返回谁
        var div = document.querySelector('div');
        div.addEventListener('click', function(e) {
            console.log(e.target);
            console.log(this);

        })
        var ul = document.querySelector('ul');
        ul.addEventListener('click', function(e) {
                // 我们给ul 绑定了事件  那么this 就指向ul  
                console.log(this);
                console.log(e.currentTarget);

                // e.target 指向我们点击的那个对象 谁触发了这个事件 我们点击的是li e.target 指向的就是li
                console.log(e.target);

            })
            // 了解兼容性
            // div.onclick = function(e) {
            //     e = e || window.event;
            //     var target = e.target || e.srcElement;
            //     console.log(target);

        // }
        // 2. 了解 跟 this 有个非常相似的属性 currentTarget  ie678不认识
    </script>
</body>

 

<body>
    <div>123</div>
    <a href="http://www.baidu.com">百度</a>
    <form action="http://www.baidu.com">
        <input type="submit" value="提交" name="sub">
    </form>
    <script>
        // 常见事件对象的属性和方法
        // 1. 返回事件类型
        var div = document.querySelector('div');
        div.addEventListener('click', fn);
        div.addEventListener('mouseover', fn);
        div.addEventListener('mouseout', fn);

        function fn(e) {
            console.log(e.type);

        }
        // 2. 阻止默认行为(事件) 让链接不跳转 或者让提交按钮不提交
        var a = document.querySelector('a');
        a.addEventListener('click', function(e) {
                e.preventDefault(); //  dom 标准写法
            })
            // 3. 传统的注册方式
        a.onclick = function(e) {
            // 普通浏览器 e.preventDefault();  方法
            // e.preventDefault();
            // 低版本浏览器 ie678  returnValue  属性
            // e.returnValue;
            // 我们可以利用return false 也能阻止默认行为 没有兼容性问题 特点: return 后面的代码不执行了, 而且只限于传统的注册方式
            return false;
            alert(11);
        }
    </script>
</body>

5.阻止事件冒泡

5.1 阻止事件冒泡的两种方式(stopPropagation())

事件冒泡:开始时由最具体的元素接收,然后逐级向上传播到到 DOM 最顶层节点。
事件冒泡本身的特性,会带来的坏处,也会带来的好处,需要我们灵活掌握。

 标准写法:利用事件对象里面的 stopPropagation()方法

e.stopPropagation()

非标准写法:IE 6-8 利用事件对象 cancelBubble 属性

e.cancelBubble = true;
    <style>
        .father {
            overflow: hidden;
            width: 300px;
            height: 300px;
            margin: 100px auto;
            background-color: pink;
            text-align: center;
        }
        
        .son {
            width: 200px;
            height: 200px;
            margin: 50px;
            background-color: purple;
            line-height: 200px;
            color: #fff;
        }
    </style>
</head>

<body>
    <div class="father">
        <div class="son">son儿子</div>
    </div>
    <script>
        // 常见事件对象的属性和方法
        // 阻止冒泡  dom 推荐的标准 stopPropagation() 
        var son = document.querySelector('.son');
        son.addEventListener('click', function(e) {
            alert('son');
            e.stopPropagation(); // stop 停止  Propagation 传播
            e.cancelBubble = true; // 非标准 cancel 取消 bubble 泡泡
        }, false);

        var father = document.querySelector('.father');
        father.addEventListener('click', function() {
            alert('father');
        }, false);
        document.addEventListener('click', function() {
            alert('document');
        })
    </script>
</body>

5.2 阻止事件冒泡的兼容性解决方案

if(e && e.stopPropagation){
    e.stopPropagation();
}else{
    window.event.cancelBubble = true;
}

6.事件委托(代理,委派)

事件委托的原理
不是每个子节点单独设置事件监听器,
而是事件监听器设置在其父节点上,然后利用冒泡原理影响设置每个子节点。

<body>
    <ul>
        <li>知否知否,点我应有弹框在手!</li>
        <li>知否知否,点我应有弹框在手!</li>
        <li>知否知否,点我应有弹框在手!</li>
        <li>知否知否,点我应有弹框在手!</li>
        <li>知否知否,点我应有弹框在手!</li>
    </ul>
    <script>
        // 事件委托的核心原理:给父节点添加侦听器, 利用事件冒泡影响每一个子节点
        var ul = document.querySelector('ul');
        ul.addEventListener('click', function(e) {
            // alert('知否知否,点我应有弹框在手!');
            // e.target 这个可以得到我们点击的对象
            e.target.style.backgroundColor = 'pink';


        })
    </script>
</body>

7.常用的鼠标事件

7.1 常用的鼠标事件

1.禁止鼠标右键菜单---e.preventDefault()
contextmenu主要控制应该何时显示上下文菜单,主要用于程序员取消默认的上下文菜单 

document.addEventListener('contextmenu', function(e) {
        // 1. contextmenu 我们可以禁用右键菜单
    e.preventDefault();//阻止默认行为
})

2.禁止鼠标选中(selectstart 开始选中)

document.addEventListener('selectstart', function(e) {
            // 2. 禁止选中文字 selectstart
    e.preventDefault();//阻止默认行为
})

7.2 鼠标事件对象

<body>
    <script>
        // 鼠标事件对象 MouseEvent
        document.addEventListener('click', function(e) {
            // 1. client 鼠标在可视区的x和y坐标
            console.log(e.clientX);
            console.log(e.clientY);
            console.log('---------------------');

            // 2. page 鼠标在页面文档的x和y坐标
            console.log(e.pageX);
            console.log(e.pageY);
            console.log('---------------------');

            // 3. screen 鼠标在电脑屏幕的x和y坐标
            console.log(e.screenX);
            console.log(e.screenY);

        })
    </script>
</body>

 【案例: 跟随鼠标的天使】

    <style>
        img {
            position: absolute;
            top: 2px;
        }
    </style>
</head>

<body>
    <img src="images/angel.gif" alt="">
    <script>
        var pic = document.querySelector('img');
        document.addEventListener('mousemove', function(e) {
            // 1. mousemove只要我们鼠标移动1px 就会触发这个事件
            // console.log(1);
            // 2.核心原理: 每次鼠标移动,我们都会获得最新的鼠标坐标, 把这个x和y坐标做为图片的top和left 值就可以移动图片
            var x = e.pageX;
            var y = e.pageY;
            console.log('x坐标是' + x, 'y坐标是' + y);
            //3 . 千万不要忘记给left 和top 添加px 单位
            pic.style.left = x - 50 + 'px';
            pic.style.top = y - 40 + 'px';


        });
    </script>
</body>

8.常用的键盘事件

8.1 常用键盘事件

 注意:
1. 如果使用addEventListener 不需要加 on
2. onkeypress 和前面2个的区别是,它不识别功能键,比如左右箭头,shift 等。
3. 三个事件的执行顺序是: keydown -- keypress --- keyup

    <script>
        // 常用的键盘事件
        // 1. keyup 按键弹起的时候触发 
        document.onkeyup = function() {//注意这里是onkeyup
                console.log('我弹起了');

            }
        document.addEventListener('keyup', function() {//这里是keyup(不能加on)
            console.log('我弹起了');
        })

        //3. keypress 按键按下的时候触发  不能识别功能键 比如 ctrl shift 左右箭头啊
        document.addEventListener('keypress', function() {
                console.log('我按下了press');
            })
            //2. keydown 按键按下的时候触发  能识别功能键 比如 ctrl shift 左右箭头啊
        document.addEventListener('keydown', function() {
                console.log('我按下了down');
            })
            // 4. 三个事件的执行顺序  keydown -- keypress -- keyup
    </script>

 8.2 键盘事件对象(keyCode--输出对应的ASCii)

 注意:

onkeydown 和 onkeyup 不区分字母大小写,onkeypress 区分字母大小写。
在我们实际开发中,我们更多的使用keydown和keyup, 它能识别所有的键(包括功能键)
Keypress 不识别功能键,但是keyCode属性能区分大小写,返回不同的ASCII值

    <script>
        // 键盘事件对象中的keyCode属性可以得到相应键的ASCII码值
        // 1. 我们的keyup 和keydown事件不区分字母大小写  a 和 A 得到的都是65
        // 2. 我们的keypress 事件 区分字母大小写  a  97 和 A 得到的是65
        document.addEventListener('keyup', function(e) {
            // console.log(e);
            console.log('up:' + e.keyCode);
            // 我们可以利用keycode返回的ASCII码值来判断用户按下了那个键
            if (e.keyCode === 65) {
                alert('您按下的a键');
            } else {
                alert('您没有按下a键')
            }

        })
        document.addEventListener('keypress', function(e) {
            // console.log(e);
            console.log('press:' + e.keyCode);

        })
    </script>

 8.3 ASCII 表

 【案例: 模拟京东按键输入内容】

<body>
    <input type="text">
    <script>
        // 核心思路: 检测用户是否按下了s 键,如果按下s 键,就把光标定位到搜索框里面
        // 使用键盘事件对象里面的keyCode 判断用户按下的是否是s键
        // 搜索框获得焦点: 使用 js 里面的 focus() 方法
        var search = document.querySelector('input');
        //这里不使用keydown----是因为如果使用了keydowm,则会在搜索框中出现s
        document.addEventListener('keyup', function(e) {
            // console.log(e.keyCode);
            if (e.keyCode === 83) {
                search.focus();
            }
        })
    </script>
</body>

【案例: 模拟京东快递单号查询】

<head>  
 <style>
        * {
            margin: 0;
            padding: 0;
        }
        
        .search {
            position: relative;
            width: 178px;
            margin: 100px;
        }
        
        .con {
            display: none;
            position: absolute;
            top: -40px;
            width: 171px;
            border: 1px solid rgba(0, 0, 0, .2);
            box-shadow: 0 2px 4px rgba(0, 0, 0, .2);
            padding: 5px 0;
            font-size: 18px;
            line-height: 20px;
            color: #333;
        }
        
        .con::before {
            content: '';
            width: 0;
            height: 0;
            position: absolute;
            top: 28px;
            left: 18px;
            border: 8px solid #000;
            border-style: solid dashed dashed;
            border-color: #fff transparent transparent;
        }
    </style>
</head>

<body>
    <div class="search">
        <div class="con">123</div>
        <input type="text" placeholder="请输入您的快递单号" class="jd">
    </div>
    <script>
        // 快递单号输入内容时, 上面的大号字体盒子(con)显示(这里面的字号更大)
        // 表单检测用户输入: 给表单添加键盘事件
        // 同时把快递单号里面的值(value)获取过来赋值给 con盒子(innerText)做为内容
        // 如果快递单号里面内容为空,则隐藏大号字体盒子(con)盒子
        var con = document.querySelector('.con');
        var jd_input = document.querySelector('.jd');
        jd_input.addEventListener('keyup', function() {
                // console.log('输入内容啦');
                if (this.value == '') {
                    con.style.display = 'none';
                } else {
                    con.style.display = 'block';
                    //将表单的值给这个盒子
                    con.innerText = this.value;
                }
            })
            // 当我们失去焦点,就隐藏这个con盒子
        jd_input.addEventListener('blur', function() {
                con.style.display = 'none';
            })
            // 当我们获得焦点,就显示这个con盒子
        jd_input.addEventListener('focus', function() {
            if (this.value !== '') {
                con.style.display = 'block';
            }
        })
    </script>
</body>

二十五、移动端网页特效

1.触屏事件

 1.1 触屏事件概述

    <style>
        div {
            width: 100px;
            height: 100px;
            background-color: pink;
        }
    </style>
</head>

<body>
    <div></div>
    <script>
        // 1. 获取元素
        // 2. 手指触摸DOM元素事件
        var div = document.querySelector('div');
        div.addEventListener('touchstart', function() {
            console.log('我摸了你');

        });
        // 3. 手指在DOM元素身上移动事件
        div.addEventListener('touchmove', function() {
            console.log('我继续摸');

        });
        // 4. 手指离开DOM元素事件
        div.addEventListener('touchend', function() {
            console.log('轻轻的我走了');

        });
    </script>
</body>

1.2 触摸事件对象(TouchEvent)

 touchstart、touchmove、touchend 三个事件都会各自有事件对象

 

    <style>
        div {
            width: 100px;
            height: 100px;
            background-color: pink;
        }
    </style>
</head>

<body>
    <div></div>
    <script>
        // 触摸事件对象
        // 1. 获取元素
        // 2. 手指触摸DOM元素事件
        var div = document.querySelector('div');
        div.addEventListener('touchstart', function(e) {
            // console.log(e);
            // touches 正在触摸屏幕的所有手指的列表 
            // targetTouches 正在触摸当前DOM元素的手指列表
            // 如果侦听的是一个DOM元素,他们两个是一样的
            // changedTouches 手指状态发生了改变的列表 从无到有 或者 从有到无
            // 因为我们一般都是触摸元素 所以最经常使用的是 targetTouches
            console.log(e.targetTouches[0]);
            // targetTouches[0] 就可以得到正在触摸dom元素的第一个手指的相关信息比如 手指的坐标等等


        });
        // 3. 手指在DOM元素身上移动事件
        div.addEventListener('touchmove', function() {


        });
        // 4. 手指离开DOM元素事件
        div.addEventListener('touchend', function(e) {
            // console.log(e);
            // 当我们手指离开屏幕的时候,就没有了 touches 和 targetTouches 列表
            // 但是会有 changedTouches


        });
    </script>
</body>

1.3 移动端拖动元素

 

    <style>
        div {
            position: absolute;
            left: 0;
            width: 100px;
            height: 100px;
            background-color: pink;
        }
    </style>
</head>

<body>
    <div></div>
    <script>
        // (1) 触摸元素 touchstart:  获取手指初始坐标,同时获得盒子原来的位置
        // (2) 移动手指 touchmove:  计算手指的滑动距离,并且移动盒子
        // (3) 离开手指 touchend:
        var div = document.querySelector('div');
        var startX = 0; //获取手指初始坐标
        var startY = 0;
        var x = 0; //获得盒子原来的位置
        var y = 0;
        div.addEventListener('touchstart', function(e) {
            //  获取手指初始坐标
            startX = e.targetTouches[0].pageX;
            startY = e.targetTouches[0].pageY;
            //盒子原来的距离
            x = this.offsetLeft;
            y = this.offsetTop;
        });

        div.addEventListener('touchmove', function(e) {
            //  计算手指的移动距离: 手指移动之后的坐标(e.targetTouches[0].pageX)减去手指初始的坐标
            var moveX = e.targetTouches[0].pageX - startX;
            var moveY = e.targetTouches[0].pageY - startY;
            // 移动我们的盒子 盒子原来的位置 + 手指移动的距离
            this.style.left = x + moveX + 'px';
            this.style.top = y + moveY + 'px';
            e.preventDefault(); // 阻止屏幕滚动的默认行为
        });
    </script>
</body>

2.移动端常见特效

【案例:移动端轮播图】

开头要将最后一张克隆放在最前面

自动播放 

window.addEventListener('load', function() {
    // alert(1);
    // 1. 获取元素 
    var focus = document.querySelector('.focus');
    var ul = focus.children[0];
    // 获得focus 的宽度
    var w = focus.offsetWidth;
    // 2. 利用定时器自动轮播图图片
    var index = 0;
    var timer = setInterval(function() {
        index++;
        var translatex = -index * w;
        //添加过度效果
        ul.style.transition = 'all .3s';
        ul.style.transform = 'translateX(' + translatex + 'px)';
    }, 2000);
})

 自动播放--无缝连接

window.addEventListener('load', function() {
    // alert(1);
    // 1. 获取元素 
    var focus = document.querySelector('.focus');
    var ul = focus.children[0];
    // 获得focus 的宽度
    var w = focus.offsetWidth;
    var ol = focus.children[1];
    // 2. 利用定时器自动轮播图图片
    var index = 0;
    var timer = setInterval(function() {
        index++;
        var translatex = -index * w;
        ul.style.transition = 'all .3s';
        ul.style.transform = 'translateX(' + translatex + 'px)';
    }, 2000);
    // 等着我们过渡完成之后,再去判断 监听过渡完成的事件 transitionend 
    ul.addEventListener('transitionend', function() {
        // 无缝滚动
        if (index >= 3) {
            index = 0;
            // console.log(index);
            // 去掉过渡效果 这样让我们的ul 快速的跳到目标位置
            ul.style.transition = 'none';
            // 利用最新的索引号乘以宽度 去滚动图片
            var translatex = -index * w;
            ul.style.transform = 'translateX(' + translatex + 'px)';
        } else if (index < 0) {
            index = 2;
            ul.style.transition = 'none';
            // 利用最新的索引号乘以宽度 去滚动图片
            var translatex = -index * w;
            ul.style.transform = 'translateX(' + translatex + 'px)';
        }
    })
})

2.1 classList

 classList属性是HTML5新增的一个属性,返回元素的类名。但是ie10以上版本支持。
该属性用于在元素中添加,移除及切换 CSS 类。有以下方法

注意以上方法里面,所有类名都不带点

         (1)添加类: 
element.classList.add(’类名’);

focus.classList.add(‘current’);

        (2) 移除类:
element.classList.remove(’类名’);

focus.classList.remove(‘current’);

        (3)切换类:
element.classList.toggle(’类名’);

focus.classList.toggle(‘current’);
    <style>
        .bg {
            background-color: black;
        }
    </style>
</head>

<body>
    <div class="one two"></div>
    <button> 开关灯</button>
    <script>
        // classList 返回元素的类名
        var div = document.querySelector('div');
        // console.log(div.classList[1]);//拿到第2个
        // 1. 添加类名  是在后面追加类名不会覆盖以前的类名 注意前面不需要加.
        div.classList.add('three');
        // 2. 删除类名
        div.classList.remove('one');
        // 3. 切换类
        var btn = document.querySelector('button');
        btn.addEventListener('click', function() {
            //点击加上类,在点击一次则去除这个类名
            document.body.classList.toggle('bg');
        })
    </script>
</body>

【小圆点跟随变化效果】

window.addEventListener('load', function() {
    // alert(1);
    // 1. 获取元素 
    var focus = document.querySelector('.focus');
    var ul = focus.children[0];
    // 获得focus 的宽度
    var w = focus.offsetWidth;
    var ol = focus.children[1];
    // 2. 利用定时器自动轮播图图片
    var index = 0;
    var timer = setInterval(function() {
        index++;
        var translatex = -index * w;
        ul.style.transition = 'all .3s';
        ul.style.transform = 'translateX(' + translatex + 'px)';
    }, 2000);
    // 等着我们过渡完成之后,再去判断 监听过渡完成的事件 transitionend 
    ul.addEventListener('transitionend', function() {
        // 无缝滚动
        if (index >= 3) {
            index = 0;
            // console.log(index);
            // 去掉过渡效果 这样让我们的ul 快速的跳到目标位置
            ul.style.transition = 'none';
            // 利用最新的索引号乘以宽度 去滚动图片
            var translatex = -index * w;
            ul.style.transform = 'translateX(' + translatex + 'px)';
        } else if (index < 0) {
            index = 2;
            ul.style.transition = 'none';
            // 利用最新的索引号乘以宽度 去滚动图片
            var translatex = -index * w;
            ul.style.transform = 'translateX(' + translatex + 'px)';
        }
        // 3. 小圆点跟随变化
        // 把ol里面li带有current类名的选出来去掉类名 remove
        ol.querySelector('.current').classList.remove('current');
        // 让当前索引号 的小li 加上 current   add
        ol.children[index].classList.add('current');
    });
})

【手指滑动轮播图】

    // 4. 手指滑动轮播图 
    // 触摸元素 touchstart: 获取手指初始坐标
    var startX = 0;
    var moveX = 0; // 后面我们会使用这个移动距离所以要定义一个全局变量
    ul.addEventListener('touchstart', function(e) {
        startX = e.targetTouches[0].pageX;
        // 手指触摸的时候就停止定时器
        clearInterval(timer);
    });
    // 移动手指 touchmove: 计算手指的滑动距离, 并且移动盒子
    ul.addEventListener('touchmove', function(e) {
        // 计算移动距离
        moveX = e.targetTouches[0].pageX - startX;
        // 移动盒子:  盒子原来的位置 + 手指移动的距离 
        var translatex = -index * w + moveX;
        // 手指拖动的时候,不需要动画效果所以要取消过渡效果
        ul.style.transition = 'none';
        ul.style.transform = 'translateX(' + translatex + 'px)';
    });

【手指拖动播放上一张或者下一张】

    // 手指离开 根据移动距离去判断是回弹还是播放上一张下一张
    ul.addEventListener('touchend', function(e) {
            // (1) 如果移动距离大于50像素我们就播放上一张或者下一张
            //abs--取绝对值
            if (Math.abs(moveX) > 50) {
                // 如果是右滑就是 播放上一张 moveX 是正值
                if (moveX > 0) {
                    index--;
                } else {
                    // 如果是左滑就是 播放下一张 moveX 是负值
                    index++;
                }
                var translatex = -index * w;
                ul.style.transition = 'all .3s';
                ul.style.transform = 'translateX(' + translatex + 'px)';
        
            }
        }

 【回弹效果】

window.addEventListener('load', function() {
    // alert(1);
    // 1. 获取元素 
    var focus = document.querySelector('.focus');
    var ul = focus.children[0];
    // 获得focus 的宽度
    var w = focus.offsetWidth;
    var ol = focus.children[1];
    // 2. 利用定时器自动轮播图图片
    var index = 0;
    var timer = setInterval(function() {
        index++;
        var translatex = -index * w;
        ul.style.transition = 'all .3s';
        ul.style.transform = 'translateX(' + translatex + 'px)';
    }, 2000);
    // 等着我们过渡完成之后,再去判断 监听过渡完成的事件 transitionend 
    ul.addEventListener('transitionend', function() {
        // 无缝滚动
        if (index >= 3) {
            index = 0;
            // console.log(index);
            // 去掉过渡效果 这样让我们的ul 快速的跳到目标位置
            ul.style.transition = 'none';
            // 利用最新的索引号乘以宽度 去滚动图片
            var translatex = -index * w;
            ul.style.transform = 'translateX(' + translatex + 'px)';
        } else if (index < 0) {
            index = 2;
            ul.style.transition = 'none';
            // 利用最新的索引号乘以宽度 去滚动图片
            var translatex = -index * w;
            ul.style.transform = 'translateX(' + translatex + 'px)';
        }
        // 3. 小圆点跟随变化
        // 把ol里面li带有current类名的选出来去掉类名 remove
        ol.querySelector('.current').classList.remove('current');
        // 让当前索引号 的小li 加上 current   add
        ol.children[index].classList.add('current');
    });

    // 4. 手指滑动轮播图 
    // 触摸元素 touchstart: 获取手指初始坐标
    var startX = 0;
    var moveX = 0; // 后面我们会使用这个移动距离所以要定义一个全局变量
    ul.addEventListener('touchstart', function(e) {
        startX = e.targetTouches[0].pageX;
        // 手指触摸的时候就停止定时器
        clearInterval(timer);
    });
    // 移动手指 touchmove: 计算手指的滑动距离, 并且移动盒子
    ul.addEventListener('touchmove', function(e) {
        // 计算移动距离
        moveX = e.targetTouches[0].pageX - startX;
        // 移动盒子:  盒子原来的位置 + 手指移动的距离 
        var translatex = -index * w + moveX;
        // 手指拖动的时候,不需要动画效果所以要取消过渡效果
        ul.style.transition = 'none';
        ul.style.transform = 'translateX(' + translatex + 'px)';
    });
    // 手指离开 根据移动距离去判断是回弹还是播放上一张下一张
    ul.addEventListener('touchend', function(e) {
        //如果移动过了 在判断 要不然白费
        if (flag) {
            // (1) 如果移动距离大于50像素我们就播放上一张或者下一张
            //abs--取绝对值
            if (Math.abs(moveX) > 50) {
                // 如果是右滑就是 播放上一张 moveX 是正值
                if (moveX > 0) {
                    index--;
                } else {
                    // 如果是左滑就是 播放下一张 moveX 是负值
                    index++;
                }
                var translatex = -index * w;
                ul.style.transition = 'all .3s';
                ul.style.transform = 'translateX(' + translatex + 'px)';
            } else {
                // (2) 如果移动距离小于50像素我们就回弹
                var translatex = -index * w;
                ul.style.transition = 'all .1s';
                ul.style.transform = 'translateX(' + translatex + 'px)';
            }
        }
        // 手指离开的时候就重新开启定时器
        clearInterval(timer);//开新的定时器之前 先将原来的删除
        timer = setInterval(function() {
            index++;
            var translatex = -index * w;
            ul.style.transition = 'all .3s';
            ul.style.transform = 'translateX(' + translatex + 'px)';
        }, 2000);
    });
})

【 案例:返回顶部】

    // 返回顶部模块制作
    var goBack = document.querySelector('.goBack');
    var nav = document.querySelector('nav');
    window.addEventListener('scroll', function() {
        if (window.pageYOffset >= nav.offsetTop) {
            goBack.style.display = 'block';
        } else {
            goBack.style.display = 'none';
        }
    });
    //快速回到顶部
    goBack.addEventListener('click', function() {
        window.scroll(0, 0);
    })

2.2 click延时解决方案

 移动端 click 事件会有 300ms 的延时,原因是移动端屏幕双击会缩放(double tap to zoom) 页面。

1. 禁用缩放。 浏览器禁用默认的双击缩放行为并且去掉 300ms 的点击延迟。

<meta name="viewport" content="user-scalable=no">

2. 利用touch事件自己封装这个事件解决 300ms 延迟。

    <script>
        //封装tap,解决click 300ms 延时
        function tap (obj, callback) {
        var isMove = false;
        var startTime = 0; // 记录触摸时候的时间变量
        obj.addEventListener('touchstart', function (e) {
        startTime = Date.now(); // 记录触摸时间
        });
        obj.addEventListener('touchmove', function (e) {
        isMove = true; // 看看是否有滑动,有滑动算拖拽,不算点击
        });
        obj.addEventListener('touchend', function (e) {
        if (!isMove && (Date.now() - startTime) < 150) { // 如果手指触摸和离开时间小于150ms 算点击
        callback && callback(); // 执行回调函数
        }
        isMove = false; // 取反 重置
        startTime = 0;
        });
        }
        //调用
        tap(div, function(){ // 执行代码 });
    </script>

3. 使用插件。 fastclick 插件解决 300ms 延迟。

3.移动端常见开发插件

3.1 什么是插件

 3.2 插件的使用

    <!-- 引入文件 -->
    <script src="fastclick.js"></script>
</head>

<body>
    <div></div>
    <script>
        if ('addEventListener' in document) {
            //DOMContentLoaded  等DOM元素执行完在操作
            document.addEventListener('DOMContentLoaded', function() {
                FastClick.attach(document.body);
            }, false);
        }
        var div = document.querySelector('div');
        div.addEventListener('click', function() {
            alert(11);
        })
    </script>
</body>

3.3 Swiper插件的使用·

 

3.4 插件的使用总结

 

3.5 【练习--移动端视频插件】

 

4.移动端常用开发框架

4.1 框架概述

 4.2 Bootstrap(要先引入jQuery)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css">
    <script src="bootstrap/js/jquery.min.js"></script>
    <script src="bootstrap/js/bootstrap.min.js"></script>
    <style>
        .focus {
            width: 800px;
            height: 300px;
            background-color: pink;
            margin: 100px auto;
        }
        
        .carousel,
        .carousel img {
            width: 100%;
            height: 300px!important;
        }
    </style>
</head>

<body>
    <div class="focus">
        <div id="carousel-example-generic" class="carousel slide" data-ride="carousel">
            <!-- Indicators 小圆点 -->
            <ol class="carousel-indicators">
                <li data-target="#carousel-example-generic" data-slide-to="0" class="active"></li>
                <li data-target="#carousel-example-generic" data-slide-to="1"></li>
                <li data-target="#carousel-example-generic" data-slide-to="2"></li>
            </ol>

            <!-- Wrapper for slides 轮播图片 -->
            <div class="carousel-inner" role="listbox">
                <div class="item active">
                    <img src="upload/banner.dpg" alt="...">
                    <div class="carousel-caption">
                        这是我的图片1
                    </div>
                </div>
                <div class="item">
                    <img src="upload/banner1.dpg" alt="...">
                    <div class="carousel-caption">
                        这是我的图片2
                    </div>
                </div>
                <div class="item">
                    <img src="upload/banner2.dpg" alt="...">
                    <div class="carousel-caption">
                        这是我的图片3
                    </div>
                </div>
                ...
            </div>

            <!-- Controls 左右箭头 -->
            <a class="left carousel-control" href="#carousel-example-generic" role="button" data-slide="prev">
                <span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
                <span class="sr-only">Previous</span>
            </a>
            <a class="right carousel-control" href="#carousel-example-generic" role="button" data-slide="next">
                <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
                <span class="sr-only">Next</span>
            </a>
        </div>
    </div>
    <script>
        $('.carousel').carousel({
            interval: 2000
        })
    </script>
</body>

</html>

二十六、BOM 浏览器对象模型

1 BOM 概述

1.1 什么是BOM

BOM(Browser Object Model)即浏览器对象模型,它提供了独立于内容而与浏览器窗口进行交互的对象,其核心对象是 window

1.2 BOM的构成

BOM 比 DOM 更大,它包含 DOM

    <script>
        // window.document.querySelector()
        var num = 10;
        console.log(num);
        console.log(window.num);

        function fn() {
            console.log(11);

        }
        fn();
        window.fn();
        // alert(11);
        // window.alert(11)
        console.dir(window);
        // var name = 10;
        console.log(window.name);
    </script>


2 window 对象的常见事件

2.1 窗口加载事件【window.addEventListener("load",function(){})

window.onload = function(){}//以最后一个onload为主
//或者
window.addEventListener("load",function(){});//多个load都可以

window.onload 是窗口 (页面)加载事件,当文档内容完全加载完成会触发该事件(包括图像、脚本文件、CSS文件等), 就调用的处理函数。
注意:
1. 有了 window.onload 就可以把 JS 代码写到页面元素的上方,因为 onload 是等页面内容全部加载完毕,
再去执行处理函数。
2. window.onload 传统注册事件方式 只能写一次,如果有多个,会以最后一个 window.onload 为准。
3. 如果使用 addEventListener 则没有限制

document.addEventListener('DOMContentLoaded',function(){})//DOM加载完就可以开始了

DOMContentLoaded 事件触发时,仅当DOM加载完成,不包括样式表,图片,flash等等
Ie9以上才支持
如果页面的图片很多的话, 从用户访问到onload触发可能需要较长的时间, 交互效果就不能实现,必然影响用户的体验,此时用 DOMContentLoaded 事件比较合适。

    <script>
        // window.document.querySelector()
        var num = 10;
        console.log(num);
        console.log(window.num);

        function fn() {
            console.log(11);

        }
        fn();
        window.fn();
        // alert(11);
        // window.alert(11)
        console.dir(window);
        // var name = 10;
        console.log(window.name);
    </script>

2.2 调整窗口大小事件--【window.addEventListener("resize",function(){});

window.onresize = function(){}
window.addEventListener("resize",function(){});

    <style>
        div {
            width: 200px;
            height: 200px;
            background-color: pink;
        }
    </style>
</head>

<body>
    <script>
        window.addEventListener('load', function() {
            var div = document.querySelector('div');
            window.addEventListener('resize', function() {
                console.log(window.innerWidth);

                console.log('变化了');
                if (window.innerWidth <= 800) {
                    div.style.display = 'none';
                } else {
                    div.style.display = 'block';
                }

            })
        })
    </script>
    <div></div>
</body>

3 定时器

3.1 两种定时器

3.2 setTimeout()定时器【只调用一次】--【window.setTimeout(调用函数, [延迟的毫秒数]);】

setTimeout() 方法用于设置一个定时器,该定时器在定时器到期后执行调用函数。


注意:


1. window 可以省略。
2. 这个调用函数可以
直接写函数,或者写函数名或者采取字符串‘函数名()'三种形式。第三种不推荐
3. 延迟的毫秒数省略默认是 0,如果写,必须是毫秒。
4. 因为定时器可能有很多,所以我们经常给定时器赋值一个标识符。

    <script>
        // 1. setTimeout 
        // 语法规范:  window.setTimeout(调用函数, 延时时间);
        // 1. 这个window在调用的时候可以省略
        // 2. 这个延时时间单位是毫秒 但是可以省略,如果省略默认的是0
        // 3. 这个调用函数可以直接写函数 还可以写 函数名 还有一个写法 '函数名()'
        // 4. 页面中可能有很多的定时器,我们经常给定时器加标识符 (名字)
        //方法一:
        setTimeout(function() {
            console.log('时间到了');

        }, 2000);
        //方法二:
        function callback() {
            console.log('爆炸了');

        }
        var timer1 = setTimeout(callback, 3000);
        var timer2 = setTimeout(callback, 5000);
        // setTimeout('callback()', 3000); // 我们不提倡这个写法
    </script>

【案例: 5秒后自动关闭的广告】

<body>
    <img src="images/ad.jpg" alt="" class="ad">
    <script>
        var ad = document.querySelector('.ad');
        setTimeout(function() {
            ad.style.display = 'none';
        }, 5000);
    </script>
</body>

3.3 停止(清除)setTimeout()定时器--【window.clearTimeout(定时器的名字)

clearTimeout()方法取消了先前通过调用 setTimeout() 建立的定时器。
注意:
1. window 可以省略。
2. 里面的参数就是定时器的标识符 。

<body>
    <button>点击停止定时器</button>
    <script>
        var btn = document.querySelector('button');
        var timer = setTimeout(function() {
            console.log('爆炸了');

        }, 5000);
        btn.addEventListener('click', function() {
            clearTimeout(timer);
        })
    </script>
</body>

3.4 setInterval()定时器【反复调用】--【window.setInterval(回调函数, [间隔的毫秒数]);】

    <script>
        // 1. setInterval 
        // 语法规范:  window.setInterval(调用函数, 延时时间);
        setInterval(function() {
            console.log('继续输出');

        }, 1000);
        // 2. setTimeout  延时时间到了,就去调用这个回调函数,只调用一次 就结束了这个定时器
        // 3. setInterval  每隔这个延时时间,就去调用这个回调函数,会调用很多次,重复调用这个函数
    </script>

【案例: 倒计时】

<head>  
  <style>
        div {
            margin: 200px;
        }
        
        span {
            display: inline-block;
            width: 40px;
            height: 40px;
            background-color: #333;
            font-size: 20px;
            color: #fff;
            text-align: center;
            line-height: 40px;
        }
    </style>
</head>

<body>
    <div>
        <span class="hour">1</span>
        <span class="minute">2</span>
        <span class="second">3</span>
    </div>
    <script>
        // 1. 获取元素 
        var hour = document.querySelector('.hour'); // 小时的黑色盒子
        var minute = document.querySelector('.minute'); // 分钟的黑色盒子
        var second = document.querySelector('.second'); // 秒数的黑色盒子
        var inputTime = +new Date('2019-5-1 18:00:00'); // 返回的是用户输入时间总的毫秒数
        countDown(); // 我们先调用一次这个函数,防止第一次刷新页面有空白 
        // 2. 开启定时器
        setInterval(countDown, 1000);

        function countDown() {
            var nowTime = +new Date(); // 返回的是当前时间总的毫秒数
            var times = (inputTime - nowTime) / 1000; // times是剩余时间总的秒数 
            var h = parseInt(times / 60 / 60 % 24); //时
            h = h < 10 ? '0' + h : h;
            hour.innerHTML = h; // 把剩余的小时给 小时黑色盒子
            var m = parseInt(times / 60 % 60); // 分
            m = m < 10 ? '0' + m : m;
            minute.innerHTML = m;
            var s = parseInt(times % 60); // 当前的秒
            s = s < 10 ? '0' + s : s;
            second.innerHTML = s;
        }
    </script>
</body>

3.5 停止(清除)setInterval()定时器--【window.clearTimeout(定时器的名字)

 

<body>
    <button class="begin">开启定时器</button>
    <button class="stop">停止定时器</button>
    <script>
        var begin = document.querySelector('.begin');
        var stop = document.querySelector('.stop');
        //一定要将timer写在外面,要不然后面的停止定时器无法使用她
        var timer = null; // 全局变量  null是一个空对象
        begin.addEventListener('click', function() {
            timer = setInterval(function() {
                console.log('ni hao ma');

            }, 1000);
        })
        stop.addEventListener('click', function() {
            clearInterval(timer);
        })
    </script>
</body>

 【案例: 发送短信】

<body>
    手机号码: <input type="number"> <button>发送</button>
    <script>
        // 按钮点击之后,会禁用 disabled 为true 
        // 同时按钮里面的内容会变化, 注意 button 里面的内容通过 innerHTML修改
        // 里面秒数是有变化的,因此需要用到定时器
        // 定义一个变量,在定时器里面,不断递减
        // 如果变量为0 说明到了时间,我们需要停止定时器,并且复原按钮初始状态
        var btn = document.querySelector('button');
        var time = 3; // 定义剩下的秒数
        btn.addEventListener('click', function() {
            btn.disabled = true;
            var timer = setInterval(function() {
                if (time == 0) {
                    // 清除定时器和复原按钮
                    clearInterval(timer);
                    btn.disabled = false;
                    btn.innerHTML = '发送';
                } else {
                    btn.innerHTML = '还剩下' + time + '秒';
                    time--;
                }
            }, 1000);

        })
    </script>
</body>

3.6 this

 现阶段,我们先了解一下几个this指向
1. 全局作用域或者普通函数中this指向全局对象window(注意定时器里面的this指向window)
2. 方法调用中谁调用this指向谁
3.构造函数中this指向构造函数的实例

<body>
    <button>点击</button>
    <script>
        // this 指向问题 一般情况下this的最终指向的是那个调用它的对象

        // 1. 全局作用域或者普通函数中this指向全局对象window( 注意定时器里面的this指向window)
        console.log(this);

        function fn() {
            console.log(this);

        }
        window.fn();
        window.setTimeout(function() {
            console.log(this);

        }, 1000);
        // 2. 方法调用中谁调用this指向谁
        var o = {
            sayHi: function() {
                console.log(this); // this指向的是 o 这个对象

            }
        }
        o.sayHi();
        var btn = document.querySelector('button');
        // btn.onclick = function() {
        //     console.log(this); // this指向的是btn这个按钮对象

        // }
        btn.addEventListener('click', function() {
                console.log(this); // this指向的是btn这个按钮对象

            })
            // 3. 构造函数中this指向构造函数的实例
        function Fun() {
            console.log(this); // this 指向的是fun 实例对象

        }
        var fun = new Fun();
    </script>
</body>

 【时钟】


4 JS 执行机制

4.1 JS是单线程

    <script>
        console.log(1);
        setTimeout(function () {
        console.log(3);
        }, 1000);
        console.log(2);
        //输出:1-->2--->3
    </script>

4.2 同步(程序的执行顺序与任务的排列顺序是一致的)与异步

    <script>
        console.log(1);
        setTimeout(function () {
        console.log(3);
        }, 0);
        console.log(2)
        //输出 1-->2-->3
    </script>

4. 3 JS执行机制

4.4 JS执行机制

  <script>
        console.log(1);
        document.onclick = function() {
        console.log('click');
        }
        console.log(2);
        setTimeout(function() {
        console.log(3)
        }, 3000)
        //如果点击了鼠标(在3秒钟之前点击),输出顺序:1--->2--->click--->3
</script>


5 location 对象 

5.1 什么是location对象

window 对象给我们提供了一个 location 属性用于获取或设置窗体的 URL,并且可以用于解析 URL 。 因为这个属性返回的是一个对象,所以我们将这个属性也称为 location 对象。

5.2 URL

5.3 location对象的属性

【案例: 5秒钟之后自动跳转页面】

<body>
    <button>点击</button>
    <div></div>
    <script>
        var btn = document.querySelector('button');
        var div = document.querySelector('div');
        btn.addEventListener('click', function() {
            // console.log(location.href);
            location.href = 'http://www.itcast.cn';
        })
        var timer = 5;
        setInterval(function() {
            if (timer == 0) {
                location.href = 'http://www.itcast.cn';
            } else {
                div.innerHTML = '您将在' + timer + '秒钟之后跳转到首页';
                timer--;
            }

        }, 1000);
    </script>
</body>

【案例: 获取 URL 参数数据】

<body>
    <form action="index.html">
        用户名: <input type="text" name="uname">
        <input type="submit" value="登录">
    </form>
</body>
<body>
    <div></div>
    <script>
        //先拿到参数
        console.log(location.search); // ?uname=andy
        // 1.先去掉?  substr('起始的位置',截取几个字符);
        var params = location.search.substr(1); // uname=andy
        console.log(params);
        // 2. 利用=把字符串分割为数组 split('=');
        var arr = params.split('=');
        console.log(arr); // ["uname", "ANDY"]
        var div = document.querySelector('div');
        // 3.把数据写入div中
        div.innerHTML = arr[1] + '欢迎您';
    </script>
</body>

5.4 location对象的方法

<body>
    <button>点击</button>
    <script>
        var btn = document.querySelector('button');
        btn.addEventListener('click', function() {
            // 记录浏览历史,所以可以实现后退功能
            // location.assign('http://www.itcast.cn');
            // 不记录浏览历史,所以不可以实现后退功能
            // location.replace('http://www.itcast.cn');
            location.reload(true);
        })
    </script>
</body>

6 navigator 对象

navigator 对象包含有关浏览器的信息,它有很多属性,我们最常用的是 userAgent,该属性可以返回由客户机发送服务器的 user-agent 头部的值。下面前端代码可以判断用户那个终端打开页面,实现跳转

    <script>
        if((navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|
        Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS
        |Symbian|Windows Phone)/i))) {
        window.location.href = ""; //手机
        } else {
        window.location.href = ""; //电脑
        }
    </script>
<body>
    <!-- 顶部 -->
    <header class="app">
        <ul>
            <li>
                <img src="images/close.png" alt="">
            </li>
            <li>
                <img src="images/logo.png" alt="">
            </li>
            <li>打开京东App,购物更轻松</li>
            <li>立即打开</li>
        </ul>
    </header>
    <!-- 搜索 -->
    <div class="search-wrap">
        <div class="search-btn"></div>
        <div class="search">
            <div class="jd-icon"></div>
            <div class="sou"></div>
        </div>
        <div class="search-login">登陆</div>
    </div>
    <!-- 主体内容部分 -->
    <div class="main-content">
        <!-- 滑动图 -->
        <div class="slider">
            <img src="upload/banner.dpg" alt="">
        </div>

        <!-- 小家电品牌日 -->
        <div class="brand">
            <div>
                <a href="#">
                    <img src="upload/pic11.dpg" alt="">
                </a>
            </div>
            <div>
                <a href="#">
                    <img src="upload/pic22.dpg" alt="">
                </a>

            </div>
            <div>
                <a href="#">
                    <img src="upload/pic33.dpg" alt="">
                </a>

            </div>
        </div>
        <!-- nav部分 -->
        <nav class="clearfix">
            <a href="">
                <img src="upload/nav1.webp" alt="">
                <span>京东超市</span>
            </a>

            <a href="">
                <img src="upload/nav2.webp" alt="">
                <span>京东超市</span>
            </a>

            <a href="">
                <img src="upload/nav1.webp" alt="">
                <span>京东超市</span>
            </a>

            <a href="">
                <img src="upload/nav1.webp" alt="">
                <span>京东超市</span>
            </a>

            <a href="">
                <img src="upload/nav1.webp" alt="">
                <span>京东超市</span>
            </a>

            <a href="">
                <img src="upload/nav3.webp" alt="">
                <span>京东超市</span>
            </a>

            <a href="">
                <img src="upload/nav1.webp" alt="">
                <span>京东超市</span>
            </a>

            <a href="">
                <img src="upload/nav1.webp" alt="">
                <span>京东超市</span>
            </a>

            <a href="">
                <img src="upload/nav1.webp" alt="">
                <span>京东超市</span>
            </a>

            <a href="">
                <img src="upload/nav1.webp" alt="">
                <span>京东超市</span>
            </a>

        </nav>
        <!-- 新闻模块 -->
        <div class="news">
            <a href="#">
                <img src="upload/new1.dpg" alt="">
            </a>
            <a href="#">
                <img src="upload/new2.dpg" alt="">

            </a>
            <a href="#">
                <img src="upload/new3.dpg" alt="">

            </a>
        </div>
    </div>
</body>


7 history 对象

 window 对象给我们提供了一个 history 对象,与浏览器历史记录进行交互。该对象包含用户(在浏览器窗口中)访问过的 URL。

 back() 

<body>
    <a href="index.html">点击我去往首页</a>
    <button>后退</button>
    <script>
        var btn = document.querySelector('button');
        btn.addEventListener('click', function() {
            // history.back();
            //go(-1)--表示后退
            history.go(-1);
        })
    </script>
</body>

forward()

<body>
    <a href="list.html">点击我去往列表页</a>
    <button>前进</button>
    <script>
        var btn = document.querySelector('button');
        btn.addEventListener('click', function() {
            // history.forward();
            //go(1)--表示前进
            history.go(1);
        })
    </script>
</body>

<body>
    <a href="list.html">点击我去往列表页</a>
    <button>前进</button>
    <script>
        var btn = document.querySelector('button');
        btn.addEventListener('click', function() {
            // history.forward();
            history.go(1);
        })
    </script>
</body>
<body>
    <a href="index.html">点击我去往首页</a>
    <button>后退</button>
    <script>
        var btn = document.querySelector('button');
        btn.addEventListener('click', function() {
            // history.back();
            history.go(-1);
        })
    </script>
</body>

二十七、本地存储

1 window.sessionStorage(数据存储在浏览器中的)--重新打开就没有了

<body>
    <input type="text">
    <button class="set">存储数据</button>
    <button class="get">获取数据</button>
    <button class="remove">删除数据</button>
    <button class="del">清空所有数据</button>
    <script>
        console.log(localStorage.getItem('username'));

        var ipt = document.querySelector('input');
        var set = document.querySelector('.set');
        var get = document.querySelector('.get');
        var remove = document.querySelector('.remove');
        var del = document.querySelector('.del');
        set.addEventListener('click', function() {
            // 当我们点击了之后,就可以把表单里面的值存储起来
            var val = ipt.value;
            sessionStorage.setItem('uname', val);
            sessionStorage.setItem('pwd', val);
        });
        get.addEventListener('click', function() {
            // 当我们点击了之后,就可以把表单里面的值获取过来
            console.log(sessionStorage.getItem('uname'));

        });
        remove.addEventListener('click', function() {
            // 
            sessionStorage.removeItem('uname');

        });
        del.addEventListener('click', function() {
            // 当我们点击了之后,清除所有的
            sessionStorage.clear();

        });
    </script>
</body>

存储数据:

sessionStorage.setItem(名字, 值)


获取数据:

sessionStorage.getItem(key)


删除数据:

sessionStorage.removeItem(key

删除所有的数据:

sessionStorage.clear(key)


2 window.localStorage--(关闭页面数据还在)

<body>
    <input type="text">
    <button class="set">存储数据</button>
    <button class="get">获取数据</button>
    <button class="remove">删除数据</button>
    <button class="del">清空所有数据</button>
    <script>
        var ipt = document.querySelector('input');
        var set = document.querySelector('.set');
        var get = document.querySelector('.get');
        var remove = document.querySelector('.remove');
        var del = document.querySelector('.del');
        set.addEventListener('click', function() {
            var val = ipt.value;
            localStorage.setItem('username', val);
        })
        get.addEventListener('click', function() {
            console.log(localStorage.getItem('username'));

        })
        remove.addEventListener('click', function() {
            localStorage.removeItem('username');

        })
        del.addEventListener('click', function() {
            localStorage.clear();

        })
    </script>
</body>

 

存储数据:

localStorage.setItem(key, value)


获取数据:

localStorage.getItem(key)



删除数据:

localStorage.removeItem(key)

删除所有数据:
 

localStorage.clear()

【案例:记住用户名】

<body>
    <input type="text" id="username"> <input type="checkbox" name="" id="remember"> 记住用户名
    <script>
        var username = document.querySelector('#username');
        var remember = document.querySelector('#remember');
        //先判断浏览器中是否有这个数据
        if (localStorage.getItem('username')) {
            //如果存在数据 则赋值给value
            username.value = localStorage.getItem('username');
            //让复选框勾选上
            remember.checked = true;
        }
        //如果复选框是未被选中 则不记得
        remember.addEventListener('change', function() {
            if (this.checked) {
                localStorage.setItem('username', username.value)
            } else {
                localStorage.removeItem('username');
            }
        })
    </script>
</body>

  • 21
    点赞
  • 170
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值