一、JavaScript基本概念和作用
-
概念:JavaScript(简称“JS”) 是一种具有函数优先的轻量级,解释型的脚本语言。与HTML和CSS合称”前端三剑客“。
-
组成:ESMA Script + BOM + DOM
-
特点:
- 基于对象。JavaScript是一种基于对象的脚本语言,它不仅可以创建对象,也能使用现有的对象
- 动态性。JavaScript是一种采用事件驱动的脚本语言,它不需要经过Web服务器就可以对用户的输入做出响应。在访问一个网页时,鼠标在网页中进行鼠标点击或上下移、窗口移动等操作JavaScript都可直接对这些事件给出相应的响应
- 交互性。JavaScript由于其动态性,让他具有良好的交互性,这也是它能被广泛应用于各大浏览器的原因之一
- 简单易学。JavaScript是一种弱类型语言,对使用的数据类型未做出严格的要求,是基于Java基本语句和控制的脚本语言,其设计简单紧凑
- 跨平台性。JavaScript运行不依赖于操作系统,同时它不需要服务器的支持,仅需要用户的浏览器支持,不同于服务端脚本语言,它是一种客户端脚本语言(相较而言,服务端脚本语言的安全性高于客户端脚本语言),而当今大部分浏览器都支持它
-
作用:
-
JavaScript发展史:点击这里查看
ES各版本发布时间:(注意ES版本不等于JS版本)
- ES1:1997
- ES2:1998
- ES3:1999
- ES4:ESMA预计在2008发布,但因兼容性成本太高,遭各大浏览器厂商抵制,未能如愿发布,详情
- ES5:2009
- ES5.1:2011
- ES6:2015
- ES7:2016
- ES8:2017
- ES9:2018
- ES10:2019
- ES11:2020
- ES12:2021
-
学习平台:菜鸟教程
-
前端小Demo:👉这里
二、JavaScript的引入方式
JavaScript的引入方式可以类比CSS的引入方式,共有三种:内联样式引入、内部样式引入、外部样式引入
这些样式的优先级:内联样式>内部样式||外部样式 后面两个的优先级遵循就近原则
-
内联样式引入:(也称标签级引入方式)直接在HTML标记属性值中直接写入JavaScript代码
<!DOCTYPE html> <html lang="zn"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>引入方式测试1</title> </head> <body> <button type="button" onclick="alert('Hello World!')"> 点我!</button> <!-- onclick是点击事件,点击就执行""中的内容,alert是应该弹窗。综合就是点击"点我!"这个按钮就弹出一个有"Hello World!"的弹窗 --> </body> </html>
-
内部样式引入:(也称页面级引入方式)使用script标签进行引入Javascript代码
<!DOCTYPE html> <html lang="zn"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>引入方式测试2</title> <script> <!-- function hello() { console.log("Hello World");//浏览器控制台输出 } --> /* 注意当浏览器能够识别script标签时,script中的<!---->不会被识别; 当浏览器不能识别script标签时 ,<!---->会被识别, 因为所有的浏览器都能识别HTML的标签, 但是不是所有的浏览器都能识别js代码, 这样写的目的是防止js代码在不能识别js的浏览 器上报错。目的是提高容错(又学到一个小技巧) */ </script> </head> <body> <script>hello();</script> </body> </html>
-
外部样式引入:(也称项目级引入方式)将JS代码写在js文件中,html代码中使用script标签引入 (常用)
//js文件中的代码: function hello() { document.write("Hello World!");//网页输出 }
<!--html文件中的代码--> <!DOCTYPE html> <html lang="zn"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>引入方式测试3</title> <script src="js代码部分.js"></script> <!--注意script必须成对出现,不可写成<script/>--> </head> <body> <script> hello(); </script> </body> </html>
三、JavaScript基础语法
1、变量
-
变量的命名规则:
变量名可以由数字0~9、字母A~Z或a~z、下划线 “_” 或美元符 “$”组成,但是不能以数字开始 例如:1a、d-c 都是非法的变量名 _1a、$bs、d_as、_、$ 都是合法变量名
注:
-
变量的声明:
变量可以用var、let、const三种方式来定义,变量不需要指定某一种数据类型,被赋值后自动确定为相应的数据类型
var 变量名 = 值; //变量都是使用var关键字来声明 //例如: var a = 123;
'use strict';//进入严格模式,可以防止一些不严谨而出现的bug(严格模式中变量的定义不能使用var来定义)
反正使用Java的规则取命名和声明肯定不会错
注:JavaScript是一种弱类型语言可以不进行声明直接使用,例如:a=123。不使用var声明时,浏览器在解析JavaScript代码时会将其解释为全局变量。在ES6中规定用let关键字来定义局部变量。
var和let的区别:(参考***)
- let是块状作用域,而var是函数作用域(var定义的变量,内部函数可以访问外部函数的,反之不成立;而let定义的变量只能在一个{}代码块中有效,作用域为声明位置到该代码块结束位置)
- let不能在定义之前访问该变量,而var可以(因为var定义的变量会被预解析,输出的是undefined,而let就直接报错)
- let不允许变量在同一个作用域被重新定义,var允许
let 和 const很相似,作用域相同,都是块状作用域、不能重复定义、不能在定义之前访问该变量,它们主要的区别是const定义的变量不允许被重新赋值,而let允许,通常用const去定义常量
所有的全局变量都绑定在window对象上,可以通过window.变量名来访问全局变量。
2、数据类型
js中的数据类型可分为两大类:基本数据类型(7种) 和 引用数据类型(……种)
2.1 基本数据类型
基本数据类型共7种:Number、Boolean、String、Null、Undefined、Symbol、BigInt。其中前五种也叫作原始数据类型,是直接存储在栈中的简单数据段,占据空间小、大小固定,属于被频繁使用的数据。引用数据类型存储在堆内存中,占据空间大、大小不固定。引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址,当解释器寻找引用值时,会检索其在栈中的地址,取得地址后从堆中获得实体。
Number和BigInt范围的区别:
Number的范围:-9007199254740991 (-(2^53-1) ~ 9007199254740991(2^53-1) )
[Number.MIN_SAFE_INTEGER,Number.MAX_SAFE_INTEGER]
BigInt的范围:5e-324 ~ 1.7976931348623157e+308
2.1.1 Number
-
Number(数值型):表示所有类型的数值(js种把 整型和浮点型 都归为数值型)
//Number类型举例: var a=123; //整数123 var b=1.23;//浮点数1.23 var c=1.2e3;//科学计数法1.2*10^3 NaN //非数字 Infinity //超出Number的最大表示范围,表示无穷大
拓展:
NaN:
- NaN虽然表示是一个非数字 , 但是一般是算术运算执行的结果, 因此NaN 仍然是 数值类型
- 含NaN执行运算时 :如果是非加法运算,执行结果都是NaN;如果是加法运算 且 都是数值型的,结果为NaN,含有字符串型的,对数据进行拼接
- NaN 永远不会等于NaN。 NaN == NaN 执行结果是 false
- 只能通过isNaN( )来判断一个数是否为NaN
Infinity:JavaScript中数值的范围:5e-324 ~ 1.7976931348623157e+308
- 超过5e-324 的是负数的就是 -0,正数的就是 0。例如:-5e-325=-0,5e325=0
- 超过1.7976931348623157e+308的是负数的就是-Infinity,是正数的就是Infinity。2e+308=Infinity,-2e308=-Infinity
2.1.2 Boolean
- Boolean(布尔型):true、false
2.1.3 String
-
String(字符串型): 表示字符串或单给字符(将Java中的String和char类型合并了)
//可以使用 '字符串' 或 “字符串” 或 `字符串` 包裹,特殊字符用转义字符表示 var str1 = "hi!"; var str2 = "ghp"; var str3 = `${str1}${str2}`;//EL表达式,必须在``中使用 alert(str3);//弹出hi!ghp
//字符串的常见操作 var str = "study"; //1.输出字符串的长度 alert(str.length()); //2.输出字符串的第1个字符(Java不能这样输出,Java只能输出字符串数组的某一个字符) alert(str[0]);//注:不能单独使用str[]去改变字符串中的一个值 //3.字母大小写转换 str.toUpperCase(); str.toLowerCase(); //4.输出需要寻找字符第一次出现的索引位置 alert(str.indexOf('t'));//如果未找到返回-1 //5.输出字符串中[a,b)之间的字符串 alert(str.substring(1,3));//输出tu
2.1.4 Null
- Null(空):表示一个空对象。当对象不存在时,输出时会返回NUll
2.1.5 Undefined
-
Undefined(未定义型):表示变量不含有任何值
常见返回是undefined的情况:
-
变量是未定义(未声明)的状态
-
变量没有进行手动赋值,编译器会默认赋值undefined(和Java不同,Java会给未赋值的变量自动赋相应初值)
-
获取对象中不存在的属性时默认为undefined
-
函数需要实参,但是调用时没有传值,形参是undefined
-
函数调用没有返回值或者return后没有数据,接受函数返回值的变量默认为undefined
-
使用Map中的set方法时未为key添加value时,value默认为undefined
-
2.1.6 Symbol
-
Symbol(独一无二的值,ES6 新增):表示独一无二的值。Symbol 值通过 Symbol() 函数生成,其中Symbol()函数可以接收字符串参数,表示对 Symbol 实例的描述
2.1.7 BigInt
-
BigInt (大整数,能够表示超过 Number 类型大小限制的整数,ES11新增):可以用来更安全的表示Number范围外的数
拓展:
-
BigInt 类型不能用 Math 对象中的方法
-
不能和 Number 示例混合运。因为 JavaScript 在处理不同类型的运算时,会把他们先转换为同一类型,而 BigInt 类型变量在被隐式转换为 Number 类型时,可能会丢失精度,或者直接报错
-
BIgInt和Number能直接使用 “==”、“<”、“>” 进行比较
-
2.2 引用数据类型
Object(对象:是拥有属性和方法的数据) :在JavaScript中,除了基本数据类型其他的都是引用数据类型,相应的每种引用数据类型都有其相应的对象。JS中一切皆为对象,常见的有:Array、Function、Window、Documen、Date、RegExp……
对象的定义:
var person = { name : "ghp", age : 19, eamail : "12345678@qq.com" } var a = new person;//构造器创建对象
JavaScript中对象按定义可以分为:预定义对象和自定义对象。预定义对象是指系统提供的已经定义好的,可直接使用的对象,包含内置对象、文档对象和浏览器对象。按是否实例化可分为:实例化对象和普通对象。实例化对象,和Java中概念相同,抽象的类的对象不能被实例化。其实实例化有两个目的:一是为类创建了一个对象方便改变类中的属性值,二是一旦实例化,系统就会为该对象创建一个存储区域,该区域用来放你该对象属性
对象的操作:(和Java类似)
//1.对象的引用:(根Java类似) var a = person.name;//将对象person的name属性值赋给变量 //2.删减已有属性:delete 对象名.属性名; delete person.name; //3.添加新属性:对象名.属性名 = 属性值; person.sex = "男"; //4.判断属性是否在对对象中:属性值 in 对象名;(还可以用来判断对象父类中的属性) console.log("toString" in person);//输出true,toString是person的一个父类中的属性 //5.判断一个属性是否是这个对象自身拥有的:对象名.hasOwnProperty(); console.log(person.hasOwnProperty("toString"));//输出false //6.判断一个对象是否为另一个类的实例(还可以用于判断是否为另一个类的子类):对象名 instanceof 类名 var a = new Array(); console.log(a instanceof Array); //输出true console.log(a instanceof Object); /*输出true,Array是Object的子类(Object是所有对象的父类,也叫原型,在第四章会讲到)*/ console.log(a instanceof Function); //输出false //7.操作当前对象的属性和方法:this(java中是无法控制this的指向的,但是JS中可以通过apply控制this指向) var Person = { name : "ghp", birth : 2002, age : getAge } 1)当以普通函数的形式调用,this指向window对象 var getAge = fun(){ var now = new Date().getFullYear();//获取当前的年号 return this.age = now-this.birth; }; getAge();//this指向window,输出:NaN getAge.apply(Person,[])//输出:20。this指向Person,传入函数的参数为空 2)当以构造函数的形式调用,this指向新创建的对象 var a = new f(){ this.age = age; };//this指向a 3)当以方法的形式调用,this就指向当前调用的对象 console.log(Person.age);//this指向Person对象,输出:20 //8.批量操作指定对象的所有属性和方法:with(对象名) with(person){ var str = "姓名:" + name; var str = "年龄:" + age; var str = "性别:" + sex; }
2.2.1 Array
-
Array(数组):是预定义对象中的内置对象。用于在单个变量中存储多个值。(一给数组可以用来存多种类型的数据并且数组长度可变,和Java不同)
//数组的常规操作 var arr[1,2,3,4,5,6]; //1.可以使用arr[]改变索引处的字符 arr[2]=7;//将3改变成7,字符串.[]不能 //2. 可以使用arr.length改变数组长度(Java中不能改变数组长度,在Java中数组一经定义长度就确定了) arr.length=2;//数组中第2个值以后的值都丢失,数组长度变为2 arr.length=8;//数组的第7,第8个为undefined //3.输出需要寻找字符第一次出现的索引位置,未找到返回-1 console.log(arr.indexOf());//字符串的“1”和数字1不同 //4.截取数组的一部分,和操作字符串的substring()相似 console.log(arr.slice(1,3));//输出[2,3] //5.push、pop arr.push("7",8);//将"7"、8加入到arr的尾部,数组长度增加2,此时arr=[1,2,3,4,5,6,"7",8] alert(arr.pop());//输出arr最后一个元素,数组长度减1,此时arr=[1,2,3,4,5] //6.unshift(与push作用相反,添加元素在头部,数组长度增加)、shift(与pop作用相反,弹出头部的第一个元素,数组长度减1) //7.对数组进行排序:arr.sort(),根据Unicode编码的字符大小来排序 //8.将数组中的元素反转:arr.reverse(),此时arr=[6,5,4,3,2,1] //9.输出一个新数组,但是不改变原数组 console.log(arr.concat(7,"8"));//输出:[1,2,3,4,5,6,7,"8"],此时arr不变 //10.输出链接字符串,不改变元素组 console.log(arr.join("-"));//输出:1-2-3-4-5-6 //多维数组的定义: var arrs[[1,2],[],[]];
2.2.2 Function
-
Function(函数):既有预定义对象,又有自定义对象。是由事件驱动的或者当它被调用时执行的可重复使用的代码块
和Java中的“函数”有很大区别,严格来讲Java中是没有“函数”这个概念的,Java中函数是必须写在类中的,是类的一个成员,需要类创建对象然后通过创建的对象来引用类中的方法(或者说直接在该函数同一个类中使用函数名来引用),它不属于一种数据类型,是属于类中的成员(或属性)。而在JS中是有函数这个概念的,函数可以自己创建对象来被调用,同时它属于引用数据类型。
Java中函数和JS中“函数”的比较:
- Java中没有“函数概念”,只有“方法“概念。JS中函数和方法概念都有,JS中函数可以有相应的对象,可以直接调用也可以通过对象调用, 但是方法不能直接调用, 只能通过对象来调用,方法是一种特殊形式的函数(写在对象中或被对象属性调用的函数);
- Java中“方法”有重写和重载。JS中“函数”不能重载但能重写;
- Java中”方法“有分为普通方法和构造方法。JS中”函数“也分为普通函数和构造函数;
- java中的”方法“必须写在一个类中,是类的一个部分,通过类或类的实例化对象来调用。JS中”函数“是一种数据类型,可以单独写在外面,通过函数名调用或函数的实例化对象来调用;
- Java中”方法“的参数是强类型的,必须要传指定的数据类型、指定个数的数据。JS中函数的参数是弱类型参数,可以传任意个参数,也可以不传参数(参数默认为undefined),也可以传任意类型的参数。
-
函数的定义:
//1.标准定义 function f([形参1],[形参2],……){ ……; return 数据;//需要返回值就写 } //2.匿名定义 var f = function ([形参1],[形参2],……){//和方法1等价 ……; return 数据;//需要返回值就写 } f();//无返回值函数的调用6 var a = f();//有返回值函数的调用 //构造函数的定义: var Person = function(name, age, gender) { this.age = age; this.name = name; this.gender = gender; } var p = new Person("ghp", 20, "男");
构造函数和普通函数的定义是差不多的,只是习惯上我们会将构造函数的函数名首字母大写。主要区别是调用方式不同,普通函数是直接调用,而构造函数必须使用new关键值创建新对象,通过对象来调用
-
解决传参问题:(函数的参数除了是基本数据类型和引用数据类型,还可以是HTML中的元素)
//手动抛出异常来判断传入的参数是否是指定数据类型 var abs = function(a) { if (typeof a !== "number") { throw "not a Number"; } if (a > 0) { return a; } else { return -a; } } //操作多余的参数:arguments(本质是用一个数组来接收所有参数) var f = function(a) {//假设调用f时多传了b、c两个参数 for(var i=0;i<arguments.length;i++){//遍历所有传进来的参数 console.log("第"+i+"参数:"+arguments[i]); } } //获取除了已定义的参数外的其他参数:...rest var f = function(a,...rest){//假设调用f时多传了2、3两个参数 console.log("已定义的参数:"+a); console.log("未定义的参数:"+rest);//输出:未定义的参数:2,3 }
-
函数中变量的作用域及其定义规范:
-
内部函数可以访问外部函数的成员,反之不成立
-
使用变量时,有一个由内而外的查找过程,当内部函数和外部函数变量重名时,内部函数会屏蔽外部函数的变量
-
var定义的变量作用域为函数式作用域,只要在函数内部(非内部函数)定义就能访问,但要访问变量的所赋的值需要先定义后使用,否则为undefined;let定义的变量作用域为块状域,一定要先声明后使用否则会报错。所以一定要在函数的头部定义变量
-
JS中只有一个全局作用域,任何函数中的变量,假定没有在其函数的作用范围中找到,就会向外查找如果在全局变量中都没有找到,就会报错
Reference Error
-
由于所有的局部变量都会绑定在window对象上,在使用过程中很容易产生冲突。解决方法:自定义一个全局变量空间,将全局变量绑定在自定义的全局变量空间上
var SPACE = {};//写在文档的最开始位置 SPACE.PI = 3.1415926;//定义一个全局变量PI
-
2.2.3 Window
-
Window:是预定义对象中的浏览器对象,代表当前的浏览器窗口(一个浏览器可以有多个窗口,代表浏览器可以用多个Window对象),是其他所有浏览器对象的父级,同时也是唯一一个引用不需要声明的对象
Window对象中常用的方法:
- window.alter()
- window.innerHerght(); //获取浏览器窗口内部高度,窗口
- window.innerWidth();
- window.outerHeight(); //获取整个浏览器窗口的高度
- window.outerWidth();
- window.screen.width(); //屏幕的宽、高
- window.screen.width();
- location.assign(‘url’); //实现网页跳转
Window对象中常用对话提示框方法:
-
Open(URL,window name, window features):打开新窗口。
-
Close():关闭一个浏览器窗口。
-
alert(message):弹出一个警示对话框。
-
prompt(message,defaultmessage):弹出一个提示对话框。
-
confirm(message):弹出一个确认对话框。
-
setTimeout(expression,time):设置一定时间后自动执行一次expression代表的代码,使用time设置时间,以毫秒为单位。
-
setInterval(expression,time,[args]):设置一个时间间隔,使expression代表的代码可以周期性地被执行。使用time设置时间,以毫秒为单位。
备注:
prompt()方法不但可以像alert()方法和confirm()方法一样显示信息,而且还提供一个文本框要求用户从键盘输入自己的信息,同时它还包括“确定”和“取消”按钮。
其基本语法格式:prompt(“提示信息1”,“提示信息2”);
2.2.4 Document
-
Document:document对象是浏览器对象,代表浏览器窗口中的文档,可以用来处理、访问文档中包含的HTML元素,如各种图像、超链接等。
write()方法,是document对象的最常用方法,他会自动解析HTML标签
document.write('<h1>待添加的文字<h1>'); //会在网页上输出h1标题类型的文字
document对象的属性有:
- title:表示文档的标题。
- bgColor:表示文档的背景色。
- fgColor:表示文档的前景色。
- alinkColor:表示点击时超链接的颜色。
- linkColor:表示未访问过的超链接颜色。
- vlinkColor:表示已访问过的超链接颜色。
- URL:表示文档的地址。
- lastModified:表示文档的最后修改时间。
拓展:
document.cookie; //获取本地信息
一些黑客可以通过伪造cookie或者劫持cookie使用你的信息盗刷卡
一般服务器会使用cookie:httpOnly;进行加密
history必须要有历史记录
-
history.back(); 网页回退
history.forward(); 网页前进
history.go(n); //n去正数表示前进几个网页 ,负数表示后退几个页面
<body> <a href="https:baidu.com">点击跳转到百度</a><br> <button onclick="advance()">点击前进</button> <script type="text/javascript"> var a = document.querySelector('button'); function advance() { history.forward(); } </script> </body>
2.2.5 Date
-
Date(日期):是JS中的预定义对象中的内置对象,其内部含有特有的方法,用于处理日期和时间
-
常用操作:
var now = new Date(); now.getFullYear();//得到当前的年份,下面同理 now.getMoth();//月 now.getDate();//日 now.getDay();//星期 now.getHours();//小时 now.getMinutes();//分 now.getSeconds();//秒 now.getTime();//时间戳,全世界统一且唯一动态变化的,起始:1970.1.1.00:00:00 console.log(new Date(now.getTime())); //对时间戳进行解码,输出当前时间:Sat Apr 16 2022 23:16:56 GMT+0800 (中国标准时间) console.log(now.toLocaleString());//输出本地时间:2022/4/16 23:23:44 now.toGMTString();//格林威治标准时间,输出:Sat, 16 Apr 2022 15:28:12 GMT
时间戳(time-stamp)是一个经加密后形成的凭证文档,它包括三个部分:
-
需加时间戳的文件的摘要(digest)
-
DTS收到文件的日期和时间
-
DTS的数字签名
一般来说,时间戳产生的过程为:用户首先将需要加时间的文件用Hash编码加密形成摘要,然后将该摘要发送到DTS,DTS在加入了收到文件摘要的日期和时间信息后再对该文件加密(数字签名),然后送回用户。
时间戳的作用:客户端在向服务端接口进行请求,如果请求信息进行了加密处理,被第三方截取到请求包,可以使用该请求包进行重复请求操作。如果服务端不进行防重放攻击,就会服务器压力增大,而使用时间戳的方式可以解决这一问题
-
-
2.2.6 …
RegExp(正则表达式)……
拓展:
数据类型的判断:typeof 参靠文章 => 这里
用法:typeof(表达式) 。eg:
console.log(tyeof((1+2)));//输出:number console.log(tyeof((1+2+"a")));//输出:string console.log(tyeof(hello));//hello是函数名,输出:function(经测试发现匿名函数前的变量名类型是函数) consele.log(tyeof(person));//输出:object
JSON:
JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式,采用完全独立于编程语言的文本格式来存储和表示数据。(早期都是使用 XML 数据交换格式的)
JSON的作用:
让数据的结构层次分明,简洁而清晰,易于人理解和阅读
易于机器解析和生成,并能够有效地提升网络传输效率
JSON和JS种对象的关系:JSON 是 JS 对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串
var obj = {a: 'Hello', b: 'World'}; //这是一个对象,注意键名也是可以使用引号包裹的 var json = '{"a": "Hello", "b": "World"}'; //这是一个 JSON 字符串,本质是一个字符串
JSON和JS对象的转换:
//将JSON字符串转换成JS对象 var obj = JSON.parse('{"a": "Hello", "b": "World"}'); //结果是 {a: 'Hello', b: 'World'} //将JS对象转换成JSON字符串 var json = JSON.stringify({a: 'Hello', b: 'World'}); //结果是 '{"a": "Hello", "b": "World"}'
3、符号
JavaScript中符号可以分为:运算符 和 转义字符,以及一些特殊字符。
注释符和Java中一样
3.1 运算符
运算表达式=运算符+数据,实质是一个值
3.1.1 算术运算符
”+“还可以用于字符串之间的连接,如:
var str1="123"; var str2="123"; var str3=str1+str2;//此时输出str3,会得到123123 //当一个表达式中有一个String类型,进行加法运算都会变成字符串的拼接
此外:”=“表示赋值
3.1.2 比较运算符
除表中的以外,还有 绝对等于:“===”,表示 数据类型相同 同时 值也相同(Java中没有)
3.1.3 逻辑运算符
运算符 | 功能描述 |
---|---|
&& | 逻辑与。同为真才为真,有一个为加,结果为假 |
|| | 逻辑或。同为假才为假,有一个为真,结果为假 |
! | 逻辑非。真为假,假为真 |
此外:“!!” 表示:将数据转成所对应的Boolean类型,例如:
var a = 123; var b = !!a; console.log(b);//输出:true。在js中+0(0)\-0转成Boolean型都是false,其他的数值都是true
3.1.4 位运算符
运算符 | 功能描述 |
---|---|
& | 位与。同为1才为1,有一个为0,结果为0 |
| | 位或。同为0才为0,有一个为1,结果为1 |
^ | 异或。不同取1,相同取0 |
~ | 取反。1取0,0取1 |
>> | 右移。等价于除以2 |
<< | 左移。等价于乘以2 |
此外:“~~” 表示:浮点数取整。例如:
var a = 3.1415926; var b = ~~a;//此时b=3
3.1.5 条件运算符
表达式1 ? 表达式2 : 表达式3 //当表达式1为真时,执行表达式2;为假时执行表达式3
3.2 转义字符
转义字符 | 功能描述 |
---|---|
\0 | 空格(\u0000) |
\b | 退格符(\u0008) |
\t | 水平制表符(\u0009) |
\n | 换行符(\u000A) |
\v | 垂直制表符(\u000B) |
\f | 换页符(\u000C) |
\r | 回车符(\u000D) |
\" | 双引号(\u0022) |
\’ | 单引号(\u0027) |
\\ | 反斜杠(\u005c) |
\xHH | 由两位十六进制数组成数值表示latin1字符 |
\XXX | 由三位十六进制数组成的数值(000~377)表示Unicode编码 |
\uHHHH | 由四位十六进制组成的数值来表示表示Unicode编码 |
退格符:用来删除前一个字符,对应键盘上的Backspace键
水平制表符:让字符串成4的倍数输出,不足的补空格
垂直制表符:让\v后面的数据换行同时接着与上面数据的末尾对齐输出 输出居然乱码了?
换行符:让光标往下一行(不一定到下一行行首,但通常是首行)
回车符:让光标重新回到本行开头
注意:在window操作系统中,Enter相当于 \r\n。参考文章 => 点击查看
4、流程控制
4.1 分支结构:
if(表达式)……else……
if(表达式)……else if(表达式)……else……
……无限套娃
4.2 循环结构:
//1.while循环:
while(表达式){
……;
}
do{
……;
}while(表达式);
//2.for循环:
for([let 变量赋值];判断;[表达式]){//[]中的内容可以省略
……;
}
for(var i in arr){//循环遍历数组时,i表示索引位置;循环遍历对象时;i表示一个属性,也能用person[i]表示和i是一样的效果
console.log(i);//输出:0,1……
}
//3.forEach循环:用于输出一个数组的所有值或对象属性的所有值
arr.forEach(function(value){
console.log(value);//循环输出数组arr中的值
});
//4.分支语句switch
switch (表达式){
case value1: 语句块1;
case value2: 语句块2;
case value3: 语句块3;
...
default: 语句块n;
}
//6.关键字:break、continue和Java中一样
拓展:
Map:给对象设置若干对相关联的键(key)和键值(value)
var map = new Map([["a",92],["b",99],["c",95]]);//输出:Map(3) {'a' => 92, 'b' => 99, 'c' => 95} //1.通过key获得value var i = map.get("a");//输出:92 //2.向map中增加一对新的key和value map.set("d",66);//输出:Map(4) {'a' => 92, 'b' => 99, 'c' => 95, 'd' => 66} //3.删除键,以及对应的键值 map.delete("a");//输出:Map(2) {'b' => 99, 'c' => 95}
Set:无序不重复的集合
var set = new Set([1,1,"1",2,3]);//set中只有一个1 //1.向set中添加一个元素,在末尾 set.add("ghp");//输出:Set(5) {1, '1', 2, 3, 'ghp'} //2.删除set中指定的一个元素 set.delete(1);//输出:Set(3) {'1', 2, 3}
Iterator:迭代器 (ES6新增)
//1.遍历数组 var arr = [1,2,3]; for(let i of arr){ console.log(i);//输出:1 2 3。和for in 是不同的,i表示的是arr的值 } //2.遍历map var map = new Map([["a",92],["b",99],["c",95]]); for(let i of arr){ console.log(i);//输出:(2)["a",92] (2)["b",99] (2)["c",95] }
原生具备 Iterator 接口的数据结构: (引自***)
Array、Map、Set、String、TypedArray、NodeList 对象、函数的 arguments 对象。
对于不具备Iterator接口的数据结构(主要是对象)都需要自己在Symbol.iterator属性上面部署,这样才会被for…of循环遍历。原因:对象(Object)之所以没有默认部署 Iterator 接口,是因为对象的哪个属性先遍历,哪个属性后遍历是不确定的,需要开发者手动指定。本质上,遍历器是一种线性处理,对于任何非线性的数据结构,部署遍历器接口,就等于部署一种线性转换。
四、面向对象编程
引言:
面向对象程序设计(Object Oriented Programming)作为一种新方法,其本质是以建立模型体现出来的抽象思维过程和面向对象的方法。模型是用来反映现实世界中事物特征的。任何一个模型都不可能反映客观事物的一切具体特征,只能对事物特征和变化规律的一种抽象,且在它所涉及的范围内更普遍、更集中、更深刻地描述客体的特征。通过建立模型而达到的抽象是人们对客体认识的深化。
1、原型
在JavaScript中有原型概念,和Java中的父类是相似的概念。原型的实现:
var Student{
name:"qingjiang",
age:2,
run:function(){
console.log(this.name+"run......");
}
};
var xiaoming = {
name:"xiaoming"
};
xiaoming._proto_ = Student;//小明的原型是Student函数对象,和Java中继承是类似的
xiaoming.run();//控制台输出:小明run......
2、继承
注:是在ES6引入的,使用关键字extend,和上面的原型是一样的效果,继承的本质就是原型
class Student{
constructor(name){//constructor是定义构造器的关键字,和Java中的构造方法是一样的效果
this.name = name;
}
hell(){
alert("hello");
}
}
var xiaoming = new Student("xiaoming");//实例化对象
class xiaoStudent extends Student{
constructor(name,grade){//重写父类的方法
super(name);//调用父类的方法
this.grade = grade;
}
}
😆感觉在写Java
拓展:
原型链:每一个对象都会有一个原型对象Object,Object本身也是一个对象,它自己的原型是它本身,如此就形成了一个原型循环链
每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么假如我们让原型对象等于另一个类型的实例,结果会怎样?显然,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立。如此层层递进,就构成了实例与原型的链条。这就是所谓的原型链的基本概念。——摘自《javascript高级程序设计》
五、操作DOM对象
引言:
DOM对象就是文档对象(document)
DOM模型:整个网页就是一个DOM树,将HTML中的每个标签看作一个结点,每个结点都可以看作一个对象,它们的层次关系表示标签在网页中的嵌套关系,同一层结点的左右次序表示这些标签对象出现的先后顺序。
对DOM的操作,要操作先要获取一个DOM节点。(对DOM的操作预示着:可以只用JS实现一个网页)
增、删、改、查
5.1 获取DOM节点
获取子节点:
document.querySelector('#idname'); //获取document节点中的拥有这个id名的节点
document.querySelector('.classname'); //获取document节点的第一个节点的拥有这个class类名的节点
document.querySelector("TagName"); //获取ducument节点中第一个标签的节点
document.querySelectorAll() //获取所有类型的节点
eg:
<!DOCTYPE html>
<html lang="zn">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Test</title>
</head>
<body>
<div id="father" class="father">
<p id="p1" class="p1">123</p>
<p id="p2" class="p1">456</p>
<p id="p3" class="p1">789</p>
</div>
<script> <!--js代码要写在HTML代码下面,否则需要写一个函数在此处调用-->
var a = document.querySelector("#p2");
a.innerText = '000';//将id="p2"的节点进行修改
var b = document.querySelector('.p1');
b.innerText = '999';//将document中的第一个class="p1"的节点进行修改
</script>
</body>
</html>
获取父级节点:
1. document.getElementById("idname").parentElement;
2. document.getElementById("idname").parentNode;
document.getElementById("idname").offsetParent; // 获取所有父节点,返回数据为数组类型
5.2 更新DOM节点
- innerText和innerHTML
var a = document.getElementById('diname');
a.innerText = '123';
a.innerHTML = '<strong>123<\strong>'; //能够转义HTML标签
可以通过获取的id操作相应的标签
例如:通过a.style.color= ‘red’; 更改css样式
- replaceChild
node.replaceChild (newnode,oldnode ) //只能使用父节点去操作替换下面的子节点
newnode : 必需,用于替换 oldnode 的对象。
oldnode : 必需,被 newnode 替换的对象。
eg:
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>无标题文档</title>
</head>
<body>
<div>
<span id="oldnode">JavaScript是一个很常用的技术,为网页添加动态效果</span>
</div>
<a href="javascript:replace()"> 将加粗改为斜体</a>
<script type="text/javascript">
function replace() {
var oldnode = document.getElementById('oldnode');
var newnode = document.createElement("i");
newnode.innerHTML = oldnode.innerHTML;//将旧节点的内容赋给新节点
oldnode.parentNode.replaceChild(newnode, oldnode);//替换旧节点原来在网页(DOM数)中的位置
}
</script>
</body>
</html>
5.3 删除节点
必须使用父节点删除子节点
object.removeChild();
<body>
<p id="p1">123</p>
<p id="p2">456</p>
<p id="p2">789 </p>
</body>
//方法1:
var self = document.getElementById('p1');
var father = self.parentElement; //获取p1的父节点
father.removeChild(self)//删除p1,可以将self换成p1
//方法2:
var self = document.getElementById('p1');
var father = self.parentElement; //获取p1的父节点
father.removeChild(father.children[0])//注意删除节点的时候,是动态变化的
father.removeChild(father.children[1])//此时删除的是第3个节点:789,因为当删除完第1个节点,第3个节点动态变化为第2个节点了
5.4 创建和插入节点
插入的节点是放在被插入节点的后面
创建节点:document.createElement()
插入节点:document.appendChild()
<!--html文件中的代码-->
<!DOCTYPE html>
<html lang="zn">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Test</title>
</head>
<body>
<div id="father" class="father">
<p id="p1" class="p1">123</p>
<p id="p2" class="p1">456</p>
<p id="p3" class="p1">789</p>
</div>
<script>
var a = document.getElementById('father');
var b = document.createElement('p');//创建一个新的节点,该节点为p标签
b.innerText = 'Hello World!';
a.appendChild(b);//此时在div的组后添加一个p标签,内容为:Hello World! 可以在浏览器中查看到该p标签
</script>
</body>
</html>
//插入css样式
var myStyle = document.createElement('style');//创建一个style节点
myStyle.setAttribute = ('type', 'text/css');//设置文档类型,可以省略不写
myStyle.innerHTML = 'body{background-color:green;}';//设置css样式
document.getElementsByTagName('head')[0].appendChild(myStyle);//注意要指明第一个,不然浏览器无法选中
//当然也可以直接使用document.querySelector('head').appendChild(myStyle);
拓展:
在JavaScript中,我们可以使用cloneNode()方法来实现复制节点。
语法:obj.cloneNode(bool)
说明:
参数obj表示被复制的节点,而参数bool是一个布尔值,取值如下:
(1)1或true:表示复制节点本身以及复制该节点下的所有子节点;
(2)0或false:表示仅仅复制节点本身,不复制该节点下的子节点;
使用这些方法去操作DOM节点太繁琐了,所以就诞生了JQuery
六、事件
在JavaScript中,事件往往是页面的一些动作引起的,例如当用户按下鼠标或者提交表单,甚至在页面移动鼠标时,事件都会出现
6.1 注册事件
给元素添加事件,称为注册事件或者绑定事件
注册事件可分为:传统注册方式 和 方法监听注册方式
-
传统注册方式:通常是利用“on”开头的事件+函数进行实践注册
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>Test</title> </head> <body> <button class="btns">鼠标点击触发点击事件1</button> <button class="btns">鼠标点击触发点击事件2</button> <script> var x = document.querySelectorAll('button'); //获取所有button节点 x[0].onclick = function() { //只对第一个button节点进行事件注册 alert("hi"); } </script> </body> </html>
注:
传统注册方式注册事件具有唯一性,DOM对象只能绑定一个事件,后面绑定的事件会覆盖掉前面绑定的事件,和CSS样式的规则一样
-
方法监听注册方式:利用addEventListener()方法(IE9之前的IE浏览器不支持,可以用AttachEvent()代替)W3C新标准
eventTarget.addEventListener(type,listener[,useCapture])
- eventTarget:目标对象
- type:事件类型,click、mouseover等,就是传统事件不带on
- listener:事件处理函数
- useCapture:可选参数,false(默认) || ture
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>Test</title> </head> <body> <button class="btns">鼠标点击触发点击事件1</button> <button class="btns">鼠标点击触发点击事件2</button> <script> var x = document.querySelectorAll('button'); //获取所有button节点 x[0].addEventListener('click', function() { alert('这是最新的事件注册方式'); }) </script> </body> </html>
注:
方法监听注册方式注册事件可以对DOM对象同时绑定多个不同类型的事件,不同事件按顺序执行,相同事件后面的覆盖前面的,和CSS样式的规则一样
6.2 删除事件
将注册的事件删除或解绑
-
传统删除事件
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>Test</title> </head> <body> <div>1</div> <div>2</div> <div>3</div> <script> var x = document.querySelectorAll('div'); //获取所有button节点 x[0].style.border = ('1px solid black'); //设置边框,更显眼 x[0].onclick = function { alert('hi'); x[0].onclick = null;//删除事件 } </script> </body> </html>
-
使用removeEventListener()方法删除事件(IE9之前的IE浏览器不支持,可以用detachEvent()代替)
eventTarget.removeEventListener(type,listener[,useCapture])
- eventTarget:目标对象
- type:事件类型,click、mouseover等,就是传统事件不带on
- listener:事件处理函数
- useCapture:可选参数,false(默认) || ture
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>Test</title> </head> <body> <div>1</div> <div>2</div> <div>3</div> <script> var x = document.querySelectorAll('div'); //获取所有button节点 x[0].style.border = ('1px solid black'); //设置边框,更显眼 x[0].addEventListener('click', f);//注:千万不要给函数加小括号 function f() { alert('hi'); x[0].removeEventListener('click', f); } </script> </body> </html>
注:要删除事件时,不能使用匿名函数的写法,因为此时用匿名函数会导致无法写明删除的是哪一个事件。同时在不使用匿名函数的写法时,在注册使事件时,不需要给函数加小括号
拓展:
不加小括号,直接使用函数名是传地址,这个可以用C语言测试,打印输出:printf(“%x\n”,f); ; 而使用小括号是代表调用函数,是传函数的返回值,如果这里加个小括号,就不需要点击事件函数会自动触发
6.3 DOM事件流
- 定义:事件发生时会在DOM节点上有一个特定的传递方向,我们将这一个过程称之为DOM事件流
主要有:捕获阶段、目标阶段、冒泡阶段
- 捕获阶段:网景最早提出,事件注册时从DOM最顶级节点向下传播,直至待处理事件的节点
- 目标阶段:传播到待处理事件的节点时称之为目标阶段
- 冒泡阶段:IE最早提出,事件从目标节点后,逐级向上传播直至DOM最顶级节点
- 示意图:假设给div节点绑定一个事件
-
DOM事件流阶段的执行
- JS代码只能执行捕获阶段或冒泡阶段
- 传统事件注册和attachEvent方法只能得到冒泡阶段
- addEventListener方法可以获取捕获阶段和冒泡阶段,当useCapture取值false时,执行冒泡阶段;取值true时执行捕获阶段
代码测试:
//useCapture默认取值为false时执行冒泡阶段 //点击son时,会先弹出son,再弹出father <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>Test</title> </head> <body> <div class="father" style="height: 100px;"> <div class="son" style="height:20px ;"></div> </div> <script> var y = document.querySelector('.son'); y.style.border = ('1px solid black'); //设置边框,更显眼 y.addEventListener('click', function() { alert('son'); }); var x = document.querySelector('.father'); x.style.border = ('1px solid black'); //设置边框,更显眼 x.addEventListener('click', function() { alert('father'); }); </script> </body> </html>
//useCapture取值为true时执行捕获阶段 //点击son时,会先弹出father,再弹出son <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>Test</title> </head> <body> <div class="father" style="height: 100px;"> <div class="son" style="height:20px ;"></div> </div> <script> var y = document.querySelector('.son'); // y.style.border = ('1px solid black'); //设置边框,更显眼 y.addEventListener('click', function() { alert('son'); }, true); var x = document.querySelector('.father'); x.style.border = ('1px solid black'); //设置边框,更显眼 x.addEventListener('click', function() { alert('father'); }, true); </script> </body> </html>
6.4 事件对象
常用事件对象event,这是一个形参通常可以写成:e、evt。再IE6、7、8需要写成window.event,
兼容型写法:
e = e | window.event
;同时它是浏览器给定的,它是一堆事件的集合
-
this和target方法的区别:
this指向我们绑定事件的对象;target指向触发事件的对象
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>Test</title>
</head>
<body>
<div class="father" style="height: 100px;">
<div class="son" style="height:20px ;"></div>
</div>
<script>
var x = document.querySelector('.father');
x.style.border = ('1px solid black'); //设置边框,更显眼
x.addEventListener('click', function(e) {
// console.log(this);//无论点击son还是father都是输出father
console.log(e.target)//点击son就输出son的div,点击father就输出father的div
});
var y = document.querySelector('.son');
y.style.border = ('1px solid black'); //设置边框,更显眼
//y.addEventListener('click', function(e) {
// console.log(this);
// console.log(e.target)
//});
</script>
</body>
</html>
- 阻止默认事件的发生:例如a标签的跳转
-
对于非IE主流浏览器:
node.addEventListerner('click',function(e){ e.preventDefault(); })
-
对于IE6、7、8
node.onclik = function(e){ e.returnvalu; }
-
对于传统的注册方式:使用return false;
node.onclik = function(e){ return false; }
注:return后面的代码是无法继续执行的,但是兼容性很好
-
阻止事件冒泡:e.stopPropagation (同时可以取消事件捕获)
点击son时,会先弹出son,再弹出father只弹出son注:哪里需要阻止就往哪里加,因为加在son里面,它只是阻止son不能往上冒泡,如果点击事件发生在father上,且father的父级也绑定了事件,那么仍会冒泡
//useCapture默认取值为false时执行冒泡阶段 <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>Test</title> </head> <body> <div class="father" style="height: 100px;"> <div class="son" style="height:20px ;"></div> </div> <script> var y = document.querySelector('.son'); y.style.border = ('1px solid black'); //设置边框,更显眼 y.addEventListener('click', function(e) { alert('son'); e.stopPropagation(); }); var x = document.querySelector('.father'); x.style.border = ('1px solid black'); //设置边框,更显眼 x.addEventListener('click', function(e) { alert('father'); }); </script> </body> </html>
拓展:
对于IE6、7、8的兼容性写法
if(e && e.stopPropagation){//如果浏览器认识e同时能够识别stopPropagation方法执行 e.stiopPropagation(); }else{ window.event.cancelBubble = true;//IE6、7、8只认识cancelBubble方法 }
-
事件委托:
事件委托:又称事件代理,在JQuery中称为事件委派。它是利用事件冒泡原理,通过父级节点来操作子节点,让程序做到只操作一次DOM节点就能操作多个DOM节点,能够有效提高程序的性能
//给父节点绑定事件,通过父节点的事件去改变字节点 <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>Test</title> </head> <body> <ul> <li>111</li> <li>222</li> <li>333</li> </ul> <script> var ul = document.querySelector('ul'); ul.addEventListener('click', function(e) {//点哪个哪个就变色 var x = e.target;//获取点击的节点 x.style.backgroundColor = 'green';//改变点击节点的背景色 // e.target.style.backgroundColor = 'green'; }) </script> </body> </html>
-
鼠标事件:
//让图片随着鼠标移动而移动
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>Test</title>
</head>
<img src="./images/guajian.png" alt="">
<body>
<script>
var img = document.querySelector('img');
document.addEventListener('mousemove', function(e) {
var x = e.pageX;
var y = e.pageY;
img.style.position = 'absolute'; //加绝对定位,让图片移动不影响其他东西
img.style.left = x - 160 + 'px'; //一定要记得加单位,其中-160是为了让图片居中
img.style.top = y - 160 + 'px';
})
</script>
</body>
</html>
- 键盘事件:
注:当使用最新的时间注册方式addEventListener()方法不需要写on。上面三个事件的执行顺序是: k e y d o w n → k e y p r e s s → k e y u p keydown\rightarrow{keypress}\rightarrow{keyup} keydown→keypress→keyup,其中 k e y p r e s s keypress keypress不能识别功能键。同时 k e y u p keyup keyup和 k e y d o w keydow keydow不区分大小写,但 k e y p r e s s keypress keypress区分
//判断用户按下的键keyCode方法
document.addEventListener('keydown', function(e) {
alert(e.keyCode);//打印出来的是相应键位的ascii码值
})
拓展:
//1.禁止鼠标右键弹出菜单 contextmenu <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>Test</title> </head> <body> <p>dslfjlasjgflsdjgl;</p> <script> var p = document.querySelector('p'); p.addEventListener('contextmenu', function(e) { e.preventDefault(); }) </script> </body> </html>
//2.禁止鼠标选中 selectstart var p = document.querySelector('p'); p.addEventListener('selectstart', function(e) { e.preventDefault(); })
//3. 获取鼠标焦点 node.focus();
mouseenter和mouseover的区别:
使用mouseover时鼠标经过自身盒子或者经过子盒子时都会触发,而mouseenter时只会经过自身盒子时才被触发
原因:mouseenter不会产生事件冒泡,而mouseover会产生事件冒泡
同样的mouseleave同样不会冒泡
6.5 BOM概述
BOM(Browser Oject Model)即浏览器对象模型,将整各浏览器看作是一个对象。它提供了独立于内容于浏览器窗口进行交互的对象,其核心对象是window,window中的方法是可以不需要使用 对象名.方法()来引用,同时它是所有浏览器对象的父级对象,并且它是由各浏览器厂商在各自的浏览器上自定义的,缺乏一个标准,所以兼容性较差。
JavaScript的标准是ECMA,DOM的标准的W3C,BOM最初是NEtscape浏览器的标准。需要注意的是:window包括document
- window是浏览器窗口的一个接口
- 它是一个全局对象,定义在全局作用域中的变量,函数都会绑定在window对象上,可以通过window.调用它们
-
load事件和DOMContentLoaded事件的区别:
- load:等页面内容全部(包括HTML标签、css、图片、音频、视频)加载完毕,在执行相应JS代码
- DOMContentLoaded:DOM(主要是HTML标签,不包括css、图片、音频、视频)加载完毕,执行相应JS代码
-
risize事件:浏览器窗口发生变化就执行相应JS代码
-
setTimeout(function,time) 定时器
time单位是毫毛:1s 对应的是 1000,表示间隔多久调用前面的函数,然后就结束 注:只调用一次,前面调用的函数不要加()
或者写成这样setTimeout(function(){},time);
-
setInterval(funtion,time) 定时器 注:重复调用
使用方法和setTimeout方法相同,每隔time调用一次
-
clearInterval(setintervalName):
//设置一个开启和关闭定时器的按钮 <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>Test</title> </head> <body> <button>开启定时器</button> <button>关闭定时器</button> <script> var btn = document.querySelectorAll('button'); var time = null; //一定要将函数的对象time定义成能被clearInterval()方法访问到,不然会报错 btn[0].addEventListener('click', function() { time = setInterval(function() { console.log("How are you?"); }, 1000); }); btn[1].addEventListener('click', function() { clearInterval(time); }) </script> </body> </html>
七、三大常用方法
7.1 offset
可以通过offset动态得到元素的位置、大小等
- 获取元素距离带有定位的父元素的位置(如果父级没有定位,以上一级有定位为主,都没有则以body为准)
- 获取元素自身大小(宽度、高度)
注:offset方法的返回值不带单位
- 相关属性:
offsetParent返回具有定位的父级标签(如果父级没有定位,以上一级有定位为主,都没有则以body为准),parentNode返回父级标签(无论是否具有定位);具体看代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Test</title>
</head>
<style>
.div1 {
position: absolute;
}
</style>
<body>
<div class="div1">
<div class="div2">
<div3 class="div3">
</div3>
</div>
</div>
<script>
var node = document.querySelector('div3');
console.log(node.offsetParent);//返回div1
</script>
</body>
</html>
动画原理:通过定时器setInterval()不断移动盒子的位置
- offset与style的区别
7.2 cilent
通过使用cilent相关属性可以获取元素可视区的相关信息,动态得到元素的边框大小、元素大小
- cilent相关属性:
7.3 scroll
- scroll相关属性:
八、动画
8.1 动画实现的基本原理
动画的实现通常需要搭配定时器setInterval来实现。
实现步骤:
- 获取当前盒子的位置 (注:需要添加动画的元素一定要加定位relative | absolute | fixed)
- 获取元素节点,使用style增加距离
- 利用定时器不断重复上面的操作
- 可以使用clearInterval清除定时器从而停止动画
<!--一个简单的从左往右的滑块-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Test</title>
</head>
<style>
div {
position: absolute;
left: 0px;
width: 100px;
height: 100px;
background-color: pink;
}
</style>
<body>
<div></div>
<script>
var div = document.querySelector("div");
setInterval(function() {
div.style.left = div.offsetLeft + 1 + "px";//只有元素添加了定位才能使用node.style.left
}, 10);//定时器设置10ms,设置间隔时间越少动画越流畅
</script>
</body>
</html>
8.2 动画函数封装
使用一个动画函数对某一类动画进行封装,能够减少很多重复的代码。
一般的动画函数需要两个参数:目标对象obj,target移动的距离
速度:
relative>absolute|fixed
,暂且不知道原因
<!--两个并列的滑块-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Test</title>
</head>
<style>
.div1 {
position: absolute;
left: 0px;
width: 100px;
height: 100px;
background-color: pink;
}
.div2 {
position: relative;<!--比absolute更快?-->
top: 100px;
left: 0px;
width: 100px;
height: 100px;
background-color: rgb(192, 255, 194);
}
</style>
<body>
<div class="div1"></div>
<div class="div2"></div>
<script>
function move(obj, target) {
var timer = setInterval(function() {
if (obj.offsetLeft >= target) {//达到条件就停止动画
clearInterval(timer);
}
obj.style.left = obj.offsetLeft + 1 + "px";
}, 50);
}
var a = document.querySelector(".div1");
var b = document.querySelector(".div2");
move(a, 100);
move(b, 300);
</script>
</body>
</html>
优化代码:给不同元素添加不同的定时器
因为前面的简单封装在调用一次move
函数就会在系统中开辟一个空间,很浪费资源核心原理:利用JS的动态性的特点,动态给对象添加属性
即只要将:上面代码的
var timer =...
改为obj.timer =...
以及clearInterval(timer);
改为clearInterval(obj.timer);
完善代码:增加点击触发事件
因为在实践中大部分情况是通过一个点击事件来触发js动画效果的
在HTML代码中添加一个按钮
button
在JS代码中获取button节点:
var bnt = document.querySelector("button")
然后绑定事件:btn.addEventListener(‘click’, function() {
move(a, 100);
});
但是会有一个bug,a会越点越快,因为每点击一次就会增加一个定时器。
需要在调用函数的最开始加一个清除定时器的代码:
clearInterval(obj.timer);
8.3 缓动动画
前面的动画效果都是匀速运动,缓动动画就是让元素的运动速度有所变化,最常见的是减速运动
减速公式: ( 目标值 − 现在的位置 ) / 10 (目标值-现在的位置)/10 (目标值−现在的位置)/10 (10是步长,步长越大减速效果更明显)
var step = (target-obj.offsetLest)/10 //向左越来越慢移动
上面step存在小数问题,因为除法是直接舍去小数部分的,会导致实际移动距离小于目标移动距离
对于正方向的移动,采用向上取整,对于负方向的移动,采用向下取整
var step = (target-obj.offsetLeft)/10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
8.4 给动画添加回调函数
回调函数是添加在动画结束的位置。动画结束后就执行回调函数。
<!--两个并列的滑块-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Test</title>
</head>
<style>
.div1 {
position: absolute;
left: 0px;
width: 100px;
height: 100px;
background-color: pink;
}
</style>
<body>
<div class="div1"></div>
<script>
function move(obj, target, callback) {
obj.timer = setInterval(function() {
if (obj.offsetLeft > target) {
clearInterval(obj.timer);
if(callback){//判断回调函数是否传进来。可以不加判断,但是严谨一点加好
callback();//当判定动画结束,就立刻执行该回调函数
}
}
obj.style.left = obj.offsetLeft + 2 + "px";
}, 15);
}
function f() {//定义回调函数
alert("动画已结束")
}
var a = document.querySelector(".div1");
move(a, 1000, f);
</script>
</body>
</html>