红魔咖啡馆

头发越掉越多,头发越掉越少

0%

【前端】JavaScript

JS基础

JS包括两大部分:ECMAScript与WebAPIs

前者用于定义基础语法知识,后者包括DOM与BOM,分别操作文档与浏览器

语句与标识符

结束符

语句尾以分号或回车结束

也可以不加,浏览器会自动

规定:要么都写要么都不写

语句

JavaScript程序的单位是行(line),每一行就是一个语句,以分号结尾

在html中可以使用<script>标签内联JavaScript语句

标识符

用来识别各种值的合法名称,如变量名

由字母、$、_、数字组成,数字不能开头,保留字也不能作为标识符

变量

使用varlet关键字创建变量

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); // 3

charAt方法

传入下标,返回对应字母

若参数为负数,或大于等于字符串长度,则返回空字符串

1
2
var str = "123";
console.log(str.charAt(0)); // 1

concat方法

连接两个字符串,返回新字符串,不改变原字符串

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); // 4

trim方法

用于去除字符串两端的空格、制表符、换行符、回车符等,返回新字符串,不改变原字符串

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-Infinity

  • Math.floor()/ceil():对参数向下/向上取整

  • Math.random():返回\([0,1)\)之间的伪随机数

    可以用以下方法生成一个任意范围的伪随机数

    1
    2
    3
    4
    5
    function getRandomNumber(min, max){
        return Math.random()*(max-min)+min;
    }
    
    getRandomNumber(5,10);

Date对象

Data对象是原生的时间库,以Unix时间戳表示

  • Date.now():返回当前时间距离时间零点的毫秒数,相当于Unix时间戳*1000

  • get方法:Date对象提供了一系列get方法,用于获取实例对象某个方面的值

    得到时间戳后,可以向Date对象传入时间戳,以获取具体信息

Date.get

环境对象

指函数内部特殊的变量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选择器作为参数,返回匹配该选择器的元素节点,若有多个匹配条件,则返回第一个匹配的节点,若没有发现匹配的节点,则返回null

  • document.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
    4
    var p = document.querySelector('p');
    p.id;
    
    p.id = "123";
  • Element.className:

    用来读写当前元素节点的class属性,值是一个字符串,每个class之间用空格分割

    1
    2
    3
    4
    5
    var div = document.getElementById('myDiv');
    div.className;
    
    // 注意,一般要同时添加自身class
    div.className = "box0 box1";
  • Element.属性:

    其实标签中包含的元素属性都可以进行修改

    1
    2
    3
    let pic = document.querySelector('img');
    pic.src = './img/1.png';
    pic.title = '这是图片'

    注意,对表单属性进行修改是,对于添加就有效果,移除就没有效果的,统一使用布尔值表示是否添加

  • Element.classList:

    • add():添加某class
    • remove():移除某class
    • contains():判断是否存在某class
    • toggle():切换某个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对象有两个与滚动事件相关的属性scrollXscrollY,返回当前页面水平与垂直滚动的像素数,返回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
  • 使用scrollTopscrollLeft设置或获取元素内容垂直滚动与从左边缘滚动的偏移数

  • 使用scrollWidthscrollHeight获取内容总宽高(不包括滚动条)

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
  • 使用offsetWidthoffsetHeight获取元素自身宽高,包含自身设置的宽高,padding,border等

  • 使用offsetLeftoffsetTop获取元素距离自己定位父级元素的左、上距离,均只读

client
  • 使用clientWidthclientHeight获取元素的可见部分宽高(即不包括边框、进度条等)

  • 使用clientLeftclientTop获取左边框与上边框宽度,均只读

同时会在窗口尺寸改变时触发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
    2
    let 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的执行顺序如下:

  1. 先执行执行栈中的同步任务
  2. 异步任务放入任务队列
  3. 一旦执行栈中的所有同步任务执行完毕,系统就会按序读取任务队列中的异步任务,被读取的异步任务结束等待,进入执行栈开始执行

这种机制被称为事件循环

location对象

location拆分并保存了url地址的各个部分

  • herf属性获取完整url地址
  • searchs属性获取地址中携带的参数,即?后面的部分
  • hash属性获取地址中的哈希值,即#后面的部分
  • reload()方法用于刷新当前页面,传入true时表示强制刷新

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():清除所有成员