JavaScript(2021.9.26)

一、引入

1.计算机语言

2.编程语言和标记语言的区别

 3.数据存储单位

 二.JavaScript简介

1.JS参考文献

菜鸟教程:AngularJS 参考手册 | 菜鸟教程

W3school:JavaScript 参考手册

MDN:JavaScript 参考 - JavaScript | MDN

现代JavaScript教程:现代 JavaScript 教程

2.JS是什么?

JS的标准命名为ECMAScript,但实际上JavaScript的含义更大一些。JavaScript包括ECMAScript、DOM(文档对象模型,通过JS操作网页)、BOM(浏览器文档对象模型,通过JS操作浏览器)

 3.JS的作用

JS的出现主要用于处理网页中的前端验证(在浏览器中检查用户输入的内容是否符合一定的规则,如:用户名的长度,密码的长度,邮箱的格式

 4.浏览器执行JS的过程

 5.JS的组成            (jsapi)

 ECMA 欧洲计算机联盟

Web APIs 包括 DOM(控制标签,对页面元素进行移动、大小、添加、删除等操作),BOM(操作浏览器,比如页面弹窗、检测窗口宽度、存储数据到浏览器)

6.JS三种书写位置

(1)行内(内联)样式,写在标签的属性中

点击按钮或超链接时,js代码才会执行

<button onclick="alert('点击后弹出的')">点击按钮</button>
<a href="javascript:alert('点击后弹出的')">点击超链接</a>
script可以写到标签内部
<div class="box">
    <script>
        document.write(`你好`)
    </script>
</div>

(2)内部(内嵌)样式,写在head的标签script中

<script type="text/javascript">alert("内嵌式的JS")</script>

(3)外部(外链)样式,head中连接,一个html文件可以连接多个js文件

<script src="JS.js"></script>

注意:不要在标签中写代码,会不生效

7. Unicode编码

可查询Unicode编码对照表

三、注释

1.//  单行注释(ctrl+/)

2./* */  多行注释(ctrl+shift+/ 或 shift+alt+A)

四、调试

断点调试

把 变量、表达式、条件判断 添加至监视表达式,然后刷新,一步一步执行(注意:点两次执行该语句或到下一句才执行)

第1个图标:跳过下一个函数调用,不进入函数内部;

第2个图标:进入下一个函数调用,会一步一步执行;

五、输入输出语句

1.输入语句

prompt();     输入框

prompt("请输入你的年龄");

2.输出语句

(1)alert();                    弹出警告框

(2)document.write();   向body中添加一个内容,可在页面中输出

//不仅可以输出文字,还可以解析输出标签
document.wwrite('<h1>名字</h1>')

(3)console.log();         向控制台输出一个内容,返回值是标签字符串

(4)console.dir();          返回的是键值对(对象)

(5)confirm();              确认框

注意

  • 结束符为英文分号,可写可不写 ;
  • document.write、console.log 输出多个变量用英文逗号隔开,alert 不能一次输出多个变量;

六.变量

1.变量的概念

变量就是一个容器,是内存里的一块空间,用于存储数据(为什么需要变量?)。方便以后使用里面的数据。

2.声明变量

let / var / const 都可以声明变量

  • let 不能多次声明同一个变量;var 可以重复声明,第二次声明会覆盖第一次声明的值;
  • let name = '比方'
    let name = '粉笔'
    此写法错误

 let / var / const 三者的区别:

  1. var属于window;let不属于window;
  2. var可重复定义赋值;let不可重复定义赋值;
  3. var定义的变量不具有块级作用域(即,在块级外也可使用);let定义的变量具有块级作用域;
  4. const定义的变量必须有初始化的值;const定义的变量不允许改值(重新赋值);把固定不变的值设置为常量;const定义的变量具有块级作用域

var a;        let b;        const c=123;

3.变量的初始化

声明变量 变量名=变量值;     例:var age=18;

声明的同时赋值称为变量的初始化

4.变量语法扩展

(1)更新变量

一个变量被重新赋值后,它原来的值就会被覆盖,变量值将以最后一次赋的值为准。

(2)同时声明多个变量

只需写一个var,多个变量名之间用英文逗号隔开

var name="旗木卡卡西",sex='女',age="30";

5.变量命名规范

JS中可以由自己命名的称为标识符。如,变量名,函数名,属性名

(1)由字母、数字、下划线(-)、美元符号($)组成(注:命名的单词间不能有空格);

(2)不能以数字开头;

(3)不能是关键字、保留字,(注:在有些浏览器中name是有含义的,所以尽量不用name,可以用myName);

(4)严格区分大小写。var age和var Age是两个变量;

(5)遵守驼峰命名法。首字母小写,后面单词的首字母大写(小驼峰),如,myFirstName

6.交换变量案例

1.交换变量
var a=1,b=3;
var t=a;   //t为临时变量
var a=b;
var b=t;
console.log(a,b);    //a=3,b=1
2.输入并弹出用户名
var a=prompt("请输入你的名字");
alert("欢迎"+a);

七、数据类型

根据数据所占空间不同,分成了不同的数据类型。(充分利用存储空间)

JS是一种弱类型语言,变量的数据类型是可以变化的。

简单类型和复杂类型: 

  • 简单类型又叫做基本数据类型或者值类型,复杂类型又叫做引用类型;
  • 简单类型:string ,number,boolean,undefined,null ;
  • 复杂类型:通过 new 关键字创建的对象(系统对象、自定义对象),如 Object、Array、Date
  • 简单类型在存储时变量中存储的是值本身; 引用类型在存储时变量中存储的仅仅是地址;
  • 简单类型的数据直接存放在栈中;引用类型在栈里存放的是地址,真正的对象实例存放在堆中

1.number

(1)进制

JS进行浮点运算可能不精确,因为计算机是二进制

(2)特殊值

Number.MAX_VALUE  表示最大数值;

Number.MIN_VALUE  表示大于0的最小值;

Infinity  表示正无穷;

-Infinity  表示负无穷;

NaN  是一个特殊的数字,表示not a number;

(3)isNaN()  用来判断一个变量是否为非数字的类型

 2.string

使用表单、prompt 获取过来的数据默认是字符串类型的

(1)引号的使用(内双外单,外单内双)

1)用 '' (单引号) / ""(双引号) / ``(反引号) 包裹的都是字符串

2)在JS中字符串需要使用引号(双引号或单引号都可以,但不要混着用)引起来,不加引号会被认为成变量;

3)引号不能嵌套。双引号里不能放双引号,单引号里不能放单引号(就近原则),但双引号里能放单引号,单引号里能放双引号;

(2)转义符(都是以\开头的)

   \” 表示  双引号“

   \’ 表示  单引号 ’

  \n  表示  换行       注:要放在引号里使用 '\n'    反引号内不用加引号    \n只能console.log中起作用

  \t  表示  Tab键

 \b  表示   空格

  \\  表示  一个斜杠 \

  \\\\ 表示  两个斜杠 \\

(3)检测获取字符串长度(length )

var s="来庄里吧!";
console.log(s.length); /*length=5*/

(4)字符串的拼接

方法一:加号(+)

只要有字符串和其他类型相拼接,最终的结果是字符串类型(拼接前会把与字符串相加的任何类型转成字符串,再拼接成一个新的字符串)

console.log("河北"+"石家庄");  /* 河北石家庄 */
console.log("SHY"+24);      /* SHY24 */
console.log("12"+12);       /* 1212 */
console.log("河北"+true);    /* 河北true */

字符串和变量的拼接(因为变量可以很方便地修改里面的值)

var name="旗木卡卡西",age="25";
console.log('你好,我叫'+name+',今年'+age+'岁了!')
用户输入并反馈给用户
var age=prompt('请输入您的年龄:');
alert('您今年'+age+'岁啦!');

 方法二:模板字符串(反引号与${})

  • 模板字符串使拼接字符串更简便;
  • 用 反引号 包含数据,用 ${变量名} 来使用变量;
let name="旗木卡卡西",age="25";
document.write(`我是${name},今年${age}了!`)

(5) 将字符串转成数组

变量.split('')    括号里遇到什么就隔开,可以用正则,''表示将每个字符都隔开

3.boolean

布尔型有两个值:true(真)和false(假);

布尔型与数字型相加时,true的值为1,false的值为0;

console.log(true+3);   //1+3=4
console.log(false+3);  //0+3=3

4.null(空值)和undefined(未定义)

(1)null表示一个为空的对象;

         使用typeof检查一个null值时,会返回object;

(2)当声明一个变量,但不赋值时,它的值就是undefined;

         使用typeof检查一个undefined时,会返回undefined;

console.log(null+2);       //0+2=2
console.log(undefined+3);  //NaN
console.log(null+'SHY');       //null+SHY
console.log(undefined+'SHY');  //undefinedSHY

5.获取变量数据类型(typeof)

形式:typeof+变量名

注:null值返回的是object;prompt返回的是字符型(string).

var name="旗木卡卡西";  //string
var a=null;          //object
var b;               //unundefined 
document.write(typeof name,a,b);
var age=prompt('请输入您的年龄:');
document.write(typeof age);  //string

6.数据类型转换

(1)转换成字符串类型

1)方式一  tostring()方法不会影响到原变量,它会将转换的结果返回,但null和undefined这两个值没有tostring()方法,如果调用他们的方法会报错。

num.toString()

// 数字17转成string类型

let A = (17).toString()

// 结果为 17,类型为 string

num.toString(进制数)

// 17转16进制

 let A = (17).toString(16)

// 结果为 11,类型为 string

2)方式二  调用  string(数据)  函数,并将被转换的数据作为参数传递给函数;使用string()函数做强制类型转换时,对null和undefined及其它所有类型都起作用(将null转换为”null”   将undefined转换为” undefined”)。

3)方式三  隐式转换的形式:变量+' '       如,alert(unm+' ');

(2)转换成数值型

1)parseInt()可以将一个以数字开头的字符串中的有效的整数(遇到出数字以外的内容就结束)内容取出来

如:123.45px经ParseInt()转换成123

12a3.45px经ParseInt()转换成12

123px456经ParseInt()转换成123

abc12.35经ParseInt()转换成NaN

true 经ParseInt()转换成 NaN

false 经ParseInt()转换成 NaN

'null' 经ParseInt()转换成 NaN

null 经ParseInt()转换成 0

2)parseFloat()可以获得以数字开头的非纯数字字符串中有效的小数

如:123.456px经ParseInt()转换成123.456

    123.456.789px经ParseInt()转换成123.456

 3)number()函数

4)隐式转换(- * /)

+数据 +作为正号,其他数据类型可以转为数字类型(input和prompt输入的都是字符串类型)
let A = +prompt('输入工资')    // A为数字类型

+的作用:连接符、运算符、正号

将字符型12转换成数值型12,再进行运算

console.log('12'-0)  //12
console.log('123'-'120')   //3
console.log('12'*1)  //12
console.log('12'/1)  //12

5)三个案例

1.计算年龄案例
var year=prompt('请输入您的出生年份:');
var nowyear=2021;
var age=nowyear-year; //year为字符串型,这里用的减法,有隐式转换
alert('今年您'+age+'岁啦!');
2.简单加法器案例
var num1=prompt('请输入第一个值:');
var num2=prompt('请输入第二个值:');
var sum=Number(num1)+Number(num2);
var minus=num1-num2;
var mul=num1*num2;
var into=num1/num2;
alert('两个值之和为:'+sum);
alert('两个值相减为:'+minus);
alert('两个值相乘为:'+mul);
alert('两个值相除为:'+into);
3.依次询问并获取用户的姓名、年龄、性别,并打印用户信息(\n要放在引号里)
var myname=prompt('姓名'),age=prompt('年龄'),sex=prompt('性别');
alert('姓名:'+myname+'\n'+'年龄:'+age+'\n'+'性别:'+sex);

(3)转换成布尔型(boolean()函数)

代表空、否定的值( ' '、0、NaN、null、undefined)会被转换为false

!!'23'   转成布尔值

八、运算符

1.算数运算符(+ - * / %)

注:

(1)浮点数值得最高精度是17位小数,但在进行算数计算时其精确度远远不如整数。所以,不要直接判断两个浮点数是否相等var a=0.1+0.2;console.log(a==0.3);

(2)如果对两个字符串进行加法运算,则会做拼串(连接符)。即,任何的值和字符串做加法运算,都会先转换为字符串,然后再和字符串做拼串

1+2+”3”=33
“1”+2+3=123
100-“2”=98
2*“8“=16
2*null=0
// 小数与大数的余数为小数本身
2 % 7 = 2
(10-2) % 12 =8
2 % 7 - 9 / 3 = -1

任何值n取余,值为 0,1,2,... n-1
例:0,1,2,3,4,5,6   与3的余数
    0  1  2  0  1  2  0    0-2 

2.自增(++)和自减(--)运算符

  • 一元运算符包括 正负号、自增自减号;
  • 自增和自减运算符必须和变量配合使用,常用于计数

++a —> a=a+1            a++ —> a=a+1

(1)前置自增(++a) 先自加后返回值

(2)后置自增(a++) 先返回原值后加1

++a,--a  (在使用a之前,先使a的值加/减1)

a++,a--  (在使用a之后,使a的值加/减1)

var num1=10;  console.log(++num1 +10);   /*21*/
var num2=10;  console.log(num2++ +10);   /*20*/
// 后置自增(减)到了运算符的另一边完成自增(减)
var d=20;   var result=d++ + ++d + d ;   /*20+22+22=64*/
let a=3;    let result=++a + a++ + a++ + ++a   /* 4+4+5+7=20 */

3.比较运算符

1.==判等号(判断两边的值是否相等)
  console.log(18=='18');    /* true */
  console.log(1 == 'pink')  /* false 值根本不相等 */
2.===全等(两侧的值和数据类型完全一致才可以)
  console.log(18==='18')    /*false*/
  console.log(1 == 'pink')  /*false*/
3.!== 不全等(两边数据类型和值,只要有一个不等,就是true)
  console.log(18!==18)    /*false*/
  console.log(18!=='18')  /*true,类型不等*/
  console.log(25!==18)    /*true,值不等*/
4. > < <= >=   不同类型的比较会默认转换成number类型再比较(把字符串型的数据转换成数字型)

(1)比较运算符的返回值为true、false

(2)任何值和NaN做任何比较都是false

    10<="hello"  //false             true>false  //true        NaN===NaN  //false

(3)不要做小数运算,会有精度问题

0.1+0.2!=0.3
(0.1*10+0.2*10)/10=0.3      因为整数没有精度问题

(4)两个字符串比较:(比较字符对应的ASCII码)

4.逻辑运算符(&& || !)

(1)真假判断

与(&&) 两个都为真,返回值为真(true);其中一个为假,返回值就为假(false);

或(||) 其中一个为真,返回值就为真(true);两个都为假,返回值才为假(false);

非(!) 取反,真为假,假为真;

(2)短路运算(逻辑中断)

短路运算原理:当有多个表达式(非布尔进行与或运算)时,左边的表达式可以确定结果时(与&&遇假确定结果,或||遇真确定结果),就不再继续运算右边的表达式的值。

总结:与(&&)返回第一个为false的值;或(||)返回第一个为真的值。

1)逻辑与

   语法:表达式1 && 表达式2

   如果第一个表达式的值为true,则返回表达式2;如果两个值都为true,则返回后边的;

   如果第一个表达式的值为false,则返回表达式1;如果两个值都为false,则返回前边的;

注意:返回的值不是true/false,因为与、或、非没有能力将两侧的值转为true、false,只是判断值为真还是假

console.log(123 && 456);         /*456*/
console.log(123 && 456 && 1+2);  /*3*/

//左边如果是 0 '' undefin null NaN 则返回自身
console.log(0 && 456);           /*0*/
console.log(null && 456);        /*null*/
console.log(0 && 456 && 1+2);    /*0*/

2)逻辑或

语法:表达式1 || 表达式2

如果第一个表达式的值为真,则返回表达式1;

如果第一个表达式的值为假,则返回表达式2.

console.log(123 || 456);         /*123*/
console.log(0 || 456 || 1+2);    /*456*/
例:
var num=0;
console.log(123 || num++);  /*中断运算*/
console.log(num);           /*0*/

5.赋值运算符

例:num += 3 —> num=num+3

6.运算符优先级

九、流程控制(顺序结构、分支结构、循环结构)

1.顺序流程控制

顺序结构是程序中最简单、最基本的流程控制,程序会按照代码的先后顺序,依次执行。

2.分支流程控制

  • 根据不同的条件,执行不同的路径代码(多选一),从而得到不同的结果。
  • 如果条件不是布尔类型,会隐式转换为布尔类型,如,if (a) {alert('监督局')}

(1)if 语句

1.if-else 双分支
  if (条件) 
     {语句1;} 
  else
     {语句2;}

2.if-else if 多分支(检查多重条件,可以处理范围)
  if (条件1) 
     {语句1;} 
  else if (条件2)
     {语句2;}
  else if (条件3)
     {语句3;}
  else
     {语句4;}
判断是否能进入网吧
var age=+prompt("请输入您的年龄:");
if (age>=18) {
	alert('允许进入网吧!');
} else{
	alert('抱歉,您还未成年!');
}

判断闰平年(能被4整除且不能整除100的年份为闰年或者能够被400整除的就是闰年)
var year=+prompt("请输入年份:");
if (year%4==0&&year%100!=0||year%400==0) {
	alert(`${year}为闰年`);
} else{
	alert(`${year}为平年`);
}

(2)三元表达式

条件表达式 ?表达式1 :表达式2;

如果条件表达式为真,则返回表达式1;如果条件表达式为假,则返回表达式2。(简化版的if-else,一般用于赋值,比如最大值比较,将最大值赋给变量)

补0操作,可用于倒计时(小于10的数补零,大于10的数不补零)
方法一:
var num=prompt("请输入一个数字:");
num<10 ? alert('0'+num+' 补零') : alert(num+' 不补零');
方法二
var num=prompt("请输入一个数字:");
var result= num<10 ? '0'+num+' 补零' : num+' 不补零';
alert (result);

(3)switch-case

一般用于等值判断,不适合于区间判断

switch (表达式/变量){
	case 值1:语句1;
		break;       //break 跳出
	case 值2:语句2;
		break;
	...
	default:
		break;
}

注:

1)case后面必须是一个整数,或是结果为整数的表达式,但不能包含任何变量

2)若某一条case后没有紧跟的语句,则不用带分号和break

3)表达式/变量的值和case里的值匹配时,得是全等关系(值和数据类型都得相同)

4)如果当前的case里没有break,则不会退出,会继续执行下一个case

5)若没有default,并且和任何case值不匹配时,则跳出switch,什么都不执行

1. 查询水果价格
var fruit=prompt("请输入一种水果:");
switch (fruit){
	case '苹果':alert("苹果 0.8/斤");    //水果名称要加引号,因为要全等匹配
		break;
	case '香蕉':alert("香蕉 1.5/斤");
		break;
	case '草莓':alert("草莓 2/斤");
		break;
	default:alert("无此种水果");
		break;
}

2. 用户输入2个数字

3.循环流程控制

在程序中,一组被重复执行的语句被称之为循环体,能否继续重复执行,取决于循环的终止条件。由循环体及循环的终止条件组成的语句,被称之为循环语句

循环的目的:重复的执行某些代码。

(1)for循环及双重for循环

for (初始化变量 ; 条件表达式 ; 操作表达式) {循环体}

for (var i=1,j=0 ; i<=10 ; i=i+2) {循环体}
(1)注意是var,不是int
(2)当有多个初始化变量时,要用“,”隔开
(3)for循环的变量(int i)是个局部变量,只在for循环体里有效
for循环可以嵌套if语句
  for (var i=1;i<=20;i++) {
	  if (i==1){
		  console.log('1岁了,出生年!');
	  }
	  else if (i==18) {
	  	console.log('18岁,成年了!');
	  }
	  else {console.log('今年'+i+'岁了');}
  }

1-100的总和、平均值、奇数、偶数、能被3整除的数字的和
  var sum=0,odd=0,even=0,result=0;
  for (var i=1;i<=100;i++) {
	  sum+=i;     //总和

	  if (i%2!=0) {  //奇数
		  odd+=i;
	  }
	  else {even+=i;}  //偶数

	  if (i%3==0){    //能被3整除的
	 	  result+=i;
	  }
  }
  document.write('和为:'+sum,'平均数为:'+sum/100,'奇数'+odd,'偶数'+even,'能被3整除的数字的和'+result);

输入班级人数并计算出总成绩和平均成绩
  var num=prompt ('请输入班级人数:');
  var sum=0;
  for (var i=1;i<=num;i++) {
	  var sorce=prompt ('请输入第'+i+'个人的成绩:')
	  sum+=parseFloat(sorce);   //prompt取过来的是字符型,需要转换成数字型
  }
  alert('班级总成绩为:'+sum);
  alert('班级平均成绩为:'+sum/num)

for循环双层嵌套
for(外边循环变量的起始值;循环条件;变化值){
    for(里边循环变量的起始值;循环条件;变化值){
    }
}


图一:一行打印5颗♥   重点:转换为字符串就可以在一行上打印出
  var str='';
  for(i=1;i<=5;i++){
    str=str+'♥';
  }
  console.log(str);

图二:五行五列
  var str='';
  for(i=1;i<=5;i++){       //外面for是列,控制里层for要循环多少次
	  for(j=1;j<=5;j++){   //里面for是行,控制每一行的个数
	    str=str+'♥';
	  }
	  str=str+'\n';
  }
  document.write(str);

图三:倒三角
  var str='';
  for(i=1;i<=10;i++){
    for(j=i;j<=10;j++){    //(j=10;j>=1;j--)错误,因为这样每次循环j=10,所以最后结果的每行都是10个
      str=str+'♥';
    }
  str=str+'\n';
  }
  console.log(str);

正三角
  var str='';
  for(i=1;i<=10;i++){
    for(j=1;j<=i;j++){
      str+='♥';
    }
  str+='\n';
  }
  console.log(str);

九九乘法表
  var str='';
  for(i=1;i<=9;i++){
    for(j=1;j<=i;j++){   //每一行公式的个数正好和行数一致,所以 j<=i
      str+=j+'*'+i+'='+j*i+'\t';   //j*i=行*列
    }
    str+='\n';
  }
  console.log(str);
打印在网页中(\n只是输出一个空格;<br>在JS里也是换行)
for(i=1;i<=9;i++){
  for(j=1;j<=i;j++){
    document.write(j+'*'+i+'='+j*i+'\t');
  }
  document.write('<br>');
}

(2)while循环及do-while循环

1.while (先判断 再执行)
  var 声明变量并初始化;
  while(循环条件) 
    {循环体;}

2.do-while (先执行 再判断)
  do {循环体;}
  while (循环条件);
循环案例
1.打印人的一生,从1岁到100岁
(1)while
  var age=1;
  while(age<=100) 
    {document.write('今年'+age+'岁了!'+'<br>');
    age++;}
(2)do-while
  var age=1;
  do{
     document.write('今年'+age+'岁了!'+'<br>');
     age++;
  }while(age<=100);

2.计算1-100之间所有整数和
(1)while
  var num=1,sum=0;
  while(num<=100){
	  sum+=num;
	  num++;
  }
  document.write(sum);
(2)do-while
	var num=1,sum=0;
	do{
		sum+=num;
		num++;
	}while(num<=100);
	document.write(sum);

3.弹出一个提示框'love?',如果输入'yes'就结束,否则就一直询问
(1)while
  var answer=prompt('love?');
  while(answer!='yes'){
	  answer=prompt('love?');  //不能只写prompt('love?'); 要给它一个变量保留它的值,才能返回判断,否则会陷入死循环
  }
(2)do-while
	var answer=prompt('love?');
	do{
		answer=prompt('love?');
	}while(answer!='yes');
(3)开关法(不确定循环的次数)

(3)for循环与while循环在什么情况下使用

循环次数已知的,用for循环;循环次数未知的用while循环;

用来计数,跟数字有关,常用for;

while和do-while可以做更复杂的判断条件,比for循环更灵活。

(4)跳转循环语句(continue / break / goto)

break跳出当前整个循环,只能跳出一层循环
continue结束本次循环,判断循环条件,如果成立,则进入下一次循环,否则退出整个循环
goto跳出多层循环
1. 求1-100之间,除了能被7整除之外的整数和
	var sum=0;
	for(var i=1;i<=100;i++){
		if(i%7==0){
			continue;
		}
		sum+=i; 
	}
	document.write(sum);

2. 打印1-10的奇数
// 思路:跳过偶数,打印奇数
        for(let i=1;i<=10;i++){
            if(i%2===0){
                continue
            }
            console.log(i)
        }

3. 求1-100前3个能被7整除的数的和
        let sum = 0
        let count = 0   //计数器
        for (let num = 1; num <= 100; num++) {
            if (num % 7 === 0) {
                count++
                
                // 当次数为4时就跳出循环
                if (count === 4) {
                    break
                }
                sum += num

                // 当计数器写在和的下面时,到3次时就要跳出循环
                /* if (count === 3) {
                    break
                } */
            }
        }
        console.log(sum)

4.综合案例—简易ATM

方法一:
// 开关法循环,在循环外边定义一个变量为true,进入循环体里边去,想要结束循环,变量在循环里=false
        let flag = true,
            money = 0
        while (flag) {
            let num = +prompt(`请选择您的操作:
            1. 存款
            2. 取款
            3. 查看余额
            4. 退出`)
            switch (num) {
                case 1:
                    let add = +prompt('请输入存款金额:')
                    money += add
                    break
                case 2:
                    let get = +prompt('请输入存款金额:')
                    money -= get
                    break
                case 3:
                    alert(`余额为${money}`)
                    break
                case 4:
                    flag = false
                    break
                default: alert('请输入正确序号')
            }
        }


方法二:
alert('欢迎登入!');
var money=0;
for (var i=1;i>=1;i++){
	var num=prompt('请输入您要的操作:'+'\n'+'1.存钱'+'\n'+'2.取钱'+'\n'+'3.查询余额'+'\n'+'4.退出')
  //一开始的输入框要写在for循环内,才能每次返回到开始的界面
	if (num=='1') {
		money1=prompt('请输入存的钱数:');
        money+=parseFloat(money1);
		alert('现在余额为:'+parseFloat(money));
	}
	else if (num=='2') {
		cut=prompt('请输入取的钱数:');
		cut=parseFloat(cut);
		if (cut<=money){
			money=parseFloat(money-cut);
		    alert('现在余额为:'+money);
		}
		else {alert('余额不足!');}
	}
	else if (num=='3') {
		alert('现在余额为:'+parseFloat(money));
	}
	else if (num=='4') {
		alert('现在正在退出!');
		break;
	}
	else {alert('请按要求输入!');}
}

5.计数器

// 计算数组[2,6,18,15,40] 中能被3整除的偶数的个数
        let arr = [2, 6, 18, 15, 40]
        let count = 0
        for (let i = 0; i < arr.length; i++) {
            if (arr[i] % 3 === 0 && arr[i] % 2 === 0) {
                count++
            }
        }
        console.log(count)

十、数组

1.数组的概念

数组是指一组数据的集合,其中的每个数据被称作数组元素,在数组中可以存放任意类型的元素。数组是一种将一组数据存储在单个变量名下的方式。

2.创建数组

数组中可以放任意类型的数据;数组元素之间要用','逗号隔开。

1)利用数组字面量创建数组

var 数组名=[ ];
var a=[20,'城南',true];

2)利用new创建数组

var 数组名=new Array( );   注意A要大写
var a = new Array(1,'短信',true);

3.获取数组元素

数组名[元素的索引号]
a[2]  获取a数组中索引号为2的元素。如果没有这个数组元素,输出的结果会是 undefined

索引(下标):用来访问数组元素的序号(数组索引号从0开始

4.遍历数组(利用for循环)

遍历:把数组中的每个元素从头到尾都访问一次。

?结果有问题      不能用name作为数组名,因为name是关键词

?结果无引号      alert无引号,console.log控制~台有引号

var forename=['刘备','关羽','张飞','马超'];
for(var i=0;i<4;i++){    //i从0开始;i<=元素个数(forename.length)
	console.log(forename[i]);
}

5.数组的长度

数组名.length
a.length   //数组a中元素的个数,length为变量属性名

 6.数组的增删改查

(1)修改数组索引,添加/更改数组元素

数组名[索引号]

用来判断数组中是否包含某个对象或值

indexOf或findIndex()

数组名[索引号]=要改成的值
数组名.push(数据1,数据2,...数据n)新增加的数组元素在数组的末尾
console.log(数组名.push(数据1,数据2,...数据n))打印此语句,返回新数组的长度
数组名.unshift(数据1,数据2,...数据n)新增加的数组元素在数组的前边
console.log(数组名.unshift(数据1,数据2,...数据n))打印此语句,返回新数组的长度
数组名.pop( )删除数组的最后一个元素
数组名.shift( )删除数组的第一个元素
console.log(数组名.pop( ))返回被删除的元素
console.log(数组名.shift( ))
数组名.splice(开始删除元素的索引值,删除的数量,新增数据1,新增数据2)

批量删除(抽奖、购物车)

返回被删除的元素

数组名.filter( )            // 过滤
var str=['红','绿','蓝','白'];
str[0]='黄';         //更改  结果:黄,绿,蓝,白

str[5]='黑';         //添加  结果:红,绿,蓝,白,黑
str.push=[1,2]       //添加  结果:红,绿,蓝,白,1,2
console.log(str.push=[1,2])  // 结果:6(长度)
str.unshift=[A,B,C]  //添加  结果:A,B,C,红,绿,蓝,白
console.log(str.unshift=[A,B,C])   // 结果:7(长度)

str.pop()                // 删除  结果:'红','绿','蓝'
console.log(str.pop())   // 结果:'白'
str.shift()              // 删除  结果:'绿','蓝','白'
console.log(str.shift()) // 结果:'红'
str.splice(1,2)          // 批量删除  结果:'红','白'
str.splice(1)            // 批量删除  结果:'红'  (将起使元素后面的元素都删除)
str.splice(1,1)          // 批量删除  结果:'红','蓝','白'  (只删除起使元素)

str='赋值没加索引';  //结果:赋值没加索引
  注意:不能直接给数组名赋值,否则会覆盖掉以前的数据

(2)修改length,实现数组扩容

数组名.length=想要扩到的长度;
a.length=7;

7.数组的方法

数组的方法作用举例

reverse

翻转数组顺序

数组名.reverse()

indexOf

在数组中查找某项首次出现的索引位置,找到返回索引值,找不到返回 -1

可用来判断数组中是否包含某个对象或值

数组名.indexOf('数组中的某一项元素')
lastIndexOf在数组中查找某项最后出现的索引位置,找到返回索引值,找不到返回 -1数组名.lastIndexOf('数组中的某一项元素')

join

拼接数组的每个元素成为字符串

数组名.join('拼接的符号或为空')

concat拼接数组成为新数组

a.concat(b, c, 'aa', 'bb', 'cc')

把数组b/c及'aa'等拼接到数组a后面

数组的静态方法作用举例
Array.isArray(数组名)判断是否是数组类型,返回布尔值

let arr=[1,2,3]

Array.isArray(arr)

Array.from(伪数组名)

把伪数组转换为真数组

注意:转真数组时,伪数组必须有length属性,length可以比伪数组元素的数量多(undefined)或少(有几个就显示几个)

let as = {

            0: 'a',

            1: 'b',

            2: 'c',

            3: 'd',

            4: 'e',

            length: 6

        }

Array.from(as)

遍历数组的方法:7种
    每个方法都有3个形参:
    item     第一个参数,代表当前项(arr[i])
    index    第二个参数,代表当前项的索引值(i)
    o        第三个参数,代表当前数组(arr)

        let arr = [3, 2, 6, 7, 9, 4]   // 全局数组变量

1. forEach:遍历数组
		arr.forEach(function (item, index, o) { console.log(item) })
		arr.forEach(item => console.log(item))   // 箭头函数

2. filter:过滤  筛选出满足条件的值放到数组中返回,可用于删除
		let re = arr.filter(function (item) {
			return item % 2 === 0    // 返回偶数
		})
		console.log(re)    // 结果:[2,6,4]

3. map:依次让每个元素执行一遍回调,把结果全部放到数组中返回
		let re = arr.map(function (item) {
			return item * item
		})
		console.log(re)    // 结果:[9,4,36,49,81,16]

 4. find:遍历查找满足条件的首个元素,并返回这个元素
		let re = arr.find(function (item) {
			return item >5
		})
		console.log(re)    // 结果:6

 5. findIndex:遍历查找满足条件的首个元素,并返回这个元素的索引值
    可用来判断数组中是否包含某个对象或值
		let re = arr.findIndex(function (item) {
			return item > 5  或  item.name=='张三'
                   大于5的值     数组中name为张三的对象
		})
		console.log(re)    // 结果:2

6.  some:遍历查找是否有满足条件的值,有一个就立刻返回true,没有则返回false
		let re = arr.some(function (item, index) {
			console.log(index)    // 输出:0 1 2  说明有一个满足条件就立刻返回
			return item > 5
		})
		console.log(re)    // 结果:true

7. every:遍历查找满足条件的值,都满足返回true,有一个不满足就返回false
		let re = arr.every(function (item, index) {
			console.log(index)    // 输出:0 1   说明有一个不满足条件就立刻返回false
			return item > 2
		})
		console.log(re)    // 结果:false

7.筛选数组

将数组[2,0,6,1,77,0,52,0,25,7]中大于等于10的元素选出来,放入新数组
  方法一:
    var num=[2,0,6,1,77,0,52,0,25,7];
    var num1=[];
    var j=0;
    for(var i=0;i<num.length;i++){
    	if(num[i]>=10){
    		num1[j]=num[i];
    		j++;
    	}
    }
    alert(num1);
  方法二:(不用申请新变量j)
    var num=[2,0,6,1,77,0,52,0,25,7];
    var num1=[];  //空数组length为0
    for(var i=0;i<10;i++){
    	if(num[i]>=10){
    		num1[num1.length]=num[i];  //length自动检测数组的长度
    	}
    }
    alert(num1);
方法三:
        let arr=[2,0,6,1,77,0,52,0,25,7]
        // 思路:遍历数组,筛选出>=10的元素,用if追加到新数组
        let newArr=[]
        for(let i=0;i<=arr.length;i++){
            if(arr[i]>=10){
                newArr.push(arr[i])
            }
        }
        console.log(newArr)

8.删除指定数组元素(去重)

1. 将[2,0,6,1,77,0,52,0,25,7]中的0去掉后,形成一个不包含0的新数组
	var num=[2,0,6,1,77,0,52,0,25,7];
	var num1=[];
	for(i=0;i<num.length;i++){
		if(num[i]!=0){
			num1[num1.length]=num[i];
            // num1.push(num[i])
		}
	}
	alert(num1);   //结果:2,6,1,77,52,25,7
2. 需求:将不重复的元素放入新数组
   思路:依次获取数组arr的值,去newArr里查看是否有这个元素,如果没有这个值就添加

		let arr = ['a', 1, 2, 'b', 'a', 'c', 'd', 1, 'a', 3, 2]
		let newArr = []
		for (let i = 0; i < arr.length; i++) {
			if (newArr.indexOf(arr[i]) === -1) {
				newArr.push(arr[i])
			}
		}
		console.log(newArr)

9.翻转数组

将数组[1,2,3,4,5]的内容反过来存放,输出:[5,4,3,2,1]
  方法一:
	var num=[1,2,3,4,5];
	var num1=[];
	for(var i=num.length-1;i>=0;i--){           *不同处
		num1[num1.length]=num[i];               *
	}
	alert(num1);
  方法二:
	var num=[1,2,3,4,5];
	var num1=[];
	for(var i=0;i<num.length;i++){              *
		num1[num1.length]=num[num.length-1-i];  *
	}
	alert(num1);

10.数组排序(冒泡排序)

冒泡排序:把一系列的数据按照一定的顺序进行排列显示(从小到大或从大到小)。

冒泡排序一次比较两个元素,如果它们的顺序错误就把它们交换过来,否则不交换。

	var num=[1,2,3,4,5];                      //可以是任意数组
	for(var i=0;i<=num.length-1;i++){         //外层循环总共的趟数(元素个数-1)
		for(var j=0;j<=num.length-1-i;j++){   //内层循环每趟比较的次数(元素个数-1-趟数)
			if(num[j]<num[j+1]){              //前后两个相邻的元素相比较(大于号、小于号控制升序还是降序)
				var temp=num[j];              //交换两个元素的值
				num[j]=num[j+1];
				num[j+1]=temp;
			}
		}
	}
	alert(num);   //结果:[5,4,3,2,1]

案例

1.返回数组的和、平均值及最大值
    var num=[2,6,1,7,4];
    var sum=0;
    var max=num[0];
    for(i=0;i<num.length;i++){
    	sum+=num[i];
    	if(max<num[i]){
    		max=num[i];
    	}
    }
	alert('所有元素的和为'+sum+';平均值为'+sum/num.length);
	alert('最大的数为'+max);

2.在每个元素之间加分隔符
    var num=['河','北','石','家','庄'];
    var str='';
    for(i=0;i<num.length;i++){
    	str+=num[i];
    	if(i<num.length-1){
    		str+='|';
    	}
    }
	alert(str);

3.在新数组中存放10个数(1~10)
    var num=[];
    for(var i=0;i<=9;i++){
    	num[i]=i+1;
    }
    alert(num);

十一、函数

函数与循环的区别:循环是一次把所有次数执行完,不方便控制执行的位置;函数时需要的时候就调用一次。

函数就是封装了一段可以被重复执行调用的代码块(封装了一定功能),可进行复用。

封装:把一个或多个功能通过函数的方式封装起来,对外只提供一个简单的函数接口。

1.函数的声明与调用

 书写格式 例子
声明函数function

function 函数名() {

        函数体代码;

}

函数名();

function getsum(num1

 ) {

        for(i=0;i<num1;i++){
        sum+=num1[i];
        }

}

getsum(20);

//求1~20的和

调用函数函数名();
注:声明函数本身并不会执行函数,只有调用函数时才会执行函数体代码(即,两者相互配合使用)

函数名的命名和变量相同,一般以动词为前缀

2.函数的参数

所在位置说明关系
形参

function 函数名(形参1,形参2) {

        函数体代码;

函数名(实参1,实参2);

在声明函数的小括号里面是形参

函数定义的时候传递的参数(形式上的参数)

1.实参把值传递给形参;形参是接受实参的

2.函数的参数可以有多个(多个参数用逗号隔开),也可以没有

3.开发中尽量保持形参和实参个数一致 

4.实参可以是任意的数据类型,数组/变量

实参

在调用函数的小括号里面是实参

函数调用的时候传递的参数(实际的参数)

逻辑中断的巧用

  • 形参如果不被赋值,就是undefined
  • 利用逻辑中断,将输入的非法值转化合法的
//形参如果不被赋值,就是undefined
        function getSum(num1, num2) {
            // 利用逻辑中断,将输入的非法值转化成0,使之能顺利进行计算
            //或运算找true
            num1 = num1 || 0
            num2 = num2 || 0
            console.log(num1 + num2)
        }
        //多余的实参(3,5)不会传入函数
        getSum(2, NaN, 3, 5)    //结果:2

3.函数返回值(return)

作用:把处理结果返回给调用的函数,可在函数外使用

关键词:return

语法:return   数据
           return [num1, num2]       (返回多个值时,保存到数组里,再返回给函数调用)

function getSum(num1, num2) {
      // 将值返回给调用的函数
      return num1 + num2
      //此语句不会执行,因为return会立即结束当前函数
      document.write('123')
}
console.log(getSum(2, 5)+10)
或
let sum=getSum(2, 5)
console.log(sum);

注意:

  • 在函数体中使用 return 关键字能将内部的执行结果交给函数外部使用
  • 函数没有 return 时,函数默认返回值为 undefined
  • 函数内部只能出现 1 次 return,并且 return 后面代码不会再被执行,所以 return 后面的数据不要换行写
  • return会立即结束当前函数

4.作用域

  • 作用域定义:代码(变量)的可用性范围
  • 作用:作用域提高了程序逻辑的局部性,增强了程序的可靠性,减少了名字冲突

(1)作用域

作用域分类说明注意
全局作用域函数外部或者整个script 有效如果函数内部或者块级作用域内部,不声明直接赋值使用的变量,是全局变量
局部作用域函数内部有效,也称为函数作用域

函数的形参可以当做局部变量;

为什么在函数内部声明的变量只能在函数内部被访问,外部无法直接访问 :

执行函数时会开拓出一块区域存放局部变量,执行完就销毁了,所以外部无法直接访问

块级作用域{ } 内有效,包括,if语 句和for语句里面的{ }等
变量分类说明
全局变量函数外部let 的变量全局变量在任何区域都可以访问和修改
局部变量函数内部let的变量局部变量只能在当前函数内部访问和修改
块级变量{} 内部的let变量let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问
let a = 1         // 全局变量
function fn() {
    let b = 2     // 局部变量
    if (true) {
       let c = 3  // 块级变量
    }
}
fn()

(2)作用域链

作用域链,由作用域串联起来的链状结构(就近查找,如果没有,去上一级)

怎么看一个区域是否有变量:是否有var / let / cost 关键词,否则是使用变量。

作用:提供查找变量的机制。

(3)闭包

定义:闭包是一个作用域有权访问另外一个(上级)作用域的变量;

判断是否产生闭包:断点调试出现closure;

作用:可以把一个变量使用范围延伸(局部变量在外边也能用)

  1. 闭包本质仍是函数,只不是从函数内部返回的;

  2. 闭包能够创建外部可访问的隔离作用域,避免全局变量污染;

  3. 过度使用闭包可能造成内存泄漏;

注:回调函数也能访问函数内部的局部变量。

let num = 123;
function f1 () {
	let n = 666;
	return function () {console.log(n);}  // 这个函数是闭包函数
}
let re = f1();  // 返回的是一个函数  结果:function () {console.log(n);}
re();  // 调用返回的这个函数 结果:666
闭包的作用:避免变量污染
let uname='阿飞'
function fn(){
    let uname='小狸'
    return function(){
        console.log(uname)
    }
}
fn()()    // 实现闭包,局部变量在外面也能用

(4)预解析 (变量提升)

名次说明
预解析代码在执行之前先要解析一边,预解析分为 变量提升 和 函数提升。
变量提升带有声明的变量,把声明变量的语法提升到当前作用域最前边(注意:只声明不赋值
函数提升带有声明的函数,把声明函数的语法提升到当前作用域最前边(注意:只声明不调用

注意点:

  • 变量在未声明即被访问时会报语法错误;变量在声明之前即被访问,变量的值为 undefined
  • 函数优先:
  • console.log(a)   // 函数a
    function a() {
    	console.log('aa')  
    }
    var a = 1
    console.log(a)   // 1
  • let 声明的变量不存在变量提升(报的错应该是:变量未定义),推荐使用 let【也有人认为具有提升但是不赋值不能使用(因为报的错是:初始化之前不能使用)】
var a=b=c=9
等于
var a=9
b=9    b / c 没有关键词,为全局变量
c=9

5.匿名函数

(1)函数表达式

将匿名函数赋值给一个变量,并且通过变量名称进行调用 我们将这个称为函数表达式

let 变量名 = function () {
    匿名函数内的代码
}
变量名()    // 调用函数

例:
let fn = function () {
    console.log(111)
}
fn()
函数也是一种数据

(2)立即执行函数

  • 定义:只执行1次的自调用函数
  • 作用:避免全局变量之间的污染
  • 注意: 多个立即执行函数前要用 ; 隔开,要不然会报错,避免和前面的语句连起来
写法一:
    ;(function () {
         console.log(111)
         })()

写法二:
    ;(function () {
         console.log(111)
     }())

 6. 箭头函数(=>)

语法:

  • 箭头函数是一种声明函数的简洁语法,不存在预解析 ;
  • 箭头函数只有一个参数时可以省略圆括号 ()
  • 箭头函数函数体只有一行代码时可以省略花括号 {},并自动做为返回值被返回
  • 举例: 
    // 具名函数
        function fn() { }
    
    // 箭头函数:相当于具名函数
        let fn = () => { 代码块 }
        fn()
    
    // 1. 1个形参省略()
    // 2. 1句代码块省略{ }
        let fn = n => n * n
    	fn(3)  // 9
    
    // 3. 定时器中的箭头函数
        setInterval(() => { }, 1000)
        setInterval(function () { }, 1000)

注意:

  • 箭头函数属于表达式函数,因此不存在预解析,所以必须先定义再使用
  • 箭头函数中没有 arguments,只能使用 ... 动态获取实参
  • 涉及到this(指向调用者) 的使用,不建议用箭头函数(所用this指向上级作用域的this)
  • let obj = {
    	uname: 'SHY',
    	age: 18,
    	school: function () {
    			console.log(this)     // this指的是obj
    			window.setInterval(() => {console.log(this)}, 1000)}   // this指的是obj,不是window
    }

7. 参数

(1)默认值

  • 声明函数时为形参赋值即为参数的默认值;
  • 如果参数未自定义默认值时,参数的默认值为 undefined;
  • 调用函数时没有传入对应实参时,参数的默认值被当做实参传入;
 // 设置参数默认值
function sayHi(name="小明", age=18) { }
sayHi( undefined, 20);   // undefined是用来给name占位的
        name      age

(2)动态参数(arguments)

函数的返回值arguments的使用函数的两种声明方式

  • arguments是函数里边的内置的伪数组变量(对象);
  • arguments是伪数组,没有push/pop 这些方法,存的是实参,可以批量处理实参;
  • 作用:用于接收传入的所有实参;
  • 使用情况:参数个数不固定时使用argument接受实参;
//取实参中的最大值
    //方法一:用arguments函数返回值
        function getMax() {
            let max = arguments[0]
            for (let i = 0; i < arguments.length; i++) {
                if (max < arguments[i]) {
                    max = arguments[i]
                }
            }
            return max
        }
        let num = getMax(5, 30, 12, 50, 25)
        console.log(`实参中的最大值是${num}`)

    //方法二:老方法,将实参装入数组中
        function getMax(a,b,c,d,e) {
            let arr = [a,b,c,d,e]
            let max=arr[0]
            for (let i = 1; i < arr.length; i++) {
                if (max < arr[i]) {
                    max = arr[i]
                }
            }
            return max
        }
        let num = getMax(5, 30, 12, 50, 25)
        console.log(`实参中的最大值是${num}`)

(3)剩余函数(...)

  • ... 是语法符号,置于最末函数形参之前,用于获取多余的实参;
  • 借助 ... 获取的剩余实参;
  • 可替换 arguments;
// ...将实参以数组形式存入num中
		function log(...num) {
			let sum = 0
			for (let i = 0; i < num.length; i++) {
				sum += num[i]
			}
			console.log(sum);
		}
		log(1, 2, 3, 4, 5, 6, 7, 8, 9)

8.高阶函数、回调函数

当一个函数A给另外一个函数B当做参数的时候,A函数就是回调函数,B函数就是高阶函数

函数也是一种数据类型。

1. fun函数是高阶函数,给fun当参数的匿名函数是回调函数
    function fun ( n ) {n();}
    fun( function () {console.log('aa');} );

2. 事件:
    document.addEventLisntener('click', function () { });
                                         回调函数
3. 定时器
    setInterval( function () { }, 1000 );
      高阶函数     回调函数

9.递归函数

(1)利用递归求n的阶乘


利用递归函数求1~n的阶乘 1 * 2 * 3 * 4 * ..n
        let num = +prompt('请输入一个数字,计算1到这个数的阶乘:')
        function getN() {
            if (num === 1) {
                return 1
            }
            else if (num > 0) {
                return num * getN(num - 1)
            }
        }
        alert(`阶乘为${getN()}`)
        getN()

(2)斐波那契数列

function fn(n) {
    if (n === 1 || n === 2) return 1
    return fn(n - 1) + fn(n - 2)
}
fn(45)    // 第45位对应的数字

斐波那契数列规律:  第n位对应的数字=第(n-1)位对应的数字+第(n-2)位对应的数字
    位置:1  2  3  4  5  6  7  8  ...  45
    数字:1  1  2  3  5  8  13 21 ...  ?

10.环境对象(this) 

环境对象指的是函数内部特殊的变量 this

this指向调用者

函数案例

1.利用函数计算1-100之间的累加值

function getsum(num) {
	var sum=0;
	for (i=0;i<=num;i++) {
		sum+=i;
	}
	alert("累加和为:"+sum);
}
getsum(100);

2.用函数求任意两个数之间的和

function getsum(start,end) {
	var sum=0;
	for (i=start;i<=end;i++) {
		sum+=i;
	}
	alert(start+"与"+end"之间的累加和为:"+sum);
}
/*getsum(start,end); */
getsum(2,10);      /*求的是2~10之间的累计和*/

3.求2个数的和 

// 方法一:将实参传入函数
        function getSum(num1, num2) {
            alert(num1 + num2)
        }
        getSum(2, 3)
        getSum(24, 56)

// 方法二:之前的写法,然后封装
        function getSum1() {
            let num1 = +prompt('输入第一个数')
            let num2 = +prompt('输入第二个数')
            alert(num1+num2)
        }
        getSum1()

4.求学生总分

// 求学生总分
        function getSum(arr) {
            let sum = 0
            for (let i = 0; i < arr.length; i++) {
                sum += arr[i]
            }
            console.log(sum)
        }
        // 实参可以是任意的数据类型
        getSum([10, 20, 30])
        let shy = [20, 30, 40, 100, 50]
        getSum(shy)

4-1.求一个数组最大值和最小值

// 求一个数组最大值和最小值
        function getNum(arr) {
            // 假设第一项为最大值/最小值
            let max =  min = arr[0]
            // 因max = arr[0],所以 i = 1 可减少一次循环
            for (let i = 1; i < arr.length; i++) {
                if (max < arr[i]) {
                    max = arr[i]
                }
                if (min > arr[i]) {
                    min = arr[i]
                }
            }
            // 返回多个值时,保存到数组里,再返回给函数调用
            return [max, min]
        }
        let res= getNum([20, 50, 30, 10, 40, 100])
        console.log(`该数组的最大值为${res[0]},最小值为${res[1]}`)

4-2.求一个数组最大值和最小值的变种

// 求一个数组最大值和最小值
// 函数调用的时候,如果第二个参数为true,返回最大值;如果没有第二个参数,返回最小值
        function getNum(arr,flag) {
            let max =  min = arr[0]
            for (let i = 1; i < arr.length; i++) {
                if (max < arr[i]) {
                    max = arr[i]
                }
                if (min > arr[i]) {
                    min = arr[i]
                }
            }

            if(flag){
                return max
            }
            else{return min}
        }
        let res= getNum([20, 50, 30, 10, 40, 100],true)
        let res1= getNum([20, 50, 30, 10, 40, 100])
        console.log(res)
        console.log(res1)

5.求两个数的最大值

// 求两个数的最大值
// 用户输入两个值,比较大小,封装函数,将大的值返回到函数外边,在页面中打印出来
        function getNum(num1, num2) {
            num1 = +prompt('请输入第一个数值:')
            num2 = +prompt('请输入第二个数值:')

            return num1 > num2 ? num1 : num2
            三元表达式 或 if表达式
            if (num1 > num2) {
                return `最大值为${num1}`
            }
            else if (num1 < num2) {
                return `最大值为${num2}`
            }
            else {
                return `两值相等`
            }
        }
        alert(getNum())

6.时间转换 (用户输入秒数,可以自动转换为天时分秒)

// 用户输入秒数,可以自动转换为天时分秒
        let num = +prompt('请输入要转换的秒数:')

        function getTime(time) {
            // 天时分秒的转换,只取整数
            // 任何值n取余,值为 0,1,2,... n-1
            let day = parseInt(num / 60 / 60 / 24)
            let hour = parseInt(num / 60 / 60 % 24)   // 1天 0-23小时
            let min = parseInt(num / 60 % 60)         // 1小时 0-59分钟
            let sec = parseInt(num % 60)              // 1分钟 0-59秒

            // 三元表达式补0操作
            day = day <= 9 ? '0' + day : day
            hour = hour <= 9 ? '0' + hour : hour
            min = min <= 9 ? '0' + min : min
            sec = sec <= 9 ? '0' + sec : sec

            // 函数返回值,一次返回多个数据,用数组
            return [day, hour, min, sec]
        }
        let time = getTime(num)      // 调用函数,实参也可以是变量
        document.write(`${num}秒被转换成${time[0]}天${time[1]}时${time[2]}分${time[3]}秒`)

十二、对象

1.定义

  • 对象(object):JavaScript里的一种数据类型;
  • 对象是无序的数据的集合;
  • 可以详细的描述描述某个事物(对象保存多个关联性比较大的属性);

2.对象的使用

(1)声明对象

let 对象名 = {        //对象声明
    属性名: 属性值,
    方法名: 匿名函数
}

let Person = {
    uname:'大黄',
    sayHi:function() { console.log('sayHi方法') },   // 方法的函数内可以传参数
    'play ball': function () { }                    //方法名也可加引号,方法之间也用逗号隔开
}

(2)访问对象的属性/方法

属性访问的2种方法    获取对象里的属性值
    (1)对象名.属性名    访问固定属性
        console.log(person.my - name)  // 不生效
        console.log(person.age)
    (2)对象名['属性名']    访问动态属性
        console.log(person['my-name'])
        console.log(person['sex'])


方法访问的2种方法    调用对象里的方法
    (1)对象名.方法名()
        person.sing(123)           // 可以传参
        person.'play ball'()       // 错误,多个单词的方法名必须用[]
    (2)对象名['方法名']()
        person['sing']()
        person['play ball']()

注意:
    [] 里面的属性名一定加引号;
    当属性为动态时用 []

3.操作对象(增删改查)

增删改都是基于查的语法之上的。

1. 改(重新赋值)    在对象外操作
    (1)对象名.属性名=属性值
        person.age = 99
        console.log(person.age)
    (2)对象名['属性名']=属性值
        person['my-name'] = '明明'
        console.log(person['my-name'])

    (3)对象名.方法名 = 匿名函数     定义时带引号的方法名用此不生效
        person.sing = function () {
            console.log('sing改1')
        }
        person.sing()
    (4)对象名['方法名']=匿名函数
        person['sing'] = function () {
            console.log('sing改2')
        }
        person.sing()

        person['play ball'] = function () {
            console.log('play ball改2')
        }
        person['play ball']()


2. 增
    (1)对象名.新属性名 = 新属性值
         对象名.新方法名 = 新匿名函数
        person.school = 'sjzxy'
        person.drink = function () {
            console.log('喝mink')
        }
    (2)对象名['新属性名'] = 新属性值
         对象名['新方法名'] = 新匿名函数


3. 删
    (1)delete 对象名.属性名
         delete 对象名.方法名
            delete person.age
            delete person.sing

    (2)delete 对象名[属性名]
         delete 对象名[方法名]
            delete person['sex']
            delete person['my-name']
            delete person['play ball']

4.遍历对象(for-in)

遍历对象   for in循环(k 是获得对象的属性名,对象名[k] 是获得属性值)
        for (let k in obj) {
            console.log(k)         // 打印属性名
            console.log(obj[k])    // 打印属性值   注意:k不能加引号,因为k是动态的
            console.log(obj['k'])  // 加引号找的是对象obj里属性名为k的值,结果为undefined
        }

for-in也可遍历数组(k代表索引值)

  • 数组 里面可以放任何的数据类型;
  • 数组名[索引值 i ]                    获取数组里的第i个元素;
  • 数组名[索引值 i ].属性名        获取数组里的第i个对象元素里的属性;
let arr = [{uname: '三剑客',age: 18,sex: '男'}, 
           {uname: '傅斯年',age: 20,sex: '女'},
           {uname: '妲己',age: 34,sex: '男'}]
for (let i = 0; i < arr.length; i++) {
    console.log(arr[0])          获取数组里的第一个元素
    console.log(arr[0].uname)    获取数组里的第一个对象元素里的属性
    console.log(arr[i])
    console.log(arr[i].uname)
}

5.内置对象(Math)

Math对象的方法作用举例
random生成0-1之间的随机数 (包含0不包括1)

取 [n-m] 之间随机整数

Math.random() * (max - min + 1)) + min

ceil向上取整(不会四舍五入)

Math.ceil(1.1) → 2

Math.ceil(1.5) → 2

floor向下取整(不会四舍五入)

Math.floor(1.1) → 1

Math.floor(1.5) → 1

round就近取整,四舍五入(.5往大取整)

Math.round(1.1) → 1

Math.round(1.5) → 2

Math.round(1.9) → 2

Math.round(-1.1) → -1

Math.round(-1.5) → -1

Math.round(-1.9) → -2

max找最大数传入的是多个数字,不是数组(利用剩余函数)

let arr = [1, 5, 9, 45]

console.log( Math.max(...arr) )

min找最小数Math.min(1, 5, 9, 45)
pow幂运算

Math.pow(2,3) 8 (2的3次方)

abs绝对值
PI

圆周率   π

随机点名

// 封装随机数函数
function getRandom(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min
}
// 声明一个数组
let arr = ['赵云', '黄忠', '关羽', '张飞', '马超', '刘备', '曹操', 'pink老师']
// 生成1个随机数 作为数组的索引号
let random = getRandom(0, arr.length - 1)
document.write(arr[random])

随机颜色

(1)随机RGB颜色

// 封装随机RGB颜色函数
function getColor() {
    let r = Math.floor(Math.random() * 256)
    let g = Math.floor(Math.random() * 256)
    let b = Math.floor(Math.random() * 256)
    // 返回值的2种形式
    // 1. 直接返回rgb内的格式,若返回数组会比较麻烦    rgb(12,12,12)
    return `${r},${g},${b}`
    // 2. 返回数组
    // return [r,g,b]
}
// 调用函数
let color = getColor()
document.write(`<div class="rgb-color" style='background:rgb(${color})'></div>`)
// document.write(`<div class="rgb-color" style='background:rgb(${color[0]},${color[1]},${color[2]})'></div>`)

(2)随机16进制颜色

// 封装随机16进制颜色函数
function getColor() {
    // 将 0-9,a-f (可数、有索引号方便操作)放入到数组中    注意:a-f要用引号
    let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'b', 'c', 'd', 'e', 'f']
    let str = '#'
    // 循环6次(因为#后是6位数),筛选出6个随机数    #ffffff
    for (let i = 0; i < 6; i++) {
        // index表为索引值,索引值0-15   取[0,15]随机整数
        let index = Math.floor(Math.random() * (arr.length))  // 15+1 = 16 = arr.length
        // #与[0,15]随机整数拼接
        str += arr[index]
    }
    // 返回拼接后的字符串
    return str
}
// 调用并输出函数
let color = getColor()
console.log(color)

(3)传参返回随机RGB颜色,不传参则返回随机16进制颜色

function getColor(flag) {
    if (flag) {
        // 返回rgb颜色
    }else {
        // 返回16进制颜色
    }
}
let flag=prompt('传实参或不传实参')
getColor(flag)
/* getColor(true)
   getColor() */

(4)RGB颜色和16进制颜色之间的相互转换

猜数字游戏

// 获取随机数
let target = Math.floor(Math.random() * 10) + 1
// 确定循环次数用for循环,不确定次数用while循环
while (true) {
    let num = +prompt('请猜1~10之间的数字:')
    if (num > target) {
        alert('猜大了')
    } else if (num < target) {
        alert('猜小了')
    } else {
        alert('正确!')
        break
    }
}

WEB APIs

一、Web API 基本认知

  • JavaScript包括ECMAScript(规定语法,规范,数据类型)、webAPIs
  • Web APIs 的作用:使用 JS 去操作 html 和浏览器。Web APIs是操作 html 和浏览器的接口(方法)
  • Web APIs 包括:DOM (Document Object Model—文档对象模型,即把文档当作对象来处理,对象里有属性和方法)、BOM(浏览器对象模型)
  • DOM是开发网页内容特效和实现用户交互;BOM是操纵浏览器的
  • DOM 树(文档树):可将 HTML 文档以树状结构直观的表现出来;直观的体现了标签与标签之间的关系
  • document 是 DOM 里提供的一个对象,所以它提供的属性和方法都是用来访问和操作网页内容的(所以主要学document的方法)。网页所有内容都在document里面
  • 节点(dom对象):元素节点(对应HTML中的标签元素)、属性节点(对应HTML中的属性)、文本节点(对应HTML的标签中的内容)、注释节点

二、获取、操作DOM对象

1、获取DOM对象

语法作用注意
获取DOM对象document.querySelector('选择器') 选中匹配的第一个元素,获取单个元素

选择器要加引号,不加为变量;

如果获取不到会返回null

document.querySelectorAll('选择器')

选中匹配的多个元素,获取伪数组

(要经过遍历给伪元素的项进行修改)

其它获取DOM对象的方法

document.getElementById('ID名')

document.getElementsByTagName('标签名')

伪数组

document.getElementsByClassName('类名')

伪数组

document.documentElement

获取html标签
document.body获取body标签
id名可放在前面id名.document.querySelector('选择器')选中id名中符合选择器要求的第一个元素
id名.document.querySelectorAll('选择器')选中id名中符合选择器要求的多个元素
选择器 不支持伪元素选择器,支持部分伪类选择器
  • querySelector() 方法能直接操作修改(a.style.color = 'red');querySelectorAll() 方法不能直接修改(a[2].style.color = 'red')
  • 伪数组:有长度有索引号,但是没有 pop() push() 等数组方法的数组;

2、操作元素

(1)设置/修改DOM元素内容

语法区别注意
对象.innerText = '修改的内容'

只识别文本,所有内容原样输出

不加=则为获取修改的内容,加=则为赋值(修改内容),不能修改表单标签

对象.innerHTML = '修改的内容'

可以解析标签

document.write()

只能将文本内容追加到 前面的位置;

文本中包含的标签会被解析

随机点名抽奖
<body>
	抽中的是:<span>名字</span>

	<script>
		// 1. 定义1个数组
		let arr = ['张飞', '关羽', '张辽', '许褚', '吕布', '貂蝉']

		// 2. 产生随机的索引数
		// let i = Math.floor(Math.random()*(arr.length - 1 - 0 + 1)) + 0
		let i = Math.floor(Math.random() * arr.length)

		// 3. 获取元素
		let span = document.querySelector('span')

		// 4. 将元素更改为随机的索引数对应的数组元素
		span.innerText = arr[i]
	</script>
</body>

(2)设置/修改DOM元素属性

样式优缺点用于
修改元素固有属性元素.属性名 = '新属性值'

href、title、src

pic.src='路径'

修改元素样式属性元素.style.属性名 = '新属性值'复合属性名用小驼峰(去掉-横线,第二个单词首字母大写)

a.style.color = 'red'

用类名修改元素

(用于修改很多元素样式时)

元素.className='类名'

因为class在JS中是关键字

弊端:会覆盖原来的类名,适用于1个类名的操作

元素.classList.add='类名' 

元素.classList.remove='类名' 

元素.classList.toggle='类名' 

元素.classList.contains='类名' 

添加类名/

删除类名/

切换类名(有就删除,没有就添加)/

是否包含类名

(适用于多个类名,不影响以前类名)

修改表单元素属性 

元素.属性名 = '新属性值'

(和固有元素修改相同)

disabled   

禁用(只用于表单)

                              checkbox   

单、多选默认选中

                              selected     

下拉列表默认选中

                              readonly     只读

input.type = 'text'

inp.value = 'abc'

// disabled 禁用

   inp.disabled = true          //禁用

   inp.disabled = false        //解除禁用

   console.log(inp.disabled)    //判断是否禁用

// checked  默认选项

        inp.checked = true

        inp.checked = false

        inp.checked

随机刷图片

当我们刷新页面,页面中的图片随机显示不同的图片
<body>

	<img src="images/1.webp">

	<script type="text/javascript">
		// 1. 获取随机数(观察图片命名规律,实际开发中,后端会把图片封装成数组)
		// let random=Math.floor(Math.random()*(6-1+1))+1
		let random = Math.floor(Math.random() * 6) + 1

		// 2. 获取:元素
		let img = document.querySelector('img')

		// 3. 修改元素的属性
		img.src = 'images/' + random + '.webp'
	</script>
</body>

3、定时器

两种定时器对比:

  • setInterval 的特征是重复执行,首次执行会延时;setTimeout 的特征是延时执行,只执行1次;
  • setTimeout 结合递归函数,能模拟 setInterval 重复执行;
  • 可以不配套使用;

(1)间歇函数

定时器(间歇函数、间歇定时器、重复性定时器)可实现 每隔一段时间需要自动执行一段代码(即,每隔一段时间调用一次函数)

1. 开启定时器     注意:函数名后不能加括号,时间是以毫秒为单位   1s=1000ms
   setInterval(函数名,时间)  	  
2. 关闭定时器     注意:必须要有定时器名字才可以清除定时器(在开启定时器时,默认返回定时器标识)
   clearInterval(定时器标识)

倒计时间案例 

<body>

	<textarea cols="60"
		rows="10">用户注册协议
    欢迎注册成为京东用户!在您注册过程中,您需要完成我们的注册流程并通过点击同意的形式在线签署以下协议,请您务必仔细阅读、充分理解协议中的条款内容后再点击同意(尤其是以粗体或下划线标识的条款,因为这些条款可能会明确您应履行的义务或对您的权利有所限制)。
    【请您注意】如果您不同意以下协议全部或任何条款约定,请您停止注册。您停止注册后将仅可以浏览我们的商品信息但无法享受我们的产品或服务。如您按照注册流程提示填写信息,阅读并点击同意上述协议且完成全部注册流程后,即表示您已充分阅读、理解并接受协议的全部内容,并表明您同意我们可以依据协议内容来处理您的个人信息,并同意我们将您的订单信息共享给为完成此订单所必须的第三方合作方(详情查看</textarea>
	<br>
	<input type="button" value="已阅读用户协议(5)" id="btn" disabled>


	<script type="text/javascript">
		 需求:开启定时器,每1S让数字减1,当数字减到0时,不再禁用元素

		// 定义一个变量表示时间
		let i = 5
		// 获取元素
		let inp = document.querySelector('input')

		// 2. 函数封装
		function getTime() {
			// 每调用1次,i就减1
			i--
			// 修改value,使能在页面中看见倒数
			inp.value = `已阅读用户协议(${i})`

			// 判断条件结束定时器(使定时器到0时,不再继续倒数)
			if (i === 0) {
				// 结束定时器
				clearInterval(time)
				// 解除禁用
				inp.disabled = false
				// 修改value值,不出现(0)
				inp.value = '已阅读用户协议'
			}
		}

		// 1. 开启定时器,并把标识赋给变量time
		let time = setInterval(getTime, 1000)
	</script>
</body>

轮播图案例

<body>
  <div class="img-box">
    <img id="pic" src="images/b01.jpg" alt="第1张图的描述信息">
    <div class="tip">
      <h3 id="text">第1张图的描述信息</h3>
    </div>
  </div>

  <script type="text/javascript">
    // 获取元素
    let pic = document.querySelector('#pic')
    let text = document.querySelector('h3')
    // 定义变量,存储第几张图片
    let i = 1

    function getTime() {
      i++
      // 修改src和h3的内容
      pic.src = `images/b0${i}.jpg`
      text.innerText = `第${i}张图的描述信息`
      // 回到第1张图,达到轮播效果
      if (i === 9) {
        i = 0
      }
    }
    // 开启定时器
    let time = setInterval(getTime, 1000)
  </script>
</body>

 (2)延时函数

延时函数只执行一次。

1.开启延时函数   使用规范与间歇函数相同
    setTimeout(函数,延迟的时间)
2.关闭延时函数
    clearTimeout(定时器标识)

延迟函数配合递归可模拟实现间歇定时器

递归函数:函数内部调用其自身

<body>
	<div>时间</div>
	<script type="text/javascript">

		let div = document.querySelector('div')
		function time() {
			div.innerHTML = new Date().toLocaleString()
			setTimeout(time,1000)   // 执行到此时,调用time函数,一直循环,就有了1S调用1次函数的效果
		}
		time()

	</script>
</body>

三、事件

事件:用户的行为、动作(点击,双击)

1.事件

(1)事件监听

事件监听:一旦事件源被事件触发,就立即调用一个函数做出响应。

事件源.addEventListener('事件类型',函数)

事件监听的三要素:

  • 事件在哪个物体上发生(被事件触发的物体),这个物体就是事件源;
  • 事件: 用什么方式触发,比如鼠标单击 click、鼠标经过 mouseover 等;
  • 事件调用的函数: 事件源被触发后要做什么事;
  • 举例:
    <body>
        <input type="button" value="按钮">
    
        <script>
            let btn=document.querySelector('input')
            btn.addEventListener('click',function () {
                console.log('点击触发按钮,调用函数')
            })
        </script>
    </body>

(2)事件类型

事件分类书写触发条件

鼠标事件

(鼠标触发)

click单击
dblclick双击
mouseenter鼠标经过
mouseleave鼠标离开
mousemove鼠标移动
mouseup/down

焦点事件

(表单获得/失去光标)

focus获得光标用于表单元素
blur失去光标

文本事件

(表单输入时触发)

input

用户输入时触发

如,textarea

change当用户更改<input><select><textarea> 元素的值并提交这个更改时,change事件在这些元素上触发。和 input 事件不一样,change事件并不是每次元素的value改变时都会触发。

键盘事件

(按键触发)

keydown

键盘按下

(所有按键都会触发)

keyup键盘抬起
keypress

键盘按下

输出按键(除了ctrl/shift/alt 操作按键)会触发 

自动触发事件      事件源.事件类型()

(3)事件绑定的2种写法

  • 事件源.on事件 = function() { }          (有的浏览器可能不支持)

  • DOM L2 事件源.addEventListener(事件类型, 事件处理函数)

区别:

(1) L0

同类型的事件,后面注册的事件会覆盖前面的;

无法开启事件捕获;

移除事件        事件源.on事件类型=null

(2) L2

同类型的事件,后面注册的事件不会覆盖前面的;

可开启事件捕获,语法:事件源.addEventListener(事件类型, 事件处理函数, 是否使用捕获);

移除事件        事件源.removeEventListener(事件类型, 事件处理函数名称)

2、事件对象(e)

定义:事件对象是事件触发时产生的对象,事件对象中存了事件触发时的相关信息.

(1)获取事件对象

  • 事件处理函数的第一个形参接受的就是事件对象;
  • 一般命名为event、ev、e;
  • 元素.addEventListener('click',function (e) { })

(1)事件对象的属性

作用事件对象的属性名说明
获取当前的事件类型type
获取鼠标位置clientX/clientY 获取光标相对于浏览器可见窗口左上角的位置
pageX/pageY获取光标相对于文档(document)左上角的位置
offsetX/offsetY获取光标相对于当前DOM元素(事件源)左上角的位置
键盘key获取按下的是哪个键,如:A
keyCode键码值,转换成二进制的数字

ctrlKey

shiftKey

altKey

判断按下是否是ctrl/shift/alt

获取目标元素

(即触发事件的元素)

target获得真正触发事件的元素

图片跟随鼠标案例

需求:一张图片一直跟着鼠标移动
<body>
	<img src="images/tianshi.gif">

	<script type="text/javascript">
		// 1. 获取元素
		let img = document.querySelector('img')
		// 2. 事件监听
		document.addEventListener('mousemove', function (e) {
			// 获取鼠标的位置
			let x = e.clientX
			let y = e.clientY
			// 设置图片的left/top值,减50(目前不会获取元素的宽高)使鼠标在图片的中心,注意要带单位
			img.style.left = x - 50 + 'px'
			img.style.top = y - 50 + 'px'
		})
	</script>
</body>

3、事件流

事件流:事件触发后的流程

事件流的两个阶段:捕获阶段、冒泡阶段

(1)事件捕获和事件冒泡

事件冒泡:

  • 当一个元素触发事件后,会依次向上调用所有父级元素(直到document)的同名事件;
  • 事件冒泡是默认存在的;
  • 判断某事件是否支持冒泡:console.log(e)的结果中 bubbles:false 则不支持冒泡。

事件捕获:

  • addEventListener第三个参数传入true代表触发捕获阶段,false代表关闭事件捕获;
  • 是从document依次向下执行到目标元素;
  • 捕获阶段是 从父到子 冒泡阶段是从子到父;
  • 事件捕获和事件冒泡不能同时开启,但是都是存在的。
<body>
	<div><p>段落</p></div>

	<script type="text/javascript">
		let p = document.querySelector('p');
		let div = document.querySelector('div')

		// 事件捕获,开启捕获——事件监听的第三个函数为true
		p.addEventListener('click', function () {
			console.log('p',e.target);
		}, true);
		div.addEventListener('click', function () {
			console.log('div',e.target);
		}, true);
		document.addEventListener('click', function () {
			console.log('document',e.target);
		}, true);
            点击p标签的结果:document    <p>段落</p>
                            div         <p>段落</p>
                            p           <p>段落</p>

		// 事件冒泡
		p.addEventListener('click', function () {
			console.log('p',e.target);
		});
		div.addEventListener('click', function () {
			console.log('div',e.target);
		});
		document.addEventListener('click', function () {
			console.log('document',e.target);
		});
            点击p标签的结果:p           <p>段落</p>
                            div         <p>段落</p>
                            document    <p>段落</p>
	</script>
</body>

(2)阻止事件流动

  • 给影响其它元素的元素添加;
  • 此方法可以阻断事件流动传播,不光在冒泡阶段有效,捕获阶段也有效;
事件对象.stopPropagation()
鼠标经过事件的两种写法:

mouseenter 和 mouseleave 没有冒泡效果

mouseover 和 mouseout 会有冒泡效果

(3)阻止默认行为

如,链接点击不跳转,表单域的不提交

事件对象.preventDefault()

4、事件委托

  • 优点:简化代码,提高性能;并且新创建的节点也会有事件
  • 原理:事件委托其实是利用事件冒泡的特点, 给父元素添加事件,子元素可以触发(点击子元素,经过冒泡,父元素执行事件)
  • 实现:事件对象.target 可以获得真正触发事件的元素
  • 事件对象.target        获取目标元素(即触发事件的元素)

步骤:

  • 1. 把子孙元素的事件注册给上级元素;
  • 2. 利用事件对象.target找到最先触发的元素;
  • 3. 利用nodeName查看节点是否是需要的节点;
  • <body>
    	<ul>
    		li*10{$}
    	</ul>
    
    	<script type="text/javascript">
    	    // 事件委托:把子孙元素的事件注册给上级元素
    		// 1、把事件给上级元素注册
    		let ul = document.querySelector('ul');
    
    		ul.addEventListener('click', function ( e ) {
    			// 2、利用事件对象.target找到最先触发的元素
    			// console.log( e.target.innerHTML );
    			// 3、如果e.target是我们要找的元素,在执行
    			if ( e.target.nodeName === 'LI' ) {
    				console.log( e.target.innerHTML );
    			}
    		});
    		
    		// 动态创建的元素也会有事件的
    		let li = document.createElement('li');
    		li.innerHTML = '新的li';
    		ul.appendChild(li);
    	</script>
    </body>

5、案例

(1)输入文字,实时显示输入的字数

1. 获取元素
    let area = document.getElementById('area')            // 文本框
    let useCount = document.querySelector('.useCount')    // 包含数字的span

2. 监听事件
    area.addEventListener('input',function(){
      // 将useCount的内容改为 用户输入的字符串的长度(字符串也可以遍历)
      useCount.innerHTML=area.value.length
    })

(2)全选

用户点击全选,则下面复选框全部选择,取消全选则全部取消,文字对应变化

(3)购物车加减操作

 

<body>
  <div>
    <input type="text" id="total" value="1" readonly>
    <input type="button" value="+" id="add">
    <input type="button" value="-" id="reduce" disabled>
  </div>

  <script>
    // 获取元素
    let text = document.getElementById('total')
    let add = document.getElementById('add')
    let reduce = document.getElementById('reduce')
    let i = 1

    // 监听事件
    add.addEventListener('click', function () {
      i++
      text.value = i
      reduce.disabled = false
    })
    reduce.addEventListener('click', function () {
      i--
      text.value = i
      if (text.value <= 1) {
        reduce.disabled = true
      }
    })
  </script>

四、节点

1、查找节点

查找...写法注意
父节点子节点.parentNode

返回最近一级的父节点 找不到返回为null

注意:子节点.parentNode.parentNode     爷爷(可以连点)

子节点父节点.childNodes获取所有的子节点(伪数组),包括文本节点(空格、换行)、注释节点等
父节点.children获取所有的元素子节点(伪数组)
父节点.firstElementChild

第一个 / 最后一个 元素子节点

父节点.lastElementChild
父节点.firstChild 

第一个 / 最后一个 子节点(可能是 #text)

父节点.lastChild 

属性:

(1)nodeType     节点类型

 如,div.childNodes[0].nodeType     是标签节点返回1,是属性节点返回2,是文本节点返回3

(2)nodeName     节点名称

 如,div.childNodes[0].nodeName     是文本节点返回#text,是标签节点返回大写标签名

兄弟节点

节点.nextElementSibling

后 / 前 一个元素节点

节点.previousElementSibling 
节点.nextSibling后 / 前 一个节点 (可能是 #text)
节点.previousSibling
需求:关闭多个二维码
思路:获取×号并遍历,关闭父元素
<body>
  <div class="erweima">
    <span class="close"></span>
  </div>
  <div class="erweima">
    <span class="close"></span>
  </div>
  <div class="erweima">
    <span class="close"></span>
  </div>
  <div class="erweima">
    <span class="close"></span>
  </div>
  <div class="erweima">
    <span class="close"></span>
  </div>

  <script>
    let spans = document.querySelectorAll('.close')

// 在遍历里加监听事件,并且伪数组添加监听事件时要用[]
    for (let i = 0; i < spans.length; i++) {
      spans[i].addEventListener('click', function () {
        this.parentNode.style.display = 'none'
      })
    }
  </script>
</body>

2、创建及追加/插入节点

创建新节点
     let 变量 = document.createElement('新节点的标签名')

1. 追加节点—在父元素内部的最后创建节点
    父元素.appendChild(新节点)

2. 插入节点—在父元素内部的旧节点的前边创建节点
    父元素.insertBefore(新节点,旧节点)

(2)移动节点

已存在的节点追加/插入到别处,可实现该节点的移动。

需求:将img移到第2个div中
<body>
	<input type="button" value="点击">
	<div>
		<img src="07.jpg">
	</div>
	<div></div>

	<script type="text/javascript">
		// 1. 获取元素
		let btn = document.querySelector('input')
		let two = document.querySelector('div:nth-child(3)')
		let img = document.querySelector('img')
		console.log(two)

		// 2. 添加点击事件
		btn.addEventListener('click', function () {
			two.appendChild(img)
		})
	</script>
</body>
向ul里添加li
<script>
        // 要填入页面准备的数据
        let data=[{对象1},{对象2},{对象3},{对象4},{对象5}]
        // 获取变量
        let ul = document.getElementsByClassName('clearfix')
        // let ul = document.querySelector('.clearfix')

        // 遍历数组 (数组里有几个元素就创建几个li)
        for (let i = 0; i < data.length; i++) {
            // 创建新节点
            let newLi = document.createElement('li')
            // 追加节点
            // ul.appendChild(newLi)
            ul[0].appendChild(newLi)
            // 向新节点中添加内容
            newLi.innerHTML = `
                    <img src="${data[i].src}" alt="">
                    <h4>
                        ${data[i].title}
                    </h4>
                    <div class="info">
                        <span>高级</span> • <span>${data[i].num}</span>人在学习
                    </div>`
        }
</script>

(3)克隆节点

要克隆的节点.cloneNode(布尔值)
  •  若布尔值为true时,则代表克隆时会包含后代节点一起克隆(即,连里边的内容一起克隆);
  • 若布尔值为false,则代表克隆时不包含后代节点(即,只克隆该节点);
  • 默认为false

3、删除节点

要删除元素必须通过父元素删除

父元素.removeChild(子元素)

img.parentNode.removeChild(img)    // 删除img的父元素里的img,即删除它自身

回流(重排)和重绘

  • 过程:布局—渲染—回流/重排—重绘;
  • 边框/字符大小/边距/宽高/激活css伪类/添加或者删除可见的节点 等其它影响元素位置和布局的属性 都会造成回流;
  • 重绘不一定引起回流,而回流一定会引起重绘;

五、时间对象(内置对象 Date)

1、创建时间日期对象

创建时间日期对象(实例化对象),是一种数据类型,可赋值给变量
    new Date();                     // 当前时间日期
	new Date('1999-9-9 6:6:6');     // 指定时间日期(字符串,用引号引起来)
	new Date(1999, 9, 9, 6, 6, 6);  // 指定时间日期(数字)
    new Date().toLocaleString();    // 将时间输出格式改为本地时间格式,小括号内不加东西

2、时间对象方法

方法作用说明
getFullYear()获得年份获取四位年份
getMonth()获得月份取值为 0(1月) ~ 11(12月)
getDate()获取月份中的每一天不同月份取值也不相同
getDay()获取星期取值为 0 ~ 6,0代表星期日
getHours()获取小时取值为 0 ~ 23
getMinutes()获取分钟取值为 0 ~ 59
getSeconds()获取秒取值为 0 ~ 59

可以用set对象来设置具体日期,如,setMonth(6)    注意:set无返回值,不能直接赋值给变量;星期无set方法。

实时钟表案例

<body>
	<!-- 需求:将当前时间以:YYYY-MM-DD HH:mm 形式显示在页面 -->
	<!-- 思路:将时间对象获取的时间存到变量中,将变量带入带页面中,并使用定时器实现每秒一变的效果 -->

	<div>时间</div>

	<script type="text/javascript">
		// 1. 获取元素
		let div = document.querySelector('div')
		// 4. 因为定时器1秒后才执行,所以刷新页面时有1秒空白,所以此语句用来弥补那1秒的空白
		showTime()
		// 3. 定时器   每1秒自动调用showTime函数,使钟表每一秒能自动变化
		let timer = setInterval(showTime, 1000)
		// 2. 封装成函数
		function showTime() {
			// 获取时间
			let year = new Date().getFullYear()
			let month = new Date().getMonth() + 1
			let date = new Date().getDate()
			let hour = new Date().getHours()
			let minute = new Date().getMinutes()
			let second = new Date().getSeconds()
			// 补0
			month = month < 10 ? '0' + month : month
			date = date < 10 ? '0' + date : date
			hour = hour < 10 ? '0' + hour : hour
			minute = minute < 10 ? '0' + minute : minute
			second = second < 10 ? '0' + second : second
			// 修改页面标签内容
			div.innerHTML = `${year}-${month}-${date} ${hour}:${minute}:${second}`
		}
	</script>
</body>

3、时间戳

时间戳是指1970年01月01日00时00分00秒起至现在的毫秒数,它是一种特殊的精确的计量时间的方式。

获取1970年到当前时间或指定时间的毫秒数:
1. getTime()方法      可以返回指定时间的时间戳
       console.log(new Date().getTime());
		console.log(new Date('2000-10-10').getTime());    // 字符串要加引号

2. +new Date()        可以返回指定时间的时间戳
		console.log(+new Date());
		console.log(+new Date('2000-10-10'));

3. Date.now()
		console.log(Date.now());   // 只能获取当前时间

下班倒计时案例

<body>
  <div class="countdown">
    <p class="next">今天是2021年8月28日</p>
    <p class="title">下班倒计时</p>
    <p class="clock">
      <span id="hour">00</span>
      <i>:</i>
      <span id="minutes">25</span>
      <i>:</i>
      <span id="scond">20</span>
    </p>
    <p class="tips">
      现在是18:30:00
    </p>
  </div>

  <script type="text/javascript">
     倒计时
     思路:结束时间戳 - 当前时间戳=差值, 然后将差值转换成时分秒, 放入页面的3个span中

    // 获取变量
    let hour = document.getElementById('hour')
    let minutes = document.getElementById('minutes')
    let scond = document.getElementById('scond')

    // 定时器
    let timer = setInterval(showTime, 1000)
    // 弥补前1秒的空白
    showTime()
    // 封装函数
    function showTime() {
      // 创建时间
      let t1 = new Date()
      let t2 = new Date('2022-2-15 20:22:00')
      // 计算现在时间和目标时间之间的毫秒数
      let t = t2.getTime() - t1.getTime()

      // 将毫秒数换算成时分秒
      let h = parseInt(t / 1000 / 60 / 60)
      let m = parseInt(t / 1000 / 60 % 60)
      let s = parseInt(t / 1000 % 60)
      // 补0
      h = h < 10 ? '0' + h : h
      m = m < 10 ? '0' + m : m
      s = s < 10 ? '0' + s : s

      // 修改页面标签的倒计时
      hour.innerHTML = h
      minutes.innerHTML = m
      scond.innerHTML = s

      // 结束 判断(因为t可能会跳过0,所以设置为t <= 0)
      if (t <= 0) {
        clearInterval(timer)
        // 结束的同时将页面中设置成'00',否则会出现'0-'
        hour.innerHTML = '00'
        minutes.innerHTML = '00'
        scond.innerHTML = '00'
      }
    }
  </script>

4、JS处理时间插件(moment.js 和 day.js)

moment.js:Moment.js 中文网

day.js:Day.js中文网

例:moment.js

六、网页特效

1、滚动事件(scroll)

监听整个页面滚动:
window.addEventListener('scroll', function () { })
注意: 给 window 或 document 添加 scroll 事件
  • 滚动事件是当页面进行滚动时触发的事件;
  • 谁有滚动条加给谁;
  • 使用情景:很多网页需要检测用户把页面滚动到某个区域后做一些处理, 比如固定导航栏,比如返回顶部;

2、加载事件

作用:当JS写在head中,添加加载事件,不会找不到DOM元素了。

DOM加载事件比资源加载事件速度快,但有兼容性问题。

(1)资源加载事件(load)

load加载事件:当所有资源(如图片、外联CSS和JavaScript等(涉及路径就要加载))加载完毕时触发的事件.

页面加载事件:    给window添加load事件
window.addEventListener('load', function () { })

针对某个资源绑定load事件:
div.addEventListener('load', function () { })

(2)DOM 加载事件(DOMContentLoaded)

当加载完DOM元素后(不用等待样式表 、图像等完全加载),DOMContentLoaded 事件被触发。

页面加载事件:    给document添加DOMContentLoaded事件
document.addEventListener('DOMContentLoaded', function () { })

 3、尺寸改变事件(resize)

当浏览器窗口被调整到一个新的高度或宽度时,就会触发resize事件.

window.addEventListener('resize', function () {
    // 不同屏幕大小,字体大小不同
	if (document.documentElement.clientWidth>800) {
		document.documentElement.style.fontSize='20px'
	}
	else{
	document.documentElement.style.fontSize='10px'
	} 
})

4、元素大小和位置

三大家族的不同与相同点:

  • 都返回的是数字类型的值;
  • scrllTop数值随滚轮的滚动而变化;offsetTop不随滚轮的滚动而变化,是固定的;
  • offsetWidth / offsetHeight 常用来获取元素的大小;clientWidth / clientHeight 用来获取页面的大小;

(1)scroll家族

作用:回到顶部scrollTop=0

获取宽高

(包含:内容宽高+padding+溢出)

scrollWidth

scrollHeight

获取元素的内容总宽高(不包含滚动条)返回值不带单位(

只读属性,不能设置)

设置宽高的方式:div.style.width = '600px'

获取位置

scrollLeft

scrollTop

获取元素内容往左、往上滚出去看不到的距离(可设置,

div.scrollTop=1000)

当前页面被卷去的顶部距离
document.documentElement.scrollTop
仿新浪 
需求:当页面滚动500像素,就显示返回顶部按钮,否则隐藏, 同时点击按钮,则返回顶部
        // 1. 滚动500px按钮显示
        let backtop = document.querySelector('.backtop')
        window.addEventListener('scroll', function () {
            if (document.documentElement.scrollTop >= 500) {
                backtop.style.display = 'block'
            }
            else { backtop.style.display = 'none' }
        })

        // 2. 点击按钮回到顶部
        backtop.addEventListener('click', function () {
            document.documentElement.scrollTop = 0
        })

(2)offset家族

获取宽高

(包含:内容宽度+padding+border)

offsetWidth

offsetHeight

只读属性,不可修改
获取位置

offsetLeft

offsetTop

获取元素距离自己有定位的上级元素,如无定位上级时参照文档的左、上距离;

只读属性,不可修改;

需求:当页面滚动到秒杀模块,导航栏自动滑入,否则滑出
思路:滚动条滚动的时候,判断scrollTop是否大于等于模块到文档顶部的距离
<body>
    <div class="header">我是顶部导航栏</div>
    <div class="content">
        <div class="sk">秒杀模块</div>
    </div>
    <div class="backtop">
        <img src="./images/close2.png" alt="">
        <a href="javascript:;"></a>
    </div>
    <script>
        // 获取元素:
        let header = document.querySelector('.header');
        let sk = document.querySelector('.sk');

        // 监听事件
        window.addEventListener('scroll', function () {
            // 如果卷除去的距离,大于或者等于,sk的offsetTop的距离
            if ( document.documentElement.scrollTop >= sk.offsetTop ) {
                header.style.top = 0;
            } else {
                header.style.top = '-80px';
            }
        });
    </script>
</body>
电梯导航案例
需求:点击可以页面调到指定模块效果
<body>
    <div class="aside">
        <div class="item active">男装/女装</div>
        <div class="item">儿童服装/游乐园</div>
        <div class="item">电子产品</div>
        <div class="item">电影/美食</div>
    </div>
    <div class="content">
        <div class="neirong content1">男装/女装</div>
        <div class="neirong content2">儿童服装/游乐园</div>
        <div class="neirong content3">电子产品</div>
        <div class="neirong content4">电影/美食</div>
    </div>

    <script>
        let items = document.querySelectorAll('.item')
        let neirongs = document.querySelectorAll('.neirong')

        // 伪数组遍历
        for (let i = 0; i < items.length; i++) {
            // 导航点击事件
            items[i].addEventListener('click', function () {
                // 类名操作:先找到带有active类名的元素,并将该元素的类名删除(排他思想:用for也可以,但麻烦);然后将类名赋予给点击的元素
                document.querySelector('.active').classList.remove('active')
                /* for (let i = 0; i < items.length; i++) {
                    items[i].classList.remove('active')
                } */
                this.classList.add('active')
                // 文档的scrollTop = 元素到文档的上边距离
                document.documentElement.scrollTop = neirongs[i].offsetTop
            })
        }
    </script>
</body>

(3)client家族

获取宽高

(包含:内容+padding)

clientWidth

clientHeight

只读属性,不可修改
获取位置

clientLeft

clientTop

获取元素的左边框和上边框宽度;

只读属性,不可修改

七、BOM(操作浏览器)

1、Window对象

(1)BOM(浏览器对象模型)

  • window 是浏览器内置中的全局对象、顶级对象;
  • window的所有属性和方法都不用加window,因window是最大的,默认都是window的;
  • window 对象下包含了 navigator、location、document(是实现 DOM 的基础)、history、screen 5个属性;

(2)JS执行机制(API-06-1.3)

同步:前一个任务结束后再执行后一个任务(从上到下执行,如,for / while 循环;

异步:(异步处理机制)异步任务没有一定顺序,谁先触发谁先处理;

一般异步任务都有回调函数,如:

  • 普通事件,如 click、resize 等
  • 资源加载,如 load、error 等
  • 定时器,包括 setInterval、setTimeout 等

等同步(for循环while)执行完才执行异步任务

由于主线程不断的重复获得任务、执行任务、再获取任务、再执行,所以这种机制被称为事件循环;

过程叙述:同步任务被放到执行栈里先被执行,异步任务被放到异步处理机制中,当某一异步任务被触发时,将该异步任务推到任务队列中,然后被执行栈调取到执行栈中执行;

(3)navigator对象

navigator

浏览器自身的相关信息

navigator.userAgent

检测浏览器的版本及平台;

userAgent是navigator的属性

(4)location对象(15-22)

http://www.baidu.com/index.html?user=123&pwd=456#abc

location的属性/方法作用
location保存了 URL 地址

location.href

location.href='网址'

获取完整的url地址

( 协议https: 或file本地协议 + 域名/主机IP地址 + 端口 + 路径名 + 参数数据 + hash数据 )

设置url,实现网页跳转(有后退按钮)

location.search获取地址中携带的参数,符号 ?后面部分
location.hash获取地址中的哈希值,符号 # 后面部分
location.reload()reload 方法用来刷新当前页面,传入参数 true 时表示强制刷新

location.assign('网址')

加载指定的url,会产生历史记录(前进/后退箭头可用)

location.replace('网址')

替换指定的url,不会产生历史记录

(5)histroy对象

history.length

只读属性

返回当前任务中的history个数,包含当前页面在内

history.back()

回到前一个页面,等同于上一页按钮

history.forward()

转到下一个页面,等同于下一页按钮

history.go(参数)

history.go(-1)

回到前一个页面,等同于调用 back()

history.go(1)

转到下一个页面,等同于调用了 forward()

(6)screen对象

screen.width屏幕的宽度(单位:像素)
screen.height屏幕的高度(单位:像素)

screen.availWidth

除去任务栏屏幕的宽度(单位:像素)

screen.availHeight

除去任务栏屏幕的高度(单位:像素)

2、swiper 插件

(1)官网:Swiper中文网-轮播图幻灯片js插件,H5页面前端开发

         swiper使用方法:Swiper使用方法 - Swiper中文网

(2)使用方法

下载Swiper(下载Swiper - Swiper中文网) 并放到根目录下 → 分别将package中的 swiper-bundle.min.css 和 swiper-bundle.min.js 移动到根目录下的css和js文件夹中 → 在html文件中引入这两个文件 → 找到要使用的案例,在新窗口打开,右击'查看网页源代码' → 将源代码中的css / html / js 复制到自己的代码中 → 进行修改(可以新添类名;需要什么效果在 API文档 中找即可)

注意:多个swiper同时使用的时候, 类名需要注意区分;

3、本地存储(2种)

localStorage

特点:

  • 生命周期永久生效,除非手动删除 否则关闭页面也会存在;
  • 可以多窗口(页面)共享(同一浏览器可以共享);
  • 以键值对的形式存储使用;
  • 在浏览器中怎么查看?      Application → Local Storage(本地存储) → file://

(1)存储数据

localStorage.setItem('键名', '值')
注意:键名相同会覆盖,存起来变成字符串类型

(2)获取数据

localStorage.getItem('键名');
注意:如果取不存在的数据就是null

(3)删除数据

localStorage.removeItem('键名');

(4)JSON对象的本地存储

1. 将复杂类型转成JSON字符串
    localStorage.setItem('键名', JSON.stringify(存储复杂类型的变量))

2. 将JSON字符串转成复杂类型(数组/对象)
    JSON.parse(字符串)

sessionStorage(会话保存)

特点:

  • 生命周期为关闭浏览器窗口;
  • 在同一个窗口(页面)下数据可以共享;
  • 以键值对的形式存储使用;

cookie

有效期,保存大小只有4kb

4、自定义属性

对象.getAttribute('属性名')获取自定义属性
对象.setAttribute('属性名', '属性值')

设置 / 添加自定义属性值

(有这个属性就是更改属性值;没有这个属性就是添加属性)

对象.removeAttribute('属性名')删除自定义属性,无返回值
能操作普通属性(index),也能操作H5属性(data-×××)
H5        只能操作data- 开头的自定义属性
对象.dataset.×××获取 data-××× 的属性值
对象.dataset.××× = 值

设置 / 添加自定义属性值

(有这个属性就是更改属性值;没有这个属性就是添加属性)

八、正则表达式

正则表达式是用于匹配、查找字符串中的字符组合的模式。

作用:表单验证(匹配)、过滤敏感词(替换)、字符串中提取关键词(提取)

正则表达式查询工具
菜鸟工具:正则表达式在线测试 | 菜鸟工具
jQuery正则表达式

 正则测试工具:在线正则表达式测试

1、正则语法

(1)创建正则

方式1:
    let 变量名 = /内容/

方式2:
    let 变量名 = new RegExp(/内容/)

(2)判断是否有符合正则的字符串(test)

test() 方法 判断是否包含符合正则的字符串

(布尔值)

正则变量名.test('被检测的字符串')
/正则内容/.test('被检测的字符串')

(3)查找符合规则的字符串(exec)

exec() 方法 查找符合正则的字符串(数组、null)

正则变量名.exec('被检测的字符串')
/正则内容/.exec('被检测的字符串')

2、元字符(特殊字符)

元字符分类

符号含义举例
边界符^以…为开头

^abc

$以…为结尾

abc$

^abc$

^与$同时使用,具有精确匹配的含义

指的就是 字符串abc

量词符*出现零次或更多次(>=0)

与$相同,写在字符后面;

/^[a-zA-Z0-9-_]{6,16}$/

逗号左右两侧不要打空格

+出现一次或更多次(>=1)
出现零次或一次(0||1)
{n}出现n次
{n,}出现n次或更多次(>=n)
{n,m}出现n次到n次  [n,m]
字符类字符集合[ ]

多选一

[男|女]

不表示男或女,表示'男''|''女'

[a-zA-Z] 表示大小写

[^a-z] 匹配除了小写字母以外的字符

[^a-zA-Z0-9_-]

[ ] 里面加上 - 连字符,表示范围
[ ] 里的 ^ 为取反
其它字符.(点)除换行符(\n)之外的任何单个字符
( )一组

^(.|\n)$

任意单字符,|表示或

预定义类\d[0-9]

日期格式:

^\d{4}-\d{1,2}-\d{1,2}

\D[^0-9]
\w[a-zA-Z0-9_]
\W[^a-zA-Z0-9_]
\s[\t\r\n\v\f],空格(包括换行符、制表符、空格符等)
\S[^\t\r\n\v\f]

3、修饰符(i/g)

i忽略大小写,ignore
g全局匹配,global
m多行匹配
/表达式/修饰符          写在正则表达式的后面

替换(replace)

字符串.replace('旧','新')     // 旧 可直接写需替换的文字,也可用正则表达式来表示
字符串.replace(/正则表达式/,'需替换的文本')
str.replace(/c/ig, '&&')     // 把字符串str里全部的c和C替换成&&

九、面向对象

对象和对象之间的相互配合。

1.字面量创建对象

字面量创建对象(一眼就可看出是对象)
let obj = {
    属性名:属性值   键值对   成员
    uname : 'SHY',
    age : 22,
    eat : funtion () {console.log('eat')}
}

访问属性:

        对象.属性:访问固定属性

        对象['属性']:动态属性

访问方法:

        对象.方法()

函数如果没有写return、或者函数写了return后面不写值,都是返回undefined

遍历对象

        for-in中为什么用[k]:k是变量,要用动态访问

        for-in也可以遍历数组

let arr = ['red', 'blue', 'yellow', 'pink'];
for ( let k in arr ) {
	console.log( arr[k] );
}

2.构造函数

构造函数就是函数,要和new一起使用

(1)内置的构造函数

大写

new Object();   创建对象 ,对象中的成员是无序的

// 创建对象
let obj = new Object();

// 添加属性:
obj.uname = '阿飞';
obj.age = 22

// 添加方法:
obj.fei = function () {
	console.log('方法');
}

new Date();   创建日期

new Array();  创建数组

(2)自定义构造函数

function Person (uname, age, sex) {
	// 设置对象公共成员:
    // 设置对象的属性
	this.uname = uname;  // 在构造函数中,this指向当前实例化对象
	this.age = age;
	this.sex = sex;
    // 设置对象的方法
	this.eat = function () {
		console.log('eat');
	}
	this.say = function () {
		console.log('shuohua');
	}
}

// 实例化对象   new一次创建1个对象
let obj = new Person('阿飞', 22, '男');
console.log( obj );

let o = new Person('带土', 21, '女');
console.log(o);

3.

instanceof:是否是某个构造函数的实例;

constructor:用于指回构造函数的

对象 instanceof 构造函数       返回值为 true、false
适用于数组,对象
对象 instanceof Object
对象 instanceof Array

4.

实例成员:给实例对象添加的成员,只能由实例对象使用

静态成员:构造函数身上添加的成员,只能由构造函数使用

九、解构赋值

解构赋值是一种快速为变量赋值的简洁语法 ,分为数组解构、对象解构两大类型。

1、数组结构

中心思想:一一对应; 变量用[]包起来

        let arr = ['张飞', '关羽', '赵云', '马超', '林冲', '吕布']
1. 正常模式(变量数=数组的长度)
        let [a, b, c, d, e, f] = arr          // 变量对应的值:张飞 关羽 赵云 马超 林冲 吕布
2. 变量数<数组的长度
        let [a, b, c, d] = arr                // 变量对应的值:张飞 关羽 赵云 马超
3. 变量数>数组的长度 (多余的变量被赋值为 undefined )
        let [a, b, c, d, e, f, g, h] = arr    // 变量对应的值:张飞 关羽 赵云 马超 林冲 吕布 undefined undefined
4. 剩余值 (剩余值只能置于最末位)
        let [a, b, ...c] = arr                // 变量对应的值:张飞 关羽 ['赵云', '马超', '林冲', '吕布']
5. 按需取值(不需要取的数组元素用 逗号占位 即可)
        let [, b, , , e] = arr                // 变量对应的值:关羽 林冲
6. 多维数组解构 
        let [, b, [c, , e,]] = ['张飞', '关羽', ['赵云', '马超', '林冲', '吕布']]    // 变量对应的值:关羽 赵云 林冲
多维数组:
                                 arr[2][1]       arr[2][3]
let arr = ['张飞', '关羽', ['赵云', '马超', '林冲', '吕布']]
           arr[0] arr[1] arr[2][0]        arr[2][2]        // 对应的索引值

2、对象解构

  • 中心思想:把属性名当作变量名直接取值即可;
  • 变量用{ }包起来;
  • 如果在外层已经有跟属性名相同的变量名,可使用:改名;
  • 如果不存在这个属性,那这个变量就是undefined;
let uname = 'SHY'
let { uname: myname, sex, index = 666, dog: { uname: dogname, sex: dogsex } } = {
    uname: '秀儿',
    age: 18,
    sex: '男',
    dog: {
        uname: '大黄',
        age: 2,
        sex: '公',
     }
}
console.log(myname, sex, index, dogname,dogsex)  
    // 变量对应的值为:秀儿 男 666 大黄 公

ES6 创建对象

let uname = '小张';
let age = 18;
// 创建函数
let obj = {
            uname,      // uname:uname
            age,        // 属性名:变量名  可以直接简写
            index: 8,   // 不引用变量的照常写
            score: 100,
            eat() { console.log('123') },    // eat : function(){ }
            go() { console.log('gogogo') }   // 方法简写:方法名(){ }
}

九、排他思想

tab栏切换

    // 1. 获取元素   tabs是tab导航条,mains是每个导航条对应的内容
    let tabs = document.querySelectorAll('.tab li')
    let mains = document.querySelectorAll('.products .main')

    // 2. 遍历伪元素
    for (let i = 0; i < tabs.length; i++) {
      // 3. 在遍历里添加监听事件
      tabs[i].addEventListener('click', function () {
        // 5. 排它思想   在步骤4之前,先把带有类名的清除(再遍历一遍)
        for (let i = 0; i < tabs.length; i++) {
          // 注意:伪元素进行修改/设置要加[]
          tabs[i].classList.remove('active')
          mains[i].classList.remove('active')
        }

        /* // 4-2. 排他思想   让之前带有类名的div隐藏
           // 因为i和j是一一对应的,所以可以写在步骤5中
        for (let j = 0; j < mains.length; j++) {
          mains[j].classList.remove('active')
        } */
        // 4-1. 点击tab选项时,给tabs/mains添加类名
        // tabs[i].classList.add('active')
        this.classList.add('active')
        mains[i].classList.add('active')
      })