原js笔记

  • Post author:
  • Post category:其他




s原生JS笔记

电话:17339853876(微信同号)



二阶段学习内容



1、B/S架构(web结构)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4LYi0Arv-1641803458638)(img1628929434621.png)]



2、前端工程师的工作

  • 人机

    交互

  • 前后端

    交互



3、HTML、CSS、JS的作用和关系

一个网页由三部分组成:HTML、CSS、Javascript

  • HTML: 结构(静态)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jcgduT0k-1641803458641)(img1628929543099.png)]

  • CSS : 表现(静态,有”动画”)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QYw5VF4O-1641803458641)(img1628929550052.png)]

  • javascript:行为(交互)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s481JdLj-1641803458645)(img1628929558088.png)]



4、二阶段学习的内容

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mraWz6sB-1641803458645)(img1628928753710.png)]



第一天、javascript的基本语法



本节课的目标:

1、明白js在web开发中的位置(B/S架构图)

2、明白js在web前端中的作用(html是结构,css是表现,js是行为)

3、Js代码的基本格式和标签

4、Js的变量,数据类型,运算符(算术,关系,逻辑),表达式,运算过程中的隐式转换和显式转换,十六进制和八进制,转义字符



一、 JS简介,JS和H5的关系



1)、简介:

​ JavaScript一种在浏览器中解释运行的脚本语言,它的解释器被称为JavaScript引擎,为浏览器的一部分,

是广泛用于客户端的脚本语言,最早是在HTML网页上使用,

用来给HTML(HTML5)网页增加动态功能

​ 如:表单验证、漂浮的广告、客户端的显示、网页游戏、地图搜索、股市信息查询、web聊天。。。



2)、历史:

​ 1994年:网景公司(Netscape)发布了Navigator浏览器0.9版,只能浏览网页,没法交互。后来,才出现了全新的语言:liveScript。

​ liveScript ==> javaScript => ECMAScript(规范)

​ 1999年:ES3,JavaScript 的第三版

​ 2019年:出现了 ES5

​ 2010年:出现了 ES6(里程碑的版本),以后,每年增加一次版本。



3)、组成部分

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BBhdOSCx-1641803458646)(img1628929675141.png)]


ECMAScript:

​ 是一种由Ecma国际(前身为欧洲计算机制造商协会),定立ECMA-262标准化的脚本程序设计语言。规定了 JavaScript 脚本的核心语法,如 数据类型、关键字、保留字、运算符、对象和语句等。


BOM:

​ 定义了 JavaScript 操作浏览器的接口,提供了访问某些功能(如浏览器窗口大小、版本信息、浏览历史记录等)的途径以及操作方法。


DOM:

​ 定义了 JavaScript 操作 HTML 文档的接口,提供了访问 HTML 文档(如body、form、div、textarea等)的途径以及操作方法。



二、 编写及运行JS



1、编写

在网页里写JS代码的位置有三种:


1)、行内式

​ 和CSS的行内是一样的,但是JS的行内写法真的很少用,几乎不用。


2)、内部式

​ 和css写在一对style标签一样,JS是写在一对script标签里

<script>    
       alert("我用来弹出消息框!") ;
</script>

注:

1、script标签,可以写在HTML中任意位置,可出现多个。但建议写在最后(body标签的末尾)

2、JS大小写敏感

3、每句话后面写上分号,虽然不是必须的,但是建议写上,可以提高性能


3)、外部式

​ 和CSS的外部引入文件是同样的道理

​ 新建JS文件,如:demo01.js

​ 引入:

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

注意:

1、引入js文件时,使用script标签的双标写法,

2、不要在script标签中间写代码,如果要写,另起一对script标签



2、注释

​ 单行注释

​ 多行注释 /*

/

​ 文档注释 /

* */



3、运行顺序

​ Javascript和HTML代码在同一个文件中写,它们的执行顺序是从上朝下,谁在前先执行谁。



4、编辑工具和运行环境

编辑工具:写代码的工具

如:dreamweaver,editplus,Notepad++,sublime,vscode,hbuild,webstrom

运行环境:看结果的地方

如:IE,firefox,chrome



三、语法:变量




1、概念:

​ 变量用来在计算机中存储和表示数据。是会根据程序的执行而变化的量(数学中学过自变量和因变量)




2、定义(声明)




var

age; var 是关键字,age是变量名




3、赋值


​ age

=

20; 20是数据,”=”是赋值




4、定义的同时赋值




var

age

=

20;




5、使用变量


​ alert(myname);

​ age = age+1;

注:

1、变量必须先赋值再使用

2、可以一次定义多个变量:

var myname=“刘德华”, age=18,weight=138;




6、变量的命名规则及关键字:


1)、变量命名规则


变量名由数字、字母以及下划线组成,但不能以数字开头

不要使用关键字及保留字


2)、变量命名规范:


尽量使用有意义的单词作为变量名(望文知义),有驼峰命名法和西班牙命名法(以小写字母b,f,i,s开头表示类型,后接大写开头的有意义的单词)等等,JS官方使用的是驼峰命名法。


3)、关键字及保留字



关键字:

​ break、do、instanceof、typeof、case、else、new、

var

、catch、finally、return、void、continue、for、switch、while、debugger、function、this、with、default、if、throw、delete、in、try、



保留字:

​ abstract、enum、int、short、boolean、

export

、interface、

static

、byte、

extends

、long、

super

、char、final、Native、synchronized、

class

、float、package、throws、

const

、goto、private、transient、

debugger、implements、protected、volatile、double、import、public、



四、语法:JS的数据类型




1、概念:



数据:


​ 在计算机的世界里,数据是指所有能输入到计算机并被计算机程序处理的符号的介质的总称,是表示和记录事物的。如:数字,字母,符号、文字、语音、图像、视频等



数据类型


​ 在计算机中对数据进行了分类,不同类型的数据有不同的运算。




2、js的数据类型


基本类型:


  • Number类型(数字)

      包含整数± 9007199254740992 和浮点数(小数)±1.7976931348623157 × 10的308次方。
    

  • String类型(字符串)

    ​ 用双(单)引号括起来的一串字符。如:“hello”,“12”,‘hi’


  • Boolean类型(布尔)

      布尔有两种取值true和false,表示真和假。非0代表真,0代表假。
    
  • Undefined类型(未定义)

    Undefined类型只有一个值undefined,它是变量未被赋值时的值。

  • Null类型(空)

    Null类型也只有一个值null 。Null类型的语义是”一个空的对象引用”,注意和空字符串区别开。


引用类型

  • ​ Object类型(对象)

    ​ JavaScript中最为复杂的类型就是Object,它是一系列属性的无序集合。



3、如何判断变量的类型

​ typeof

运算符

​ 结果是字符串。

​ var age=20;

​ alert(typeof age);

​ alert(typeof typeof age);



4、javascript是弱类型的语言


1)变量声明时不需要指明类型


var age;

age=20;


2)变量的类型在使用中可变


age=“年龄:”+age;

注:JS变量的类型由其存放的数据类型确定



五、语法:运算符

计算机编程语言的三大运算符:算术,关系,逻辑。




1、算术运算符

+:加法
-:减法
*:乘法
/:除法
%:取余,求余数 (保留整数)

由算术运算符组成的式子叫

算术表达式

,结果是

数值类型




2、关系运算符

> :大于
>= :大于等于
<  :小于
<= :小于等于
== :等于
!= :不等于
=== :恒等
!== :不等于   

由关系运算符组成的式子叫

关系表达式

,关系表达式返回的结果是

布尔类型




=的区别:

=== :恒等,如果两个数据的类型不一样,肯定不等.

== :等同,如果两个数据的类型不相同,但是值一样(还有很多内涵)

​ 会做类型的转换(隐式转换)




3、逻辑运算符

&& : 与,运算规则: 一假则假, 全真为真(两个为真就是真).

|| :或,运算规则: 一真则真, 全假为假(两个为假就是假).

! :非,运算规则: 真是假,假是真. 

逻辑短路:

当逻辑运算符前面的值能够决定整个表达式的结果.那么,逻辑运算符后面的式子不再进行运算




4、赋值运算符

=  : 把赋值号右边的值赋给左边

复合赋值运算符:
+= :加并且赋值,如:x += y  等价于: x = x+y;
-= : 减并且赋值,如:x -= y  等价于: x = x-y;
*=
/=
%=



5、自增自减

++:自加一,对变量自加1
      如: 
        var x =1;  
        x++;等价于:x = x+1;
        console.log(x);2

--:自减一,对变量自减1
        var x =1;  
        x--;等价于:x = x-1;
        console.log(x);0      




运算符的优先级:

运算符 描述
. [] () 字段访问、数组下标、函数调用以及表达式分组
++ – – ~ ! delete new typeof void 一元运算符、返回数据类型、对象创建、未定义值
* / % 乘法、除法、取模
+ – + 加法、减法、字符串连接
< <= > >= instanceof 小于、小于等于、大于、大于等于、instanceof
== != === !== 等于、不等于、严格相等、非严格相等
&& 逻辑与
|| 逻辑或
?: 条件
= oP= 赋值、运算赋值
, 多重求值



6、字符串连接符

+:在字符串的操作中,表示连接字符串

var str="java";
str = str + "script" ;
结果是	javascript

var str1="苹果单价:";
var str1=str1+5;	把数值类型与字符串类型连接,结果会得到字符串类型
结果是	苹果单价:5



六、语法:类型转换



1、隐式(自动)转换



概念:

​ 不同的数据类型参与运算时,JS会先把数据类型转成一致的,然后进行运算,这叫作隐式(自动)转换。这个转换不需要程序员参与。



转换规则

​ 1)、字符串与数值类型:

  • 字符串加数字,数字就会转成字符串。

    var str2 = "hello "
    str2 = str2 + 2; 先把数字2 转成 字符串 "2",然后再进行拼接(这是隐式转换)
    console.log(str2);
    
  • 数字与字符串进行 减(乘,除,大于,小于)运算时,字符串转成数字。两个字符串也会转成数字。

    2)、其它的类型的隐式转换先不用着急,后续可以看这篇文章:

​ https:blog.csdn.net/jiang7701037/article/details/86538545



2、显式(手动)转换



1)、概念:

​ 程序员按需进行手动转换,需要程序员自己进行转换。



2)、其它类型转数字类型:


​ parseInt():把其它类型转成整型

​ parseFloat():把其它类型转成浮点型(小数)

​ Number():把其它类型转成数字型



转换规则:

​ 1) parseInt 和parseFloat 会按顺序一个个转字符串中的字符,直到碰到转不成数字的字符为止,如果第一个字符就转不成数字将返回NaN。

​ 2) parseInt 认为小数点不能转, parseFloat 会转换遇到的第一个小数点。

​ 3)Number对整个字符串进行转换,根据有没有包含一个小数点来确定转换为整数还是浮点,有任意字符不能转成数字时将返回NaN。

 var num1 = "20";
 var num2 = 5;
 document.write(Number(num1)+num2);

面试题:请问parseInt(),parseFloat(),Number()的区别?

https:blog.csdn.net/jiang7701037/article/details/80739820

注:NaN表示”不是数字”,但是仍是数值类型, not a number,NaN是Number类型。



3)、其它类型转字符串:

  • ​ 变量.toString():有一些数据类型不能使用 toString() 方法,比如 undefined 和 null

  • ​ String(变量):所有数据类型都可以

var num = 10;
var str =  num.toString();
document.write(typeof str);string



4)、其它转布尔

  Boolean(变量)。

​ 在 js 中,只有 ’ ‘、0、null、undefined、NaN,这些是 false,其余都是 true



七、十六进制和八进制

进制:又叫进位制,逢几进一。



1、八进制:



概念:

​ 逢八进一,有效数字是:0,1,2,3,4,5,6,7



表示

​ JS中八进制的是以”0″(零)打头的数字; 如: 012,06,025等;

​ var num1=012;



2、十六进制:



概念:

​ 逢十六进一,有效数字是:0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F 其中A-F表示10-15;



表示

​ JS中十六进制的数以”0x”打头,如0xA, 0X1B

​ var num2=0x1B;



3、进制转换

parseInt可以把2-36之间的进制数转换成十进制:

​ 如:console.log(parseInt(“101”,2))把101当作二进制,

​ 如:console.log(parseInt(“FF”,16))把”FF”当作十六进制,



八、扩展案例:



需知:

  • 如何点击按钮调用js的代码

    (第一周周五会讲):
<body>
  <input type="button" value="点我" onclick="testf()" />
</body>
<script> 

function testf() {
      
  alert("hi");
      
}
</script>

  • 如何在js中获取文本框的内容

    (第二周会讲):

document: 文档,表示当前网页

document.getElementById(“num1”): 就是id为num1的标签(如:文本框)

document.getElementById(“num1”).value: 就是文本框的值。




1、交换两个数

​ 步骤:

​ 1,添加两个文本框和一个按钮,分别设置id值为text1、text2、btn1、

​ 2,编写函数function test1(){},给按钮添加属性οnclick=“test1()”

​ 3,在函数test1中编写交换文本框内容的代码

​ a)使用document.getElementById().value;分别取得两个文本框的内容,赋值给两个变量num1,num2

​ b)定义临时变量temp1存储num1的值

​ c)将num2的值赋给num1

​ d)将temp1的值赋给num2

​ e)使用document.getElementById().value=num1将交换后的值显示在文本框中



2、在文本框输入两个数,完成运算



九、转义字符



1、概念:

​ 在任何计算机语言中,

1)、总有一些字符代表特殊意义,如果要使用这些字符本身时,需要用到转义字符。

2)、 有些字符没有用键盘直接表示,也得用转义。



2、HTML中的转义字符

在html中 ,”<” 表示标签的开始符号, “>” 表示标签的结束符号,

表示段落

如要在页面上显示

,则这样写:

&lt;p&gt;

在html中,&copy; 就是 版权符号



3、JS中的转义字符

转义字符的格式: 以 反斜杠 开头,后面跟字符本身。

js中常见的转义字符:

" 表示双引号;
' 表示单引号
/  表示 反斜杠。
  表示 反斜杠。
n 就是回车。



十、作业:

1,课堂案例敲两遍

2,判断类型(要区分出整形和浮点型)

3,为抵抗洪水,战士连续作战89小时,编程计算共多少天零多少小时?

4,小明要到美国旅游,可是那里的温度是以华氏度为单位记录的。它需要一个程序将华氏温度(80度)转换为摄氏度,并以华氏度和摄氏度为单位分别显示该温度。

​ 提示:摄氏度与华氏度的转换公式为:摄氏度 = 5/9.0*(华氏度-32)

5,给定一个数,把它保留3位小数,百度搜索:toFixed()。

6,var k=0; alert(k++ + ++k +k +k++); 结果是?

7、0.2+0.3==0.5 的结果是true还是false

8,扩展:使用文本框和按钮实现加减乘除、求余五种运算;

​ 要求: 1)使用parseInt方法类型转换。

​ 2)计算结果使用Math.round方法四舍五入

​ 注:文本框取到的value值是字符串类型,需要转成数字



第二天、逻辑分支语句



本节课的目标:

1 程序运行的三大结构(逻辑):顺序,选择,循环

2 if的单分支,双分支,多分支,嵌套

3 switch结构,break的作用,case穿透

4 三元运算符(表达式)



一、程序逻辑的三大结构

程序逻辑的三大结构:顺序,选择,循环;

  • 顺序结构:从上朝下执行的代码就是顺序
  • 选择(分支)结构:根据不同的情况,执行对应代码
  • 循环结构:重复做一件事情

除顺序结构外,其余两种程序结构由流程控制语句实现。

选择(分支)结构的程序由条件分支语句实现。



二、条件分支语句if



1、单分支

if(条件){
    语句   
}

示例:偶数(单分支)



2、双分支

if(条件){
    条件满足时执行的语句
}eles{
    条件不满足时执行的语句
}

示例:

1)、闰年(双分支)

2)、逢七过的数(双分支)



3、多分支

if(条件1){
    条件1满足时执行的语句
}eles if(条件2){
    条件1不满足,条件2满足时执行的语句
}else{
    条件1,条件2都不满足时执行的语句
}

示例:

1)、计算输入的y值并输出到文本框。

​ x (x<1)

​ y = 2x+1 (1≤x<10)

​ 5x-17 (x≥10)

2)、多分支(BMI,也可以使用输入石头剪刀布来判断输赢)



4、if嵌套

if(条件1){    
    条件1满足时执行的语句
}eles{
    if(条件2){
        条件1不满足,条件2满足时执行的语句
    }else{
        条件1,条件2都不满足时执行的语句
    }
}

示例:

1)、计算输入的y值并输出到文本框。

​ x (x<1)

​ y = 2x+1 (1≤x<10)

​ 5x-17 (x≥10)

示例:

输入成绩判定,输出到文本框

如果你的成绩在85—100之间,显示A

如果你的成绩在70—84之间,显示B

如果你的成绩在60—69之间,显示C

如果你的成绩小于60,显示D



三、switch结构

语法:
	switch(表达式){
		case 表达式1:分支语句一;break;
		case 表达式2:分支语句二;break;
		case 表达式3:分支语句三;break;case 表达式n:分支语句n;break;
		default:默认分支语句;break;
	}

执行过程:
   1switch表达式的值和case表达式的值进行比较,两值相等就执行case对应的分支语句。
   2、分支语句可以有任意多个,如果没有任何case表达式的值与switch表达式值相等就执行default的默认分支语句。
   3、因为执行完分支语句后不会自动退出switch语句,会继续执行后续的分支语句,这叫做switch(case)的穿透,为避免穿透,需要在每条分支语句后添加break,跳出switch语句。

示例:

1)、输入数字显示星期几

2)、switch五分制(成绩等级)



四、三元运算符

三元运算符,又叫三目运算符。


普及概念:

  • ​ 操作数:运算符数,数据

  • ​ 运算符:+ –

  • ​ 表达式:由操作数和运算符构成的式子就是表达式。

  • ​ 单目运算符:此运算符只需要一个操作数,如:! ++ –

  • ​ 双目运算符:此运算符需要两个操作数,如:+ – && ||

  • ​ 三目运算符:此运算符需要三个操作数,如:?:

  • ​ 一元表达式:由单目运算符和一个操作数组成式子。如: i++;

  • ​ 两元表达式:由双目运算符和二个操作数组成式子。如: a+b;

  • ​ 三元表达式:由三目运算符和三个操作数组成式子。如: a>b?a:b;

语法格式:

表达式1?表达式2:表达式3

1、根据表达式1执行的结果,来决定执行表达式2还是表达式32、表达式1结果是true执行表达式2,最终返回表达式2的结果。
3、表达式1结果是false执行表达式3,最终返回表达式3的结果。

 var num1=3,num2=5,y="";
 y=num1>num2?"第一个大":"第二个大";
 alert(y);



五、规范:

1,所有的括号成对输入

2,所有的引号成对输入

3,一句话占一行(不要多句话写在一行)

4,最后的花括号单独占一行,花括号所在的行的里面不要写代码(左花括号的右边,和右花括号的左边不要写代码)

5,缩进对齐:

a) 同级对齐

b) 子一级比上一级缩进4个空格



六、条件分支的注意点:

1,if分支中即使一句话也要把{}写上

2,if嵌套不要超过三层

3,Switch语句里把default分支一定要写上,防止在以后的维护中产生歧义

4,把执行概率更大(正常情况)放在前面(if,case)

5,if else 和 switch:

if else 一般来表示两个分支或者嵌套比较少的分支,如果分支多的话,用switch。



七、作业:

1,课堂案例敲一遍

2,输入一个整数,判断大于0小于0还是等于0

3,国内BMI:开发一款软件,根据公式(身高-108)*2=体重,可以有10斤左右的浮动。来观察测试者体重是否合适

提示:输入身高算体重,然后判断算出的体重和输入的体重差值在正负10斤以内,可使用Math.abs(),

4,输入一个数判断是奇数还是偶数,并输出结果

5,输入一个年份判断是否是闰年

6,输入成绩转成成绩等级(A,B,C,D,E),使用if和switch两种写法完成

7,输入年和月份显示当月天数

8,输入数字显示星期几

9,根据会员积分,计算折扣大小(积分10000以上,打5折;积分8000-10000之间,打6折;积分6000-8000打7折;积分6000以下打8折)

10,加减乘除计算器

11、国际BMI

扩展:

输入年,月,日,求出该日期是当前年的第几天

个人所得税的计算器



第三天、循环语句



本节课的目标:

  1. 循环的概念
  2. while循环语句
  3. do while 循环语句
  4. for循环语句
  5. break和continue
  6. 死循环和循环嵌套



一、循环的概念:

1、循环就是重复执行一段代码,是一种最能发挥计算机优势的程序结构。

2、循环结构的代码由

循环体



循环变量

、和

循环条件

组成。

3、当循环变量满足循环条件时会重复执行循环体内的代码,直到循环变量不满足循环条件时就终止循环,接着执行循环结构后的代码。

4、JS中有while、do while、for三种循环。



二、while循环语句

1、语法:

while(表达式){	
	循环体;
}

当表达式成立时,会一直执行循环体的代码,直到表达式不成立。
所以,循环体中,必然有一句话是朝着表达式越来越不成立的方向发展,否则就是死循环

示例:从1加到100

要点:

1,循环变量要先初始化。

2,每次循环前判断表达式,表达式成立后执行循环体语句。

3,循环体中,应有结束循环的条件(有个代码朝着循环条件不满足的方向靠近),否则会造成死循环。

4,当不确定循环多少次时可用死循环。

5,循环体中可以写若干句合法的javaScript代码,包括if,也可以再套个循环语句。

示例:

  • 输出100以内的奇数
  • 输出100以内所有偶数的和
  • 输出100以内7的倍数



三、do while循环语句(不常用)

1、语法:

 do{

     循环体;

}while(表达式);
当表达式成立时,会一直执行循环体的代码,直到表达式不成立。

示例:从1加到100

2、while和do while的区别:

​ do while先循环一次,后判断循环条件。也就是说无论如何都会执行一次循环体。



四、for循环语句(最常用的)

1、语法:

for(表达式1;表达式2;表达式3){
   循环体;
}

表达式1初始化循环变量,
表达式2判断循环条件,
表达式3改变循环变量的值。
如:
var sum = 0;
for(var i=1;i<=100;i++){
	sum = sum + i;
}
利用三个表达式把循环控制与循环体分离,结构更加清晰。是使用最多的循环语句。

请问for循环执行完毕后,循环变量的值是多少?

示例:

​ 输出100以内的奇数

​ 输出100以内所有偶数的和

​ 输出100以内7的倍数



五、continue

1、continue只能在循环语句(循环体)中使用

2、作用是:使本次循环结束,即跳过循环体中continue下面尚未执行的语句,接着进行下次是否执行循环的判断。

示例:输出100以内的奇数,不包含3的倍数

示例:

1,求出1-1/2+1/3-1/4+1/5…1/100的和(加奇减偶)。

2,一个新入职,月工资为10K的员工,每年涨工资5%,50年后的月工资是多少?

3,山上有口缸,可以装50升水,现有15升,小和尚下山挑水,每次挑5升,几次挑满。

4,打印1000-2000年间的闰年,每行打印4个。

5,打星星

۞۞۞۞۞

۞۞۞۞۞

۞۞۞۞۞

۞۞۞۞۞

۞۞۞۞۞



六、break

1,在switch语句中使流程跳出(终止) switch结构。

2,在循环语句中使流程跳出(终止)当前循环。

注意:

1,如果已执行break语句,就不会执行循环体中位于break后的语句。

2,在多层循环中,一个break语句只向外跳一层。

示例:

判断一个数是不是合数。(能被1和自身以外的数的整除)

判断一个数是不是素数(质数)。



七、死循环

死循环:循环条件永远满足

while(true){

}

do{

}while(true);

for(;😉{

}



八、循环嵌套

1,一个循环内包含完整的另一个循环语句。

2,被包含的循环语句叫内循环,包含别的循环的循环语句叫外循环。

3,外循环每执行一次循环,内循环都会完全执行所有循环次数。

4,循环嵌套的总执行次数是外循环次数乘以内循环次数。

注:可使用浏览器的调试功能(打断点)一步步查看循环嵌套的执行

示例:

  1. 打印矩形
  2. 打印左直角三角形
  3. 打印左直角梯形
  4. 打印99乘法表



九、作业:

1,课堂案例敲一遍

2,打印100-200之间所有能被3或者7整除的数

3,求1!+2!+3!+。。。+10!

4,打印100-999之间的水仙花数(153==1

3+5

3+3^3)

5,打印100-200间所有的素数

6,打印三角形

7,打印梯形

8,一球从5米高度自由落下,每次落地后反跳回原高度的30%;经过几次弹起,篮球弹起高度小于0.1米。

9,64格棋盘,第一格放一粒芝麻,重0.00001KG,第二格放两粒芝麻,第三格放4个芝麻,求棋盘上芝麻总重。

扩展作业:

1、斐波那契数列:1,1,2,3,5,8 。。。。从第三项开始每项都等于前两项之和。

提示:

我们假设n1是第一项,n2是第二项,最初n1=1,n2=1;然后n1=1,n2=2;再然后n1=2,n2=3;其实从第二个式子起n1等于前一次的n2,n2等于前一次的n1+n2,一直都是这个规律。

2、找数字: 求所有满足条件的四位数abcd:

(1)这四位数是11的倍数;

(2)a, b, c, d均是小于10的互不相同的自然数;

(3)b + c = a;

(4)bc是完全平方数(bc=n的平方)。

3、求Sn=a+aa+aaa+……+aa……a之值,其中a、n是文本框输入的两个数字。



第四天、函数



本节课的目标:

​ 函数的概念

​ 函数语法

​ 函数的参数

​ 函数的返回值

​ 匿名函数

​ 函数的两种定义方式



一、函数的概念:

​ 1、概念:

​ 函数就是把完成特定功能的一段代码抽象出来,使之成为程序中的一个独立实体,起个名字(函数名)。

可以在同一个程序或其他程序中多次重复使用(通过函数名调用)。

​ 2、作用:

  • 使程序变得更简短而清晰
  • 有利于程序维护
  • 可以提高程序开发的效率
  • 提高了代码的重用性(复用性)



二、函数的语法格式:

1、函数的定义

function 函数名([形参列表]){
	
	函数体
    
	[return 返回值;]
    
}

普及:程序行业里,描述格式时,使用方括号,表示可选。

2、函数的调用

函数名([实参列表])

示例:无参函数的使用

  • 输出100以内的奇数
  • 输出100以内所有偶数的和
  • 输出100以内7的倍数
  • 判断一个数是不是质数
  • 判断一个年份是不是闰年
  • 根据年月计算当月天数



三、函数的参数



1、形参

​ 又叫形式参数。函数

定义

时,写在圆括号内的参数列表。和定义的局部变量是同样的意思。



2、实参

​ 又叫实际参数,函数

调用

时,写在圆括号内的数据。

示例:

  • 判断一个数是不是质数

  • 判断一个年份是不是闰年

  • 根据年月计算当月天数

  • 编写一个函数,计算两个数字的和、差、积、商

  • 编写一个函数,计算三个数字的大小,按从小到大顺序输出



四、函数的返回值



1、概念:

​ 在函数内使用return语句返回函数执行的结果,我们在调用函数时用一个变量来接收函数返回的结果。



2、return的作用:

  • ​ 返回函数的执行结果,只能返回一个值
  • ​ 结束函数的执行,(即:当执行到return后,函数将直接退出,return后如果还有代码将不再执行)

示例:

  • 判断一个数是不是质数
  • 判断一个年份是不是闰年
  • 根据年月计算当月天数
  • 编写一个函数,计算两个数字的和、差、积、商



五、函数的分类



1、函数分类(从定义函数的角度):


1)、内置函数

​ 是官方提供的函数,直接使用,如:alert()、Math.pow()等。

​ isNaN()判断是不是数字。(NaN:not a nubmer)

​ 注:通过JS帮助文档可以查阅JS所有内置函数的使用。


2)、自定义函数:


程序员自己编写的函数



六、封装自定义函数:



1、函数的三个要素(封装函数的要素)

1、功能:

2、参数:完成一件事情的已知条件。

3、返回值:结果。



2、定义函数的注意点:

1、形参,不要在函数内部重新定义

2、函数内部尽量不要出现输出语句(alert,document.write),但是console.log是可以的

3、return后面只能出现一个值(即函数只能返回一个值)

注:编写好的函数代码只有通过

调用

才会

执行

,不调用的时候不会执行。


示例:

1.编写生成n位数字验证码的函数



七、匿名函数

函数可以当成值存储在变量里,通过变量名来调用,调用时必须加圆括号。

var fun1 = function(){
    alert("我是匿名函数");
};	匿名函数声明

fun1();							匿名函数调用

var fun2= function(num1,num2){
    return num1+num2;
};	匿名函数声明
var num3=fun2(1,2);					匿名函数调用
alert(num3);



八、函数的两种定义方式:

1、声明式

function 函数名(参数){
	函数体
}

2、(赋值)表达式

var 变量名 = function(){
	函数体
}



九、作业

1,编写一个函数,计算m到n之间所能组成的奇数(奇数的每位数字不能相同)个数,m和n必须是个位数,

比如:计算0-3之间能组成的奇数个数(所有奇数:01,21,03,13,23,31)

2,某公司用电话传递数据(如:6234),需要加密,规则如下

a,每位数字都加上5然后用除以10的余数代替(如:1789)

b,将第一和第四位交换,第二和第三位交换(如:9871)

编写函数,输入原文,输出密文



第五天、函数(下)



本节课目标:

​ js编译执行

​ 变量作用域

​ 变量的声明提升(面试)

​ 事件调用函数

​ 递归



一、JS的编译和执行

​ javascript代码在运行时有(预)编译和执行两个阶段,在预编译阶段会对函数和变量进行处理,对所有的声明(未赋值)变量会赋值为undefined,变量的声明提升,对所有的声明函数也会赋值为函数的定义等等。

在执行阶段会按照代码顺序,一行行的翻译并执行代码。

​ 编辑代码(写代码):源代码———–(

预编译

)————》编译(翻译):机器代码—————》执行:看到结果。

预编译阶段会做的事情:

1、(变量,函数)声明提升

2、定义的变量没有赋值时,会默认赋值为undefined

3、优化



二、变量的作用域

变量的作用域:就是变量起作用的范围。或者说有效范围。



1、局部变量

​ 局部变量就是定义在函数内部的变量,这个变量只能在函数内部使用,即作用域范围只是函数内部,另外,形参也是局部变量。

function fn(){
    fn2();
    console.log("fn里面,fn2_a",fn2_a);不能使用fn2里的局部的变量的
}

function fn2(){
    var fn2_a = 100;局部变量,
    console.log("fn2里面,fn2_a",fn2_a);
}



2、全局变量

​ 全局变量就是定义在函数外部的变量,这个变量在任何函数中都有效,即作用域范围是当前文件的任何地方。不但在任何函数中都可以使用,而且值是

共享

。即A函数改变值后,B函数拿到的就是改后的值。就像共享单车一样。



1)、全局变量,又叫共享变量
 

var global_n = 250;

function fn(){
    global_n = 100;
    console.log("fn里:global_n",global_n);100
    fn2();
}

function fn2(){
    console.log("fn2里:global_n",global_n);100
}


2)、 当全局变量和局部变量重名时

在函数内部使用的是局部变量,这叫就近原则

var age = 250;

function fn(){
    var age = 100;
    console.log("fn里:age",age);100,就近原则。
}

什么时候使用全局变量:

1、当在若干个函数里使用同名变量g,并且,A函数中改了g的值,B函数中要使用改后的值,使用全局变量。如果,A函数中改了g的值,其它函数中并不使用,那么,根本不需要定义成全局变量。



三、变量的声明提升

​ 在预编译阶段,声明的变量,总是会被提升,提升到变量的作用域的顶端(如:函数体的顶端)。



1、声明的变量会被提升作用域范围的顶端。

    function fn(){
        
        console.log(a);
        var a ;
    }

     预编译阶段:会把以上代码中 var a ,提升到函数体的第一行。即:在编译之前,以上代码,会被处理成:

    function fn(){
        var a;
        console.log(a);
    }



2、提升的是声明,不是赋值。

function fn(){
        console.log(a);
        var a = 100;定义(声明)变量,并且赋值;        
    }

    预编译后,以上代码被改为:

    function fn(){
        var a;
        console.log(a);
        a = 100;定义(声明)变量,并且赋值;        
    }

​ 请注意,变量赋值并没有被提升,只是声明被提升了。



3)、当全局变量和局部变量重名并且碰到了声明提升

var age = 250;
function fn(){
    console.log(age);250undefinedvar age = 100;
    console.log(age);100
}

 以上代码经过预编译后,成为以下代码:

var age = 250;
function fn(){
    var age;
    console.log(age);undefined;
    age = 100;
    console.log(age);100
}



四、事件调用函数



事件:发生并得到处理的操作

​ 我们把事件和自定义函数建立起对应关系,当事件发生时就去调用我们的函数。

​ 我们已经学习了onclick事件,还有onload, onfocus、onblur等等。

​ onload:标签(页面)加载完成(页面完全打开)

​ onfocus:获得焦点(简单理解为,光标进入)

​ onblur:失去焦点(简单理解为,光标离开)



五、递归函数



1、概念:

  • JS函数可以相互调用,这叫嵌套调用。

  • JS函数也能调用自己,调用自己的函数叫做

    递归函数

    ,递归函数就是特殊的嵌套调用函数。

  • 递归,递的是参数,归的是返回值

  • 注意:递归函数中肯定有一个分支是不调用自己的

  • 示例:5的阶乘

    
     1! = 1;
     2! = 2*1!;
     3! = 3*2!
     4! = 4*3!;
     5! = 5*4!
    
     n的阶乘 = n*(n-1)的阶乘
    
    封装一个函数,求n的阶乘
     参数:n
     返回值:就是阶乘的结果
    
    function jieCheng(n){
        if(n==1 || n==0){
            return 1;
        }
    
        var result =  n*jieCheng(n-1);
    
        return result;
    }
    
  • 示例:斐波那契数列:1,1,2,3,5,8 ……从第三项开始每项都等于前两项之和


 需求:求斐波那契数列中的第n个数。
 112358 ……从第三项开始每项都等于前两项之和

 一、循环的方式做:

                n1    n2    n3
   n1    n2     n3
    112358 ……从第三项开始每项都等于前两项之和
          n1    n2    n3


 功能:求斐波那契数列中的第n个数。
 参数:n
 返回值:第n个数

function feiBoNaQi(n){
    if(n<=2){
        return 1;
    }

    var n1 = 1;
    var n2 = 1;
    for(var i=3;i<=n;i++){
        var n3 = n1+n2; 2;  3;
        n1 = n2; 新的n1
        n2 = n3; 新的n2;
    }
    return n3;
}

function fn(){
     一、输入
    var n = Number(document.getElementById("n").value);

     二、运算
    var num = feiBoNaQi(n);

     三、输出
    console.log(num);
}


 二、递归的写法:
/*
                n1    n2    n3
   n1    n2     n3
    1,   1,    2,   3,   5,  8 ……从第三项开始每项都等于前两项之和
          n1    n2    n3

 功能:求斐波那契数列中的第n个数。
 参数:n
 返回值:第n个数

function feiBoNaQi(n){3
   if(n<=2){
      return 1;
   }
   return feiBoNaQi(n-1)+feiBoNaQi(n-2);
}

function fn(){
     一、输入
    var n = Number(document.getElementById("n").value);

     二、运算
    var num = feiBoNaQi(n);

     三、输出
    console.log(num);
}

*/
  • 示例:求两个数的最大公约数

首先确定如何求最大公约数,我们采用欧几里得算法(辗转相除法),算法描述如下:

例:48 , 57

57%48=9 大数对小数求余

48%9=3 小数对上次的余数求余,重复这个过程直到余数为0

9%3=0 余数为0时,此次求余的小数就是最大公约数


 需求:求两个数的最大公约数
 功能:求两个数的最大公约数
 参数:
    第一个数:m
    第二个数:n
 返回值:m和n的最大公约数

 12  12  6    4   3   2   1
 20           4       2    1
  一、循环的方式
 function maxGongYue(m,n){
      1、求两个数的最小数
     var min = m<n?m:n;
      2、从大到小循环,第一个能够整除m和n的数就是最大公约数
     for(var i=min;i>=1;i--){
         if(m%i==0 && n%i ==0){
             return i;
         }
     }   
 }

 二、欧几里得算法
 例:48 , 57

 m    n  = y
 48 % 57 = 48


 m    n = y
 57 % 48 = 9     大数对小数求余

 48 % 9 = 3      小数对上次的余数求余,重复这个过程直到余数为0
 9 % 3 = 0	   余数为0时,此次求余的小数就是最大公约数

 function maxGongYue(m,n){57,48
     do{
       var y = m%n; y = 9   3
       m = n;  48
       n = y;  9
     }while(y>0);
     return m;
 }

 三、递归写法

 m和n的最大公约数就是 n 和 m%n 的最大公约数
 function maxGongYue(m,n){57,48
     if(n==0){
         return m;
     }
     return maxGongYue(n,m%n);
 }

function maxGongYue(n1,n2){
	var n3=n1%n2;
	if(n3==0){
		return n2;
	}
	return gys(n2,n3);
}



六、作业

1,编写函数,输入n,n为偶数时调用函数求1/2+1/4+。。。1/n

n为奇数时调用函数求1/1+1/3+。。。1/n

2,页面输入数字,点击按钮用函数求阶乘

3、把本周讲的案例,封装成函数,单独写在一个tools.js的文件里。



第六天、数组



本节课的目标:

  • ​ 数组概念及定义
  • ​ 数组的遍历
  • ​ 数组常用的官方方法(函数)
  • ​ 二维数组的了解


  • 排序(重点)


  • 基本(值)类型和引用(地址)类型的区别(重点)
  • ​ 对象的初步了解



一、数组的概念及定义



1、概念:

数组就是:一组数(据),是数据的集合。数组里可以放置任何类型的数据,包括数组、对象,函数等等。

​ 一个变量只能存储一个数据,如果我们要保存一个班级所有人员的成绩,那么,就需要定义若干个变量,这是非常夸张的一件事情,这时候我们就需要数组来存储这样的数据。



2、定义数组:



1)、构造函数的方式

第一种写法:

 var arr = new Array(7);    定义了一个7个元素的数组,没有给元素赋值

第二种写法:

 var arr = new Array(12,23,34)  定义了一个3个元素的数组,元素的值分别是12,2334
 var names = new Array("王翠霞","李彤","张亚飞");定义了一个3个元素的数组


2)、直接量
var arr = [12,23,34];	等价于: var arr = new Array(12,23,34) 





3、数组中的名词

1)、元素:

​ 数组中保存的每个数据,叫作元素。

2)、长度

​ 数组中保存的元素个数,叫作长度。使用数组的属性length

      ```js
      var arr = [12,23,34];	
      arr.length;   数组的元素个数,3.
      ```

3)、下标

  • 下标就是序号。从0开始。访问数组中的每个元素时,使用下标。下标也叫索引。下标的最大取值:length-1
  • 下标可以是变量,表达式

​ 格式: 数组名[下标]

var arr = [12,23,34];	
arr[0]; 就是数组的第一个元素,12;
arr[1]; 就是数组的第二个元素,23var i=0;
arr[i];就是arr[0]
arr[i+1];就是arr[1]



二、数组的遍历

​ 循环访问数组的每一个元素就叫做数组的遍历。



1、for循环

var arr1=[1,2,3,4,5,6];
for(var i=0;i<arr1.length;i++){
	console.log(arr1[i]);
}



2、for in 方式:

var arr1=[1,2,3,4,5,6];
for(var i in arr1){
	console.log(arr1[i]);
} 	

示例:

1、创建一个数组,给元素赋值1到100。

2、求一个班级的最高成绩(最大数)

3、求一个班级的最低成绩(最小数)

4、求一个班级的平均成绩

5、把一组数进行倒置

6、创建一个数组,从2开始给元素赋偶数值(到100结束),按顺序每5个求一次平均值放在另一个数组并输出。



三、数组的常用官方方法(函数)

学习官方方法,就是看官方文档(这个技能我们必须要掌握)

对数组的操作包括以下几类:

  • 给数组添加元素(增):push(), unshift(),splice()

  • 从数组中删除元素(删):pop(), shift(),splice()

  • 修改数组中的元素(改):splice(),reverse(),sort()

  • 从数组中查询:slice(),indexOf()

  • 不会改变元素数组的内容的函数:concat(),slice()


1、push() 方法

功能:推,向数组的

末尾添加

一个或多个元素,并返回新的长度(

尾加

参数:添加的元素(可以是一个,也可以是多个)

返回值:新的数组长度

var arr1=[1,2,3];
var len = arr1.push(7);	向数组末尾添加元素,并返回数组的新长度
alert(len);			输出4

 实际应用:
 班级里有新的学生进入
 var arr =['张三疯','张四风','张武峰'];
 有新学生要进入
arr.push("宝宝");
var t = arr.push("宝宝的宝宝");
console.log(arr);
console.log(t);


2、pop() 方法

功能:

删除

并返回数组的

最后

一个元素(

尾删

)。弹,弹出最后一个元素,和push是相反的操作。

参数:无

返回值:被删除的元素

var arr1=[1,2,3,7];
x=arr1.pop();	删除最后一个元素并返回被删除的元素
alert(x);		输出7

 实际应用:
 班级里有学生留级,删除留级的学生。


3、unshift() 方法

功能:向数组的

开头添加

一个或多个元素,并返回新的长度(

头加

),跟push()位置不同,push是从后面插入;unshift是从前面插入

参数:添加的元素(可以是一个,也可以是多个)

返回值:新的数组长度

var arr1=[1,2,3];
x=arr1.unshift(8,9);	向数组开头添加了两个元素,并返回新长度
alert(x);			输出5


4、shift() 方法

功能:方法把数组的第一个元素从其中删除,并返回第一个元素的值(

头删

参数:无

返回值:删除的元素

var arr1=[1,2,3];
x=arr1.shift();	删除第一个元素并返回被删除的元素
alert(x);		输出1


5、 splice(下标,长度,新的元素):

功能:splice函数可以完成添加元素,也可以完成删除元素,可以在指定位置添加元素,可以在指定位置删除,

参数:

​ 下标:既是添加元素的下标,也是删除元素的下标

​ 长度:删除元素的长度(你要删除几个元素)

​ 新的元素:添加的元素(可选参数)

返回值:被删除的元素。

 示例:
     1、仅仅只做添加
    /*
    var arr=[12,23,34,45,56];
    arr.splice(2,0,100);
    console.log(arr); [12,23,100,34,45,56]
    */
     2、用splice 和push对比
    /*
    var arr=[12,23,34,45,56];    
     arr.splice(5,0,100);
    arr.push(100);
    console.log(arr);
    */
    
     3、仅仅只做删除
    
    var arr=[12,23,34,45,56];    
     var t = arr.splice(2,1);
     console.log(arr);
     console.log(t);
    var t = arr.splice(2,2);
    console.log(arr);
    console.log(t);

     4、用splice模拟pop
     var arr=[12,23,34,45,56];   
     arr.splice(4,1);
     console.log(arr);
    
     5、用splice模拟shift
        下标:既是添加元素的下标,也是删除元素的下标
        长度:删除元素的长度(你要删除几个元素)
        新的元素:添加的元素
     var arr=[12,23,34,45,56];   
     arr.splice(0,1);
     console.log(arr);


6、reverse函数:

​ 功能:对数组的元素进行翻转。第一个元素和倒数第一个元素交换,第二个元素和倒数第二个元素交换,以此类推……

   如:1,2,3,4,5 翻转后:5,4,3,2,1

   如:1,2,3,4 翻转后:4,3,2,1

​ 参数:无

​ 返回值:翻转后的数组(会改变原始数组)

    var arr=[1,2,3,4];
    var t = arr.reverse();
    console.log(arr);
    console.log(t);


7、自行研究的方法:

sort()、slice()、concat()、join()



四、二维数组的了解

数组的每个元素也是个数组,这就是二维数组。

如:定义一个两行四列的数组

var arr = [
    	[1,2,3,4],
    	[5,6,7,8]
]

示例:

通过循环为一个5X5的二维数组a赋值1到25,然后输出该数组的左下半三角



五、排序算法(数组的应用)



1、冒泡法排序


思路:

​ 相邻两个数进行比较,如果前一个数大于后一个数,那么就交换,否则不交换,以此类推,一轮下来,最大的数,沉到了最下面。依次类推,n个数做n-1的循环。最终数组的数排成了从小到大的顺序。

冒泡排序的思路(数组的应用):
相邻两个数进行比较,如果前一个数大于后一个数,那么就交换,否则不交换

如:[5,3,7,1,2,6]
 一共几个数 6个数;做5次循环
/*
 第一轮,把最大数放在了最后
 i=0;
 做了几次比较:5次比较,也是循环
   j=   0           1                2         3
       
        5           3                3         3         3     3               
        3           5                5         5         5     5        
        7           7                7         1         1     1         
        1           1                1         7         2     2         
        2           2                2         2         7     6         
        6           6                6         6         6     7 
   arr[0]>arr[1]  arr[1]>arr[2]   arr[2]>arr[3]
  arr[j]>arr[j+1] arr[j]>arr[j+1]


 第二轮,把次大的数,放在了倒数第二个位置
 i=1
 做了几次比较:4次比较,也是循环
j=  0            1             2             3

	3            3             3             3    3
    5            5             1             1    1
    1            1             5             2    2
    2            2             2             5    5
    6            6             6             6    6

 第三轮,把第三大的数,放在了倒数第三个位置
 i=2
 做了几次比较:3次比较,也是循环

    3   1   1   1 
    1   3   2   2
    2   2   3   3
    5   5   5   5

*/


代码:


function testf(){
    var arr=[9,8,3,7,6];

    for(var i=0;i<arr.length-1;i++){
        for(var j=0;j<arr.length-1-i;j++){
            相邻两个数,进行比较,如果前一个大于后一个,则交换。
            if(arr[j]>arr[j+1]){
                var temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }            
        }
    }
    console.log(arr);
}



2、选择法


思路:

​ 先在所有数中,找到最小的数,放在第一个位置。再在剩下的数中找到最小的,放在第二个位置。依次类推,n个数,做n-1次循环。最终数组的数排成了从小到大的顺序。

 9 , 8, 3, 7, 6
 选择法排序:
 1)、找最小数
 2)、交换(放在最前面)

 i=0;
1)、找最小数:从下标0开始朝最后一个数,找最小数
2)、交换:最小数和 arr[i];
 min = 3;

 3,  8,  9, 7, 6

i=1
1)、找最小数:从下标1开始朝最后一个数,找最小数
2)、交换:最小数和 arr[i];

3,  6, 9,7,8 

i=2;
1)、找最小数:从下标2开始朝最后一个数,找最小数
2)、交换:最小数和 
 3, 6, 7, 9, 8

i=3;
 3, 6, 7, 8, 9


代码:


function testf(){
    var arr=[9,8,3,7,6];

    for(var i=0;i<arr.length-1;i++){
        1、找最小数(哪个位置上的数最小,就是求小数的下标)
        var min = arr[i];假定最小数
        var minIndex = i;下标,最小数的下标。
        for(var j=i+1;j<arr.length;j++){
            if(arr[j]<min){
                min = arr[j];
                minIndex = j;
            }
        }
        
        2、交换
        var temp = arr[minIndex];
        arr[minIndex] = arr[i];
        arr[i] = temp;
    }

    console.log(arr);
}



六、 基本(值)类型和引用(地址)类型的区别


普及:

​ cpu:程序逻辑和计算是靠 cpu 做的

​ 内存:数据是靠 内存 存的。即,变量的值都在内存中存储着呢。


内存分区:

​ 栈区(堆栈区): 局部变量,形参。

​ 堆区:存放的都是new出来的数据。

​ 全局/静态区: 全局变量和静态变量。

​ 只读区:分为常量和代码区。



1、内存的区别


  • 基本(值)类型:


    占据内存:

    基本类型的变量对应的内存存储的是值,只

    占用一块内存区域

    **取值:**直接取值

    包括:number,string,boolean,null,undefined


  • 引用(地址)类型:

​ 占据内存:引用类型的变量对应的内存存储的是地址,

占用两块内存区域

(一块存的是地址,一块存的是数据)。

​ **取值:**间接取值,先找地址,根据地址找数据(值)。

​ 包括:Object,Array,Date,RegExp,Function等等



2、赋值的区别



  • 基本类型:

​ 赋的是值(对应的内存内容)



  • 引用类型

​ 赋的是地址(对应的内存内容)

记住:不管是什么类型的变量,

通过变量名,获取的都是变量名对应的内存的内容

(基本类型是值,引用类型是地址)

var arr = [12,23,34]; new了一块空间   

arr和arr[0]不是同一块空间。

arr里存的是地址

arr[0]存的是值

var arr2 = [12,23,34];new了一块空间
arr == arr2 false;,因为arr和arr2所存储的内容(地址)是不同的
arr[0] ==  arr2[0];true,因为arr[0]和arr2[0]所存储的内容(值)是一样的。



3、作为函数参数的区别



函数传参相当于赋值

(把实参的值赋给形参),所以:



  • 基本类型:

​ 传的是值



  • 引用类型

​ 传的是地址

​ 优点:

​ 1)、在被调用的函数内部,可以改变主调函数里的数据

​ 2)、节约了内存。



七、对象的了解(重要但不紧急)


概念:

​ 对象是用来描述复杂的数据的,是引用类型。

​ 对象包括:官方对象和自定义对象。

​ 在javascript中可以使用JSON(JavaScript Object Notation, JS 对象标记)表示对象,就是花括号里的键值对。


定义:

var 对象名 = {
    属性名1:属性值1,
    属性名2:属性值2,
    属性名3:属性值3
    ……
}

语法要点:

1、花括号

2、键名(属性名)要有双引号

3、键值(属性名和属性值)之间是冒号

4、键值对之间用逗号分隔

5、属性值可以是任意类型的,包括数组,函数,json对象

如:

 var mimi={
 	"name":"咪咪""age": 2"color":"虎皮""hobby":""fish",
 	"action": function(){ 
        alert("喵~~");
    }
 };



八、作业、

1,随机生成5位以内的数,然后输出该数字有多少位,每位分别是什么?

2,编写函数map(arr)把数组中的每一位数字都增加30%。

3,编写函数has(arr,n)判断数组中是否存在n这个元素,返回布尔类型。

4,编写函数norepeat(arr)将数组的重复元素去掉,并返回新数组(面试题)。

5,随机点名程序

提示:把人名都放进数组,随机产生一个下标,根据下标取出一个人名

6、完成一个其它进制转换成十进制的函数(convert(‘123’,8):表示把8进制的123转成10进制)

7、完成一个十进制转换成其它进制的函数(convert(512,2) :表示把十进制的512转成2进制))

8、把写的关于数组的示例封装成函数。

9、冒泡排序

10,选择排序

11,有一个从小到大排好序的数组。现输入一个数,要求按原来的规律插入数组中。( 可用splice函数 )

12,用JSON创建对象存储学生信息,包括学号、身份证、年龄、性别、专业等信息,同时包含一个方法自我介绍,用来输出对象所有信息。

13,计算总成绩,按总成绩排名。统计各单科成绩第一名,输出其成绩与学号。

学号 语文 数学 英语 总成绩 备注

1 105 62 118

2 89 78 120

3 86 64 80

4 78 99 91

5 107.5 97 70

6 112 61 92

7 101 79 104

8 71 72 105

9 56 68 61

10 98 83 77



第七天、ES5新增的数组的方法和字符串



本节课目标:

​ ES5数组新方法

​ 字符串概念和定义

​ 字符集

​ 字符串常见API



一、 ES5数组新方法

https:developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array



1、indexOf

​ **功能:**查找元素,在数组中查找一个数出现的第一个下标

​ **参数:**要查找的元素

​ **返回值:**要查找元素的下标,如果找不到,那就是 -1

	var arr1 = [12,23,34,45,56,67,23];
	
	console.log(arr1.indexOf(23));	结果是1

	console.log(arr1.indexOf(45));	结果是3

	console.log(arr1.indexOf(100));	结果是-1



2、forEach

​ **功能:**对数组的每个元素做某个处理(函数的方式)。即:对数组的每个元素要做遍历,遍历时做的事情,是由参数(回调函数)决定。

​ **参数:**回调函数,该回调函数的参数有三个(数组元素,元素索引(可选),数组本身(可选))

​ **返回值:**无

示例:

1var arr1 = [12,23,34,45,56,67];
arr1.forEach(alert);使用官方函数alert,让数组的每个元素都alert一次

2var arr1 = [12,23,34,45,56,67];
arr1.forEach(arrInc);调用自定义函数arrInc,让数组的每个元素都加1

function arrInc(num,index,arr){
	arr[index] = num+1;	每个元素都加1
}



3、map

​ **功能:**把原始数组的每个元素进行某种处理后,产生(映射)一个新的数组。map不会改变原始数组的内容

​ **参数:**回调函数,

​ 该回调函数的参数有三个(数组元素内容,元素索引(可选),数组本身(可选))

​ 该回调函数的返回值是处理后的结果。

​ **返回值:**新的数组,经过回调函数处理过的数组

示例:

var arr1 = [12,23,34,45,56,67];
var arr2 = arr1.map(arrSqr);

function arrSqr(num){
	return num*num
}



4、filter



功能:

过滤的意思,根据

条件

过滤数组的元素,filter的回调函数需要返回的是boolean类型的值(满足条件)。

​ **参数:**回调函数,该回调函数的参数有三个(数组元素内容,元素索引(可选),数组本身(可选))

​ **返回值:**新的数组,存放”满足回调函数中条件的数组元素“

示例:

var arr1 = [-12,23,-34,45,-56,67];
var arr2 = arr1.filter(eqZero);	过滤掉所有小于等于0的数,留下大于0的数
	
function eqZero(num){
	return num>0;
}



5、every

​ **功能:**方法用于检测数组所有元素是否都符合指定条件。

​ **参数:**回调函数,该回调函数的参数有三个(数组元素内容,元素索引(可选),数组本身(可选))

​ **返回值:**true:都满足;false:不是都满足

示例:

var scores = [81, 75, 51, 60];
function gt(num) {
      return num>=60;
}
scores.every(gt); 结果是false。不是所有人都及格(大于等于60

还记得判断质数的那个需求吗?



6、some

​ **功能:**方法用于检测数组中的元素是否至少有1个元素满足指定条件(函数提供)

​ **参数:**回调函数,该回调函数的参数有三个(数组元素内容,元素索引(可选),数组本身(可选))

​ **返回值:**true:有一些满足;false:都不满足

示例:

var scores = [81, 75, 95, 86];
function gt(num) {
     return num>=90;
}
scores.some(gt); 结果是true。包含大于等于90的成绩



二、字符串的定义和创建



1、概念:

字符串就是一串字符,由双(单)引号括起来。字符串是 JavaScript 的一种基本的数据类型。



2、定义:

1)、字面量的方式:

var str=‘亲’; 定义一个字符串变量str,内容为‘亲’                     基本类型

2)、构造函数的方式:

js
var str = new String(“hello”);  定义一个字符串变量str,内容为hello,   引用类型

用new产生的变量都是引用类型的变量,也叫对象。



3、示例用法:


var s1 = "string";
var s2 = new String("string");
 
console.log(typeof(s1)); 输出的是 string 
console.log(typeof(s2)); 输出的 object



三、字符串的常用属性和函数



1、字符串的属性

​ **length:**表示字符串的长度;

​ 如 : var str=“how are you”;

​ alert(str.length);



2、字符串的函数(方法)



charAt(3)

获取下标为3的字符



charCodeAt(3)

获取下标为3的字符的编码



String.fromCharCode(94)

编码转换成字符

​ 该方法是 String 的静态方法,所以用String调用,

​ 例:String.fromCharCode(98,99);


  • 字符串的查找方法



    indexOf(“abc”)

    查找字符串第一次出现的位置, 如果没找到 返回-1


    lastIndexOf(“abc”)

    查找字符串最后一次出现的位置, 如果没找到 返回-1
var str="hello world hello JS"
str.indexOf("h");0
str.lastIndexOf("h")12

  • 跟正则相关的几个函数

    1、search(正则对象) :正则匹配 (返回索引位置,没有找到就返回-1)

    2、match () 方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。该方法类似 indexOf() 和 lastIndexOf(),但是它返回指定的值,而不是字符串的位置。将匹配的内容存入数组(后期讲正则表达式详讲)

    3、replace 替换字符串

    如: var str=“how are you”;

    document.write(str.replace(“are”,“old are”));

    这里的替换只能执行一次,不能够进行全局匹配,而且区分大小写,如果需要全局匹配,则应使用正则表达式: str.replace(/are/gi,“old are”) :表示把str中所有的are,全部替换为 old are,g表示进行全局匹配,i表示匹配的时候忽略大小写;



  • 截取字符串:


    1、slice(start,end) 提取字符串的某个部分,并以新的字符串返回被提取的部分。

    ​ 两个参数表示截取的开始下标和结束下标。

    2、substring(start,stop) 提取字符串中介于两个指定下标之间的字符,并以新的字符串返回被提取的部分。

    ​ 两个参数表示截取的开始下标和结束下标。

    区别, slice参数支持负数(从后往前算),substring不支持。



  • 字符串分割:


    1、split 根据分隔符、拆分成数组

    ​ var str=“aaa1bbb1cc1d1”;

    ​ var arr=str.split(“1”);

    2、大小写转换:

    toLowerCase、toUpperCase



四、字符编码

​ ASCII(美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。

​ GBK 共收录了21003个汉字,英文使用单字节编码,兼容ASCII编码,中文部分采用双字节编码。

​ Unicode为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。

​ UTF-8是一种针对Unicode的可变长度字符编码。用在网页上可以统一页面显示中文简体繁体及其它语言。



五、



=


===(恒等)

一、如果类型不同,就[不相等] false

二、如果类型相同,值也相同,那就相同 true

1、如果两个都是数值,并且是同一个值,那么[相等]。

2、如果两个都是字符串,每个位置的字符都一样,那么[相等];否则[不相等]

3、如果两个值都是true,或者都是false,那么[相等]。

4、如果两个值都是null,或者都是undefined,那么[相等]。

5 、如果两个值都引用同一个对象或函数,那么[相等];否则[不相等]。


==(等同)


1、如果两个值类型相同,进行 === 比较。

2、如果两个值类型不同,他们可能相等。根据下面规则进行类型转换再比较

​ a、如果一个是null、一个是undefined,那么[相等]。

​ b、如果一个是字符串,一个是数值,把字符串转换成数值再进行比较。

​ c、如果任一值是 true,把它转换成 1 再比较;如果任一值是 false,把它转换成 0 再比较。

​ d、任何其他组合,都[不相等]。



六、作业

1,敏感词过滤。(用户输入的内容中的敏感词替换为*)

例:“今天有个SB在旁边大喊大叫,影响了我的操作。”,

过滤后“今天有个

在旁边大喊大叫,影响了我的

作。”。

思路:把敏感词汇放在数组里:var arr=[‘SB’,’cab’,’nnd’]; 可以不用正则

2,密码格式要求。(必须包含字母、数字、特殊字符,长度6个字符以上)

3,留言过滤。(不能重复发言不能包含敏感词)

4,统计字符串中每个字符的个数。(原始字符串是:“aabccdeefffaaaa”,结果是:a2 b1 c2 d1 e2 f3 a4)

5,数字字母混合验证码。(例:6yF3)

6,统计字符串中每个字符的个数,并去掉连续重复的字符。(原始:“aabccdeefff”,统计后是:a2 b1 c2 d1 e2 f3,取重后是:abcdef)



第八天、Math和Date



本节课目标:

​ Math对象

​ 数学运算相关知识回顾(三角函数)

​ 日期对象

​ 日期函数

​ 使用定时器



一、Math对象的方法:

Math 对象用于执行数学任务。

Math对象的常用函数:

Math.round(3.6) 四舍五入

Math.random() 返回0-1之间的随机数

Math.max(num1, num2) 返回较大的数

Math.min(num1, num2) 返回较小的数

Math.abs(num) 绝对值

Math.ceil(19.3) 向上取整“20”

Math.floor(11.8) 向下取整“11”

Math.pow(x,y) x的y次方

Math.sqrt(num) 开平方


示例:

​ 1、随机数

任意两个整数之间的随机数=取整(小数+随机数*(大数-小数))

parseInt(1+Math.random()*10);

	function getRandomNum(min,max){
		var cha=max-min;
		var rund=Math.random()*cha;
		return min+parseInt(rund);
	}

2、十进制转16进制或8进制的函数

十进制转其他 :

var x=110;

alert(x);

alert(x.toString(8));

alert(x.toString(32));

alert(x.toString(16));



二、三角函数

Math.sin();正弦;

Math.cos();余弦;

https:developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math



三、日期对象的定义:



1、概念:

​ Date对象用于处理日期和时间,Date对象记录着从1970年1月1日00:00:00开始以来的毫秒数。



2、定义:


1)、无参

​ var myDate=new Date() ; Date 对象自动使用当前的日期和时间作为其初始值。


2)、有参:创建Date对象同时指定日期和时间:

​ new Date(“month dd,yyyy hh:mm:ss”);

​ new Date(“month dd,yyyy”);

​ new Date(yyyy,mth,dd,hh,mm,ss);

​ new Date(yyyy,mth,dd);

​ new Date(ms);



四、日期对象的官方方法:


  • 获取时间:


    getFullYear() 返回年份


    getMonth() 返回月份值 ,从0开始


    getDate() 返回天

    getHours() 返回小时数

    getMinutes() 返回分钟数

    getSeconds() 返回秒数


    getDay() 返回星期几 ,从0开始(星期天是0)

​ getTime() 返回完整的时间 ,毫秒数


  • 设置时间:

    ​ setYear() 改变年份


    setMonth() 改变月份,从0开始

    ​ setDate() 改变Date对象的日期

    setHours() 改变小时数

    setMinutes() 改变分钟数

    setSeconds() 改变秒数

    setTime() 改变完整的时间,毫秒数


  • 字符串转换时间戳:

    Date.parse(日期字符串或日期对象) 返回自1970年1月1日起至参数日期止的毫秒数

    返回值:指定日期的毫秒数

    如:

    var t = Date.parse(“8 26,2021 11:34:00”);

    console.log(t);1629948840000


  • Date转换为字符串:

    ​ toDateString() 把 Date 对象的

    日期部分

    转换为字符串。

    ​ toTimeString() 把 Date 对象的

    时间部分

    转换为字符串。

    ​ toLocaleString() 根据本地时间格式,把 Date 对象转换为字符串。

    ​ toLocaleDateString() 根据本地时间格式,把 Date 对象的日期部分转换为字符串。

    ​ toLocaleTimeString() 根据本地时间格式,把 Date 对象的时间部分转换为字符串。

    ​ toUTCString() 根据世界时,把 Date 对象转换为字符串。



五、封装日期函数

  1. 输出自己的日期格式
  2. 转换周几为汉字
  3. 计算两个日期天数差
  4. 计算两个日期的月份差 (year2-year1)*12+(month2-month1);
  5. 计算两个日期的年分差



六、定时器:



1、间隔固定时间,周期性执行


  • setInterval(回调函数,毫秒数)

功能: 设置每隔指定的毫秒数执行一次回调函数,返回定时器编号。 体现是

周期性

不停重复的完成一件事情

参数:

​ 回调函数,

​ 设定的时间

返回值:

​ 定时器的编号


  • clearInterval(定时器编号)清除定时器设置。



2、超过指定时间执行一次


  • setTimeout(回调函数,毫秒数)

​ 功能:设置在指定的毫秒数后执行一次回调函数。 体现的是

延迟

完成一件事情

​ 参数:

​ 回调函数

​ 设定的时间

​ 返回值:定时器编号


  • clearTimeout (定时器编号)

    ​ 清除定时器设置。

    注:因为只执行一次回调函数,所以为达到不停执行回调函数的效果必须在回调函数中再次设置。



七、作业:

  1. 编写函数获得随机的颜色字符串(#20cd4f);
  2. 倒计时和正计时
  3. 数码时钟



第九天、BOM和DOM



本节课目标:

​ BOM

​ onload事件与匿名函数

​ DOM

​ 表格操作



一、BOM




概念:

​ BOM是Browser Object Model的缩写,浏览器对象模型,把浏览器的组成部分在JS里叫作对象。




作用:

​ 可以用JS操作浏览器窗口的每个部分。




Window对象:

 window 对象表示浏览器中打开的窗口。

​ window对象是全局最大的对象:

​ 1)、var声明的全局变量,就是window对象的属性(window对象可以省略)

​ 2)、声明的全局函数,就是window对象的方法(window对象可以省略)




Window对象官方属性:

  • ​ document对象:文档(网页)
  • ​ history对象:历史(浏览器的浏览历史)
  • ​ Location对象:地址信息
  • ​ Screen对象:屏幕相关
  • ​ name 浏览器窗口的名字
  • ​ status 设置窗口状态栏的文本

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kRIECvG2-1641803458647)(imgimage-20210826223133558.png)]



window.Location 对象

​ 包含有关当前 URL 的信息。(了解即可)



location对象的属性



  • href属性 控制浏览器地址栏的内容(相当于在浏览器中输入网址打回车)

  • ​ hostname 设置或返回当前 URL 的主机名。


    location对象的方法

  • ​ reload()方法 刷新页面(相当于F5)

  • ​ reload(true) 刷新页面,不使用缓存 (相当于ctrl+F5)



window.history对象

history对象包含用户(在浏览器窗口中)访问过的 URL。(了解即可)


  • 属性



    length: 返回浏览器窗口历史列表中的 URL 数量。

  • 方法 :
  • back(): 加载 history 列表中的前一个 URL。 (相当于 后退按钮)
  • forward(): 加载 history 列表中的下一个 URL。 (相当于 前进按钮)
  • go():加载 history 列表中的某个具体页面,或者要求浏览器移动到指定的页面数量(负数为后退,正数为前进)(go(1)相当于forward() go(1)相当于back(-1))



window对象的方法

  • ​ alert(“”) 显示带有一段消息和一个确认按钮的警告框。

  • ​ confirm(“”) 显示带有一段消息以及确认按钮和取消按钮的对话框。

  • ​ prompt(“”) 显示可提示用户输入的对话框。

  • ​ open(“url”,”name”,”打开窗口的设置”) 打开一个新的浏览器窗口或查找一个已命名的窗口。

  • ​ setInterval() 按照指定的周期(以毫秒计)来调用函数或计算表达式。

  • ​ clearInterval() 取消由 setInterval() 设置的 timeout。

  • ​ setTimeout() 在指定的毫秒数后调用函数或计算表达式。

  • ​ clearTimeout() 取消由 setTimeout() 方法设置的 timeout。

  • ​ close() 关闭浏览器窗口



window对象的事件

  • onload:窗口内容加载完毕,触发(调用函数)

    window.onload = function(){
        document.getElementById("num").value = "hi";
    }
    
  • onscroll:窗口滚动时,触发(调用函数)

     onscroll:当窗口的页面滚动了的时候,调用函数
    window.onscroll = function(){
        console.log("滚了");
    }
    
  • onresize:窗口调整大小时,触发

     onresize:窗口调整大小时,调用函数
    window.onresize = function(){
        console.log("窗口大小发生了变化");
    }
    



二、onload事件与匿名函数

onload 事件会在页面或图像加载完成后立即发生。

​ 支持的标签:, , , , , ,

例:

1<body onload=“alert(‘页面加载完毕。');">
                      
2<body onload=“jiazaiwanbi()">

	function jiazaiwanbi(){
		alert(“页面加载完毕。”);
	}

3,window.onload=jiazaiwanbi;

4,window.onload=function(){
            alert(“页面加载完毕。”);
}



三、DOM



概念:

​ DOM(Document Object Model),文档(网页)对象模型。网页中的每个标签在JS中都是对象。



作用:

​ 可以用JS操作网页的每个标签和内容,属性。

​ DOM定义了表示和修改文档所需的对象、对象的行为、属性以及这些对象之间的关系



节点:

  • HTML的标签在JS中是对象,在DOM树(的世界里)叫节点。(在DOM(树)的世界里,

    一切皆节点

    ):

    • 整个文档是一个文档节点(document)(document.documentElement:根节点HTML标签)
    • 每个 HTML 标签是一个元素节点
    • 包含在 HTML 元素中的文本是文本节点
    • 每一个 HTML 属性是一个属性节点
    • 注释属于注释节点
  • 对象之间的关系叫做Node (节点)层次:
  • 节点彼此都有等级关系。
  • HTML 文档中的所有节点组成了一个文档树(或节点树)。HTML 文档中的每个元素、属性、文本等都代表着树中的一个节点。
  • HTML标签是根(root)节点。节点之间都存在着父子(parent child)、同胞(sibling)的关系。



DOM树

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0iAMaWQz-1641803458648)(imgimage-20210826225211626.png)]



节点操作

​ 节点的

查找

(获取)

​ 节点的




​ 节点的




​ 节点的




查询元素节点


1、通过选择器获取元素节点



getElementById()


​ 功能:根据HTML标签的id,获取节点

​ 返回值:dom对象(dom节点)



getElementsByTagName()


​ 功能:获取相同标签名的元素节点,

​ 返回值:返回节点集合(等同为数组),使用下标的方式来获取dom元素。



getElementsByName()


​ 获取相同name属性的元素节点,不是所有标签都有name属性,(低版本浏览器会有兼容性问题)



getElementsByClassName()


​ 获取相同class属性的节点列表(IE8及以下不支持)



2、通过层级关系访问节点(包括文本节点和元素节点)



parentNode

父节点。


childNodes

当前节点包含的所有子节点(文本和元素节点都有)。


firstChild

当前节点的第一个子节点。


lastChild

当前节点的最后一个子节点。



previousSibling

访问前一个同胞节点。


nextSibling

访问后一个同胞节点。

**注:**childNodes中的所有节点都具有相同的父节点,因此它们的 parentNode 属性都指向同一个节点。包含在childNodes 列表中的每个节点相互之间都是同胞节点。

**注:**列表中第一个节点的 previousSibling 属性值为 null ,而列表中最后一个节点的 nextSibling 属性的值同样也为 null。



3、节点的类型
子节点中包含文本节点和元素节点。

​ 通过nodeType属性来判断节点类型,1 代表元素节点,2 代表属性节点,3 代表文本节点

示例:文本节点与元素节点过滤


传入父节点和需要的节点的类型,返回符合要求的子节点
		function filternode(node,data){
			var arr=[];
			for(var i=0;i<node.childNodes.length;i++){
				if(node.childNodes[i].nodeType==data){
					arr.push(node.childNodes[i]);
				}
			}
			return arr;
		}

	filternode(div1,1);



4、通过层级关系访问元素节点


parentNode

父节点。


children

当前节点的所有子节点(只有元素节点),即所有的元素类型的子节点


firstElementChild

当前节点的第一个元素类型的子节点。


lastElementChild

当前节点的最后一个元素类型的子节点。


previousElementSibling

访问前一个同胞元素类型的节点。


nextElementSibling

访问后一个同胞元素类型的节点。

注:列表中第一个节点的 previousElementSibling 属性值为 null ,而列表中最后一个节点的 nextElementSibling 属性的值同样也为 null。



节点的增删改
  • 创建一个元素节点 :document.createElement(HTML标签名)
  • 创建一个文本节点:document.createTextNode(文字)
  • 添加一个子节点: 父节点对象.appendChild(newChild) newChild 被添加到孩子列表中的末端。
  • 插入一个子节点: 父节点.insertBefore(newChild, referenceNode) 将 newChild 节点插入到 referenceNode 之前。
  • 删除一个子节点: 父节点.removeChild(oldChild) 删除 oldChild子节点。
  • 删除本身: 节点本身.remove();
  • 替换节点: 父节点.replaceChild(newChild, oldChild) 用newChild节点替换oldChild节点



四、表格的增删改查


获取表上的行、列集合

​ 表格对象.rows[index]; 取得表中的某一行

​ 行对象.cells[index]; 取得行中某一列




插入

​ 表格对象.insertRow(index);

​ 功能:插入新行,

​ 参数:下标。如果不写参数,那就是插入到末尾

​ 返回值:新行的dom对象

​ 行对象.insertCell(index);

​ 功能:插入新的单元格

​ 参数:下标。如果不写参数,那就是插入到末尾

​ 返回值:新行的dom对象(td)


删除


表格.deleteRow(index);

行对象.deleteCell(index);



作业:

1,动态创建表格(每行最后一个单元格中要有一个删除按钮)

2,删除表格(点击每行的删除按钮,删除这一行)

3,广告弹出窗(自动关闭,window.close())



第十天、DOM(下)



本节课目标:

​ 操作节点属性

​ 操作节点样式

​ 坐标和尺寸

​ offset属性



一、操作节点属性



1、官方属性

​ 一般来说,html中的属性直接在js中是可以使用的,但是也有特殊情况,如下:

  • tagName :表示元素节点的标签名

  • innerHTML :获取元素节点里的内容(用的频次超级多)

  • outerHTML: 包含元素自身的标签.(非 W3C DOM 规范)

  • innerText :获取元素内文本内容,html标签被忽略(非 W3C DOM 规范)

  • className:CSS元素的类 (不可以使用class)(用的频次挺多)



2、自定义属性
  • 增加自定义属性: node.setAttribute(“属性名”,”值”)

  • 获取自定义属性的值:node.getAttribute(“属性名”)

另外,setAttribute和getAttribute也可以操作官方属性。



二、 操作节点样式属性

1、修改样式属性:

​ 使用style,与修改其它官方属性是一样的。

​ 注意:

​ 样式属性名的规则:一般情况下,css的样式属性中出现“-”号,js中会去掉“-”号,把“-”号后面单词的第一字母大写。如果没有“-”号,则两者一样。

2、获取样式属性(兼容性问题):

​ 1)、内联(style)

​ style属性

​ 2)、内部和外部(class)

  • 针对IE(IE8及其以前的版本)使用currentStyle属性。
  • 针对非IE的主流浏览器,使用 window.getComputedStyle(dom对象).样式属性名

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cZCy1aLV-1641803458648)(imgimage-20210902194847811.png)]



三、坐标和尺寸



1、取得滚动条位置

(scrollTop和scrollLeft)

​ document.body.scrollTop 谷歌(设为0页面回到顶部)

​ document.documentElement.scrollTop 标准

兼容写法

document.documentElement.scrollTop || document.body.scrollTop



2、页面可见区域尺寸


​ 1)、宽度

​ document.body.clientWidth

var clientWidth =  document.documentElement.clientWidth || document.body.clientWidth;

​ 2)、高度

​ document.body.clientHeight(如果出现问题用document.documentElement.clientHeight)

var clientHeight =  document.documentElement.clientHeight || document.body.clientHeight;



3、取得某元素距离外层元素的距离

​ 取得某元素距离父级元素的距离,父级元素必须定位,否则就会找父级的父级找,直到body


  • offsetTop

    获取距离顶部的距离(计算出来)(不能设置,仅能获取) 设置元素顶部距离使用


  • offsetLeft

    (不能设置,仅能获取) 设置元素左边使用



四、offset属性

offset相关属性,一般都是计算出来的,

必须等到元素渲染在页面上,才能获取到结果


1、坐标(offsetLeft和offsetTop):


1、如果设置了left和top,则直接使用style获取,内部和外部用兼容性写法

2、如果没有设置left和top,则使用offsetLeft和offsetTop可以获取到元素距离父级有定位的元素的相对位置,如果父级都没有定位,则是以body标签为基准的定位。


2、尺寸(offsetWidth和offsetHeight):


1、如果设置的width和height,则直接使用style获取,内部和外部用兼容性写法

2、如果没有设置width和height,则使用offsetWidth和offsetHeight可以计算出元素占据页面区域的宽和高。


3、父 级元素:


element.offsetParent

返回元素的偏移容器 (最近的有定位的父级元素),如果父元素没有定位,则找父级的父级,直到找到有定位父级元素,如果都没有定位,则是body



附:所有 HTML 元素上的属性和方法

element.accessKey 设置或返回元素的快捷键。

element.appendChild() 向元素添加新的子节点,作为最后一个子节点。

element.attributes 返回元素属性的 NamedNodeMap。

element.childNodes 返回元素子节点的 NodeList。

element.className 设置或返回元素的 class 属性。

element.clientHeight 返回元素的可见高度。

element.clientWidth 返回元素的可见宽度。

element.cloneNode() 克隆元素。

element.compareDocumentPosition() 比较两个元素的文档位置。

element.dir 设置或返回元素的文本方向。

element.firstChild 返回元素的首个子。

element.getAttribute() 返回元素节点的指定属性值。

element.getAttributeNode() 返回指定的属性节点。

element.getElementsByTagName() 返回拥有指定标签名的所有子元素的集合。

element.getFeature() 返回实现了指定特性的 API 的某个对象。

element.getUserData() 返回关联元素上键的对象。

element.hasAttribute() 如果元素拥有指定属性,则返回true,否则返回 false。

element.hasAttributes() 如果元素拥有属性,则返回 true,否则返回 false。

element.hasChildNodes() 如果元素拥有子节点,则返回 true,否则 false。

element.id 设置或返回元素的 id。

element.innerHTML 设置或返回元素的内容。

element.insertBefore() 在指定的已有的子节点之前插入新节点。

element.isDefaultNamespace() 如果指定的 namespaceURI 是默认的,则返回 true,否则返回 false。

element.isEqualNode() 检查两个元素是否相等。

element.isSameNode() 检查两个元素是否是相同的节点。

element.isSupported() 如果元素支持指定特性,则返回 true。

element.lang 设置或返回元素的语言代码。

element.lastChild 返回元素的最后一个子元素。

element.namespaceURI 返回元素的 namespace URI。

element.nextSibling 返回位于相同节点树层级的下一个节点。

element.nodeName 返回元素的名称。

element.nodeType 返回元素的节点类型。

element.nodeValue 设置或返回元素值。

element.normalize() 合并元素中相邻的文本节点,并移除空的文本节点。

element.offsetHeight 返回元素的高度。

element.offsetWidth 返回元素的宽度。

element.offsetLeft 返回元素的水平偏移位置。

element.offsetParent 返回元素的偏移容器。

element.offsetTop 返回元素的垂直偏移位置。

element.ownerDocument 返回元素的根元素(文档对象)。

element.parentNode 返回元素的父节点。

element.previousSibling 返回位于相同节点树层级的前一个元素。

element.removeAttribute() 从元素中移除指定属性。

element.removeAttributeNode() 移除指定的属性节点,并返回被移除的节点。

element.removeChild() 从元素中移除子节点。

element.replaceChild() 替换元素中的子节点。

element.scrollHeight 返回元素的整体高度。

element.scrollLeft 返回元素左边缘与视图之间的距离。

element.scrollTop 返回元素上边缘与视图之间的距离。

element.scrollWidth 返回元素的整体宽度。

element.setAttribute() 把指定属性设置或更改为指定值。

element.setAttributeNode() 设置或更改指定属性节点。

element.setIdAttribute()

element.setIdAttributeNode()

element.setUserData() 把对象关联到元素上的键。

element.style 设置或返回元素的 style 属性。

element.tabIndex 设置或返回元素的 tab 键控制次序。

element.tagName 返回元素的标签名。

element.textContent 设置或返回节点及其后代的文本内容。

element.title 设置或返回元素的 title 属性。

element.toString() 把元素转换为字符串。

nodelist.item() 返回 NodeList 中位于指定下标的节点。

nodelist.length 返回 NodeList 中的节点数。


示例:

​ 用JS控制css的过渡效果;



作业:

1,进度条

2,隔行变色

3,网页换肤

4,简易年历

5,自动登录勾选(onmouseover,onmouseout)

6,点击按钮换图片

7,tab切换

8,扩展:qq延迟提示框

9,密码强度(onblur,oninput或者用onkeyup,onkeydown)



第十一天、事件(上)



本节课目标:

​ 事件的概念

​ DOM0,DOM2,IE事件绑定

​ 事件冒泡、捕获、事件流(面试题)

​ event对象

​ event对象的常用属性



一、事件的概念:

  • 什么是事件:

    事件是发生并得到处理的操作,即:事情来了,然后处理。

  • 事件触发:

    当用户点击按钮时,我们就说,触发了按钮的onclick事件。

  • 事件处理程序:

    也就是响应(处理)某个事件的函数。

  • 绑定事件

    事件与事件处理程序建立对应关系的过程我们称为给事件绑定函数,或给事件注册函数。现在常用的绑定方式有DOM0级,DOM2级和IE特殊方式。

  • 事件源:

    ​ 发生事件的dom对象(元素)。



二、DOM0事件:



1、绑定事件:

1)、使用HTML的属性指定方式,如果要调用函数,这个函数在JS中要处于全局作用域。

   <input type="button" id = "button1" value="按钮" onclick="queren(this)">
       
   function queren(data) {   
       alert(data.value);    
   }

2)、

DOM对象的事件属性绑定(匿名函数方式)

<input type="button" id = "button1" value="按钮" >
     
var btn1=document.getElementById(" button1 ");
btn1.onclick = function () {
    this是事件源,就是btn1。
    alert(this.value);
}

第二种(在js中绑定事件,在html啥也不写)好:

1、在js中可以通过循环的方式,批量绑定事件

2、解耦:

​ 耦:就是耦合度的意思。即:就是联系的意思。解耦:让联系不紧密。



2、解绑:

​ 1)、btn1.onclick = “”;

​ 2)、btn1.onclick =null

注:this代表触发事件的当前元素,也叫事件源。在例子中代表按钮button1



三、常见事件类型

onclick 当用户点击某个对象时调用的事件。

ondblclick 当用户双击某个对象时调用的事件。

onfocus 元素获得焦点。

onblur 元素失去焦点。

onchange 域的内容被改变(下拉列表选择改变)。

onkeydown 键盘上某个按键被按下。

onkeyup 键盘上某个按键被松开。

onkeypress 键盘上某个按键被按下并松开。

oninput 这是HTML5新增的事件,用户输入时,触发

onmousedown 鼠标按钮被按下。

onmouseup 鼠标按键被松开。

onmouseover 鼠标移到某元素之上(会有冒泡影响)

onmouseout 鼠标从某元素移开(会有冒泡影响)

onmouseenter 鼠标进入某个元素。 (不会受冒泡影响)

onmouseleave 鼠标离开某个元素(真正的离开,不会受冒泡影响)

onmousemove 鼠标被移动。

onresize 窗口(body)或框架被重新调整大小。

onsubmit onsubmit是form标签的事件。在点击type=”submit”按钮时,会触发。

onload 一张页面或一幅图像完成加载。

onunload (body)用户退出(刷新)页面。



四、DOM2级

DOM0级很浓的绑定味道,DOM2真正体会注册的概念。



1、绑定事件:

​ dom对象.addEventListener(事件类型名,事件处理函数,是否捕获)

​ 事件名: 就是事件类型,不带on。如:click,mouseover等等

​ 事件处理函数:可以匿名,可以有名。

​ 是否捕获:true:事件绑定在捕获阶段,false:事件绑定在冒泡阶段

DOM0级和DOM2级绑定事件的区别:

1)、DOM0 只能绑定一个事件处理函数,后面的把前面的覆盖了

2)、DOM2可以绑定多个事件处理函数。执行顺序是按照绑定顺序。



2、 解绑(删除)事件

​ dom对象.removeEventListener(事件类型名,事件处理函数名,是否捕获);

注意:如果要删除事件,就必须保证绑定时,使用有名的函数。

​ 即:匿名添加的事件处理函数无法移除。



五、IE事件注册

IE8及其以前的版本不支持DOM2级注册方式,自己提供了attachEvent和detachEvent来实现相似功能。



1、绑定事件:

​ dom对象.attachEvent(事件名,事件处理函数)

​ 事件名:带 on。如:onclick,onmouseover等等

​ 事件处理函数:可以匿名,也可以有名



2、解绑(删除)事件

​ dom对象.detachEvent(事件名,事件处理函数名)

​ 事件名:带 on。如:onclick,onmouseover等等

​ 事件处理函数名:同样的,匿名事件处理函数,不能删除

DOM2级和IE绑定事件的区别:

触发顺序:

1、DOM2触发顺序是按照绑定顺序。

2、IE触发顺序和绑定顺序相反。



六、事件冒泡、捕获、事件流(面试题)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r80g8S5w-1641803458649)(imgimage-20210829231315005.png)]



1、事件冒泡

​ 当触发了某个元素的某类型(如:onclick)的事件后,该元素的父元素的同类型(如:onclick)的事件也会被触发,依次类推最终会触发到最根的元素(HTML标签)

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>作业5</title>
<style type="text/css">
#grandpaBox{
	width:300px;
	height:300px;
	background-color:pink;
}
#fatherBox{
	width:200px;
	height:200px;
	background-color:orange;
}
#meBox{
	width:100px;
	height:100px;
	background-color:blue;
}
</style>
</head>
<body>
	<div id="grandpaBox">
		<div id="fatherBox">
			<div id="meBox">
				
			</div>
		</div>
	</div>
</body>
</html>

<script type="text/javascript">
function $(id){
	return document.getElementById(id);
}

事件冒泡:当触发了某个元素的某类型(如:onclick)的事件后,
该元素的父元素的同类型(如:onclick)的事件也会被触发,
依次类推最终会触发到最根的元素(HTML标签)

$("meBox").addEventListener("click",function(){
	alert("我被点了");
},false);

 $("fatherBox").addEventListener("click",function(){
 	alert("爸爸被点了");
 },false);

$("grandpaBox").addEventListener("click",function(){
	alert("爷爷被点了");
},false);


</script>


2、事件捕获

​ 与事件冒泡的触发顺序相反,当触发了某个元素的某类型(如:onclick)的事件后,先从最外层的元素进行触发,由外朝内直到触发到元素本身

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>作业5</title>
<style type="text/css">
#grandpaBox{
	width:300px;
	height:300px;
	background-color:pink;
}
#fatherBox{
	width:200px;
	height:200px;
	background-color:orange;
}
#meBox{
	width:100px;
	height:100px;
	background-color:blue;
}
</style>
</head>
<body>
	<div id="grandpaBox">
		<div id="fatherBox">
			<div id="meBox">
				
			</div>
		</div>
	</div>
</body>
</html>

<script type="text/javascript">
function $(id){
	return document.getElementById(id);
}

事件捕获:与事件冒泡的触发顺序相反,
当触发了某个元素的某类型(如:onclick)的事件后,
先从最外层的元素进行触发,由外朝内直到触发到元素本身。

$("meBox").addEventListener("click",function(){
	alert("我被点了");
},true);

$("fatherBox").addEventListener("click",function(){
	alert("爸爸被点了");
},true);

$("grandpaBox").addEventListener("click",function(){
	alert("爷爷被点了");
},true);

</script>


3、事件流

面试题:请问您对DOM事件流怎么理解?

​ 1)、事件流:就是事件触发的流向,即事件触发的顺序。

DOM事件流分为三个阶段:

1、第一个阶段是捕获阶段

2、第二个阶段是事件源

3、第三个阶段是冒泡阶段

几种事件绑定方式对比

DOM0 DOM2 IE
事件名 事件名以“on”开头 事件名前没有“on” 事件名以“on”开头
冒泡 支持 支持,最后参数设为false 支持
捕获 不支持 支持,最后参数设为true 不支持
阻止冒泡 event.stopPropagation() event.stopPropagation() event.cancelBubble=true
多处理程序注册 不支持 支持 支持,执行顺序与注册顺序相反
删除事件绑定 设置为空字符串或null 用处理程序名删除,匿名绑定不能删除 用处理程序名删除,匿名绑定不能删除
兼容性 全兼容 非IE IE



七、事件对象



1、事件对象的解释

event对象:

event对象只在事件发生(如:点击事件)的过程中才有效。如:当点击按钮时,就会自动产生event对象。event对象是自带的对象,是固定写法。



2、事件对象的获取:

在W3C标准中,直接在函数中声明该参数即可

btn.onclick = function(event) { 	

};

兼容性写法,支持老版本的IE

 var evt = event ? event : window.event;

       或者 
  var evt = event || window.event;	



八、事件对象的属性



1、

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ffso7JqC-1641803458649)(imgimage-20210829231656530.png)]

重点要说的是:

target

currentTarget

stopPropagation

preventDefault



2、键盘相关的属性


altKey

返回当事件被触发时,“ALT” 是否被按下。


shiftKey

返回当事件被触发时,“SHIFT” 键是否被按下。


ctrlKey

返回当事件被触发时,“CTRL” 键是否被按


keyCode

:(火狐用which)获取按键的键码 回车是:13;空格:32

示例:尝试实现简单聊天室(快捷键的使用)。



3、鼠标相关属性


button

鼠标按键事件中按了哪个鼠标键,0:左,1:中,2:右


clientX

基于浏览器可视区域的左上角的鼠标x坐标(坐标原点在浏览器左上角)。


clientY

基于浏览器可视区域的左上角的鼠标y坐标。


pageX

基于网页左上角的鼠标x坐标(坐标原点在网页左上角)。


pageY

基于网页左上角的鼠标y坐标。

pageY-clientY == scorllTop


offsetX

基于事件元素(事件源)左上角的鼠标 x 坐标(坐标原点在事件源左上角)。


offsetY

基于事件元素(事件源)左上角的鼠标 y 坐标。


screenX

基于显示器左上角的鼠标x坐标。


screenY

基于显示器左上角的鼠标y坐标。

总结:事件相关的兼容性写法

var ev = event||window.event; 取得event对象,兼容IE的写法

var ele = evn.currentTarget||evn.srcElement; 当前事件元素,兼容IE写法

var keycode = evn.which || evn.keyCode;兼容火狐的写法



作业

1,鼠标跟随提示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RQ6pQZlv-1641803458650)(imgimage-20210829231821956.png)]

2,输入框默认提示(鼠标经过显示提示,当获得焦点或输入内容时,提示消失)

3,表格即时编辑

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oPUJCldU-1641803458650)(imgimage-20210829231830115.png)]

4,聊天室(快捷键)

5,下拉菜单(鼠标经过改变背景色)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vPYxZtnO-1641803458650)(imgimage-20210829231835467.png)]



第十二天、事件(下)



本节课目标:

​ 阻止浏览器默认行为

​ 事件绑定的封装

​ 事件的委托(面试题)

​ 鼠标拖拽



一、阻止浏览器默认行为

  • ​ 什么是浏览器的默认行为

    ​ 就是浏览器在网页上的功能,

  • ​ 阻止浏览器的默认行为三种情况:

    ​ w3c使用 event.preventDefault(),

    ​ IE则是使用 event.returnValue = false;

    ​ 事件处理函数直接 return false也行的。

  • 示例: 限制文本框中只输入指定的字符、超链接跳转、表单提交、右键菜单的弹出等。



二、事件绑定的封装

		function(ele,event,func){
		     if(ele.addEventListener)
            {
				ele.addEventListener(event,func,false);
            }else if(ele.attachEvent){
				ele.attachEvent('on'+event,func);
             }else{
				ele['on'+event]=func;
             }
         }



三、事件的委托

1、什么叫委托:

把本该属于自己做的事情,交给别人做。

2、 事件的委托:

把本该属于某个DOM元素的事件,交给

父级

元素来完成。

3、事件委托的原理:

利用事件的冒泡。并结合 target确定出真正的事件源

4、事件委托的优点:

1)、可以让后来新添加的子元素的事件有效。

2)、减少绑定元素的代码,减少了(编译后的)文件的大小。



四、鼠标拖拽

1,给目标元素添加onmousedown事件;document添加onmousemove事件和onmouseup事件。

2,目标元素的onmousedown事件,计算鼠标和目标元素的坐标差。

鼠标和元素的横坐标差 x = 鼠标的clientX –目标元素的left

鼠标和元素的纵坐标差 y = 鼠标的clientY–目标元素的top

同时记录鼠标键被按下。

3,document的onmousemove事件,如果鼠标是按下状态,让目标元素跟随鼠标。

目标元素的left = 鼠标的clientX + 鼠标和元素的横坐标差 x。

目标元素的top = 鼠标的clientY + 鼠标和元素的纵坐标差 y。

4,document的onmouseup事件,记录鼠标键被放开。

拖拽事实上是给跟随鼠标增加了一个开关,鼠标按下打开开关,鼠标弹起关闭开关。



五、补充事件

1、鼠标滚轮事件 onmousewheel:鼠标滚动后,触发该事件

    var i=0;
当鼠标在meBox上滚动时触发
document.getElementById("meBox").onmousewheel = function(){
	console.log("i:"+i++);
}

var j=0;
window.onmousewheel = function(){有没有滚动条无所谓,只要鼠标的滚轮滚动了,就会触发
	console.log("j:"+j++);
}

var scroll=0;
window.onscroll = function(){页面有滚动条,并且进行了滚动,触发该事件
	console.log("onscroll:"+scroll++);
}

2、window.onmousewheel和window.onscroll的区别

onscroll:有滚动条,并且滚动条移动了,触发该事件

onmousewheel :有没有滚动条无所谓,只要鼠标的滚轮滚动了,就会触发

示例:

var j=0;
window.onmousewheel = function(){有没有滚动条无所谓,只要鼠标的滚轮滚动了,就会触发
	console.log("j:"+j++);
}
var scroll=0;
window.onscroll = function(){页面有滚动条,并且进行了滚动,触发该事件
	console.log("onscroll:"+scroll++);
}


模拟滚动条对比window.onmousewheel和window.onscroll的区别

var i=0;
当鼠标在meBox上滚动时触发
document.getElementById("subBox").onmousewheel = function(){
	console.log("i:"+i++);
}

var scroll=0;
onscroll事件不会触发
document.getElementById("subBox").onscroll = function(){
	console.log("onscroll:"+scroll++);
}



六、扩展: 事件处理函数防抖

定义:多次触发事件后,事件处理函数只执行一次,并且是在触发操作结束时执行。

如:

onkeyup,onkeypress等事件,在用户输入过程中不希望触发(如:搜索时,输入汉字)当用户停止输入时,再进行触发。

原理:对处理函数进行延时操作,若设定的延时到来之前,再次触发事件,则清除上一次的延时操作定时器,重新启动定时器。

onkeyup(抖动)的示例:

<input id="txt" type="text" >
document.getElementById("txt").onkeyup = function(){
	console.log(this.value);
}
这样写,就会不停地触发,而且,输入汉字(拼音的每个字母也会触发)时,
我们希望用户输入完毕,或者汉字输入完毕时触发。

input id="txt" type="text" >
var myTimer = null;

document.getElementById("txt").onkeyup = function(){
	if(myTimer!=null){
		clearTimeout(myTimer);
		myTimer = null;
	}
	myTimer = setTimeout(()=>{
		console.log(this.value);
	},300);
}

启动定时器,用定时器来测试用户是否输入完毕(其实,就是用户“长时间不输入”,说明,输入完毕)



作业:

1,显示鼠标移动轨迹

2,弹出窗拖拽

3,阻止超链(阻止浏览器的默认行为)

扩展:

1,拖拽轨迹回放

2,九宫格

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AeQFHSlt-1641803458651)(imgimage-20210829232609928.png)]



第十三天、正则表达式对象



本节课目标:

​ 正则的概念

​ 正则对象(RegExp)的创建和属性方法

​ 正则表达式的规则(特殊字符)

​ 常见正则

​ 正则封装



一、正则的概念:


概念:


正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“

规则

字符串”,这个“规则字符串”用来表达对字符串的一种

过滤逻辑

​ 规则举例:

​ 1、包含字母,数字,下划线;

​ 2、开头不能是数字;

​ 3、一共有11位,而且必须以1 开头。

​ 4、包含数字,字母,特殊字符,长度至少6位。


作用:


给定一个正则表达式和另一个字符串,我们可以达到如下的目的:

  1. 给定的字符串是否符合正则表达式的过滤逻辑(称作“匹配”);

  2. 可以通过正则表达式,从字符串中获取我们想要的特定部分。


特点:

  1. 灵活性、逻辑性和功能性非常的强;

  2. 可以迅速地用极简单的方式达到字符串的复杂控制。

  3. 对于刚接触的人来说,比较晦涩难懂。



二、正则对象



1、正则(RegExp)对象的定义



1)、构造函数的方式

​ 格式:

​ new RegExp(pattern, attributes); regular expression的简写

​ 参数:

​ pattern:模式字符串(规则字符串) 或者正则表达式对象

​ attributes:字符串,可选。包含属性 “g”、“i” 和 “m”,分别用于指定全局匹配、忽略大小写的匹配和多行匹配。ECMAScript 标准化之前,不支持 m 属性。如果 pattern 是正则表达式,而不是字符串,则必须省略该参数

​ 示例:

   例如:
   	var reg = new RegExp(‘study’);    表示含有study,(默认区分大小写)
   	var reg = new RegExp('study', 'ig'); 其中i 表示忽略大小写,g 表示全局匹配,


2)、常量方式直接声明()

如:var reg = /study/ig; 
	等价于:var reg= new RegExp('study',"ig"); 
	等价于:var reg= new RegExp(/study/,"ig");



2、属性和方法


1)、属性



global

RegExp对象是否具有标志 g。 全局


ignoreCase

RegExp对象是否具有标志 i。 忽略大小写


source

正则表达式的源文本。


Multiline

RegExp 对象是否具有标志 m。 不只匹配一行。


2)、方法



exec(字符串)

检索字符串中指定的值。返回找到的值,如果没有匹配到,则返回null。


test (字符串)

检索字符串中指定的值。返回 true 或 false。



三、正则表达式规则



1、正则表达式的特殊字符

	限制位置:   
	^ 	匹配一行的开头,/^a/匹配"abc",而不匹配“bca“
	$ 	匹配一行的结尾,/a$/匹配“bca",而不匹配"abc" 
	
    限制数量:    
	.   匹配单个字符,除了换行和行结束符,等价于[^n] 
	* 	匹配前面元字符0次或多次,/ba*/将匹配b,ba,baa,baaa 
	+ 	匹配前面元字符1次或多次,/ba+/将匹配ba,baa,baaa 
	? 	匹配前面元字符0次或1次,/ba?/将匹配b,ba 
	{n} 	精确匹配n次 ,/d{4}/  将匹配,出现连续4个d的字符串
	{n,} 	匹配n次以上 ,/d{4,}/将匹配,出现连续4个及其以上多个d的字符串
	{n,m} 	匹配n-m次,/d{4,6}/将匹配,出现连续46个d的字符串
       
    限制字符:
	(x|y) 	匹配x或y ,/a|b/ 将匹配只要出现a或者b的字符串,不含a与b的不匹配
	[xyz] 	匹配这个集合中的任一一个字符,如:[a-z] 表示小写a到小写z范围的字符。
	[^xyz] 不匹配这个集合的任何一个字符 ,同样可以写范围,如:[^a-z]
	(red|blue|green)  将一些正则匹配规则合成一个小组。    
	d 	匹配一个数字字符,/d/ 等价于 /[0-9]/ 
	D 	匹配一个非数字字符,/D/ 等价于 /[^0-9]/ 
	w 	匹配一个可以组成单词(变量)的字符(包括字母,数字,下划线)等价于[a-zA-Z0-9_]
	W 	匹配一个不可以组成单词的字符    
    n 	匹配一个换行符 
	s 	匹配一个空白字符,包括n,r,f,t,v等 
	S 	匹配一个非空白字符,等于/[^nfrtv]/ 
    
    限制位置:
	b 	匹配一个单词的边界 (单词是以空格分割的)
	B 	匹配一个单词的非边界 
    
    限制字符:
	0	匹配NUL 字符。 
	ddd	匹配以八进制数 ddd 规定的字符。 如:var reg = /^141/; 等价于 var reg = /^a/; 
	xdd	匹配以十六进制数 dd 规定的字符。         
	udddd	匹配以十六进制数 dddd 规定的 Unicode 字符

总结:

​ 字符串的规则:一般是限定,某个位置可以出现什么字符,出现多少次。



2、直接量字符(转义字符)

n     换行符

/     一个 / 直接量
     一个  直接量
.     一个 . 直接量
*     一个 * 直接量
+     一个 + 直接量
?     一个 ? 直接量
|     一个 | 直接量
(     一个 ( 直接量
)     一个 ) 直接量
[     一个 [ 直接量
]     一个 ] 直接量
{     一个 { 直接量
}     一个 } 直接量
-     一个-直接量



3、量词

c{n}      	匹配包含 n 个 c 的序列的字符串。 
c{m,n}  	匹配包含 m 到 n 个 c 的序列的字符串。 
c{n,}     	匹配包含至少 n 个 c 的序列的字符串。 
c+         	匹配任何包含至少一个 c 的字符串,等价于c{1,} 。 
c*         	匹配任何包含零个或多个 c 的字符串,等价于c{0,}  
c?        	匹配任何包含零个或一个 c 的字符串,等价于 c{0, 1} 



4、位置

c$         	匹配任何结尾为 c 的字符串。 
^c         	匹配任何开头为 c 的字符串。

?=c       	匹配任何其后紧接指定字符串 c 的字符串。

		     如:找后面跟着" all"的is。             
             var patt1=/is(?= all)/g;

		     var str="Is this all there is"; 

?!c        	匹配任何其后没有紧接指定字符串 c 的字符串。 
		     如:找后面没有跟着" all"的is
              var patt1=/is(?! all)/gi; 
			  var str="Is this all there is";



四、常见正则

检查邮政编码		共 6 位数字,第一位不能为 0
	/^[1-9]d{5}$/
        
检查文件压缩包  	xxx.zipxxx.gzxxx.rar
	/^w+.(zip|gz|rar)$/   
        
删除空格  
	str.replace(/s+/,'');

删除首尾空格
	var str = str.replace(/^s+/,'');
	str  = str.replace(/s+$/,'');

(面试题)电子邮件( xxxxx @ xxxx(.xxxx)+)	770107@qq.com; 770107@qq.com.cn   
	/^w+@w+(.w+)+$/ 
    
手机号
	/^1d{10}$/
        
身份证
	/^d{17}(d|X)$/ 
             
日期  (xxxx-xx-xx| xxxx/xx/xx | xxxx.xx.xx)
	/^d{2}|d{4}[-/.]d{2}[-/.]d{2}$/
                  
只能输入中文
	str.replace(/[^u4e00-u9fa5]/g,'');

账户名只能使用数字字母下划线,且数字不能开头,长度在6-15之间
	/^[a-zA-Z_]w{5,14}$/
        
验证IP(xxx.)xxx.xxx.xxx|254.245.255.255      240.196.19.5
	/^((25[0-5]|2[0-4]d|1d{2}|[1-9]?d).){3}(25[0-5]|2[0-4]d|1d{2}|[1-9]?d)$/



五、正则的封装


 常见正则:

 邮箱
 身份证号码
 手机号码的
 密码

 功能:常见正则的封装
 参数:
     类型(邮箱,身份证号码,手机号码,密码)
     字符串
 返回值:true或者false

 zhengZe("email","7701037@qq.com");

 function zhengZe(type,str){
     switch(type){
         case "email" : var reg = /^w+@w+.(com|net|cn)$/;break;
         case "cardId": var reg = /^[1-9]d{16}(d|x|X)$/;break;
         case "phone": var reg = /^1d{10}$/;break;
          密码的规则:不能纯数字,数字,字母,下划线组成
         case "pass": var reg = /^(?![0-9]+$)w{6,16}$/;break;
     }    
     return reg.test(str);
      if(reg.test(str)==true){
          return true;
      }else{
          return false;
      }
 }

//用json对象的方式

function zhengZe(type,str){
    var regObj = {
        "email":/^w+@w+.(com|net|cn)$/,
        "cardId":/^[1-9]d{16}(d|x|X)$/,
        "phone":/^1d{10}$/,
        "pass":/^(?![0-9]+$)w{6,16}$/
    }
    return regObj[type].test(str);
}



六、字符中使用正则的方法

var pattern = /good/ig;  				
var str = 'good good study!,day day up!';
1,match:
	使用 match 方法获取获取匹配数组a
	alert(str.match(pattern));  			匹配到两个 good,good
2,search
	使用 search 来查找匹配数据
	alert(str.search(pattern)); 			查找到返回位置,否则返回-1
3,replace
	使用 replace 替换匹配到的数据(找到并替换)
	alert(str.replace(pattern, 'hard')); 			
4,split:
	使用 split 拆分成字符串数组,    var arr = str.split(pattern);  将按空格拆开字符串成数组



七、扩展

扩展: $n

 $n 表示 正则表达式中第n个圆括号(圆括号是子匹配)里匹配到的字符:

示例一:
	var str = 'abc250-hello';
	var strReg = str.replace(/(d+)-([A-Za-z]+)/g,'$1');$1表示250,即整体表示把正则对象匹配到的字符串替换成正则对象中第一个圆括号里匹配到内容替换。
	console.log(strReg);abc250
	console.log(RegExp.$1);250
示例二:
var re=/(13)(/d)(/d{8})/; 该正则表达式可以匹配手机号码以13开头的11位号码以()为子匹配的标志 
document.write(objStr.replace(re,"$1$2********"));处于隐私对字符串按照正则表达式的内容进行替换 
如果第二个子匹配结果,即手机号码中的第三位数字小于等于3则该手机号为联通号码,否则为其他运营商的号码 
if(RegExp.$2<=3){ 
	document.write("这是联通手机号"); 
}else{ 
	document.write("这是移动或者电信手机号"); 
} 

 $n 表示 正则表达式中第n个圆括号(圆括号是子匹配)里匹配到的字符:
示例三:
但是每进行一次匹配$n的值就改变一次,因此当我们匹配在一个字符串中出现多个手机号码时我们需要这样匹配 
var objStr="手机号码1:131456321, 这是我朋友的手机号码139987654"; 
         var reg=/(13)(/d)(/d{8})/g
var arr=objStr.match(reg); 将字符串进行匹配如果符合结果则将返回结果放入数组中 

if(arr!=null){如果匹配有结果 
    for(var i=0;i<arr.length;i++){ 
        arr[i]=arr[i].toString().replace(reg,"$1$2********"); 对匹配出来的电话号码进行二次匹配	document.write("<li>"+arr[i]); 以列表的形式输出各个电话号码 
        if(RegExp.$2<=3){ 如果第三个电话号码小于等于3则该电话号码为联通号码,否则为其他运营商的号码 
            document.write("这是一个联通手机号码!"); 
        }else{ 
            document.write("这是一个移动或者电信手机号码!"); 
        } 
	}
}




作业

1,用正则实现注册表单的验证

2,敏感词过滤

注:要求使用封装的正则函数实现

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wLE2BqbE-1641803458651)(imgimage-20210901233331533.png)]



第十四天、

ES5及ES6



本节课目标:

ES5

  • ​ 严格模式

  • ​ JSON.parse/JSON.stringify

ES6

  • letconst关键字(重点)
  • 箭头函数(重点)
  • ES6新增的方法和模板字符串
  • 解构赋值(重点)
  • 字符串扩展方法
  • Symbol类型
  • Set和Map结构
  • class和继承 (面向对象中讲解)
  • promise(AJAX中学习)



一、ES5



1、严格模式



1)、概念:

​ ECMAscript 5添加了第二种运行模式:严格模式(strict mode)。顾名思义,这种模式使得Javascript在更严格的条件下运行。“严格模式”体现了Javascript朝着更合理、更安全、更严谨的发展方向,把以前的模式就叫“正常模式”

特点和目的:

消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;

消除代码运行的一些不安全之处,保证代码运行的安全;

提高编译器效率,增加运行速度;

为未来新版本的Javascript做好铺垫



2)、 严格模式的兼容性:

​ 包括IE 10在内的主流浏览器,都已经支持它。同样的代码,在“严格模式”中,可能会有不一样的运行结果;一些在“正常模式”下可以运行的语句,在“严格模式”下将不能运行。



3)、 使用严格模式的标志:

​ 要使用严格模式,首先需要增加代码: “use strict“; 老版本的浏览器会把它当作一行普通字符串,加以忽略。



4)、严格模式的作用域

  • 针对单个函数

    将”use strict”放在函数体的第一行,则整个函数以”严格模式”运行。

    function strict(){
    	"use strict";
    	return "这是严格模式。";
    }
    

  • 针对单个文件

    将整个脚本文件放在一个立即执行的匿名函数之中。

    (function(){
        "use strict";
        function fn(){
                 
             }
        
        function fn02(){
            
        }
     
    })();
            
    


5)、语法和行为的改变举例:

全局变量显式声明,严格模式下声明变量时,必须明确写上关键字。

  "use strict";
  age = 20;  报错,v未声明

因此,严格模式下,变量都必须先用var命令声明,然后再使用



2、bind和this关键字

在第六周讲解



3、 JSON.parse/JSON.stringify


  • JSON.parse:


    字符串转换成JSON对象。

    注意点:属性名必须要带上双引号

如:var jsonStr ={
    	“id”:1,
    	 “name”:”宝宝”
    }'; 
JSON.parse(jsonStr); 


  • JSON. Stringify:


    JSON对象转换成字符串。

var person= { 
		name : '宝宝',
		password : '123456'
	}; 
	var str = JSON.stringify(person);
	alert(typeof str);
	alert(str);



二、ES6



1、let和const(面试题)


  • let



    1)、 Let用来声明变量


    它的用法类似于var,但是所声明的变量,只在let命令所在的代码块(一对花括号里)内有效。


    2)、 Let声明的变量不会声明提升(前置)



    3)、块级作用域


    ES5及其以前只有全局作用域和函数作用域,没有块级作用域。一般来说,一对花括号属于一个块级作用域。

    let实现了js的块级作用域,我们在使用条件语句 循环语句等就会不担心变量污染的问题了。


    4)、在同一个作用域中不能用let声明同名的变量



    5)、let声明的变量有暂时性死区


6)、 let和var定义全局变量的区别


7)、let和var在for循环里的区别


  • const

    const (constant的前五个字母)

    const修饰的变量是只读变量(也叫常量), 它的特性和let一样。

        const num =20;
    	alert(“num=+num);
    	num = 100;此句话执行失败
    	alert(“num=+num);	
    	const arr = [12,34,45,56];
    	console.log(“arr=+arr);	
    	arr[0]=90;此句话可以执行
    	console.log(“arr=+arr);	
    	arr = [100,200];此句话不可以执行
    	console.log("arr="+arr);
    

    注意:const修饰的是变量代表内存,而不是引用的内存。

面试题:请问let,var,const的区别?

https://blog.csdn.net/jiang7701037/article/details/83929296

https://blog.csdn.net/jiang7701037/article/details/83929371



2、箭头函数

​ Arrow Function(箭头函数),箭头函数不使用关键字function,箭头函数是匿名函数,参数的圆括号可以省略,return也可以省略,函数体的花括号也可以省略。

​ 1)、箭头函数的格式:

    定义函数:
    let 函数名 = (形参列表) => {
    	函数体
    }
    
    调用函数
    函数名(实参列表) 

​ 2)、箭头函数中省略形参列表的圆括号

​ 当箭头函数里,有形参并且形参只有一个时,可以省略圆括号,没有形参(或者多个形参)时,不能省略圆括号。

let 函数名 = 形参 =>{
	函数体
}

​ 3)、箭头函数中省略return和函数体的花括号

当函数体中只有一句代码时,可以省略掉花括号和return关键字。

let 函数名 = (形参列表) =>函数体

​ 4)、箭头函数返回对象时,加上圆括号

当函数体中只有一句代码,但是返回值是json对象时,如果想省略掉花括号和return关键字。那么就需要增加圆括号

let fn = (name,sex) =>({name:name,sex:sex})

箭头函数的特点(第六周会讲解):

1、箭头函数里没有arguments

2、箭头函数里没有this



3、ES6新增方法:

ES6中的Array.from();

​ https://blog.csdn.net/jiang7701037/article/details/94735705

Object.assign();

​ https://blog.csdn.net/jiang7701037/article/details/94736917



4、模板字符串

​ 1、使用字符 `

​ 2、使用模板字符串表示字符串时,可以换行了

​ 3、曾经用 + 拼接变量和字符串的写法,用 ${变量}

function fn1(){
    // 2、使用模板字符串表示字符串时,可以换行了
    var str = `{
                    "name":"李茂军",
                    "sex":"男",
                    "age":12
                }`;

    var person = JSON.parse(str);

    //3、 曾经用 +  拼接变量和字符串的写法,用  ${变量}
    let name = "李茂军";
    let age = 12;
    let sex = "男";
    let str = `我叫${name},今年${age}岁,性别:${sex}`;
    console.log(str);
    
} 

fn1();



5、解构赋值

解构赋值可将数组的元素或对象的属性赋予给另一个变量。该变量的定义语法与数组字面量或对象字面量很相似



1)、解构数组

批量给变量赋值一:

 如下数组:
     let  arr=[12,23,34];
     如果需要把每个数组元素对应一个变量,以前的做法,分别定义三个变量,一一赋值。
     let  v1 = arr[0];
     let  v2 = arr[1];
     let  v3 = arr[2];
     而使用解构赋值,就可以一次性搞定:
     let [v1,v2,v3]=arr;

批量给变量赋值二:

 let [v1,v2,v3] = [12,23,34]; 

批量给变量赋值三:

 左侧的变量列表可以用逗号的形式跳过右侧对应的值
 let [,,v3]=[12,23,34];     这相当于只定义了一个变量v3,值为34

批量给变量赋值四(数组嵌套):

左右两侧的格式应保持一致
let [v1,[v2,v3],[v4,v5]]= [12,[23,34],[45,56]];数组嵌套

批量给变量赋值五:

将右侧多余的值以数组的形式赋值给左侧变量的语法——“rest“模式
let [v1,...v2]=[12,23,34,45,56];23开始朝后的所有数字作为数组赋给v2。
console.log("v1="+v1);
console.log("v2="+v2);


2)、解构对象

基本格式:

	var p = {
		"id":"007",
		"name":"张三疯",
		"sex":"男"
	};

 定义变量,把属性的值进行对应赋值	
 
	var {id:idV,name:nameV} = p;定义变量idV和nameV,分别用属性id和name的值进行赋值。	
    
	console.log("idV="+idV);
	console.log("nameV="+nameV);

如果属性名和变量名一样时,可以简写:

var {id,name} = p;
	console.log("id="+id);
	console.log("name="+name);

对象嵌套

var {id,name,address:{city,area}} = {id:"007",name:"李四",address:{city:"beijing",area:"昌平"}};
	
console.log("id="+id);
console.log("name="+name);
console.log("city="+city);
console.log("area="+area);



6、字符串扩展

includes(), startsWith(), endsWith()

​ 以前,JavaScript只有indexOf方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6又提供了三种新方法。

 includes():返回布尔值,表示是否找到了参数字符串。
 startsWith():返回布尔值,表示参数字符串是否在源字符串的头部
 endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部
var s = 'what are you 弄啥哩';
console.log(s.startsWith('what'));  true
console.log(s.endsWith('啥哩'));  true
console.log(s.includes('are'));  true



7、Symbol类型

Symbol是一种ES6新增的数据类型,是JavaScript的第七种原始类型,JavaScript以前的六种类型:Undefined,Null 空值,Boolean 布尔类型,Number 数字类型,String 字符串类型,Object 对象类型。

Symbol,它由全局 Symbol() 函数创建,每次调用 Symbol()函数,都会返回一个唯一的 Symbol。

Symbol 是表示一个全球唯一的数据。

let symbol1 = Symbol();
let symbol2 = Symbol();
console.log( symbol1 === symbol2 ); false;因为每个 Symbol 值都是唯一的

​ Symbol 充当唯一的对象键。即你写的键永远不可能和其它地方重复,特别是当你做插件或者类库时,需要给某个DOM元素增加属性时,为了保证不和其它使用该DOM元素的地方冲突,就可以使用

	var  p={};
	let s1 = Symbol();   	
	let s2 = Symbol();
	p[s1] = "hello";  
    p[s2] = "hi";



8、Set和Map结构

​ set和Map在很多的面向对象编程语言中(java,c++)中都有。在其它的编程语言中,有个集合(容器)的技术名词,集合包括:数组,set,Map等等,都是属于能够保存很多数据的一个集合。




Set

类似于数组,但是

成员的值都是唯一的

,没有重复的值,可以简单的理解为

无重复值的数组


定义和创建


Set函数可以接受一个数组(或类似数组的对象)作为参数,用来初始化

如:

	var set = new Set([1, 2, 3, 4, 4,2,5]);new的方式实例化一个set实例
	
    for (let i of set) {  
        console.log(i);   	
    }

在Set内部,两个NaN是相等。两个对象总是不相等的(比较的是地址)



Set结构的四个操作方法:

add(value):添加某个值,返回Set结构本身。
		var set = new Set([1, 2, 3, 4, 4,2,5]);
		set.add(100);
		set.add(107).add(108);

 delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
        set.delete(4); 

 has(value):返回一个布尔值,表示该值是否为Set的成员。
 
 clear():清除所有成员,没有返回值


Set结构的属性

 size:set结构的元素个数,与数组中length是一样的。


Set结构的应用:


如何去掉一个数组中重复的元素

// 封装数组去重的方法:
// 参数:
//   数组
// 返回值:去重后的数组
function noRepeat(arr){
    let s1 = new Set(arr);
    let arr2 = [];
    for(let i of s1){
        arr2.push(i);
    }
    return arr2;
}


Map

​ Map结构提供了“键—值”的对应,是一种更完善的Hash(哈希)结构(键值对的集合)实现。如果你需要“键值对”的数据结构,Map比Object更合适。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map结构中的键是唯一的。


1、Map使用:

//1)、定义map
var m = new Map();
//2)、给map中增加或者修改数据
map对象.set(键,值);

m.set(1,{id:"1",name:'张三'});键是数字类型,值是对象
m.set(2,{id:"2",name:'张四'});键是数字类型,值是对象

//3)、根据键获取map中的数据
map对象.get();   
console.log(m.get(2));

键可以是对象类型的数据。

	var m = new Map();
	var o1 = {"id":"001"};
	var o2 = {"id":"002"};

	m.set(o1,{id:"1",name:'张三'});键是对象,值是对象
	m.set(o2,{id:"2",name:'张四'});键是对象,值是对象	
    
	console.log(m.get(o2));	


Map的方法:

set:给Map对象增加一个键值对,如果,存在相同的键,就会覆盖
get:根据键获取值,
delete:根据键删除Map对象中的某对键值对。
has:判断Map对象中是否存在某个键
keys:获取Map结构的所有的键。返回值是迭代器
values:获取Map结构中的所有的值。返回值是迭代器


Map的属性:

size:Map结构中的数量



9、新增的循环for of


1、以前for循环:for in循环 (for in循环:既可以循环数组,又可以循环json)

2ES6新增循环:for of 循环:遍历(迭代)整个对象,

https:blog.csdn.net/jiang7701037/article/details/94739725 



作业:

1. 封装拖拽效果

2. 封装常见兼容



第十五天、DOM高级(应用)



本节课目标:

运动原理

匀速运动,边界处理

加速减速

透明度运动(淡入淡出)

多属性缓冲运动函数封装

圆周运动



一、运动的原理


物体运动原理:

​ 通过改变物体的位置(尺寸,颜色,透明度等等),而发生的变化。


要素:

​ 起始位置,方向,速度(步长和频率),终点位置


思路:

​ 1、已知:dom元素,起始位置,终点位置,方向,步长,时间间隔

​ 2、启动定时器

​ 3、按照方向和步长改变dom元素的样式属性

​ 4、判断停止条件,清除定时器。



二、匀速运动,边界处理

使用定时器修改left或top值,每次增加固定的数值(速度),实现匀速运动。


运动填坑:

1,运动不会停止:移动位置不可用==来判断,因为可能(位置+速度)会越过目标值。

2,不能停到位置:停止时直接修改元素位置为目标位置。

3,每次点击都会加速:设定定时器前先清除定时器。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
    #box{
        position: absolute;
        left:80px;
        top:100px;
        width:400px;
        height: 150px; 
        background-color:red;
    }
    </style>
</head>
<body>
    <input id="btn01" type="button" value=" 走起来 " />
    <div id="box">

    </div>
</body>
</html>
<script>
// $("btn01").onclick = function(){
//     let boxDom = $("box");
//     let width = 200;
//     let myTimer = setInterval(function(){
//         //一、处理数据
//         //1、计算数据
//         width+= 1*7;
//         //2、边界处理(数据的合法性)
//         if(width>=500){
//             width = 500;
//             window.clearInterval(myTimer);
//         }

//         //二、改变外观
//         boxDom.style.width = `${width}px`;

//     },20);
// }

$("btn01").onclick = function(){
    let boxDom = $("box");
    let width = 400;
    let myTimer = setInterval(function(){
        //一、处理数据
        //1、计算数据
        width += -1*7;
        //2、边界处理(数据的合法性)
        if(width<=200){
            width = 200;
            window.clearInterval(myTimer);
        }

        //二、改变外观
        boxDom.style.width = `${width}px`;

    },20);
}

function $(id){
    return document.getElementById(id);
}

</script>



三、变速运动

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
    #box{
        position: absolute;
        left:80px;
        top:100px;
        width:100px;
        height: 150px; 
        background-color:red;
    }
    </style>
</head>
<body>
    <input id="btn01" type="button" value=" 走起来 " />
    <div id="box">

    </div>
</body>
</html>
<script>
$("btn01").onclick = function(){
    let boxDom = $("box");
    let width = 100;
    let step = 0.5;
    let myTimer = setInterval(function(){
        //一、处理数据
        //1、计算数据
        width+= step;
        step++;

        //2、边界处理(数据的合法性)
        if(width>=1000){
            width = 1000;
            window.clearInterval(myTimer);
        }

        //二、改变外观
        boxDom.style.width = `${width}px`;
    },20);
}

function $(id){
    return document.getElementById(id);
}


</script>



四、透明度运动(淡入淡出)



五、多属性缓冲运动函数封装



六、圆周运动



七、轮播图



作业:

1、课堂案例

2、侧边栏分享广告

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y7G6750F-1641803458652)(C:Users31759AppDataRoamingTyporatypora-user-imagesimage-20210903224936498.png)]

扩展:

​ 抛物线运动



第十六天、面向对象



本节课目标:

面向对象的概念和特点

面向对象里的名词:类和对象

用构造函数的方式创建对象

用ES6类的方式创建对象

用Object的方式创建对象

用json的方式创建对象

面向对象编程和面向过程编程的区别

示例



一、面向对象的概念和特点

https://blog.csdn.net/jiang7701037/article/details/79053238



1、概念:

​ 面向对象是一种编程思想,使用面向对象编程时,最基本的数据单位是对象,而不是基本类型。也是从人类的思维习惯转变过来的一种思维。



2、特点:

​ 封装、继承、多态。

​ 封装:把一堆相关的数据和函数封装在一起形成一个有机的类。

​ 继承:子类自动 拥有父类的所有属性和方法。继承是程序复用性的体现

​ 多态:解决同一个问题可以有不同的做法。



二、面向对象的名词



1、类

​ 就是分类,类型,数据类型,类是复杂的数据类型,包括了很多信息的数据类型。如:人,猪,狗等等。

  • JavaScript官方提供Array,Date,RegExp等都是由Object衍生出来的类型。Object类型是一个抽象的类型,就像生活中的名词“东西”;而Array,Date,RegExp才是具体的类型。

  • 除了官方定义的类型,在实际开发中,特别是面向对象的开发中,程序员需要定义很多的数据类型(自定义类型)来满足实际的需求。



2、对象

​ 就是变量,是类的实例,即类的一个示例,类的一个举例说明。如:诸葛亮是人的一个示例,大黄是狗的一个举例说明。

我们在实际开发中,会定义若干个自定义类型(类),然后,再根据类来定义变量。



3、类和对象的关系:

类是对象的抽象,如:人,医生,猪,狗等

对象是类的具体,如:宝宝,小花,大黄等等



三、ES3和ES6中的概念(类,对象,实例)、

  • ES3中对象:就是ES6中的类

  • ES6中实例:就是ES6中的对象

​ 类和对象是传统面向对象编程语言的名词,ES6之前,Javascript没有类的概念,从ES6开始,javascript开始有了类的叫法,也是为了朝着传统面向对象编程语言(java,c++,c#等等)的概念靠拢。

​ 以后的沟通中,建议大家多说类和对象。也可以偶尔说说实例。



四、定义类:



1、用构造函数的方式创建类

//定义构造函数(也叫类)
function Person(id,name){
	//属性:
	this.id = id;
	this.name = name;
	//方法:
	this.eat = function(str){	
        alert(this.name+"在吃"+str); 	
    }
}

用构造函数实例化一个实例(对象)
let p1 = new Person("007","宝宝");
let p2 = new Person("008","宝宝的宝宝");

1、ES3的构造函数的缺点:

1)、造成内存的浪费:

​ 如果把类的方法写在构造函数里面,就会造成内存的浪费(每调用一次构造函数,都会定义一个类的方法)

2)、语法格式有点不利于阅读。

如果用构造函数和prototype(第六周时会给大家解惑)配合起来定义类的方式,不会造成内存的浪费,但是,可阅读性不好。



1、构造函数和普通函数的区别:

1)、函数名的首字母大写

2)、构造函数的调用,是用new运算符。

3)、构造函数内部,在预编译时,还会增加 var this = new Object();

4)、构造函数没有返回值,但是,其实是有的。即:虽然程序员没有写返回值,预编译时,会增加返回值

​ 预编译时,会增加一句:return this。



2、构造函数的执行过程(面试时有时会问):

1、立刻在堆内存中申请空间,这个空间就是新的对象的内存空间

2、将新建的对象设置为函数中的this

3、逐个执行函数中的代码

4、将新建的对象作为返回值 return this



3、判断一个对象是不是某个构造函数(类)创建的

​ instanceof

console.log(p1 instanceof Person);

​ 数组也可以:

let arr = new Array();

console.log(arr instanceof Array);//这个存在问题是:如果在某个框架里,自己定义了构造函数Array,那么,就会出现歧义,这个需要后期再去理解。

console.log(Array.isArray(arr));//ES5新增的判断数组的方法是没有问题的。



2、用ES6类的方式创建类

//用class来代替构造函数
class Person{
	constructor(id,name){
		//属性
		this.id = id;   
        this.name = name;
	}	
	eat(str){     
        alert(this.name+"在吃"+str); 	
    }
}
//实例化一个实例(对象)
let p1 = new Person("007","宝宝");
let p2 = new Person("008","宝宝的宝宝");

ES6中的class是个语法糖,即在ES3的构造函数的基础上,增加了一个外包装。本质还是构造函数,只不过,class的写法和传统面向对象编程语言的写法一致。



五、定义对象



1、用官方类Object的方式创建对象

​ Object是官方的类,是一切类的父类,是一个没有具体意义的类。抽象的类。Object的英文的单词的解释中有“东西”的意思。你知道不知道,世界的一切都可以叫“东西”(哈哈)

let o1 = new Object();
o1.name = "张三疯";
console.log(o1);



2、用json的方式创建对象

json对象就是

new Object()的简写

,字面量的写法。相当于 [] 是 new Array()的简写、//是newRegExp()的简写

 var p1 = {
     "id":"007",
     "name":"张浩清"
 }
 

创建对象的两种方式:

1、构造函数的方式:new Object();

2、简写(字面量)的写法:json对象。

json定义对象的缺点:

1、代码复用性不好

2、没法确定若干个对象是否属于同一类别。而构造函数可以使用instanceof进行判断



3、自定义类创建对象

let 对象名  = new 类名();



总结:

类:官方类 Object,Array,Date。也可以自定义类(ES5构造函数的写法,ES6中class的写法)

对象:用类new出来的。

​ 如: let o1 = new Object(); //json是定义对象的简写。



六、面向对象和面向过程的区别



1、相同点:都是编程的思想(思维)

在完成项目时,使用不同的思维完成项目的功能。



2、不同点:



2.1 、从编程的步骤:


1)、面向过程(函数):

​ 做小的项目时,会使用面向过程的思路完成

​ 第一步:先思考项目的流程,流程中的每一步就是一个模块(功能:就是个函数)。

​ 第二步:我们实现每个函数(定义每个函数),在定义函数时,就需要考虑到数据(形参或者变量)。


2)、面向对象:

​ 做大型项目时,会使用面向对象的思路完成。

​ 第一步:要考虑的是项目中需要的类(属性和方法)。

​ 第二步:实现流程(new出对象,调用对象的方法)

​ 第一步做什么,第二步做什么。



2.2、从描述和理解上


1)、面向过程:

​ 在面向过程里,每个函数和变量没有所属。所以调用函数时,前面都没有对象。

​ 所以,在面向过程里,描述时,没有主语,如,先干啥,再干啥

​ 面向过程相当于: 单干(一个人做事情)


2)、面向对象里:

​ 在面向对象里,每个函数(方法)和变量(属性)都有所属,调用函数时,前面有对象。

​ 所以,在面向对象里,描述时,有主语,如,谁先干啥,谁再干啥

​ 面向对象相当于: 团队配合(多个人做事情)



九、示例:

1、弹力球



十、this的理解及其指向

this是代名词,代表谁呢?要看场景。this只能出现在函数里,函数就是this的场景。

1、当this所在函数是事件处理函数时,this表示事件源

2、当this所在函数是构造函数时,this表示new出来的对象

3、当this所在函数是类的方法时,this是调用该方法的对象(写在调用方法前面的那个对象)

4、当this所在函数不是以上情况时,this时window对象。

​ 其实就是第三种情况。因为,window对象是网页里所有的数据的根对象,只要函数没有所属的对象,那么,就一定是window对象的属性。

注意:以上所说的函数不能是箭头函数,因为,箭头函数里没有this。既就是:判断this的指向时,忽略掉箭头函数。



第十七天、面向对象案例

1、轮播图

​ 面向过程的轮播图改造成面向对象的轮播图:

把面向过程的代码改造面向对象的注意点:

1、面向过程的全局变量,在面向对象中是属性

2、初始化的过程,全部放在构造函数里

3、面向过程的函数,在面向对象中去掉function关键字

4、面向对象的方法中,使用全局变量的地方,前面加上this。这时候,就需要注意this的指向问题。

2、购物车(localStorage)

​ // localStorage是本地(浏览器端)保存数据的对象。

​ // setItem:保存数据的。localStorage.setItem(“键”,值)

​ // getItem:获取数据的。localStorage.getItem(“键”)



第十八天、面向对象案例

​ 1、评分特效

​ 2、放大镜

​ 3、瀑布流



第十九天、复习



第二十天、考试



第二十一天、PHP+mySQL



本节课目标:



1、php

​ web服务器介绍

​ PHP介绍

​ PHP环境搭建

​ PHP语法



PHP与前端交互(重点)



2、mysql

 数据库的概念

	mySQL的使用

​ 创建数据库和表

​ 增删改查的SQL语句

​ PHP连接mySQL(重点)



一、web服务器介绍



1、web服务器介绍

​ 以前写的网页,别人是访问不了的,只能自娱自乐。如果想众乐乐,那么就得用web服务器。

​ 1)、本地web服务器:自己的计算机或者在局域网内的计算机都可以访问。

​ 2)、互联网web服务器:又叫www服务器,全世界的计算机都可以访问(需要域名)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b59Zt2Ip-1641803458652)(imgimage-20210911132344080.png)]



2、web服务器的作用

​ 1)、完成(接收)请求和响应

​ 2)、执行后端的代码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6C65w6K4-1641803458652)(imgimage-20210911132416831.png)]



二、 PHP介绍


  • PHP是服务器端语言

​ 服务器端除了PHP还有,JSP(java),ASP,python,nodeJS,go等等。

​ PHP:personal home page;


  • PHP优点:

    跨平台(linux,windows) ,同时支持多种数据库

    安全性和效率好

    使用成本低 ( linux apache mysql PHP内核)

    相对jsp ,和 asp.net 简单

    开发源码(可以做二次开发) / 开源软件


  • 缺点:


    安装比较复杂,配置比较多, 太灵活



三、PHP环境搭建(phpStudy )



介绍

​ phpStudy是搭建服务器的工具,集成了apche和mySQL。我需要把网站的代码放在phpStudy工具里的“www”目录下,“www”目录就是服务器的目录,即放置项目代码的目录。

​ 如:把index.html文件拷贝至www下,则用服务器的方式打开index.html的做法是:在浏览器中输入 http://ip地址/index.html



访问举例:

​ 假如:www的全路径是:D:phpStudy_proWWW,本机的ip地址是:10.35.165.10

​ 那么:在局域网内的任何一台计算机的浏览中输入:http://10.35.165.10就相当于,在找

​ D:phpStudy_proWWW下的文件

​ 注:自己的机子访问自己的服务器地址栏中输入:localhost或者127.0.0.1



硬盘访问和服务器访问的对比

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g4EldJdH-1641803458653)(imgimage-20210911132643985.png)]



四、PHP语法



1、PHP的简单了解


1)、文件扩展名: php



2)、代码写在<?php 与 ?>之间

如:
 <?php
  	echo "hello,world";
 ?>


3)、 PHP代码每句话必须以分号结束


4)、 PHP的注释:


单行用 // 或 #

多行用 /* */


5)、PHP的变量

以**$**符号打头,变量名区分大小写

如:$age = 250;


6)、php是弱类型语音

​ php 的变量的数据类型,是变化的,php变量的数据类型是由运行时的上下文决定。

:    $a = 90; //相当于我们定义了一个变量 $a,并赋值90,是数字型
	  $a =”hello”; //表示$a 的类型变化,是字符串型



2、PHP的数据类型

有三大类


  1. 基本数据类型


    1.1 整型

    1.2 小数型(浮动数) 包含单精度和双精度

    1.3 布尔类型 (表示真 和 假)

    1.4 字符串


  2. 复合数据类型

    2.1 数组(array)

    2.2 对象 (object)


  3. 特殊数据类型


    3.1 null

示例:

■ 整型
       整数类型可以表示一个整数,比如:
       $ts=1;  $ts=-12;
■ 布尔类型
       布尔型可以表示真或者假,比如:
       $ts=true;  $ts=false;
■ 小数类型
       小数类型可以表示一个小数(float,double)
       $ts=1.12   $ts=3.1415926
■ 字符串类型
       字符串类型可以表示一个字符串(用单(双)引号括起来)
       $ts=“我是一个好人 ”



3、PHP的运算符

  • 算术运算符: + – * / % ++ –

  • 关系:> < <= >= != == === !==

  • 逻辑运算符: && || !

  • 复合赋值运算符: += -= 等


  • 字符串连接用 “.”


    如:

$age = 20;
$str=“我今年”.$age.”岁了”;



4、循环:(break,continue)

while
do while
    
for(循环初值; 循环的条件; 步长){
      
}

for($i=0;$i<10;$i=$i+2){
 
}



5、PHP的函数


1)、定义函数

function testFunc($num1,$num2){
  $res = $num1+$num2;
  return $res;
} 


2)、调用函数

$res1= testFunc (250,40);
echo "计算结果是=".$res1; 



6、PHP的数组(创建)


1)、PHP的数组分类


值数组

​ 每个元素存储的是数据


关联数组:

​ 每个元素是一对键值对



2)、两种数组的定义方式



2.1)、值数组



第一种:直接写

 $arr[0]=123;
 $arr[1]="hello";
 $arr[2]=45.6;
 $arr[3]=true;
 $arr[4]=null;//即使放入了一个null,也要占一个空间


第二种:用array函数

$数组名 = array(,.......);

如: $arr = array(1,90,"helllo",89.5);    


2.2)、关联数组

  **第一种:直接写**
$ages['threeFeng']="35";
$ages['fourFeng']="36";



第二种:用array函数

$ages = array("threeFeng"=>"35","fourFeng"=>"36");



访问关联数组的元素

```php

echo $ages[‘threeFeng’]

“`



3)、PHP的数组的元素个数



count函数


$arr=array(1,90,"helllo",89.5);

echo "数组大小".count($arr); 


4)、遍历数组

值数组 
   $arr=array(1,90,"helllo",89.5);

    for($i=0;$i<count($arr);$i++){
        echo $arr[$i];
    }

关联数组
$age=array("threeFeng"=>"35","fourFeng"=>"36","fiveFeng"=>"47");

 foreach($age as $key=>$value) {
        echo "Key=" . $key . ", Value=" . $value;
        echo "<br>";
 }



7、PHP的对象(类与对象)

定义一个类:
class Person{
    
     public $name;//属性
     public $age;
    
     public function speak(){
        echo $this->name."is a good man!";
     }
    
}

实例化对象,并使用:
    $p1 = new Person();
    $p1->name="tianwater";
    $p1->age=3;
    $p1->speak();



五、

PHP与前端交互(重点)



1、PHP接收前端的数据



1)、get请求的数据
 $_GET['参数名‘] ;// 参数名:如果是form表单提交的,那么就是表单元素的name属性的值

如:

$_GET['username']获取username

$_GET['password']获取password


2、post请求的数据

$_POST['参数名‘]

3、所有请求类型的数据:

 $_REQUEST["参数名"];



2、PHP给前端响应数据

 echo
如:echo”我来自服务器端”;




3、请求参数有中文的解决方案:

在php文件的第一行,增加以下代码:
header("Content-type: text/html; charset=utf-8");



4、网页生成原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S6VhwdKO-1641803458653)(imgimage-20210911133624301.png)]



5、生成动态网页

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>显示年龄</title>
</head>
<body>
    <h1>各位的年龄</h1>    
    <?php
        $age=array("张三疯"=>"35","张四疯"=>"36","张五疯"=>"47");
    
        foreach($age as $key=>$value)
        {
            echo "Key=" . $key . ", Value=" . $value;
            echo "<br>";
        }
    ?>    
</body>
</html>



6、返回json格式的数据

json_encode(): 主要用来将数组和对象,转换为json格式。


    $arr = array('a'=>1,'b'=>2,'c'=>3,'d'=>4,'e'=>5);

echo json_encode($arr);

class Person{
        public $name="tianwater";//属性
        public $age=12;
 }

 $p1 = new Person();
  echo json_encode($p1);



六、数据库的概念


1、数据库概念:


数据库(Database)是按照数据结构来组织、存储和管理数据的仓库。


2、库:仓库



3、表:一个仓库被分为了许多部分,很像类



4、字段:很像类的每个属性。


每个字段的数据类型:

如:

int —-> 整数

varchar —–>(可变长度)字符串

​ char —->(不可变长度)字符串

​ blob —–> 二进制数据

​ date —–> 日期


5、常见的数据库


1)、关系型数据库:


Oracle、

MySQL

、SQLServer、DB2、sybase等等


2)、非关系型的数据库:


Redis,Tokyo Cabinet,Cassandra,Voldemort,

MongoDB

,Dynomite, HBase,CouchDB,Hypertable, Riak,Ti,



七、mySQL的使用


1、mySQL简单介绍

​ mySQL是一种开源的数据库软件,市场占有率很高,现在属于Oracle公司。


2、使用工具:


phpStudy

点击 MySQL管理器–》选择 MySQL-Front



八、创建数据库和表

进入 SQL编辑器



1、创建数据库:

​ 可视化:点击鼠标右键

​ 代码:

 create database 数据库名

如: create database db20170203



2、打开某一个库 use db20170203;



3、创建表:

create table students(
     id int not null   primary key,-- primary key表示主键(唯一性)
     name varchar(8) not null, 
     sex char(4) not null, 
     age tinyint not null, 
     tel char(13) null default "-"  -- 默认值 是  -
);       



九、增删改查的SQL语句



1、增(insert):

insert [into] 表 名 [(列 名 1, 列 名 2, 列 名 3, ...)] 
                values (1,2,3, ...);

如:
   insert into students (name, sex, age) values("孙丽华", "女", 21);



2、查询( select ):

select 列名称 from 表名称 [查询条件];

如:
 select name, age from students; 
 select * from students; 
 select * from students where age > 21 ; 
 select * from students where name like "%王%"; --模糊查询
 select * from students where id<5 and age>20;       



3、删除( delete):


 delete from 表名称 where 删除条件; 

如:
 delete from students; 删除表中的所有数据,
 delete from students where id=2; //删除表中,id是2的数据。
 delete from students where age<20;        



4、修改( update):

update 表名称 set 列名称=新值 where 更新条件;
如:
 update students set tel=110 where id=5;
 update students set age=age+1;
 update students set name="张伟鹏", age=19 where tel="13288097888";;   



扩展练习示例:

create table EMP
(
    EMPNO numeric(4) PRIMARY KEY,
    ENAME VARCHAR(10),
    JOB VARCHAR(9),
    MGR numeric(4),
    HIREDATE DATE,
    SAL numeric(7,2),
    COMM numeric(7,2),
    DEPNO numeric(4)
);
CREATE TABLE Dept(
    DEPTNO numeric(4),
    DNAME VARCHAR(14),
    LOC VARCHAR(13)
);
CREATE TABLE Salgrade
(
    GRADE numeric,
    LOSAL numeric,
    HISAL numeric
);


INSERT INTO Dept VALUES (10,'ACCOUNTING','NEW YORK');
INSERT INTO Dept VALUES (20,'RESEARCH','DALLAS');
INSERT INTO Dept VALUES (30,'SALES','CHICAGO');
INSERT INTO Dept VALUES (40,'OPERATIONS','BOSTON');
INSERT INTO EMP VALUES(7369,'SMITH','CLERK',7902,'1980-12-17',800,null,20);
INSERT INTO EMP VALUES(7499,'ALLEN','SALESMAN',7698,'1981-02-20',1600,300,30);
INSERT INTO EMP VALUES(7521,'WARD','SALESMAN',7698,'1981-02-22',1250,500,30);
INSERT INTO EMP VALUES(7566,'JONES','MANAGER',7839,'1981-04-02',2975,NULL,20);
INSERT INTO EMP VALUES(7654,'MARTIN','SALESMAN',7698,'1981-09-28',1250,1400,30);
INSERT INTO EMP VALUES(7698,'BLAKE','MANAGER',7839,'1981-05-01',2850,NULL,30);
INSERT INTO EMP VALUES(7782,'CLARK','MANAGER',7839,'1981-06-09',2450,NULL,10);

INSERT INTO EMP VALUES(7844,'TURNER','SALESMAN',7698,'1981-09-08',1500,0,30);
INSERT INTO EMP VALUES(7900,'JAMES','CLERK',7698,'1981-12-03',950,NULL,30);
INSERT INTO EMP VALUES(7902,'FORD','ANALYST',7566,'1981-12-03',3000,NULL,20);
INSERT INTO EMP VALUES(7934,'MILLER','CLERK',7782,'1982-01-23',1300,NULL,10);
INSERT INTO SALGRADE VALUES (1,700,1200);
INSERT INTO SALGRADE VALUES (2,1201,1400);
INSERT INTO SALGRADE VALUES (3,1401,2000);
INSERT INTO SALGRADE VALUES (4,2001,3000);
INSERT INTO SALGRADE VALUES (5,3001,9999);


# 要求列出每个雇员的姓名及年薪
          SELECT ename,sal*12 FROM emp;
# 查看每月可以得到奖金的雇员信息
          SELECT * FROM emp WHERE comm is NOT NULL;
#要求基本工资大于1500,同时可以领取奖金的雇员信息
          SELECT * FROM emp WHERE sal>1500 AND comm is NOT NULL;
#查询在1981年雇佣的全部雇员信息,BETWEEN .. AND 包含等于的情况
          SELECT * FROM emp WHERE hiredate BETWEEN '01-JAN-81' AND '31-DEC-81';
#要求查询出雇员编号不是 7369、7499的雇员信息
          SELECT * FROM emp WHERE empno NOT IN(7369,7499);
#SQL中LIKE语句要注意通配符 % 和 _  
         SELECT * FROM emp WHERE hiredate LIKE '%81%‘;
#要求对雇员的工资由低到高进行排序,升序为默认(ASC),降序(DESC)
          SELECT * FROM emp ORDER BY sal  desc;

#找出佣金高于薪金的60%的员工
       SELECT * FROM emp WHERE comm>sal*0.6

#找出部门10中所有经理(MANAGER)和部门20中所有办事员(CLERK)的详细资料
       SELECT * FROM emp WHERE (deptno=20 AND job='CLERK') OR (deptno=10 AND job='MANAGER');

#找出既不是经理又不是办事员但其薪金大于或等于2000的所有员工的资料
       SELECT * FROM emp WHERE job NOT IN('MANAGER','CLERK') AND sal >= 2000;

#显示不带有"R"的员工的姓名
       SELECT ename FROM emp WHERE ename NOT LIKE '%R%';




十、PHP连接mySQL



1、连接数据库 mysqli_connect();


语法:


mysqli_connect(servername,username,password,dbname);


参数描述


servername :规定要连接的服务器。默认是 “localhost:3306”。

username:规定登录所使用的用户名。默认值是拥有服务器进程的用户的名称。

password:规定登录所用的密码。默认是 “”。

dbname:表示数据库名


示例

	
$con=mysqli_connect("localhost","root","root","qianfeng");// $con是连接对象

	if (!$con) {	
        die(‘Could not connect:. mysql_error());
	}else{     
        echo('连接成功');	
    }



2、 关闭数据库 mysqli_close()

​ 脚本一结束,就会关闭连接。如需提前关闭连接,请使用 mysqli_close(); 函数


语法:


​ mysqli_close(连接对象变量);


示例:

$con=mysqli_connect("localhost","root","root","qianfeng");// $con是连接对象
	if (!$con){	    die(‘Could not connect:. mysql_error());
	}else{	echo('连接成功');   }
	………………………………
	mysqli_close($con);	



3、执行SQL语句



1)、增: mysqli_query()
$conn=mysqli_connect("localhost","root","root","qianfeng");// $con是连接对象


if (!$conn){	 
    die(‘Could not connect:. mysql_error());
}else{	
	//给student表中插入数据
	$sql = "insert into student values('xa20160801','刘德华',22,'男')";	
    if (mysqli_query($conn, $sql)) {
           echo "新记录插入成功";
    } else {
           echo "Error: " . $sql . "<br>" . mysqli_error($conn);
    }	
   
	mysqli_close($conn); 
}


2)、 查:
$sqlstr = “select * from vipinfo “;

$result = mysqli_query($conn,$sqlstr);//执行查询的sql语句后,返回的是查询结果

//查询列数  $query_cols = mysqli_num_fields($result)
//查询行数  $query_num = mysqli_num_rows($result);

$str="[";
$query_row = mysqli_fetch_array($result);//游标下移,拿出结果集中的某一行,返回值是拿到的行;

while($query_row){        
    $str = $str.'{"username":'.$query_row[0].',"userpass":'.$query_row[1].'}';
    $query_row = mysqli_fetch_array($result);
    if($query_row){  
        $str = $str.",";   
    }
}

$str = $str."]";


注册示例:

<h5>欢迎亲的注册:</h5>
<form action="regSave.php" method="get">
    用户名:<input type="text" name="username" value="wcx" /><br/>
    密码:<input type="password" name="userpass" value="123" /><br/>
    重复密码:<input type="password" /><br/>
    <input type="submit" value="注册" />
</form>


解释:

当使用form表单的方式给后端发送请求时。

1、get方式:

​ 当点击submit按钮时,

​ 1)、组织请求的数据(发给后端的数据)

​ 浏览器会把form表单(submit按钮所在的form表单)里的表单元素进行处理:把每个表单元素的name属性的值,作为键;表单元素的值作为值,拼接成 形如:key1=value1&key2=value2 的字符串。

​ 如:以上代码的结果就是: username=wcx&userpass=123

​ 2)、发送请求

​ 把以上拼接好的字符串跟在url(regSave.php)的后面,用问号隔开。然后发送请求

​ 如:以上代码的结果:regSave.php?username=wcx&userpass=123

​ 3)、后端php接收数据:

​ $_GET[‘键’]

​ 如以上代码,就是这样写 $_GET[‘username’]



作业:

1、完成注册,登录功能(完整的,从页面到数据库)



第二十二天、AJAX



本节课目标:

同步和异步

AJAX的概念及优势

AJAX中的核心

XMLHttpRequest

的使用

AJAX编写步骤

示例



一、同步和异步



1、什么是异步?

​ 同时执行,也叫并发



2、什么是同步?

​ 按步骤顺序执行

同步示意图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3NLL1kGe-1641803458654)(imgimage-20210912235446331.png)]

异步示意图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ePEVw461-1641803458654)(imgimage-20210912235514922.png)]



二、AJAX的概念及优势



1、什么是AJAX

AJAX(Asynchronous JavaScript And XML)异步 JavaScript 和 XML,中文名:阿贾克斯。

AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术

​ 前端通过与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。



2、AJAX的优势

​ 更自然、流畅的用户体验,对用户的操作即时响应

​ 在不中断用户操作的情况下与Web服务器进行通信

​ 更灵敏的响应用户访问,实现近似于桌面应用程序的交互效果

​ 通过局部更新页面降低网络流量,提高网络的使用效率



三、AJAX中的核心

XMLHttpRequest

的使用



1、XMLHttpRequest对象

​ AJAX的核心对象是XMLHttpRequest,和服务器 交互主要依赖该对象。 这是官方对象。

​ XMLHttprequest对象提供了对 HTTP 协议的完全的访问



2、XMLHttpRequest的理解:

​ 浏览器

负责显示页面

和发送请求接收响应(

和后端交互

)。两件事情同一时刻只能做一件,没法同时进行。这样会让用户感觉不好(友好性不好),比如:当浏览器发送请求时,就没法显示内容,这时浏览器中是空白显示,给用户的感觉不好。

​ 使用XMLHttpRequest对象,可以把浏览器解脱出来,让浏览器只负责显示,而完成和后端交互的事情由XMLHttpRequest对象负责。这样两者各负其责,效率更高,效果更好,用户体验很好,用户永远不会看到浏览器空白。



3、XMLHttpRequest的属性



1)、readyState

对象状态值: 也就是请求和响应整个过程中进行到哪一步了。

0 = 未初始化(uninitialized)对象创建完毕就是0

1 = 正在加载(loading) 装载,准备工作,调用open函数后就是1,

2 = 加载完毕(loaded) 表示到了后端。

3 = 交互(interactive)服务器在处理……

4 = 完成(complete) 服务器端处理完毕了。表示响应完成了,但是,不保证结果是否正确



2)、 onreadystatechange

​ 每次状态改变所触发事件的事件处理程序



3)、responseText

​ 从服务器进程返回的数据的字符串形式



4)、 status

​ 从服务器返回的数字代码,如404(未找到)或 200(就绪)

​ 代表响应结果。

​ 200:表示响应正确

​ 500:表示服务器端出错,拿不到应有的结果

​ 404:表示你请求的资源不存在。



4、XMLHttpRequest的方法



1)、open(”method”,”URL”,是否异步)

​ 建立对服务器的调用,

​ 方法通常是post或get,URL可以绝对路径或相对路径



2)、send(content)

向服务器发送请求,参数可以是null



3)、setRequestHeader(”header”, ”value”)

设置HTTP请求的首部信息,可以向服务器传递参数,这个方法必须在open之后调用。



5、HTTP的状态码

200 服务器已成功处理了请求并提供了请求的网页

404 服务器找不到请求的网页

500 服务器遇到错误,无法完成请求



四、AJAX编写步骤



1、创建XMLHttpRequest对象

var xhr = new XMLHttpRequest();



2、设置请求参数

需要说清楚,找后端的哪个文件,请求方式,是否异步

xhr.open("get", "checkUser.php", true);



3、设置回调函数

xhr.onreadystatechange = function(){
	if(xhr.readyState == 4  && xhr.status==200) {
		console.log(xhr.responseText);
	}
}



4、发送请求

xhr.send();



5、接收响应

xhr.responseText或者xhr.responseXML



五、get请求和post请求的区别



1、概念,作用的区别



1)、相同点:

​ get和post就是客户端给服务器传输数据的方式。



2)、不同点:

​ get: 速度快,传输的数据量小,安全性不好

​ post:速度慢,传输的数据量大,

安全性好



2、ajax使用中的区别:

1)、get请求参数,直接跟在url后面,用问号隔开。多个键值对之间用&分割。

如:

 xhr.open("get", "http://localhost/a.php?name=zsf&age=12", true);

2)、post请求的参数放在send函数里(是send函数的参数),也是键值对的方式(如:name=zsf&age=12)。并且还要设置请求头

如:

  xhr.open("post", "http://localhost/a.php", true);

  xhr.setRequestHeader(“content-type”,”application/x-www-form-urlencoded”)

  xhr.send(“name=zsf&age=12);



六、 示例

1)、验证用户名是否存在

2)、搜索提示框

3)、加载更多按钮(分页),阅读全文。

4)、AJAX瀑布流 从后端获取的数据格式:[“img/1.jpg”,”img/2.jpg”]



作业

必做:

  1. 显示学生信息

  2. 省市联动

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tbsv8sXL-1641803458654)(imgimage-20210913000151793.png)]

扩展:

  1. 用客户端的JSON数据模拟显示销售数据

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vgjFY8tK-1641803458654)(imgimage-20210913000142911.png)]



第二十三天、http+cookie



本节课目标:

HTTP

cookie的概念

cookie的特点

cookie的使用

cookie的封装

简单购物车的应用



一、HTTP

HTTP:Hyper Text Transfer Protocol,超文本传输协议,是一个请求响应的协议,是无状态的协议。指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。规定了请求和响应过程中的约定:

https://blog.csdn.net/jiang7701037/article/details/118500982


1、请求:



请求报文

的内容包括三部分,分别是:

请求行



请求头



请求体

。这三部分放的都是信息。


2、响应:



响应报文

的内容也包括三部分,分别是:

响应行



响应头



响应体

。这三部分放的都是信息


常见响应状态码


200 OK 响应成功


400 Bad Request //客户端请求有语法错误,不能被服务器所理解


401 Unauthorized //请求未经授权,这个状态代码必须和WWW-Authenticate


报头域一起使用


403 Forbidden //服务器收到请求,但是拒绝提供服务


404 Not Found //请求资源不存在,输入了错误的URL


500 Internal Server Error //服务器发生不可预期的错误


503 Server Unavailable //服务器当前不能处理客户端的请求,一段时间后可能恢复正常


3、url地址格式:

​ http://host:port/abs_path ?请求参数#hash

host 主机名,对应IP地址的一个点或一段;

port 端口号 ;

abs_path 主机上的资源 路径

? 后面的是get请求方式的参数



后面是hash

扩展:

请求和响应消息的头以

ASCII

形式给出;而消息内容则具有一个类似

MIME

的格式

MIME:

​ MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型。是设定某种

扩展名



文件

用一种

应用程序

来打开的方式类型,当该扩展名文件被访问的时候,

浏览器

会自动使用指定应用程序来打开。



二、cookie



1、概念:

​ cookie是浏览器提供的一种机制,它将

document 对象的cookie属性

提供给JavaScript。可以由JavaScript对其进行控制,而并不是JavaScript本身的性质。

​ cookie是存于用户硬盘的一个文件,这个文件通常对应于一个域名,当浏览器再次访问这个域名时,便使这个cookie可用。因此,cookie可以跨越一个域名下的 多个网页,但不能跨越多个域名使用。不同的浏览器对cookie的实现也不一样,但其性质是相同的。

​ cookie 是存储于访问者的计算机中的变量。每当同一台计算机通过浏览器请求某个页面时,就会发送这个 cookie。你可以使用 JavaScript 来创建和取回 cookie 的值。



2、cookie 作用



1)、识别客户端

​ http是无状态的协议,为了让服务器端能够识别客户端,客户端使用cookie,服务端使用session。

​ 具体过程:当浏览器首次发送请求时,服务端会产生一个唯一的编号(sessionId)。响应时,把编号(sessionId)发给浏览器端,浏览器端把接收到sessionId保存在cookie里。下次请求时,把sessionId携带上。服务器根据携带的sessionId,就能区分不同的客户端。

​ 亲:这个过程是原理性知识。不需要程序员写任何代码。



2)、保存数据

​ cookie可以在客户端保存数据(这是今天要讲的重点)

​ cookie会把数据存储在浏览器端的硬盘上



3、cookie的使用场景

​ 1)、一周内免登录

​ 2)、日期 cookie

​ 当我们访问某些网站时,首页会显示:“你上次访问的时间是:2016.03.21”。日期是在 cookie 中保存着

​ 3)、跟踪用户行为。

​ 例如一个天气预报网站,能够根据用户选择的地区显示当地的天气情况。如果每次都需要选择所在地是烦 琐的,当利用了 cookie后就会显得很人性化了,系统能够记住上一次访问的地区,当下次再打开该页面时,它就会自动显示上次用户所在地区的天气情况。因为一切都是在后 台完成,所以这样的页面就像为某个用户所定制的一样,使用起来非常方便。



4、cookie代码:



1)、保存cookie:

document.cookie ="键=值;expires=失效时间点;path=允许访问该cookie的文件的路径;domain=允许方式该cookie的域名"

1、键=值 : 保存在cookie里的数据,键是表示唯一性的

2、expires=失效时间点:表示cookie在什么时间点失效。这是GMT格式的日期字符串。默认情况是临时存储,会话级别(简单粗暴的理解为:打开网站到关掉网站),会话结束后,就失效了

3、path=允许访问该cookie的文件的路径

3.1)、不设置path时(默认情况下),保存的cookie只有当前路径(保存cookie的html文件所在的路径)下的html文件,及其当前路径的子路径下的html文件可以使用,当前路径的父路径,兄弟路径不能拿到的

3.2)、设置path后,在path所设置的路径及其子路径下的html文件都可以访问。

​ 如: path=/ 表示根目录及其子目录可以访问(其实就是整个网站)

​ 如: path=/abc 表示网站根目录下的abc目录及其子目录可以访问

4、domain=允许访问该cookie的域名

​ 如:domain=“www.163.com”;表示保存的cookie可以被www.163.com里的网页读取。



2)、获取cookie:

  document.cookie



3)、删除cookie

​ cookie没法直接被删除,当过了失效时间点时,cookie会自动消失;或者覆盖某个键值对,并设置失效时间过期时,则cookie被删除,不能恢复,如果手动调整系统的时间,让cookie消失,可以进行恢复,此时为假删除



5、封装cookie


// 保存cookie
// 参数:
    // 键
    // 值
    // 有效期(单位:天)
    // 可以访问的路径
    // 可以访问的域名
// 返回值:无

function saveCookie(key,value,dayCount,path,domain){
    let d = new Date();
    d.setDate(d.getDate()+dayCount); 
    // document.cookie= `${key}=${value};expires=${d.toGMTString()}`;
    let str  = `${key}=${escape(value)};expires=${d.toGMTString()}`;
    // path&&(str+=`path=${path};`);
    if(path){
        str+=`path=${path};`
    }

    if(domain){
        str+=`domain=${domain};`
    }

    document.cookie= str;
}

// 获取cookie:根据键获取值
// 参数
//   键
// 返回值:键对应的值;

function getCookie(key){
    let str = unescape(document.cookie);
    let arr = str.split("; "); //["username=jzm","city=西安","userpass=123"]
    for(let i in arr){
        if(arr[i].split("=")[0]==key){            
            return arr[i].split("=")[1];
        }
    }
    return null;
}

// 删除cookie
// 参数:
// 键
// 返回值:无

function removeCookie(key){
    saveCookie(key,"byebye",-1)
}

注意:

​ 如果有中文问题(以前的浏览器),使用encodeURIComponent进行编码(URI的编码),使用decodeURIComponent进行解码。

如:

let str=“渠 博”;

let result =encodeURIComponent(str);

console.log(result);//E6%B8%A0%20%E5%8D%9A

console.log(decodeURIComponent(result));//渠 博



6、cookie的特点:

1、生命周期:

​ cookie如果不设置有效期,那么就是临时存储(存储在内存中),是会话级别的,会话结束后,cookie也就失效了;如果设置了有效期,那么cookie存储在硬盘里,有效期到了,就自动消失了。

2、网络流量:cookie的数据每次都会发给服务器端,占用了网络流量。

3、安全性:cookie会随着HTTP header发送到服务器端,所以安全性有问题,容易被截获。

4、大小限制:cookie大小限制在4KB,非常小;

5、cookie只能保存字符串

6、cookie设置了有效期时,保存在硬盘上。

面试题:请问 cookie,localStorage,sessionStorage 的区别

https://blog.csdn.net/jiang7701037/article/details/89118086



7、XSS攻击

1)、概念:

​ XSS攻击,是跨站脚本攻击(Cross Site Scripting)。通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript,但实际上也可以包括Java、 VBScript、ActiveX、 Flash 或者甚至是普通的HTML。攻击成功后,攻击者可能得到包括但不限于更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容。

2)、原理:

​ 通过将一些字符特殊地对待来区别文本和标记,例如,小于符号(<)被看作是HTML标签的开始,

<br /> 与<br />
之间的字符是页面的标题等等。当动态页面中插入的内容含有这些特殊字符(如<)时,用户浏览器会将其误认为是插入了HTML标签,当这些HTML标签引入了一段JavaScript脚本时,这些脚本程序就将会在用户浏览器中执行。所以,当这些特殊字符不能被动态页面检查或检查出现失误时,就将会产生XSS漏洞。

3)、防止XSS攻击

<style>
    #box{
        width: 300px;
        height: 400px;
        border:1px solid black;
        }
</style>

<body>
    <div id="box">
    </div>    
    <input id="txt" type="text" style="width: 800px"><br/>
    <input type="button" value="留言" onclick="send()">
</body>
</html>
<script>

    function send(){        
        // $("box").innerHTML += $("txt").value+"<br/>"
        // 如何防止
        let str = $("txt").value;
        str = str.replace(/</g,"&lt;");
        str = str.replace(/>/g,"&gt;");
        console.log("str",str);        
        $("box").innerHTML += str;
    }

</script>



作业:

1、一周内免登陆:在上节课的登录成功后保存cookie,在首页显示欢迎语

2、购物车信息保存到cookie(cookie只能保存字符串)



第二十四天、jsonp和promise



本节课目标


一、jsonp

​ 什么是跨域访问及同源策略

​ 跨域访问的几种方式

​ JSONP跨域

​ JSONP和AJAX的跨域访问


二、Promise

​ Promise的概念

​ Promise的作用

​ Promise的方法

​ Promise封装AJAX

​ 示例:红绿黄灯



一、jsonp



1、什么是跨域访问及同源策略



跨域访问

​ 跨域访问就是跨域名访问,即A网站的网页在代码上访问了B网站的后端代码

​ 由于同源策略(浏览器的安全机制),所以,AJAX不能实现跨域访问



同源策略

​ 同源就是:同样的协议,同样的地址,同样的端口。

​ 同源策略:就是JavaScript或Cookie只能访问同源下的内容,这是浏览器的安全机制



2、跨域访问的几种方式

​ AJAX不支持跨域访问,为了达到跨域访问的目的,出现了很多的解决方案 :JSONP、iframe、flash、xhr2等。但是比较常用的是JSONP。



3、JSONP跨域



JSONP:

​ JSONP(JSON with Padding)可用于解决主流浏览器的跨域数据访问的问题。跟JSON没有关系。



JSONP原理:

​ JSONP是如何实现跨域访问的?本质上是利用HTML元素的

src属性

都可以跨域的思路来解决的。

如:img,script,iframe等标记的src属性的值都可以赋成其它域名的合法地址。

示例:

<body>
    <input type="button" value="要数据" οnclick="getData()">
</body>
</html>
<script>
    function showData(str){
        console.log(str);
    }
    
    function getData(){
        let scriptDom = document.createElement("script");
        scriptDom.src = "http://10.35.165.78/data.php?callback=showData";
        document.body.appendChild(scriptDom);
        scriptDom.remove();
    }

</script>



4、AJAX的跨域访问

亲,AJAX是不能跨域的。如想跨域,得靠后端

在服务器端的页面上增加以下代码完成。
response.setHeader(“Access-Control-Allow-Origin”,*); //jsp(java server page)的写法
header(“Access-Control-Allow-Origin:*);  //php的写法
第二个参数*:表示所有的跨域请求都可以;也可以是某个域名或者用逗号隔开的域名列表。



5、面试题:


1)、请问jsonp是不是ajax中实现跨域访问的技术

​ jsonp不是AJAX中实现跨域访问的技术

​ 1、jsonp没有使用XMLHttpRequest对象。

​ 2、jsonp只是在一种跨域的技巧。

​ 3、jsonp只支持Get方式

​ 由于按照jsonp的这种方式跨域访问时,就是可以利用javascript和服务器端交互,能达到AJAX中XMLHttpRequest对象同样的效果。所以,人们总是把

jsonp和AJAX联系在一起,这个面试题也是看看你是不是在不懂原生JS,而直接用的jQuery。


2)、jsonp和json的区别?

​ 1、jsonp和json根本就没有关系,不是同类型的技术

​ 2、jsonp是跨域访问的技巧

​ 3、json是描述数据的格式,经常用于在网络上传输数据的格式,如:前后端交互时,会用json




扩展:

XML和JSON都是描述数据的格式。


1、JSON:

[
    {
        name:"三国演义",
        author:"王翠霞"
    },
    {
        name:"大学",
        author:"甘万鑫"
    }
]


2、XML:

XML是最早的描述数据的一种格式,在AJAX中使用(前后端交互使用),现在大部分都用JSON了。

XML:extension markup langugage; 可扩展的标记语音(标签可自定义,但必须要成对)

<books>

  <book>

    <name>三国演义</name>

    <author>王翠霞</author>

  </book>

  <book>

    <name>大学</name>

    <author>甘万鑫</author>

  </book>

</books>




二、Promise



1、Promise的概念

​ Promise是ES6提供的

原生的类

(构造函数), 用来传递异步操作的消息。它代表了某个未来才会知道结果的事件(通常是一个异步操作)



2、Promise的两个特点:

1)、对象的状态不受外界影响。

​ Promise 有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和 Rejected(已失败)。

2)、一旦状态改变,就不会再变

​ 状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected



3、Promise的作用

​ 解决回调地狱的问题。


回调地狱的代码(使用setTimeout):

function fn1(cb){
    console.log("fn1开始");
    setTimeout(function(){
        console.log("fn1的异步操作");
        cb();
    },1000);
    console.log("fn1结束");    
}

function fn2(cb){
    console.log("fn2开始");
    setTimeout(function(){
        console.log("fn2的异步操作");
        cb();
    },1000);
    console.log("fn2结束");    
}
function fn3(cb){
    console.log("fn3开始");
    setTimeout(function(){
        console.log("fn3的异步操作");
        cb();
    },1000);
    console.log("fn3结束");    
}


function fn4(){
    console.log("fn4开始");
    console.log("fn4结束");    
}

// 把fn1 里的代码全部(包括异步操作)执行完毕后,执行fn2,fn2的异步操作执行完毕,再执行fn3;
fn1(function(){
    fn2(function(){
        fn3(fn4);
    })
});

回调地狱的代码(使用ajax)




Promise 对象可以将异步操作以同步操作的流程表达出来(使用链式的写法),避免了层层嵌套的回调函数。

​ 如:函数fn1和fn2,当我们需要在fn1(fn1函数里有异步操作)调用结束后调用fn2,一般的解决方案是fn1(fn2)。而promise的做法是 fn1().then(fn2);即Promise将回调模式的主从关系调换了一个位置,变成了同等的只是顺序的关系

fn1().then(fn2).then(fn3).then(fn4)



4、Promise类的方法

console.dir(Promise)查看一下。

1、promise构造函数的参数是个函数

2、该函数(Promise的参数)的参数有两个:resolve,reject

resolve 表示异步操作成功时,要调用的函数。是then函数的第一个参数

reject 表示异步操作失败时,要调用的函数。是then函数的第二个参数

new Promise(function(resolve, reject){
    
    
	// 异步操作的代码

	// 如果异步操作成功,调用resolve;如果失败,调用reject;
    
});


1)、 对象方法:then、catch



then方法:

​ 功能:把then方法的参数传给resolve和reject。 promise对象.then(resolve回调函数,reject回调函数);

​ 参数:

​ then方法的第一个参数是resolve

​ then方法的第二个参数是reject。

​ 返回值:promise对象本身,所以,then调用完毕后,还可以继续调用then(即:链式调用)


then方法的基本使用:

let p1 = new Promise(function(resolve,reject){
    resolve();
    reject();
});


p1.then(function(){
    console.log("then方法的第一个参数");
},function(){
    console.log("then方法的第二个参数");
});


带上异步操作

function fn(){
    var p = new Promise(function(resolve, reject){
        
        //做一些异步操作
        setTimeout(function(){
            console.log(‘异步执行完成');
             resolve(‘姥姥的话就是你长大听不到的那些话');//这是then函数的参数;
        }, 2000);
        
    });
    return p;
}

fn().then((str)=>{
    console.log(str);
},()=>{})




catch方法:

​ 它和then的第二个参数一样,用来指定reject的回调,

function fn(){
    var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log(‘异步执行完成');
             resolve(‘姥姥的话就是你长大听不到的那些话');//这是then函数的参数;
        }, 2000);
    });
    return p;
}

fn()
.then(()=>{})
.catch(()=>{})


Promise中then方法的数据传递

​ 上一个then的返回值(结果)是下一个then的参数(输入)

function fn1(){
    console.log("fn1开始");
    let p = new Promise(function(resolve,reject){
        setTimeout(function(){            
            console.log("fn1的异步操作");
            resolve(3)
        },1000);
    });
    console.log("fn1结束");  
    return p;
}


fn1().then(function(num){
    console.log("第一次",num);//3
    return num+1;
}).then(function(num){
    console.log("第二次",num);//4
    return num+1;
}).then(function(num){
    console.log("第三次",num);//5
    return num+1;
});



2)、类方法: all , race



all方法:

​ 功能: Promise.all可以并行执行多个异步操作,并且在一个回调中处理所有的返回数据。返回的数据与传的参数数组的顺序是一样的。当所有的异步操作都成功才表示成功 。

​ 参数:数组。数组里是若干个返回promise对象的函数(异步操作);

​ 返回值:promise对象。promise对象的then方法的回调函数的参数是 所有promise对象的resolve的参数(数组形式)。

//示例一:
function fn1(num){
    console.log("fn1开始",num);
    let p = new Promise(function(resolve,reject){
        setTimeout(function(){            
            console.log("fn1的异步操作",num);
            resolve("fn1异步的结果:"+num)
        },1000);
    });
    console.log("fn1结束",num);    
    return p;
}


Promise.all([fn1(1),fn1(2)]).then(function(result){
    // 这个函数要执行,必须保证,fn1(1)和fn1(2)都成功,才调用。
    // 参数result是数组,保存着多次异步操作的结果,也就是把多个异步操作中resolve函数的参数放到了result里
    console.log("result",result);
});


示例二:

function fn1(){
    console.log("fn1开始");
    let p = new Promise(function(resolve,reject){
        setTimeout(function(){            
            console.log("fn1的异步操作");
            resolve("fn1异步的结果")
        },1000);
    });
    console.log("fn1结束");    
    return p;
}


function fn2(str){
    console.log("fn2开始");
    let p = new Promise(function(resolve,reject){
        setTimeout(function(){
            console.log("fn2的异步操作");
            resolve("fn2异步的结果");
        },3000);
    });    
    console.log("fn2结束");    
    return p;
}

Promise.all([fn1(),fn2()]).then(function(result){
    console.log("result",result); //["fn1异步的结果","fn2异步的结果"]
});

​ 用Promise.all来执行,all接收一个数组参数,两个异步操作是并行执行的,等到它们

都执行完后

才会进到then里面。而两个异步操作返回的数据都在then里面,all会把所有异步操作的结果放进一个数组中传给then,就是上面的results



race方法:

​ 功能:也是并发,但是,与all不同之处时,当一个异步操作完成(resolve或reject)时,就调用方法了。即:多个异步操作,同时执行,谁快就用谁的结果,所以,结果不再是数组。

function fn1(){
   console.log("fn1开始");
   let p = new Promise(function(resolve,reject){
       setTimeout(function(){            
           console.log("fn1的异步操作");
           resolve("fn1异步的结果")
       },1000);
   });
   console.log("fn1结束");    
   return p;
}


function fn2(str){
   console.log("fn2开始");
   let p = new Promise(function(resolve,reject){
       setTimeout(function(){
           console.log("fn2的异步操作");
           resolve("fn2异步的结果");
       },3000);
   });    
   console.log("fn2结束");    
   return p;
}

Promise.race([fn1(),fn2()]).then(function(result){
   console.log("result",result); //"fn1异步的结果"
});



总结Promise的使用步骤

1、找到(曾经的)异步操作的代码,放在Prmoise构造函数的参数(函数)里

2、参数(函数)的第一个参数resolve是成功时调用的函数,对应then方法(函数)的第一个参数

3、参数(函数)的第二个参数reject是失败时调用的函数,对应then方法(函数)的第二个参数



5、 Promise封装AJAX


function ajaxUsePromise(obj){
    let p = new Promise(function(resolve,reject){
       let defaultObj = {
           url:"#",
           method:"get",
           params:"",
           isAsync:true
       }

       for(let key in defaultObj){
           if(obj[key]!=undefined){
               defaultObj[key] = obj[key];
           }
       }

       let xhr =new XMLHttpRequest();

       let urlAndParams = defaultObj.url;

       if(defaultObj.method.toLowerCase()=="get"){
           urlAndParams += "?"+ defaultObj.params;
       }

       xhr.open(defaultObj.method,urlAndParams,defaultObj.isAsync);    
      
           xhr.onreadystatechange = function(){
               if(xhr.readyState==4){
                   if(xhr.status==200){
                       resolve&&resolve(xhr.responseText);
                   }else{
                       reject&&reject("服务器出错了");
                   }
               }
           }  
       });

       if(defaultObj.method.toLowerCase()=="post"){
           xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
           xhr.send(defaultObj.params);
       }else{
           xhr.send();
       }

       return p;
}



6、示例:红绿黄灯的闪烁

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title></title>
		<style type="text/css">
			div{
				width: 50px;
				height: 50px;
				border-radius: 50%;
				border:1px solid black;
				text-align: center;
				line-height: 50px;
				font-size: 30px;
				font-weight: bold;
				color:white;
			}
			#redLight{
				background-color: white;
			}
			#greenLight{
				background-color: white;
			}
			#yellowLight{
				background-color: white;
			}
		</style>
	</head>
	<body>
		<div id="redLight">
			
		</div>
		<br/>
		<div id="greenLight">
			
		</div>
		<br/>
		<div id="yellowLight">
			
		</div>
	</body>
</html>
<script type="text/javascript" src="js/tools.js" ></script>
<script type="text/javascript">

//红灯亮五秒,绿灯亮3秒,黄灯亮2秒,如何让
//三个灯不断交替重复亮灯

//红灯的函数
function redFunc(count){
    $("#redLight").innerHTML=count;
    $("#redLight").style.backgroundColor = "red";

    let p = new Promise (function(resolve,reject){

        var myTimer=setInterval(()=>{
            count--;
            if(count<=0){
                window.clearInterval(myTimer);
                $("#redLight").innerHTML="";
                $("#redLight").style.backgroundColor = "white";
                resolve(3);
            }
            $("#redLight").innerHTML=count;

        },1000)
    })
    return p;
}

function greenFunc(count){
    //1、让红灯的div变红,并且把秒数写上
    $("#greenLight").innerHTML = count;
    $("#greenLight").style.backgroundColor = "green";
    
    let p= new Promise(function(resolve,reject){
        //2、启动定时器,计时
        var myTimer = setInterval(()=>{
            count--;
            if(count<=0){
                window.clearInterval(myTimer);
                $("#greenLight").innerHTML = "";
                $("#greenLight").style.backgroundColor = "white";
                resolve(2);
                return;
            }
            $("#greenLight").innerHTML = count;
        },1000);
    });
	return p;
}


function yellowFunc(count){

    //1、让红灯的div变红,并且把秒数写上
    $("#yellowLight").innerHTML = count;
    $("#yellowLight").style.backgroundColor = "yellow";
    
    let p= new Promise(function(resolve,reject){
        //2、启动定时器,计时
        var myTimer = setInterval(()=>{
            count--;
            if(count<=0){
                window.clearInterval(myTimer);
                $("#yellowLight").innerHTML = "";
                $("#yellowLight").style.backgroundColor = "white";
                resolve();
                return;
            }
            $("#yellowLight").innerHTML = count;
        },1000);
    });
	return p;
}

function shine(){
	redFunc(5).then(greenFunc).then(yellowFunc).then(shine);
}

window.onload = function(){
	shine();
}

function $(str){
    if(str.charAt(0)=="#"){
        return document.getElementById(str.substring(1));
    }else if(str.charAt(0)=="."){
        return document.getElementsByClassName(str.substring(1));
    }else{
        return document.getElementsByTagName(str);
    }
}	
</script>

补充:

面试题:深拷贝和浅拷贝?

https://blog.csdn.net/jiang7701037/article/details/98738487

​ 拷贝就是复制。

​ 首先,谈到深拷贝和浅拷贝是针对引用类型来说的。

// 封装函数:复制一个对象,新对象和源对象不能共享内存空间
// 参数:源对象
// 返回值:新对象

function copyObj(obj){
  let newObj = {};
  for(let key in obj){
      if(typeof obj[key]=="object"){
          newObj[key] = copyObj(obj[key]);
      }else{
          newObj[key]=obj[key];
      }
  }
  return newObj;
}

备忘面试题:内置对象,本地对象,宿主对象??

https://blog.csdn.net/jiang7701037/article/details/85448739



第二十五天、闭包和继承



本节课目标:


一、闭包(重要的不行不行的)

​ 闭包的概念

​ 闭包的代码(格式)

​ 闭包的作用

​ 闭包的注意点

​ 闭包的示例


二、函数是个对象(double 重要)

​ 函数的三种定义方式

​ 函数是功能完整的类(对象)

​ 函数的内置对象

   函数的属性和方法


三、继承(重要的不行不行的)

​ 继承的概念和优点

​ Prototype实现继承

​ Apply()和call()实现继承

​ 组合继承



一、闭包



1、 闭包的概念

​ 官方解释:一个函数和对其周围状态(

lexical environment,词法环境

)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是

闭包



closure

)。闭包让你可以在一个内部函数中访问到其外部函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。

​ 通俗的来说:JavaScript中所有的function都是一个闭包。不过一般来说,

嵌套的function

所产生的闭包更为强大,也是大部分时候我们所谓的“闭包”。



2、 闭包的代码(格式)

function f1(){
	var n = 250;
	function f2(){//f2定义在f1的内部,可以读取f1的局部变量; f2是闭包
		alert(n);
	}
	return f2;//返回f2的目的是外部可以调用f2函数,不至于,因为,f2函数定义在f1内部,而导致外部不能调用。这样就相当于f2还是可以在外部调用的,巧妙地,在f2里面使用了f1的局部变量。
}

奇文共欣赏

function fn1(){
    var age = 100;    
    let fn2 = function(){//闭包  
        age++;
        console.log("age",age);
    }
    return fn2;
}

//fn1()(); //等价于:let f =fn1();  f(); 打印:101

//fn1()(); // 101 

let f1 = fn1();
f1();//?? 101

let f2 = fn1();
f2();//?? 101
f2();//?? 102

f1();//?? 102



3、闭包的作用

1)、可以读取函数内部的变量(局部变量)

2)、让这些变量(外部函数的局部变量)的值始终保持在内存中

​ 通常,函数的作用域及其所有变量都会在函数执行结束后被销毁。但是,在创建了一个闭包以后,这个函数的作用域就会一直保存到闭包不存在为止。

function f1(){
	var n = 1;
	function f2(){
	      n++; 
	      console.log(n); 	
	 }
	return f2;
}
var f = f1();//f是全局变量
f();//但是,在f1之外调用f2时,也能正确显示出f1里给n赋的值,这就是使得变量n一直保存在内存中了。



4、 闭包的注意点


1)、内存泄漏

​ 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露(有些内存不再使用了,但是,没有被释放)。



5、闭包的示例和场景



1)、只有一个方法的对象

​ 因为闭包允许将函数与其所操作的某些数据(环境)关联起来。这显然类似于面向对象编程。在面向对象编程中,对象允许我们将某些数据(对象的属性)与一个或者多个方法相关联。

因此,通常你使用只有一个方法的对象的地方,都可以使用闭包。

示例:

//面向对象的类
// class Person{
//     constructor(){
//         this.name = "陈琳"
//     }

//     eat(){
//         console.log(this.name+"在吃,天在看……");
//     }
// }

// let p1 = new Person();

// p1.eat();

//使用闭包
function person(str){
    var name = str;
    return function(){
        console.log(name+"在吃,天在看……");
    }
}

let eat = person("陈琳");
eat();


2)、如果某些数据,只希望某些函数操作使用



3)、用闭包模拟私有方法

编程语言中,比如 Java,是支持将方法声明为私有的,即它们只能被同一个类中的其它方法所调用。

而 JavaScript 没有这种原生支持,但我们可以使用闭包来模拟私有方法。私有方法不仅仅有利于限制对代码的访问:还提供了管理全局命名空间的强大能力,避免非核心的方法弄乱了代码的公共接口部分


function person(str){
    var name = str;
    var age = 20;
    
    function setAge(transAge){
        fn2();
        age = transAge;
    }

    function getAge(){
        fn2();
        return age;
    }    

    function fn2(){
        console.log("fn2");
    }

    return {
        setAge:setAge,
        getAge:getAge
    }   
}

let p = person("陈琳");
p.setAge(18);
console.log(p.getAge());

p.fn2();


4)、循环里使用闭包


1、不 自调用

function fn1(){
    let arr = [];
    for(var i=0;i<5;i++){
        //循环体中,完成的事情:给数组的元素赋值,不会调用函数
        arr[i] = function(){
            return i;
        }
    }
    console.log(i); //5,循环结束后,i的值是5;
    return arr;
}

let arr2 = fn1();

// 首先,arr2数组里存储的都是函数

console.log(arr2[0]());//5
console.log(arr2[1]());//5
console.log(arr2[2]());//5


2、自调用(自运行)

function fn1(){
    let arr = [];
    for(var i=0;i<5;i++){
        //循环体中,完成的事情:给数组的元素赋值,赋的值是函数的结果。
        // 即:循环的过程中,函数已经被调用了。
        arr[i] = (function(){
            return i;
        })();
    }
    return arr;
}

let arr2 = fn1();

console.log(arr2[0]);//0
console.log(arr2[1]);//1
console.log(arr2[2]);//2


5)、局部变量的累加
function outerF(){
	var t=0;
	function innerF(){
		t++;
		console.log(t);
	}
	return innerF;
}
var f = outerF();



扩展:
function f1(){
	var n = 250;
	function f2(){
	      n++;	alert(n);
	} 
	return f2;
}
var f21 = f1();//f21就是f2
f21();//调用f2;//251
var f22= f1();//f22就是f2
f22();//调用f2;//251
f22();//调用f2;//252
f22();//调用f2;//253
f21();//调用f2;//252
f21();//调用f2;//253
虽然f21和f22的代码一样,但是,它们是两个不同的对象,即,每个的n值不一样



6、总结,提升认识

函数可以访问自己内部的变量(局部变量)

函数可以访问外部的变量

全局变量是函数外部变量的一种体现

闭包中,父函数定义的变量之于闭包(函数)就是外部变量(全局变量)



7、作用域及其作用域链

https://blog.csdn.net/jiang7701037/article/details/101314293



二、函数是个对象

​ 函数的三种定义方式



函数是功能完整的类(对象)

​ 函数的内置对象

   函数的属性和方法



1、函数的三种定义方式

1)、函数声明

function test(ord){
	alert(“亲”+ord+“!”);
}

2)、函数表达式(匿名函数)

var test = function(ord){
	alert(“亲”+ord+“!”);	
}

3)、 对象的方式:

var test =  new Function("ord","alert('亲'+ord+'!')");



2、函数是功能完整的类(对象)

函数是功能完整的类

ECMAScript 中函数实际上是功能完整的对象。即函数就是一个对象

格式:

var functionName = new Function(arg1,..., argN, function_body)

​ 每个 arg 都是一个参数,最后一个参数是函数主体(要执行的代码)。这些参数必须是字符串。



3、函数的内置对象

​ 内置对象:就是自动产生,随着函数的调用而存在的对象,函数里的内置对象有:arguments和this。箭头函数中没有arguments和this。



1)、this

​ this对象引用的是函数赖以执行的环境的对象,

即函数属于哪个对象的,this就是哪个对象

, 有可能出现this代表不同的对象,因为,一个函数有可能被赋给不同的对象。如果函数哪个对象都不属于,就属于全局的window对象

https://blog.csdn.net/jiang7701037/article/details/79153906

https://blog.csdn.net/jiang7701037/article/details/79314864

https://blog.csdn.net/jiang7701037/article/details/111837813 (暂时不看,以后再看)



2)、arguments

  • arguments是个伪数组

    (不是用Array new出来的,但是可以用下标方式访问元素,也可以使用length属性。但是不能使用数组的官方方法(如:push,pop等等)),arguments对象保存这函数的所有参数
  • arguments对象有一个名叫callee的属性。Callee属性是个指针(地址,引用类型),指向了arguments对象所在的函数。Callee属性有个好处,看如下阶乘递归的示例,即可明白
function factorial(num){
	if(num<=1){
		return 1;
    } else{
                //return num*factorial(num-1);
        return num*arguments.callee(num-1);
    }
}
如果函数名需要修改,函数体里就不用修改函数名了。



4、函数的属性和方法



1)、 prototype属性(重要的不行不行的)

提示:一定要把内存图搞清楚




概念:

​ 每个函数都有一个属性是

prototype(原型)

,这是属性是个指针(地址,引用类型),指向一个对象,该对象的用途是包含所有

实例(对象)共享的属性和方法

。所有通过同一个构造函数创建的实例对象,都会共享同一个prototype。


1.1)问题:


ES5的构造函数方式定义类,浪费了内存空间


function Person(name,sex){
    // 属性:
    this.name = name;
    this.sex = sex

    // 方法:以下写法,浪费了内存空间,因为,每个对象的eat的值(函数)是一样的。
    //
    this.eat = function(){
        console.log(this.name+"在吃");
    }
    
}

let p1 = new Person("陈琳","女");

let p2 = new Person("李茂军","男");

内存示意图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Aek0JjYC-1641803458655)(imgimage-20210917233545014.png)]

为什么浪费了内存空间?

答:eat方法在内存是存在了两份,而两个eat的值是一样的,这样造成了内存的浪费


1.2)使用了prototype的写法后,节约了内存空间:


使用ES5的构造函数和prototype定义类

// prototype属性:保存着实例(对象)所共享的属性和方法(大部分是方法)

function Person(name,sex){
    // 属性:
    this.name = name;
    this.sex = sex 
}

 // 方法:
Person.prototype.eat = function(){
    console.log(this.name+"在吃");
}

// Person.prototype.eat();//可以调用eat,但这不是目的

let p1 = new Person("陈琳","女");
//对象为何能够调用 类Person的prototype上定义的方法呢?
// 答:每个对象的内存中有个属性 __proto__。这个属性也是对象,指向 Person的prototype

p1.eat();

// let p2 = new Person("李茂军","男");

每个对象都有个属性

proto

。这个属性也是对象,它和类 Person的prototype指向了同一块内存空间

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wWsMV3Uk-1641803458656)(imgimage-20210917233900704.png)]



补充说明(可以以后再看):

从上图可以看出来:

1、每个构造函数有个prototype属性(引用类型)

2、prototype指向的内存空间,有个属性constructor。constructor指向了构造函数(Person)本身。

3、每个对象内存中有个属性

proto

,它和构造函数的prototype属性指向了同一块内存空间。

代码示意:

function Person(name, sex) {
    // 属性:
    this.name = name;
    this.sex = sex
}

// 方法:
Person.prototype.eat = function () {
    console.log(this.name + "在吃");
}

// Person.prototype.eat();//可以调用eat,但这不是目的

let p1 = new Person("陈琳", "女");

 console.log(p1 instanceof Person);//true

 console.log("p1.__proto__",p1.__proto__);// 有属性:constructor和eat方法
 console.log("Person.prototype",Person.prototype);//有属性:constructor和eat方法
 console.log(p1.__proto__ == Person.prototype);//true

console.log("Person.prototype.constructor", Person.prototype.constructor);//构造函数Person
console.log(Person == Person.prototype.constructor);//true


ES6的class写法是(ES5构造函数和prototype定义类)的语法糖

 //1、 ES5构造函数和prototype定义类

function Person(name, sex) {
    // 属性:
    this.name = name;
    this.sex = sex
}

// 方法:
Person.prototype.eat = function () {
    console.log(this.name + "在吃");
}

//2、ES6的class

class Person {
    constructor(name, sex) {
        // 属性:
        this.name = name;
        this.sex = sex
    }
    eat() {
        console.log(this.name + "在吃");
    }
}

//以上两种定义类的方式,使用时是一样

let p1 = new Person("陈琳", "女");

p1.eat();



原型的属性和实例的属性:

  • 实例属性:用new调用构造函数创建出来的对象叫做实例。构造函数里写的属性就是实例属性,每个实例属性的内存空间是

    独立的,不会共享

    如:

function Person(name, sex) {
    // 实例属性:
    this.name = name;
    this.sex = sex
}
  • 原型属性:写在prototype后面的叫做原型属性,是所有实例

    共享的内存空间

如:

Person.prototype.country = "中国";
Person.prototype.eat = function () {
    console.log(this.name + "在吃");
}

当给原型属性赋值时,那么,会成为实例属性(在p2实例的内存空间中,重新申请空间)

https://blog.csdn.net/jiang7701037/article/details/80530931

function Person(name, sex) {
    // 属性:
    this.name = name;
    this.sex = sex
}

Person.prototype.country = "中国";
// 方法:
Person.prototype.eat = function () {
    console.log(this.name + "在吃");
}

let p1 = new Person("陈琳", "女");
console.log(p1.country);//中国

let p2 = new Person("李茂军", "男");
console.log(p2.country);//中国

p2.country = "俄罗斯"; //当给原型属性赋值时,那么,会成为实例属性(在p2实例的内存空间中,重新申请空间)

console.log(p1.country);//中国
console.log(p2.country);//俄罗斯



面试题

​ 通过prototype属性给官方对象增加方法,如:给Array可以增加求数组最大数的函数。

// 求数组中最大值
Array.prototype.max = function(){
    if(this.length==0){
        return null;
    }
    let m = this[0];
    for(let i=1;i<this.length;i++){
        if(this[i]>m){
            m = this[i];
        }
    }
    return m;
}

let arr = new Array(12,23,100,8,20);
console.log(arr.max());


2)、扩展:

1、用实例不能访问prototype属性,虽然拥有,但是不能访问。

var person1 = new Person();

// alert(person1.prototype);//不能访问

//alert(person1.prototype.name);//不能访问

//alert(person1.name);//直接访问属性是可以的

2、person1.__proto__访问

实例不能直接访问prototype,但是可以用属性__proto__访问。

alert(person1.

proto

.name);//火狐谷歌可以访问,但是IE8不能访问

3、Person.prototype.isPrototypeOf()检测 该原型是否属于某对象

alert(Person.prototype.isPrototypeOf(person1));//true; person1是Person对象。

4、Object.getPrototypeOf()函数可以得到对象的原型

function Person(){ }

Person.prototype.name=”张三丰“

var p1 = new Person();

alert(Object.getPrototypeOf(p1)==Person.prototype);//true;



3)、call和apply方法(重要得很)



概念:

​ 每个函数都有两个非继承而来的方法apply()和call(),这两个方法的用途都是用来调用函数(

在特定的作用域中

),实际上等于设置函数体内的this对象的值。调用函数,实际上就是调用该函数对象的call内部方法。




call作用:

1、是调用函数本身的

    function eat(){
        console.log("this",this);
        console.log("吃");
    }
    eat(); //js背后会这么写:eat.call()
    eat.call();//等价于eat(); 

2、重点是:调用函数时,

会指定(或者是改变)函数内部的this

1)、call方法的

第一个参数是函数内部的this指向

2)、call方法的从第二个朝后的参数是函数本身的参数。

3)、你可以认为,把对象和函数解耦了

window.eat(); //js背后是这么写的:eat.call(window);

let p1 = {
    name:"陈琳",
    sex:"女",
}

let p2 = {
    name:"李茂军",
    sex:"男",
}

function  eat(eatStr,drinkStr){
    console.log(this.name+"一边吃"+eatStr+",一边喝"+drinkStr)
}

eat.call(p1,"油泼面","面汤");//等价于:p1.eat("油泼面","面汤");
eat.call(p2);//等价于:p2.eat();

let d3 = {
    name:"小黑"
}

eat.call(d3);//等价于:d3.eat();



两者的区别:

​ apply()方法有两个参数,分别是运行函数的作用域(this指向),另一个是参数数组(可以是Array也可以是arguments)。

​ Call()方法的第一个参数和apply()的第一个参数一样,其它参数就是调用函数的参数(相当于把,apply第二个参数的每个元素单列出来)

//eat.call(p1,"油泼面","面汤")
// eat.apply(p1,["油泼面","面汤"]);

function fn(){

    eat.apply(p1,arguments);
    
}

fn("油泼面","面汤");


4)、ES5_bind和this关键字

​ bind方法也是函数的方法。bind方法可以改变this的指向。

​ bind()方法会

创建一个新函数

,称为**绑定函数(家).**当调用这个绑定函数时,绑定函数会以创建它时传入bind()方法的第一个参数作为 this。

let p1 = {
    name:"陈琳",
    sex:"女",
}

function  eat(eatStr,drinkStr){
    console.log(this.name+"一边吃"+eatStr+",一边喝"+drinkStr)
}

let p1eat = eat.bind(p1); //不会调用eat函数,只是完成eat函数和p1的绑定,产生新的函数p1eat

p1eat("油泼面","面汤"); //调用p1eat时,相当于 p1.eat();


5)、面试题:bind,call和apply的区别

https://blog.csdn.net/jiang7701037/article/details/99975245

1、相同点:

三个函数都会改变this的指向(调用这三个函数的函数内部的this)

2、不同点:

1)、bind会产生新的函数,(把对象和函数

绑定死

后,产生新的函数),而

不调用函数本身

2)、call和apply不会产生新的函数,只是在调用时,

绑定一下

而已。

会调用函数本身

3)、call和apply的区别,第一个参数都是要绑定的this,apply第二个参数是数组(是函数的所有参数),call把apply的第二个参数单列出来。



三、继承



1、继承的概念和优点

所谓继承:


就是子类自动拥有父类的属性和方法

, 继承可以提高代码的复用性。

JS里的继承主要依靠是的原型链。让原型属性(每一个构造函数都有一个原型对象prototype),等于另一个类型的实例,即实现了继承;另外一个类型的原型再指向第三个类型的实例,以此类推,也就形成了一个原型链



2、Prototype实现继承


原型继承

让子对象(ES6中的子类)的原型指向父对象(类)的实例(对象),就完成 了继承


function Animal(sex){
    this.sex = sex;
}

Animal.prototype.eat = function(){
    console.log("吃");
}

function Person(name){
    this.name = name;
}

// 原型继承:让子类的原型属性 指向 父类的对象
Person.prototype = new Animal("男");
let p1 = new Person("李茂军");

console.log( p1.sex);
p1.eat();


function Dog(name){
    this.name = name;
}

Dog.prototype = new Animal("公");

let d1 = new Dog("小黑");
console.log( d1.sex);
d1.eat();

原型继承的示意图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m6dKd0Mg-1641803458656)(C:UserstianAppDataRoamingTyporatypora-user-imagesimage-20210919111704128.png)]


原型链:

让子类的原型指向父类的对象,父类的原型指向爷爷类的对象,依次类推,就形成了原型链


// 爷爷类
function Animal(sex){
    this.sex = sex;
}

Animal.prototype.eat = function(){
    console.log("吃");
}

// 父类
function Person(name){
    this.name = name;
}

// 原型继承:让子类的原型属性 指向 父类的对象
Person.prototype = new Animal("男");
Person.prototype.work = function(){
    console.log("在工作");
}

// 子类
function Programmer(computedLanguage){
    this.computedLanguage = computedLanguage;
}

// 原型继承:让子类的原型属性 指向 父类的对象
Programmer.prototype = new Person("张三疯");
Programmer.prototype.writeCode = function(){
    console.log("写代码");
}


let p1 = new Programmer("js");

console.log(p1.sex);
console.log(p1.name);
console.log(p1.computedLanguage);

p1.eat();
p1.work();
p1.writeCode();


原型链继承的注意点:

  1. 先定义原型继承关系,再添加子类的特有方法或属性(原型的属性,即共享的属性和方法要在原型继承关系确立后,再定义)。

  2. 利用原型链继承,给子类添加特有的原型方法时,不可以重写prototype(不要再给prototype属性赋JSON类型的值就行)


原型链继承的缺点:


1. 创建子类型的实例时,没法传参给被继承类型。

2. 被继承的类型(父类)里包括引用类型的属性的时候,它会被所有实例共享其值



3、Apply()和call()实现继承

apply()和call()是调用函数的,当然也能调用构造函数

​ 这种方式,其实就是在子类型中借用父类型的构造函数来生成自己的实例对象的属性。它可以向父类型的构造函数传递参数来初始化自己的实例属性。

​ 单独使用这种借用构造函数的模式,如果所有要继承的属性和方法都在父类型的构造函数里定义,

特别是实例共享的属性和方法也写在构造函数里,那么这样会浪费内存。所以,很少很少单独使用


//定义一个构造函数Person();
function Person(name1,sex1,age1){
	this.name=name1;
	this.sex=sex1;
	this.age=age1;	
	this.eat = function(){

	}	
}
//定义一个构造函数Teacher()
function Teacher(name1,sex1,age1,course1){
	Person.call(this,name1,sex1,age1);
	this.course=course1;
}

var t = new Teacher("刘德华","男",25,"语文");

alert(t.name+","+t.sex+","+t.course);
	

call继承的内存示意图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Db6swpmC-1641803458656)(C:UserstianAppDataRoamingTyporatypora-user-imagesimage-20210919112909858.png)]



4、组合继承

​ 结合前两种方式:原型链式继承和Call()/Apply()方式继承,我们就能解决前面提出的那些问题。

​ 利用原型链继承共有的属性和方法,利用Call/Apply来初始化自己的但是和父类型同名的属性或方法。


//call和apply的作用:继承父类的构造函数里的属性(和方法)
//prototype的作用:继承父类原型的方法和(属性)

//定义Person类(ES3中叫对象)
function Person(id,name,age){
	if(arguments.length>0){
		this.id = id;
		this.name = name;
		this.age = age;	
	}
}

Person.prototype.eat = function(){
	console.log(this.name+"在吃");
}

//定义Programmer类型
function Programmer(id,name,age,language){
	Person.call(this,id,name,age);//
	this.language = language;
}

Programmer.prototype = new Person();

window.onload = function(){
	let p1 = new Programmer("007","宝宝",12,"js");
	p1.eat("泡面");
	console.log(p1.name);
}

​ 组合继承的内存示意图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fybhuLwE-1641803458657)(C:UserstianAppDataRoamingTyporatypora-user-imagesimage-20210919113018849.png)]



5、ES6的继承

关键字:extends,super,constructor



第二十六天、ES6的模块化

https://blog.csdn.net/jiang7701037/article/details/101215999



本节课目标:

  1. webstorage

​ localStorage

​ sessionStorage

​ 2.ES6模块化



一、webstorage




webStorage


是 HTML5新增的

存储数据

的方案,比使用 cookie 更加直观。它提供了访问特定域名下的会话存储或本地存储的功能,如果是会话存储,则使用sessionStorage,如果是本地存储(硬盘),则使用localStorage。sessionStorage和localStorage的官方函数一样。



1、官方方法

​ 1)、存储数据:

​ localStorage.setItem(键,值);

​ 2)、获取数据:

​ localStorage.getItem(键);

​ 3)、删除数据:

​ localStorage.remove(键);

​ 4)、删除所有的数据:

​ localStorage.clear();



2、面试题:

请问 cookie,localStorage,sessionStorage 的区别:

https://blog.csdn.net/jiang7701037/article/details/89118086



二、ES6模块化

https://blog.csdn.net/jiang7701037/article/details/101215999



第二十七天、设计模式



本节课目标

  1. 设计模式的解释

  2. 单例模式

  3. 工厂模式

  4. 观察者模式

  5. 代理模式



一、设计模式的解释

​ 设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码

设计经验的总结

。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。其实就是前人总结的,针对特定类型问题的类设计方案

​ 总体来说设计模式分为三大类:

1、创建型模式:共五种:

工厂方法模式

、抽象工厂模式、

单例模式

、建造者模式、原型模式。

2、结构型模式:共七种:

适配器模式

、装饰器模式、

代理模式

、外观模式、桥接模式、

组合模式

、享元模式。

3、行为型模式:共十一种:策略模式、模板方法模式、

观察者模式

、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

​ 设计模式遵循的原则:

开闭原则(Open Close Principle)


​ 开闭原则就是说

对扩展开放,对修改关闭

。在程序需要进行扩展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。



二、单例模式



1、场景:



某个类只允许创建一个实例

,这就是单例模式。优点如下:

​ 1)、某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销

​ 2)、省去了new操作符,降低了系统内存的使用频率,减轻GC压力。

​ 3)、有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。(比如:中国国家主席只有一个,飞机大战的地图对象只有一个),所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程。



2、代码:

// ES3(ES5)

let singleton = (function(){
    function Map(width,height){
        this.width = width;
        this.height = height;    
    }    

    let instance;//单例对象

    return  {
        getInstance:function(width,height){
            if(instance==undefined){
                instance = new Map(width,height);
            }else{                   
                instance.width = width;
                instance.height = height;
            }
            return instance;
        }
    }
})();

let t1 = singleton.getInstance(100,200);
console.log(t1);//100,200

let t2 = singleton.getInstance(300,400);

console.log(t1);//300,400
console.log(t2);//300,400

// ES3(ES5)

let singleton = (function(){

    class Map{
        constructor(width,height){
            this.width = width;
            this.height = height;    
        }
    }

    let instance;//单例对象

    return {
        getInstance:function(width,height){
            if(instance==undefined){
                instance = new Map(width,height);
            }else{
                instance.width = width;
                instance.height = height;
            }
            return instance;
        }
    }
})();

let t1 = singleton.getInstance(100,200);
let t2 = singleton.getInstance(300,400);

console.log(t1);//300,400
console.log(t2);//300,400



三、工厂模式



1、场景

​ 工厂模式,就是用工厂的思路,创建对象。工厂是造产品的。现在用工厂来造对象。即一个工厂可以制造很多种类型的对象,这些对象一般具有共同的父类,即相似的类。

以下几种情景下工厂模式特别有用:

  • ​ 对象的构建十分复杂
  • ​ 需要依赖具体环境创建不同实例
  • ​ 处理大量具有相同属性的小对象



2、代码

使用json对象的方式:

// 工厂对象:创建html标签的;
let htmlFactory = {
}


// 创建dom对象的方法
htmlFactory.createDom = function (tagName, obj, boxDom) {
    let dom = document.createElement(tagName);
    for (let key in obj) {
        if (typeof obj[key] == "object") {
            for (let stylekey in obj[key]) {
                dom[key][stylekey] = obj[key][stylekey];
            }
        } else {
            dom[key] = obj[key];
        }
    }
    if (boxDom) {
        boxDom.appendChild(dom);
    }
    return dom;
}

// 创建img标签的
htmlFactory.createImg = function (obj, boxDom) {
    this.createDom("img", obj, boxDom);
}

// 创建input标签的
htmlFactory.createInput = function (obj, boxDom) {
    this.createDom("input", obj, boxDom);
}

// 创建a标签的
htmlFactory.createA = function (obj, boxDom) {
    this.createDom("a", obj, boxDom);
}

window.onload = function () {
    let arr = [
        {
            "src": "img/27.jpg",
            "alt": "这是一个美女",
            "style": {
                width: "200px",
                height: "300px"
            }
        },
        {
            "src": "img/1.jpg",
            "alt": "这是一个美女",
            "style": {
                width: "300px",
                height: "400px"
            }
        },
        {
            "src": "img/2.jpg",
            "alt": "这是一个美女",
            "style": {
                width: "400px",
                height: "500px"
            }
        },
        {
            "src": "img/3.jpg",
            "alt": "这是一个美女",
            "style": {
                width: "200px",
                height: "100px"
            }
        }
    ]

    for(let i in arr){
        htmlFactory.createImg(arr[i],document.body);
    }

}
class HtmlFactory{
    constructor(){

    }
    createDom(tagName, obj, boxDom) {
        let dom = document.createElement(tagName);
        for (let key in obj) {
            if (typeof obj[key] == "object") {
                for (let stylekey in obj[key]) {
                    dom[key][stylekey] = obj[key][stylekey];
                }
            } else {
                dom[key] = obj[key];
            }
        }
        if (boxDom) {
            boxDom.appendChild(dom);
        }
        return dom;
    }
}

class Img extends HtmlFactory{
    constructor(obj, boxDom) {
        super();
        this.createDom("img", obj, boxDom);
    }
}

window.onload = function () {
    let arr = [
        {
            "src": "img/27.jpg",
            "alt": "这是一个美女",
            "style": {
                width: "200px",
                height: "300px"
            }
        },
        {
            "src": "img/1.jpg",
            "alt": "这是一个美女",
            "style": {
                width: "300px",
                height: "400px"
            }
        },
        {
            "src": "img/2.jpg",
            "alt": "这是一个美女",
            "style": {
                width: "400px",
                height: "500px"
            }
        },
        {
            "src": "img/3.jpg",
            "alt": "这是一个美女",
            "style": {
                width: "200px",
                height: "100px"
            }
        }
    ]

    for(let i in arr){
        new Img(arr[i],document.body);
    }

}



四、观察者模式(重点)



1、概念和场景:

​ 观察者模式又叫发布订阅模式(Publish/Subscribe),它定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使得它们能够自动更新自己

使用观察者模式的好处:

​ 1.支持简单的广播通信,自动通知所有已经订阅过的对象。

​ 2.页面载入后目标对象很容易与观察者存在一种动态关联,增加了灵活性。

​ 3.目标对象与观察者之间的抽象耦合关系能够单独扩展以及重用。



2、代码:

class Observer{
    constructor(){
        this.arr=[];
    }
    // 添加订阅者
    addSubsciber(cb){
        this.arr.push(cb);
    }
    // 删除订阅者
    removeSubscriber(cb){
        let index = this.arr.indexOf(cb);
        this.arr.splice(index,1);
    }
    // 发布
    publish(what){
        this.arr.forEach(item=>{
            item(what);
        });
    }
}



示例:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cw8BY5wH-1641803458657)(C:UserstianAppDataRoamingTyporatypora-user-imagesimage-20210919114605825.png)]

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    请输入数字:<input type="text" id="num" /><br />
    平方:<input type="text" id="sqrNum" /><br />
    立方:<input type="text" id="cubeNum" /><br />
    四次方:<input type="text" id="fourNum" /><br />
</body>

</html>
<script src="./js/观察者的代码.js"></script>
<script>

    
window.onload = function(){
    //1、定义观察者对象:
    
    let obj1 = new Observer();
    
    // 2、订阅    
    // 订阅(完成平方)
    obj1.addSubsciber(function(what){
        document.getElementById("sqrNum").value = what*what;
    });

    // 订阅(完成立方)
    obj1.addSubsciber(function(what){
        document.getElementById("cubeNum").value = what*what*what;
    });
    
    // 订阅(完成四次方)
    obj1.addSubsciber(function(what){
        document.getElementById("fourNum").value = what*what*what*what;
    });

    // 3、发布
    document.getElementById("num").oninput = function(){
        obj1.publish(this.value);
    }

}

</script>



五、代理模式



1、概念和应用场景

​ 代理模式的定义是把对一个对象的访问, 交给另一个代理对象来操作。

​ 如:

​ 一般人需要打官司,需要找代理律师(术业有专攻);

​ 你需要打扫房子,可以找保洁公司(术业有专攻);

​ 我们在租房子的时候去找中介(因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做)

应用场景:

如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:

1、修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。

2、就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。

使用代理模式,可以将功能划分的更加清晰,有助于后期维护!



2、代码:


// 代理模式:
//   一个类需要完成一件事情,它会委托给另外一个类完成。

function Person(name,money){
    this.name = name;
    this.money = money;
    // 让另外一个类作为委托类的属性
    this.delegate = null;
}

// 设置代理
Person.prototype.setDelegate = function(obj){
    
    this.delegate = obj; //人的代理方是狗
    obj.weituo = this; //狗的委托方是人
}

Person.prototype.lookDoor = function(){
    this.delegate.lookDoor();
}

function Dog(name){
    this.name = name;  
    // 狗的委托方 
    this.weituo = null;
}

Dog.prototype.lookDoor = function(){
    console.log(this.name+"在聚精会神地盯着大门…………");
    this.weituo.money -= 5;
}

let p1 = new Person("老王");

// 设置dog为person的代理
let d1 = new Dog("小黑");
p1.setDelegate(d1);

console.log(p1.delegate);



第二十八天、jQuery



第二十九天、jQuery



第三十天、jQuery



第三十一天、nodeJS



本节课目标

  1. Nodejs简介及COMMONJS模块化规范
  2. nodeJS搭建web服务器
  3. 内置模块 http , fs , path (根据情况进行讲解)
  4. Nodejs路由搭建(根据情况讲解)
  5. nodemon 测试工具使用
  6. npm , nrm 工具使用;



一、 nodeJS介绍



1、回忆以前使用php时,

当在浏览器中输入一个网址时,计算机如何找到对相应的文件:

http://ip地址:端口号/路径/文件

1)、根据ip地址,找到对应的服务器(计算机)

2)、根据端口号,找到服务器上对应的软件(如:apache)

3)、服务器软件(apache),根据路径和文件,在服务器软件对应的根目录下找到对应的文件。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zDmgzgPn-1641803458657)(C:\Users\tian\AppData\Roaming\Typora\typora-user-images\image-20210919171112920.png)]



2、nodeJS介绍

NodeJS不但自己可以完成服务器软件的功能,也不再需要php,asp,jsp等传统的服务器语言

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rWBA9QcX-1641803458658)(C:\Users\tian\AppData\Roaming\Typora\typora-user-images\image-20210919171210126.png)]

1)、nodeJS就是让JavaScript代码可以实现服务器端的功能,相当于php,jsp,asp。打破了JS只能做前端的局限。 JS既能做前端又能做后端,意味着……

2)、nodeJS是一个基于Chrome V8引擎的

JavaScript运行环境

。nodeJS使用了事件驱动,非阻塞式的I/O模型,使其轻量又高效。即:

JavaScript既可以运行在浏览器上,也可以运行在nodJS的环境里。

3)、nodeJS可以解析JS代码,而没有浏览器安全级别的限制,因为,它的运行可以脱离浏览器的环境,而是在服务端运行;提供了很多系统级别的API,如:

文件的读写

进程的管理

网络通信

​ ……

4)、NodeJS的包管理器npm,成为世界上最大的开放源代码的生态系统。



二、环境搭建


1、下载:

进入nodeJS官网(https://nodejs.org/en/),中文官网(http://nodejs.cn/)进行下载


2、安装:

​ windows环境下安装,没有特殊的,只需要全部下一步即可。


3、配置环境变量:

https://blog.csdn.net/jiang7701037/article/details/80707786


4、第一个nodeJS代码的体验

​ 1)、新建js文件(如:hello.js)

   console.log("原生JS的代码在node下可以直接执行");

​ 2)、在命令行运行js:

​ 进入cmd(命令行),输入 node hello.js或者node hello

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-afpcrBDm-1641803458658)(C:\Users\tian\AppData\Roaming\Typora\typora-user-images\image-20210919172232343.png)]

​ 这是令我们惊奇的地方(让我静静):“js不需要浏览器也可以运行,因为有了nodeJS环境”(我想静静,静静不想我)



三、COMMONJS模块化规范

commonJS的模块化是后端的模块化规范,nodeJS使用的就是commonJS规范。



1、引入别的模块:require

1)、引入官方模块和第三方模块时,不需要写路径

const http = require('http');//引入http模块

2、引入自定义模块需要写路径:

const routes = require("./routes/index");//表示引入当前路径下的routes文件夹下的index模块(文件)



2、对外导出:module.exports

对外导出,就是对外暴露,可以暴露基本类型的变量,对象,函数等等。


1)、对外导出一个:

//person.js
var p = { 
	id : "007",
	name :"张三丰",
	work:function(str){
		return this.name+"在努力地敲代码";
	},
	
	add:function(n1,n2){
		return n1+n2;
	}	
}
//模块接口暴露
module.exports = p; //外部可以使用p对象里的所有属性和方法


2)、对外导出多个:

//person.js
var p = { 
	id : "007",
	name :"张三丰",
	work:function(str){
		return this.name+"在努力地敲代码";
	},
	
	add:function(n1,n2){
		return n1+n2;
	}	
}

//模块接口暴露
module.exports.work = p.work; //表示外部只能使用work函数
module.exports.add = p.add;//表示外部只能使用add函数



四、nodeJS搭建web服务器

const http = require(‘http’);//引入http模块
const hostname ='127.0.0.1';//服务器地址
const port = 706;//服务器的端口号
const server = http.createServer((request,response)=>{//使用http来创建服务器,
		response.statusCode=200;
		response.setHeader('Content-Type','text/plain');
		response.end('hello nodeJS\n');
	});
server.listen(port,hostname,()=>{//服务器监听
	console.log(`Server running at http://${hostname}:${port}/`);
}); 

1)启动服务器: 在命令行窗口输入命令:node server.js

2)在浏览器输入 http://localhost:706/



五、内置模块 http , fs , path (根据情况进行讲解)



1、http

​ http模块主要完成和前端交互(还可以完成请求其它服务器),http模块对外暴露的是对象。



方法:



  • createServer(function(request,response){}): 创建服务器

    **
  • ​ get(): 发送get请求,获取地址对应的数据(主要是获取其它服务器的数据)。
  • ​ request():如何抓取异步的数据


使用Http模块搭建服务器的步骤

1)、引入http模块

let http = require('http')

2)、创建web服务 返回http对象

let app = http.createServer((req,res)=>{
	req 请求体(请求对象)  浏览器->服务器
	
	req.url  地址   提取地址栏数据
	req.on('data') 提取非地址栏数据 所有的http[s]都会触发end事件
	req.on('end') 
	
	res 响应体(响应对象)  服务器->浏览器
	
	res.writeHead(200,{'Content-Type':'text/html;charset=utf-8'});响应头设置
	res.write(字符/数据<string><buffer>) 返回数据
	res.end() 结束响应 必须
	
})

3)、监听服务器

app.listen(端口,[地址],[回调])

监听成功,回调一次

端口: 1-65535 1024以下系统占用

虚拟地址localhost 真实域名xx.duapp.com

4)、启动服务器的命令:

node 文件名

热部署和启动命令

由于,每次更新代码后, 需要重新启动服务器,很麻烦

推荐命令行工具:

supervisor

或者

nodemon

安装方式:

npm install supervisor -g

或者

npm install nodemon -g

启动服务器 nodemon 文件名



2、静态资源托管(fs模块)




2.1)介绍

fs:fileSystem; 这个模块可以读取文件的内容,给文件写内容,创建文件夹,删除文件夹等等有关文件和文件夹的一切操作。


一般来说,前端的如下代码都会请求一个静态资源

<a href=".."></a>
<img src="..."/>
location.href="..."
body{
    background:url(....)
}


后端资源读取静态资源文件就要使用fs模块

fs.readFile(文件名,[编码方式],回调(err,data));


2.2) fs模块使用


读取

  • ​ 异步的方式:
fs.readFile('文件路径',[编码方式],(err,data)=>{})

[^err ]: err 错误 ,null没有错误

  • 同步的方式:
let data = fs.readFileSync('文件路径') 

处理错误

try{

​ 要排错的代码

}catch(e){

}


更名

fs.renameSync('改前','改后');


删除

fs.unlinkSync('文件路径')



3 path系统模块

操作系统磁盘路径

windows:

c:\user\admin

磁盘的路径的分割是: \ 网络上的路径分割是 /

mac:

~/desktop/1901


API

磁盘路径解析

parse

path.parse('c:\\wamp\\xx.png') // string -> object

//返回
{
   root: 'c:\\', 盘符
   dir: 'c:\\wamp', 目录
   base: 'xx.png',  文件名
   ext: '.png', 扩展名
   name: 'xx' 	文件,不含扩展名
}

片段合并

join

path.join('磁盘路径1','磁盘路径2''磁盘路径n')

__dirname 魔术变量 返回当前文件所在的磁盘路径

片段合并

resolve

path.resolve('磁盘路径1','磁盘路径n')

合并磁盘片段,右到左找,如果找到了根,那就不再朝前找了;拼接时,是从左到右拼接,如果没有给根路径,以当前文件路径为根(即默认:加上 __dirname)。



六、Nodejs路由搭建

1、概念:

路由是指如何定义应用的端点(URIs)以及如何响应客户端的请求。

即:根据前端请求路径,调用不同的函数,就是引路者的角色

2、代码:

//1、server.js
var http = require('http');
var url = require('url');
var router = require('./router/index');//包含路由设置的模块

var server = http.createServer((request,response)=>{
    response.writeHeader(200,{"Content-type":"text/html;charset=utf-8"});
    if(request.url!=='/favicon.ico'){
        var pathName = url.parse(request.url).pathname.replace(/\//,'');
        console.log(pathName);
        try{
            router[pathName](request,response);
        }catch(err){
            router['error'](request,response);
        }
    }
    response.write("hello,我来自nodeJS");
    response.end();
});
server.listen(8000);
console.log("server listen localhost:8000");


//2、./router/index.js
let file = require("./file");
module.exports = {
      …………  
    //读取图片文件
    "img":function(request,response){
        file.readImg("img/2.jpg",response);
    },
    //读取html文件。
    "index":function(request,response){
        file.readHTML("index.html",response);
    }
};

//3、./file.js

路由模块文件(file.js):
var fs = require('fs');

module.exports = {
  readImg: function (file, res) {
      fs.readFile(file, 'binary', function (err, data) {
         if (err) throw err
         res.writeHead(200, {'Content-Type': 'image/jpeg'})
         res.write(data, 'binary')
         res.end()
    })
  }readHTML: function (fileName, res) {
        fs.readFile(fileName, 'utf8',  (err, data)=> {
            if (err) throw err;
            res.writeHead(200, {'Content-Type': 'text/html'})
            res.write(data, 'utf8')
            res.end()
        });
    }
}



七、nodemon 测试工具使用

热部署和启动命令

由于,每次更新代码后, 需要重新启动服务器,很麻烦

推荐命令行工具:

supervisor

或者

nodemon

安装方式:

npm install supervisor -g

或者

npm install nodemon -g

启动服务器 nodemon 文件名



八、npm , nrm 工具使用;



1、npm



介绍:

​ npm是Node.js的包管理工具(nodeJS package manager)。

​ 前面,我们用过官方模块,也用过自己写的模块。但是,我们在实际开发中,会用到很多很多很多的第三方模块。

​ 如果需要使用别人写的某个包,每次都根据名称搜索一下官方网站,下载代码,解压,再使用,非常繁琐。于是一个集中管理的工具应运而生:大家都把自己开发的模块打包后放到npm官网上,如果要使用,直接通过npm安装就可以直接用,不用管代码存在哪,应该从哪下载。npm服务器上放置了50多万的包,进行统一管理。

​ 更重要的是,如果我们要使用模块A,而模块A又依赖于模块B,模块B又依赖于模块X和模块Y,npm可以根据依赖关系,把所有依赖的包都下载下来并管理起来。否则,靠我们自己手动管理,肯定既麻烦又容易出错。

​ npm已经在Node.js安装的时候顺带装好了。我们在命令提示符或者终端输入npm -v



使用:

​ 明天讲解gulp的时候会实战

​ 1)、 安装第三方模块:

           ```js

npm install 模块名 参数(可选:-g –save-dev –save)

“`

​ 2)、卸载第三方模块:

   ```js

npm uninstall 模块名 参数(可选:-g –save-dev –save)

“`



2、nrm

可以选择npm的服务器来源(选择最快的源进行安装)

npm install nrm -g     安装选择源的工具包
nrm ls 查看所有源
nrm test 测试所有源,可以看到当前哪个源的速度快
nrm use 切换源名 ,切换到最快的源



第三十二天、gulp



本节课目标:

  1. 前端工程化
  2. gulp介绍
  3. gulp的环境搭建(电脑环境)
  4. 本地环境搭建(项目环境)
  5. gulp工程化的工作:复制/编译/压缩/合并/即时刷新/



一 、前端工程化


1、前端为什么要工程化

​ 在web开发的初期,前后端没有分离(全栈时期)。前端完成页面布局,简单的JS交互。没有任何业务逻辑而言。前端处理的工作非常简单(叫做瘦客户端),用户体验不是很好。

​ 随着ajax的出现,前端开始完成更多的事情(富客户端),用户体验开始变好,业务也越来越复杂。这样,就带来了性能问题。同时,由于前端的html,CSS,JS代码是直接暴露给浏览器端的,所以,带来了一定的代码泄露。

​ 随着智能手机的出现,前端运行的平台越来越多,市场竞争越来越激烈。用户也越来越考虑到体验。前后端开始分离,有了前端和后端工程师的区分。前端开发由WebPage模式为主转变为WebApp模式

​ 如果使用原始的开发方式完成前端的开发,会导致的问题:

​ 1)、代码暴露

​ 2)、性能问题

​ 3)、兼容性问题

​ 4)、没法完成模块化

…………………………………………


2、工程化的作用

​ 前端工程化就是为了让前端开发能够“

自成体系

”。而不是后端的附属品。分别从

模块化



组件化



规范化



自动化

着手。从设计—》到开发—》到编译打包—-》到上线。

如:

​ 1)、为了解决代码暴露,性能问题:可以对代码进行压缩、合并

​ 2)、兼容性问题:ES6转ES5

​ 3)、模块化方案

​ 4)、开发服务器的搭建,自动部署

​ 5)、自动化的处理


3、工程化的工具:

​ 打包相关的:grunt,gulp,webpack,vite

​ 版本管理的:git



二、gulp的介绍

​ gulp是前端开发过程中

自动化

项目的构建利器;她不仅能对网站资源进行优化,而且在开发过程中很多

重复的任务

能够使用正确的工具自动完成;gulp是基于Nodejs的。

​ Gulp能完成 javascript/coffee/sass/less/html/image/css 等文件的的测试、检查、合并、压缩、格式化、浏览器自动刷新、部署文件生成,并监听文件在改动后重复指定的这些步骤。

​ 在实现上,Gulp借鉴了Unix操作系统的管道(pipe)思想,前一级的输出,直接变成后一级的输入,使得在操作上非常简单。



三、gulp环境搭建(电脑环境)



1、安装node

​ 测试:

​ node -v

​ npm -v



2、全局安装gulp

​ 全局安装,就是给计算机(操作系统)安装软件。跟安装qq,微信,wps是同样的道理,只不过这个安装不是可视化的,是用命令进行安装的。

​ 全局安装gulp的

目的

是:可以在命令行使用gulp命令(和在命令行使用node是同样的道理)


命令:

npm install gulp -g     

g:global,全局的意思


测试是否安装成功:

gulp -v

注意配置环境变量(C:\Users\用户名\AppData\Roaming\npm\gulp)



四、本地环境搭建(项目环境)



1、产生package.json文件

​ 使用工程化的方式进行项目的管理,由package.json文件完成项目的描述和配置。


命令:

npm init -y    //回车后,在项目目录下,就会产生一个package.json文件。

注意:

​ 给项目建立个目录。这些命令是在项目目录下执行的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sm61neFN-1641803458658)(C:\Users\tian\AppData\Roaming\Typora\typora-user-images\image-20210919205602712.png)]

package.json文件示例:

{
  "name": "gulpdemo",//项目名称,不能有大写,也不要用gulp
  "version": "1.0.0",//项目版本号
  "description": "",//项目描述
  "main": "index.js",//入口文件
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],//关键词
  "author": "",//作者
  "license": "ISC"//许可协议
}



2、本地安装gulp:

​ 本地安装gulp,就是给项目目录下安装。用npm从npm服务器上把gulp的相关代码下载到项目目录(node_modules)下


命令:

npm install gulp --save-dev  //  --save-dev   :是开发依赖,后期会给大家慢慢解释这个参数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1QcyIVDp-1641803458659)(C:\Users\tian\AppData\Roaming\Typora\typora-user-images\image-20210919205551570.png)]



3、在项目目录下创建gulpfile.js文件

​ gulpfile.js文件一定要放在项目目录下,文件名不要自定义。这个文件的作用就是告诉gulp应该做什么事(也就是工程化中所做的事情)。如:复制文件,压缩文件,合并文件。

var gulp = require("gulp");//必须要引入

gulp有四个API:

gulp.task(),
gulp.src()
gulp.dest()
gulp.watch()



五、写gulp的代码完成工程化的工作

**非常重要的一步:**也是学习gulp的目的。因为,gulp在公司里用的越来越少了,所以,我们学习gulp的目的是:

1)、了解工程化的作用

2)、

区分开发目录和发布目录



1、复制单个文件

**需求:**把项目目录(开发目录)下的index.html文件复制到(发布目录):D:\phpStudy\WWW\web912


代码:

//gulpfile.js

var gulp = require("gulp");//必须要引入

gulp.task("copy-html", async function(){
	gulp.src("index.html").pipe(gulp.dest("D:\\phpStudy\\WWW\\web912"));
});

定义任务的函数:gulp.task(任务名,任务名对应函数);

选择文件来源:gulp.src();

管道:pipe();

文件放置的目的地:gulp.dest();


执行:

gulp copy-html



2、复制多个文件

gulp.task("copy-file", async function(){
	gulp.src("*.html").pipe(gulp.dest("D:\\phpStudy\\WWW\\web912"));
});



3、复制文件时,指定多个扩展名:

gulp.task("copy-file", async function(){
	gulp.src("*.{jpg,png}").pipe(gulp.dest("D:\\phpStudy\\WWW\\web912"));
});



4、复制某个文件夹下的所有文件:

gulp.task("copy-css", async function(){
	gulp.src("css/**/*").pipe(gulp.dest("D:\\phpStudy\\WWW\\web912"));
});

以上代码中的 “css/**/*” 把css文件夹下的所有的内容(包括子文件夹,不管有多少级)都进行复制



5、复制多个文件夹下的文件:

gulp.task("copy-cssandimg", async function(){
	gulp.src(['css/**/*','img/*']).pipe(gulp.dest("D:\\phpStudy\\WWW\\web912\\static"));
});



6、复制时过滤文件:

gulp.task("copy-filter", async function(){
	gulp.src( ['img/*',"!img/02.jpg"] ).pipe(gulp.dest("D:\\phpStudy\\WWW\\web912"));
});



7、监视:


以上命令,每次当文件改变后,都得执行命令,如果有自动执行就非常好了,肯定有。

gulp.task("watchall",async function(){
   gulp.watch("index.html", async function(){
       gulp.src("index.html").pipe(gulp.dest("D:\\phpStudy\\WWW\\web912"));
   });
});

gulp.watch(); 监听的



六、使用gulp插件

​ gulp本身只做一些文件的拷贝,监视等等,但是它提供了很多的接口,由插件完成更多对应的任务,如:sass文件的编译,合并文件,压缩文件,优化图片的尺寸,创建本地的开发服务器。下面先介绍常用的,更多的可以找gulp的插件。



1、合并文件

安装插件:gulp-concat

npm install gulp-concat --save-dev

代码:

修改gulpfile.js

// let basePath = "D:\\phpStudy\\WWW\\2104\\taobao";

let basePath = ".\\dist";

gulp.watch(["js/index.js","js/login.js"],async function(){
    gulp.src(["js/index.js","js/login.js"])
        .pipe(concat("tools.js"))
        .pipe(gulp.dest(basePath+"\\js"));
})



2、合并并压缩

安装压缩插件:gulp-uglify

npm install gulp-uglify --save-dev

代码:

修改gulpfile.js

const gulp = require("gulp"); 
const concat = require("gulp-concat");
const uglify = require("gulp-uglify");

gulp.watch(["js/index.js","js/login.js"],async function(){
    gulp.src(["js/index.js","js/login.js"])
        .pipe(concat("tools.js"))
        .pipe(uglify())
        .pipe(gulp.dest(basePath+"\\js"));
})

注意:uglify没法压缩ES6的语法,所以,需要安装babel模块把ES6的代码转成ES5的代码。



3、ES6+ 代码转成ES5

安装插件:gulp-babel 和 @babel/core @babel/preset-env

npm install  --save-dev gulp-babel @babel/core @babel/preset-env

代码:

修改gulpfile.js

const gulp = require("gulp"); 
const concat = require("gulp-concat");
const uglify = require("gulp-uglify");
const babel = require('gulp-babel');

 
gulp.task('default', () =>
    gulp.src('src/app.js')
        .pipe(babel({
            presets: ['@babel/env']
        }))
        .pipe(gulp.dest('dist'))
);

// 合并并压缩,转成ES5

gulp.watch(["js/index.js","js/login.js"],async function(){
    gulp.src(["js/index.js","js/login.js"])
        .pipe(concat("tools.js"))            
        .pipe(babel({
        presets: ['@babel/env']
    }))
    .pipe(uglify())
    .pipe(gulp.dest(basePath+"\\js"));
    
})



4、合并,压缩,重命名

安装插件:

npm install gulp-rename --save-dev

代码:

const gulp = require("gulp"); 
const concat = require("gulp-concat");
const uglify = require("gulp-uglify");
const babel = require('gulp-babel');
const rename = require("gulp-rename");

gulp.watch(["js/index.js","js/login.js"],async function(){
    gulp.src(["js/index.js","js/login.js"])
        .pipe(concat("tools.js"))
        .pipe(gulp.dest(basePath+"\\js"))
        .pipe(babel({
        presets: ['@babel/env']
    }))
        .pipe(uglify())            
        .pipe(rename("tools.min.js"))
        .pipe(gulp.dest(basePath+"\\js"));
})



5、压缩css

安装插件:

npm install gulp-minify-css --save-dev

代码:




6、启动服务

前端服务器,只完成请求响应,不做代码的执行。用在前后端分离的场景

npm install gulp-connect --save-dev

代码:

const connect = require("gulp-connect");

gulp.task("server",async function(){
    connect.server({
        host:"10.35.165.16",
        root:"./dist"
    })
});



第三十三天、sass



本节课目标:

sass介绍

sass的安装

sass的基本使用步骤

sass的语法



一、sass介绍

sass是可以让程序员使用编程的方式编写CSS。语法上又完全兼容CSS3。所以,是CSS的扩展语言。


sass官网



二、sass的安装

​ sass是基于ruby语言开发的,所以,必须要安装ruby环境,但是不需要学习ruby语言(注:mac下自带Ruby无需在安装Ruby!)。先从官网

下载Ruby

并安装

​ 安装ruby:可视化方式。

​ 测试ruby是否安装成功

ruby -v

​ 安装sass

gem install sass

因为国内网络的问题导致

gem

源间歇性中断因此我们需要更换

gem

源。

1、把当前文件夹(readme_sass安装问题.txt 所在文件夹)的所有文件放在自己配置的服务器上,如:d:/phpStudy/www

2、改变sass的服务器来源

在命令行执行如下两行命令:

gem sources –remove https://rubygems.org/

gem sources -a http://localhost

3、安装sass

gem install sass



三、sass的基本使用步骤


1、新建sass文件

//demo01.scss

@charset ”utf-8; // 设置支持中文注释
body{
    background-color:#fff;
	font-size:16px;
}


2、使用sass编译成css文件


sass  scss文件 : 编译好的目标 css 全路径名称


3、自动编译命令:

 sass --watch 要监听的目录:编译后的 css 文件的位置
 
 如:
 sass --watch .:css  //表示把当前目录下的sass文件编译后放在css文件夹下



四、sass的语法



1、变量

SASS 中的变量,

必须是$符号开头

,后面紧跟变量名,变量名称和变量值之间要使用冒号:进行分隔,可以把反复使用的

css

属性值 定义成变量,然后通过变量名来引用它们,而无需重复书写这一属性值。

$nav-color: #F90;

.nav {
   width: 100px;
   color: $nav-color;
}



2、嵌套——Nesting

SASS 中的嵌套主要说的是选择器嵌套和属性嵌套两种方式,正常项目中通常使用的都是选择器嵌套方案



1)、选择器嵌套

css中重复写选择器是非常恼人的。如果要写一大串指向页面中同一块的样式时,往往需要 一遍又一遍地写同一个ID

如下的CSS代码:

#content article h1 { color: #333 }
#content article p { margin-bottom: 1.4em }
#content aside { background-color: #EEE }

改写成sass的写法

#content {
  article {
    h1 { color: #333 }
    p { margin-bottom: 1.4em }
  }
  aside { background-color: #EEE }
}

把sass的嵌套和html标签的嵌套关系对应起来就像



2)、属性嵌套
#box4{    
    border:{
        top:{
            width: 100px;
            color:yellow;
        }
    }
}



3、混合——Mixin

sass 中可以通过@mixin 声明混合,可以传递参数,参数名称以$开始,多个参数之间使用逗号分隔,@mixin 的混合代码块由@include 来调用

1)、无参数混合——不建议使用,如果是这样的代码块,直接使用后面提到的@extend 来处理

定义:

@mixin large-text {
  font: {
    family: Arial;
    size: 20px;
    weight: bold;
  }
  color: #ff0000;
}

引用

.page-title {
  @include large-text;
  padding: 4px;
  margin-top: 10px;
}

2)、有参混合

参数用于给混合指令中的样式设定变量,并且赋值使用。在定义混合指令的时候,按照变量的格式,通过逗号分隔,将参数写进圆括号里。引用指令时,按照参数的顺序,再将所赋的值对应写进括号:

定义:

@mixin sexy-border($color, $width) {
  border: {
    color: $color;
    width: $width;
    style: dashed;
  }
}

引用:

p { @include sexy-border(blue, 1in); }

3)、参数的默认值

定义:

@mixin redbox($width:300px,$height:300px) {
    width: $width;
    height: $height;
    background-color: red;
}

引用:

#box5{
    @include redbox();
    border:1px solid black;
}

#box6{
    @include redbox(500px,400px);
    opacity: 0.5;
    font-size: 12px;
}


#box6{
    @include redbox($height:400px);
    opacity: 0.5;
    font-size: 12px;
}



4、继承——extend

在设计网页的时候常常遇到这种情况:一个元素使用的样式与另一个元素完全相同,但又添加了额外的样式。通常会在 HTML 中给元素定义两个 class,一个通用样式,一个特殊样式。假设现在要设计一个普通错误样式与一个严重错误样式,一般会这样写:

.error {
  border: 1px #f00;
  background-color: #fdd;
}
.error.intrusion {
  background-image: url("/image/hacked.png");
}
.seriousError {
  @extend .error;
  border-width: 3px;
}

编译结果为:

.error, .seriousError {
  border: 1px #f00;
  background-color: #fdd; }

.error.intrusion, .seriousError.intrusion {
  background-image: url("/image/hacked.png"); }

.seriousError {
  border-width: 3px; }



5、Partials && @import

​ CSS 本身包含一个指令@import,但是 CSS 中的@import 每次执行都会发送一次新的请求都会消耗一定的资源

​ SASS 中扩展了这个指令,会将包含的编译成一个 css 文件,切割成小的部分(Partials)包含进来进行处理。

Partials 这样的文件,命名规范是以下划线开头的,这样的 scss 文件不会被编译成 css

​ 在 SCSS 文件中引入指令@import 在引入 Partials 文件时,不需要添加下划线。

如:parials文件名 为 _colors.scss。

引入时:

@import "colors";



6、数据类型

Sass 支持 7 种主要的数据类型:

  • 数字,

    1, 2, 13, 10px
  • 字符串,有引号字符串与无引号字符串,

    "foo", 'bar', baz
  • 颜色,

    blue, #04a3f9, rgba(255,0,0,0.5)
  • 布尔型,

    true, false
  • 空值,

    null
  • 数组 (list),用空格或逗号作分隔符,

    1.5em 1em 0 2em, Helvetica, Arial, sans-serif
  • maps, 相当于 JavaScript 的 object,

    (key1: value1, key2: value2)

详细使用参照官网:https://www.sass.hk/docs/



7、控制指令——Control Directives

SASS 中为了更加方便的处理一些带有逻辑性的样式,如满足某些条件的时候使用指定的样

式,或者根据指定列表中的项目循环输出样式等,提供了一些控制指令进行处理

@if:条件控制指令

@for:循环指令

@each:循环指令

@while:循环指令



1)、@if

@if 指令是 SASS 中的一个控制指令,用于在表达式满足条件(true)的时候输出指定的样式,在不满足条件(false)或者表达式为 null 的情况下输出其他的样式

$theme:light;

// body的样式搜索
body{
    @if $theme==dark{
        background-color: black; 
    }@else if $theme==light{
        background-color: white;
    }@else{
        background-color: red;
    }    
    font-size: $fontSize;
}



2)、@for

@for 指令在 SASS 中用于重复处理一组指令

有两种表现形式:

@for $var from <开始值> through <结束值>

@for $var from to

@for $i from 1 through 10 {
    ul li:nth-child(#{$i}){
        left:($i - 1)*50px;
    }
}


3)、

@each

@each 在 Sass 中主要被用来进行列表或者映射数据的循环

主要表示形式:@each $var in

$var 可以是任意变量名称,是 SASS 表达式并且必须是 List


$icons: error waring success;

@each $icon in $icons {
   .icon-#{$icon}{
       background-image: url(img/#{$icon}.jpg);
   } 
}

$colors: red orange yellow green blue;
$i:0;

@each $color in $colors {
    $i:$i + 1;
    ul li:nth-child(#{$i}) div:first-child{
        background-color:$color;
    }
}


4)、

@while

$i: 6;
@while $i > 0 {
  .item-#{$i} { width: 2em * $i; }
  $i: $i - 2;
}



8、函数指令 (Function Directives)

Sass 支持自定义函数,并能在任何属性值或 Sass script 中使用:

$grid-width: 40px;
$gutter-width: 10px;

@function grid-width($n) {
  @return $n * $grid-width + ($n - 1) * $gutter-width;
}

#sidebar { width: grid-width(5); }



第三十四天、git



本节课目标

1.版本控制工具的介绍

2.版本管理工具

3.Git 本地操作(add commit status –reset hard refglog) 实际操作;

4.注册gitHub账号

1.git分支结构(branch checkout merge )

2.Git提交远程分支( git push origin 本地分支:远程分支)

3.Git 获取远程代码( clone pull )

4..gitignore使用

git提交gulp构建的项



一、版本控制工具的介绍

1、

2、版本管理工具所经历的阶段:



二、git工具的环境搭建



1、安装



2、初始化user.name,user.email

  1. 进入git bash

  2. 在命令行,输入以下内容(把your Name 改成你自己的用户名):

git config --global user.name "Your Name" 
  1. 在命令行,输入以下内容(把email@example.com 改成你自己的邮箱):
git config --global user.email  "email@example.com"



3、在github上或者gitee上注册账号



4、在本地创建ssh key(公钥)

在git bash里输入:

ssh-keygen -t rsa -C youremail@example.com

你需要把邮件地址换成你⾃⼰的邮件地址,然后⼀路回车,使⽤默认值即可.

在⽤户主目录下(/c/Users/Administrator/.ssh/id_rsa)),看看有没有.ssh目录,如果有,再看看这个目录下 有没有id_rsa和id_rsa.pub这两个⽂件,如果已经有了,可直接 跳到下⼀步。



5、在远程创建ssh key:



三、在项目中使用git



1、建立远程仓库



2、建立本地仓库

(1)克隆远程仓库到本地

git clone   远程仓库地址



3、在项目中使用git进行代码的管理



1).把开发的代码放在本地版本库

(1) Git add

假设在项目下有个index.html文件。在 git Bash,输入:

git  add  index.html

(2) Git commit(提交)

在git Bash里输入:

git commit -m "这是新建的index.html"

其中”这是新建的index.html”只是说明或者说解释。

理解: add和commit;

​ Add:添加;相当于打了标记(实际上是存储在了暂存区stage里),告诉git,下次提交时把该文件进行提交。

Commit:提交。把所有打过标记的(即用add进行添加的文件),一次性进行提交(此时才有版本号)。即可以一次性把暂存区里的文件全部进行提交,提交了master分支(主分支)。提交完成后,暂存区里就没有文件了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uGCTvqq6-1641803458659)(file:///C:\Users\tian\AppData\Local\Temp\ksohtml11164\wps1.jpg)]



2).恢复(跳转)到某个版本的文件

1)、查看所有的版本的命令

git  log 

2)、要恢复到上一个版本

git reset  --hard  HEAD^

3)、恢复到指定版本

```js

git reset –hard 版本序列号 (可以用版本号的前七位就行)

“`



3).把本地版本库的代码放在远程库
git push -u git@github.com:tianwater/mytaobao.git



4、总结:

以后,每天做的git相关工作:

1)、git add 文件名

2)、git commit –m “版本描述”

3)、 git push -u 远端地址master

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WCZJbllp-1641803458660)(C:\Users\tian\AppData\Roaming\Typora\typora-user-images\image-20210920103220742.png)]



5、补充:git和gulp应用于现有的项目

a) 远程建立仓库(如:taobao1809),git clone到本地,并自动创建项目文件夹(taobao1809)

b) 在项目文件夹中(taobao1809),npm init,并本地安装gulp和gulp插件

c) 把现有的代码(html,css,图片文件等等)拷贝到项目目录(taobao1809)下

d) 编辑.gitignore文件

e) 把当前项目文件夹下的所有内容(不包括node _modules文件夹等等)add到暂存区,并且commit到版本库



四、gitignore的过滤规则



1、哪些文件需要进行版本管理:

​ 记住:版本管理工具,管的源代码的版本,即程序员写的代码。

​ 哪些不需要管理版本: .git文件夹,node_modules等等。

​ 那么:版本管理工具管的是开发目录下的(只有开发目录下的代码才是程序员写的),而不是发布目录下。



2、.gitignore文件的作用

如果希望一次性把所有的文件进行add。那么可以使用命名: git add .

但是,有些文件不需要放入版本库(如:.git,node_modules),如何解决,使用 .gitignore文件。

在使用git的时候,项目目录下的.gitignore文件的作用是告诉git哪些文件不需要添加到版本管理中。



3、以下列出常见的规则:

.git/ 过滤掉整个.git文件夹

node_modules/ 过滤掉整个node_modules文件夹

*.css 过滤所有.css后缀的文件;

css/common.css : 过滤掉具体的文件



4、如何产生 .gitignore文件

使用命令: touch .gitignore



五、git分支



1、为什么要使用分支

在开发项目的过程中使用版本控制工具,建立版本库(仓库),需要分为开发库,测试库,发布库。因为,开发人员需要不断前进完成功能,测试人员在后面紧跟测试,售后人员需要稳定版本上线。

举例说明:

如果只有一个版本库,会存在什么问题:

1、 场景1:程序员在下班时想把自己的代码,提交到版本库,但是,此代码并没有写完(开发人员自己还没有测试),如果这个时候,测试人员下载最新版本进行测试时,那么就会莫名其妙(以为,程序员提交了的功能有问题)。因为,他们是共享一个库的。开发人员只要上传了代码,测试人员立即就可以得到。如果不上传代码,那么代码就有可能会丢失。

2、 场景2:程序员A修改了自己的代码,还需要等待程序员B的代码,才能一起联调功能,而此时,程序员A上传了自己的代码,测试人员得到代码后,也会莫名其妙(以为,程序员提交了一个有问题的代码)

3、 由于没有明确的稳定版本(stable版本),导致上QA(测试库),上生产(发布库),只能采用增量更新,代码管理非常混乱,而且,测试人员的代码和开发人员的代码耦合度很高。

解决问题:

1、 分支管理策略:采用适当的分支管理策略来保证开发库、测试库、发布库的隔离。有了各自的库,开发人员随时可以放心的提交自己没有写完的代码(提交的开发库,甚至自己可以有独立的开发库)而不用担心测试人员不小心拿到了还没有写完的代码。等到,开发人员都写完后(开发人员认为功能没有问题了),再把代码放到测试库里,供测试人员进行测试,这样一来,对于测试库来说,每个版本都是可以进行测试的版本;同理,测试人员测试完成认为可以上线时,才生成发布库,这样一来,发布库的每个版本都是可以发布的。即,开发库的版本数量是大于测试部版本数,测试库的版本数大于发布库的版本数,而发布库的版本就是对外开放的版本(即,用户使用的版本)。

2、 适当引入每日编译、持续集成、Code Review(代码评审)等敏捷开发的最佳实践

3、 采用自动化脚本完成上QA库、上发布库的部署工作,避免人工失误

在项目开发中,经常使用的三种版本管理策略是:不稳定主干策略、稳定主干策略、敏捷发布策略。

l 不稳定主干策略:使用用主干作为新功能开发主线,分支用作发布。

n 使用用主干作为新功能开发主线,分支用作发布。

n 被广泛的应用于开源项目。

n 比较适合诸如传统软件产品的开发模式,比如微软的office等。

n bug修改需要在各个分支中合并。

n 新代码在主干上开发,因此如果主干不能达到稳定的标准,就不可以进行发布。

n 这种策略的好处是没有分支合并的工作量,因此比较简单。


l



*稳定主干策略*

n 使用主干作为稳定版的发布。

n bug的修改和新功能的增加,全部在分支上进行。

n 不同的修改或新功能开发以分支隔离。

n 分支上的开发和测试完毕以后才合并到主干。

n 主干上的每一次发布都做一个标签而不是分支。

n 每次发布的内容调整起来比较容易。

n 缺点是分支合并所增加的成本。


l



*敏捷发布策略*

n 敏捷开发模式的项目中广泛采用,敏捷开发的项目具有固定的发布周期。

n 为每个task建立分支。

n 为每个发布建立分支,每个周期内的task分支需要合并到发布分支发布。

n 在完成发布后,发布分支的功能合并到主干和尚在进行的任务分支中。

n 一种稳定主干策略的变体。

n 团队成员要求较高。


l



*建议方案:*

此方案已稳定主干策略为主结合了一些敏捷发布策略的思路,具体实施方案如下:

1、主干时刻处于稳定状态,随时可以发布。设SCM人员对主干代码进行管理,普通开发人员只读。

2、SCM为开发任务建立开发分支。常规的可以以小组为单位建立分支,较大的任务可以建立专门的分支。

3、在发布日,从主干复制一个测试分支,需要在本发布日发布的各开发分支向此测试分支合并。

4、对测试分支代码进行测试,出现bug在测试分支上更改,无误后发布。

5、测试分支代码发布后,合并入主干,并在主干上进行标记。

6、对紧急修复(Hotfix)的情况,可以从主干复制出测试分支,在测试分支上进行紧急修改,并在测试后发布,发布后同样将代码合并会主干,做标记。

7、 Hotfix仅限于可以很快解决的小问题,如果更改时间过长,则需采用常规方法完成。

8、如果在测试分支测试过程中需要hotfix工作,则在复制一个新的测试分支进行hotfix,测试后发布。然后同时合并入原测试分支和主干,并在主干上做标记。此过程未在上图中画出。

9、测试分支发布后,开发分支可以删除;测试分支合并入主干后,测试分支可以定期删除。



*方案的优缺点*

方案优点

1、解决了没有实施分支策略时,代码不能经常签入的问题。

2、主干代码始终处于稳定的状态随时可以发布,降低了风险。

3、可以基于一个完整的测试分支进行测试及发布,而不是以口口相传的方式增量更新。

方案缺点

1、建立分支、合并分支增加了工作量。考虑实际情况,以及版本控制工具的辅助,增加的工作量应该可以接受。

2、如果某些开发分支工期跨越多个发布周期,修改过于剧烈,合并分支时可能工作量较大。可以考虑分解任务,避免过大的任务出现。

3、在同一时间最好只有一个测试分支,因此建立测试分支的权限需要限制,除hotfix场景外应当避免。



2、 git中使用分支

Git的分支比起svn的分支,git创建分支,切换分支,合并分支,删除分支都非常快(因为,git使用的是指针的方式切换分支的)

在没有使用分支之前,git会默认有一个分支,就是主分支(master分支,还记得 git push –u origin master这个命令吗?)

a) 创建分支(分支名为dev。)

Git branch dev

b) 切换当前分支到dev

Git checkout dev

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tuEw1hpd-1641803458660)(file:///C:\Users\tian\AppData\Local\Temp\ksohtml11164\wps2.jpg)]

此后的add和commit最终是提交到了dev分支。如果切换到master分支,那么,修改时不能看到的,因为,修改时在dev分支上进行的。

c) 可以一条命令完成创建并切换到新分支(-b:表示创建并切换)

Git checkout -b dev

d) 查看所有分支(当前分支前面会有星号*)

Git branch

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TOOjTyOH-1641803458660)(file:///C:\Users\tian\AppData\Local\Temp\ksohtml11164\wps3.jpg)]

e) 把dev分支的内容合并到当前分支(如:master分支)里。

1)、首先确保当前分支是master分支(用命令切换:git checkout master)

2)、命令合并 git merge dev

f) 删除分⽀:git branch -d dev

注意当前分支一定不能是要删除的分支(dev)



3、分支策略

分⽀策略 在实际开发中,我们应该按照⼏个基本原则进⾏分⽀管理: ⾸先,master分⽀应该是⾮常稳定的,也就是仅⽤来发布新版本,平时不能在上⾯干活; 那在哪干活呢?干活都在dev分⽀上,也就是说,dev分⽀是不稳定的,到某个时候,⽐如 1.0版本发布时,再把dev分⽀合并到master上,在master分⽀发布1.0版本; 你和你的⼩伙伴们每个⼈都在dev分⽀上干活,每个⼈可以都有⾃⼰的开发分⽀,时不时地往dev分 ⽀上合并就可以了。 所以,团队合作的分⽀看起来就像这样:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tnAV80hA-1641803458660)(file:///C:\Users\tian\AppData\Local\Temp\ksohtml11164\wps4.jpg)]

每个开发人员有自己的分支,完成后,放到dev分支。每次修改也可以临时建立一个分支,修改完成后,合并即可。



最后:项目

ottom: 1.4em }

#content aside { background-color: #EEE }


改写成sass的写法

```css
#content {
  article {
    h1 { color: #333 }
    p { margin-bottom: 1.4em }
  }
  aside { background-color: #EEE }
}

把sass的嵌套和html标签的嵌套关系对应起来就像



2)、属性嵌套
#box4{    
    border:{
        top:{
            width: 100px;
            color:yellow;
        }
    }
}



3、混合——Mixin

sass 中可以通过@mixin 声明混合,可以传递参数,参数名称以$开始,多个参数之间使用逗号分隔,@mixin 的混合代码块由@include 来调用

1)、无参数混合——不建议使用,如果是这样的代码块,直接使用后面提到的@extend 来处理

定义:

@mixin large-text {
  font: {
    family: Arial;
    size: 20px;
    weight: bold;
  }
  color: #ff0000;
}

引用

.page-title {
  @include large-text;
  padding: 4px;
  margin-top: 10px;
}

2)、有参混合

参数用于给混合指令中的样式设定变量,并且赋值使用。在定义混合指令的时候,按照变量的格式,通过逗号分隔,将参数写进圆括号里。引用指令时,按照参数的顺序,再将所赋的值对应写进括号:

定义:

@mixin sexy-border($color, $width) {
  border: {
    color: $color;
    width: $width;
    style: dashed;
  }
}

引用:

p { @include sexy-border(blue, 1in); }

3)、参数的默认值

定义:

@mixin redbox($width:300px,$height:300px) {
    width: $width;
    height: $height;
    background-color: red;
}

引用:

#box5{
    @include redbox();
    border:1px solid black;
}

#box6{
    @include redbox(500px,400px);
    opacity: 0.5;
    font-size: 12px;
}


#box6{
    @include redbox($height:400px);
    opacity: 0.5;
    font-size: 12px;
}



4、继承——extend

在设计网页的时候常常遇到这种情况:一个元素使用的样式与另一个元素完全相同,但又添加了额外的样式。通常会在 HTML 中给元素定义两个 class,一个通用样式,一个特殊样式。假设现在要设计一个普通错误样式与一个严重错误样式,一般会这样写:

.error {
  border: 1px #f00;
  background-color: #fdd;
}
.error.intrusion {
  background-image: url("/image/hacked.png");
}
.seriousError {
  @extend .error;
  border-width: 3px;
}

编译结果为:

.error, .seriousError {
  border: 1px #f00;
  background-color: #fdd; }

.error.intrusion, .seriousError.intrusion {
  background-image: url("/image/hacked.png"); }

.seriousError {
  border-width: 3px; }



5、Partials && @import

​ CSS 本身包含一个指令@import,但是 CSS 中的@import 每次执行都会发送一次新的请求都会消耗一定的资源

​ SASS 中扩展了这个指令,会将包含的编译成一个 css 文件,切割成小的部分(Partials)包含进来进行处理。

Partials 这样的文件,命名规范是以下划线开头的,这样的 scss 文件不会被编译成 css

​ 在 SCSS 文件中引入指令@import 在引入 Partials 文件时,不需要添加下划线。

如:parials文件名 为 _colors.scss。

引入时:

@import "colors";



6、数据类型

Sass 支持 7 种主要的数据类型:

  • 数字,

    1, 2, 13, 10px
  • 字符串,有引号字符串与无引号字符串,

    "foo", 'bar', baz
  • 颜色,

    blue, #04a3f9, rgba(255,0,0,0.5)
  • 布尔型,

    true, false
  • 空值,

    null
  • 数组 (list),用空格或逗号作分隔符,

    1.5em 1em 0 2em, Helvetica, Arial, sans-serif
  • maps, 相当于 JavaScript 的 object,

    (key1: value1, key2: value2)

详细使用参照官网:https://www.sass.hk/docs/



7、控制指令——Control Directives

SASS 中为了更加方便的处理一些带有逻辑性的样式,如满足某些条件的时候使用指定的样

式,或者根据指定列表中的项目循环输出样式等,提供了一些控制指令进行处理

@if:条件控制指令

@for:循环指令

@each:循环指令

@while:循环指令



1)、@if

@if 指令是 SASS 中的一个控制指令,用于在表达式满足条件(true)的时候输出指定的样式,在不满足条件(false)或者表达式为 null 的情况下输出其他的样式

$theme:light;

// body的样式搜索
body{
    @if $theme==dark{
        background-color: black; 
    }@else if $theme==light{
        background-color: white;
    }@else{
        background-color: red;
    }    
    font-size: $fontSize;
}



2)、@for

@for 指令在 SASS 中用于重复处理一组指令

有两种表现形式:

@for $var from <开始值> through <结束值>

@for $var from to

@for $i from 1 through 10 {
    ul li:nth-child(#{$i}){
        left:($i - 1)*50px;
    }
}


3)、

@each

@each 在 Sass 中主要被用来进行列表或者映射数据的循环

主要表示形式:@each $var in

$var 可以是任意变量名称,是 SASS 表达式并且必须是 List


$icons: error waring success;

@each $icon in $icons {
   .icon-#{$icon}{
       background-image: url(img/#{$icon}.jpg);
   } 
}

$colors: red orange yellow green blue;
$i:0;

@each $color in $colors {
    $i:$i + 1;
    ul li:nth-child(#{$i}) div:first-child{
        background-color:$color;
    }
}


4)、

@while

$i: 6;
@while $i > 0 {
  .item-#{$i} { width: 2em * $i; }
  $i: $i - 2;
}



8、函数指令 (Function Directives)

Sass 支持自定义函数,并能在任何属性值或 Sass script 中使用:

$grid-width: 40px;
$gutter-width: 10px;

@function grid-width($n) {
  @return $n * $grid-width + ($n - 1) * $gutter-width;
}

#sidebar { width: grid-width(5); }



第三十四天、git



本节课目标

1.版本控制工具的介绍

2.版本管理工具

3.Git 本地操作(add commit status –reset hard refglog) 实际操作;

4.注册gitHub账号

1.git分支结构(branch checkout merge )

2.Git提交远程分支( git push origin 本地分支:远程分支)

3.Git 获取远程代码( clone pull )

4..gitignore使用

git提交gulp构建的项



一、版本控制工具的介绍

1、

2、版本管理工具所经历的阶段:



二、git工具的环境搭建



1、安装



2、初始化user.name,user.email

  1. 进入git bash

  2. 在命令行,输入以下内容(把your Name 改成你自己的用户名):

git config --global user.name "Your Name" 
  1. 在命令行,输入以下内容(把email@example.com 改成你自己的邮箱):
git config --global user.email  "email@example.com"



3、在github上或者gitee上注册账号



4、在本地创建ssh key(公钥)

在git bash里输入:

ssh-keygen -t rsa -C youremail@example.com

你需要把邮件地址换成你⾃⼰的邮件地址,然后⼀路回车,使⽤默认值即可.

在⽤户主目录下(/c/Users/Administrator/.ssh/id_rsa)),看看有没有.ssh目录,如果有,再看看这个目录下 有没有id_rsa和id_rsa.pub这两个⽂件,如果已经有了,可直接 跳到下⼀步。



5、在远程创建ssh key:



三、在项目中使用git



1、建立远程仓库



2、建立本地仓库

(1)克隆远程仓库到本地

git clone   远程仓库地址



3、在项目中使用git进行代码的管理



1).把开发的代码放在本地版本库

(1) Git add

假设在项目下有个index.html文件。在 git Bash,输入:

git  add  index.html

(2) Git commit(提交)

在git Bash里输入:

git commit -m "这是新建的index.html"

其中”这是新建的index.html”只是说明或者说解释。

理解: add和commit;

​ Add:添加;相当于打了标记(实际上是存储在了暂存区stage里),告诉git,下次提交时把该文件进行提交。

Commit:提交。把所有打过标记的(即用add进行添加的文件),一次性进行提交(此时才有版本号)。即可以一次性把暂存区里的文件全部进行提交,提交了master分支(主分支)。提交完成后,暂存区里就没有文件了。

[外链图片转存中…(img-uGCTvqq6-1641803458659)]



2).恢复(跳转)到某个版本的文件

1)、查看所有的版本的命令

git  log 

2)、要恢复到上一个版本

git reset  --hard  HEAD^

3)、恢复到指定版本

```js

git reset –hard 版本序列号 (可以用版本号的前七位就行)

“`



3).把本地版本库的代码放在远程库
git push -u git@github.com:tianwater/mytaobao.git



4、总结:

以后,每天做的git相关工作:

1)、git add 文件名

2)、git commit –m “版本描述”

3)、 git push -u 远端地址master

[外链图片转存中…(img-WCZJbllp-1641803458660)]



5、补充:git和gulp应用于现有的项目

a) 远程建立仓库(如:taobao1809),git clone到本地,并自动创建项目文件夹(taobao1809)

b) 在项目文件夹中(taobao1809),npm init,并本地安装gulp和gulp插件

c) 把现有的代码(html,css,图片文件等等)拷贝到项目目录(taobao1809)下

d) 编辑.gitignore文件

e) 把当前项目文件夹下的所有内容(不包括node _modules文件夹等等)add到暂存区,并且commit到版本库



四、gitignore的过滤规则



1、哪些文件需要进行版本管理:

​ 记住:版本管理工具,管的源代码的版本,即程序员写的代码。

​ 哪些不需要管理版本: .git文件夹,node_modules等等。

​ 那么:版本管理工具管的是开发目录下的(只有开发目录下的代码才是程序员写的),而不是发布目录下。



2、.gitignore文件的作用

如果希望一次性把所有的文件进行add。那么可以使用命名: git add .

但是,有些文件不需要放入版本库(如:.git,node_modules),如何解决,使用 .gitignore文件。

在使用git的时候,项目目录下的.gitignore文件的作用是告诉git哪些文件不需要添加到版本管理中。



3、以下列出常见的规则:

.git/ 过滤掉整个.git文件夹

node_modules/ 过滤掉整个node_modules文件夹

*.css 过滤所有.css后缀的文件;

css/common.css : 过滤掉具体的文件



4、如何产生 .gitignore文件

使用命令: touch .gitignore



五、git分支



1、为什么要使用分支

在开发项目的过程中使用版本控制工具,建立版本库(仓库),需要分为开发库,测试库,发布库。因为,开发人员需要不断前进完成功能,测试人员在后面紧跟测试,售后人员需要稳定版本上线。

举例说明:

如果只有一个版本库,会存在什么问题:

1、 场景1:程序员在下班时想把自己的代码,提交到版本库,但是,此代码并没有写完(开发人员自己还没有测试),如果这个时候,测试人员下载最新版本进行测试时,那么就会莫名其妙(以为,程序员提交了的功能有问题)。因为,他们是共享一个库的。开发人员只要上传了代码,测试人员立即就可以得到。如果不上传代码,那么代码就有可能会丢失。

2、 场景2:程序员A修改了自己的代码,还需要等待程序员B的代码,才能一起联调功能,而此时,程序员A上传了自己的代码,测试人员得到代码后,也会莫名其妙(以为,程序员提交了一个有问题的代码)

3、 由于没有明确的稳定版本(stable版本),导致上QA(测试库),上生产(发布库),只能采用增量更新,代码管理非常混乱,而且,测试人员的代码和开发人员的代码耦合度很高。

解决问题:

1、 分支管理策略:采用适当的分支管理策略来保证开发库、测试库、发布库的隔离。有了各自的库,开发人员随时可以放心的提交自己没有写完的代码(提交的开发库,甚至自己可以有独立的开发库)而不用担心测试人员不小心拿到了还没有写完的代码。等到,开发人员都写完后(开发人员认为功能没有问题了),再把代码放到测试库里,供测试人员进行测试,这样一来,对于测试库来说,每个版本都是可以进行测试的版本;同理,测试人员测试完成认为可以上线时,才生成发布库,这样一来,发布库的每个版本都是可以发布的。即,开发库的版本数量是大于测试部版本数,测试库的版本数大于发布库的版本数,而发布库的版本就是对外开放的版本(即,用户使用的版本)。

2、 适当引入每日编译、持续集成、Code Review(代码评审)等敏捷开发的最佳实践

3、 采用自动化脚本完成上QA库、上发布库的部署工作,避免人工失误

在项目开发中,经常使用的三种版本管理策略是:不稳定主干策略、稳定主干策略、敏捷发布策略。

l 不稳定主干策略:使用用主干作为新功能开发主线,分支用作发布。

n 使用用主干作为新功能开发主线,分支用作发布。

n 被广泛的应用于开源项目。

n 比较适合诸如传统软件产品的开发模式,比如微软的office等。

n bug修改需要在各个分支中合并。

n 新代码在主干上开发,因此如果主干不能达到稳定的标准,就不可以进行发布。

n 这种策略的好处是没有分支合并的工作量,因此比较简单。


l



*稳定主干策略*

n 使用主干作为稳定版的发布。

n bug的修改和新功能的增加,全部在分支上进行。

n 不同的修改或新功能开发以分支隔离。

n 分支上的开发和测试完毕以后才合并到主干。

n 主干上的每一次发布都做一个标签而不是分支。

n 每次发布的内容调整起来比较容易。

n 缺点是分支合并所增加的成本。


l



*敏捷发布策略*

n 敏捷开发模式的项目中广泛采用,敏捷开发的项目具有固定的发布周期。

n 为每个task建立分支。

n 为每个发布建立分支,每个周期内的task分支需要合并到发布分支发布。

n 在完成发布后,发布分支的功能合并到主干和尚在进行的任务分支中。

n 一种稳定主干策略的变体。

n 团队成员要求较高。


l



*建议方案:*

此方案已稳定主干策略为主结合了一些敏捷发布策略的思路,具体实施方案如下:

1、主干时刻处于稳定状态,随时可以发布。设SCM人员对主干代码进行管理,普通开发人员只读。

2、SCM为开发任务建立开发分支。常规的可以以小组为单位建立分支,较大的任务可以建立专门的分支。

3、在发布日,从主干复制一个测试分支,需要在本发布日发布的各开发分支向此测试分支合并。

4、对测试分支代码进行测试,出现bug在测试分支上更改,无误后发布。

5、测试分支代码发布后,合并入主干,并在主干上进行标记。

6、对紧急修复(Hotfix)的情况,可以从主干复制出测试分支,在测试分支上进行紧急修改,并在测试后发布,发布后同样将代码合并会主干,做标记。

7、 Hotfix仅限于可以很快解决的小问题,如果更改时间过长,则需采用常规方法完成。

8、如果在测试分支测试过程中需要hotfix工作,则在复制一个新的测试分支进行hotfix,测试后发布。然后同时合并入原测试分支和主干,并在主干上做标记。此过程未在上图中画出。

9、测试分支发布后,开发分支可以删除;测试分支合并入主干后,测试分支可以定期删除。



*方案的优缺点*

方案优点

1、解决了没有实施分支策略时,代码不能经常签入的问题。

2、主干代码始终处于稳定的状态随时可以发布,降低了风险。

3、可以基于一个完整的测试分支进行测试及发布,而不是以口口相传的方式增量更新。

方案缺点

1、建立分支、合并分支增加了工作量。考虑实际情况,以及版本控制工具的辅助,增加的工作量应该可以接受。

2、如果某些开发分支工期跨越多个发布周期,修改过于剧烈,合并分支时可能工作量较大。可以考虑分解任务,避免过大的任务出现。

3、在同一时间最好只有一个测试分支,因此建立测试分支的权限需要限制,除hotfix场景外应当避免。



2、 git中使用分支

Git的分支比起svn的分支,git创建分支,切换分支,合并分支,删除分支都非常快(因为,git使用的是指针的方式切换分支的)

在没有使用分支之前,git会默认有一个分支,就是主分支(master分支,还记得 git push –u origin master这个命令吗?)

a) 创建分支(分支名为dev。)

Git branch dev

b) 切换当前分支到dev

Git checkout dev

[外链图片转存中…(img-tuEw1hpd-1641803458660)]

此后的add和commit最终是提交到了dev分支。如果切换到master分支,那么,修改时不能看到的,因为,修改时在dev分支上进行的。

c) 可以一条命令完成创建并切换到新分支(-b:表示创建并切换)

Git checkout -b dev

d) 查看所有分支(当前分支前面会有星号*)

Git branch

[外链图片转存中…(img-TOOjTyOH-1641803458660)]

e) 把dev分支的内容合并到当前分支(如:master分支)里。

1)、首先确保当前分支是master分支(用命令切换:git checkout master)

2)、命令合并 git merge dev

f) 删除分⽀:git branch -d dev

注意当前分支一定不能是要删除的分支(dev)



3、分支策略

分⽀策略 在实际开发中,我们应该按照⼏个基本原则进⾏分⽀管理: ⾸先,master分⽀应该是⾮常稳定的,也就是仅⽤来发布新版本,平时不能在上⾯干活; 那在哪干活呢?干活都在dev分⽀上,也就是说,dev分⽀是不稳定的,到某个时候,⽐如 1.0版本发布时,再把dev分⽀合并到master上,在master分⽀发布1.0版本; 你和你的⼩伙伴们每个⼈都在dev分⽀上干活,每个⼈可以都有⾃⼰的开发分⽀,时不时地往dev分 ⽀上合并就可以了。 所以,团队合作的分⽀看起来就像这样:

[外链图片转存中…(img-tnAV80hA-1641803458660)]

每个开发人员有自己的分支,完成后,放到dev分支。每次修改也可以临时建立一个分支,修改完成后,合并即可。



最后:项目



版权声明:本文为Pwsifeng原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。