主人稍等一会喵`(*>﹏<*)

从零开始的前端学习-js


js的引入使用

<script></script>

标签,一般写在body的上方,在结构之后,因为功能一般我们会选择后于结构和样式进行加载

变量

使用js声明一个变量

var a = 10;

使用

console.log(a)

可以在浏览器的控制台看到打印日志

var a = 10;
var b = 20;
var c = b = a;

console.log(a);
console.log(b);
console.log(c);

可以看到控制台输出的结果为a,b,c都等于10

因为当var c=b=a时,程序会从右往左复制,先把a的值赋给b,此时b也等于了10,之后再将b的值10赋给c,所以c也变成了10,最终结果就会如上所示。

var a = 10;
var a = 20;

因为前期js的语言语法并不严谨,类似这种重复声明虽然一般在其他语言都会报错,但在js中是被允许的。

a = 5;
console.log(a)

同样由于js的不严谨

同上述这样即使不声明变量,也可以正常使用,js会帮我们自动声明。

变量声明的默认值为undefined

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

同同同样由于js的不严谨,上述这种先调用后声明,也不会报错,控制台会输出undefined。

使用var 声明变量时时,var会优于其他的语句执行,提前声明。

alert()函数,可以开启弹窗

var a = 1;
alert(a);
console.log(a);

执行结果

可以看到左上角网页一直处于加载状态,且console.log函数并未执行,由此可以得出推论。alert()函数会阻塞程序的进行,在点确定之后程序才会恢复运行。

数据类型
  • 基本数据类型
    • number
    • string
    • boolean
    • undefined
    • null
  • 引用数据类型

js中只有字符串类型,没有字符类型,而且字符串属于基本数据类型

运算符
  • 算数运算符 + - * / % ++
  • 赋值运算符 = += -= *= /= %=
  • 字符串里拼接 +
  • 关系运算符 > < >= <= == === != !==
  • 逻辑运算符 && || !
  • 三目运算符 ? : (如:a<b?a:b)
  • typeof() (数据类型检测)

在js的语法中,== 会发生数据类型的转换,如

var a ="1";
var b = 1;
console.log(a==b);

控制台会输出true

上述案例number类型会转化为字符串类型,任何数据在和字符串做关系运算的时候,都会转换为字符串。

var a = 1;
var b = true;
console.log(a==b);

控制台会输出true

数字和布尔值做运算时,true会转换成数字1,false会转为0。

=== 为严格等于,不会发生隐式的数据类型转换

同理!==为严格不等于

var a = 1;
var b = 2;
var c = 3;

console(a<c<b);

控制台还是会输出true

首先运算a<c,结果为true。然后判断true<b,true发生类型转换为1,1<2,所以最终结果为true

在逻辑运算中&&的优先级由于||

var a = 1;
var b = 2;
var c = 3;
var d = 4;
console.log(a>b||a<b&&b<c)

程序会优先进行与运算,即先运算a<b&&b<c,结果为true,然后再将a>b||true运算,所以最终结果将为true

js中逻辑运算的逻辑为

  • 只要||前为false,无论后面是什么都会返回后面的值
  • 只要||前为true,无论后面是什么都会返回前面的值
  • 只要&&前面为true,无论后面是什么都会返回后面的值。
  • 只要&&前面为false,无论后面是什么都会返回前面的值。
var a = "cat";
var b = "dog";
console.log(a||b);

结果会返回”cat”。因为程序判断中a为true

条件判断中,只有以下六种情况算为false

  • false
  • 0
  • undefined
  • null
  • “”
  • NaN
var a = 1;
console(typeof(a));

结果为number

函数

函数的作用是为了专门封装一段执行专门任务的代码段

封装方法为

function method(prarm){
	//TO DD
}
fn();
fn(1,2,3);
function fn(a){
	console.log(a);
}

如上代码也是可以正常执行的,因为函数的定义语句会如同var一样,会优先于其他代码先执行,所以可以在构造语句之前调用。

而第一个不给参数,会默认a为undefined,而第二个传入多个参数的话,只有最后一个参数会被输入,所以上述代码的结果会是

undefined
3

js的函数和java一样,在参数结尾可以使用return关键字退出函数运行,并返回规定的返回值。

函数也可以通过变量声明的方式声明

var fn = function(){
	//TO DO
}

但是要注意,这种方式定义的函数,不可以在函数声明语句前调用。

如果在之前调用,var会优于其他语句执行,即会变成

var fn = undefined

此时调用控制台会发生报错

作用域
var a = 1 ;

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

上述结果为6,因为在函数中有var a的声明,var会听提升到函数的最前面执行,也就是说fn中的a其实是局部变量声明的a,和全局变量无关,所以最后输出的结果为6。

var a = 1;
function fn(){
	var a = 2;
	function fn1(){
		a = 3;
	}
	fn1();
}

fn();
console.log(a)

结果为1

var a = 1;
function fn1(){
	console.log(a);
}
function fn2(){
	var a = 2;
	fn1();
}

fn2();

结果为1。结果看起来很奇怪,但主要是由于fn1是在根作用域定义的,fn1内的数据无权访问fn2内的内容,如果将代码中的fn1在fn2中定义,如

var a = 1;

function fn2(){
	var a = 2;
	function fn1(){
	console.log(a);
}
	fn1();
}

fn2();

此时的结果就可以访问到fn2()中的数据,即结果为2了

闭包

闭包结构:函数套函数,内部函数使用了外部函数定义的局部变量

完成一个计数器功能

var a = 0;
function add(){
	return a++;
}
console.log(add());
console.log(add());
console.log(add());
var a = "hello"
console.log(add());
console.log(add());

因为全局变量a在被调用的过程中被重新赋值,那么这个计数器的函数就会出问题。

js中的return是可以返回一个函数的,所以上述问题可以这样解决

function add(){
	var a = 0;
	function fn(){
		return a++;
	}
	return fn;
}
var b = add();
	
console.log(b());
console.log(b());
console.log(b());
var a = "hello"
console.log(b());
console.log(b());

上述代码中add()函数返回的其实就是其中的fn函数,这种结构就叫做闭包,闭包结构可以保护数据的安全,不会被再次声明的变量污染。


闭包的性能问题

局部作用域只有在函数调用的时候才会创建,在调用结束之后会被立即销毁

但是上述的结构在外部获得到闭包结构内的函数后,其实相当于是一个全局变量了,并不会被垃圾回收机制销毁掉,所以在写闭包结构的时候要注意内存泄漏的问题。(2022年了真的会有电脑因为多定义了几个全局变量就会内存泄漏的吗

同样因为垃圾回收机制,如果没用通过定义全局变量获取闭包结构内的函数而是直接使用的话,闭包结构内的数据会被垃圾回收机制销毁

function add(){
	var a = 0;
	function fn(){
		return a++;
	}
	return fn;
}
	
console.log(add()());
console.log(add()());
console.log(add()());
var a = "hello"
console.log(add()());
console.log(add()());

如上代码会输出0 0 0 0 0 ,原因就是因为add函数在调用后被销毁掉了

数组

声明数组的方式

var arr = [];

js中数组内可以存储不同类型的数组,但一般情况下我们不会选择这么干

在数组内存储一个一组,就是js中二维数组的写法

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

取数据的方式

console.log(arr[0]);

二维数组取值方式

console.log(arr[0][0])
var arr = [1,2,3];
var b = arr;
b[0] = 4;
conlose.log[arr];

和java中类似,这样的话arr中的数据会变成4,2,3。

在使用arr赋值给b变量的时候,赋值的时候本质是将arr内存储的堆地址的指针赋值。

获取数组的长度

var arr = [1,2,3]
conlose.log(arr.length)
对象

js中创建对象的方式

var car = {name : "benz",type : "g63",price ; 400};

类似json串的写法,但和json串不同的是,冒号前面的变量名可以不用加引号,js中都会认为他是字符串。

取值

console.log(car.name);

js中关于对象的复制方式默认为浅复制,只会复制指针,所以这样该值的换原本car对象内的数据也会受到影响。

var car = {name : "benz",type : "g63",price ; 400};
var newCar = car;
newCar.name = "bmw"

对象内的属性名也可以设置为变量

var temp = "type"
var car = {name : "benz",[temp] : "g63",price ; 400};

像这样使用中括号将属性名包住,这样js就会将这个属性名字识别为变量

增加对象中的属性

var car = {name : "benz",[temp] : "g63",price ; 400};
car.owner = "me";

修改对象属性的时候,如果对象本身不存在这个属性的话,会自动增加上这个属性

使用for in 循环遍历对象中的变量

var car = {name : "benz",[temp] : "g63",price ; 400};
for (var key in car){
    console.log(key);
    console.log(car[key]);
}
内置对象

程序中有着一些事先提供好的对象可供使用。js中的数组就是一个内置对象。

基本包装类型

  • String
    • length
    • toUpperCase()
    • toLowerCase()
    • substring(start,end)
    • slice()
    • indexOf()
    • split()
    • toString()

substring()含头不含尾,如

var str = “hello"
console.log(str.substring(1,2));

只能取到”e”

substring 也可以只给一个参数,代表从哪一位开始截取

console.log(str.substring(1));

输出”ello”

slice的作用是截取子数组,但是在字符串中也可以使用,在字符串中效果和substring类似

indexOf() 查找关键字

var str = “hello"
console.log(str.indexOf("l"));

返回值为输入数据在字符串中的索引值,如果想要查找的数据在字符串中存在多个,会返回数据首次出现时的索引值

如果查找不到,会返回-1

split()方法会将字符串切割为数组

var str = “hello"
console.log(str.split("l"));

会输出[“he”,””,”o”]

var str = “hello"
console.log(str.split(""));

输出[“h”,”e”,”l”,”l”,”o”]

var str = “hello"
console.log(str.split("z"));

输出[“hello”]


  • Array

    • length

    • toString()

    • indexOf()

    • slice()

    • join()

    • map()

    • every()

    • filter()

    • forEach()

    • includes()

    • some()

      —- 以下的方法都会直接修改原数组 —–

    • push()

    • unshift()

    • pop()

    • shift()

    • splice()

    • reverse()

    • sort()

    • concat()

join()方法和split()方法对应,作用是把数组拼接成字符串

var arr = ["h","e","l","l","o"];
console.log(arr.join(""));

会输出hello

push()方法可以向数组结尾添加数据

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

输出:[1,2,3,4]

unshift()方法可以向数组开头添加数据

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

输出:[4,1,2,3]

pop()方法为删除数组最后一项

var arr = [1,2,3];
arr.pop();
console.log(arr);

输出:[1,2]

shift()方法为删除数组的第一项

var arr = [1,2,3];
arr.shift();
console.log(arr);

输出:[2,3]

splice()方法可以在指定位置删除,替换,添加

var arr = [1,2,3];
arr.splice(1,1,11,111);
console.log(arr);

输出:[1,11,111,3]

splice()的第一个参数为从哪里开始,第二个参数为删除多少个,第三个参数为要加的新值

sort()方法可以使数组排序

var arr = [3,2,1];
arr.sort();
console.log(arr);

但并不是按照数字的大小排序,而是按照编码的角度进行排序,所以数字排序可能会有些问题,且字符串也可以进行排序。

想要数字排序可以自己封装一个简单的方法

//冒泡排序
 var arr = [100, 5, 94, 580, 20, 54, 78, 60, 780 ,50500 ,120, 55 ,60 ,123, 710];
        function bubble(arr) {
            for (var i = arr.length; i > 0; i--) {
                for (var j = 0; j < i-1; j++) {
                    if (arr[j + 1] < arr[j]) {
                        var temp = arr[j];
                        arr[j] = arr[j + 1];
                        arr[j + 1] = temp;
                    }
                }
            }
            return arr;
        }
        console.log(bubble(arr));

concat()方法可以连接两个数组

var arr1 = [1,2,3];
var arr2 = [4,5,6];
var arr3 = [7,8,9];

console.log(arr1.concat(arr2,arr3));

map()方法可以对数组每一项进行处理,并返回一个数组

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

console.log(arr.map(function(item,index,arr){
    return item+"块钱";
}));

输出: [‘1块钱’, ‘2块钱’, ‘3块钱’, ‘4块钱’, ‘5块钱’, ‘6块钱’, ‘7块钱’, ‘8块钱’, ‘9块钱’]

map中的function中的第一个参数为每一个的值,index为索引,arr为数组本身

every()方法可以检测数组中所有元素是否都符合指定条件

var arr = [1,2,3,4,5,6,7,8,9];
console.log(arr.every(function(item){
    return item%2==0
}))

返回值为false,因为数组中有元素%2后不等于0

filter()用法与every()类似,但他可以具体找出符合条件的元素

var arr = [1,2,3,4,5,6,7,8,9];
console.log(arr.filter(function(item){
    return item%2==0
}))

输出:[2, 4, 6, 8]

forEach()方法可以对数组的每一个元素都执行一段方法

var arr = [1,2,3,4,5,6,7,8,9];
arr.forEach(function(item){
    console.log(item);
})

输出:

1
2
3
4
5
6
7
8
9

includes()方法可以判断数组是否包含某元素,返回值为布尔值

var arr = [1,2,3,4,5,6,7,8,9];
console.log(arr.includes(1));

some()方法与every()方法类似

var arr = [1,2,3,4,5,6,7,8,9];
console.log(arr.some(function(item){
    return item%2==0
}))

但这个返回值为true,只要数组中有符合条件的元素就会返回true


RegExp(正则表达式)

    • 正则表达式要求写在双斜线中 ,如/reg/
    • 正则表达式由若干个备选字符组成
    • 备选字符要求写在中括号中,/[reg]/
    • 一个中括号中只代表一位字符的规则
    • 数量词要求写在大括号中,代表前面一位规则重复几次,{num}。
      • 数量词默认就修饰前面一位。如果想要修饰前面多位,用小括号()将想要修饰的括起来
      • {min,}这样写代表前面一位规则至少重复min次
      • {min,max}代表前面一位规则至少重复min次,最多重复max次
    • 正则表达式有一些由预定义字符集
      • \d 代表所有数字
      • \w 代表所有的数字、字母、下划线
      • . 代表任意字符
      • \s 代表空格
    • 如果中括号中只有一个备选字符,或者只有一个预定义字符集,那么可以省略中括号
    • 使用正则表达式对象.text()方法可以验证一个数据是否符合定义的正则规则
      • .test()方法是部分匹配,只要有符合整体规则的字符,.test()方法返回值就会为true
    • 在整条正则表达式前输入^,代表以……开头。在整条正则表达式结尾加***$***,代表以……结尾
    • 特殊数量词
      • ? 前面一位规则可有可无,最多一次,即{0,1}
      • * 前面一位规则可有可无,最多不限制,即{0,}
      • + 前面一位规则至少重复一次,即{1,}
    • 正则表达式中,对于有特殊含义的字符,如果希望以原文的形式取匹配,那么要加\转义
    • 正则对于所有连续的区间可以用-连接
      • [0-9]
      • [a-z]
      • [A-Z]
    • ^也可以使用在备选字符中,如/[^1]/,代表除了……

写一个手机号的正则规则

var reg = /^1[3-9]\d{9}$/ ;
var num = 18888888888;

console.log(reg.test(num));

返回值为false

写一个邮箱的正则规则

var reg = /^\w+@\w+([-]\w)*(\.\w)+$/ 

  • Math对象
    • round()
    • ceil()
    • floor()
    • abs()
    • min()/max()
    • random()

round()方法可以四舍五入取整

var a = 3.5;
console.log(Math.round(a));

输出: 4

ceil()方法为向上取整

var a = 3.4;
console.log(Math.ceil(a));

输出: 4

floor()方法为向下取整

var a = 4.9;
console.log(Math.floor(a));

输出: 4

abs()方法为取绝对值

var a = -4;
console.log(Math.abs(a));

输出: 4

min()/max()为取最大值/最小值

console.log(Math.max(1,2,3,4));
console.log(Math.min(1,2,3,4));

输出结果为4和1

但是要注意,这个方法不可以传入数组,如果传入数组,会输出NaN

NaN全称为 not a number,类型为number

random()为取随机数

console.log(Math.random());

返回值区间为0~1之间


  • Date
    • getFullYear()
    • getMonth()
    • getDate()
    • getDay()
    • getHours()
    • getMinutes()
    • getSeconds()
    • getMilliseconds()
    • getTime()

以上get改为set即为设置对应的日期,但要注意,Date,Day是有对应关系的,所以并没有setDay()方法

Date的创建方式,不输入参数的话默认返回当前时间

var date = new Date();
console.log(date);

输出:

Wed Oct 05 2022 20:14:43 GMT+0800 (中国标准时间)

Date可以传入一个字符串,也可以做差值,返回的是一个时间戳

var date = new Date("2022-10-05 00:00:00");
var now = new Date();
console.log(date);

返回: -73035728

getFullYear()方法可以获取当前年份,返回值为number

var now = new Date();
console.log(now.getFullYear());

getMouth()为返回当前月份,但要注意,返回值为0~11,需要+1修正

var now = new Date();
console.log(now.getMonth());

getDate()为返回当前日期,很神奇的是,返回日期的返回值为1~31,不需要修正

var now = new Date();
console.log(now.getDate());

getDay()为返回当前星期,又很神奇的是,返回星期的返回值为0~6,0为周日

var now = new Date();
console.log(now.getDay());

getTime()可以获得时间戳

var now = new Date();
console.log(now.getTime());
DOM

js是由 ECMAScript + DOM + BOM,其中ECMAScript是js的核心

DOM的全程为 Document object model 文档对象模型,主要左右是将js和html联系起来。

即将html文档标签视为一个对象,在js中操作该对象,既而操作html标签

DOM的四个作用,即

document为最大的文档对象,且通过document获取的元素对象同样具有上述方法

var box = document.getElementById("box");
var ele = box.getElementsByTagName("div")

查找

  • document.getElementById()

    document.getElementById()方法可以获取对象的所有属性

    通过document.getElementById()获得标签元素对象,参数传入tag的id字符串

  • document.getElementsByClassName()

    class在js中是一个关键字,所以这个方法名字是byClassName而不是class

    因为class可以重名,所以名字中的Elements是复数,且document.getElementsByClassName()返回的是一个类数组,类数组内存在的是想要的元素

  • document.getElementsByTagName()

  • document.getElementsByName()

    name为表单的name

  • doucument.querySelector()

    元素查找器,参数内放入css选择器

    返回值为一个元素对象,就算目标有多个,也只会返回第一个

  • document.querySelectorAll()

    返回值也是一个类数组,但类别和上述的类数组不同,为NodeList,上述的类型为HTMLCollection

修改

  • 改属性

    var box = document.getElementById("box");
    box.style = "color:blue";
    var box = document.getElementById("box");
    box.setAttribute ("class" : "test");

    通过.style的方式会更改元素所有的css样式,如果想单独更改其中一个样式可以使用.继续调用

    var box = document.getElementById("box");
    box.style.fontSize = "12px";

    这样更改的时候要注意,在html中的带”-“的样式,都要换成驼峰式命名。如font-size要转变为fontSize

  • 改内容

    innerText 返回的是元素开始标签到结束标签之中的文本内容

    <body>
     <div id = "box">
         hello
    	<div>
         world
        </div>
     </div>
        
    
    <script>
    var box = document.getElementById("box");
    console.log(box.innerText);
    </script>
    
    </body>
    
    会输出:
    	hello
        world
    box.innerText = "test"

    如果这样更改内容文本,那么标签内的所有内容都会变更为设定的内容,如上面的案例就会变为<div id = “box”>test</div>

    innerHTML返回的是元素开始标签到结束标签之中的内容,包括标签

    使用innerText的话是无法识别标签的,<div>会被innerText识别成文本,使用innerHTML可以解决这个问题

删除

  • 删内容
    • 通过innerText/innerHTML = ”“即可删除
  • 删属性
    • 使用removeAttribute()方法可以删除属性
  • 删元素
    • 使用removeChild()方法可以删除元素,由父级调用,参数内放子元素对象

增加

  • 增加元素

    使用 document.creatElement()创建元素, 参数内传入标签名

    var div = document.creatElement("div")

    使用 父元素对象.appendChild()方法,把元素添加到父元素之中,参数内传入子元素对象

    var superClass = document.getElementById("superClass");
    superClass.appendChild(div);

小练习,使用DOM创建一个动态表格

//模拟一段后端数据
       var data = [
           { "name": "test", "hobby1": "sing", "hobby2": "dance", "hobby3": "rap", "hobby4": "basketball" },
           { "name": "test", "hobby1": "sing", "hobby2": "dance", "hobby3": "rap", "hobby4": "basketball" },
           { "name": "test", "hobby1": "sing", "hobby2": "dance", "hobby3": "rap", "hobby4": "basketball" },
           { "name": "test", "hobby1": "sing", "hobby2": "dance", "hobby3": "rap", "hobby4": "basketball" }
       ]
       //1.创建表格
       var table = document.createElement("table");
       table.width = "500px";
       table.style.border = "1px #000 solid";
       table.style.borderCollapse = "collapse";
       table.style.margin = "auto";
       document.body.appendChild(table);
       //2.创建表头行
       var tr = document.createElement("tr");
       table.appendChild(tr);
       //3.创建表头单元格
       for (var key in data[0]) {
           var td = document.createElement("td");
           td.style.border = "1px #000 solid";
           tr.appendChild(td);
           td.innerHTML = key;
       }
       //4.追加单元数据
       for (var i = 0 ; i<data.length;i++){
           var tr = document.createElement("tr");
           table.appendChild(tr);
           for(var key in data[i]){
               var td = document.createElement("td");
               td.style.border = "1px #000 solid";
               tr.appendChild(td);
               td.innerHTML = data[i][key];
           }
       }

这样我们就不通过任何html和css代码,靠纯js写出了一个动态的表格

事件

由一些用户行为触发的一些功能

onclick 点击事件

给元素对象绑定事件的方法

元素对象.事件名 = 函数名

 <div id = "btn" style="width: 100px;margin:auto;border: 1px #000 solid;text-align: center;">点我</div>

function fn(){
            alert("好点");
        }
        var btn = document.getElementById("btn");
        btn.onclick=fn;

注意fn后不要加括号,加括号为直接调用

也可以改为这样写

btn.onclick = function{
    alert("好点")}

如果原函数需要传入参数,可以改成这样

function fn(m){
            alert(m);
        }
        var btn = document.getElementById("btn");
        btn.onclick=function(){
        	fn("好点")
        }

onmouseover 鼠标移入事件

onmouseout 鼠标移出事件

一个元素可以绑定多个事件

var btn = document.getElementById("btn");
        btn.onmouseover=function(){
        	console.log("我移入了");
        }
	 btn.onmouseout=function(){
        	console.log("我移出了");
        }

onmousemove 元素内鼠标移动事件

onkeydown 键盘被按下了

onkeyup 键盘被抬起了

onkeypress 键盘被按下去的过程

<input id ="search"/>
    
<script>
var search = document.geiElementById("search");
search.onkeydown=function(e){
    if(e.keyCode == 13){
        conlose.log("按下了回车");
    }
}
</script>

function中的e为事件对象,其中的keyCode为键盘每个按键的编号,13为回车

onfocus 表单获得焦点

onblur 表单失去焦点

onchange 表单值改变了

oninput 表单输入

获取表单对象内的value属性,即可获取用户输入的值


写个小练习,判断密码强度

<body>

    <input type="password" id="psw" />
    <span></span>
    <p>规则:
    <p>1.纯大写字母、纯小写字母、纯数字为弱密码</p>
    <p>2.大小写字母、大小写字母+数字为中密码</p>
    <p>3.大小写字母+特殊符号+数字为强密码</p>
    </p>
    <script>
        var psw = document.getElementById("psw");
        psw.onfocus = function () {
            psw.oninput = function () {
                console.log(psw.value)
                var span = document.getElementsByTagName("span");
                var allNum = /^\d+$/;
                var allUpLet = /^[A-Z]+$/;
                var allLowLet = /^[a-z]+$/;
                var spChar = /[^\w]/;
                var lowLet = /[a-z]+/;
                var upLet = /[A-Z]+/;
                var num = /\d+/;
                if (!allNum.test(psw.value) && !allUpLet.test(psw.value) && !allLowLet.test(psw.value)) {
                    if (spChar.test(psw.value) && lowLet.test(psw.value) && upLet.test(psw.value) && num.test(psw.value)) {
                        span[0].style.color = "green";
                        span[0].innerHTML = "强密码";
                    } else {
                        span[0].style.color = "yellow";
                        span[0].innerHTML = "中密码";
                    }

                } else {
                    span[0].style.color = "red";
                    if (psw.value.length != 0) {
                        span[0].innerHTML = "弱密码";
                    }
                }
                if (psw.value.length == 0) {
                    span[0].innerHTML = "";
                }

            }
        }
    </script>

</body>

小练习 写一个简单的注册验证

<body>
    <div>
        username:<input type="text">
        <span></span>
    </div>
    <div>
        password:<input type="password">
        <span></span>
    </div>
    <div>
        checkPassword:<input type="password">
        <span></span>
    </div>
    <button id="btn">submit</button>

    <script>
        //获取所需元素
        var input = document.getElementsByTagName("input");
        var span = document.getElementsByTagName("span");
        var btn = document.getElementById("btn");

        //编写正则规则
        var regUser = /^\w{4,8}$/;
        var regPsw = /^\d{6}$/;

        //判断用户名格式是否正确
        input[0].onblur = function () {
            if (regUser.test(input[0].value)) {
                span[0].innerHTML = ""
            } else {
                span[0].innerHTML = "用户名格式有问题";
            }
        }

        //判断密码格式是否有问题
        input[1].onblur = function () {
            if (regPsw.test(input[1].value)) {
                span[1].innerHTML = ""
            } else {
                span[1].innerHTML = "密码格式有问题";
            }

            //当确认密码栏内容不为空时,进行check
            if (input[2].value.length != 0) {
                if (input[1].value === input[2].value) {
                    span[2].innerHTML = "";
                } else {
                    span[2].innerHTML = "两次密码不一致";
                }
            }
        }

        //判断确认密码是否一致
        input[2].onblur = function () {
            if (input[1].value === input[2].value) {
                span[2].innerHTML = "";
            } else {
                span[2].innerHTML = "两次密码不一致";
            }
        }

        //提交按钮
        btn.onclick = function(){
            if(regUser.test(input[0].value)&&regPsw.test(input[1].value)&&input[1].value === input[2].value){
                alert("success");
            }else{
                alert("fail");
            }
        }
    </script>

</body>
this

this代表函数运行时所在的对象,即谁调用了this,this就指向谁。默认没有人调用this指向window,为整个页面中最大的对象。

<body>
    <div>1</div>
    <div>2</div>
    <div>3</div>
    <div>4</div>

    <script>
        var divs = document.getElementsByTagName("div");
        for(var i = 0;i < divs.length; i++){
            divs[i].onclick = function(){
                alert(divs[i].innerHTML);
            }
        }
    </script>
</body>

如果这么写,功能是不生效的,因为

  • 在循环运行的时候,function内的函数是没有运行的
  • var i是一个全局变量

所以在运行的时候,alert内的divs[i]中的i其实是4,所以当点击的时候会报错

这时将代码中的divs[i]改为this就可以正常运行了

<body>
    <div>1</div>
    <div>2</div>
    <div>3</div>
    <div>4</div>

    <script>
        var divs = document.getElementsByTagName("div");
        for(var i = 0;i < divs.length; i++){
            divs[i].onclick = function(){
                alert(this.innerHTML);
            }
        }
    </script>
</body>
定时器

定时器是是让网页自动运行的唯一办法

  • setInterval(方法,间隔毫秒数) 周期性定时器
  • setTimeout(方法,等待毫秒数) 一次性定时器

定时器是window对象的方法

定时器是一个异步的程序

停止定时器的方法

  • clearInterval()
  • clearTimeout()
var btn = document.getElementById("btn");
var index = setInterval(function(){
    console.log("test");
},1000);
btn.onclick = function(){
    clearInterval(index);
}

setInterval()方法返回的值是他的线程号,使用clearInterval()方法时,参数传入想要停止定时器的线程号就可以停止对应的定时器。所以为了能够将来能够停止对应的定时器,所以定时器启动时一般要带着变量。

但是这样的,因为index是全局变量,所以定时器方法是有被指针指向的,停到后不会被垃圾回收机制判断为垃圾,内存并不会得到释放,所以一般在定时器停掉后会将指向他的index赋值为空来释放内存

btn.onclick = function(){
    clearInterval(index);
    index = null;
}

小练习,写一个简单倒计时

<body>

    <p id="text" style="width:500px;margin: auto;text-align:center"></p>

    <script>
        var text = document.getElementById("text");
        var target = new Date("2022-10-13 00:00:00");
        var flag = true;
        var index = setInterval(function () {
            var now = new Date();
            var cd = target - now;
            var hour = Math.floor(cd / (1000 * 60 * 60));
            var min = Math.floor((cd % (1000 * 60 * 60)) / (1000 * 60));
            if (min < 10) {
                min = "0" + min;
            }
            var sec = Math.ceil(cd % (1000 * 60) / 1000);
            if (sec < 10) {
                sec = "0" + sec;
            }
            if (flag) {
                text.innerHTML = "距离星期四还有:&nbsp&nbsp&nbsp&nbsp" + hour + ":" + min + ":" + sec;
                flag = !flag;
            } else {
                text.innerHTML = "距离星期四还有:&nbsp&nbsp&nbsp&nbsp" + hour + " " + min + " " + sec;
                flag = !flag;
            }
            if (cd <= 0) {
                clearInterval(index);
                index = null;
                text.innerHTML = "KFC v me 50";
            }
        }, 1000)


    </script>
</body>

.style无法获取外部样式,要通过“document.defaultView.getComputedStyle(元素,null)”获取元素的所有样式

使用Number()方法可以把字符串转换为数字类型

parseInt()也可以将字符串转换为数字类型,它可以截取以整数开头的部分并转换为数字类型,类似的还有parseFloat()

小练习 写个弹窗小广告

<body>


    <div id="ad">
        <div id="close">close</div>
        <img id="pic" src="https://blog-1312179285.cos.ap-nanjing.myqcloud.com/img/-5afbac6b595add15.jpg" alt="">
    </div>
    <script>

        //获取元素
        var ad = document.getElementById("ad");
        var close = document.getElementById("close");

        //声明定时器
        var timeoutTimer = null;
        var intervalTimer = null;

        //定义向上走和向下走的方法
        function moveUp() {
            //获取ad的所有样式
            var cssStyle = document.defaultView.getComputedStyle(ad, null);
            var bottom = parseInt(cssStyle.bottom);
            //当ad没完全露出时执行
            if (bottom < 0) {
                //定义每次调用上升的像素
                bottom = bottom + 1;
                console.log(bottom);
                //更改ad的样式
                ad.style.bottom = bottom + "px";
            } else {
                //完全弹出后关闭定时器
                clearInterval(intervalTimer);
                intervalTimer = null;
            }
        }

        function moveDown() {
            //获取ad的所有样式
            var cssStyle = document.defaultView.getComputedStyle(ad, null);
            var bottom = parseInt(cssStyle.bottom);
            //当ad没完全隐藏时执行
            if (bottom > -250) {
                //定义每次调用下降的像素
                bottom = bottom - 1;
                console.log(bottom);
                //更改ad的样式
                ad.style.bottom = bottom + "px";
            } else {
                //完全隐藏后关闭定时器
                clearInterval(intervalTimer);
                intervalTimer = null;
                //并开启打开弹窗的一次性计时器
                startAd();
            }
        }

        //定义定时器,使用一次性定时器开启周期性定时器,调用moveUp方法
        function startAd() {
            timeoutTimer = setTimeout(function () {
                intervalTimer = setInterval(moveUp, 10);
            }, 1000)
        }

        //当点击close按钮后,开启周期性定时器调用moveDown方法,使广告下降至屏幕外
        close.onclick = function () {
            //清除周期性定时器
            clearInterval(intervalTimer);
            intervalTimer = setInterval(moveDown, 10);
        };
        
        //进入网页后开启弹出广告的定时器
        window.onload = startAd();

    </script>
</body>

放个效果链接

razuberiii.github.io/html/testAd.html

防抖

防抖功能就是设置一个能够成功发送请求的时间间隔,就算一秒钟内点了一万次,最后也只会触发一次,原理是用一个一次性定时器来延缓运行,防止误操作。

封装一个防抖函数

function dobounce(fn){
    var timer = null;
    return function(){
        clearTimeout(timer);
        timer=setTimeout(fn,500);
    }
}
节流

节流和防抖类似,但不同的是,节流会触发多次。即一秒钟走一万次,最后可能只执行五千次。

封装一个节流函数

/*
        练习封装一个节流函数
        */

       function throttle(fn) {

           var timer = null;
           var flag = true;

           return function () {
               if (!flag) {
                   return;
               }
               flag = false;
               clearTimeout(timer);
               timer = setTimeout(function () {
                   flag = true;
                   fn();
               }, 500);
           }
       }
原型与继承

原型:prototype 方法背后,专门保存由方法创建出来对象的共有属性

prototype是个属性,值为一个对象

prototype:{}只有函数有原型

构造函数:专门用来创建相同结构对象的专门方法

构造函数在项目中默认首字母大写

function Student(name,age,fn){
	this.name = name;
	this.age = age;
    this.fn = fn;
}
原型链

原型:任何函数都有原型

隐式原型:__proto__,任何对象都有隐式原型。任何一个对象的隐式原型,指向创建该对象的构造函数的原型对象,即

function Student(name,age){
    this.name = name;
    this.age = age;
}
var ll = new Student("ll",0);
consolog(ll.__proto__===Student.prototype);

返回值为true

隐式原型的作用是为了保证继承关系

Object:为所有对象的父类

Function:为所有函数的父类

理解以上概念,就可以理解原型链了

new

想了解new要先了解三个函数apply() call() bind()

三个函数作用类似,都是用来主动改变this的指向

var ll = {
       "name" : "ll",
       "age" : 18,
       "intr" : function(para1,para2){
           console.log("i'm "+this.name,"and i'm "+this.age+" years old"+para1+para2);
       }
   } 

   var hmm = {
       "name" : "hmm",
       "age" : 17,
   } 

   ll.intr.call(hmm,"test","test");

结果: i’m hmm and i’m 17years old

可以看到使用.call()方法改变了ll中this的指向

三个函数不同点:

call和apply的区别是传参形式不同,call的参数为单独添加使用,而apply中参数使用数组形式,bind则会返回一个函数,需要主动调用执行

   ll.intr.call(hmm,"test","test");

ll.intr.apply(hmm,["test","test"]);

var fn = ll.intr.bind(hmm);
fn("test","test");

自己封装一个new

function myNew(){

            //创建一个对象
            var obj = {};

            //取出构造函数
            var fn = [].shift.call(arguments);

            //修改原型链指向
            obj.__proto__ = fn.prototype ; 

            //调用构造函数
            fn.apply(obj,arguments);

            return obj;
           
        }

        function Student(name,age){
            this.name = name;
            this.age = age;
        }
        Student.prototype.car = "gtr";

        var student = myNew(Student,"小明",20);

        console.log(student);
instanceof

instanceof:用于检测某个属性是否出现在某个实例的原型链上,原始数据类型不适用此方法

实现一个insatanceof

function myInsatanceof(child,parent){
           var left = child.__proto__;
           var right = parent.prototype;

           while(true){
               if(left===right){
                   return true;
               }
               if(left===null){
                   return false;
               }
               left = left.__proto__;
           }
       }

      
       var b = myInsatanceof(myInsatansof,Object);
       console.log(b);
时间触发周期
  • ie(ie6以下):目标触发—事件冒泡
  • 其他:事件捕获—目标触发—事件冒泡
    - 事件捕获:从外往里做标记
    - 目标触发
    - 事件冒泡:从里往外冒泡

如果想要给事件绑定多个函数,可以使用addEventListener(事件名,函数,是否在捕获阶段触发)事件名,函数,是否在捕获阶段触发

添加的事件监听可以使用removeEventListrene(事件名,函数,是否在捕获阶段触发)移除绑定的事件

事件对象

事件对象会在事件发生时作为第一个参数自动传入

事件对象的offsetX,offsetY是指相对于元素的位置

target属性是事件源对象


阻止事件冒泡

事件对象中有一个方法,e.stopPropagation()

使用这个方法可以阻止事件冒泡

其他的一些内置对象
  • window.screen
    • 内有一些分辨率之类的关于屏幕信息的属性
  • window.history
    • history.back()方法效果与浏览器点击后退按钮相同
    • history.forward()效果与浏览器点击前进按钮相同
    • history.go(1) 这个方法也可以实现向前和后退的功能,参数内为跳转几次,如果要后退的话参数写入负数即可,0的话为刷新页面
  • window.location
    • location.hostname 返回web主机的域名
    • location.pathname 返回当前页面的路径和文件名
    • location.port 返回web主机的端口
    • location.protocol 返回所使用的web协议
    • location.href 返回当前页面的URL

在js中定义对象的location.href可以实现直接跳转,无需使用a标签

btn.onclick = function(){
	location.href = "razuberiii.github.io";
}

使用location.reload()方法可以实现刷新

事件代理/事件委托
<div id="d1">
	<div id="d2">
		<div id="d3"></div>
	</div>
</div>

<script>
	var div = document.getElementById)("d1");
	d1.onclick = function(){
		console.log(e.target.className);
	}
</script>

这样的代码叫做事件代理,或者叫做事件委托,即d2和d3将事件委托给了d1

而且html中的元素未必是固定的,元素是可以通过js动态创建的,在js还没创建元素之前,如果想给元素绑定事件的话,也需要使用事件代理

ajax

ajax是前端向后端取数据而无需刷新页面的技术

XMLHttpRequest为ajax的核心对象

  1. 使用ajax的第一步,创建一个ajax核心对象
var xmlhttp = new XMLHttpRequest;
  1. 创建请求,使用open()方法 有三个参数 第一个为请求方式 请求地址 是否异步
xmlhttp.open("get","https://explame.com",true);
  1. 发送请求参数,使用send()方法

    当请求方式为post,参数内键值对形式传输,多个参数使用&符号链接,且需要设置请求头

xmlhttp.xmlhttp.setRequestHeader("content-type","application/json");
xmlhttp.send(“user=test&psw=test”)

当请求方式为get,参数拼接在请求地址后面,使用get方式即使用?和&拼接

xmlhttp.send(null)
  1. 接收响应,XMLHttpRequest中有readyState,status 属性
    • readyState 代表返回的状态码代表响应的进程进行到哪一步
      • 0:(尚未初始化)
      • 1:(正在请求)
      • 2:(请求完毕)
      • 3:(正在响应)
      • 4:(响应完毕)
    • status 代表请求的结果
      • 200(成功)
      • 404(找不到资源)
      • 500(服务器内部错误)
      • ……

当两个前提都成立时,代表请求成功

使用onreadystatechange()方法,当readyState发生改变时执行

xmlhttp.onreadystatechange = function(){
	if(xmlhttp.readyState==4&&xmlhttp.status==200){
		var data = xmlhttp.responseText;
	}
}

通过XMLHttpRequest.responseText拿到响应内容

此上为ajax使用的流程

JSON.parse()方法可以把json串转化为对象

JSON.stringify()可以把对象转化为json串

跨域

由于浏览器的同源策略,即当前地址和发送请求的地址必须同源

  • 同协议
  • 同域名
  • 同端口号

三者有一个不同即为跨域

因为跨域为浏览器的安全策略,所有只有浏览器和服务器之间才会产生跨域问题,服务器和服务器间没有跨域问题

因为这种特性,可以使用跨域代理解决跨域问题,是一种比较主流的跨域解决方案

即 本地—>本地服务器—>远程服务器

使用jsonp也可以解决跨域问题,在此不详细展开


文章作者: Razuberi
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Razuberi !
评论