JavaScript

JavaScript简介

JavaScript 是一种轻量级的脚本语言。所谓“脚本语言”,指的是它不具备开发操作系统的能力,而是只用来编写控制其他大型应用程序的“脚本”。

JavaScript 是一种嵌入式(embedded)语言。它本身提供的核心语法不算很多

JavaScript与ECMAScript的关系

ECMAScript和JavaScript的关系是,前者是后者的规格,后者是前者的一种实现。在日常场合,这两个词是可以互换的。
在这里插入图片描述

JavaScript语句、标识符

语句

JavaScript 程序的单位是行(line),也就是一行一行地执行。一般情况下,每一行就是一个语句

var num = 10;

语句以分号结尾,一个分号就表示一个语句结束

标识符

标识符(identifier)指的是用来识别各种值的合法名称。最常见的标识符就是变量名
标识符是由:字母、美元符号($)、下划线(_)和数字组成,其中数字不能开头

JavaScript保留关键字

JavaScript有一些保留字,不能用作标识符:arguments、
break、case、catch、class、const、continue、debugger、default、delete、do、else、enum、eval、export、extends、false、finally、for、function、if、implements、import、in、instanceof、interface、let、new、null、package、private、protected、public、return、static、super、switch、this、throw、true、try、typeof、var、void、while、with、yield。

变量

在这里插入图片描述

var num = 10; 

变量的重新赋值

var num = 10;
num = 20;

变量提升

JavaScript 引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。这造成的结果,就是所有的变量的声明语句,都会被提升到代码的头部,这就叫做变量提升(hoisting)。

var num=10;
console.log(num);

JavaScript引入到文件

在这里插入图片描述

嵌入到HTML文件中

<script>
   var age=20;
</script>

引入本地独立JS文件

<script type="text/javascript" src="xyy.js">
</script>

引入网络来源文件

 <script src=""https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js">
 </script>

JavaScript注释与常见输出方式

在这里插入图片描述

JavaScript注释

源码中注释是不被引擎所解释的,它的作用是对代码进行解释。Javascript 提供两种注释的写法:一种是单行注释,用//起头;另一种是多行注释,放在/和/之间。

// 这是单行注释

/* 
这是 
多行 
注释
*/

嵌入在HTML文件中的注释

<!-- 注释 -->

注释的快捷键:ctrl+/

JavaScript输出方式

JavaScript有很多种输出方式,都可以让我们更直观的看到程序运行的结果

// 在浏览器中弹出一个对话框,然后把要输出的内容展示出来,alert都是把要输出的内容首先转换为字符串然后在输出的
alert("要输出的内容");

document.write("要输出的内容");

//在控制台输出内容
console.log("要输出的内容");

数据类型

JavaScript 语言的每一个值,都属于某一种数据类型。JavaScript 的数据类型,共有六种。(ES6 又新增了第七种 Symbol 类型的值和第八种 BigInt类型)

数据类型分类

原始类型(基础类型)

在这里插入图片描述

var age=20;
var name="喜羊羊";
var learn=true;

合成类型(复合类型)

对象:因为一个对象往往是多个原始类型的值的合成,可以看作是一个存放各种值的容器
在这里插入图片描述

var user={
     name:"喜羊羊",
     age:12,
     learn:true
}

至于undefined和null,一般将它们看成两个特殊值。

typeof运算符

在这里插入图片描述
JavaScript 有三种方法,可以确定一个值到底是什么类型。现在需要接触到的就是typeof

数值返回number

typeof 123 // "number"

字符串返回string

typeof '123' //"string"

布尔值返回boolean

typeof false //"boolean"

对象返回object

typeof {}//"object"

null和undefined的区别

null与undefined都可以表示“没有”,含义非常相似。将一个变量赋值为undefined或null,老实说,语法效果几乎没区别。
null表示"没有对象",即该处不应该有值。典型用法是:

(1) 作为函数的参数,表示该函数的参数不是对象。
(2) 作为对象原型链的终点。

Object.getPrototypeOf(Object.prototype) // null 

undefined表示"缺少值",就是此处应该有一个值,但是还没有定义。典型用法是:

(1)变量被声明了,但没有赋值时,就等于undefined。
(2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。
(3)对象没有赋值的属性,该属性的值为undefined。
(4)函数没有返回值时,默认返回undefined。

 
var i;
i // undefined
 
function f(x){console.log(x)} f() // undefined var o = new Object(); o.p // undefined var x = f(); x // undefined 
var a = undefined;
var a = null; 

a变量分别被赋值为undefined和null,这两种写法几乎等价。

既然undefined和null的含义与用法都差不多,为什么要同时设置两个这样的值,这不是无端增加JavaScript的复杂度,令初学者困扰吗?Google公司开发的JavaScript语言的替代品Dart语言,就明确规定只有null,没有undefined!
这与JavaScript的历史有关。1995年JavaScript诞生时,最初像Java一样,只设置了null作为表示"无"的值。根据C语言的传统,null被设计成可以自动转为0。
JavaScript的设计者Brendan Eich,觉得这样做还不够,有两个原因。

首先,null像在Java里一样,被当成一个对象。但是,JavaScript的数据类型分成原始类型(primitive)和合成类型(complex)两大类,Brendan Eich觉得表示"无"的值最好不是对象。

其次,JavaScript的最初版本没有包括错误处理机制,发生数据类型不匹配时,往往是自动转换类型或者默默地失败。Brendan Eich觉得,如果null自动转为0,很不容易发现错误。

因此,Brendan Eich又设计了一个undefined。

Number(null) // 0 5 + null // 5 

JavaScript的最初版本是这样区分的:null是一个表示"无"的对象,转为数值时为0;undefined是一个表示"无"的原始值,转为数值时为NaN。

Number(undefined) // NaN 5 + undefined // NaN 

运算符之算术运算符

在这里插入图片描述

加减乘除运算符

加减乘除运算符就是基本的数学运算符效果

10 + 10; // 20
100 - 10; // 90
10 * 2; //20
10 / 5; 2

余数运算符

余数运算符是比较常用的,因为在逻辑思维上寻找规律,余数运算符是很好用的

13 % 5 // 3

自增和自减运算符

自增和自减运算符,是一元运算符,只需要一个运算子。它们的作用是将运算子首先转为数值,然后加上1或者减去1

var x=1;
var y=1;
++x //2
--y //0

自增和自减运算符有一个需要注意的地方,就是放在变量之后,会先返回变量操作前的值,再进行自增/自减操作;放在变量之前,会先进行自增/自减操作,再返回变量操作后的值。

var x = 1;
var y = 1;

x++ // 1
++y // 2
var x = 10;
var y = 20;
console.log(x++ + y);// 30

运算符之赋值运算符

在这里插入图片描述
赋值运算符(Assignment Operators)用于给变量赋值最常见的赋值运算符,当然就是等号(=)

// 将 1 赋值给变量 x
var x = 1;

// 将变量 y 的值赋值给变量 x
var x = y;

赋值运算符还可以与其他运算符结合,形成变体。下面是与算术运算符的结合

// 等同于 x = x + y
x += y

// 等同于 x = x - y
x -= y

// 等同于 x = x * y
x *= y

// 等同于 x = x / y
x /= y

// 等同于 x = x % y
x %= y

运算符之比较运算符

在这里插入图片描述
比较运算符用于比较两个值的大小,然后返回一个布尔值,表示是否满足指定的条件。

2 > 1 // true

JavaScript 一共提供了8个比较运算符。
< > <= == >=
!= 不相等运算符
严格不相等运算符 !==
严格相等运算符 ===

==:双等比较值
===:三等比较值和类型
“==”模糊等,意思就是在做运算判断时,只需其值相等即可,因为在运算之前会进行一个隐式转换,可以将等式两边的数值转换成相同类型,然后再进行判断。
“===”绝对等,意思就是在做运算判断时,等式两边的数值以及数据类型都必须相同。

运算符之布尔运算符

在这里插入图片描述

取反运算符(!)

布尔值取反

!true // false
!false // true

非布尔值取反

对于非布尔值,取反运算符会将其转为布尔值。可以这样记忆,以下六个值取反后为true,其他值都为false。

undefined
null
false
0
NaN
空字符串(’ ')

!undefined // true
!null // true
!0 // true
!NaN // true
!"" // true
!54 // false
!'hello' // false

且运算符(&&)

多个条件都要满足

console.log(10 < 20 && 10 >5); // true

条件语句之if语句

if结构先判断一个表达式的布尔值,然后根据布尔值的真伪,执行不同的语句。所谓布尔值,指的是 JavaScript 的两个特殊值,true表示真,false表示伪。
在这里插入图片描述

if语句语法规范

if (布尔值){   
语句; 
}

需要注意的是,“布尔值”往往由一个条件表达式产生的,必须放在圆括号中

var m = 3;
if (m === 3) {    
m++;}
console.log(m); // 4

赋值表达式不具有比较作用。

var x = 1;
var y = 2;
if (x = y) {    
console.log(x);}

条件语句之 if…else

if…else基本结构

if代码块后面,还可以跟一个else代码块,表示不满足条件时,所要执行的代码。
在这里插入图片描述

if (m === 3) {  
// 满足条件时,执行的语句
} else {  
// 不满足条件时,执行的语句
}

多个if…else连接

对同一个变量进行多次判断时,多个if…else语句可以连写在一起。
在这里插入图片描述

if (m === 0) {  
// ...
}else if (m === 1) {  
// ...
} else if (m === 2) {  
// ...
} else {  
// ...
}

if…else嵌套

var eat = true;
var food = "猪肉炖粉条";
if (eat) {   
    if (food == "双椒鱼头") {  
          console.log('双椒鱼头');       
  }else{        
        console.log("猪肉炖粉条");    
  }
} 
else {    
     console.log('不吃了');
     }

else代码块总是与离自己最近的那个if语句配对。

条件语句之 switch

多个if…else连在一起使用的时候,可以转为使用更方便的switch结构
在这里插入图片描述

switch (fruit) {  
case "banana":    
  // ...    
  break;  
case "apple":    
  // ...    
  break;  
default:    
  // ...
}

需要注意的是,每个case代码块内部的break语句不能少,否则会接下去执行下一个case代码块,而不是跳出switch结构。

var x = 1;

switch (x) {  
   case 1:    
     console.log('x 等于1');  
   case 2:    
     console.log('x 等于2');  
   default:    
     console.log('x 等于其他值');}
     // x等于1
     // x等于2
     // x等于其他值

三元运算符

在这里插入图片描述
JavaScript还有一个三元运算符(即该运算符需要三个运算子)也可以用于逻辑判断。

(条件) ? 表达式1 : 表达式2

在这里插入图片描述
这个三元运算符可以被视为if…else…的简写形式,因此可以用于多种场合。

判断一个整数是奇数与偶数

if…else语句

var n=100;
if(n % 2 ==0){
     console.log("偶数");
}else{
    console.log("奇数");
}

三元运算符

var n=100;
n % 2 ===0 ?'偶数':'奇数'

循环语句之for

循环语句用于重复执行某个操作
for语句就是循环命令,可以指定循环的起点、终点和终止条件。它的格式如下

for (初始化表达式; 条件; 迭代因子) {  
语句
}

for语句后面的括号里面,有三个表达式。

  1. 初始化表达式(initialize):确定循环变量的初始值,只在循环开始时执行一次。
  2. 布尔表达式(test):每轮循环开始时,都要执行这个条件表达式,只有值为真,才继续进行循环。
  3. 迭代因子(increment):每轮循环的最后一个操作,通常用来递增循环变量。
    在这里插入图片描述
var x=3;
for(var i=0;i<x;i++){
  console.log(i);
}

for语句的三个表达式,可以省略任何一个,也可以全部省略,结果就导致了一个无限循环(死循环)

for(;;){
    console.log('Hello World');
}

for循环语句实操

循环输出1~100之间数字的和

var sum=0;
for(var i=1;i<=100;i++){
    sum+=i;
}
console.log(sum);

循环输出1000以内的奇数

console.log("奇数:");
for(var i=1;i<=1000;i++){
   if(i%2!=0){
        console.log(i+ " ");
   }
}

打印九九乘法表

for(var i=1;i<=9;i++){
  document.write("<br>");
   for(var j=1;j<=i;j++){
      console.log(i+'*'+j+'='+(i*j));
   }
}

循环语句之while

While语句包括一个循环条件和一段代码块,只要条件为真,就不断循环执行代码块。

while (条件) {  
语句;
}

在这里插入图片描述

while例子

var i=0;
while(i<100){
   console.log('i的当前值为:'+i);
   i=i+1;
}

下面的例子是一个无限循环,因为循环条件总是为真

while(true){
   console.log('Hello world');
}

所有for循环,都可以改写成while循环

//for
var x=3;
for(var i=0;i<x;i++){
   console.log(i);
}
//while
var x=3;
var i=0;
while(i<x){
  console.log(i);
  i++;
}

break 语句和 continue 语句

在这里插入图片描述
break语句和continue语句都具有跳转作用,可以让代码不按既有的顺序执行

break

break语句用于跳出代码块或循环

for(var i=0;i<5;i++){
   if(i==3){
      break;
   }
   console.log(i);
}

输出结果: 1 2 3

continue

continue语句用于立即终止本轮循环,返回循环结构的头部,开始下一轮循环

for(var i=0;i<5;i++){
   if(i==3){
     continue;
   }
   console.log(i);
}

输出结果:1 2 4 5
在这里插入图片描述

字符串

字符串就是零个或多个排在一起的字符,放在单引号或双引号之中

'xiongda'
"xionger"

单引号字符串的内部,可以使用双引号。双引号字符串的内部,可以使用单引号

'key = "value"'
"It's a long time"

如果要在单引号字符串的内部,使用单引号,就必须在内部的单引号前面加上反斜杠,用来转义。双引号字符串内部使用双引号,也是如此。

'Did she say \'Hello\'?'
// "Did she say 'Hello'?"

"Did she say \"Hello\"?"
// "Did she say "Hello"?"

字符串默认只能写在一行内,分成多行将会报错

如果长字符串必须分成多行,可以在每一行的尾部使用反斜杠

var longString = 'Long \
long \
string';
alert(longString);
// "Long long string"

length 属性

length属性返回字符串的长度,该属性也是无法改变的

var s='xiyangyang';
alert(s.length)

字符串方法_charAt()

charAt方法返回指定位置的字符,参数是从 0开始编号的

var s=new String('xiyangyang');
s.charAt(1);//"i"
s.charAt(s.length-1);//"g"

如果参数为负数,或大于等于字符串的长度,charAt返回空字符串。

'xiyangyang'.charAt(-1);//""
'xiyangyang'.charAt(11);//""

字符串方法_concat()

concat方法用于连接两个字符串,返回一个新字符串,不改变原字符串

var s1 = 'yangcun';
var s2 = 'xiyangyang';
s1.concat(s2) //yangcunxiyangyang
s1//yangcun
s2//xiyangyang

该方法可以接受多个参数

'xiyangyang'.concat('meiyangyang', 'lanyangyang') // "xiyangyangmeiyangyanglanyangyang"

如果参数不是字符串,concat方法会将其先转为字符串,然后再连接。

var one = 1;
var two = 2;
var three = '3';

''.concat(one, two, three) // "123"

字符串方法_substring()

substring方法用于从原字符串取出子字符串并返回,不改变原字符串。它的第一个参数表示子字符串的开始位置,第二个位置表示结束位置(返回结果不含该位置)

'xiyangyang'.substring(0, 2) // "xi"

如果省略第二个参数,则表示子字符串一直到原字符串的结束

'xiyangyang'.substring(2) // "yangyang"

如果第一个参数大于第二个参数,substring方法会自动更换两个参数的位置

'xiyangyang'.substring(9, 2) // "yangya"// 等同于
'xiyangyang'.substring(2, 9) // "yangya"

如果参数是负数,substring方法会自动将负数转为0

'xiyangyang'.substring(-3) // "xiyangyang"
'xiyangyang'.substring(2, -3) // "xi"

字符串方法_substr()

substr方法用于从原字符串取出子字符串并返回,不改变原字符串,跟 substring方法的作用相同
substr方法的第一个参数是子字符串的开始位置(从0开始计算),第二个参数是子字符串的长度

'xiyangyang'.substr(2, 7); //yangyan

如果省略第二个参数,则表示子字符串一直到原字符串的结束

'xiyangyang'.substr(2) // "yangyang"

如果第一个参数是负数,表示倒数计算的字符位置。如果第二个参数是负数,将被自动转为0,因此会返回空字符串

'xiyangyang'.substr(-7) //"angyang"
'itbaizhan'.substr(4, -1) // ""

字符串方法_indexOf()

indexOf方法用于确定一个字符串在另一个字符串中第一次出现的位置,返回结果是匹配开始的位置。如果返回 -1,就表示不匹配

'hello world'.indexOf('o') //4
'xyymyylyy'.indexOf('htf') // -1

indexOf方法还可以接受第二个参数,表示从该位置开始向后匹配

'hello world'.indexOf('o', 6) // 7

字符串方法_trim()

trim方法用于去除字符串两端的空格,返回一个新字符串,不改变原字符串

'  hello world  '.trim()
// "hello world"

该方法去除的不仅是空格,还包括制表符( \t、\v)、换行符( \n)和回车符( \r)

'\r\nxiyangyanglanyangyang \t'.trim() // 'xiyangyanglanyangyang'

ES6扩展方法, trimEnd()和 trimStart()方法

"   xiyangyanglanyangyang   ".trimEnd(); 
//    xiyangyanglanyangyang
"   xiyangyanglanyangyang   ".trimStart(); 
//xiyangyanglanyangyang

字符串方法_split()

split方法按照给定规则分割字符串,返回一个由分割出来的子字符串组成的数组

'xyy|lyy|myy'.split('|') 
// ["xyy", "lyy", "myy"]

如果分割规则为空字符串,则返回数组的成员是原字符串的每一个字符。

'a|b|c'.split('') 
// ["a", "|", "b","|", "c"]

如果省略参数,则返回数组的唯一成员就是原字符串

'a|b|c'.split() // [a|b|c]

split方法还可以接受第二个参数,限定返回数组的最大成员数。

'it|sxt|bz'.split('|', 0) //[]
'it|sxt|bz'.split('|', 1) // ["it"]
'it|sxt|bz'.split('|', 2) // ["it", "sxt"]
'it|sxt|bz'.split('|', 3) // ["it", "sxt", "bz"]
'it|sxt|bz'.split('|', 4) // ["it", "sxt", "bz"]

数组

数组(array)是按次序排列的一组值。每个值的位置都有编号(从0开始),整个数组用方括号表示。

var arr = ['sxt', 'baizhan', 'it'];

两端的方括号是数组的标志。 sxt是0号位置,baizhan是1号位置,it是2号位置。(位置也被称为下标)。

除了在定义时赋值,数组也可以先定义后赋值。

var arr = [];

arr[0] = 'sxt';
arr[1] = 'baizhan';
arr[2] = 'it';

任何类型的数据,都可以放入数组

var arr = [ 100, [1, 2, 3],false ];

如果数组的元素还是数组,就形成了多维数组

var a = [[1, 2], [3, 4]];
a[0][1] // 2
a[1][1] // 4

length 属性

数组的length属性,返回数组的成员数量

['sxt', 'baizhan', 'it'].length // 3

数组的遍历

数组的遍历可以考虑使用for循环或while循环

var a = ['sxt', 'baizhan', 'it'];

// for循环
for(var i = 0; i < a.length; i++) {  console.log(a[i]);}

// while循环
var i = 0;
while (i < a.length) {  
console.log(a[i]);  
i++;
}

for…in遍历数组

var a = ['sxt', 'baizhan', 'it'];

for (var i in a) {  
console.log(a[i]);
}

数组静态方法_Array.isArray()

Array.isArray()方法返回一个布尔值,表示参数是否为数组。它可以弥补typeof运算符的不足。

var arr = ["尚学堂", 100, true];
console.log(typeof arr); // object
var arr = ['sxt', 'baizhan', 'it'];
Array.isArray(arr) // true

数组方法_push()/pop()

push方法用于在数组的末端添加一个或多个元素,并返回添加新元素后的数组长度。注意,该方法会改变原数组

var arr = [];
arr.push("尚学堂") // 1
arr.push('itbaizhan') // 2
arr.push(true, {}) // 4
arr // [尚学堂, 'itbaizhan', true, {}]

pop方法用于删除数组的最后一个元素,并返回该元素。注意,该方法会改变原数组

var arr = ['尚学堂', 'itbaizhan', 'WEB前端'];

arr.pop() // 'WEB前端'
arr // ['尚学堂', 'itbaizhan']

数组方法_shift()/unshift()

shift方法用于删除数组的第一个元素,并返回该元素。注意,该方法会改变原数组

var arr = ['尚学堂', 'itbaizhan', 'WEB前端'];

arr.shift() // '尚学堂'
arr // ['itbaizhan', 'WEB前端']

shift方法可以遍历并清空一个数组

var list = [1, 2, 3, 4, 5, 6];
var item;
while (item = list.shift()) {  console.log(item);
}
list // []

unshift方法用于在数组的第一个位置添加元素,并返回添加新元素后的数组长度。注意,该方法会改变原数组

var arr = ['尚学堂', 'itbaizhan', 'WEB前端'];

arr.unshift('baizhan'); // 4
arr // ['baizhan', '尚学堂', 'itbaizhan', 'WEB前端']

unshift方法可以接受多个参数,这些参数都会添加到目标数组头部

var arr = [ '尚学堂', 'itbaizhan' ];
arr.unshift('WEB前端', 'baizhan') // 4
arr // [ 'WEB前端', 'baizhan', '尚学堂', 'itbaizhan' ]

数组方法_join()

join方法以指定参数作为分隔符,将所有数组成员连接为一个字符串返回。如果不提供参数,默认用逗号分隔。

var a = [1, 2, 3, 4];

a.join(' ') // '1 2 3 4'
a.join(' | ') // "1 | 2 | 3 | 4"
a.join() // "1,2,3,4"

如果数组成员是 undefined或 null或空位,会被转成空字符串

[undefined, null].join('#')
// '#'

['a',, 'b'].join('-')
// 'a--b'

数组的 join配合字符串的 split可以实现数组与字符串的互换

var arr = ["a","b","c"];
var myArr = arr.join("");
console.log(myArr);
console.log(myArr.split(""));

数组方法_concat()

concat方法用于多个数组的合并。它将新数组的成员,添加到原数组成员的后部,然后返回一个新数组,原数组不变

['hello'].concat(['world'])
// ["hello", "world"]

['hello'].concat(['world'], ['!'])
// ["hello", "world", "!"]

除了数组作为参数,concat也接受其他类型的值作为参数,添加到目标数组尾部。

[1, 2, 3].concat(4, 5, 6)
// [1, 2, 3, 4, 5, 6]

数组方法_reverse()

reverse方法用于颠倒排列数组元素,返回改变后的数组。注意,该方法将改变原数组

var a = ['a', 'b', 'c'];

a.reverse() // ["c", "b", "a"]
a // ["c", "b", "a"]

实现一个字符串反转排列

var str = "hello";
str.split("").reverse().join("")

数组方法_indexOf()

indexOf方法返回给定元素在数组中第一次出现的位置,如果没有出现则返回 -1

var arr = ['a', 'b', 'c'];

arr.indexOf('b') // 1
arr.indexOf('y') // -1

indexOf方法还可以接受第二个参数,表示搜索的开始位置

['尚学堂', '百战程序员', 'itbaizhan'].indexOf('尚学堂', 1) // -1

函数

函数是一段可以反复调用的代码块

函数的声明

function 命令:
function命令声明的代码区块,就是一个函数。 function命令后面是函数名,函数名后面是一对圆括号,里面是传入函数的参数。函数体放在大括号里面。

function print(s){
   console.log(s);
}

函数名的提升

JavaScript 引擎将函数名视同变量名,所以采用function命令声明函数时,整个函数会像变量声明一样,被提升到代码头部。

add();
function(){}

函数参数

函数运行的时候,有时需要提供外部数据,不同的外部数据会得到不同的结果,这种外部数据就叫参数

function square(x){
   console.log(x*x);
}
square(2)
square(3)

函数返回值

JavaScript函数提供两个接口实现与外界的交互,其中参数作为入口,接收外界信息;返回值作为出口,把运算结果反馈给外界

function getName(name){
  return name;
}

var myName=getName("XYY");
console.log(myName);

return 后面不能在添加任何代码,因为不会执行

对象概述

对象(object)是 JavaScript 语言的核心概念,也是最重要的数据类型,对象就是一组“键值对”(key-value)的集合,是一种无序的复合数据集合。
在这里插入图片描述

var user={
   name:'xiyangyang',
   age:13
};

对象的每一个键名又称为“属性”(property),它的“键值”可以是任何数据类型。如果一个属性的值为函数,通常把这个属性称为“方法”,它可以像函数那样调用

var user={
    getName:function(name){
       return name;
    }
}
user.getName("xyy");

如果属性的值还是一个对象,就形成了链式引用

var user={
    name:xyy,
    age:12,
    container:{
        frontEnd:["Web前端","Android","iOS"],
        backend:["Java","Python"]
    }
}
user.container.fontEnd //["Web前端","Android","iOS"]

Math对象

Math是 JavaScript 的原生对象,提供各种数学功能

Math.abs()

Math.abs方法返回参数值的绝对值

Math.abs(1) // 1
Math.abs(-1) // 1

Math.max(),Math.min()
Math.max方法返回参数之中最大的那个值, Math.min返回最小的那个值。如果参数为空, Math.min返回 Infinity , Math.max返回 -Infinity。

Math.max(2, -1, 5) // 5
Math.min(2, -1, 5) // -1
Math.min() // Infinity
Math.max() // -Infinit

Math.floor(),Math.ceil()
Math.floor方法返回小于参数值的最大整数

Math.floor(3.2) // 3
Math.floor(-3.2) // -4

Math.ceil方法返回大于参数值的最小整数

Math.ceil(3.2) // 4
Math.ceil(-3.2) // -3

Math.random()
Math.random()返回0到1之间的一个伪随机数,可能等于0,但是一定小于1

Math.random() // 0.28525367438365223

任意范围的随机数生成函数如下

function getRandomArbitrary(min, max) {  return Math.random() * (max - min) + min;}

getRandomArbitrary(5, 10)

Date对象

Date对象是 JavaScript 原生的时间库。它以1970年1月1日00:00:00作为时间的零点,可以表示的时间范围是前后各1亿天(单位为毫秒)

Date.now()

Date.now方法返回当前时间距离时间零点(1970年1月1日 00:00:00 UTC)的毫秒数,相当于 Unix 时间戳乘以1000

Date.now();   // 1635216733395

时间戳

时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数。
格林威治和北京时间就是时区的不同。
Unix是20世纪70年代初出现的一个操作系统,Unix认为1970年1月1日0点是时间纪元。JavaScript也就遵循了这一约束

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

实例方法get类

  • getTime():返回实例距离1970年1月1日00:00:00的毫秒数
  • getDate():返回实例对象对应每个月的几号(从1开始)
  • getDay():返回星期几,星期日为0,星期一为1,以此类推
  • getYear():返回距离1900的年数
  • getFullYear():返回四位的年份
  • getMonth():返回月份(0表示1月,11表示12月)
  • getHours():返回小时(0-23)
  • getMilliseconds():返回毫秒(0-999)
  • getMinutes():返回分钟(0-59)
  • getSeconds():返回秒(0-59)
var d = new Date('January 6,2022');
d.getDate() // 6
d.getMonth() // 0
d.getYear() // 122
d.getFullYear() // 2022

编写函数获得本年度剩余天数

function leftDays() {  
var today = new Date();  
var endYear = new Date(today.getFullYear(), 11, 31, 23, 59, 59, 999);  
var msPerDay = 24 * 60 * 60 * 1000;  
return Math.round((endYear.getTime() - today.getTime()) / msPerDay);}

DOM概述

DOM 是 JavaScript 操作网页的接口,全称为“文档对象模型”(Document Object Model)。它的作用是将网页转为一个 JavaScript 对象,从而可以用脚本进行各种操作(比如对元素增删内容)。

浏览器会根据 DOM 模型,将结构化文档HTML解析成一系列的节点,再由这些节点组成一个树状结构(DOM Tree)。所有的节点和最终的树状结构,都有规范的对外接口

DOM 只是一个接口规范,可以用各种语言实现。所以严格地说,DOM 不是 JavaScript 语法的一部分,但是 DOM 操作是 JavaScript 最常见的任务,离开了 DOM,JavaScript 就无法控制网页。另一方面,JavaScript 也是最常用于 DOM 操作的语言

节点

DOM 的最小组成单位叫做节点(node)。文档的树形结构(DOM 树),就是由各种不同类型的节点组成。每个节点可以看作是文档树的一片叶子
在这里插入图片描述
节点的类型有七种:

  • Document:整个文档树的顶层节点
  • DocumentType:doctype标签
  • Element:网页的各种HTML标签
  • Attribute:网页元素的属性(比如class=“right”)
  • Text:标签之间或标签包含的文本
  • Comment:注释
  • DocumentFragment:文档的片段

节点树

一个文档的所有节点,按照所在的层级,可以抽象成一种树状结构。这种树状结构就是 DOM 树。它有一个顶层节点,下一层都是顶层节点的子节点,然后子节点又有自己的子节点,就这样层层衍生出一个金字塔结构,倒过来就像一棵树
浏览器原生提供document节点,代表整个文档。

document// 整个文档树

除了根节点,其他节点都有三种层级关系
父节点关系(parentNode):直接的那个上级节点 子节点关系(childNodes):直接的下级节点 同级节点关系(sibling):拥有同一个父节点的节点

Node.nodeType属性

不同节点的nodeType属性值和对应的常量如下

  • 文档节点(document):9,对应常量Node.DOCUMENT_NODE
  • 元素节点(element):1,对应常量Node.ELEMENT_NODE
  • 属性节点(attr):2,对应常量Node.ATTRIBUTE_NODE
  • 文本节点(text):3,对应常量Node.TEXT_NODE
  • 文档片断节点(DocumentFragment):11,对应常量Node.DOCUMENT_FRAGMENT_NODE
document.nodeType // 9
document.nodeType === Node.DOCUMENT_NODE // true

document对象_方法/获取元素

在这里插入图片描述

document.getElementsByTagName()

document.getElementsByTagName方法搜索 HTML 标签名,返回符合条件的元素。它的返回值是一个类似数组对象( HTMLCollection实例),可以实时反映 HTML 文档的变化。如果没有任何匹配的元素,就返回一个空集

var paras = document.getElementsByTagName('p');

如果传入 *,就可以返回文档中所有 HTML 元素

var allElements = document.getElementsByTagName('*');

document.getElementsByClassName()

document.getElementsByClassName方法返回一个类似数组的对象( HTMLCollection实例),包括了所有 class名字符合指定条件的元素,元素的变化实时反映在返回结果中

var elements = document.getElementsByClassName(names);

由于 class是保留字,所以 JavaScript 一律使用 className表示 CSS 的class,参数可以是多个 class,它们之间使用空格分隔

var elements = document.getElementsByClassName('foo bar');

document.getElementsByName()

document.getElementsByName方法用于选择拥有 name属性的 HTML 元素(比如 、 、 等),返回一个类似数组的的对象( NodeList实例),因为 name属性相同的元素可能不止一个

// 表单为 <form name="itbaizhan"></form>
var forms = document.getElementsByName('itbaizhan');

document.getElementById()

document.getElementById方法返回匹配指定 id属性的元素节点。如果没有发现匹配的节点,则返回 null

var elem = document.getElementById('para1');

注意,该方法的参数是大小写敏感的。比如,如果某个节点的 id属性是 main,那么 document.getElementById(‘Main’)将返回 null

document.querySelector()

document.querySelector方法接受一个 CSS 选择器作为参数,返回匹配该选择器的元素节点。如果有多个节点满足匹配条件,则返回第一个匹配的节点。如果没有发现匹配的节点,则返回 null

var el1 = document.querySelector('.myclass');

document.querySelectorAll()

document.querySelectorAll方法与 querySelector用法类似,区别是返回一个NodeList对象,包含所有匹配给定选择器的节点

var elementList = document.querySelectorAll('.myclass');

document对象_方法/创建元素

在这里插入图片描述

document.createElement()

document.createElement方法用来生成元素节点,并返回该节点

var newDiv = document.createElement('div');

document.createTextNode()

document.createTextNode方法用来生成文本节点( Text实例),并返回该节点。它的参数是文本节点的内容

var newDiv = document.createElement('div');
var newContent = document.createTextNode('Hello');
newDiv.appendChild(newContent);

document.createAttribute()

document.createAttribute方法生成一个新的属性节点( Attr实例),并返回它

var attribute = document.createAttribute(name);
var root = document.getElementById('root');
var it = document.createAttribute('itbaizhan');
it.value = 'it';
root.setAttributeNode(it);

Element对象_属性

Element对象对应网页的 HTML 元素。每一个 HTML 元素,在 DOM 树上都会转化成一个Element节点对象(以下简称元素节点)

Element.id

Element.id属性返回指定元素的 id属性,该属性可读写

// HTML 代码为 <p id="foo">
var p = document.querySelector('p');
p.id // "foo"

Element.className

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

// HTML 代码 <div class="one two three" id="myDiv"></div>
var div = document.getElementById('myDiv');
div.className

Element.classList

classList对象有下列方法:

  • add():增加一个 class。
  • remove():移除一个 class。
  • contains():检查当前元素是否包含某个 class。
  • toggle():将某个 class 移入或移出当前元素。
var div = document.getElementById('myDiv');
div.classList.add('myCssClass');
div.classList.add('foo', 'bar');
div.classList.remove('myCssClass');
div.classList.toggle('myCssClass');// 如果 myCssClass 不存在就加入,否则移除
div.classList.contains('myCssClass'); // 返回 true 或者 false

Element.innerHTML

Element.innerHTML属性返回一个字符串,等同于该元素包含的所有 HTML 代码。该属性可读写,常用来设置某个节点的内容。它能改写所有元素节点的内容,包括 和 元素

el.innerHTML = '';

Element.innerText

innerText和 innerHTML类似,不同的是 innerText无法识别元素,会直接渲染成字符串

Element获取元素位置

属性描述
clientHeight获取元素高度包括 padding部分,但是不包括 border、margin
clientWidth获取元素宽度包括 padding部分,但是不包括 border、margin
scrollHeight元素总高度,它包括 padding,但是不包括 border、margin包括溢出的不可见内容
scrollWidth元素总宽度,它包括 padding,但是不包括 border、margin包括溢出的不可见内容
scrollLeft元素的水平滚动条向右滚动的像素数量
scrollTop元素的垂直滚动条向下滚动的像素数量
offsetHeight元素的 CSS 垂直高度(单位像素),包描括述元素本身的高度、padding 和 border
offsetWidth元素的 CSS 水平宽度(单位像素),包括元素本身的高度、padding 和 border
offsetLeft到定位父级左边界的间距
offsetTop到定位父级上边界的间距

Element.clientHeight,Element.clientWidth

Element.clientHeight属性返回一个整数值,表示元素节点的 CSS 高度(单位像素),只对块级元素生效,对于行内元素返回 0。如果块级元素没有设置 CSS 高度,则返回实际高度

除了元素本身的高度,它还包括 padding部分,但是不包括 border、margin。如果有水平滚动条,还要减去水平滚动条的高度。注意,这个值始终是整数,如果是小数会被四舍五入。

Element.clientWidth属性返回元素节点的 CSS 宽度,同样只对块级元素有效,也是只包括元素本身的宽度和 padding,如果有垂直滚动条,还要减去垂直滚动条的宽度。

document.documentElement的 clientHeight属性,返回当前视口的高度(即浏览器窗口的高度)。 document.body的高度则是网页的实际高度。

// 视口高度
document.documentElement.clientHeight

// 网页总高度
document.body.clientHeight

Element.scrollHeight,Element.scrollWidth

Element.scrollHeight属性返回一个整数值(小数会四舍五入),表示当前元素的总高度(单位像素),它包括 padding,但是不包括 border、margin以及水平滚动条的高度(如果有水平滚动条的话)

Element.scrollWidth属性表示当前元素的总宽度(单位像素),其他地方都与 scrollHeight属性类似。这两个属性只读

整张网页的总高度可以从 document.documentElement或 document.body上读取

// 返回网页的总高度
document.documentElement.scrollHeight
document.body.scrollHeight

Element.scrollLeft,Element.scrollTop

Element.scrollLeft 属性表示当前元素的水平滚动条向右侧滚动的像素数量, Element.scrollTop 属性表示当前元素的垂直滚动条向下滚动的像素数
量。对于那些没有滚动条的网页元素,这两个属性总是等于0。
如果要查看整张网页的水平的和垂直的滚动距离,要从document.documentElement 元素上读取。

document.documentElement.scrollLeft
document.documentElement.scrollTop

Element.offsetHeight,Element.offsetWidth

Element.offsetHeight 属性返回一个整数,表示元素的 CSS 垂直高度(单位像素),包括元素本身的高度、padding 和 border,以及水平滚动条的高度(如果存在滚动条)。

这两个属性都是只读属性,只比Element.clientHeight 和 Element.clientWidth 多了
边框的高度或宽度。如果元素的 CSS 设为不可见(比如 display: none; ),则返回 0

Element.offsetLeft,Element.offsetTop

Element.offsetLeft 返回当前元素左上角相对于 Element.offsetParent 节点的水平位移, Element.offsetTop 返回垂直位移,单位为像素。通常,这两个值是指相对于父节点的位移

<div class="parent">
    <div class="box" id="box"></div>
</div>
.parent{
    width: 200px;
    height: 200px;
    background: red;
    position: relative;
    left: 50px;
    top: 50px;
}
.box{
    width: 100px;
    height: 100px;
    background: yellow;
    position: relative;
    left: 50px;
    top: 50px;
}
var box = document.getElementById("box");
console.log(box.offsetLeft);
console.log(box.offsetTop);

CSS操作

HTML 元素的 style 属性

操作 CSS 样式最简单的方法,就是使用网页元素节点的 setAttribute 方法直接操作网页元素的 style 属性。

div.setAttribute(
  'style'
,
  'background-color:red;' + 'border:1px solid   black;'
);

元素节点的 style 属性

var divStyle=document.querySelector('div').style;
 
 divStyle.backgroundColor='red';
 divStyle.border='1px solid black';
 divStyle.width='100px';
 divStyle.height='100px';
 divStyle.fontSize='10em';

cssText 属性

var divStyle=document.querySelector('div').style;
divStyle.cssText='background-color:red;'+'border:1px solid black;'+'height:100px;'+'width:100px';

事件处理程序

事件处理程序分为:

  1. HTML事件处理
  2. DOM0级事件处理
  3. DOM2级事件处理

HTML事件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Js事件详解--事件处理</title>
</head>
<body>
    <div id="div">
        <button id="btn1" onclick="demo()">按钮</button>
    </div>
    <script>
        function demo(){
            alert("hello html事件处理");
        }
    </script>
</body>
</html>

DOM0级事件处理

<body>
    <div id="div">
        <button id="btn1" onclick="demo()">按钮</button>
    </div>
    <script>
        var btn1=document.getElementById("btn1");
        btn1.onclick=function(){
            alert("Hello DOM0级事件处理程序1");//被覆盖掉
        }
        btn1.onclick=function(){
            alert("Hello DOM0级事件处理程序2");
        }
    </script>
</body>

DOM2级事件处理

<body>
    <div id="div">
        <button id="btn1">按钮</button>
    </div>
    <script>
        var btn1=document.getElementById("btn1");
        btn1.addEventListener("click",demo1);
        btn1.addEventListener("click",demo2);
        btn1.addEventListener("click",demo3);
        function demo1(){
            alert("DOM2级事件处理程序1");
        }
        function demo2(){
            alert("DOM2级事件处理程序2");
        }
        function demo3(){
            alert("DOM2级事件处理程序3");
        }
        btn1.removeEventListener("click",demo2);
    </script>
</body>

鼠标事件

鼠标事件指与鼠标相关的事件,具体的事件主要有以下一些

  1. click:按下鼠标时触发
  2. dblclick:在同一个元素上双击鼠标时触发
  3. mousedown:按下鼠标键时触发
  4. mouseup:释放按下的鼠标键时触发
  5. mousemove:当鼠标在节点内部移动时触发。当鼠标持续移动时,该事件会连触发。
  6. mouseenter:鼠标进入一个节点时触发,进入子节点不会触发这个事件
  7. mouseleave:鼠标离开一个节点时触发,离开父节点不会触发这个事件
  8. mouseover:鼠标进入一个节点时触发,进入子节点会再一次触发这个事件
  9. mouseout:鼠标离开一个节点时触发,离开父节点也会触发这个事件
  10. wheel:滚动鼠标的滚轮时触发
 var btn1=document.getElementById("btn1");
        btn1.onclick=function(){
            console.log("click事件")
        }

这些方法在使用的时候,除了DOM2级事件,都需要添加前缀on

Event事件对象

事件发生以后,会产生一个事件对象,作为参数传给监听函数。

Event对象属性

  1. Event.Target
  2. Event.type

Event.target

Event.target属性返回事件当前所在的节点

<p id="para">hello</p>
    <script>
        function setColor(e){
            console.log(this==e.target);
            e.target.style.color='red';
        }
        para.addEventListener('click',setColor);
    </script>

Event.type

Event.type属性返回一个字符串,表示事件类型。事件的类型是在生成事件的时候。该属性只读

Event对象方法

  1. Event.preventDefault()
  2. Event.stopPropagation()
Event.preventDefault

Event.preventDefault方法取消浏览器对当前事件的默认行为。比如点击链接后,浏览器默认会跳转到另一个页面,使用这个方法以后,就不会跳转了

btn.onclick = function(e){
    e.preventDefault(); // 阻止默认事件
    console.log("点击A标签");
}
Event.stopPropagation()

stopPropagation方法阻止事件在 DOM 中继续传播,防止再触发定义在别的节点上的监听函数,但是不包括在当前节点上其他的事件监听函数

btn.onclick = function(e){
    e.stopPropagation(); // 阻止事件冒泡
    console.log("btn");
}

事件类型之键盘事件

键盘事件由用户击打键盘触发,主要有keydown、keypress、keyup三个事件

  1. keydown:按下键盘时触发。
  2. keypress:按下有值的键时触发,即按下 Ctrl、Alt、Shift、Meta 这样无值的键,这个事件不会触发。对于有值的键,按下时先触发keydown事件,再触发这个事件。
  3. keyup:松开键盘时触发该事件
username.onkeypress = function(e){
    console.log("keypress事件");
}

event对象

keyCode:唯一标识

var username = 
document.getElementById("username");
username.onkeydown = function(e){
    if(e.keyCode === 13){
        console.log("回车");
   }
}

事件类型之表单事件

表单事件是在使用表单元素及输入框元素可以监听的一系列事件

  1. input事件
  2. select事件
  3. Change事件
  4. reset事件
  5. submit事件

input事件

input事件当 < input>、< select>、< textarea> 的值发生变化时触发。对于复选框( < input type=checkbox> )或单选框( < input type=radio> ),用户改变选项时,也会触发这个事件input事件的一个特点,就是会连续触发,比如用户每按下一次按键,就会触发一次input事件。

 <div>
        <input id="username" type="text" value="请填空">
    </div>
    <script>
        var username=document.getElementById("username");
        username.oninput=function(e){
            console.log(e.target.value);
        }
    </script>

select事件

select事件当在 < input>、< textarea> 里面选中文本时触发

// HTML 代码如下
<input id="test" type="text" value="Select 
me!" />

var elem =document.getElementById('test');
elem.addEventListener('select', function (e) {
  console.log(e.type); // "select"
}, false);

Change 事件

Change事件当 < input>、< select>、< textarea> 的值发生变化时触发。它与input事件的最大不同,就是不会连续触发,只有当全部修改完成时
才会触发。

<div>
<input id="email" type="text" value="change事件"></div>
 var email=document.getElementById("email");
        email.onchange=function(e){
            console.log(e.target.value);
        }

reset 事件,submit 事件

这两个事件发生在表单对象 < form> 上,而不是发生在表单的成员上。

reset事件当表单重置(所有表单成员变回默认值)时触发。

submit事件当表单数据向服务器提交时触发。注意,submit事件的发生对象是 < form> 元素,而不是 < button> 元素,因为提交的是表单,
而不是按钮

<div>
        <form id="myForm" onsubmit="submitHandle">
            <button onclick="resetHandle">重置数据</button>
            <button>提交</button>
        </form>
    </div>
    <script>
        var myForm=document.getElementById("myForm");
        function resetHandle(){
            myForm.reset();
        }
        function submitHandle(){
            console.log("提交");
        }
    </script>

事件代理(事件委托)

由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。这种方法叫做事件的代理(delegation)

var ul=document.querySelector('ul');
        ul.addEventListener('click',function(even){
            if(even.target.tagName.toLowerCase()==='li'){
                //some code
            }
        })
    </script>

定时器之 setTimeout()

JavaScript 提供定时执行代码的功能,叫做定时器(timer),主要由 setTimeout() 和 setInterval() 这两个函数来完成。它们向任务队列添加定
时任务。

setTimeout 函数用来指定某个函数或某段代码,在多少毫秒之后执行。它返回一个整数,表示定时器的编号,以后可以用来取消这个定时器。

 var timerId=setTimeout(func|code,delay);

setTimeout 函数接受两个参数,第一个参数 func|code 是将要推迟执行的函数名或者一段代码,第二个参数 delay 是推迟执行的毫秒数

  setTimeout(function(){
            console.log("定时器")
        },1000)

如果回调函数是对象的方法,那么setTimeout 使得方法内部的 this 关键字指向全局环境,而不是定义时所在的那个对象

var name = "sxt";
var user = {
    name: "itbaizhan",
    getName: function () {
        setTimeout(function(){
            console.log(this.name);
       },1000)
   }
};
user.getName();

解决方案

var name = "sxt";
var user = {
    name: "itbaizhan",
    getName: function () {
        var that = this;
        setTimeout(function(){
            console.log(that.name);
       },1000)
   }
};
user.getName();

定时器可以进行取消

var id = setTimeout(f, 1000);
clearTimeout(id);

定时器之 setInterval()

setInterval 函数的用法与 setTimeout 完全一致,区别仅仅在于 setInterval 指定某个任务每隔一段时间就执行一次,也就是无限次的定时执行

var timer = setInterval(function() {
  console.log(2);
}, 1000)

通过setInterval方法实现网页动画

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #someDiv{
            width:100px;
            height:100px;
            background: red;
        }
    </style>
</head>
<body>
    <div id="someDiv"></div>
    <script>
        var div=document.getElementById('someDiv');
        var opacity=1;
        var fader=setInterval(function(){
            opacity-=0.05;
            if(opacity>0){
                div.style.opacity=opacity;
            }else{
                clearInterval(fader);
            }
        },30);
        //定时器可以进行取消
        var id=setInterval(f,1000);
        clearInterval(id);
    </script>

防抖(debounce)

防抖严格算起来应该属于性能优化的知识,但实际上遇到的频率相当高,处理不当或者放任不管就容易引起浏览器卡死。

从滚动条监听的例子说起

function showTop () {
    var scrollTop = 
document.documentElement.scrollTop;
    console.log('滚动条位置:
' + scrollTop);
}
window.onscroll  = showTop

在运行的时候会发现存在一个问题:这个函数的默认执行频率,
太!高!了!。 高到什么程度呢?以chrome为例,我们可以点击
选中一个页面的滚动条,然后点击一次键盘的【向下方向键】,会
发现函数执行了8-9次!

然而实际上我们并不需要如此高频的反馈,毕竟浏览器的性能是有
限的,不应该浪费在这里,所以接着讨论如何优化这种场景。
基于上述场景,首先提出第一种思路:在第一次触发事件时,不立
即执行函数,而是给出一个期限值比如200ms,然后

  1. 如果在200ms内没有再次触发滚动事件,那么就执行函数
  2. 如果在200ms内再次触发滚动事件,那么当前的计时取消,重新开始计时
    效果:如果短时间内大量触发同一事件,只会执行一次函数

实现:既然前面都提到了计时,那实现的关键就在于setTimeout这个函数,由于还需要一个变量来保存计时,考虑维护全局纯净,可以借助闭包来实现

function debounce(fn,delay){
    let timer = null //借助闭包
    return function() {
        if(timer){
            clearTimeout(timer) 
       }
        timer = setTimeout(fn,delay) // 简化
写法
   }
}
// 然后是旧代码
function showTop () {
    var scrollTop = 
document.documentElement.scrollTop;
    console.log('滚动条位置:
' + scrollTop);
}
window.onscroll = debounce(showTop,300)

到这里,已经把防抖实现了
对于短时间内连续触发的事件(上面的滚动事件),防抖的含义就是让某个时间期限(如上面的1000毫秒)内,事件处理函 数只执行一次

节流(throttle)

节流严格算起来应该属于性能优化的知识,但实际上遇到的频率相当高,处理不当或者放任不管就容易引起浏览器卡死
继续思考,使用上面的防抖方案来处理问题的结果是如果在限定时间段内,不断触发滚动事件(比如某个用户闲着无聊,按住滚动不断的拖来拖去),只要不停止触发,理论上就永远不会输出当前距离顶部的距离但是如果产品同学的期望处理方案是:即使用户不断拖动滚动条,也能在某个时间间隔之后给出反馈呢?
其实很简单:我们可以设计一种类似控制阀门一样定期开放的函数,也就是让函数执行一次后,在某个时间段内暂时失效,过了这段时间后再重新激活(类似于技能冷却时间)
效果:如果短时间内大量触发同一事件,那么在函数执行一次之后,该函数在指定的时间期限内不再工作,直至过了这段时间才重新生效

实现

这里借助setTimeout来做一个简单的实现,加上一个状态位valid来表示当前函数是否处于工作状态

function throttle(fn,delay){
    let valid = true
    return function() {
       if(!valid){
           //休息时间 暂不接客
           return false 
       }
       // 工作时间,执行函数并且在间隔期内把状态位
设为无效
        valid = false
        setTimeout(function(){
            fn()
            valid = true;
       }, delay)
   }
}
function showTop () {
    var scrollTop = 
document.documentElement.scrollTop;
    console.log('滚动条位置:
' + scrollTop);
}
window.onscroll = throttle(showTop,300)

如果一直拖着滚动条进行滚动,那么会以300ms的时间间隔,持续输出当前位置和顶部的距离
讲完了这两个技巧,下面介绍一下平时开发中常遇到的场景:

  1. 搜索框input事件,例如要支持输入实时搜索可以使用节流方案(间隔一段时间就必须查询相关内
    容),或者实现输入间隔大于某个值(500ms),就当做用户输入完成,然后开始搜索,具体使用哪种方案要看业务需求
    2.页面resize事件,常见于需要做页面适配的时候。需要根据最终呈现的页面情况进行dom渲染(这种情形一般是使用防抖,因为只需要判断最后一次的变化情况)
  • 28
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值