计算机基础
区分:
- 编程语言
- 编程语言和标记语言的不同
- 常见的数据存储单位及其换算关系
- 内存的主要作用以及特点
①编程: 让计算机为解决某个问题而使用某种程序设计语言编写程序代码,并最终得到结果的过程。
②计算机程序: 就是计算机所执行的一系列的指令集合,而程序全部都是用我们所掌握的语言来编写的,所以人们要控制计算机一定要通过计算机语言向计算机发出命令。
1️⃣编程语言
1、计算机语言
2、编程语言
可以通过类似于人类语言的“语言”来控制计算机 ,让计算机为我们做事情,这样的语言就叫做编程语言( Programming Language) 。
编程语言是用来控制计算机的一系列指令,它有固定的格式和词汇(不同编程语言的格式和词汇不一样) , 必须遵守。
如今通用的编程语言有两种形式: 汇编语言和高级语言。
●汇编语言和机器语言实质是相同的,都是直接对硬件操作,只不过指令采用了英文缩写的标识符,容易识别和记忆。
●高级语言主要是相对于低级语言而言,它并不是特指某一种具体的语言,而是包括了很多编程语言,常用的有C语言、C++、Java、 C#、 Python、 PHP、JavaScript、 Go语言、Objective-C、 Swift等。例如:
- C语言: puts ("你好") ;
- PHP : echo "你好";
- Java : System. out .println ("你好");
- JavaScript : alert ("你好") ;
3、翻译器
4、编程语言和标记语言区别
●编程语言有很强的逻辑和行为能力。在编程语言里你会看到很多if else、for 、while等 具有逻辑性和行为能力的
指令,这是主动的。
●标记语言( html )不用于向计算机发出指令,常用于格式化和链接。标记语言的存在是用来被读取的,他是被动的。
⭕总结
1.计算机可以帮助人类解决某些问题;
2.程序员利用编程语言编写程序发出指令控制计算机来实现这些任务;
3.编程语言有机器语言、汇编语言、高级语言;
4.高级语言需要一个翻译器转换为计算机识别的机器语言;
5.编程语言是主动的有很强的逻辑性。
2️⃣计算机组成
(1)数据存储
1.计算机内部使用二进制0和1来表示数据。
2.所有数据,包括文件、图片等最终都是以二进制数据( 0和1 )的形式存放在硬盘中的。
3.所有程序,包括操作统,本质都是各种数据,也以二进制数据的形式存放在硬盘中。平时我们所说的安装软件,其实就是把程序文件复制到硬盘中。
4.硬盘、内存都是保存的二进制数据。
(2)数据存储单位
bit < byte < kb < GB < TB .....
●位(bit): 1bit 可以保存一个0 或者1 ( 最小的存储单位)
●字节(Byte): 1B= 8b ;
●千字节(KB): 1KB= 1024B ;
●兆字节(MB) : 1MB = 1024KB ;
●吉字节(GB): 1GB = 1024MB ;
●太字节(TB): 1TB= 1024GB
JavaScript简介
学习任务:
- 能够说出JavaScript是什么
- 能够知道JavaScript的发展历史
- 能够说出浏览器执行JavaScript的原理
- 能够说出JavaScript由哪三部分组成
- 能够写出JavaScript三个输入输出语句
(1)JS简介:
●JavaScript 是世界上最流行的语言之一, 是一 种运行在客户端的脚本语言( Script是脚本的意思);
●脚本语言: 不需要编译,运行过程中由JS解释器(JS引擎)逐行来进行解释并执行;
●现在也可以基于 Node.js 技术进行服务器端编程。
(2)JavaScript的作用
●表单动态校验(密码强度检测) ( JS 产生最初的目的)
●网页特效
●服务端开发(Node.js)
●桌面程序(Electron)
●App(Cordova)
●控制硬件物联网(Ruff)
●游戏开发(cocos2d-js)
(3)HTML/CSS/JS的联系
(4)游览器执行JS简介
●渲染引擎:用来解析HTML与CSS ,俗称内核,比如chrome浏览器的blink , 老版本的webkit
●JS引擎:也称为JS解释器。 用来读取网页中的JavaScript代码,对其处理后运行, 比如chrome浏览器的V8
浏览器本身并不会执行JS代码, 而是通过内置JavaScript引擎(解释器)来执行JS代码。JS引擎执行代码时逐行解释每一句源码(转换为机器语言) , 然后由计算机去执行,所以JavaScript语言归为脚本语言,会逐行解释执行。
JS的组成
(1)ECMAScript
ECMAScript 是一种标准规范,用来描述 JavaScript 等脚本语言的基本语法、数据类型、操作符、函数定义、控制流程等方面的语言特性,它为开发人员提供了一个统一的语言标准,使得编写跨平台的代码变得更加容易和高效。
ES6 中新增了箭头函数、let 和 const 声明等重要的新特性。
(2) DOM-文档对象模型
文档对象模型( Document ObjectModel ,简称DOM) , 是W3C组织推荐的处理可扩展标记语言的标准编程接口。
通过DOM提供的接口可以对页面上的各种元素进行操作(大小位置、颜色等) 。
DOM 将文档表示为节点树,其中每个节点都是文档中的元素、属性、文本或注释等不同类型的组件。节点树中的所有节点都是对象,它们具有一些公共的属性和方法,可以使用 JavaScript 来操作它们。例如,开发者可以使用 DOM 去查找或创建节点、改变节点的属性或文本内容、添加或删除节点等等。
HTML 和 XML 文档中的每个元素都成为了 DOM 中的一个节点,这些节点之间的关系构成了一棵树形结构。根据节点之间的父子关系,整个文档被表示为一个 DOM 树,可以使用 JavaScript 访问树中的任何一个节点,并且可以通过修改节点的属性和方法来实现对文档的交互操作,例如表单验证、动态内容生成和响应用户事件等等。
(3)BOM一浏览器对象模型
BOM (Browser Object Model ,简称BOM) 是指浏览器对象模型, 它提供了独立于内容的、可以与浏览器窗口进行
互动的对象结构。通过BOM可以操作浏览器窗口,比如弹出框、控制浏览器跳转、获取分辨率等。
BOM 的核心对象包括:
- Window:表示浏览器窗口,并提供了许多与窗口操作相关的方法和属性,例如打开一个新窗口、修改窗口的大小和位置等。
- Location:表示当前窗口的 URL 地址,并提供了许多与地址栏操作相关的方法和属性,例如读取或修改当前 URL、重定向到其他网页等。
- Navigator:表示浏览器的信息,并提供了许多与浏览器版本、插件和平台相关的信息,例如浏览器的名称、版本、插件信息等。
- History:表示浏览器的历史记录,并提供了一些与浏览器导航相关的方法和属性,例如回退、前进等。
- Screen:表示客户端显示屏幕的信息,并提供了关于屏幕分辨率、颜色等信息的属性。
JS入门
JS有3种书写位置, 分别为 行内、内嵌和外部。
(1)行内式JS
<input type="button" value="点我试试" onclick="alert ('Hello World')" />
●可以将单行或少量JS代码写在HTML标签的事件属性中(以on开头的属性) , 如: onclick;
●注意单双引号的使用: 在HTML中我们推荐使用双引号, JS中我们推荐使用单引号;
●可读性差, 在html中编写JS大量代码时, 不仿便阅读;
●引号易错,引号多层嵌套匹配时,非常容易弄混;
●特殊情况下使用
(2)内嵌JS
<script>
alert( 'Hello World~!') ;
</script>
●可以将多行JS代码写到<script>标签中
●内嵌JS是学习时常用的方式
(3)外部JS文件
<script src= "my. js"> </script>
●利于HTML页面代码结构化,把大段JS代码独立到HTML页面之外, 既美观, 也方便文件级别的复用
●引用外部JS文件的script标签中间不可以写代码
●适合于JS代码量比较大的情况
JS注释
1.单行注释
ctrl+/
2.多行注释
默认的快捷键 shift +alt+a
vscode中修改多行注释的快捷键: ctrl + shift + /
输入输出语句
为了方便信息的输入输出, JS中提供了一些输入输出语句,其常用的语句如下:
方法 | 说明 | 归属 |
alert(msg) | 浏览器弹出警示框 | 浏览器 |
console.log(msg) | 浏览器控制台打印输出信息 | 浏览器 |
prompt(info) | 浏览器弹出输入框,用户可以输入 | 浏览器 |
变量
学习任务:
- 能够说出变量的主要作用
- 能够写出变量的初始化
- 能够说出变量的命名规范
- 能够画出变量是如何在内存中存储的
- 能够写出交换变量案例
章节学习内容:
- 变量概述
- 变量的使用
- 变量语法扩展
- 变量命名规范
- 交换变量案例
变量概述及使用
变量就是一个装东西的盒子。
通俗: 变量是用于存放数据的容器。我们通过变量名获取数据, 甚至数据可以修改。
变量在内存中的存储
变量的使用
变量在使用时分为两步:
1.声明变量
2. 赋值
1.声明变量
//声明变量
var age;
//声明一个名称为age的变量
●var 是一个JS关键字, 用来声明变量( variable变量的意思)。使用该关键字声明变量后, 计算机会自动为变分配内存空间, 不需要程序员管;
●age是程序员定义的变量名, 我们要通过变量名来访问内存中分配的空间。
2.赋值
age = 10;
//给age这个变量赋值为10
● = 用来把右边的值赋给左边的变量空间中此处代表赋值的意思
●变量值是程序员保存到变量空间里的值
控制台输出 console.log(age)
3.变量的初始化
var age = 18;
// 声明变量同时赋值为18
声明一个变量并赋值,我们称之为变量的初始化。
✍🏻案例:变量的使用
1.弹出一个输入框,提示用户输入姓名。
2.弹出一一个对话框,输出用户刚才输入的姓名。
// 1.用户输入姓名 存储到一个myname的变量里面
var myname = prompt('请输入您的名字');
// 2.输出这个用户名
alert ( myname );
变量语法扩展
1.更新变量
一个变量被重新复赋值后,它原有的值就会被覆盖,变量值将以最后一次赋的值为准。
var age = 18;
age = 81;
//最后的结果就是81 因为18被覆盖掉了
2.同时声明多个变量
同时声明多个变量时,只需要写一个var ,多个变量名之间使用英文逗号隔开。
var age = 10, name='ZS', sex = 2;
3.声明变量特殊情况
情况 | 说明 | 结果 |
var age; console.log (age); | 只声明,不赋值 | undefined |
console.log(age) | 不声明,不赋值,直接使用 | 报错 |
age = 10; console.log (age); | 不声明,只赋值 | 10 |
变量命名规范
●字母(A-Za-z)、数字(0-9)、 下划线(_)、元符号($ )组成,如: usrAge, num01,_ name
●严格区分大小写。var app; 和var App; 是两个变量
●不能以数字开头。18age 是错误的
●不能是关键字、保留字。例如: var、 for、 while
●变量名必须有意义。MMD BBD n|-→ age
●遵守驼峰命名法。首字母小写,后面单词的首字母需要大写。myFirstName
●推荐翻译网站:有道 爱词霸
name有一些游览器含有特殊含义,尽量不要直接使用name作为变量名
交换两个变量的值
JS是编程语言有很强的逻辑性在里面: 实现这个要求的思路先怎么做后怎么做
小结
●为什么需要变量?
●因为我们一些数据需要保存,所以需要变量
●变量是什么?
●变量就是一个容器,用来存放数据的。方便我们以后使用里面的数据
●变量的本质是什么?
●变量是内存里的一块空间,用来存储数据。
●变量怎么使用的?
●我们使用变量的时候, 一定要声明变量,然后赋值
●声明变量本质是去内存申请空间。
●什么是变量的初始化?
●声明变量并赋值我们称之为变量的初始化
●变量命名规范有哪些?
●变量名尽量要规范,见名知意——驼峰命名法
●交换2个变量值的思路?
●区分哪些变量名不合法
●学会交换2个变量
数据类型
学习任务:
能够说出5种简单数据类型
能够使用 typeof 获取变量的类型
能够说出1~2种转换为数值型的方法
能够说出1~2种转换为字符型的方法
能够说出什么是隐式转换
1.为什么需要数据类型
在计算机中,不同的数据所需占用的存储空间是不同的, 为了便于把数据分成所需内存大小不同的数据,充分利用存储空间,于是定义了不同的数据类型。——数据的类型型号
2.变量的数据类型
变量是用来存储值的所在处, 它们有名字和数据类型。变量的数据类型决定了如何将代表这些值的位存储到计算机的内存中。JavaScript 是一种弱类型或者说动态语言。 这意味着不用提前声明变量的类型,在程序运行过程中, 类型会被自动确定。
var age = 10;
//这是一一个数字型
var areYouok = '是的';
// 这是一个字符串
在代码运行时,变量的数据类型是由JS引擎 根据 = 右边变量值的数据类型来判断的, 运行完毕之后,变量就确定了数据类型。
即:JS的变量数据类型是只有程序在运行过程中,根据等号右边的值来确定的。
JavaScript拥有动态类型,同时也意味着相同的变量可用作不同的数据类型:
var X = 6; //x为数字
var X = "Bill"; //x为字符串
3.数据类型的分类
JS把数据类型分为两类: 原始类型和引用类型。
①原始类型指的是简单的数据段,包括数字(Number)、字符串(String)、布尔值(Boolean)、null、undefined 和 Symbol(ES6 新增)。这些数据类型的值都是不可变的,也就是说,当我们对一个原始类型的变量进行赋值时,实际上是将新的值复制到了该变量中,而原来的值并没有被改变。
②引用类型指的是由多个值构成的对象,每个对象都是引用类型的实例,包括数组(Array)、函数(Function)、对象(Object)等等。与原始类型不同的是,引用类型的值是可变的,即我们可以改变对象中的属性或方法,并且在进行赋值的时候,实际上只是将该对象的引用地址复制到变量中,而没有真正复制对象本身。
简单数据类型( Number, string, Boolean, Undefined, Null )
简单数据类型(基本数据类型)
JavaScript中的简单数据类型及其说明如下:
简单数据类型 | 说明 | 默认值 |
Number | 数字型,包含 整型值和浮点型值,如 21、0.21 | 0 |
Boolean | 布尔值类型,如 true 、false,等价于1和0 | false |
String | 字符串类型,如“张三” 注意咱们is 里面,字符串都带引号 | " " |
Undefined | var a; 声明了变量 a 但是没有给值,此时 a = undefined | undefined |
Null | var a= null; 声明了变量 a 为空值 | null |
(1)数字型Number
JavaScript数字类型既可以用来保存整数值,也可以保存小数(浮点数)。
var age = 21; //整数
var Age = 21.3747; // 小数
1️⃣ 数字型进制
最常见的进制有二进制、八进制、十进制、十六进制。
// 1.八进制数字序列范围: 0~7
var num1 = 07; //对应十进制的7
var num2 = 019; //对应十进制的19
var num3 = 08; //对应十进制的8
// 2.十六进制数字序列范围: 0~9以及A~F
var num = 0xA;
现阶段我们只需要记住,在JS中八进制前面加0 ,十六进制前面加0x
2️⃣数字型范围
JavaScript中数值的最大和最小值
alert (Number .MAX VALUE); // 1. 7976931348623157e+308
alert (Number .MIN VALUE); // 5e-324
3️⃣数字型三个特殊值
alert (Infinity); // Infinity
alert(-Infinity); // - Infinity
alert (NaN) ; // NaN
●Infinity ,代表无穷大,大于任何数值
●-Infinity ,代表无穷小,小于任何数值
●NaN,Not a number,代表一个非数值
isNaN( )
isNaN( ) 这个方法用来判断非数字,并且返回一个值,如果是数字返回的是false,如果不是数字返回的是true。
(2)字符串型String
字符串型可以是引号中的任意文本,其语法为双引号 " " 和单引号 ' '
var strMsg = "我爱北京天安门~"; // 使用双引号表示字符串
var strMsg2 ='我爱吃猪蹄~'; // 使用单引号表示字符串
//常见错误
var strMsg3 =我爱大肘子; //报错,没使用引号,会被认为是js代码,但j s没有这些语法
因为HTML标签里面的属性使用的是双引号, JS更推荐使用单引号。
1️⃣字符串引号嵌套
JS可以用单引号嵌套双引号, 或者用双引号嵌套单引号(外双内单,外单内双)。
var strMsg = '我是"高帅富"程序猿'; // 可以用,,包含”
var strMsg2 = "我是'高帅富'程序猿"; // 也可以用"包含"
//常见错误
var badQuotes = 'What on earth?';
//报错,不能单双引号搭配
2️⃣字符串转义符
类似HTML里面的特殊字符,字符串中也有特殊字符,我们称之为转义符。(而不是用< br / >)
转义符都是 \ 开头的,常用的转义符及其说明如下:
转义符 | 解释说明 |
\n | 换行符,n是 newline 的意思 |
\ \ | 斜杠\ |
\ | ‘ 单引号 |
\* | " 双引号 |
\t | tab 缩进 |
\b | 空格,b是 blank 的意思 |
字符串转义字符都是用 \ 开头,但是这些转义字符写到引号里面。
案例✍🏻:弹出网页警示框⭕
3️⃣字符串长度
字符串是由若干字符组成的,这些字符的数量就是字符串的长度。通过字符串的length属性可以获取整个字符串的长度。
var strMsg = "我是帅气多金的程序猿! ";
alert (strMsg. length); //显示11
4️⃣字符串拼接
●多个字符串之间可以使用 + 进行拼接,其拼接方式 字符串 + 任何类型 = 拼接之后的新字符串
●拼接前会把与字符串相加的任何类型转成字符串,再拼接成一个新的字符串
+ 号总结口诀 : 数值相加,字符相连
//1.1字符串"相加"
alert('hello' + ' ' + 'world'); // hello world
//1.2数值字符串"相加"
alert('100' + '100') ; // 100100
//1.3数值字符串+数值
alert('11' + 12) ;// 1112
字符串的拼接 + 只要有字符串和其他类型相拼接,最终的结果是字符串类型。
变量不要写到字符串里面,是通过和 字符串 相连的方式实现的。
5️⃣字符串拼接加强
console.log ('pink老师' + 18) ; //只要有字符就会相连
var age = 18;
// console. log('pink老师age岁啦' );
//这样不行哦
console. log ('pink老师' + age) ;
// pink老师18
console. log('pink老师' + age + '岁啦'); // pink老师18岁啦
●我们经常会将字符串和变量来拼接,因为变量可以很方便地修改里面的值;
●变量是不能添加引号的,因为加引号的变量会变成字符串;
●如果变量两侧都有字符串拼接, 口诀“引引加加”, 删掉数字, 变量写加中间。
案例✍🏻:显示年龄⭕
交互编程的三个基本要素:
1.你喜欢我吗?一这是用户输入
2.女孩想了想一这是程序内部处理
3.最后给了你 - 巴掌一这是输出结果
在程序中要如何实现?
①弹出一个输入框( prompt) , 让用户输入年龄(用户输入)
②把用户输入的值用变量保存起来, 把刚才输入的年龄与所要输出的字符串拼接( 程序内部处理)
③使用alert语句弹出警示框(输出结果)
(3)布尔型Boolean
布尔类型有两个值: true和false, 其中true示真(对), 而false示假(错)。
布尔型和数字型相加的时候,true 的值为1 , false 的值为0。
console.log(true + 1) ; // 2
console.log(false + 1); // 1
(4)Undefined和Null
一个声明后没有被赋值的变量会有一个默认值undefined (如果进行相连或者相加时, 注意结果)
var variable;
console.log (variable) ; // undefined
console.log('你好' + variable); // 你好undefined
console.log(variable + ' pink'); // undefinedpink
console.log(11 + variable) ;// NaN
console.log(true + variable) ;//NaN
一个声明变量给null值,里面存的值为空。
var var1 = null;
console.log('你好' + var1); // 你好null
console.log(var1 + 'pink'); // nullpink
console.log(11 + var1) ;// 11
console.log(true + var1) ;// 1
获取检测变量的数据类型
(1)typeof 可用来获取检测变量的数据类型
prompt取过来的值是字符型的
(2)字面量
字面量是在源代码中一个固定值的表示法,通俗来说,就是字面量表示如何表达这个值。
●数字字面量: 8,9,10 在控制台为蓝色
●字符串字面量: '黑马程序员', "大前端" 黑色
●布尔字面量: true , false 深蓝色
数据类型转换
使用 表单、prompt 获取过来的数据默认是字符串类型的,此时就不能直接简单的进行加法运算,而需要转换变量的数据类型。通俗来说,就是把一种数据类型的变量转换成另外一种数据类型。
通常会实现3种方式的转换:
(1)转换为字符串类型 toString:
方式 | 说明 | 案例 |
toString( ) | 转成字符串 | var num= 1; alert(num.toString()): |
String( ) 强制转换 | 转成字符串 | var num = 1; alert(String(num)): |
加号拼接字符串 | 和字符串拼接的结果都是字符串 | var num = 1; alert(num + "我是字符串"): |
三种转换方式,我们更喜欢用第三种加号拼接字符串转换方式,这一种方式也称之为隐式转换。
(2)转换为数字型
方式 | 说明 | 案例 |
parselnt(string)函数 | 将string类型转成整数数值型 | parselnt('78') |
parseFloat(string)函数 | 将string类型转成浮点数数值型 | parseFloat('78.21') |
Number0) 强制转换函数 | 将string类型转换为数值型 | Number('12') |
js 隐式转换(- * /) | 利用算术运算隐式转换为数值型 | '12' -0 |
parseInt(没有进位的取整情况,会去掉单位)
⭕注意 parselnt 和 parseFloat 单词的大小写, 这2个是重点。
案例✍🏻①:计算年龄⭕
实现思路:
①弹出一个输入框( prompt) ,让用户输入出生年份(用户输入)
②把用户输入的值用变量保存起来,然后用今年的年份减去变量值,结果就是现在的年龄( 程序内部处理)
③弹出警示框( alert),把计算的结果输出(输出结果)
var year = prompt( '请您输入您的出生年份');
var age = 2022 - year;
// year取过来的是字符串型 但是这里用的减法有隐式转换
alert('您今年已经' +age +岁了');
案例✍🏻②:简单加法器⭕
实现思路:
①先弹出第一个输入框, 提示用户输入第一个值保存起来
②再弹出第二个框, 提示用户输入第二一个值保存起来
③把这两个值相加,并将结果赋给新的变量(注意 字符串——> 数据类型转换)
④弹出警示框( alert),把计算的结果输出( 输出结果)
(3)转换为布尔型 Boolean( )函数
解释型语言和编译型语言
1. 概述
计算机不能直接理解任何除机器语言以外的语言,所以必须要把程序员所写的程序语言翻译成机器语言才能执行程序。程序语言翻译成机器语言的工具,被称为翻译器。
2.执行过程
类似于请客吃饭:
●编译语言: 首先把所有菜做好,才能上桌吃饭
●解释语言: 好比吃火锅,边吃边涮,同时进行
标识符、关键字、保留字
1.标识符
标识(zhi)符: 就是指开发人员为变量、属性、函数、参数取的名字。
标识符不能是关键字或保留字。
2.关键字
关键字: 是指JS本身已经使用了的字, 不能再用它们充当变量名、方法名。
包括: break、case、 catch、 continue、 default、 delete、 do、 else、 finally、 for、 function、 if、 in、instanceof、new、 return、 switch、 this、 throw. try、 typeof、 var、 void. while、 with等。
3.保留字
保留字: 实际上就是预留的“关键字”, 意思是现在虽然还不是关键字, 但是未来可能会成为关键字, 同样不能使用它们当变量名或方法名。
包括: boolean、byte、 char、 class、 const、 debugger. double、 enum. export、 extends、fimal、float、 goto、implements、 import、 int、 interface、 long、 mative、package、private、protected、public、short、 static、 super、 synchronized、 throws、 transient、volatile等。
运算符
①算数运算符
概念: 算术运算使用的符号,用于执行两个变量或值的算术运算。
运算符 | 描述 | 实例 |
+ | 加 | 10 + 20 = 30 |
- | 减 | 10-20 =-10 |
* | 乘 | 10* 20 = 200 |
/ | 除 | 10 / 20 = 0.5 |
% | 取余数(取模) | 返回除法的余数 9 % 2 = 1 |
浮点数算术运算里面会有问题(小数已经很小了,而且还需要转换为二进制,让二进制再进行算术运算,因此会产生误差)。
浮点数的精度问题
浮点数值的最高精度是17位小数,但在进行算术计算时其精确度远远不如整数。
var result = 0.1 + 0.2;
//结果不是0.3 , 而是: 0.30000000000000004
console.log(0.07*100);
//结果不是7,而是: 7.000000000000001
所以:不要直接判断两个浮点数是否相等!
1.怎么判断一个数能够被整除呢?
它的余数是0就说明这个数能被整除,这就是 %取余运算符 的主要用途。
2.请问1+2*3结果是?
结果是7 , 注意算术运算符优先级的, 先乘除, 后加减, 有小括号先算小括号里面的。
表达式和返回值
表达式: 是由数字、运算符、变量等 以能求得数值的有意义排列方法所得的组合
简单理解: 是由数字、运算符、变量等组成的式子
// 是由数字、运算符、变量等组成的式子我们称为表达式 1+1
console.log(1 + 1); // 2就是返回值
//1+1=2
//而在程序里面 2=1+1 把我们的右边表达式计算完毕把返回值给左边
var num = 1+1;
②递增和递减运算符
如果需要反复给数字变量添加或减去1 ,可以使用递增( ++ )和递减( -- )运算符来完成。
在JavaScript中,递增( ++ )和递减( -- )既可以放在变量前面,也可以放在变量后面。放在变量前面时,我们可以称为前置递增(递减)运算符,放在变量后面时,我们可以称为后置递增(递减)运算符。
注意:递增和递减运算符必须和变量配合使用。
1.前置递增运算符
+ +num前置递增,就是自加1 ,类似于num=num+1,但是++num写起来更简单。
使用口诀:先自加,后返回值
2.后置递增运算符
num++后置递增,就是自加1 , 类似于num=num+1,但是num++写起来更简单。
使用口诀:先返回原值,后自加
1.前置自增和后置自增如果单独使用效果是一样的
2.后置自增口诀: 先返回原值后自加1
var e = 10;
var f = e++ + ++e;
//1. e++ = 10 ——> e = 11 2.e = 12 <—— ++e = 12
console.1og(f); // 22
//后置自增 先表达式返回原值后面变量再自加1
前置递增和后置递增运算符可以简化代码的编写, `让变量的值+ 1比以前写法更简单
单独使用时,运行结果相同
与其他代码联用时,执行结果会不同
后置: 先原值运算,后自加(先人后己)
前置: 先自加,后运算(先已后人)
开发时, 大多使用后置递增/减,并且代码独占-行,例如: num+ +;或者num--;
③比较运算符
概念:比较运算符(关系运算符)是两个数据进行比较时所使用的运算符, 比较运算后, 会返回一个布尔值。
( true/false )作为比较运算的结果。
程序里面的等于符号是 == 默认转换 数据类型会把字符串型的数据转换为数字型, 只要求值相等就可以。
程序里面有全等一模样要求两侧的值 还有数据类型完全一 致才可以true。
console.log(18 === 18) ;
console.1og(18 === '18'); // false
符号 | 作用 | 作用 |
= | 赋值 | 把右边给左边 |
== | 判断 | 判断两边值是否相等 (注意此时有隐式转换) |
=== | 全等 | 判断两边的值和数据类型是否完全相同 |
④逻辑运算符
概念: 逻辑运算符是用来进行布尔值运算的运算符,其返回值也是布尔值。后面开发中经常用于多个条件的判断。
逻辑运算符 | 说明 | 案例 |
&& | "逻辑与”,简称"与” and | true && false |
|| | "逻辑或”,简称“或” or | true || false |
! | "逻辑非”,简称“非”not | ! true |
(1)逻辑与 && 即 and 两侧都为true,结果才是 true。只要有一 侧为false 结果就为false。
(2)逻辑或 || 即 or 两侧都为false,结果才是假 false。只要有一侧为true,结果就是true。
用我们的布尔值参与的逻辑运算 true && false == false
123 && 456 是值 或者是 表达式 参与逻辑运算?
短路运算(逻辑中断)
短路运算的原理: 当有多个表达式(值)时, 左边的表达式值可以确定结果时, 就不再继续运算右边的表达式的值;
1.逻辑与 &&
●语法:表达式1 &&表达式2
●如果第一个表达式的值为真, 则返回表达式2
●如果第一个表达式的值为假, 则返回表达式1
console.1og(123 && 456); // 456
console.1og(0 && 456); //0
如果有空的或者否定的为假(null undefined NaN),其余是真的
逻辑中断(短路操作)
2.逻辑或 ||
●语法: 表达式1 || 表达式2
●如果第一个表达式的值为真,则返回表达式1
●如果第一个表达式的值为假,则返回表达式2
console.log( 123 || 456 ) ; // 123
console.log( 0 || 456); // 456
console.log(123 || 456 || 789); //123
var num = 0;
console.log(123 || num++) ;
console.log (num) ;
⑤赋值运算符
概念: 用来把数据赋值给变量的运算符。
赋值运算符 | 说明 | 案例 |
= | 直接赋值 | var usrName ='我是值'; |
+=、-= | 加、减 一个数 后在赋值 | var age = 10; age+=5; // 15 |
*=、/=、%= | 乘、除、取模 后在赋值 | var age = 2; age *=5; // 10 |
var age = 10;
age += 5;//相当于age=age+5;
age -= 5;//相当于age=age-5;
age *=10;// 相当于age=age *10;
运算符优先级
一元运算符里面的逻辑非优先级很高
逻辑与比逻辑或优先级高
console.log( 4>= 6 || ' 人' != '阿凡达 '&& !(12*2==144) && true )
var num = 10;
console.log( 5 == num /2 && (2 +2 * num) . toString() === '22') ;
流程控制-分支
学习任务:
- 能够使用if分支语句
- 能够使用switch分支语句
- 能够使用三元表达式
流程控制
在一个程序执行的过程中, 各条代码的执行顺序对程序的结果是有直接影响的。很多时候我们要通过控制代码的执行顺序来实现我们要完成的功能。
简单理解: 流程控制就是来控制我们的代码按照什么结构顺序来执行
流程控制主要有三种结构,分别是顺序结构、分支结构 和 循环结构, 这三种结构代表三种代码执行的顺序。
顺序流程控制
顺序结构是程序中最简单、最基本的流程控制, 它没有特定的语法结构, 程序会按照代码的先后顺序,依次执行, 程序中大多数的代码都是这样执行的。
分支流程控制 if语句
分支结构
由上到下执行代码的过程中,根据不同的条件,执行不同的路径代码(执行代码多选一的过程 ) , 从而得到不同的结果
JS语言提供了两种分支结构语句:
●if语句
●switch 语句
// 1. if的语法结构 如果 if
if (条件表达式) {
// 执行语句
}
// 2.执行思路 如果 if里面的条件表达式结果为真true 则执行大括号里面的执行语句
//如果 if 条件表达式 结果为假,则不执行大括号里面的语句,则执行 if语句 后面的代码
语句可以理解为一个行为, 循环语句和分支语句就是典型的语句。一个程序由很多个语句组成, 一般情况下,会分割成一个一个的语句。
执行过程
✍🏻案例:进入网吧
弹出一个输入框,要求用户输入年龄,如果年龄大于等于18岁,允许进网吧。
①弹出prompt输入框,用户输入年龄,程序把这个值取过来保存到变量中
②使用if语句来判断年龄,如果年龄大于18就执行if大括号里面的输出语句
// 弹出输入框
var age = window.prompt("请输入您的年龄");
// 判断年龄是否大于等于18岁
if (age >= 18) {
console.log("您已经成年,可以进入网吧");
} else {
console.log("您还未成年,不能进入网吧");
}
if else 语句(双分支语句)
1.语法结构
//条件成立执行if里面代码,否则执行else里面的代码
if (条件表达式) {
// [如果]条件成立执行的代码
} else {
// [否则]执行的代码
}
2.执行思路
3.代码验证
✍🏻案例:判断闰年
接收用户输入的年份,如果是闰年就弹出闰年,否则弹出是平年。
算法分析
①算法:能被4整除且不能整除100的为闰年(如2004年就是闰年, 1901年不是闰年)或者能够被400整除的就是闰年
②弹出prompt 输入框,让用户输入年份,把这个值取过来保存到变量中
③使用if语句来判断是否是闰年,如果是闰年,就执行if大括号里面的输出语句,否则就执行else里面的输出语句
④一定要注意里面的 且&& 还有 或者|| 的写法,同时注意判断整除的方法是取余为0
var year = prompt('请您输入年份: ' );
if( year % 4==0 && year % 100 != 0 || year % 400==0 ){
alert('您输入的年份是闰年');
} else {
alert('您输入的年份是平年');
}
if else if语句(多分支语句)
1.语法结构
//适合于检查多重条件。
if(条件表达式1) {
语句1 ;
} else if (条件表达式2) {
语句2 ;
} else if (条件表达式3) {
语句3 ;
} else {
//上述条件都不成立执行此处代码
}
2.执行流程
3.⭕注意点:
(1) 多分支语句还是多选一 最后只能有一个语句执行
(2) else if里面的条件理论上是可以任意多个的
(3) else if中间有个空格了
✍🏻 案例:判断成绩级别
要求: 接收用户输入的分数,根据分数输出对应的等级字母A、B、C、D、E。
其中:
1.90分(含)以上,输出: A
2.80分(含)~ 90分(不含) ,输出: B
3. 70分(含)~ 80分(不含) ,输出: C
4.60分(含)~ 70分(不含) ,输出: D
5.60分(不含)以下,输出: E
思路(伪代码):
①按照从大到小判断的思路
②弹出prompt输入框,让用户输入分数,把这个值取过来保存到变量中
③使用多分支if else if语句来分别判断输出不同的值
// 弹出输入框
var score = window.prompt("请输入您的分数");
// 将分数转化为数字类型
score = Number(score);
// 判断分数所对应的等级并输出
if (score >= 90) {
console.log("您的分数为" + score + ",等级为A");
} else if (score >= 80 && score < 90) {
console.log("您的分数为" + score + ",等级为B");
} else if (score >= 70 && score < 80) {
console.log("您的分数为" + score + ",等级为C");
} else if (score >= 60 && score < 70) {
console.log("您的分数为" + score + ",等级为D");
} else {
console.log("您的分数为" + score + ",等级为E");
}
三元表达式
1.有三元运算符组成的式子我们称为三元表达式
2. ++num(一元表达式);3+5(二元表达式); ?:
3.语法结构
条件表达式?表达式1 : 表达式2
4.执行思路
如果条件表达式结果为真则返回表达式1的值如果条件表达式结果为假则返回表达式2的值
✍🏻 案例:数字补0
用户输入数字,如果数字小于10 ,则在前面补0 , 比如01 , 09 , 如果数字大于10,则不需要补,比如20.
案例分析
①用户输入0~59之间的一个数字
②如果数字小于10 ,则在这个数字前面补0, (加0)否则不做操作
③用一个变量接受这个返回值,输出
- 字符串拼接(不能是数值型,否则会加上)
var result = time<10? '0' + time : time; //把返回值赋值给一个变量
alert(result);
分交流程控制switch语句
语法结构
switch语句也是多分支语句,它用于基于不同的条件来执行不同的代码。当要针对变量设置一系列的特定值的选项时,就可以使用switch。
switch转换、开关case小例子或者选项的意思。
执行思路
利用我们的表达式的值和case 后面的选项值相匹配。如果匹配上,就执行该case里面的语句如果 都没有匹配上,那么执行default 里面的语句
⭕注意:
- 开发里面表达式 经常写成变量
- num的值和case 里面的值相匹配的时候是全等,必须是值和数据类型致才可以num === 1
- break如果当前的case里面没有break,则不会退出switch,是继续执行下一个case
✍🏻案例:查询水果
用户在弹出框里面输入一个水果,如果有就弹出该水果的价格,如果没有该水果就弹出“没有此水果”。
①弹出prompt输入框,让用户输入水果名称,把这个值取过来保存到变量中。
②将这个变量作为switch括号里面的表达式。
③case 后面的值写几个不同的水果名称,注意-定要加引号, 因为必须是全等匹配。
④弹出不同价格即可。同样注意每个case之后加上break , 以便退出switch语句。
⑤将default设置为没有此水果。
// 创建水果价格表对象
var fruitPrices = {
苹果: 5,
香蕉: 3,
葡萄: 8,
草莓: 12,
橙子: 4,
};
// 弹出输入框
var fruitName = window.prompt("请输入您要查询的水果名称");
// 使用 switch 语句查找水果价格并输出结果
switch (fruitName) {
case "苹果":
console.log(fruitName + "的价格为" + fruitPrices[fruitName] + "元/斤");
break;
case "香蕉":
console.log(fruitName + "的价格为" + fruitPrices[fruitName] + "元/斤");
break;
case "葡萄":
console.log(fruitName + "的价格为" + fruitPrices[fruitName] + "元/斤");
break;
case "草莓":
console.log(fruitName + "的价格为" + fruitPrices[fruitName] + "元/斤");
break;
case "橙子":
console.log(fruitName + "的价格为" + fruitPrices[fruitName] + "元/斤");
break;
default:
console.log("没有此水果");
}
switch语句和if else if语句的区别
①一般情况下,它们两个语句可以相互替换
②swith...case 语句通常处理case为比较确定值的情况,而if..else.. .语句更加灵活,常用于范围判断(大于、等于某个范围)
③switch 语句进行条件判断后直接执行到程序的条件语句,效率更高。而f.. else语句有几种条件,就得判断多少次。
④当分支比较少时, if.. else语句的执行效率比switch语句高。
⑤当分支比较多时, switch语句的执行效率比较高,而且结构更清晰。
流程控制-循环
学习任务:
- 能够说出循环的目的
- 能够说出for循环的执行过程
- 能够使用断点调试来观察代码的执行过程
- 能够使用for循环完成累加求和等案例
- 能够使用双重for循环完成乘法表案例
- 能够说出while循环和do while循环的区别
- 能够说出break和continue的区别
1️⃣for循环
在程序中,一组被重复执行的语句被称之为循环体,能否继续重复执行,取决于循环的终止条件。由循环体及循环的终止条件组成的语句,被称之为循环语句。
// 1. for重复执行某些代码, 通常跟计数有关系
// 2. for语法结构
for (初始化变量;条件表达式;操作表达式) {
//循环体
}
// 3.初始化变量:用var声明的一个普通变量,通常用于作为计数器使用
// 4.条件表达式:用来决定每一次循环是否继续执行就是终止的条件
//5.操作表达式是每次循环最后执行的代码经常用于我们计数器变量进行更新(递增或者递减)
1.首先执行里面的计数器变量var i=1.但是这句话在for里面只执行一次 index
2.去i <= 100 来判断是否满足条件, 如果满足条件 就去执行循环体不满足条件退出循环
3.最后去执行1++ i++是单独写的代码递增第一轮结束
4.接着去执行i<=100如果满足条件就去执行循环体不满足条件退出循环 第二轮。
断点调试:
断点调试是指自己在程序的某一行设置一个断点 ,调试时,程序运行到这一行就会停住,然后你可以一步一步往 下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码行即显示错误,停下。
断点调试可以帮我们观察程序的运行过程
浏览器中按F12--> sources -->找到需要调试的文件-->在程序的某-行设置断点
Watch: 监视,通过watch可以监视变量的值的变化,非常的常用。
F11:程序单步执行,让程序- -行行的执行,这个时候,观察watch中变量的值的变化。
for循环执行相同的代码
for(vari=1;i<=100;i++){
if(i==1){
console.1og( '这个人今年1岁了,他出生了');
}else if(i==100){
console.log('这个人今年100岁了,他死了');
} else {
console.log('这个人今年’+ i + 岁了');
}
}
for循环重复某些相同操作
for循环因为有了计数器的存在,我们还可以重复的执行某些操作,比如做一些算术运算。
✍🏻案例:累加和
案例分析:
①需要循环100次,我们需要一个计数器i
②我们需要一个存储结果的变量sum , 但是初始值一定是0
③核心算法:1+2+3+4....,sum = sum+i;
1.求1-100之间所有数的平均值 需要一个 sum 和的变量 还需要个平均值 average 变量
var sum = 0;
var average = 0;
for(var i=1;i<=100;i++){
sum=sum+i;
}
average = sum / 100;
console .log(average);
2.求1-100之间所有偶数和奇数的和 我们需要一个偶数的和变量even 还需要一个奇数odd
var even = 0;
var odd = 0;
for(vari=1;i<=100;i++){
if(i % 2==0){
even=even+1;
} else {
odd=odd+i;
}
}
3.求1-100之间所有能被3整除的数字的和
var result = 0;
for (var i =1;i<=100;i++){
if(i%3==0){
result = result + i;
}
}
console.1og( '1~100之间能够被3整数的数字的和是:' +result)
✍🏻案例:求学生成绩
要求用户输入班级人数,之后依次输入每个学生的成绩,最后打印出该班级总的成绩以及平均成绩。
案例分析:
①弹出输入框输入总的班级人数(num )
②依次输入学生的成绩(保存起来score ) ,此时我们需要用到for循环,弹出的次数跟班级总人数有关系,条件表达式 i <= num
③进行业务处理:计算成绩。先求总成绩( sum) , 之后求平均成绩( average )
④弹出结果
⭕prompt取到的为字符型的需要转换类型 parseFloat
// 弹出输入框,获取班级人数
var studentCount = window.prompt("请输入班级人数");
// 创建一个数组,用来保存每个学生的成绩
var scores = [];
// 循环遍历数组,依次输入每个学生的成绩
for (var i = 0; i < studentCount; i++) {
var score = window.prompt("请输入第" + (i + 1) + "个学生的成绩");
scores.push(parseFloat(score)); // 将成绩转换为浮点数,并添加到数组中
}
// 计算班级总成绩和平均成绩
var totalScore = 0;
for (var i = 0; i < studentCount; i++) {
totalScore += scores[i];
}
var averageScore = totalScore / studentCount;
// 输出结果
console.log("该班级总成绩为:" + totalScore);
console.log("该班级平均成绩为:" + averageScore);
一行打印五个星星🌟
我们采取追加字符串的方式,这样可以打印到控制台上。
2️⃣双重for循环
很多情况下,单层for循环并不能满足我们的需求,比如我们要打印一个5行5列的图形、打印一个倒直角三角形等,此时就可以通过循环嵌套来实现。
循环嵌套是指在一个循环语句中再定义一个循环语句的语法结构,例如在for循环语句中,可以再嵌套一个for循环,这样的for循环语句我们称之为双重for循环。
我们可以把里面的循环看做是外层循环的语句
外层循环循环一次,里面的循环执行全部。
✍🏻案例:打印五行五列星星
图解思路:
核心:
1.内层循环负责一行打印五个星星
2.外层循环负责打印五行
换行 为 +'\n'
// 使用两个嵌套的 for 循环打印星星
for(var i=0; i<5; i++) {
var row = ''; // 用来存储每一行的星星
for(var j=0; j<5; j++) {
row += '🌟'; // 将星星添加到行字符串中
}
console.log(row); // 输出该行的星星
}
使用了两个嵌套的for循环语句,其中外层循环i控制行数,内层循环j控制每行星星的数量。在内层循环中,我们用一个空字符串row来存储每一行的星星,然后在每次循环中将*号添加到row字符串中。当内层循环执行完毕后,使用console.log方法输出该行字符串,即完成了一行星星的打印。最终,当外层循环执行完毕后,就完成了五行五列星星的打印。
打印n行n列的星星
要求用户输入行数和列数, 之后在控制台打印出用户输入行数和列数的星星。
// 弹出输入框,获取用户输入的行、列数
var n = window.prompt("请输入您要打印的行、列数:");
// 使用两个嵌套的 for 循环打印星星
for(var i=0; i<n; i++) {
var row = ''; // 用来存储每一行的星星
for(var j=0; j<n; j++) {
row += '🌟'; // 将星星添加到行字符串中
}
console.log(row); // 输出该行的星星
}
✍🏻案例:打印倒三角形
①一共有10行,但是每行的星星个数不一样,因此需要用到双重for循环
②外层的for控制行数i , 循环10次可以打印10行
③内层的for控制每行的星星个数j
var str = ' ' ;
for(var i =1;i<=10;i++){//外层循环控制行数
for (var j =i; j<= 10; j++) { //里层循环打印的个数不一样
str = str +'★';
}
str += '\n' ;
}
console.1og(str);
打印正三角形
✍🏻 案例:九九乘法表
案例解析:
①一共有9行,但是每行的个数不一样,因此需要用到双重for循环
②外层的for循环控制行数i , 循环9次,可以打印9行
③内层的for循环控制每行公式j
④核心算法:每一行公式的个数正好和行数一致, j<=i;
⑤每行打印完毕,都需要重新换一行
var str =' ';
for(var i=1;i<=9;i++){//外层循环控制行数
for(var j=1;j<=i;j++){//里层循环控制每一行的个数j<=i
//1x2= 2
//str=str+'★';
str+= j+ 'x' +i+ '=' + i*j +'\t';
}
str +=' \n' ;
}
console.log(str);
小结:
●for 循环可以重复执行某些相同代码
●for 循环可以重复执行些许不同的代码,因为我们有计数器
●for 循环可以重复执行某些操作,比如算术运算符加法操作
●随着需求增加,双重for循环可以做更多、更好看的效果
●双重for循环,外层循环一次,内层for循环全部执行
●for 循环是循环条件和数字直接相关的循环
3️⃣do while循环
弹出一个提示框,你爱我吗?如果输入我爱你,就提示结束,否则,-直询问。
①弹出输入框,要求用户输入。
②判断条件比较复杂我们使用while循环。
③while 循环语句中的条件表达式只要输入的不是我爱你,就一直循环。
var message = prompt( '你爱我吗?');
while (message !== '我爱你') {
message = prompt( '你爱我吗?');
}
do... while语句其实是while语句的一个变体。该循环会先执行一次代码块 ,然后对条件表达式进行判断,如果条件为真,就会重复执行循环体,临则退出循环。
do {
//循环体代码-条件表达式为true时 重复执行循环体代码
} while (条件表达式) ;
执行思路:
①先执行一次循环体代码
②再执行条件表达式,如果结果为true, 则继续执行循环体代码,如果为false ,则退出循环,继续执行后面代码
⭕注意: 先再执行循环体,再判断, 我们会发现do...while循环语句至少
小结
●JS 中循环有for、while、 do while
●三个循环很多情况下都可以相互替代使用
●如果是用来计次数,跟数字相关的,三者使用基本相同,但是我们更喜欢用for
●while 和do..while可以做更复杂的判断条件,比for循环灵活- -些
●while 和d..while执行顺序不一样, while先判断后执行,do..while先执行一次,再判断执行
●while 和do...while执行次数不-样, do...while至少会执行一次循环体,而while可能一次也不执行
●实际工作中,我们更常用for循环语句,它写法更简洁直观,所以这个要重点学习
4️⃣continue break
continue 关键字用于立即跳出本次循环,继续下一次循环(本次循环体中continue之后的代码就会少执行一次)。
var sum = 0;
for(var i=1;i<=100;i++){
if(i%7 == 0){
continue ;
}
sum += i;
}
break关键字用于立即跳出整个循环(循环结束)。
命名规范以及语法格式
能够知道JS中命名规范规则
能够书写规范的JS代码
标识符命名规范
- 变量、函数的命名必须要有意义
- 变量的名称一般用名词
- 函数的名称一般用动词
变量名通常采用驼峰命名法(CamelCase),第一个单词首字母小写,后面每个单词的首字母大写,如
firstName
、lastName
等。函数名也采用驼峰命名法,但第一个单词首字母应大写,如
getUserName()
、calculateSum()
等。常量的命名通常采用全大写的形式,并用下划线将多个单词连接起来,如
MAX_LENGTH
、PI
等。构造函数的名称应该以大写字母开头,以便与普通函数区分开来,如
Person()
、Car()
等。对象属性的命名通常采用和变量名相同的驼峰命名法,如
person.firstName
、car.model
等。类名的命名通常采用大驼峰命名法(PascalCase),即每个单词的首字母都大写,如
PersonClass
、CarModel
等。私有属性和方法的命名通常以下划线“_”开头,如
_privateVar
、_privateMethod()
等。变量和函数名应当具有描述性,尽量体现出变量、函数的用途和作用。
操作符规范
//操作符的左右两侧各保留一个空格
for(var i=1;i<=5;i++){
if(i==3) {
break; //直接退出整个for循环,跳到整个for循环下面的语句
}
console. log( '我正在吃第' + i + '个包子呢') ;
}
单行注释规范
for(var i=1;i<=5;i++){
if (i==3) {
break; //单行注释前面注意有个空格
}
console. log('我正在吃第'+ i + '个包子呢') ;
}
其他规范
✍🏻 案例:简易ATM
- 里面现存有100块钱
- 如果存钱,就用输入钱数加上先存的钱数之后弹出显示余额提示框
- 如果取钱,就减去取的钱数,之后弹出显示余额提示框
- 如果显示余额,就输出余额如果退出,弹出退出信息提示框
var balance = 100; // 初始余额为100元
// 存款函数
function deposit(amount) {
balance += amount;
alert('存入' + amount + '元成功,当前余额为' + balance + '元');
}
// 取款函数
function withdraw(amount) {
if (amount <= balance) {
balance -= amount;
alert('取出' + amount + '元成功,当前余额为' + balance + '元');
} else {
alert('余额不足,无法取款');
}
}
// 查看余额函数
function checkBalance() {
alert('当前余额为' + balance + '元');
}
// 退出函数
function exitATM() {
alert('欢迎下次使用!');
}
// ATM机主程序
while (true) {
var option = prompt('请选择操作(1.存款,2.取款,3.查看余额,4.退出)');
switch (option) {
case '1':
var amount = parseFloat(prompt('请输入存款金额:'));
if (!isNaN(amount)) {
deposit(amount);
} else {
alert('输入金额不合法,请重新输入。');
}
break;
case '2':
var amount = parseFloat(prompt('请输入取款金额:'));
if (!isNaN(amount)) {
withdraw(amount);
} else {
alert('输入金额不合法,请重新输入。');
}
break;
case '3':
checkBalance();
break;
case '4':
exitATM();
return;
default:
alert('输入选项不合法,请重新输入。');
}
}
数组
学习任务:
- 能够知道为什么要有数组
- 能够创建数组
- 能够获取数组中的元素
- 能够对数组进行遍历
- 能够给数组新增一个元素
- 能够独立完成冒泡排序的案例
1️⃣数组
问: 之前学习的变量,只能存储一个值。 如果我们想存储班级中所有学生的姓名,那么该如何存储呢?
答: 可以使用数组(Array)。数组可以把一组相关的数据一起存放,并提供方便的访问(获取)方式。
问:什么是数组呢?
答: 数组是指一组数据的集合,其中的每个数据被称作元素,在数组中可以存放任意类型的元素。数组是一种将一组数据存储在单个变量名下的优雅方式。
创建数组
1.利用new创建数组
var 数组名=new Array( ) ;
var arr = new Array ( ) ;
//创建一个新的空数组
● 注意Array( ), A要大写
2.利用数组字面量创建数组
//1.使用数组字面量方式创建空的数组
var 数组名=[ ];
//2.使用数组字面量方式创建带初始值的数组
var 数组名= ['小白', '小黑','大黄','瑞奇'];
数组的数据一定要用逗号隔开
数组的字面量是方括号[ ]
声明数组并赋值称为数组的初始化
这种字面量方式也是我们以后最多使用的方式
数组元素的类型
数组中可以存放任意类型的数据,例如字符串,数字,布尔值等。
2️⃣获取数组中的元素
数组的索引
索引(下标) : 用来访问数组元素的序号(数组下标 / 索引号从0开始)。
数组可以通过索引来访问、设置、修改对应的数组元素,我们可以通过“数组名[索引]”的形式来获取数组中的元素。
这里的访问就是获取得到的意思。
//定义数组
var arrStus =[1,2,3];
//获取数组中的第2个元素N
alert (arrStus [1]) ;
3️⃣遍历数组
问: 数组中的每一项我们怎么取出来?
答:可以通过“数组名[索引号]” 的方式一项项的取出来。
var arr = ['red' ,'green', 'blue'];
console.log(arr[0]) // red
console. log (arr[1]) // green
console. log(arr[2]) // blue
问:怎么把数组里面的元素全部取出来?
规律:
从代码中我们可以发现,从数组中取出每一个元素时 ,代码是重复的,有所不一样的是索引值在递增
答案就是循环。
// 遍历数组: 就是把数组的元素从头到尾访问一次
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计数器当索引号来用
数组的长度
使用“数组名.length"可以访问数组元素的数量(数组长度)。
- 数组的长度是元素个数,不要跟索引号混淆
- arr. length动态监测数组元素的个数
✍🏻案例:数组求和及平均值
例:求数组[2,6,1,7, 4]里面所有元素的和以及平均值。
案例分析:
①声明一个求和变量sum。
②遍历这个数组,把里面每个数组元素加到sum里面。(使用 JavaScript 的数组方法 reduce() 来求数组的和)
③用求和变量sum除以数组的长度就可以得到数组的平均值。
加的是数组元素arr[i] ,不是计数器i
console.log(sum, average); //想要输出多个变量,用逗号分隔即可
var arr = [2, 6, 1, 7, 4];
// 求数组的和
var sum = arr.reduce(function(a, b) {
return a + b;
});
// 求数组的平均值
var avg = sum / arr.length;
console.log('数组元素之和为:' + sum);
console.log('数组元素平均值为:' + avg);
✍🏻案例: 数组最大值
例:求数组[2,6,1,77,52,25,7]中的最大值。
案例分析
①声明一个保存最大元素的变量max。
②默认最大值可以取数组中的第一个元素。
③遍历这个数组,把里面每个数组元素和max相比较。
④如果这个数组元素大于max就把这个数组元素存到max里面,否则继续下一轮比较。
⑤最后输出这个max。
var arr = [2, 6, 1, 77, 52, 25, 7];
// 求数组中的最大值
var max = Math.max.apply(null, arr);
console.log('数组中的最大值为:' + max);
var max = arr[0];
for(var i=1;i<arr.length;i++){
if (arr[i] > max) {
max = arr[i];
}
}
console.log('该数组里面的最大值是: ' + max);
✍🏻案例: 数组转换为分割字符串
要求:将数组['red', 'green', 'blue','pink']转换为字符串,并且用|其他符号分割
输出: 'red|green|bluelpink|'
思路:
①需要一个新变量用于存放转换完的字符串str。
②遍历原来的数组,分别把里面数据取出来,加到字符串里面。(JavaScript 的数组方法 join() 来将数组转换为字符串,并在参数中指定分隔符。)
③同时在后面多加一个分隔符。
var arr = ['red', 'green', 'blue', 'pink'];
// 将数组转换为字符串,并用“|”分隔
var str = arr.join('|');
console.log(str);
5️⃣数组中新增元素
1.通过修改length长度
●可以通过修改 length长度来实现数组扩容的目的
●length 属性是可读写的
var arr = ['red', 'green', 'blue', 'pink'];
arr. length = 7 ;
console. log(arr) ;
console. log(arr[4]) ;
console. log(arr[5]) ;
console.1og(arr[6]) ;
其中索引号是4, 5 , 6的空间没有给值,就是声明变量未给值,默认值就是undefined。
2.修改索引号增加数组元素——追加数组元素
- 可以通过修改数组索引的方式追加数组元素
- 不能直接给数组名赋值,否则会覆盖掉以前的数据
✍🏻案例: 数组新增元素
例: 新建一个数组,里面存放10个整数( 1~10 )
①使用循环来追加数组。
②声明一一个空数组arr.
③循环中的计数器i可以作为数组元素存入。
④由于数组的索引号是从0开始的,因此计数器从0开始更合适,存入的数组元素要+1。
var arr=[ ];
for(var i=0;i<100;i++){
//arr=i;不要直接给数组名赋值否则以前的元素都没了
arr[i]=i+1;
console.log(arr);
✍🏻案例:筛选数组
要求: 将数组[2,0, 6, 1, 77,0, 52, 0, 25, 7]中大于等于10的元素选出来,放入新数组。
实现思路:
①声明一个新的数组用于存放新数据newArr。
②遍历原来的旧数组,找出大于等于10的元素。
③依次追加给新数组newArr.
var arr=[2,0,6,1,77, 0, 52, 0, 25, 7];
var newArr=[ ];
var i=0;
for (var i = 0; i < arr.length; i++) {
if (arr[i] > 10) {
// 新数组应该从0开始 依次递增
newArr[j] = arr[i];
j++ ;
}
if (arr[i] >= 10) {
//新数组索引号应该从0开始依次递增
newArr [newArr .length] = arr[i];
}
}
console.log(newArr);
数组案例
✍🏻案例: 删除指定数组元素
要求: 将数组 [2,0,6, 1, 77, 0, 52, 0, 25, 7] 中的0去掉后,形成一个不包含0的新数组。
实现思路:
①需要一个新数组用于存放筛选之后的数据。
②遍历原来的数组,把不是0的数据添加到新数组里面(此时要注意采用数组名+索引的格式接收数据)。
③新数组里面的个数,用length不断累加。
var arr = [2, 0, 6, 1, 77, 0, 52, 0, 25, 7];
// 过滤数组中的0
var newArr = arr.filter(function(item) {
return item !== 0;
});
console.log(newArr);
使用 JavaScript 的数组方法 filter() 来过滤掉数组中的0,然后得到一个新的不包含0的数组。
✍🏻案例: 翻转数组
要求:将数组['red', 'green','blue', 'pink', 'purple']的内容反过来存放。
输出: ['purple', 'pink', 'blue', 'green', 'red']
1、声明一个新数组newArr
2、把旧数组索引号第4个取过来(arr.length - 1),给新数组索引号第0个元素(newArr. length)
3、我们采取递减的方式 i--
var newArr=[ ];
for(var i=arr.length-1;i>=0;i--){
newArr [newArr.length] = arr[i ]
}
console . log(newArr );
✍🏻案例:冒泡排序🌟
冒泡排序: 是一种算法,把一系列的数据按照-定的顺序进行排列显示(从小到大或从大到小)。
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[i + 1];
arr[j + 1] = temp;
}
}
}
函数
学习任务:
- 能够说出为什么需要函数
- 能够根据语法书写函数
- 能够根据需求封装函数
- 能够说出形参和实参的传递过程
- 能够使用函数的返回值
- 能够使用arguments获取函数的参数
函数
在JS里面,可能会定义非常多的相同代码或者功能相似的代码,这些代码可能需要大量重复使用。
虽然for循环语句也能实现一些简单的重复操作,但是比较具有局限性,此时我们就可以使用JS中的函数。
函数: 封装了一段可被重复调用执行的代码块。 通过此代码块可以实现大量代码的重复使用。
函数的使用
函数在使用时分为两步:
1️⃣声明函数
function 函数名() {
函数体代码
}
(1) function 声明函数的关键字全部小写
(2) 函数是做某件事情,函数名一般是动词
(3) 函数不调用自己不执行
2️⃣调用函数
函数名();
●调用的时候千万不要忘记添加小括号
●口诀: 函数不调用,自己不执行。
⭕注意: 声明函数本身并不会执行代码,只有调用函数时才会执行函数体代码。
1.函数的封装
●函数的封装是把一个或者多个功能通过函数的方式封装起来,对外只提供一个简单的函数接口
●简单理解: 封装类似于将电脑配件整合组装到机箱中(类似快递打包)
2.函数的参数
function函数名(形参1,形参2...) {
//在声明函数的小括号里面是形参(形式 上的参数)
}
函数名(实参1,实参2...); //在函数调用的小括号里面是实参(实际的参数)
形参是接受实参的,形参类似于一个变量
函数的参数可以有,也可以没有个数不限。
在声明函数时,可以在函数名称后面的小括号中添加一些参数,这些参数被称为形参,而在调用该函数时,同样也需要传递相应的参数,这些参数被称为实参。
参数 | 说明 |
形参 | 形式上的参数 函数定义的时候 传递的参数 当前并不知道是什么 |
实参 | 实际上的参数 函数调用的时候传递的参数 实参是传递给形参的 |
(1)利用函数求任意两个数的和
function getSum( num1, num2) {
console .1og(num1 + num2) ;
}
getSum(1, 3);
(2)利用函数求任意两个数之间的和
function getSums(start, end) {
var sum = 0;
for (var i = start; i <= end; i++) {
sum +=i
}
console.log(sum);
}
(3)注意点
- 多个参数之间用逗号隔开
- 形参可以看做是不用声明的变量
⭕函数形参和实参个数不匹配问题
参数个数 | 说明 |
实参个等于形参个数 | 输出正确结果 |
实参个数多于形参个数 | 只取到形参的个数 |
实参个数小于形参个数 | 多的形参定义为undefined,结果为NaN |
‼️ 注意: 在JavaScript中,形参的默认值是undefined。
⭕小结:
●函数可以带参数也可以不带参数
●声明函数的时候,函数名括号里面的是形参,形参的默认值为undefined
●调用函数的时候,函数名括号里面的是实参
●多个参数中间用逗号分隔
●形参的个数可以和实参个数不匹配 ,但是结果不可预计,我们尽量要匹配
3.函数的返回值
1️⃣return语句
有的时候,我们会希望函数将值返回给调用者,此时通过使用return语句就可以实现。
(1)函数只是实现某种功能,最终的结果需要返回给函数的调用者函数名( )通过return 实现的
(2)只要函数遇到return,就把后面的结果 返回给函数的调用者,函数名( ) = return后面的结果
✍🏻例:利用函数求数组[5,2,99,101, 67,77] 中的最大数值。
function getArrMax(arr) { // arr接受个数组
var max = arr[0];
for (var i = 1; i <= arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
return max;
}
2️⃣return终止函数
return语句之后的代码不被执行。
return只能返回一个值。如果用逗号隔开多个值,以最后一一个为准。
✍🏻例:求任意两个数的加减乘数结果
function getResult(num1, num2) {
return [num1 + num2 ,num1 -num2, num1 * num2, num1 / num2 ] ;
}
var re = getResult(1, 2); I
console .log(re);
函数如果有return则返回的是return 后面的值,如果函数么有return 则返回undefined
函数都是有返回值的
1.如果有return则返回return后面的值
2.如果没有return 则返回undefined
break ,continue ,return 的区别
●break : 结束当前的循环体(如for、while )
●continue :跳出本次循环,继续执行下次循环(如for、 while )
●return :不仅可以退出循环 ,还能够返回 return语句中的值,同时还可以结束当前的函数体内的代码
arguments的使用
当我们不确定有多少个参数传递的时候,可以用arguments来获取。在JavaScript中, arguments实际上它是当前函数的一个内置对象。所有函数都内置了一个arguments对象, arguments对象中存储了传递的所有实参。
arguments展示形式是一个伪数组,因此可以进行遍历。伪数组具有以下特点:
●具有length属性
●按索引方式储存数据
●不具有数组的push, pop等方法
只有函数才有arguments对象, 而且是每个函数都内置好了这个arguments。
✍🏻案例:利用函数求任意个数的最大值
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 ;
}
📜函数案例
✍🏻案例: 利用函数封装方式, 翻转任意一个数组
//利用函数翻转任意数组reverse翻转
function reverse(arr) {
var newArr=[];
for(var i=arr.length-1;i>=0;i--){
newArr[newArr.length] = arr[i];
}
return newArr ;
}
✍🏻案例:利用函数封装方式, 对数组排序--冒泡排序
//利用函数冒泡排序sort 排序
function sort(arr) {
for(vari=0;i<arr.length-1;i++){
for(varj=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;
}
✍🏻案例: 判断闰年
要求: 输入一个年份,判断是否是闰年(闰年:能被4整除并且不能被100整数,或者能被400整除)
// 利用函数判断闰年
function isRunYear(year) {
//如果是闰年我们返回true 否则返回false
var flag =false;
if(year %4==0&&year %100!=0year %400==0){
flag = true;
}
return flag;
}
函数可以调用另外一个函数
因为每个函数都是独立的代码块,用于完成特殊任务,因此经常会用到函数相互调用的情况。
✍🏻案例:用户输入年份,输出当前年份2月份的天数
如果是闰年,则2月份是29天,如果是平年,则2月份是28天
判断闰年函数
//用户输入年份,输出当前年份2月份的天数
function backDay() {
var year = prompt( '请您输入年份:');
if(isRunYear(year)){//调用函数需要加小括号
alert('当前年份是闰年2月份有29天');
} else {
alert('当前年份是平年2月份有28天');了
}
}
backDay();
函数的两种声明方式
1.利用函数关键字自定义函数(命名函数)
function fn() {
}
fn();
2.函数表达式(匿名函数)
var 变量名= function( ) { };
var fun = function(aru) {
console.log('我是函数表达式' );
console.log(aru);
}
fun('pink老师');
(1) fun是变量名 不是函数名
(2) 函数表达式声明方式跟声明变量差不多,只不过变量里面存的是值,而函数表达式里面存的是函数
(3)函数表达式也可以进行传递参数
作用域
学习任务:
- 能够说出JavaScript的两种作用域
- 能够区分全局变量和局部变量
- 能够说出如何在作用域链中查找变量的值
作用域
通常来说,一段程序代码中所用到的名字并不总是有效和可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。作用域的使用提高了程序逻辑的局部性,增强了程序的可靠性,减少了名字冲突。
1. JavaScript作用域:就是代码名字 (变量)在某个范围内起作用和效果,目的是为了提高程序的可靠性更,重要的是减少命名冲突。
2. JS的作用域(es6)之前: 全局作用域 局部作用域。
3.全局作用域: 整个script标签或者是一个单独的JS文件 var num = 10;
4.局部作用域:(函数作用域)在函数内部就是局部作用域这个代码的名字只在函数内部起效果和作用
function fn() { // 局部作用域 }
变量的作用域
在JavaScript中,根据作用域的不同,变量可以分为两种:
1️⃣全局变量:
在全局作用域下声明的变量叫做全局变量(在函数外部定义的变量)。
●全局变量在代码的任何位置都可以使用
●在全局作用域下var声明的变量是全局变量
●特殊情况下,在函数内不使用var声明的变量也是全局变量(不建议使用)
在全局作用域下的变量 在全局下都可以使用
⭕注意:如果在函数内部没有声明直接,赋值的变量也属于全局变量
2️⃣局部变量:
在局部作用域下声明的变量叫做局部变量(在函数内部定义的变量)
●局部变量只能在该函数内部使用
●在函数内部var声明的变量是局部变量
●函数的形参实际上就是局部变量
在局部作用域下的变量 后者在函数内部的变量就是局部变量
⭕注意:函数的形参也可以看做是局部变量
从执行效率来看全局变量和局部变量
(1)全局变量:只有浏览器关闭的时候才会销毁,比较占内存资源
(2)局部变量:当其所在的代码块被执行时, 会被初始化。当我们程序执行完毕就会销毁,比较节约内存资源
JS没有块级作用域
JS也是在es6的时候新增的块级作用域
//块级作用域{} if {} for {}
//java
if(xx) {
int num = 10;
}
//外面的是不能调用num的
if(3<5){
var num = 10;
}
console . log(num);|
作用域链
- 只要是代码,就至少有一个作用域
- 写在函数内部的局部作用域
- 如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域
- 根据在内部函数可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问,就称作作用域链——就近原则
案例:
function f1() {
var num=123;
function f2() {
console . log (num);
}
f2();
}
var num = 456;
f1();
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); //a的值?
console.log(b); //b的值?
}
}
}
fn1() ;
a = 4 , b = 22
预解析
学习任务:
- 能够知道解析器运行JS分为哪两步
- 能够说出变量提升的步骤和运行过程
- 能够说出函数提升的步骤和运行过程
预解析简介
JavaScript代码是由浏览器中的JavaScript解析器来执行的。JavaScript 解析器在运行JavaScript代码的时候分为两步:预解析和代码执行。
(1).预解析:js引擎会把js里面所有的var还有 function 提升到当前作用域的最前面
(2).代码执行:按照代码书写的顺序从 上往下执行
变量预解析和函数预解析
预解析分为变量预解析(变量提升) 和函数预解析(函数提升)
(1)变量提升
就是把所有的变量声明提升到当前的作用域最前面, 不提升赋值操作
console.1og(num); // undefined坑1
var num = 10;
//相当于执行了以下代码
// var num;
// console. log( num);
// num = 10;
fun(); //报错 坑2
var fun = function() {
console.log(22);
}
//相当于执行了以下代码
var fun;
fun();
fun = function() {
console.log(22);
}
函数表达式调用必须写在函数表达式的下面。
(2)函数提升
就是把所有的函数声明提升到当前作用域的最前面不调用函数
fn();
function fn( )
console .log(11);
}
✍🏻预解析案例
var num = 10;
fun() ;
function fun() {
console. log (num) ;
var num = 20;
}
//相当于执行了以下操作
var num;
function fun() {
var num;
console.log(num);
num = 20;
}
num = 10;
fun();
//结果为undefined
var num = 10;
function fn() {
console.log (num) ;
var num = 20;
console. log (num) ;
}
fn() ;
//相对于以下代码
var num ;
function fn() {
var num;
console.log(num);
num=20;
console .log(num);
}
num = 10;
fn();
//结果为undefined 20
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);
console.log(b);
a='123' ;
}
a=18;
f1();
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声明当全局变量看
console.log(a);
console.log(b);
console.log(c);
}
f1();
console.log(c);
console.log(b);
console.log(a);
对象
学习任务:
- 能够说出为什么需要对象
- 能够使用字面量创建对象
- 能够使用构造函数创建对象
- 能够说出new的执行过程
- 能够遍历对象
对象简介
现实生活中:万物皆对象,对象是一个具体的事物 ,看得见摸得着的实物。例如,一本书、一辆汽车、一个人可以是“对象”,一个数据库、一张网页、 一个与远程服务器的连接也可以是“对象”
在JavaScript中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等。
对象是由属性和方法组成的。
●属性:事物的特征,在对象中用属性来表示(常用名词)
●方法:事物的行为,在对象中用方法来表示(常用动词)
为什么需要对象
保存一个值时,可以使用变量,保存多个值( 一组值)时,可以使用数组。如果要保存一个人的完整信息呢 ?
创建对象的三种方式
1️⃣利用字面量创建对象
对象字面量: 就是花括号{ }里面包含了表达这个具体事物(对象)的属性和方法。
var obj={ }; //创建了一个空的对象
var obj={
uname :'张三疯',
age: 18,
sex :'男',
sayHi: function() {
console.1og('hi~' );
}
}
(1) 里面的属性或者方法我们采取键值对的形式 键属性名:值属性值
(2) 多个属性或者方法中间用逗号隔开的
(3)方法冒号后面跟的是一个匿名函数
⭕ 使用对象
(1).调用对象的属性我们采取对象名.属性名 .我们理解为 的
console.log(obj .uname);
(2). 调用属性还有一种方法对象名['属性名']
console.log(obj['age ']);
(3) 调用对象的方法sayHi 对象名.方法名()千万别忘记添加小括号
obj.sayHi();
变量、属性、函数、方法总结
(1)变量和属性的相同的,都是用来存储数据的
变量:单独声明并赋值,使用的时候直接写变量名单独存在
属性:在对象里面的不需要声明的,使用的时候必须是 对象.属性。对象里面的变量称为属性,不需要声明,用来描述该对象的特征
(2)函数和方法的相同点都是实现某种功能,做某件事
函数 是单独声明,并且调用的函数名()单独存在的
方法 在对象里面调用的时候 对象.方法()。对象里面的函数称为方法,方法不需要声明,使用“对象.方法名。
2️⃣利用new Object创建对象
//利用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();
3️⃣利用构造函数创建对象
为什么需要使用构造函数
就是因前面两种创建对象的方式一次 只能创建一 个对象
因为一次创建个对象,里面很多的属性和方法是大量相同的只能复制
因此可以利用函数的方法, 重复这些相同的代码, 就把这个函数称为构造函数。又因为这个函数不一样,里面封装的不是普通代码,而是对象
构造函数就是把对象里面一些相同的属性和方法抽象出来封装到函数里面
构造函数: 是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值, 它总与new运算符一起使用。可以把对象中一些公共的属性和方法抽取出来, 然后封装到这个函数里面。
//构造函数的语法格式
function构造函数名() {
this.属性=值;
this.方法= function() {}
}
new 构造函数名 ();
typeof检测数据类型
1.构造函数名字首字母要大写
2.构造函数不需要return就可以返回结果
3.调用构造函数必须使用new,即创建了一个对象
4.属性和方法前面必须添加this
构造函数和对象
●构造函数,如Stars( ),抽象了对象的公共部分,封装到了函数里面,它泛指某一大类(class) 类似java语言里面的 类 class
●创建对象,如new Stars( ),特指某一个,通过new关键字创建对象的过程我们也称为对象实例化
利用构造函数创建对象的过程,也称为对象的实例化。
new关键字
new关键字执行过程
1.new构造函数可以在内存中创建了一个空的对象
2.this就会指向刚才创建的空对象
3.执行构造函数里面的代码给这个空对象添加属性和方法
4.返回这个对象(所以构造函数里面不需要return )
遍历对象属性
for in 遍历对象
for(变量in对象){
}
变量 输出 得到的是属性名
obj[k] 得到是属性值
我们使用for in里面的变量我们喜欢写k或者key
总结:
1.对象可以让代码结构更清晰
2.对象复杂数据类型object.
3.本质:对象就是一组无序的相关属性和方法的集合。
4.构造函数泛指某一大类,比如苹果,不管是红色苹果还是绿色苹果,都统称为苹果。
5.对象实例特指一个事物,比如这个苹果、正在给你们讲课的pink老师等。
6. for···in语句用于对对象的属性进行循环操作。
内置对象
学习任务:
- 能够说出什么是内置对象
- 能够根据文档查询指定API的使用方法
- 能够使用Math对象的常用方法
- 能够使用Date对象的常用方法
- 能够使用Array对象的常用方法
- 能够使用String对象的常用方法
内置对象简介
●JavaScript 中的对象分为3种:自定义对象、内置对象、浏览器对象
●前面两种对象是JS基础内容,属于ECMAScript ;第三个浏览器对象属于我们JS独有的,我们JS API讲解
●内置对象就是指JS语言自带的一 些对象,这些对象供开发者使用,并提供了一些常用的或是最基本而必要的功能(属性和方法)
查文档
学习一个内置对象的使用,只要学会其常用成员的使用即可,我们可以通过查文档学习, 可以通过MDN / W3C来查询。
Mozilla开发者网络( MDN )提供了有关开放网络技术( Open Web )的信息,包括HTML、CSS和万维网及HTML5应用的API。
如何学习对象中的方法:
1.查阅该方法的功能
2.查看里面参数的意义和类型
3.查看返回值的意义和类型
4.通过demo进行测试
1️⃣Math对象
// 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
✍🏻案例: 封装自己的数学对象
利用对象封装自己的数学对象,里面有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));
Math对象不是构造函数,它具有数学常数和函数的属性和方法。跟数学相关的运算(求绝对值, 取整、最大值等)可以使用Math中的成员。
Math. PI //圆周率
Math. floor () //向下取整 往最小取值
Math.ceil () // 向上取整
Math. round() // 四舍五入版 就近取整 注意-3.5 结果是-3,其他数字都是四舍五入,但是 .5特殊它往大了取
Math.abs () //绝对值
Math .max () /Math. min() // 求最大和最小值
Math对象随机数方法
random() 返回一个随机的小数 0=<x<1,这个方法里面不跟参数。
//想要得到两个数之间的随机整数并且包含这2个整数
// Math. floor (Math. random() * (max - min + 1)) + min;
function getRandom(min, max) {
return Math. floor (Math. random() * (max - min + 1)) + min;
}
// 随机点名
var arr = ['张三','张三丰','张三疯子','李四','李思思','pink老师'];
console . log( arr[getRandom(0, arr.length - 1)]);
✍🏻案例: 猜数字游戏
程序随机生成一个1~ 10之间的数字,并让用户输入一个数字,
1.如果大于该数字,就提示,数字大了,继续猜;
2.如果小于该数字,就提示数字小了, 继续猜;
3.如果等于该数字,就提示猜对了,结束程序。
案例分析
①随机生成一个1~ 10的整数我们需要用到Math.random0方法。
②需要一直猜到正确为止,所以一直循环。
③用while 循环合适更简单。
④核心算法:使用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次猜的机会 ——> for循环。
2️⃣日期对象
- Date对象和Math对象不一样,是一个构造函数,所以需要实例化后才能使用
- Date实例用来处理日期和时间
var arr=newArray();//创建一个数组对象
var obj=newObject();//创建了一个对象实例
//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月,0-11默认加1
var date2 = new Date (' 2019-10-1 8:8:8');
console .log(date2);
Date()构造函数的参数
日期格式化
2019-8-8 8:8:8 格式的日期
需要获取日期指定的部分,所以要手动的得到这种格式。
方法名 | 说明 | 代码 |
getFullYear() | 获取当年 | dobj.getFullYear() |
getMonth() | 获取当月(0-11) | dobj.getMonth() |
getDate() | 获取当天日期 | dobj.getDate() |
getDay() | 获取星期几(周日0到周六6) | dobj.getDay() |
getHours() | 获取当前小时 | dobj.getHours() |
getMinutes() | 获取当前分钟 | dobj.getMinutes() |
getSeconds() | 获取当前秒钟 | dobj.getSeconds() |
//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]);
//要求封装一个函数返回当前的时分秒格式08:08:08
function getTime() {
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(getTime());
//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]);
//要求封装一个函数返回当前的时分秒格式08:08:08
function getTime() {
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(getTime());
获取日期的总的毫秒形式
Date对象是基于1970年1月1日(世界标准时间)起的毫秒数
经常利用总的毫秒数来计算时间,因为它更精确。
//获得Date总的毫秒数(时间戳)不是当前时间的毫秒数,而是距离1970年1月1号过了多少毫秒数
// 1.通过valueOf() getTime()
var date = new Date();
console. log(date.value0f()); //就是我们现在时间距离1970.1.1总的毫秒数
console . log(date . getTime());
// 2.简单的写法(最常用的写法)
var date1=+new Date(); // +new Date()返回的就 是总的毫秒数
console .1og(date1);
// 3. H5新增的获得总的毫秒数
console.log(Date.now());
✍🏻案例:倒计时🌟
案例分析
①核心算法:输入的时间减去现在的时间就是剩余的时间,即倒计时,但是不能拿着时分秒相减,比如05分减去25分,结果会是负数的。
②用时间戳来做。用户输入时间总的毫秒数减去现在时间的总的毫秒数,得到的就是剩余时间的毫秒数。
③把剩余时间总的毫秒数转换为天、时、分、秒(时间戳转换为时分秒)
转换公式如下:
●d = parseInt(总秒数/ 60/60/24); //计算天数
●h= parselnt(总秒数/ 60/60 %24) // 计算小时
●m = parselnt(总秒数/60 %60); // 计算分数
●S = parselnt(总秒数%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); //分
var s = parseInt(times % 60); //当前的秒
returnd d+'天'+h+'时'+m+分’+S+'秒';
}
console. log( countDown( ' 2019-5-1 18:00:00'));
var date = new Date();
console.log(date);
3️⃣数组对象
1.数组对象的创建
创建数组对象的两种方式
(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);
2.检测是否为数组
// (1) instanceof 运算符它可以用来检测是否为数组
var arr =[];
var obj = {};
console.log(arr instanceof Array );
console.log(obj instanceof Array) ;
//翻转数组
function reverse(arr) {
if (arr instanceof Array) {
var newArr = [ ];
for (var i =arr.length - 1; i >= 0; i--) {
newArr[newArr .length] = arr[i];
}
return newArr ;
} else {
return 'error 这个参数要求必须是数组格式[1,2,3]'
}
}
// (2) Array . isArray(参数);
H5新增的方法ie9以 上版本支持
console . log(Array . isArray(arr));
console . log(Array .isArray(obj));
3.添加删除数组元素的方法
方法名 | 说明 | 返回值 |
push(参数1....) | 末尾添加一个或多个元素,注意修改原数组 | 并返回新的长度 |
pop() | 删除数组最后一个元素,把数组长度减1无参数、修改原数组 | 返回它删除的元素的值 |
unshift(参数1..) | 向数组的开头添加一个或更多元素,注意修改原数组 | 并返回新的长度 |
shift() | 删除数组的第一个元素, 数组长度减1无参数、修改原数组 | 并返回第一个元素的值 |
1️⃣push( )在我们数组的末尾添加一个或者多个数组元素 push 推
var arr =[1, 2, 3];
console. log(arr .push(4, 'pink' ));
console.log(arr );
// (1) push 是可以给数组追加新的元素
// (2) push() 参数直接写数组元素就可以了
// (3) push完毕之后,返回的结果是新数组的长度
// (4)原数组也会发生变化
2️⃣unshift 在我们数组的开头添加一个或者多个数组元素
console. log(arr .unshift('red', ' purple'));
console.log(arr);
//(1)unshift是可以给数组前面追加新的元素
// (2) unshift() 参数直接写数组元素就可以了
// (3) unshift完毕之后,返回的结果是新数组的长度
// (4) 原数组也会发生变化|
3️⃣pop( ) 它可以删除数组的最后一个元素
console . log(arr .pop());
console .log(arr);
// (1) pop是可以删除数组的最后一个元素,一 次只能删除一 个元素
// (2) pop() 没有参数
// (3) pop完毕之后,返回的结果是删除的那个元素
// (4) 原数组也会发生变化
✍🏻案例:筛选数组
有一个包含工资的数组[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);
//将小于2000的数从后追加到新数组
4.数组排序
方法名 | 说明 | 是否修改原数组 |
reverse() | 颠倒数组中元素的顺序,无参数 | 该方法会改变原来的数组返回新数组 |
sort() | 对数组的元素进行排序 | 该方法会改变原来的数组返回新数组 |
// 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);
5.数组索引方法
方法名 | 说明 | 返回值 |
indexOf() | 数组中查找给定元素的第一个索引 | 如果存在返回索引号如果不存在,则返回-1。 |
lastIndexOf() | 在数组中的最后一个的索引 | 如果存在返回索引号如果不存在,则返回-1。 |
//返回数组元素索引号方法
indexOf(数组元素) 作用就是返回该数组元素的索引号
//它只返回第一个满足条件的索引号
//它如果在该数组里面找不到元素,则返回的是-1
//var arr==['red', ' green', 'blue', 'pink', 'blue'];
var arr = ['red',' green', 'pink' ] ;
console . log(arr . indexOf('blue '));
✍🏻案例:数组去重🌟
有一个数组['c', 'a’,'z', 'a’, 'x', 'a','x', 'c', 'b'] ,要求去除数组中重复的元素。
案例分析
①目标:把旧数组里面不重复的元素选取出来放到新数组中,重复的元素只保留一个,放到新数组中去重。
②核心算法:遍历旧数组,然后拿着旧数组元素去查询新数组, 如果该元素在新数组里面没有出现过就添加,否则不添加。
新数组[ ]
//封装一个去重的函数unique
function unique(arr) {
var newArr = [];
for(vari=0;i<arr.length;i++){
if (newArr . indexOf(arr[i]) ===-1){
newArr . push(arr[i]);
}
}
return newArr ;
}
7.数组转换为字符串
方法名 | 说明 | 返回值 |
toString() | 把数组转换成字符串,逗号分隔每一项 | 返回一个字符串 |
join('分隔符') | 方法用于把数组中的所有元素转换为一个字符串。 | 返回一个字符串 |
// 1. toString() 将我们的数组转换为字符串
var arr = [1, 2, 3];
console .1og(arr .toString()); // 1,2,3
// 2. join(分隔符)
var arr1=['green',' blue','pink']; // green, blue,pink
console . log(arr1.join());
console.log(arr1. join('-')); // green- blue- pink
console. log(arr1. join('&' )); // green&blue&pink
方法名 | 说明 | 返回值 |
concat() | 连接两个或多个数组不影响原数组 | 返回-个新的数组 |
slice() | 数组截取slice(begin, end) | 返回被截取项目的新数组 |
splice() | 数组删除splice(第几个开始,要删除个数) | 返回被删除项目的新数组注意,这个会影响原数组 |
slice() 和splice()目的基本相同,重点看splice()。
4️⃣字符串对象
// 基本包装类型
var str =' andy' ;
console .log(str .1ength);
//对象才有属性和方法,复杂数据类型才有属性和方法
//简单数据类型为什么会有length 属性呢?
基本包装类型:就是把简单数据类型包装成为了复杂数据类型
为了方便操作基本数据类型, JavaScript还提供了三个特殊的引用类型: String、Number和 Boolean。
按道理基本数据类型是没有属性和方法的,而对象才有属性和方法,但上面代码却可以执行,这是因为JS会把基本数据类型包装为复杂数据类型,其执行过程如下:
// (1) 把简单数据类型包装为复杂数据类型
var temp = new String( ' andy' );
// (2) 把临时变量的值给str
str = temp;
// (3)销毁这个临时变量
temp = null ;
1.字符串的不可变
指的是里面的值不可变,虽然看上去可以改变内容,但其实是地址变了,内存中新开辟了一个内存空间。
var str = 'abc' ;
str = 'hello' ;
//当重新给str赋值的时候,常量'abc'不会被修改,依然在内存中
//重新给字符串赋值,会重新在内存中开辟空间,这个特点就是字符串的不可变
//由于字符串的不可变,在大量拼接字符串的时候会有效率问题
var str ='' ;
for(vari=0;i<100000;i++){
str += i;
}
console.log(str); //这个结果需要花费大量时间来显示,因为需要不断的开辟新的空间。
2.根据字符返回位置
方法名 | 说明 |
indexOf(要查找的字符',开始的位置) | 返回指定内容在元字符串中的位置,如果找不到就返回-1,开始的位置是 |
lastIndexOf() | 从后往前找,只找第一个匹配的 |
//字符串对象根据字符返回位置
str . index0f( '要查找的字符',[起始的位置])
var str = '改革春风吹满地,春天来了';
console . log(str .indexOf('春'));
console. log(str . indexOf('春',3)); //从索引号是3的位置开始往后查找
✍🏻案例:返回字符位置
查找字符串" abcoefoxyozzopp"中所有o出现的位置以及次数
①核心算法:先查找第一个o出现的位置
②然后只要indexOf返回的结果不是-1就继续往后查找
③因为indexOf 只能查找到第一个 ,所以后面的查找,利用第二个参数,当前索引加1 ,从而继续查找
var str =" abcoefoxyozzopp" ;
var index = str . index0f('o');
var num= 0;
// console. log(index);
while (index !==-1) {
console . log(index);
num++;
index = str.indexOf('o', index + 1);
}
console.log('o出现的次数:'+num);
3.根据位置返回字符🌟
方法名 | 说明 | 使用 |
charAt(index) | 返回指定位置的字符(index字符串的索引号) | str.charAt(0) |
charCodeAt(index) | 获取指定位置处字符的ASCII码(index索引号) | str.charCodeAt(0) |
str[index] | 获取指定位置处字符 | HTML5, IE8+支持和charAt()等效 |
// 1. charAt(index) 根据位置返回字符
var str ='andy ' ;
console.log(str .charAt(3));
//遍历所有的字符
for(vari=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.1og(str[0]); // a
✍🏻案例: 返回字符位置
判断以一个字符串 'abcoefoxyozzopp' 中出现次数最多的字符,并统计其次数。
有一个对象,来判断是否有该属性对象['属性名']
①核心算法:利用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);
for in 遍历对象
var max = 0;
var ch =' ';
for(var k in o){
// k得到是属性名
// o[k] 得到的是属性值
if (o[k] > max) {
max = o[k];
ch=k;
}
}
console . log(max);
console.log('最多的字符是' + ch);
4.字符串操作方法🌟
方法名 | 说明 |
concat(str1,str2.,str3..) | concat() 方法用于连接两个或多个字符串。拼接字符串,等效于+, +更常用 |
substr(start,length) | 从start位置开始 (索引号),length 取的个数重点记住这个 |
slice(start, end) | 从start位置开始,截取到end位置, end取不到(他们俩都是索引号) |
substring(start, end) | 从start位置开始,截取到end位置,end取不到基本和slice 相同但是不接受负 |
// 1. concat('字符串1', '字符串2'....)
var str ='andy' ;
console.log(str .concat('red'));
// 2. substr( '截取的起始位置', '截取几个字符');
varstr1='改革春风吹满地';
console. log(str1.substr(2, 2)); //第一个2是索引号的2从第几个开始第 二个2是取几个字符
//1.替换字符 replace('被替换的字符',' 替换为的字符')它只会替换第一个字符
var str = ' andyandy';
console.log(str .replace('a', 'b'));
//有一个字符串 ' abcoefoxyozzopp' 要求把里面所有的。替换为
var str1 = ' abcoefoxyozzopp';
while (str1.indexOf('o') !== -1) {
str1 = str1.replace('o', '*');
}
console . log(str1);
// 2.字符转换为数组split('分隔符') 前面我们学过join把数组转换为字符串
var str2 = 'red, pink, blue' ;
console.log(str2.split(','));
var str3 = ' red&pink&blue ';
console .log(str3.split('&'));
例:给定一个字符串 ,如: "abaasdffggghhjjkkgfdssss3444343” , 问题如下:
1、 字符串的长度
2、取出指定位置的字符,如: 0,3,5,9等
3、查找指定字符是否在以 上字符串中存在,如:i,c , b等
4、替换指定的字符,如: g替换为22,ss替换为b等操作方法
5、截取指定开始位置到结束位置的字符串,如:取得1-5的字符串
6、找出以上字符串中出现次数最多的字符和出现的次数
简单类型与复杂类型
学习任务:
- 能够说出简单数据类型的内存分配
- 能够说出复杂数据类型的内存分配
- 能够说出简单类型如何传参
- 能够说出复杂类型如何传参
1.简单类型与复杂类型
简单类型又叫做基本数据类型或者值类型,复杂类型又叫做引用类型。
●值类型: 简单数据类型/基本数据类型,在存储时变量中存储的是值本身,因此叫做值类型
string,number , boolean , undefined , null。
//简单数据类型null 返回的是一个空的对象 object
var timer = null;
console. log(typeof timer );
//如果有个变量我们以后打算存储为对象,暂时没想好放啥,
这个时候就给null
●引用类型: 复杂数据类型,在存储时变量中存储的仅仅是地址(引用) ,因此叫做引用数据类型。通过new关键字创建的对象(系统对象、自定义对象) , 如Object. Array、 Date等。
2.堆和栈
1、栈(操作系统) :由操作系统自动分配释放存放函数的参数值、局部变量的值等。其操作方式类似于数据结构中的栈;
1️⃣简单数据类型存放到栈里面。
2、堆(操作系统) : 存储复杂类型(对象) ,一般由程序员分配释放 ,若程序员不释放,由垃圾回收机制回收。
2️⃣复杂数据类型存放到堆里面。
⭕注意: JavaScript中没有堆栈的概念 。
1️⃣简单类型的内存分配
●值类型(简单数据类型) : string , number , boolean , undefined , null
●值类型变量的数据直接存放在变量 (栈空间)中
简单数据类型 是存放在栈里面,里面直接开辟一个空间存放的是值。
2️⃣复杂类型的内存分配
- 引用类型(复杂数据类型) :通过new关键字创建的对象(系统对象、自定义对象) , 如Object. Array、 Date等
- 引用类型变量(栈空间)里存放的是地址,真正的对象实例存放在堆空间中
复杂数据类型 首先在栈里面存放地址,十六进制表示然后 这个地址指向堆里面的数据
1️⃣简单类型传参
函数的形参也可以看做是一个变量 ,当我们把一一个值类型变量作为参数传给函数的形参时 ,其实是把变量在栈
空间里的值复制了一份给形参 ,那么在方法内部对形参做任何修改,都不会影响到的外部变量。
函数的形参也可以看做是一个变量,当我们把一个值类型变量作为参数传给函数的形参时 ,其实是把变量在栈空间里的值复制了一份给形参 ,那么在方法内部对形参做任何修改,都不会影响到的外部变量。
function fn(a) {
a++;
console. log(a) ;
}
var x=10;
fn(x) ;
console.log (x);
2️⃣复杂类型传参
函数的形参也可以看做是一个变量 ,当我们把引|用类型变量传给形参时, 其实是把变量在栈空间里保存的堆地址复制给了形参,形参和实参其实保存的是同一一个堆地址,所以操作的是同一个对象。
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.这个输出什么?
Web APIs
学习任务:
- 能够说出Web APIs阶段与JavaScript语法阶段的关联性
- 能够说出什么是API
- 能够说出什么是Web API
1.Web APIs和JS基础关联性
JS的组成:
JS基础阶段
●我们学习的是ECMAScript标准规定的基本语法
●要求同学们掌握JS基础语法
●只学习基本语法,做不了常用的网页交互效果
●目的是为了JS后面的课程打基础、做铺垫
Web APIs阶段
●web APIS是W3C组织的标准
●Web APIs我们主要学习DOM和BOM
●Web APIs是我们JS所独有的部分
●我们主要学习页面交互功能
●需要使用JS基础的课程内容做基础
JS基础学习ECMAScript基础语法为后面作铺垫,Web APIs是JS的应用,大量使用JS基础语法做交互效果。
2.API和Web API
1️⃣API
API ( Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。
简单理解: API是给程序员提供的一种工具,以便能更轻松的实现想要完成的功能。
比如手机充电的接口,我们要实现充电这个功能:
●我们不关心手机内部变压器,内部怎么存储电等
●我们不关心这个充电线怎么制作的
●我们只知道,我们拿着充电线插进充电接口就可以充电
●这个充电接口就是一个API
2️⃣Web API
Web API是浏览器提供的一套操作浏览器功能和页面元素的API( BOM和DOM)。
现阶段我们主要针对于浏览器讲解常用的API,主要针对浏览器做交互效果。
比如我们想要浏览器弹出一个警示框,直接使用alert(弹出’)
因为Web API很多,所以我们将这个阶段称为Web APIs
⭕API和Web API总结
1. API是为我们程序员提供的一个接口,帮助我们实现某种功能,我们会使用就可以了,不必纠结内部如何实现
2. Web API主要是针对于浏览器提供的接口,主要针对于浏览器做交互效果。
3. Web API 一般都有输入和输出(函数的传参和返回值) , Web API很多都是方法(函数)
4.学习Web API可以结合前面学习内置对象方法的思路学习。
DOM
学习任务:
- 能够说出什么是DOM
- 能够获取页面元素
- 能够给元素注册事件
- 能够操作DOM元素的属性
- 能够创建元素
- 能够操作DOM节点
DOM简介
文档对象模型( Document ObjectModel ,简称DOM) , 是W3C组织推荐的处理可扩展标记语言( HTML或者XML )的标准编程接口。
W3C已经定义了一系列的DOM接口,通过这些DOM接口可以改变网页的内容、结构和样式。
⭕DOM树如下图:
●文档:一个页面就是一个文档, DOM中使用document表示
●元素:页面中的所有标签都是元素, DOM中使用element表示
●节点: 网页中的所有内容都是节点(标签、属性、文本、注释等) , DOM中使用node表示
DOM把以上内容都看做是对象。
1.获取元素
如何获取页面元素
DOM在我们实际开发中主要用来操作元素。
获取页面中的元素可以使用以下方式:
1️⃣根据ID获取
使用 getElementByld() 方法可以获取带有ID的元素对象。
<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);
</script>
console. dir打印我们返回的元素对象更好的查看里面的属性和方法。
2️⃣根据标签名获取
使用getElementsByTagName( )方法可以返回带有指定标签名的对象的集合。
document . getElementsByTagName ( '标签名' ) ;
⭕注意:
1.因为得到的是一个对象的集合,所以想要操作里面的元素就需要遍历。
2.得到元素对象是动态的。
// 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.如果页面中没有这个元素,返回的空的伪数组的形式
还可以获取某个元素(父元素)内部所有指定标签名的子元素.
element . getElementsByTagName ( '标签名'); 父元素必须是指定的单个元素
⭕注意: 元素必须是单个对象(必须指明是哪一个元素对象). 获取的时候不包括父元素自己。
3️⃣通过HTML5新增的方法获取
1. document . getElementsByClassName('类名') ; //根据类名返回元素对象集合
2. document . querySelector ('选择器') ;//根据指定选择器返回第一个元素对象
3. document . querySelectorAll( '选择器');//根据指定选择器返回
⭕说明:
1. querySelectorAll 是找出所有匹配的节点后,返回对应的元素节点数组.
2. querySelector 是找到一个后立刻返回找到的第一个节点对象,如果没有则返回null.
3. 返回的结果是静态的,之后对document结构的改变不会影响到之前取到的结果
// 1. getElementsByClassName根据类名获得某些元素集合
var boxs = document . getElementsByClassName( 'box' );
console .log(boxs);
//2.querySelector返回指定选择器的第一个元素对象切记里面的选择器需要加符号 .box(类);#nav(id)
var firstBox = document . querySelector(' .box' );
console . log(firstBox);
var nav = document . querySelector( ' #nav' );
console .log(nav);
var li = document . querySelector('li' );
console.log(li);
// 3. querySelectorAll( )返回指定选择器的所有元素对象集合
4️⃣特殊元素获取
①获取body元素
doucumnet.body // 返回body元素对象
②获取html元素
document.documentElement / /返回HTML元素对象
2.事件基础
JavaScript使我们有能力创建动态页面,而事件是可以被JavaScript侦测到的行为。
简单理解: 触发---响应机制。
网页中的每个元素都可以产生某些可以触发JavaScript的事件,例如,可以在用户点击某按钮时产生一个事件,然后去执行某些操作。
1.事件是有三部分组成 事件源 事件类型 事件处理程序,我们也称为事件三要素
(1)事件源 事件被触发的对象即是谁,例如 按钮
(2) 事件类型 如何触发什么事件,如:鼠标点击(onclick) 还是鼠标经过还是键盘按
(3)事件处理程序 通过 一个函数赋值的方式完成
(1)执行事件的步骤
1.获取事件源
2.注册事件(绑定事件)
3.添加事件处理程序(采取函数赋值形式)
1.var div = document . querySelector( 'div' ) ;
2.div . onclick
3.div.onclick = function() {
console.log('我被选中了');
}
(2)常见的鼠标事件
onclick | 鼠标点击左键触发 |
onmouseover | 鼠标经过触发 |
onmouseout | 鼠标离开触发 |
onfocus | 获得鼠标焦点触发 |
onblur | 失去鼠标焦点触发 |
onmousemove | 鼠标移动触发 |
onmouseup | 鼠标弹起触发 |
onmousedown | 鼠标按下触发 |
分析事件三要素
3.操作元素
JavaScript的DOM操作可以改变网页内容、结构和样式,我们可以利用DOM操作元素来改变元素里面的内容、属性等。注意以下都是属性。
(1)改变元素内容
1️⃣element.innerText
从起始位置到终,止位置的内容,但它去除html标签,同时空格和换行也会去掉。
2️⃣element. innerHTML
起始位置到终止位置的全部内容,包括html标签,同时保留空格和换行。
//当我们点击了按钮,div里面的文字会发生变化
// 1.获取元素
var btn = document.querySelector( ' button' );
var div = document.querySelector( 'div' );
// 2.注册事件
btn.onclick = function() {
div.inneEText = '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];
}
//元素可以不用添加事件 —— 操作DOM
var p = document.querySelector('p' );
p.innerText =getDate( ) ;
(2)innerText和innerHTML 的区别
// 1. innerText 不识别html标签 非标准 去除空格和换行
var div = document . querySelector( 'div');
// 2. innerHTML 识别htm1标签 W3C标准,同时保留空格和换行
div. innerHTML = '<strong>今天是: </strong> 2019 ' ;
// 这两个属性是可读写的,可以获取元素里面的内容
4.常用元素的属性操作
1. innerText、 innerHTML 改变元素内容
2. src、href
3. id、alt、title
✍🏻案例: 分时显示不同图片,显示不同问候语
根据不同时间,页面显示不同图片,同时显示不同的问候语。
如果上午时间打开页面,显示上午好,显示上午的图片。
如果下午时间打开页面,显示下午好,显示下午的图片。
如果晚上时间打开页面,显示晚上好,显示晚上的图片。
案例分析
①根据系统不同时间来判断,所以需要用到日期内置对象
②利用多分支语句来设置不同的图片
③需要一个图片,并且根据时间修改图片,就需要用到操作元素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 = '亲,上午好' ;
}elseif(h<18){
img.src ='images/x.gif' ;
div. innerHTML = '亲, 下午好' ;
} else {
img.src = ' images/w.gif' ;
div. innerHTML = '亲,晚上好';
}
表单元素的属性操作
利用DOM可以操作如下表单元素的属性:
type、value、checked、 selected、 disabled
<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>
✍🏻案例: 仿京东显示密码
点击按钮将密码框切换为文本框,并可以查看密码明文。
案例分析
①核心思路:点击眼睛按钮,把密码框类型改为文本框就可以看见里面的密码
②一个按钮两个状态,点击一次,切换为文本框,继续点击次切换为密码框
③算法:利用一个flag变量 ,来判断flag的值,如果是1就切换为文本框, flag设置为0 ,如果是0就切换为密码框, flag设置为1。
// 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;
}
}
4.样式属性操作
可以通过JS修改元素的大小、颜色、位置等样式。
1. element. style 行内样式操作
2. element. className 类名样式操作
⭕注意:
1.JS里面的样式采取驼峰命名法比如 fontsize、backgroundColor
2.JS修改style样式操作,产生的是行内样式, css权重比较高(假如JS样式和内嵌样式冲突了)。
✍🏻案例: 淘宝点击关闭二维码
当鼠标点击二维码关闭按钮的时候,则关闭整个二维码。
案例分析
①核心思路: 利用样式的显示和隐藏完成,display.none隐藏元素 display:block 显示元素
②点击按钮,就让这个二维码盒子隐藏起来即可。
// 1.获取元素
var btn = document . querySelector(' .close-btn');
var box =document. querySelector(' .box' );
// 2.注册事件 程序处理
btn.onclick = function() {
box.style.display = ' none';
}//因为点击按钮关的是box,因此不能用this
✍🏻案例: 循环精灵图背景
可以利用for循环设置一组元素的精灵图背景。
⭕分析:
①首先精灵图图片排列有规律的(竖着排列的)
②核心思路:利用for循环修改精灵图片的背景位置background-position
③剩下的就是数学功底了
④让循环里面的i索引号* 指定数值 就是每个图片的y坐标
// 1.获取元素所有的小li
var lis = document . querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
//让索引号乘以指定数值就是每个li的背景y坐标
index就是我们的y坐标
var index=i*指定数值;
lis [i] . style. backgroundPosition = '0 -' + index + 'px';
}
✍🏻案例: 显示隐藏文本框内容
当鼠标点击文本框时,里面的默认文字隐藏,当鼠标离开文本框时,里面的文字显示。
①首先表单需要2个新事件,获得焦点onfocus失去焦点onblur
②如果获得焦点,判断表单里面内容是否为默认文字,如果是默认文字,就清空表单内容
③如果失去焦点,判断表单内容是否为空,如果为空,则表单内容改为默认文字
// 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.1og('失去 了焦点');
if (this.value === ' ' ) {
this.value = '手机' ;
}
//失去焦点需要把文本框里面的文字颜色变浅色
this. style.color = '#999 ' ;
}
className修改样式属性
使用element.style 获得修改元素样式 如果样式比较少或者功能简单的情况下使用
className会直接更改元素的类名,会覆盖原先的类名。
多类名选择器
语法:
.类名1 {属性1:属性值1; 属性2:属性值2; 属性3:属性值3; ......}
.类名2 {属性4:属性值4; 属性5:属性值5; 属性6:属性值6; ......}
标签调用的时候用: class = "类名1 类名2"
⭕注意:
(1)样式显示效果跟HTML元素中的类名先后顺序没有关系,爱CSS样式书写的上下顺序有关.
(2)各个类名中间用空格隔开.
多类名选择器在后期布局比较复杂的情况下,还是较多使用的.
类选择器与ID选择器的区别:
类选择是可以重复多次使用的, id只能使用一次。
//2.我们可以通过修改元素的className更改元素的样式适合于样式较多或者功能复杂的情况
// this.className = ' change' ;
// 3.如果想要保留原先的类名,我们可以这么做多类名选择器
this.className = 'first change' ;
✍🏻案例: 密码框格式提示错误信息
用户如果离开密码框,里面输入个数不是6~ 16 , 则提示错误信息, 否则提示输入正确信息。
分析:
①首先判断的事件是表单失去焦点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. className =' message wrong ';
message. innerHTML = ' 您输入的位数不对要求6~16位';
} else {
message .className =' message right' ;
message . innerHTML = '您输入的正确';
}
}
⭕总结
操作元素是DOM核心内容。
5.排他思想
如果有同一组元素,我们想要某一个元素实现某种样式 ,需要用到循环的排他思想算法:
1.所有元素全部清除样式(干掉其他人)
2.给当前元素设置样式( 留下我自己)
3.注意顺序不能颠倒,首先干掉其他人, 再设置自己
✍🏻案例:百度换肤🌟
①这个案例练习的是给一组元素注册事件
②给4个小图片利用循环注册点击事件
③当点击了这个图片,让页面背景改为当前的图片
④核心算法: 把当前图片的src路径取过来,给body做为背景即可
// 1.获取元素 ul baidu里面的所有文件img
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 .1og(this.src);
//把这个路径this.src 给body 就可以了
document. body . style . backgroundImage = 'url(' + this.src + ' )';
}
}
✍🏻案例: 表格隔行变色
①用到新的鼠标事件鼠标经过onmouseover;鼠标离开 onmouseout
②核心思路:鼠标经过tr行,当前的行变背景颜色,鼠标离开去掉当前的背景颜色
③注意:第一行( thead里面的行)不需要变换颜色,因此我们获取的是tbody里面的行
<tbody>
<thead>
<tr>
<th>代码</th>
<th>名称</th>
<th>最新公布净值</th>
</tr>
</thead>
</tbody>
// 1.获取元素获取的是tbody 里面所有的行
var trs = document . querySelector( 'tbody' ) . querySelectorAll('tr');
// 2.利用循环绑定注册事件
for(vari=0;i<trs.length;i++){
// 3.鼠标经过事件onmouseover
trs [i ] . onmouseover = function() {
this.className = 'bg';
}
// 4.鼠标离开事件onmouseout
trs [i] . onmouseout = function() {
this. className = ' ';
}
}
✍🏻案例: 表单全选取消全选案例🌟
1.点击上面全选复选框,下面所有的复选框都选中(全选)
2.再次点击全选复选框,下面所有的复选框都不中选(取消全选)
3.如果下面复选框全部选中,上面全选按钮就自动选中
4.如果下面复选框有一个没有选中,上面全选按钮就不选中
5.所有复选框一开始默认都没选中状态
案例分析
①全选和取消全选做法:让下面所有复选框的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 ;
}
}
②下面复选框需要全部选中,上面全选才能选中做法:给下面所有复选框绑定点击事件,每次点击,都要循环查看下面所有的复选框是否有没选中的,如果有一个没选中的 ,上面全选就不选中。
③可以设置一个变量,来控制全选是否选中。
for (var i = 0; i < j_tbs.length; i++) {
j_tbs[i]. onclick= function() {
// flag控制全选按钮是否选中
var flag = true ;
// 每次点击 下面的复选框都要循环检查者4个小按钮是 否全被选中
for(vari=0;i<j_tbs.length;i++){
if (! j_tbs[i]. checked) {
flag = false;
break ;
//退出for循环 这样可以提高执行效率因为只要有一个没有选中,剩下的就无需循环判断了
}
}
j_ cbAll.checked = flag;
}
}
6.自定义属性的操作
1️⃣获取属性值
●element.属性 获取属性值。
●element .getAttribute('属性');
get得到获取 attribute 属性的意思程序员自己添加的属性,我
们称为自定义属性,例如index
⭕区别:
●element .属性 获取内置属性值(元素本身自带的属性)
●element .getAttribute( '属性'); 主要获得自定义的属性 ( 标准)我们程序员自定义的属性
2️⃣设置属性值
●element.属性= '值'
设置内置属性值。
●element . setAttribute('属性','值') ;
⭕区别:
●element.属性 设置内置属性值
●element. setAttribute('属性' ); 主要设置自定义的属性 ( 标准)
// (2) element. setAttribute('属性','值'); 主要针对于自定义属性
div . setAttribute( ' index', 2);
div. setAttribute( 'class', 'footer');
// class 特殊这 里面写的就是class不是className
移除属性
●element . removeAttribute ('属性') ;
✍🏻案例: tab栏切换🌟
当鼠标点击上面相应的选项卡( tab),下面内容跟随变化
①Tab栏切换有2个大的模块
②上面的模块选项卡,点击某一个, 当前这一个底色会是红色,其余不变(排他思想)修改类名的方式
③下面的模块内容,会跟随上面的选项卡变化。所以下面模块变化写到点击事件里面。
④规律:下面的模块显示内容和上面的选项卡一对应 ,相匹配。
⑤核心思路:给上面的tab_ list 里面的所有小li添加自定义属性,属性值从0开始编号。
⑥当我们点击tab_ list 里面的某个小i,让tab_ .con 里面对应序号的内容显示,其余隐藏(排他思想)
//获取元素
var tab_ list = document . querySelector(' .tab_ list');
var lis = tab_ list. querySelectorAll( 'li' );
var items = document . querySelectorAll(' .item');
// for循环绑定 点击事件
for(vari=0;i<lis.length;i++){
lis[i]. setAttribute( ' index',i);
lis[i].onclick = function() {
//干掉所有人其余的1i清除class这个类
for (var i = 0; i < lis.length; i++) {
lis[i ] . className= ' ' ;
}
//留下我自己
this. className = ' current' ;//current即设置红色背景
}
//2.下面的显示内容模块
var index = this . getAttribute( ' index' )
//干掉所有人让其余的item这些div隐藏
for(vari=0;i<items.length;i++){
items[i].style.display = 'none' ;
}
//留下我自己让对应的item显示出来
items [index]. style.display = 'block';
}
7.H5自定义属性
自定义属性不能通过元素的属性来获取
自定义属性目的:是为了保存并使用数据。有些数据可以保存到页面中而不用保存到数据库中。
自定义属性获取是通过getAttribute ( '属性' )获取。
但是有些自定义属性很容易引起歧义,不容易判断是元素的内置属性还是自定义属性。
因此H5给我们新增了自定义属性:
1️⃣设置H5自定义属性
H5规定自定义属性 data- 开头做为属性名并且赋值。
或者使用JS设置
element.setAttribute( 'data-index' , 2)
2️⃣获取H5自定义属性
1.兼容性获取element.getAttribute( 'data-index' );
2. H5新增 element.dataset.index 或者 element.dataset [ 'index'] ie 11才开始支持
// h5新增的获取自定义属性的方法它只能获取data-开头的
//如果自定义属性里面有多个-链接的单词,我们获取的时候采取驼峰命名法
console . log(div . getAttribute( 'data-list-name' ));
console. log(div .dataset [' listName ' ] ) ;
8.节点操作
获取元素通常使用两种方式:
1.利用DOM提供的方法获取元素
●document.getElementByld( )
●document.getElementsByTagName(
●document.querySelector等
●逻辑性不强、繁琐.
2.利用节点层级关系获取元素
●利用父子兄节点关系获取元素
●逻辑性强,但是兼容性稍差
(1)节点概述
网页中的所有内容都是节点(标签、属性、文本、注释等) , 在DOM中,节点使用node来表示。
HTML DOM 树中的所有节点均可通过JavaScript进行访问,所有HTML元素(节点)均可被修改,也可以创建或删除。
一般地, 节点至少拥有nodeType (节点类型)、nodeName (节点名称)和nodeValue (节点值)这三个基本属性。
●元素节点nodeType为1
●属性节点nodeType为2
●文本节点nodeType为3 (文本节点包含文字、空格、换行等)
在实际开发中,节点操作主要操作的是元素节点
(2)节点层级
利用DOM树可以把节点划分为不同的层级关系,常见的是父子兄层级关系。
①父级节点
node . parentNode
得到的是离元素最近的父级节点(亲爸爸),如果找不到父节点就返回为null。
②子节点
1️⃣parentNode . childNodes (标准 )
parentNode. childNodes返回包含指定节点的子节点的集合,该集合为即时更新的集合。
⭕注意: 返回值里面包含了所有的子节点,包括元素节点,文本节点等。
如果只想要获得里面的元素节点,则需要专门处理。所以我们一般不提倡使用 childNodes
var ul = document. querySelector ('ul') ;
for(var i = 0; i < ul. childNodes . length;i++) {
if (ul. chi ldNodes[i] . nodeType == 1) {
// ul. childNodes[i]是元素节点
console. log (ul . childNodes[i]) ;
}
}
2️⃣parentNode . children (非标准)
parentNode. children是一个只读属性,返回所有的子元素节点。它只返回子元素节点,其余节点不返回( 这个是重点掌握的)。
虽然children是一个非标准,但是得到了各个浏览器的支持,因此我们可以放心使用
3️⃣ parentNode . firstChild
firstChild返回第一个子节点 ,找不到则返回null。同样,也是包含所有的节点。 第一个子节点不管是文本节点还是元素节点
4️⃣parentNode . lastChild
5️⃣parentNode . firstElementChild
firstElementChild返回第一个子元素节点,找不到则返回null。
6️⃣parentNode . lastElementChild
lastElementChild返回最后一个子元素节点 ,找不到则返回null。
⭕注意: 这两个方法有兼容性问题,IE9以上才支持。
实际开发中, firstChild和lastChild 包含其他节点,操作不方便,而firstElementChild和lastElementChild又有兼容性问题,那么如何获取第一个子元素节点或最后一个子元素节点呢 ?
实际开发的写法 既没有兼容性问题又返回第一个子元素
console.log(o1. children[ol.childrem.length-1]);
✍🏻案例:下拉菜单
案例分析
①导航栏里面的li都要有鼠标经过效果,所以需要循环注册鼠标事件
②核心原理:当鼠标经过li里面的第二个孩子ul显示,当鼠标离开,则ul隐藏
// 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';
//1为ul,2为a
lis[i] . onmouseout = function() {
this. children[1].style.display = 'none';
}
}
③兄弟节点
1. node . nextSibling
nextsibling返回当前元素的下一个兄弟节点,找不到则返回null。同样,也是包含所有的节点。
2. node . previousSibling
previoussibling返回当前元素上一个兄弟节点,找不到则返回null。同样,也是包含所有的节点。
3. node . nextElementsibling
nextElementsibling返回当前元素下一个兄弟元素节点,找不到则返回null。
4. node .previousElementsibling
previousElementsibl ing返回当前元素上一个兄弟节点,找不到则返回null。
⭕注意: 这两个方法有兼容性问题,IE9 以上才支持。
问:如何解决兼容性问题?
答: 自己封装一个兼容性的函数
function getNextElementSibl ing (element) {
var el = element ;
while (el = el. nextSibling) {
if (el .nodeType = == 1) {
return el;
}
}
return null;
}
(3)创建节点
document. createElement ( 'tagName ')
document. crea teElement()方法创建由tagName指定的HTML元素。因为这些元素原先不存在,是根据需求动态生成的,所以也称为动态创建元素节点。
(4)添加节点
1. node . appendChild (child)
node . appendchild()方法将一个节点添加到指定父节点的子节点列表末尾。类似于css里面的after伪元素。
添加节点noder appendChild(child) node 父级,child是子级后面追加元素类似于 数组中的push
var ul = document . querySelector('ul');
ul. appendChild(li);
2. node . insertBefore (child,指定元素)
node. insertBefore ()方法将一个节点添加到父节点的指定子节点前面。类似于Css里面的before伪元素。
var lili = document . createElement( 'li' );
ul. insertBefore(lili, ul. children[0]);
想要页面添加一个新的元素: 1. 创建元素 2.添加元素。
✍🏻案例: 简单版发布留言案例
案例分析
①核心思路:点击按钮之后,就动态创建一个li ,添加到ul里面。
②创建li的同时,把文本域里面的值通过li.innerHTML赋值给li
③如果想要新的留言后面显示就用appendChild如果想要前面显示就用insertBefore
// 1.获取元素
var btn = documeht . 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]);//添加到第一个
}
}
9. 删除节点
node . removeChild (child)
node. removeChild() 方法从DOM中删除个子节点,返回删除的节点。
// 1.获取元素
var ul = document . querySelector('ul' );
var btn = document . querySelector( ' button' );
//点击按钮依次删除里面的孩子
btn.onclick = function() {
if (ul. children. length == 0) {
this.disabled = true;
} else {
ul. removeChild(ul.children[0]);
}
}
✍🏻案例: 删除留言案例
①当我们把文本域里面的值赋值给li的时候,多添加一个删除的链接
②需要把所有的链接获取过来,当点击当前的链接的时候,删除当前链接所在的Ii
③阻止链接跳转需要添加javascriptvoid(0);或者javascript;
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 ;因为a的父亲是li,li的父亲是ul
ul. removeChild( this . parentNode );
}
}
}
10.复制节点(克隆节点)
node. cloneNode ()
node. cloneNode ()方法返回调用该方法的节点的一个副本。也称为克隆节点/拷贝节点
⭕注意:
1.如果括号参数为空或者为false ,则是浅拷贝,即只克隆复制节点本身,不克隆里面的子节点。
2.如果括号参数为true ,则是深度拷贝,会复制节点本身以及里面所有的子节点。
✍🏻案例:动态生成表格
①因为里面的学生数据都是动态的,需要js动态生成。这里模拟数据,自己定义好数据。数据(在数组中)采取对象形式存储。
②所有的数据都是放到tbody里面的行里面。
③因为行很多,需要循环创建多个行(对应多少人)——>数组的元素个数,即数组的长度
var tbody = document . querySelector( ' tbody');
for (var i = 0; i < datas.length; i++) {/ /外面的for循环管行tr
//创建tr行
var tr = document . createElement('tr ' );
tbody . appendChild(tr);
//行里面创建单元格 td单元格的数量取决于每个对象里面的属性个数 for循环遍历对象datas [i]
for (var k in datas[i]) { //里面的for循环管列td
//创建单元格
var td = document . createElement('td');
// 把对象 里面的属性值datas[i][k] 给td
td. innerHTML = datas[i][k];
tr . appendChild(td);
}
// 3.创建有删除2个字的单元格
var td = document . createElement('td' );
td. innerHTML = '<a href="javascript:;">删除 </a>';
tr . appendChild(td);
}
④每个行里面又有很多单元格(对应里面的数据) , 我们还继续使用循环创建多个单元格,并且把数据存入里面(双重for循环)
for(var k in obj) {
k得到的是属性名
obj[k]得到是属性值
}
⑤最后一列单元格是删除,需要单独创建单元格。
// 删除操作开始
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)
}
}
11.三种动态创建元素区别
1. document . write 是直接将内容写入页面的内容流,但是文档流执行完毕,则它会导致页面全部重绘。
var btn = document . querySelector(' button');
btn. onclick = function() {
document . write( ' <div>123</div>');
}
2. innerHTML 是将内容写入某个DOM节点,不会导致页面全部重绘
3. innerHTML 创建多个元素效率更高(不要拼接字符串,采取数组形式拼接) , 结构稍微复杂
4.createElement() 创建多个元素效率稍低一点点,但是结构更清晰
⭕总结: 不同浏览器下, innerHTML效率要比creatElement 高。
12.DOM 重点核心
1.对于JavaScript ,为了能够使JavaScript操作HTML , JavaScript就有了-套自己的dom编程接口。
2.对于HTML , dom使得html形成-棵dom树. 包含文档、元素、节点.
获取过来的DOM元素是一个对象( object) , 所以称为文档对象模型
关于dom操作,主要针对于元素的操作。主要有创建、增、删、改、查、属性操作、事件操作。
1️⃣创建
1. document.write
2. innerHTML
3. createElement
2️⃣增
1. appendChild
2. insertBefore
3️⃣删
1. removeChild,
4️⃣改
主要修改dom的元素属性, dom元素的内容、属性,表单的值等
1.修改元素属性: src、 href、title等
2.修改普通元素内容: innerHTML、innerText
3. 修改表单元素: value、 type、 disabled等
4.修改元素样式: style、 className
5️⃣查
主要获取查询dom的元素
1. DOM提供的API方法: getElementByld、getElementsByTagName 古老用法不太推荐
2. H5提供的新方法: querySelector. querySelectorAll 提倡
3.利用节点操作获取元素:父(parentNode)、子(children)、 兄(previousElementSibling、nextElementSibling)提倡
属性操作
主要针对于自定义属性。
1. setAttribute :设置dom的属性值
2. getAttribute :得到dom的属性值
3. removeAttribute移除属性
事件操作
给元素注册事件,采取事件源事件类型 = 事件处理程序
鼠标事件 | 触发条件 |
onclick | 鼠标点击左键触发 |
onmouseover | 鼠标经过触发 |
onmouseout | 鼠标离开触发 |
onfocus | 获得鼠标焦点触发 |
onblur | 失去鼠标焦点触发 |
onmousemove | 鼠标移动触发 |
onmouseup | 鼠标弹起触发 |
onmousedown | 鼠标按下触发 |
事件高级
学习任务:
- 能够写出元素注册事件的两种方式
- 能够说出删除事件的两种方式
- 能够说出DOM事件流的三个阶段
- 能够利用事件对象完成跟随鼠标案例
- 能够封装阻止冒泡的兼容性函数
- 能够说出事件委托的原理
- 能够说出常用的鼠标和键盘事件
1.注册事件(绑定事件)
概述
给元素添加事件,称为注册事件或者绑定事件。
注册事件有两种方式: 传统方式和方法监听注册方式
1️⃣传统注册方式
●利用on开头的事件onclick
●< button οnclick=“ alert('hi~")" > </button>
●btn.onclick = function() {}
●特点:注册事件的唯一性
●同一个元素同一个事件只能设置一个处理函数 ,最后注册的处理函数将会覆盖前面注册的处理函数
2️⃣方法监听注册方式
●w3c 标准推荐方式
●addEventListener(它是一个方法
●IE9之前的IE不支持此方法,可使用attachEvent()代替
●特点:同一个元素同一个事件可以注册多个监听器
●按注册顺序依次执行
2.addEventListener事件监听方式
eventTarget . addEventListener (type, listener [, useCapture] )
eventTarget . addEventListener ()方法将指定的监听器注册到eventTarget (目标对象)上,当该对象触发指定的事件时,就会执行事件处理函数。
该方法接收三个参数:
●type :事件类型字符串,比如click、mouseover ,注意这里不要带on
●listener :事件处理函数,事件发生时,会调用该监听函数
●useCapture :可选参数,是-一个布尔值,默认是false。学完DOM事件流后,我们再进一步学习
// (1) 里面的事件类型是字符串必定加引号而且不带on
// (2) 同一个元素同一个事件可以添加多个侦听器(事件处理程序)
btns [1] . addEventListener( 'click', function( ) {
alert(22);
})
3.attachEvent事件监听方式
eventTarget . attachEvent (eventNamewithOn, callback)
eventTarget . attachEvent ()方法将指定的监听器注册到eventTarget (目标对象)上,当该对象触发指定的事件时,指定的回调函数就会被执行。
该方法接收两个参数:
●eventNameWithOn :事件类型字符串,比如onclick、onmouseover ,这里要带on
●callback :事件处理函数,当目标触发事件时回调函数被调用
btns[2]. attachEvent( 'onclick', function() {
alert(11);
})
⭕注册事件(绑定事件)
注册事件兼容性解决方案
function addEventLi stener (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;
}
}
兼容性处理的原则:首先照顾大多多数浏览器,再处理特殊浏览器。
4.删除事件(解绑事件)
1️⃣传统注册方式
eventTarget. onclick = null;
2️⃣方法监听注册方式
① eventTarget. removeEventListener (type, listener[,useCapture]) ;
② eventTarget. detachEvent (eventNameWithOn, callback) ;
var divs = document . querySelectorAll('div' );
divs[0].onclick = function() {
alert(11);
// 1.传统方式删除事件
divs[0].onclick = nu11;
}
// 2. removeEventL istener 删除事件
divs[1]. addEventListener('click', fn) //里面的fn不需要调用加小括号
function fn( ) {
alert(22);
divs [1] . removeEventListener( 'click', fn);
}
⭕删除事件兼容性解决方案
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;
}
}
5.DOM事件流
事件流描述的是从页面中接收事件的顺序。
事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即DOM事件流。
⭕如给一个div 注册了点击事件:
DOM事件流分为3个阶段:
1.捕获阶段
2.当前目标阶段
3.冒泡阶段
●事件冒泡: IE最早提出,事件开始时由最具体的元素接收,然后逐级向上传播到到DOM最顶层节点的过程。
●事件捕获:网景最早提出,由DOM最顶层节点开始,然后逐级向下传播到到最具体的元素接收的过程。
如向水里面扔一块石头,首先它会有一个下降的过程,这个过程就可以理解为从最顶层向事件发生的最具体元素(目标点)的捕获过程;之后会产生泡泡,会在最低点(最具体元素)之后漂浮到水面上,这个过程相当于事件冒泡。
⭕注意:
1. JS代码中只能执行捕获或者冒泡其中的一个阶段。
2. onclick和attachEvent只能得到冒泡阶段。
3. addEventListener (type, listener [, useCapture])第三个参数如果是true ,表示在事件捕获阶段调用事件处理程序;如果是false (不写默认就是false ) ,表示在事件冒泡阶段调用事件处理程序。
4.实际开发中很少使用事件捕获,我们更关注事件冒泡。
5.有些事件是没有冒泡的,比如onblur、onfocus、onmouseenter. onmouseleave
6.事件对象
eventTarget.onclick = function (event) { }
eventTarget . addEventListener('click', function (event) { })
- 官方解释: event 对象代表事件的状态,比如键盘按键的状态鼠标的位置、鼠标按钮的状态。
- 简单理解: 事件发生后,跟事件相关的一系列信息数据的集合都放到这个对象里面,这个对象就是事件对象event ,它有很多属性和方法。
比如:
1.谁绑定了这个事件。
2.鼠标触发事件的话,会得到鼠标的相关信息,如鼠标位置。
3.键盘触发事件的话,会得到键盘的相关信息,如按了哪个键。
这个event是个形参,系统帮我们设定为事件对象,不需要传递实参过去。
当注册事件时, event对象就会被系统自动创建,并依次传递给事件监听器(事件处理函数)。
⭕事件对象的兼容性方案
事件对象本身的获取存在兼容问题:
1.标准浏览器中是浏览器给方法传递的参数,只需要定义形参e就可以获取到。
2.在IE6~8中,浏览器不会给方法传递参数,如果需要的话,需要到window.event中获取查找。
解决 : e = e | | window. event;
var div = document . querySelector( 'div' );
// div.onclick = function( event) {
//console . log(event);
// }
//div . addEventListener( 'click', function(e) {
e =e II window. event ; //因为IE兼容性问题
console . log(e);I
})
// 1. event 就是一个事件对象写到我们侦听函数的小括号里面当形参来看
//2.事件对象只有有了事件才会存在,它是系统给我们自动创建的,不需要我们传递参数
// 3.事件对象是事件的一系列相关数据的集合跟事件相关的比如鼠标点击里面就包含了鼠标的相关信息,鼠标坐标啊,如果是键盘事件里面就包含的键盘事件的信息比如判断用户按下了那个键
// 4.这个事件对象我们可以自己命名比如event、evt、e
// 5.事件对象也有兼容性问题ie678通过window. event
7.事件对象的常见属性和方法
事件对象属性方法 | 说明 |
e.target | 返回触发事件的对象 标准 |
e.srcElement | 返回触发事件的对象 非标准ie6-8使用 |
e.type | 返回事件的类型比如click mouseover 不带on |
e.cancelBubble | 该属性阻止冒泡非标准ie6-8使用 |
e.returnValue | 该属性阻止默认事件(默认行为)非标准ie6-8使用比如不让链接跳转 |
e.preventDefault() | 该方法阻止默认事件(默认行为)标准比如不让链接跳转 |
e.stopPropagation() | 阻止冒泡标准 |
1️⃣// 1. e.target 返回的是触发事件的对象(元素)
this返回的是绑定事件的对象(元素)
var div = document . querySelector( 'div');
div . addEventL istener( 'click', function(e) {
console . log(e. target);
console .1og(this);
})
var ul = document . querySelector('ul' );
ul. addEventListener('click', function(e) {
//我们给ul绑定了事件 那么this就指向ul
console.log(this);
// e. target 指向我们点击的那个对象谁触发了这个事件,点击的是li e. target 指向的就是li
console.log(e.target);
})
//区别: e.target 点击了那个元素,就返回那个元素。this 那个元素绑定了这个点击事件,那么就返回谁
//了解兼容性
div. onclick = function(e) {
e = e window. event ;
var target = e.target || e. srcElement ;
console .log(target);
}
//跟this 有个非常相似的属性currentTarget ie678不认识
2️⃣// 传统的注册方式
a.onclick = function(e) {
//普通浏览器e. preventDefault(); 方法
e. preventDefault( );
//低版本浏览器ie678 returnValue属性
e. returnValue ;
// 我们可 以利用return false也能阻止默认行为没有兼容性问题特点:return后面的代码不执行了,而且只限于传统的注册方式
return false;
}
8.阻止事件冒泡
阻止事件冒泡的两种方式
事件冒泡: 开始时由最具体的元素接收,然后逐级向上传播到到DOM最顶层节点。
事件冒泡本身的特性,会带来的坏处,也会带来的好处,需要灵活掌握
阻止事件冒泡
1️⃣标准写法:利用事件对象里面的stopPropagation( )方法
e. stopPropagation ( )
2️⃣非标准写法:IE6-8 利用事件对象cancelBubble属性
e.cancelBubble = true; //非标准cancel 取消bubble 泡泡
阻止事件冒泡的兼容性解决方案
if(e && e. stopPropagation) {
e. stopPropagation( ) ;
}else{
window . event . cancelBubble = true;
}
9.事件委托(代理、委派)
咱们班有100个学生,快递员有100个快递,如果一个个的送花费时间较长。 同时每个学生领取的时候,也需要排队领取,也花费时间较长,何如?
解决方案: 快递员把100个快递,委托给班主任,班主任把这些快递放到办公室,同学们下课自行领取即可。
优势: 快递员省事,委托给班主任就可以走了。同学们领取也方便,因为相信班主任。
点击每个li都会弹出对话框,以前需要给每个li注册事件,是非常辛苦的,而且访问DOM的次数越多, 这就会延长整个页面的交互就绪时间。
事件委托也称为事件代理,在jQuery里面称为事件委派。
(1)事件委托的原理
不是每个子节点单独设置事件监听器,而是事件监听器设置在其父节点上,然后利用冒泡原理影响设置每个子节点。
以上案例:给ul注册点击事件, 然后利用事件对象的 target 来找到当前点击的li ,因为点击li,事件会冒泡到 ul 上, ul 有注册事件, 就会触发事件监听器。
(2)事件委托的作用
我们只操作了一次DOM ,提高了程序的性能。
//事件委托的核心原理:给父节点添加侦听器, 利用事件冒泡影响每一个子节点
var ul = document. querySelector('ul' );
ul. addEventListener('click', function(e) {
// e. target这个可以得到我们点击的对象
e. target. style. backgroundColor = ' pink' ;
})
10.常用的鼠标事件
鼠标事件 | 触发条件 |
onclick | 鼠标点击左键触发 |
onmouseover | 鼠标经过触发 |
onmouseout | 鼠标离开触发 |
onfocus | 获得鼠标焦点触发 |
onblur | 失去鼠标焦点触发 |
onmousemove | 鼠标移动触发 |
onmouseup | 鼠标弹起触发 |
onmousedown | 鼠标按下触发 |
1️⃣禁止鼠标右键菜单
contextmenu 主要控制应该何时显示上下文菜单,主要用于程序员取消默认的上下文菜单。
document . addEventListener ( ' contextmenu',function(e) {
e. preventDefault () ;//阻止默认行为
})
2️⃣禁止鼠标选中( selectstart开始选中)
document . addEventListener (' selectstart' , function(e) {
e. preventDefault () ;
})
3️⃣鼠标事件对象
event对象代表事件的状态,跟事件相关的一系列信息的集合。现阶段我们主要是用鼠标事件对象
MouseEvent和键盘事件对象KeyboardEvent。
鼠标事件对象 | 说明 |
e.clientX | 返回鼠标相对于浏览器窗口可视区的X坐标 |
e.clientY | 返回鼠标相对于浏览器窗口可视区的Y坐标 |
e.pageX | 返回鼠标相对于文档页面的X坐标lE9+ 支持 |
e.pageY | 返回鼠标相对于文档页面的Y坐标IE9+支持 |
e.screenX | 返回鼠标相对于电脑屏幕的X坐标 |
e.screenY | 返回鼠标相对于电脑屏幕的Y坐标 |
✍🏻案例: 跟随鼠标的天使
这个天使图片一直跟随鼠标移动
案例分析
①鼠标不断的移动,使用鼠标移动事件: mousemove
②在页面中移动,给document注册事件
③图片要移动距离,而且不占位置,使用绝对定位即可
④核心原理:每次鼠标移动,都会获得最新的鼠标坐标,把这个x和y坐标做为图片的top和left值就可以移动图片
<img src="薯片.png" alt="">
<script>
var pic = document.querySelector('img');
document.addEventListener('mousemove', function (e) {
var x = e.pageX;
var y = e.pageY;
//千万不要忘记给left和top添加px单位
pic.style.left = x - 100 + 'px';
pic.style.top = y - 100 + 'px';
//走图片的一半就能居中
})
</script>
11.常用的键盘事件
事件除了使用鼠标触发,还可以使用键盘触发。
键盘事件 | 触发条件 |
onkeyup | 某个键盘按键被松开时触发 |
onkeydown | 某个键盘按键被按下时触发 能识别功能键比如ctrl shift 左右箭头啊 |
onkeypress | 某个键盘按键被按下时触发 但是它不识别功能键 比如ctrl shift箭头等 |
⭕注意:
1.如果使用addEventListener不需要加on
2. onkeypress 和前面2个的区别是,它不识别功能键,比如左右箭头, shift 等。
3.三个事件的执行顺序是: keydown-- keypress --- keyup
键盘事件对象
键盘事件对象属性 | 说明 |
keyCode | 返回该键的ASCII值 |
键盘事件对象中的keyCode属性可以得到相应键的ASCII码值
- keyup和keydown事件不区分字母大小写,a 和A得到的都是65
- keypress事件区分字母大小写, a 97和A得到的是65
⭕注意: onkeydown 和onkeyup不区分字母大小写, onkeypress区分字母大小写。
在实际开发中,我们更多的使用keydown和keyup ,它能识别所有的键(包括功能键)
Keypress不识别功能键,但是keyCode属性能区分大小写,返回不同的ASCII值。
✍🏻案例: 模拟京东按键输入内容
案例分析
①核心思路:检测用户是否按下了s键,如果按下s键,就把光标定位到搜索框里面
②使用键盘事件对象里面的keyCode判断用户按下的是否是s键
③搜索框获得焦点:使用js里面的focus()方法
var search = document . querySelector( ' input' );
document . addEventL istener( ' keyup', function(e) {
//keyup后再做判断所以输入框中才没有文本,因此不能用keydown
if (e.keyCode === 83) {
search. focus();
}
})
✍🏻案例: 模拟京东快递单号查询
要求:当在文本框中输入内容时,文本框上面自动显示大字号的内容。
①快递单号输入内容时,上面的大号字体盒子 ( con )显示(这里面的字号更大)
②表单检测用户输入:给表单添加键盘事件
③同时把快递单号里面的值( value )获取过来赋值给con盒子( innerText )做为内容
var con = document . querySelector(' . con' );//放大的显示框
var jd_ input = document . querySelector('.jd' );
jd_ input . addEventListener( ' keyup', function() {
if (this. value ==' ') {
con.style.display =' none' ;
} else {
con. style.display = 'block';
con. innerText = this. value;
}
})
④注意: keydown和keypress在文本框里面的特点:他们两个事件触发的时候,文字还没有落入文本框中。
⑤keyup事件触发的时候,文字已经落入文本框里面了。keypress功能键用不了,例如backspace键
⑥当失去焦点,就隐藏这个con盒子
⑦当获得焦点,并且文本框内容不为空,就显示这个con盒子
//失去焦点,就隐藏这个con盒子
jd_ input . addEventListener( 'blur', function() {
con.style.display = ' none ' ;
}
//获得焦点,就显示这个con盒子
jd_ input. addEventListener( 'focus', function() {
if (this.value !== ' ') {
con. style.display ='block' ;
}
}
BOM
学习任务 :
- 能够说出什么是BOM
- 能够知道浏览器的顶级对象window
- 能够写出页面加载事件以及注意事项
- 能够写出两种定时器函数并说出区别
- 能够说出JS执行机制
- 能够使用location对象完成页面之间的跳转
- 能够知晓navigator对象涉及的属性
- 能够使用history提供的方法实现页面刷新
BOM概述
BOM ( Browser Object Model )即浏览器对象模型,它提供了独立于内容而与浏览器窗口进行交互的对象,其核心对象是window。
BOM由一系列相关的对象构成,并且每个对象都提供了很多方法与属性。
BOM缺乏标准, JavaScript 语法的标准化组织是ECMA , DOM的标准化组织是W3C , BOM最初是Netscape浏览器标准的一部分。
1️⃣DOM
●文档对象模型
●DOM就是把「文档」当做一个「对象」来看待
●DOM的顶级对象是document
●DOM主要学习的是操作页面先素
●DOM是W3C标准规范
2️⃣BOM
●浏览器对象模型
●把「浏览器」当做一个「对象」来看待
●BOM的顶级对象是window
●BOM学习的是浏览器窗口交互的一些对象
●BOM是浏览器厂商在各自浏览器上定义的,兼容性较差
📜BOM的构成
BOM比DOM更大,它包含DOM。
window对象是浏览器的顶级对象, 它具有双重角色。
1.它是JS访问浏览器窗口的一一个接口。
2.它是一一个全局对象。定义在全局作用域中的变量、函数都会变成window对象的属性和方法。
在调用的时候可以省略window ,前面学习的对话框都属于window对象方法,如alert()、prompt0等。
⭕注意: window下的一个特殊属性window.name
1.window对象的常见事件
window.onload = function() {}
//或者
window . addEventListener ("load" , function() {}) ;
window.onload是窗口(页面)加载事件,当文档内容完全加载完成会触发该事件(包括图像、脚本文件、CSS文件等),就调用的处理函数。
⭕注意:
1.有了window.onload就可以把JS代码写到页面元素的上方,因为onload是等页面内容全部加载完毕,再去执行处理函数。
2. window.onload传统注册事件方式只能写一次 ,如果有多个,会以最后一个window.onload为准。
3.如果使用 addEventListener则没有限制
①窗口加载事件
document . addEventListener ( ' DOMContentLoaded' , function() {})
DOMContentLoaded事件触发时,仅当DOM加载完成,不包括样式表,图片, flash等等。le9以上才支持。
如果页面的图片很多的话,从用户访问到onload触发可能需要较长的时间,交互效果就不能实现,必然影响用户的体验,此时用DOMContentLoaded事件比较合适。
- load等页面内容全部加载完毕,包含页面dom元素图片flash、CSS等等
- DOMContentLoaded是DOM 加载完毕,不包含图片falsh css等就可以执行加载速度比load更快些
②调整窗口大小事件
window. onresize = function() { }
window. addEventListener ("resize", function() {});
window . onresize是调整窗口大小加载事件,当触发时就调用的处理函数。
⭕注意:
1.只要窗口大小发生像素变化,就会触发这个事件。
2.经常利用这个事件完成响应式布局。window.innerWidth 当前屏幕的宽度
if ( window. innerwidth <= 800) {
div.style.display ='none' ;
}
2.定时器
①setTimeout()定时器
window . setTimeout (调用函数,[延迟的毫秒数]) ;
setTimeout()方法用于设置-一个定时器,该定时器在定时器到期后执行调用函数。
1.这个window在调用的时候可以省略
2.这个延时时间单位是毫秒但是可以省略,如果省略默认的是0
3.这个调用函数可以直接写函数还可以写函数名,还有一个写法'函数名 ( )'
4.页面中可能有很多的定时器,经常给定时器加标识符(名字)
② 回调函数
setTimeout(这个调用函数也称为回调函数 callback)
普通函数是按照代码顺序直接调用。
而这个函数,需要等待时间,时间到了才去调用这个函数,因此称为回调函数。
简单理解:回调,就是回头调用的意思。上一 件事干完,再回头再调用这个函数。
以前的element.onclick = function(){}或者element. addEventListener("click", fn);里面的函数也是回调函数。
✍🏻案例: 5秒后自动关闭的广告
①核心思路: 5秒之后,就把这个广告隐藏起来
②用定时器setTimeout
// 在页面加载完毕后执行
window.onload = function() {
// 获取广告元素
var ad = document.getElementById('ad');
// 5秒后隐藏广告
setTimeout(function() {
ad.style.display = 'none';
}, 5000);
}
③停止setTimeout()定时器
window. clearTimeout (timeoutID)
clearTimeout ()方法取消了先前通过调用setTimeout () 建立的定时器。
⭕注意:
1. window可以省略。
2.里面的参数就是定时器的标识符。
④setInterval()定时器
window . set Interval (回调函数,[间隔的毫秒数]);
⭕注意:
1. window可以省略。
2.这个调用函数可以直接写函数,或者写函数名或者采取字符串'函数名()'三种形式。
3.间隔的毫秒数省略默认是0 ,如果写,必须是毫秒,表示每隔多少毫秒就自动调用这个函数。
4.因为定时器可能有很多,所以经常给定时器赋值一一个标识符。
- setTimeout 延时时间到了, 就去调用这个回调函数,只调用一次就结束了这个定时器
- setInterval 每隔这个延时时间,就去调用这个回调函数,会调用很多次,重复调用这个函数
✍🏻案例: 倒计时
①这个倒计时是不断变化的,因此需要定时器来自动变化( setInterval )
②三个黑色盒子里面分别存放时分秒
③三个黑色盒子利用innerHTML放入计算的小时分钟秒数
④第一次执行也是间隔毫秒数,因此刚刷新页面会有空白
⑤最好采取封装函数的方式,这样可以先调用一次这个函数,防止刚开始刷新页面有空白问题
var begin = document. querySelector( ' . begin' );
var stop = document. querySelector(' . stop ' );
var timer = null; //全局变量null是一个空对象
begin. addEventListener( 'click', function() {
timer = setInterval (function() {
console.log('***' ) ;
}, 1000);
})
stop. addEventListener( 'click', function() {
clearInterval(timer);
})
✍🏻案例: 发送短信
点击按钮后, 该按钮60秒之内不能再次点击, 防止重复发送短信。
案例分析
①按钮点击之后,会禁用disabled为true
②同时按钮里面的内容会变化,注意button里面的内容通过innerHTML修改(不是通过value)
③里面秒数是有变化的,因此需要用到定时器
④定义一个变量,在定时器里面,不断递减
⑤如果变量为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 = '发送';
time = 3;// 三需要重新开始
} else {
btn.innerHTML = '还剩下' + time + '秒';
time--;
}
}, 1000);
})
④this
this 的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,一般情况下this的最终指向的是那个调用它的对象。
1.全局作用域或者普通函数中this指向全局对象window (注意定时器里面的this指向window )
console . log(this);
function fn() {
console .log(this);
}
window. fn();
setTimeout(function(){ },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这个按钮对象
}
3.构造函数中this指向构造函数的实例
function Fun() {
console. log(this); // this指向的是fun实例对象
}
var fun = new Fun();
3.JS执行机制
JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。这是因为Javascript这门脚本语言诞生的使命所致一JavaScript 是为处理页面中用户的交互,以及操作DOM而诞生的。比如对某个DOM元素进行添加和删除操作,不能同时进行。应该先进行添加,之后再删除。
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。 这样所导致的问题是:如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。
①同步和异步
为了解决这个问题,利用多核CPU的计算能力,HTML 5提出Web Worker标准,允许JavaScript脚本创建多个线程。于是, JS中出现了同步和异步。
同步
前一个任务结束后再执行后一个任务,程序的执行顺序与任务的排列顺序是一致的、 同步的。比如做饭的同步做法:我们要烧水煮饭,等水开了( 10分钟之后) , 再去切菜, 炒菜。
同步任务
同步任务都在主线程上执行,形成一个执行栈。
异步
你在做一件事情时,因为这件事情会花费很长时间,在做这件事的同时,你还可以去处理其他事情。比如做饭的异步做法,我们在烧水的同时,利用这10分钟,去切菜,炒菜。
本质区别: 这条流水线上各个流程的执行顺序不同。
异步任务
JS的异步是通过回调函数实现的。
一般而言,异步任务有以下三种类型:
1、普通事件,如click、 resize等
2、资源加载,如load、error等
3、定时器,包括setInterval、setTimeout 等
异步任务相关回调函数添加到任务队列中(任务队列也称为消息队列)。
console .log(1) ;
setTimeout ( function () {
console.log(3) ;
},0);
console.log(2) ;
1.先执行执行栈中的同步任务。
2.异步任务(回调函数)放入任务队列中。
3. 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行。
②JS执行机制
console.log(1) ;
document. onclick = function () {
console.log('click') ;
}
console .log (2) ;
setTimeout ( function () {
console.log (3)
},3000)
③事件循环
由于主线程不断的重复获得任务、执行任务、再获取任务、再执行,所以这种机制被称为事件循环( event loop )。
4.location对象
window对象提供了一个location属性用于获取或设置窗体的URL ,并且可以用于解析URL。因为这个属性返回的是一个对象 ,所以这个属性也称为location对象。
①URL
统一资源定位符(Uniform Resource Locator, URL)是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的URL, 它包含的信息指出文件的位置以及浏览器应该怎么处理它。
URL的一般语法格式为:
protocol: //host [: port] /path/ [ ?query] # fragment
http: // www. itcast. cn/ index. html ?name=andy&age =18# link
组成 | 说明 |
protocol | 通信协议常用的http,ftp,maito等 |
host | 主机(域名) www.itheima.com |
port | 端口号可选,省略时使用方案的默认端口如http的默认端口为80 |
path | 路径由零或多个/符号隔开的字符串,一般用来表示主机上的一个目录或文件地址 |
query | 参数 以键值对的形式,通过&符号分隔开来 |
fragment | 片段#后面内容常见于链接锚点 |
②location对象属性
location对象属性 | 返回值 |
location.href | 获取或者设置整个URL |
location. host | 返回主机(域名) www.itheima.com |
location.port | 返回端口号 如果未写返回空字符串 |
location.pathname | 返回路径 |
location. search | 返回参数 |
location. hash | 返回片段 #后面内容常见于链接锚点 |
重点记住: href 和search
✍🏻案例: 获取URL参数数据
主要练习数据在不同页面中的传递。
案例分析
①第一个登录页面,里面有提交表单,action提交到index.html页面
②第二个页面,可以使用第一个页面的参数,这样实现了一个数据不同顶面之间的传递效果
③第二个页面之所以可以使用第一个页面的数据,是利用了URL里面的location.search参数
④在第二个页面中,需要把这个参数提取。
⑤第一步去掉 ? 利用substr substr('起始的位置',截取几个字符)没写则默认到最后
⑥第二步利用=号分割键和值 split( '= )
表单域,表单0
<form action="index.html">
用户名:<input type="text" name="uname" id="">
<input type="submit" value="登录">
</form>
<div></div>
<script>
//先去掉? substr('起始的位置',截取几个字符)
var params = location.search.substr(1);
//利用=把字符串分割为数组 split('=');
var arr = params.split('=');
var div = document.querySelector('div');
div.innerHTML = arr[1] + '欢迎您';
</script>
③location对象方法
location对象方法 | 返回值 |
location.assign() | 跟href一样,可以跳转页面(也称为重定向页面) |
location.replace() | 替换当前页面,因为不记录历史,所以不能后退页面 |
location.reload() | 重新加载页面,相当于刷新按钮或者f5 如果参数为true强制刷新ctrl+f5 |
5.navigator对象
navigator对象包含有关浏览器的信息,它有很多属性,最常用的是userAgent ,该属性可以返回由客户机发送服务器的user-agent头部的值。
下面前端代码可以判断用户那个终端打开页面,实现跳转
if( (navigator . userAgent . match (/ (phone IpadlpodI iPhone liPodl ios liPad |AndroidIMobile |BlackBerryI IEMobile |MQQBrowser I JUC I Fennec IwOSBrowser | Browse rNG| WebosISymbian |Windows Phone)/i))) {
window. location.href ="";//手机
} else {
window. location.href ="";//电脑
}
6.history对象
window对象提供了一个history对象,与浏览器历史记录进行交互。该对象包含用户(在浏览器窗口中)
history对象方法 | 作用 |
back( ) | 可以后退功能 |
forward( ) | 前进功能 |
go(参数) | 前进后退功能 参数如果是1前进1个页面如果是-1后退1个页面 |
history对象一般在实际开发中比较少用,但是会在一些OA办公系统中见到。
PC端网页特效
学习任务:
- 能够说出常见offset系列属性的作用
- 能够说出常见client系列属性的作用
- 能够说出常见scroll系列属性的作用
- 能够封装简单动画函数
- 能够写出网页轮播图案例
1.元素偏移量offset系列
①offset概述
offset翻译过来就是偏移量,我们使用offset系列相关属性可以动态的得到该元素的位置(偏移)、大小等。
●获得元素距离带有定位父元素的位置
●获得元素自身的大小(宽度高度)
●注意: 返回的数值都不带单位
📜offset系列常用属性:
offset系列属性 | 作用 |
element.offsetParent | 返回作为该元素带有定位的父级元素如果父级都没有定位则返回body |
element.offsetTop | 返回元素相对带有定位父元素上方的偏移 |
element.offsetLeft | 返回元素相对带有定位父元素左边框的偏移 |
element.offsetWidth | 返回自身包括padding、边框、内容区的宽度,返回数值不带单位 |
element.offsetHeight | 返回自身包括padding.边框、内容区的高度,返回数值不带单位 |
1️⃣ element.offsetTop、element.offsetLeft 可以得到元素的偏移 位置 返回的不带单位的数值
它以带有定位的父亲为准 如果么有父亲或者父亲没有定位则以body 为准。
2️⃣ element.offsetWidth、element.offsetHeight 可以得到元素的大小宽度和高度是包命padding + border + width
3️⃣ element.offsetParent 有定位的父亲否则返回的是body
.parentNode; //返回父亲是最近一 级的父亲 亲爸爸 不管父亲有没有定位。
2.offset与style 区别
1️⃣offset
●offset 可以得到任意样式表中的样式值
●offset系列获得的数值是没有单位的
●offsetWidth包含padding+ border +width
●offsetWidth等属性是只读属性,只能获取不能赋值
●所以,想要获取元素大小位置,用offset更合适
2️⃣style
●style 只能得到行内样式表中的样式值(内嵌样式表得不到)
●style.width 获得的是带有单位的字符串
●style.width 获得不包含 padding 和 border 的值
●style.width 是可读写属性,可以获取也可以赋值
●所以, 想要给元素更改值,则需要用style改变
✍🏻案例: 获取鼠标在盒子内的坐标
- 在盒子内点击,想要 得到鼠标距离盒子左右的距离。
- 首先得到鼠标在页面中的坐标( e. pageX, e.pageY)
- 其次得到盒子在页面中的距离(box. offsetLeft, box. offsetTop)
- 用鼠标距离页面的坐标减去盒子在页面中的距离,得到鼠标在盒子内的坐标
// 获取盒子元素
const box = document.querySelector('#box');
// 监听盒子的点击事件
box.addEventListener('click', function(e) {
// 获取鼠标在页面中的坐标
const pageX = e.pageX;
const pageY = e.pageY;
// 获取盒子在页面中的位置
const boxRect = box.getBoundingClientRect();
const boxLeft = boxRect.left;
const boxTop = boxRect.top;
// 计算鼠标在盒子内的位置
const mouseX = pageX - boxLeft;
const mouseY = pageY - boxTop;
// 输出结果
console.log(`鼠标距离盒子左边的距离为 ${mouseX}px,距离盒子上边的距离为 ${mouseY}px`);
});
✍🏻案例: 模态框拖拽
弹出框, 也称为模态框。
1.点击弹出层,会弹出模态框, 并且显示灰色半透明的遮挡层。
2.点击关闭按钮,可以关闭模态框, 并且同时关闭灰色半透明遮挡层。
3.鼠标放到模态框最上面一行, 可以按住鼠标拖拽模态框在页面中移动。
4.鼠标松开, 可以停止拖动模态框移动。
📜案例分析
①点击弹出层,模态框和遮挡层就会显示出来 display:block;
②点击关闭按钮,模态框和遮挡层就会隐藏起来 display:none;
③在页面中拖拽的原理:鼠标按下并且移动,之后松开鼠标
④触发事件是鼠标按下mousedown,鼠标移动 mousemove 鼠标松开 mouseup
⑤拖拽过程:鼠标移动过程中,获得最新的值赋值给模态框的 left 和 top 值,这样模态框可以跟着鼠标走了
⑥鼠标按下触发的事件源是最上面一行,就是 id 为 title
⑦鼠标的坐标减去鼠标在盒子内的坐标,才是模态框真正的位置。
🌟⑧鼠标按下,我们要得到鼠标在盒子的坐标。
⑨鼠标移动,就让模态框的坐标设置为: 鼠标坐标减去盒子坐标即可,注意移动事件写到按下事件里面。
// (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' ;
}
//注意offset和style.left的区别
// (3) 鼠标弹起, 就让鼠标移动事件移除
document . addEventListener( ' mouseup', function() {
document . removeEventListener( ' mousemove',move);
})
})
⑩鼠标松开,就停止拖拽,就是可以让鼠标移动事件解除
✍🏻案例: 仿京东放大镜
opacity 半透明
cursor: move ; // 鼠标十字形状
- 方向箭头叠加在其他图片上方,应该使用绝对定位,因为绝对定位完全脱标,完全不占位置。
- 父级盒子应该使用相对定位,因为相对定位不脱标,后续盒子仍然以标准流的方式对待它。
如果父级盒子也使用绝对定位,会完全脱标,那么下方的广告盒子会上移,这显然不是我们想要的
⭕结论:父级要占有位置,子级要任意摆放。
①整个案例可以分为三个功能模块
②鼠标经过小图片盒子,黄色的遮挡层和大图片盒子显示,离开隐藏2个盒子功能
③黄色的遮挡层跟随鼠标功能。
- 黄色的遮挡层跟随鼠标功能。
- 把鼠标坐标给遮挡层不合适。因为遮挡层坐标以父盒子为准。
- 首先是获得鼠标在盒子内的坐标。
- 之后把数值给遮挡层做为left和top值。
- 此时用到鼠标移动事件,但是还是在小图片盒子内移动。
④移动黄色遮挡层,大图片跟随移动功能。
🌟求大图片的移动距离公式
<script src=" "></script> //引入js
window . addEventListener( ' load', function ( ){ })
//等页面加载完再加载
//让鼠标在盒子中央——>让盒子往上走高度的一半,往左走宽度的一半
var maskX = x - mask.offsetWidth / 2;
⑦遮挡层不能超出小图片盒子范围。
⑧如果小于零, 就把坐标设置为0
⑨如果大于遮挡层最大的移动距离, 就把坐标设置为最大的移动距离
⑩遮挡层的最大移动距离:小图片盒子宽度减去遮挡层盒子宽度
if (maskX <= 0) {
maskX = 0;
} else if (maskX >= preview_ img . offsetWidth - mask . offsetWidth) {
maskX = preview_ img. offsetwidth - mask. offsetWidth;
}
if(maskY<=0){
maskY = 0;
} else if (maskY >= preview_ img . offsetHeight - mask . offsetHeight) {
maskY = preview_ img. offsetHeight -mask.offsetHeight;
//注意不是offsetLeft
}
//遮挡层的最大移动距离
var maskMax = preview_ img . offsetWidth - mask . offsetWidth;
//大图片最大移动距离 = 大图宽度 - 盒子宽度
var bigMax = bigIMg. offsetWidth - big . offsetWidth;
maskX 遮挡层移动距离
//大图片的移动距离X
var bigX = maskX *bigMax / maskMax;
⭕注意给大图片添加定位
bigIMg. style.left = -bigX +'px' ;
bigIMg.style.top = -bigY +'px' ;
注意是负值!(因为是反方向走的)
3.元素可视区client系列
client翻译过来就是客户端,使用client系列的相关属性来获取元素可视区的相关信息。通过client系列的相关属性可以动态的得到该元素的边框大小、元素大小等。
client系列属性 | 作用 |
element.clientTop | 返回元素上边框的大小 |
element. clientLeft | 返回元素左边框的大小 |
element.clientWidth | 返回自身包括padding、内容区的宽度, 不含边框,返回数值不带单位 |
element.clientHeight | 返回自身包括padding、内容区的高度, 不含边框,返回数值不带单位 |
✍🏻案例: 淘宝flexible.js源码分析🌟
立即执行函数
(function( { })( ) 或者是(function( ){}( )), 不需要调用,立马能够自己执行的函数。第二个小括号可以看做是调用函数
主要作用:创建一个独立的作用域,避免命名冲突问题
里面所有的变量都是局部变量不会有命名冲突的情况。
rem布局 rem单位
- rem(root em)是一个相对单位,基准是相对html元素字体大小,类似em(父元素字体大小)
- 比如,根元素(html)设置font-size=12px,非根元素设置width:2rem;,转换成px就是24px。
- rem的优势:父元素文字大小可能不一致,但是整个页面只有一个html,可以很好的来控制整个页面的元素大小比例。
//获取的html的根元素
var docE1 = document . documentElement
//dpr物理像素比 获取当前游览器物理像素比,若拿不到则为1
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这个元素(或者是没在body中插入),则等着我们页面主要的DOM元素加载完毕再去设置body的字体大小
document. addEventListener( 'DOMContentLoaded', setBodyFontSize )
}
}
setBodyFontSize( );
DOMContentLoaded 页面主要要素加载完毕之后。
// set 1rem = viewWidth / 10
设置我们html元素的文字大小,即rem中多少像素
function setRemUnit() {
var rem = docEl. clientWidth / 10 //HTML宽度
docEl.style. fontSize = rem + ' px '
}
setRemUnit( );
// reset rem unit on page resize
当页面尺寸大小发生变化的时候,要重新设置下rem的大小
window. addEventListener( 'resize', setRemUnit)
//pageshow是重新加载页面触发的事件
window . addEventListener(' pageshow',function(e) {
if (e.persisted) {
setRemUnit( )
}
})
e. persisted返回的是true就是说,如果这个页面是从缓存取过来的页面,也需要从新计算一下rem 的大小
下面三种情况都会刷新页面都会触发load事件。
1. a标签的超链接
2. F5或者 刷新按钮(强制刷新)
3.前进后退按钮
但是火狐中,有个特点,有个“往返缓存”, 这个缓存中不仅保存着页面数据,还保存了DOM和JavaScript的状态;实际上是将整个页面都保存在了内存里。
所以此时后退按钮不能刷新页面。
此时可以使用pageshow事件来触发。这个事件在页面显示时触发 ,无论页面是否来自缓存。在重新加载页面中, pageshow会在load事件触发后触发;根据事件对象中的persisted来判断是否是缓存中的页面触发的pageshow事件,注意这个事件给window添加。
// detect 0.5px supports 有些移动端的浏览器不支持0.5像素的写法
if(dpr>=2){
var fakeBody = document . createElement( ' body')
var testElement = document. createElement( 'div' )
testE lement. style.border = ' . 5px solid transparent '
fakeBody . appendChild( testElement)
docE1. appendChild(fakeBody)
if (testElement. offsetHeight === 1) {
docEl. classList.add( 'hairlines')
}
docE1. removeChild(fakeBody)
}
4.元素滚动scroll系列
scroll翻译过来就是滚动的,使用scroll系列的相关属性可以动态的得到该元素的大小、滚动距离等。
scroll系列属性 | 作用 |
element.scrollTop | 返回被卷去的上侧距离,返回数值不带单位 |
element.scrolLeft | 返回被卷去的左侧距离,返回数值不带单位 |
element. scrollWidth | 返回自身实际的宽度,不含边框,返回数值不带单位 |
element.scrollHeight | 返回自身实际的高度,不含边框,返回数值不带单位 |
scroll翻译过来就是滚动的,使用scroll系列的相关属性可以动态的得到该元素的大小、滚动距离等。
(1)页面被卷去的头部
如果浏览器的高(或宽)度不足以显示整个页面时, 会自动出现滚动条。当滚动条向下滚动时, 页面上面被隐藏掉的高度,就称为页面被卷去的头部。滚动条在滚动时会触发 onscroll 事件。
// scroll滚动事件当滚动条发生变化会触发的事件
div . addEventListener( 'scroll', function() {
console. log(div . scrollTop);
})
✍🏻案例: 仿淘宝固定右侧侧边栏🌟
1.原先侧边栏是绝对定位
2.当页面滚动到一定位置,侧边栏改为固定定位
3.页面继续滚动,会让返回顶部显示出来
①需要用到页面滚动事件scroll因为是页面滚动,所以事件源是document(不是某一个元素)
②滚动到某个位置,就是判断页面被卷去的上部值。
③页面被卷去的头部:可以通过window.pageYOffset获得。如果是 被卷去的左侧window.pageXOffset
④注意,元素被卷去的头部是element.scrollTop ,如果是页面被卷去的头部则是window.pageYOffset
//banner.offestTop就是被卷去头部的大小,一定要写到滚动的外面,否则随着滚动function会失效
var bannerTop = banner.offsetTop
//当侧边栏固定定位之后应该变化的数值,top的数值不能还是固定死的,否则会掉下来
var sliderbarTop = sliderbar.offsetTop - bannerTop;
// 当页面被卷去的头部大于等于了固定值,此时侧边栏就要改为固定定位
if (window . pageYOffset >= bannerTop) {
sliderbar .style.position =' fixed ' ;
sliderbar.style.top = sliderbarTop + ' px';
} else {
sliderbar .style.position ='absolute' ;
sliderbar.style.top = ' 固定栏高度px ' ;
}
//获取main主体元素
var main = document . querySelector(' .main' );
var goBack = document . querySelector(' . goBack' );
var mainTop = main. offsetTop;
(2)页面被卷去的头部兼容性解决方案
需要注意的是,页面被卷去的头部,有兼容性问题,因此被卷去的头部通常有如下几种写法:
1.声明了DTD(< !DOCTYPE>html),使用document .documentElement . scrollTop
2.未声明DTD ,使用document . body. scrollTop
3.新方法window . pageYOffset和window . pagexOffset,IE9 开始支持
function getScroll() {
return {
left: window . pagexoffset || document . documentElement . scrollLeft | | document . body . scrollLeftI 10,
top: window. pageYoffset | | document . documentElement. scrolltop 1I document . body . scrolltop | | 0
};
}
//使用的时候getScroll ().left
📜三大系列总结
三大系列大小对比 | 作用 |
element.offsetWidth | 返回自身包括padding、边框、内容区的宽度,返回数值不带单位 |
element.clientWidth | 返回自身包括padding、内容区的宽度, 不含边框,返回数值不带单位 |
element.scrollWiqth | 返回自身实际的宽度,不含边框,返回数值不带单位(包含超出部分) |
主要用法:
1. offset系列 经常用于获得元素位置offsetLeft ,offsetTop
2. client 经常用于获取元素大小clientWidth, clientHeight
3. scroll经常用于获取滚动距离 scrollTop, scrolleft
4.注意页面滚动的距离通过wi ndow . pageXoffset获得
5.mouseenter和mouseover的区别
mouseenter 鼠标事件
●当鼠标移动到元素上时就会触发 mouseenter 事件
●类似mouseover ,它们两者之间的差别是
●mouseover鼠标经过自身盒子会触发,经过子盒子还会触发。mouseenter 只会经过自身盒子触发
●之所以这样,就是因为mouseenter不会冒泡
●跟mouseenter搭配鼠标离开mouseleave同样不会冒泡
6.动画函数封装
核心原理:通过定时器setInterval()不断移动盒子位置。
实现步骤:
1.获得盒子当前位置
2.让盒子在当前位置加上1个移动距离
3.利用定时器不断重复这个操作
4.加一个结束定时器的条件
5.注意 此元素需要添加定位,才能使用element.style.left
⭕盒子一定要加定位!!
var div = document . querySelector('div');
var timer = setInterval( function() {
if (div.offsetLeft >= 400) {
//停止动画本质是停止定时器
clearInterval(timer);
}
div.style.left = div.offsetLeft + 1 + 'px';
//offseLeft获取当前的位置,修改赋值需要通过style
}, 30);
(1)动画函数简单封装
注意函数需要传递2个参数,动画对象和移动到的距离。
//简单动画函数封装obj目标对象target 目标位置
function animate(obj, target) {
var timer = setInterval(function() {
if (obj.offsetLeft >= target) {
//停止动画 本质是停止定时器
clearInterval(timer );
}
obj.style.left = obj.offsetLeft + 1 + ' px';
}, 30);
}
(2)动画函数给不同元素记录不同定时器
如果多个元素都使用这个动画函数,每次都要var声明定时器。可以给不同的元素使用不同的定时器(自己专门用自己的定时器)。
核心原理: 利用JS是一门动态语言,可以很方便的给当前对象添加属性。
//给不同的元素指定了不同的定时器
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);
}
7.缓动效果原理
缓动动画:让元素运动速度有所变化,最常见的是让速度慢慢停下来
思路:
1.让盒子每次移动的距离慢慢变小,速度就会慢慢落下来。
2.核心算法: (目标值-现在的位置) / 10 做为每次移动的距离步长
3.停止的条件是 :让当前盒子位置等于目标位置就停止定时器
4.注意步长值需要取整
function animate(obj, target) {
clearInterval(obj. timer);
obj. timer = setInterval (function() {
//步长值写到定时器的里面
var step = Math.ceil(target - obj.offsetLeft) / 10;
//取整避免出现小数走不满距离
if (obj.offsetLeft == target) {
clearInterval(obj. timer);
}
//把每次加1这个步长值改为一个慢慢变小的值 步长公式: (目标值-现在的位置) / 10
obj.style.left = obj.offsetLeft + step + 'px' ;
}, 30);
}
匀速动画:盒子当前的位置+固定的值
缓动动画:盒子当前的位置+变化的值 (目标值-现在的位置) / 10)
实现多目标值移动
可以让动画函数从800移动到500。
当点击按钮时候,判断步长是正值还是负值
1.如果是正值,则步长往大了取整
2.如果是负值,则步长向小了取整
var step = (target - obj.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math. floor(step);
//负值应该是向下取整的,可以通过三元表达式实现,注意obj.offsetLeft == target 是等于等于并非大于等于才能实现
8.动画函数添加回调函数
回调函数原理: 函数可以作为一个参数。将这个函数作为参数传到另一个函数里面,当那个函数执行完之后,再执行传进去的这个函数,这个过程就叫做回调。
回调函数写的位置:定时器结束的位置。
//回调函数写到定时器结束里面
if (callback) {
//调用函数
callback();
}
可以简化为:callback && callback() ;
短路运算符,即如果有参数传进来,有回调函数左边为true时才会去看右边,加括号调用。
9.动画函数封装到单独JS文件里面
因为以后经常使用这个动画函数,可以单独封装到一个Js文件里面,使用的时候引用这个Js文件即可。
✍🏻案例:当鼠标经过sliderbar就会让con这个盒子滑动到左侧
animate(con, -160, function() {
//通过回调函数修改内容,当动画执行完毕,就把左箭头改为右箭头
sliderbar . children[0]. innerHTML = '→';
});
10.常见网页特效案例
✍🏻案例: 网页轮播图🌟
轮播图也称为焦点图,是网页中比较常见的网页特效。
需要盒子,核心滚动区域的轮播图,左右侧箭头,切换点
盒子中的li需要浮动,保证在同一行内(前提是ul得非常宽,宽过展示的图片,因此li的轮播图才不会掉下来。因此ul可以设置为父级宽度的百分之几百)
功能需求+分析:
①因为js较多,单独新建js文件夹,再新建js文件,引入页面中。
②此时需要添加load事件。
1.鼠标经过轮播图模块,左右按钮显示,离开隐藏左右按钮。
mouseenter, mouseleave load
④显示隐藏display按钮
①动态生成小圆圈 (限定需要写清楚)
②核心思路:小圆圈的个数要跟图片张数一致
③所以首先先得到ul里面图片的张数(图片放入i里面,所以就是li的个数)
④利用循环动态生成小圆圈(这个小圆圈要放入ol里面)
⑤创建节点createElement(1i")
⑥插入节点ol. appendChild(li)
⑦第一个小圆圈需要添加current类
window. addEventListener(”load',function() {
var arrow_ l = document . querySelector(' . arrow-l' );
var arrow_ r = document . querySelector(' . arrow-r ' );
var focus = document . querySelector(' . focus ' );
var focusWidth = focus . offsetWidth; //图片宽度
focus . addEventL istener( ' mouseenter',function() {
arrow_ l.style.display = 'block' ;
arrow_ r .style.display = 'block' ;
clearInterval(timer);
timer = null; //清除定时器变量
});
focus . addEventL istener( ' mouseleave',function() {
arrow_ l.style.display =' none' ;
arrow_ r.style.display =" none' ;
timer = setInterval(function() {
//手动调用点击事件
arrow_ r.click();
}, 2000);
});
var ul = focus . querySelector('ul');
var ol = focus . querySelector(' .circle' );
for (var i = 0; i < ul.children.length; i++) {
//创建一个li
var li = document . createElement( 'li' );
//记录当前小圆圈的索引号 通过自定义属性来做
li. setAttribute( ' index', i);
//把小li插入到ol里面
ol. appendChild(li);
li . addEventListener('click',function() {
//干掉所有人 把所有的小li,清除current 类名
for (var i = 0; i < ol. children.length; i++) {
ol. children[i] . className =' ';
}
//留下当前的小li,设置current 类名
this. className = ' current' ;
//当点击了某个小li 就拿到当前小li的索引号,因此就要拿到当前小li的索引号
var index = this. getAttribute( ' index' );
//解决bug:1️⃣当点击了某个小li就要把这个li的索引号给num
num = index;
//2️⃣当点击了某个小li就要把这个li的索引号给circle
circle = index;
animate(ul, - index *focusWidth,); //注意负值
//一定要先引入animate.js
})
//把ol里面的第一个小li设置类名为current(即当前页面的所在点)
ol. children[0].className = ' current';
// 克隆第一张图片(li)放到ul最后面。并且克隆需要在for生成li之后。因此不会生成多了轮播图上的小圆圈
var first = ul. children[e]. cloneNode(true);
ul. appendChild(first);
// 点击右侧按钮, 图片滚动一张
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; //打开节流阀
}) ;
circle++ ;
//如果circle == 4 说明走到最后克隆的这张图片了,就需要复原
if (circle == ol.children.length) {
circle = 0;
}
//先清除其余小圆圈的current类名
// for (var i = 0; i < ol. children.length; i++) {
// ol. childrer[i] .className = ' ';
// }
//留下当前的小圆圈的current类名——>左右侧相同可以封装为函数
// ol. children[circle]. className = ' current' ;
circleChange( );
}
});
function circleChange() {
//先清除其余小圆圈的current类名
for (var i = 0; i < ol.children.length; i++) {
ol. children[i ]. className;
}
//留下当前的小圆圈的current类名
ol. children[circle ]. className = ' current' ;
}
}
11.节流阀
防止轮播图按钮连续点击造成播放过快。
节流阀目的: 当上一一个函数动画内容执行完毕,再去执行下一个函数动画,让事件无法连续触发。
核心实现思路:利用回调函数,添加个变量来控制,锁住函数和解锁函数。
- 开始设置一个变量 var flag= true;
- lf(flag) {flag = false; do something} 关闭水龙头
- 利用回调函数动画执行完毕, flag=true 打开水龙头
✍🏻案例: 返回顶部
滚动窗口至文档中的特定位置。
//里面的x和y不跟单位的,直接写数字即可
window.scroll(x, y)
①带有动画的返回顶部
②此时可以继续使用我们封装的动画函数
③只需要把所有的Ieft相关的值,改为跟页面垂直(上下)滚动距离相关就可以了
④页面滚动了多少距离,可以通过 window.pageYoffset 得到
⑤最后是页面滚动,使用 window.scroll(x,y)
goBack. addEventListener( 'click', function() {
//因为是窗口滚动所以对象是window
animalte (window, 0); //调用函数
});
// 滚动动画函数
function animate(obj, target, 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);
// 回调函数写到定时器结束里面
callback && callback();
}
// 把每次加1 这个步长值改为一个慢慢变小的值 步长公式:(目标值 - 现在的位置) / 10
//没到则继续滚动页面
window. scroll(0, window. pageYoffset + step)
//x永远为0,不需要左右滚动屏幕,通过值和步长累加
}, 15);
}
✍🏻案例: 筋头云案例
- 鼠标经过某个小li,筋斗云跟这到当前小i位置
- 鼠标离开这个小li,筋斗云复原为原来的位置
- 鼠标点击了某个小li,筋斗云就会留在点击这个小i的位置
①利用动画函数做动画效果
②原先筋斗云的起始位置是0(注意与li之间的层级问题)
③鼠标经过某个小li ,把当前小li的offsetLeft位置做为目标值即可
④鼠标离开某个小li ,就把目标值设为0
⑤如果点击了某个小li,就把Ii当前的位置存储起来,做为筋斗云的起始位置
span标签 | div标签 |
内联级元素 | 块级元素 |
用于对小块文本进行分组 | 用于将大块文本组合在一起 |
不得嵌套以避免混淆 | 通常嵌套 |
span
当想要以不同方式设置内容特定部分的样式或使用 JavaScript 对其进行操作时,您应该使用它。还可以将其用作内联元素的容器。
div
另一方面,如果要将大块内容组合在一起,以及要在网页上布局元素,则应使用该标记。
window . addEventL istener( ' 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(vari=0;i<lis.length:i++){
lis [i] . addEventList this: HTMLLElement, function() {
animate(cloud, this.offsetLeft);
});
lis [i] . addEventListener( ' mouseleave', function() {
animate(cloud, current);
});
lis [i ]. addEventListener('click', function() {
current = this .offsetLeft;
});
}
})
移动端网页特效
学习任务:
- 能够写出移动端触屏事件
- 能够写出常见的移动端特效
- 能够使用移动端开发插件开发移动端特效
- 能够使用移动端开发框架开发移动端特效
1.触屏事件
触屏事件概述
移动端浏览器兼容性较好,不需要考虑以前JS的兼容性问题,可以放心的使用原生JS书写效果,但是移动端也有自己独特的地方。比如 触屏事件touch (也称触摸事件) , Android和IOS都有。
touch对象代表一个触摸点。 触摸点可能是一根手指 ,也可能是-根触摸笔。触屏事件可响应用户手指(或触控笔)对屏幕或者触控板操作。
常见的触屏事件如下:
触屏touch事件 | 说明 |
touchstart | 手指触摸到一个DOM元素时触发(鼠标点击) |
touchmove | 手指在一个DOM元素上滑动时触发(鼠标滑动) |
touchend | 手指从一个DOM元素上移开时触发 |
TouchEvent是一 类描述手指在触摸平面(触摸屏、触摸板等)的状态变化的事件。这类事件用于描述一个或多个触点,使开发者可以检测触点的移动,触点的增加和减少, 等等。
touchstart、touchmove、 touchend 三个事件都会各自有事件对象。
触摸列表 | 说明 |
touches | 正在触摸屏幕的所有手指的一个列表 (length: 1) |
targetTouches | 正在触摸当前DOM元素上的手指的一个列表 |
changedTouches | 手指状态发生了改变的列表,从无到有,从有到无变化 |
当手指离开屏幕的时候,就没有了touches 和targetTouches 列表,但是会有changedTouches
因为一般都是触摸元素(元素注册触摸事件)所以最经常使用的是targetTouches
targetTouches[0] 就可以得到正在触摸dom元素的第一个手指的相关信息,比如手指的坐标等等。
2.移动端拖动元素
1. touchstart、touchmove、 touchend 可以实现拖动元素
2.但是拖动元素需要当前手指的坐标值我们可以使用targetTouches[0]里面的pageX和pageY
3.移动端拖动的原理:手指移动中,计算出手指移动的距离。然后用盒子原来的位置+手指移动的距离
4.手指移动的距离:手指滑动中的位置 减去手指刚开始触摸的位置
拖动元素三步曲:
(1 )触摸元素touchstart :获取手指初始坐标,同时获得盒子原来的位置
(2)移动手指touchmove :计算手指的滑动距离,并且移动盒子
(3 )离开手指touchend;
⭕注意: 手指移动也会触发滚动屏幕所以这里要阻止默认的屏幕滚动e.preventDefault();
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[e].pagex;
startYI= e. targetTouches[0].pageY;
X = this.offsetLeft;
y = this. offsetTop;
})
div.addEventL istener( 'touchmove', function(e) {
// 计算手指的移动距离: 手指移动之后的坐标减去手指初始的坐标
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(); //阻止屏幕滚动的默认行为
});