JS基础
JS包括两大部分:ECMAScript与WebAPIs
前者用于定义基础语法知识,后者包括DOM与BOM,分别操作文档与浏览器
语句与标识符
结束符
语句尾以分号或回车结束
也可以不加,浏览器会自动
规定:要么都写要么都不写
语句
JavaScript程序的单位是行(line),每一行就是一个语句,以分号结尾
在html中可以使用<script>标签内联JavaScript语句
标识符
用来识别各种值的合法名称,如变量名
由字母、$、_、数字组成,数字不能开头,保留字也不能作为标识符
变量
使用var或let关键字创建变量
let用于声明局部变量,是块级作用域
而var是函数级作用域
1
2
3
4
5
6{ let a = 1; var b = 2; } console.log(a) // 未定义 console.log(b) // 2注意:
- let不能重复声明
- let不存在变量提升
现在统一用let即可
规则:
- 不能用关键字
- 不能数字开头
- 区分大小写
规范:小驼峰命名法
变量提升:JavaScript是解释型语言,获取所有被声明的变量并一行行运行,造成的结果是所有变量的声明语句会被提升到代码头部
常量
使用const声明只读常量,const声明的变量不能改变值,因此一旦声明变量就可以立即初始化
作用域与let相同,只在声明所在的块级作用域内有效
const命令声明的常量也不提升,不能重复声明
推荐统一使用const,因为声明的变量基本不需要修改,若需要修改再改为let
引入到文件
嵌入到HTML文件中
写在html文件里,用script标签包裹
规范:script写在</body>上方
因为加载顺序是自顶向下的,若先加载的js期望修改下面的html,可能因为未被加载而失效
1
2
3
4
5
<body>
<script>
var age = 10
</script>
</body>引入本地独立JS文件
代码写在以.js结尾的文件内
通过script标签引入:中间无序写代码,写了也会被忽略
1
2
3
4
5
<body>
<script type="text/javascript" scr"./">
</script>
</body>引入网络来源文件
1
2
3
4
5
<body>
<script scr="url">
</script>
</body>内联JavaScript
代码可以写在标签内部
1
2
3
<body>
<button onclick="alert('111')">点击</button>
</body>I/O方式
prompt
弹出对话框,包含一条文字信息,提示用户输入文字
prompt('请输入姓名')
注意,接收到的数据是字符串类型,需要按需转换
如转换为整形可以使用+prompt()或parseInt(prompt())等
alert
在浏览器中弹出对话框
1
alert("我是弹出框");
write
使用document.write可以输出到页面
若输入的是标签,会被解析为网页元素
1
document.write("我是输出到页面");log
使用console.log可以输出到控制台
1
2
var age = 20;
console.log(age);
执行顺序
- 按HTML文档流顺序执行
- alert和prompt会跳过页面渲染先被执行
数据类型
JS是一门弱数据类型语言
JS一共由六种数据类型,ES6后又添加了两种新类型
原始类型
包括数值、字符串与布尔值和两种特殊值:
- null:空值,一般代表对象的空,把它作为尚未创建的对象
- undefined:未定义,一般代表数值的空,只声明不赋值的情况下变量默认为该值
其中数字类型中包含NaN,表示计算错误,是一个不正确或未定义的数学操作得到的结果
1
2
3
4
5
<script>
var a = 1;
var b = "1";
var c = true;
</script>引用类型
对象:一个对象可以看作是多个数据类型的容器
1
2
3
4
5
var user = {
a: 1,
b: "1",
c: true,
}typeof
用于判断(基本)数据类型
语法:
1
typeof var- 数值返回number
- 字符串返回string
- 布尔返回boolean
- 对象返回object(不准确,很多情况下都会返回object,如数组也属于对象,返回object)
类型转换
prompt取得的数据类型是字符串类型
隐式转换
默写运算符运行时,内部自动将数据类型进行转换
- 加号两边只要有一个是字符串,会把另一个转为字符串
- 除了加号以外的算术运算符都会把数据转为数字类型
显式转换
可以自己识别px
- Number():转化为数字型,若字符串内有非数字,转换失败结果为NaN
- parseInt():只保留整数
- parseFloat():可以保留小数
运算符
加减乘除取余,自加自减,复合运算符均与C++类似
严格相等/不等运算符
==或!=仅比较数值是否相同或不同
===或!==比较数值是否相同或不同的同时,匹配数据类型是否相同或不同
1
2
3
4
var a = "10";
var b = 10;
console.log(a==b); // true
console.log(a===b); // false布尔运算符
与运算符:&&
或运算符:||
非运算符:!
非布尔值取反:对于非布尔值取反时会将其转为布尔值
其中undefined、null、false、0、NaN、空字符串取反后为true,其余为false
运算符短路
在&&与||中,可以发生短路效果
字符串
字符串是零个或多个排在一起的字符,用单引号或双引号包裹
默认只能写在一行中,分成多行时需要在尾部添加\
模板字符串
以反引号`开头的字符串,可以在字符串中添加变量
这样就不需要将多个字符串用+号连接起来
1
var a = `<a href="${href}">${text}</a>`length方法
返回字符串长度
1
2
var s = "111";
console.log(s.length); // 3charAt方法
传入下标,返回对应字母
若参数为负数,或大于等于字符串长度,则返回空字符串
1
2
var str = "123";
console.log(str.charAt(0)); // 1concat方法
连接两个字符串,返回新字符串,不改变原字符串
concat可以接受多个参数,同时连接
若传入的不是字符串类型,则会转换为字符串类型
其实可以用+拼接字符串
1
2
3
4
5
var a = "1";
var b = "2";
var c = "3";
var re = a.concat(b,c);
console.log(re);substring方法
截取字符串,返回新字符串,不改变原字符串,第一个参数表示开始位置,第二个参数表示结束位置(不含)
- 若省略第二个参数,则表示子字符串一直到原字符串结束
- 若第一个参数大于第二个参数,则会自动更换两个参数的位置
- 若参数是负数,会自动把负数转为0
1
'12345'.substring(0,2); // '12'substr方法
substr的作用和substring一样,但第二个参数表示截取字符串长度
若省略第二个参数,则直到原字符串结束
若第一个参数是负数,表示倒数计算的字符位置
若第二个参数是负数,则会自动转为0,返回空字符串
indexOf方法
确定一个字符串在另一个字符串中第一次出现的位置,返回结果是匹配开始的位置,若返回-1则表示不匹配
也可以接受第二个参数,表示从该位置开始向后匹配
1
2
'hello'.indexOf('o'); // 4
'Hollo'.indexOf('o',3); // 4trim方法
用于去除字符串两端的空格、制表符、换行符、回车符等,返回新字符串,不改变原字符串
1
' hello world '.trim(); // 'hello world'- trimEnd():去除后面的
- trimStart():去除前面的
split方法
按给定规则分割字符串,返回一个由分割出来的子字符串组成的数组
- 若分割规则为空字符串,则返回的是每一个字符
- 若省略参数,则返回数组唯一成员是原字符串
- 可以接受第二个参数,限定返回数组的最大成员数
数组
方括号括起来的数组,任何数据都可以放进数组
不需要提前分配内存,可以一直往后加
遍历
除了传统方法,可以使用for in遍历数组
1
2
3
4
var a = [1,2,3,4,5];
for (var i in a){
console.log(a[i]);
}isArray方法
返回布尔值,表示参数是否为数组
push/pop方法
push用于在数组末端添加一个或多个元素,并返回添加新元素后的数组长度
可以同时添加多个数据,用逗号分隔
pop用于删除数组的最后一个元素,并返回该元素
shift/unshift方法
shift用于删除数组第一个元素,并返回该元素
unshift用于在数组第一个位置添加元素,并返回添加新元素后的数组长度
splice方法
用于删除指定元素,语法:arr.splice(起始位置, 删除元素个数)
第二个参数缺省默认删除到最后
1
arr.splice(1,2);join方法
以指定参数作为分隔符,将所有数组成员连接为一个字符串返回,默认用逗号分隔
若数组成员是undefined或null或空位,会被转换成空字符串
1
2
var a =[1,2,3,4];
a.join(' ') // '1 2 3 4'join可以配合split实现数组与字符串的互换
1
2
3
4
var arr = ['a', 'b', 'c'];
var myarr = arr.join("");
console.log(myarr);
console.log(myarr.split(""));concat方法
用于多个数组的合并,将新数组的的成员添加到原数组成员的尾部,返回一个新数组
也可以接受其他值类型作为参数添加到目标数组尾部
应用场景:上拉加载,合并数据
1
2
['a'].concat(['b','c']);
['a'].concat('b','c');reverse方法
颠倒数组元素,返回改变后的数组
1
2
var a=['a','b','c'];
a.reverse(); // ['c', 'b', 'a']indexOf 方法
返回给定元素在数组中第一次出现的位置,若没有出现则返回-1
也可以接受第二个参数,表示搜索开始的位置
1
2
3
var a = [1,2,3,4,3];
a.indexOf(3); // 2
a.indexOf(3,3); // 4函数
使用function命令,后面接代码名、圆括号里写入传参、大括号里写入函数体
使用return返回返回值,若没有return默认返回值是undefined
1
2
3
function print(s){
console.log(s);
}JavaScript将函数名视同变量名,采用function声明函数时,整个函数会提升到代码头部
作用域
限定某个变量的可用性的代码范围就是这个变量的作用域
函数内部的形参位于局部作用域
注意:若作用域内变量未声明就定义,默认为var,看作全局变量
访问原则:能够访问到的情况下,先局部再全局,即采用就近原则来查找变量最终的值
匿名函数
没名字的函数,两种使用方法:
将匿名函数赋值给一个变量,作为函数名,称为函数表达式
1
let f = fucntion(){return 1+1};不需要调用,直接执行,称为立即执行函数
注意:多个立即执行函数之间用分号分隔
1
2
3(function(){console.log(11)})(); (function(){console.log(11)}());
对象
JavaScript中的对象是一组键值对的集合,是一种无序的复合数据集合
声明
对象的每一个键名称为属性,可以是任何数据类型
对象通过对象名.属性读取
若一个属性的值为函数,将这个属性称为方法,可以像函数那样调用
若一个属性的值还是一个对象,就形成了链式引用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var user={
name: 'aaa',
f: function (name){
return name;
}
container:{
a: 1;
b: 2;
}
}
let user = new Object()
user.f('bbb');
console.log(user.container.a); // 1使用
- 增:
对象.新属性名 = 新值 - 删:
delete 对象名.属性名 - 查:
对象.属性或对象['属性'] - 改:
对象.属性 = 值
遍历
1
2
3
4
for(let k in obj){
console.log(k)
console.log(obj[k])
}内置对象
JS中提供了一些即时调用的内置对象
Math对象
Math.abs():返回参数的绝对值Math.max()/min():返回参数值中的最大值或最小值,若参数为空,则返回Infinity或-InfinityMath.floor()/ceil():对参数向下/向上取整Math.random():返回\([0,1)\)之间的伪随机数可以用以下方法生成一个任意范围的伪随机数
1
2
3
4
5function getRandomNumber(min, max){ return Math.random()*(max-min)+min; } getRandomNumber(5,10);
Date对象
Data对象是原生的时间库,以Unix时间戳表示
Date.now():返回当前时间距离时间零点的毫秒数,相当于Unix时间戳*1000get方法:Date对象提供了一系列get方法,用于获取实例对象某个方面的值
得到时间戳后,可以向Date对象传入时间戳,以获取具体信息
环境对象
指函数内部特殊的变量this,代表着当前函数运行时所处对的环境
函数的调用方式不同,this指代的对象也不同
大致规则:谁调用this就是谁,如果直接调用就是window.调用,this指代window
Web API
WebAPI分为DOM(文档对象模型)与BOM(浏览器对象模型)
DOM
DOM是用来呈现以及与任意HTML或XML文档交互的API
浏览器会根据DOM模型,将结构化文档解析为一系列节点,再组成一个树状结构(DOM Tree),所有节点和最终的树状结构,都有规范的对外接口
DOM只是一个接口规范,可以用各种语言实现,JS是最常用于DOM操作的语言
节点
DOM的最小组成单位是节点,每个节点可以看作文档树的叶子,节点有其中类型
- Document:整个文档树的顶层节点
- DocumentType:doctype标签
- Element:网页的各种HTML标签
- Attribute:网页元素的属性
- Text:标签之间或标签包含的文本
- Comment:注释
- DocumentFragment:文档片段
节点树
节点数将html文档以树状结构表现,直观体现了标签之间的关系
浏览器原生提供document节点,代表整个文档
除了根节点,其他节点有三种层级关系
父节点:直接上级节点
子节点:直接下级节点
同级节点:拥有同一个父节点的节点
DOM对象
浏览器根据HTML标签生成的js对象
- 所有标签属性都可以在这个对象上面找到
- 修改对象属性会自动映射到标签身上
如document对象就是DOM里提供的一个对象,他提供的属性和方法都是用来访问和操作网页内容的
Node.nodeType属性
不同节点的nodeType属性值不同,还有对应常量
获取DOM对象
document.getElementsByTagName():搜素HTML标签名,返回符合条件的元素,返回值是一个类似数组的对象,可以试试反应HTML文档变化,若没有任何匹配元素,则返回一个空集,若传入
'*'则返回文档中所有HTML元素可以使用
[index]选择需要的标签
1
2
3
4
5
6
7
8
9
<body>
<div>hello</div>
<div>world</div>
<script>
var divs = document.getElementsByTagName("div")[0];
console.log(divs);
</script>
</body>document.getElementsByClassName():
返回包含所有class名字复合指定条件的元素
参数可以是多个class,使用空格分隔
1
2
3
4
5
6
7
8
<body>
<p class="text">hello2</p>
<script>
var text = document.getElementsByClassName("text")[0];
console.log(text);
</script>
</body>document.getElementsByName():
选择拥有name属性的HTML元素,返回NodeList示例
1
2
3
4
5
6
7
8
<body>
<form name="a">hello3</form>
<script>
var names = document.getElementsByName("a");
console.log(names);
</script>
</body>document.getElementById():
返回匹配指定id属性的元素节点,若没有发现则返回null
document.querySelector():接受一个CSS选择器作为参数,返回匹配该选择器的元素节点,若有多个匹配条件,则返回第一个匹配的节点,若没有发现匹配的节点,则返回
nulldocument.querySelectorAll():
与上面用法类似,接受一个或多个有效的CSS选择器作为参数,区别是返回一个NodeList对象,包含所有匹配给定选择器的节点
注意,这里的NodeList对象是一个为数组,有长度与索引,但是没有数组方法,需要通过for遍历获得每一个对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<body>
<div class="nav">hello5</div>
<div class="nav">hello6</div>
<script>
var sel = document.querySelector(".nav");
console.log(sel);
var sels = document.querySelectorAll(".nav");
console.log(sels);
</script>
</body>创建/增加节点
document.createElement():生成元素节点并返回该节点
document.createTextNode():生成文本节点(text实例)并返回该节点,参数是文本节点的内容
document.createAttribute():生成一个新的属性节点(attr实例)并返回该节点
使用
id.value(值)对id赋值使用
var.setAttributeNode(id)将id放入元素,只有属性的放入有这个方法var.appendChild(var):将内容或子元素放入容器中
var.insertBefore(要插入的节点, 在哪个节点前面):插入到父元素中某个子元素的前面
var.cloneNode(boolen):克隆一个已有节点,其中true表示克隆时包含后代节点,false表示不包含,默认false
1
2
3
4
5
6
7
8
9
10
11
12
<body>
<script>
var text = document.createElement("p");
var content = document.createTextNode("text");
var id = document.createAttribute("id");
id.value = "root";
text.appendChild(content);
text.setAttributeNode(id);
console.log(text);
</script>
</body>修改节点
document.write():追加文本内容,且只能追加到
</body>之前的位置文本中的标签会被解析
查找节点
- 父节点查找:
Element.parentNode:返回最近一级的父节点,找不到返回null
- 子节点查找:
Element.childNode:返回所有子节点,包括文本,注释等Element.children:仅获得所有元素节点,返回一个NodeList对象
- 兄弟节点查找:
nextElementSibling:下一个兄弟节点previousElementSibling:上一个兄弟节点
删除节点
Element.removeChild(要删除的元素)
注意:
- 在原生DOM操作中,要删除元素必须通过父元素删除
- 若不存在父子关系则删除不成功
display:none是隐藏,这个才是真的删除
Element对象
Element对象对应网页的HTML元素,每个元素在DOM树上都会转换为一个Elment节点对象
Element.id:
返回指定元素的id属性,可读写
1
2
3
4var p = document.querySelector('p'); p.id; p.id = "123";Element.className:
用来读写当前元素节点的class属性,值是一个字符串,每个class之间用空格分割
1
2
3
4
5var div = document.getElementById('myDiv'); div.className; // 注意,一般要同时添加自身class div.className = "box0 box1";Element.属性:
其实标签中包含的元素属性都可以进行修改
1
2
3let pic = document.querySelector('img'); pic.src = './img/1.png'; pic.title = '这是图片'注意,对表单属性进行修改是,对于添加就有效果,移除就没有效果的,统一使用布尔值表示是否添加
Element.classList:
add():添加某classremove():移除某classcontains():判断是否存在某classtoggle():切换某个class
Element.innerHTML/innerText:
返回一个字符串,等同于该元素包含的所有HTML代码/文本,可读写
常用来设置某个节点的内容
与innerText的区别在于innerText无法识别标签,会被直接渲染为字符串
获取元素位置
前两者若有水平滚动条,则要减去水平滚动条的高度
document.documentElement.clientHeight返回当前视口的高度document.body.clientHeight返回网页的实际高度offsetLeft/offsetTop是相对于父级的,会一直往上找有定位的父级知道文档顶
CSS操作
HTML的style属性
最简单的方式是使用网页元素节点的setAttribute方法直接操作网页的style属性
setAttribute有两个参数,分别为设置样式的属性,设置的值
1
2
3
4
div.setAttribute(
'style',
'background-color:red'
);元素节点的style属性
1
2
3
4
var divstyle = document.querySelector('div').style;
divstyle.backgroundColor = 'red';
divstyle.border = '1px solid black';
divstyle.width = '100px';cssText形式
1
box.style.cssText = "width:200px;height:200px;backgroudn:red;"注意,若属性中有-连接符,需要转换为小驼峰命名,且不要忘记添加单位
事件处理程序
事件指的是系统内发生的动作或事情
HTML事件
创建一个函数来设置HTML中的事件,在对应事件属性中添加该函数名即可
缺点:HTML与JS没有分开
1
2
3
4
5
6
7
8
<body>
<button onclick="click()">点击</button>
<script>
function click(){
console.log("hello world");
}
</script>
</body>DOM0事件
实现了HTML与JS的分离,但是无法同时添加多个事件(后面的会覆盖前面的)
格式为Element.on事件 = function(){}
1
2
3
4
5
6
7
8
9
<body>
<button id="btn">点击</button>
<script>
var btn = document.getElementById("btn");
btn.onclick = function(){
console.log("点击了");
}
</script>
</body>DOM2事件
可以添加多个事件,实现事件监听
使用Element.addEventListener('事件', 要执行的函数)进行监听
1
2
3
4
5
6
7
8
<body>
<button id="btn">点击</button>
<script>
var btn = document.getElementById("btn");
btn.addEventListener("click",function(){console.log("1");});
</script>
</body>Event事件对象
事件发生后,会产生一个事件对象,作为参数传给监听函数
该对象内存储了事件触发时的相关信息
在事件绑定的回调函数的第一个参数就是事件对象,一般命名为e, event等
常见事件类型
- 鼠标事件
- 焦点事件
focus:获得焦点blur:失去焦点
- 键盘事件
keydown:键盘按下触发keyup:键盘抬起触发
- 文本事件
input:用户输入事件
属性
event.target返回事件当前所在的节点,返回后就可以进行编辑
1
2
3
4
5
6
7
8
9
10<body> <button id="btn">点击</button> <script> var btn = document.getElementById("btn"); btn.onclick = function(event){ console.log(event.target); } </script> </body>event.type返回字符串,表示事件类型
1
2
3
4
5
6
7
8
9
10
11<body> <button id="btn">点击</button> <script> var btn = document.getElementById("btn"); btn.onclick = function(event){ event.target.innerHTML = "after"; console.log(event.type); } </script> </body>event.clientX/clientY获取光标相对于浏览器可见窗口左上角的位置
event.offsetX/offsetY获取光标相对于当前DOM元素左上角的位置
event.key获取用户按下的键盘键的值
键盘事件
对于keyCode,现在不提倡使用,故删去
可以使用event对象中的key属性代替
表单事件
使用表单元素及输入框元素时可以监听的一系列事件
input事件
当<input>,<select>,<textarea>的值发生变化时触发,用户改变复选框与单选框选项时也会触发这个事件
input事件可以实时读取数据
1
2
3
4
5
6
7
8
9
10
<body>
<input type="text" id="username">
<script>
var username = document.getElementById("username");
username.oninput = function(e){
console.log(e.target.value);
}
</script>
</body>select事件
当在<input>,<textarea>里面选中文本时触发
1
2
3
4
5
6
7
8
9
10
11
<body>
<input type="text" id="username">
<script>
var username = document.getElementById("username");
username.onselect = function(e){
console.log(e.target.value);
}
</script>
</body>change事件
当<input>,<select>,<textarea>的值发生变化时触发,不会连续触发,只有当全部修改完成时才会触发(失去焦点或回车)
1
2
3
4
5
6
7
8
9
10
11
<body>
<input type="text" id="username">
<script>
var username = document.getElementById("username");
username.onchange = function(e){
console.log(e.target.value)
}
</script>
</body>reset与submit事件
发生在表单对象<form>中
reset事件当表单重置(所有表单成员变回默认值)时触发
submit事件当表单数据向服务器提交时触发
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<body>
<form id="myform" onsubmit="submitHandle">
<input type="text" id="username">
<button id="resetb">重置</button>
<button>提交</button>
</form>
<script>
var resetb = document.getElementById("resetb");
var myform = document.getElementById("myform")
reset.onclick = function(){
myform.reset();
}
function submitHandle(){
console.log("submit")
}
</script>
</body>事件流
事件被触发时,会经历两个阶段:捕获阶段与冒泡阶段
事件冒泡
当一个元素的事件被触发时,同样的事件会在该元素的所有祖先元素中依次被触发
即当一个元素触发事件后,会依次向上调用所有父级元素的同名事件
事件捕获
从DOM的根元素开始执行对应事件,需要手动编写代码
DOM.addEventListener(事件类型, 事件处理函数, 是否使用捕获机制)
- 第三个参数传入true代表捕获阶段触发,传入false代表冒泡阶段触发
- 若用DOM0事件监听,则只有冒泡阶段
事件代理/事件委托
由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父结点上,由父节点的监听函数统一处理多个子元素的事件,这种方法叫做事件的代理
1
2
3
4
5
6
7
8
9
10
11
12
<body>
<ul id="list">
<li>列表1</li>
<li>列表2</li>
</ul>
<script>
var list = document.getElementById("list");
list.onclick = function(e){
console.log("触发")
}
</script>
</body>如这里的事件是放在ul标签中的,但点击子元素时同样可以触发父元素事件
故我们可以将子元素事件统一放给父元素处理
使用Element.target可以获得真正触发事件的元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<body>
<ul id="list">
<li>列表1</li>
<li>列表2</li>
</ul>
<script>
var list = document.getElementById("list");
// list.onclick = function(e){
// console.log("触发")
// }
list.addEventListener("click", function(e){
if(event.target.tagName==="LI"){
console.log(event.target.innerHTML);
}
})
</script>
</body>DOM2事件也可以,指定监听标签
阻止事件流动
event.preventDefault()该方法可以取消浏览器对当前事件的默认行为,如点击链接后,浏览器会跳转到另一个页面,使用后就不会跳转了
1
2
3
4
5
6
7
8
9
10
11<body> <a href="https://baidu.com" id="it">点击</button> <script> var it = document.getElementById("it"); it.onclick = function(e){ e.preventDefault(); console.log("点击A"); } </script> </body>event.stopPropagation()若想将事件限制在当前元素内,就需要阻止事件流动
该方法可以阻断事件流动传播,在冒泡和捕获阶段都有效
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<body>
<div class="root" id="root">
<div class="box" id="box"></div>
</div>
<script>
var root = document.getElementById("root");
var box = document.getElementById("box");
it.onclick = function(e){
e.stopPropagation();
console.log("点击A");
}
</script>
</body>滚动事件
当我们滚动页面或某个元素时就会触发滚动事件,包括如下操作:
- 使用右侧滚动条
- 使用鼠标滚轮
- 点击锚点链接
- 调用JS函数
事件名称:scroll
使用addEventListener或onscroll树形绑定事件处理函数
监听整个网页滚动
给window对象绑定scroll事件
1
2
3
4
5
6
7
8
9
<div style="height: 6000px;">
1
</div>
<script>
window.onscroll = function(event) {
console.log('滚动');
};
</script>1
window.addEventListener('scroll', function(){})当滚动页面时,可以发现scroll函数一直在触发,控制台一直在打印
滚动偏移量
window对象有两个与滚动事件相关的属性scrollX或scrollY,返回当前页面水平与垂直滚动的像素数,返回double值,若没有滚动,默认为0
1
2
3
4
5
6
7
8
9
<div style="width: 3000px; height: 6000px;">
1
</div>
<script>
window.addEventListener('scroll',(event) => {
console.log(window.scrollX, window.scrollY)
});
</script>加载事件
加载外部资源完毕时触发的事件
不仅可以监听整个页面资源加载完毕,也可以针对某个资源绑定load事件
事件名:load
1
window.addEventListener('load', function(){})当初始HTML文档被完全加载和解析完成后,还会触发另一个事件DOMContentLoaded,而无需等待样式表与图像等完全加载
事件名:DOMContentLoaded
1
window.addEventListener('DOMContentLoaded', function(){})获取元素大小与位置
scroll
使用
scrollTop与scrollLeft设置或获取元素内容垂直滚动与从左边缘滚动的偏移数使用
scrollWidth与scrollHeight获取内容总宽高(不包括滚动条)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script>
let control = document.querySelector('#control');
control.addEventListener('click', function (e) {
// 获取滚动容器元素
let div = document.getElementById('scrollDemo');
// 获取点击的目标元素
let target = e.target;
// 处理每个按钮的行为
switch (target.id) {
case 'btnScrollLeft':
div.scrollLeft += 20;
break;
case 'btnScrollTop':
div.scrollTop += 20;
break;
}
});
</script>offset
使用
offsetWidth与offsetHeight获取元素自身宽高,包含自身设置的宽高,padding,border等使用
offsetLeft与offsetTop获取元素距离自己定位父级元素的左、上距离,均只读
client
使用
clientWidth与clientHeight获取元素的可见部分宽高(即不包括边框、进度条等)使用
clientLeft与clientTop获取左边框与上边框宽度,均只读
同时会在窗口尺寸改变时触发resize事件
1
window.addEventListener('resize', function(){})定时器
JS提供定时执行代码的功能,叫做定时器主要由setTimeout()和setInterval()两个函数来完成,它们向任务队列添加定时任务
setTimeout()
setTimeout()用来指定某个函数或某段代码在多少毫秒后执行,返回一个整数表示定时器的编号,接受两个参数,第一个参数是要推迟执行的函数名或一段代码,第二个参数是推迟执行的毫秒数
1
2
3
4
5
6
7
<body>
<script>
setTimeout(function(){
console.log("hello");
},3000);
</script>
</body>注意,如果回调函数是对象的方法,那么setTimeout使得方法内部的this关键字指向全局环境,而不是定义时所在的那个对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<body>
<script>
var name = "a";
var user = {
name: "b",
getName: function(){
setTimeout(function(){
console.log(this.name);
},3000);
}
}
user.getName();
</script>
</body>此时打印的是a
结局方法就是将this存储在临时变量里,使用临时变量读取成员变量
定时器可以取消:
1
2
var id = setTimeout(f, 1000);
clearTimeout(id);setInterval()
用法与上面一致,但setInterval指定某个任务每隔一段时间执行一次,setInterval返回一个该定时器的id
1
2
3
4
5
6
7
8
9
10
11
12
<body>
<script>
var user = {
var i = 0;
setInterval(function(){
i++;
console.log(i);
}, 1000);
</script>
</body>同样可以使用clearInterval(定时器id)清楚
实例化
使用new关键字对对象实例化,如时间对象
时间对象
- 获得当前时间:
let date = new Date(); - 获得指定时间:
let date = new Date(指定时间)
其他方法如下:
时间戳
获取方法:
获取时间戳,括号内可以指定时间
1
2let date = new Date(); console.log(date.getTime());+new Date():上面的简写Date.now():无需实例化,但是只能得到当前时间戳
BOM
BOM是浏览器对象模型,其中windows对象是浏览器内置的全局对象,下面包含了navigator、location、document、history、screen 5个属性
window对象
其中document是实现DOM的基础
依附于window对象的属性与方法使用时可以省略window
JS执行机制
JS是单线程,前一个任务结束才会执行后一个任务,如果JS执行时间过长,就会导致页面的渲染不连贯,有阻塞的感觉
因此HTML5开始,允许JS创建多个线程,出现了同步与异步
同步任务都在主线程上,形成一个执行栈
异步任务通过回调函数实现,一般有以下三种类型
- 普通事件
- 资源加载
- 定时器
异步任务相关添加到任务队列中
JS的执行顺序如下:
- 先执行执行栈中的同步任务
- 异步任务放入任务队列
- 一旦执行栈中的所有同步任务执行完毕,系统就会按序读取任务队列中的异步任务,被读取的异步任务结束等待,进入执行栈开始执行
这种机制被称为事件循环
location对象
location拆分并保存了url地址的各个部分
- herf属性获取完整url地址
- searchs属性获取地址中携带的参数,即
?后面的部分 - hash属性获取地址中的哈希值,即
#后面的部分 - reload()方法用于刷新当前页面,传入true时表示强制刷新
navigator对象
navigator记录了浏览器自身的相关信息
- userAgent属性用于检测浏览器版本与平台
1
2
3
4
5
6
7
8
9
10
11
//检测userAgent(浏览器信息)
!(function(){
constuserAgent=navigator.userAgent
//验证是否为Android或iPhone
constandroid=userAgent.match(/(Android);?[\s\/]+([\d.]+)?/)
constiphone=userAgent.match(/(iPhone\sOS)\s([\d_]+)/)
//如果是Android或iPhone,则跳转至移动站点
if(android|| iphone){
location.href='http://m.itcast.cn'
}
})()history对象
history与浏览器地址栏的操作相对应
- back()方法用于后退
- forward()方法用于前进
- go()方法用于前进后退若干页面,参数正数表示前进若干,负数表示后退若干
本地存储
localStorage
可以将数据暂存于用户浏览器中
生命周期永久生效,除非手动删除,否则关闭页面也会存在
可以多窗口共享
以键值对形式存储使用
方法如下:
存储:
localStorage.setItem(key, value)获取:
localStorage.getItem(key)删除:
localStorage.removeItem(key)
复杂数据类型需要转为JSON字符串
- 对象转为JSON,存储在本地:
JSON.stringify(数据) - JSON转换为对象,取出使用:
JSON.parse(JSON)
sessionStorage
- 生命周期为关闭浏览器窗口
- 在同一窗口下数据可以共享
- 键值对形式存储使用,用法与localStorage基本相同
防抖
防抖操作是优化性能的一种操作,对于短时间内连续触发的事件,防抖使得在某个时间期限内,事件处理函数只执行一次
滚动监听防抖
我们可以发现,滚动监听中的默认执行频率太高,会浪费性能
思路
在第一次触发事件时,不立即执行函数,而是给出一个期限值
- 若在200ms内没有再触发滚动事件,就执行函数
- 若再200ms内再次触发滚动事件,则当前计时取消,重新开始计时
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script>
function debounce(fn, delay){
var timer = null;
return function(){
if (timer){
clearTimeout(timer)
}
timer = setTimeout(fn,delay)
}
}
window.onscroll = debounce(scrollHandle,200)
function scrollHandle(){
var scrollTop = document.documentElement.scrollTop;
console.log(scrollTop);
}
</script>节流
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script>
function throttle(fn, delay){
var valid = true;
return function(){
if (!valid){
return false;
}
valid = false;
setTimeout(function(){
fn()
valid = true;
},delay)
}
}
window.onscroll = throttle(scrollHandle,2000)
function scrollHandle(){
var scrollTop = document.documentElement.scrollTop;
console.log(scrollTop);
}
</script>应用
- 搜索框input事件,支持实时搜索时可以使用节流,或实现某个输入间隔大于某个值
- 页面resize事件,常用于需要页面适配时,需要根据最终呈现的页面进行DOM渲染,一般使用防抖,因为只需要判断最后一次的变化
ECMAScript
ECMAScript是JS的一种规范
Babel转码器
该转码器可以将ES6代码转为ES5代码
通过在.babeirc中进行配置,用来设置转码规则与插件,
需要下载babel-env环境后配置如下内容
1
2
3
4
5
6
{
"presets": [
"@babel/env"
],
"plugins": []
}用法如下:
1
2
3
4
5
6
# 转码结果输出到标准输出
npx babel example.js
# 转码结果写入文件
npx babel example.js -o compiled.js
# 整个目录转码
npx babel src -d lib对象解构赋值
结构可以用于对象,对象属性没有次序,变量必须与属性同名,才能取到正确值
1
2
3
4
5
var user = {
name:"a",
age:10
}
let{name, age} = user常用于将现有对象方法复制到某个变量
1
let{log}=console字符串扩展
允许采用
\uxxxx形式来用unicode码点表示一个字符可以使用for…of循环遍历
1
2
3
for (let i of "abcd"){
console.log(i);
}includes():返回bool,表示是否找到参数字符串,第二个参数为开始搜索的位置startsWith():返回bool,表示参数字符串是否在原字符串的头部,第二个参数为开始搜索的位置endsWith():返回bool,表示参数字符串是否在原字符串的尾部,第二个参数为开始搜索的位置repeat():返回一个新字符串,表示将原字符串重复n次padStart():若某个字符串不够指定长度,会在头部自动补全padEnd():若某个字符串不够指定长度,会在尾部自动补全trimStart():消除字符串头部空格trimEnd():消除字符串尾部空格at():接受一个整数作为参数,返回参数指定位置的字符,支持负索引若超出字符串范围,返回
undefined
扩展运算符
由...组成的运算符,将一个数组转为用逗号分隔的参数序列
有了拓展运算符,就不需要使用apply方法将数组转为函数参数了
1
console.log(Math.max(...arr));可以使用扩展运算符合并数组
1
console.log([...arr1, ...arr2]);数组扩展
Array.form():将类数组转换为真正的数组常见类数组如下:
- arguments:函数中的可选参数,即在不需要传参的函数中传参时的参数,存在arguments中
- 元素集合:如document中选择所有指定标签组成的集合
- 类似数组的对象:一个对象,但是键是012下标,且存在一个length值
类数组只能读取元素,长度,不能使用数组方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script>
// arguments
function add(){
console.log(arguments);
var re = Array.from(arguments);
re.push(40);
console.log(re);
}
add(10,20,30);
// 元素集合
let titles = document.querySelectorAll('h3');
console.log(Array.from(titles));
// 类数组的对象
var user = {
"0": '小明',
"1": '小红',
"2": '小刚',
length: 3
}
console.log(Array.from(user));
</script>Array.of():用于将一组值转化为数组
1
Array.of(3,11,8);对象扩展
- 简介表示:ES6允许在大括号内直接写入变量和函数,作为对象的属性和方法,更简洁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script>
let name = "小明";
const user={
// 相同值的变量可以直接省略
name,
age: 18,
// 函数的简写
getName(){
console.log(this.name);
},
// 返回值的简写
getInfo(){
return {name, age};
}
}
</script>- 属性名表达式:允许字面量定义对象时,用表达式作为对象的属性名,即把表达式放到方括号内
1
2
3
4
5
6
<script>
let id = "a";
let user = {
[id]:"111"
}
</script>此时相当于a对应的111
- 对象扩展运算符:
...也可以用于对象
箭头函数
简化函数
1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
// 多个参数
let f = (x,y) => x + y;
// 单个参数
let f2 = x => x * 2;
// 无参数
let f3 = () => console.log('hello');
// 多行语句
let f4 = (x,y) => {
let sum = x + y;
return sum * 2;
};
</script>由于大括号被解释为代码块,所以若箭头函数直接返回一个对象,必须在外面加括号
用处:简化回调函数
1
2
3
4
5
6
7
<script>
// 简化回调
let arr = [10,20,30];
arr.map(item=>{
console.log(item);
})
</script>注意:箭头函数没有自己的this对象,内部的this就是定义时上层作用域中的this
set
定义
集合,成员唯一
Set()本身是一个构造函数Set()函数可以接受一个数组作为参数- 使用
add(x)向set中添加成员
1
2
3
const s = new Set(); // 直接构造
const s = new Set([1,2,3]); // 数组构造
[2,3,4].forEach(x=>s.add(x)); // 添加元素- 数组去除重复成员/字符
1
2
[...new Set(array)];
[...new Set('abcccd')].join('');- 注意,向set加入值时不会转换数据类型
方法
add():添加元素delete():删除某个元素,返回布尔值,表示是否删除has():返回一个布尔值,表示该值是否为set的成员clear():清除所有成员