02--JS02--高级

news/2024/9/23 13:31:12

JavaScript02: 进阶

一. 变量声明

1.1 变量提升

// 以下代码,或多或少会有些问题的
function fn(){console.log(name);var name = '大马猴';
}fn()// 问题: 
name变量先使用,再定义   这么写代码,在其他语言里. 绝对是不允许的
但是在js里,不但允许,还能执行,为什么呢? 
因为在js执行的时候,它会首先检测整体代码,发现在代码中会有name使用
则运行时,就会自动变成这样的逻辑:// 变量提升的逻辑:
function fn(){var name;console.log(name);name = '大马猴';
}
fn()
console.log(a);  // undefined// 看到了么,实际运行的时候和写代码的顺序可能会不一样
这种 把变量 提升到代码块第一部分运行  的逻辑,被称为变量提升

1.2 let 声明变量

结论一:用let声明变量,是新版本javascript ES6 提倡的一种声明变量的方案

// ES6提出 用let 声明变量,防止变量提升的逻辑function fn(){console.log(name);  // 直接报错, let变量不可以变量提升let name = '大马猴'; 
}
fn()

结论二:在同一个作用域内. let声明的变量只能声明一次,其他使用上和var没有差别

function fn(){// console.log(name);  // 直接报错, let变量不可以变量提升.// let name = '大马猴';var name = "周杰伦";var name = "王力宏";   // 不会报错console.log(name);
}fn()// var本意是声明变量,同一个变量,被声明两次(都是函数体内部,局部变量),显然也是不合理的

ES6规定:let声明的变量,在同一个作用域内,只能声明一次

function fn(){// console.log(name);  // 直接报错, let变量不可以变量提升.// let name = '大马猴';let name = "周杰伦";console.log(name);let name = "王力宏";    // 报错,同一个作用域 let 声明同一个变量,只能一次console.log(name);
}
fn()// 注意: 报错是发生在代码检查阶段. 所以上述代码,根本就执行不了

二. 闭包函数

先看一段代码

let name = "周杰伦"
function chi(){name = "吃掉"
}
chi();
console.log(name);  // "吃掉"// 发现没有: 在函数内部修改,外部的变量是十分容易的一件事. 尤其是全局变量. 这是非常危险的. 
// 试想 写了一个函数. 要用到name, 结果被别人写的某个函数给修改掉了...

接下来,看一个案例:

准备两个工具人. 来编写代码. 分别是js01和js02.

// 1号工具人
var name = "alex"// 定时器,5000秒后执行
setTimeout(function(){console.log("一号工具人:" + name)   // 一号工具人还以为是alex呢, 但是该变量是不安全的.
}, 5000);
// 2号工具人
var name = "周杰伦"
console.log("二号工具人", name);

html:

<script src="js01.js"></script><script src="js02.js"></script>

此时运行的结果:

很明显, 虽然各自js在编写时是分开的. 但是在运行时, 是在同一个空间内执行的. 他们拥有相同的作用域

此时的变量势必是非常非常不安全的. 那么如何来解决呢?

注意:在js里 变量是有作用域的. 也就是说一个变量的声明和使用是有范围的,不是无限的

// 验证: 变量是有作用域的
function fn(){let love = "爱呀"
}
fn()console.log(love)// 直接就报错了   也就是说. 在js里 变量作用域是有全局和局部  的概念 

直接声明在最外层的变量,就是全局变量。所有函数、所有代码块都可以共享的

在函数内和代码块内声明的变量,尤其是函数内,声明出来的变量它是一个局部变量,外界是无法进行访问的

我们就可以利用这一点,来给每个工具人创建一个局部空间. 就像这样:

// 1号工具人   都是自运行函数
(function(){var name = "alex";setTimeout(function(){console.log("一号工具人:"+name)}, 5000);
})()
// 二号工具人  都是自运行函数
!function(){var name = "周杰伦"console.log("二号工具人", name);
}()

运行结果

这样,虽然解决了变量的冲突问题

但是想想. 如果在外面,需要函数内部的一些东西,来进行相关操作,怎么办?

比如 一号工具人要提供一个功能(加密),外界要调用, 怎么办?

// 1号工具人// 局部函数中,对外接口 方式一: return 返回到 全局变量中   ===> 闭包函数
// 1.首先:全局要使用,那js文件中,就不能是自运行函数,要设置一个名字
let jiami = (function(){let key = "10086" // 假装我是秘钥// 我是一个加密函数let mi = function(data){  // 数据console.log("接下来, 我要加密了,rsa哦. 很厉害的")console.log("秘钥:"+key);console.log("数据:"+data);// 返回密文return "我要返回密文";}// 2.其次:外面可能需要用到该功能. 故 需要该变量返回(暴露到全局空间). 返回加密函数return mi;
})();// 局部函数中,对外接口 方式二:借助于window对象,将返回的变量 直接 赋值到全局变量中
(function(){let key = "10086" // 假装我是秘钥// 我是一个加密函数let mi = function(data){  // 数据console.log("接下来, 我要加密了,rsa哦. 很厉害的")console.log("秘钥:"+key);console.log("数据:"+data);// 返回密文return "我要返回密文";}// 对外的接口window.mi = mi;
})();

注意:如果封装一个加密js包的时候,就还得准备出解密的功能

并且, 不可能一个js包就一个功能吧。 那也太痛苦了(js文件起名字),那怎么办?

可以返回一个对象,对象里面可以存放好多个功能

而一些不希望外界触碰的功能. 就可以很好的保护起来.

// 1号工具人.
let jiami = (function(){let key = "10086" // 加装我是秘钥// 该函数只属于该模块内部,外界无法访问.  就不返回let n = {abc:function(){console.log("我是abc. 你叫我干什么?")}}// 外面需要用到的功能,就进行返回.return {rsa_jiami: function(data){console.log("接下来, 我要加密了,rsa哦. 很厉害的")console.log("秘钥:"+this.get_rsa_key() + key);n.abc();console.log("数据:"+data);return "我要返回密文";},aes_jiami: function(data){console.log("接下来, 我要加密了,aes哦. 很厉害的")console.log("秘钥:"+this.get_aes_key());n.abc();console.log("秘钥:"+key);console.log("数据:"+data);return "我要返回密文";},get_rsa_key: function() {return this.rsa_key = "rsa的key", this.rsa_key},get_aes_key: function() {return this.rsa_key = "aes的key", this.rsa_key}}
})();

html里面使用时:

<script>miwen = jiami.rsa_jiami("吃你的糖葫芦吧");console.log(miwen);
</script>

OK. 至此. 何为闭包? 上面这个就是闭包

相信你百度一下就会知道,什么内层函数使用外层函数变量、什么让一个变量常驻内存等等

其实细看,它之所以称之为闭包,它是一个封闭的环境。在内部. 自己和自己玩儿

避免了对该模块内部的冲击和改动. 避免的变量之间的冲突问题

闭包的特点:

  1. 内层函数对外层函数变量的使用
  2. 会让变量常驻于内存

这俩玩意就不解释了, 和python的闭包是一个意思。不懂没关系,能看懂它的执行过程就好

三. JS中的各种操作(非交互)

3.1 定时器

在JS中, 有两种设置定时器的方案

// 延时器:经过xxx时间后, 执行xxx函数
t = setTimeout(函数, 时间)// 5000毫秒  5秒后打印我爱你
t = setTimeout(function(){console.log("我爱你")
}, 5000); clearTimeout(t)  // 停止一个定时器
// window.clearTimeout(t)  
// 定时器:每隔 xxx时间, 执行一次xxx函数
t = setInterval(函数, 时间) // 每隔5秒钟, 打印`我爱你`
t = setInterval(function(){console.log("我爱你")
}, 5000)window.clearInterval(t)  // 停止一个定时器for(let i = 0; i <= 9999; i++)window.clearInterval(i);  // 清理掉所有定时器
// 定时器 关于js逆向,常遇到的:// 1.心跳检测     一般是用来监测用户 是否掉线了,也可以用来监测用户浏览器环境是否健康
// 这个就是  服务端 主动向 浏览器端 发送请求检测
http是被动响应的  服务器端只能不停的间隔发送检测请求,才能时刻监测客户端某个元素的状态(是否点击 或 扫描二维码)等js逆向中:心跳检测
就是正常浏览器是 不停的心跳检测 且有数据返回,
js逆向代码时,可能只发一次,服务端就给做反爬 禁IP什么的// 2.无限debugger
// 无限debugger的核心
setInterval(function(){debugger;   // 设置断点
}, 1000)网页页面中,会正常显示其他的html代码,但是一旦F12调试,就会一直处理断点中// 解决原理:
在source源代码中,setInderval 这一行(没进入定时器体内之前),左键点击行号(设置断点)刷新页面,页面会调试暂停到 设置断点这一行再在控制台(console)中,将定时器干掉 (重置定时器为普通的空函数 setInterval=function(){}; )

3.2 关于时间

eg:http://www.baidu.com/s?word=jfdsaf&t=1640090719637 参数t就是时间戳

var d = new Date(); // 获取当前系统时间
var d = new Date("2018-12-01 15:32:48");  // YYYY-MM-DD HH:mm:ss得到一个具体时间// 时间格式化
year = d.getFullYear();   // 年份
month = d.getMonth() + 1; // 月份. 注意月份从0开始
date = d.getDate();       // 日期
hour = d.getHours();      // 小时
minute = d.getMinutes();  // 分钟
seconds = d.getSeconds(); // 秒format_date = year + "-" + month + "-" + date + " " + hour + ":" + minute + ":" + seconds;// 时间戳     表示从1970-1-1 00:00:00 到现在一共经过了多少毫秒
var d = new Date(); 
console.log(d.getTime())// 注意1:python的时间戳 单位是秒
import time
print( int(time.time() * 1000) )  # 扩大一千倍,再取整// 注意2: 有些时候,前端实例化日期对象时,会少一个调用括号
var d = new Date;     // 坑人写法,也可以
console.log(d.getTime())

3.3 eval函数(必须会)

http://tools.jb51.net/password/evalencode 一个在线JS处理eval的网站. 大多数的eval加密. 都可以搞定了.

// eval:
可以动态的把字符串,当成js代码运行   // 从功能上讲非常简单,和python里面的eval是一样的s = "console.log('我爱你')";
eval(s);// 重点:eval加密
拓展使用 --> 前端利用eval的特性来完成反爬操作// 解决核心:
eval函数,里面传递的应该是  即将要执行的 代码(字符串)// eg:
eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('0.1(\'我爱你\')',62,2,'console|log'.split('|'),0,{}))// 记住eval()里面是字符串  !!!
这一坨看起来, 肯定很不爽. 怎么变成看着很舒服的样子呢?  想看看这个字符串长什么样?  
就把eval()里面的东西拷贝出来
执行一下  // 注意:一般是个自运行函数,直接粘贴会报错  故加个括号 eg: (eval中的代码) //  或 和下面截图一样  赋值给变量,再打印变量
最终一定会得到一个字符串,要不然eval()执行不了的

3.4 prototype 原型对象

prototype 原型对象,作用是 给类增加功能扩展 的一种模式

写个面向对象来看看.

function People(name, age){this.name = name;this.age = age;this.run = function(){console.log(this.name+"在跑")}
}p1 = new People("张三", 18);
p2 = new People("李四", 19);p1.run();
p2.run();

现在代码写完了. 突然之间, 感觉好像少了个功能. 人不应该就一个功能. 光会吃是不够的. 还得能够ooxx. 怎么办?

直接改代码? 可以,但不够好. 如果这个类不是我写的呢? 随便改别人代码是很不礼貌的,也很容易出错. 怎么办?

可以在我们自己代码中,对某个类型动态增加功能。此时就用到了prototype

function People(name, age){this.name = name;this.age = age;this.run = function(){console.log(this.name+"在跑")}
}// 通过prototype,可以给People增加功能  属性或方法
People.prototype.xxoo = function(){console.log(this.name+"还可以xxoo");
}p1 = new People("张三", 18);
p2 = new People("李四", 19);p1.run();
p2.run();p1.xxoo();
p2.xxoo();

几个重要概念

3.4.1 构造器

构造一个对象的函数. 叫构造器.

function People(){       // People 就是构造器 constractor}var p = new People();    // 调用构造器 ---> 对象p.constractor == People; // true

3.4.2 原型对象

每一个js对象中,都有一个隐藏属性__proto__,指向该对象的 原型对象

在执行该对象的方法或者查找属性时,首先, 对象自己(构造器中声明的)是否存在该属性或者方法

如果存在, 就执行自己的. 如果自己不存在. 就去找 原型对象

function Friend(){this.chi = function(){console.log("我的朋友在吃");}
};// 指定Friend 的原型对象  
Friend.prototype = {chi: function(){console.log("我的原型在吃")}
};// 或者这种写法
Friend.prototype.chi = function(){console.log("我的原型在吃")
};f = new Friend();
f.chi();   // 运行结果: 我的朋友在吃// 属性查找顺序:
先查找该对象(构造器中声明的)中  是否有chi这个方法
再找,它的原型对象上 是否有chi这个方法// 总结: !!!
Friend    //  构造器f         //  对象f.__proto__  <===> Friend.prototype  // 构造器的prototype属性 和 对象的 __proto__,都是指向f对象 的 原型对象

3.4.3 原型链

原型链(prototype chain):是属性查找的方式

当调用一个对象的属性时,如果对象没有该属性,从对象的原型对象上去找该属性,

如果原型上也没有该属性,那就去找原型的原型,直到最后返回null为止,null没有原型。

// 前提:
每个对象身体里. 都隐藏着 __proto__属性 也就是它的 原型对象
同时 原型对象 也是对象, 也就是说 原型对象 也有  __proto__ 属性类似于.....这样:f.__proto__.__proto__  ===> Friend.prototype.__proto__ ===>  Object.prototype // Object对象的原型

打印出来的效果是这样的:

// 故:在执行 f.toString() 的时候不会报错. 可以正常运行的原因,就在这里执行过程:  
先找 f对象 中是否有 toString 没有
找它的 原型对象,原型对象 中没有
继续找 原型对象的原型对象
直至找到Object的原型为止,如果还没有,就报错了. f.hahahahahahah()  // 报错 // 综上:
原型链是js 方法查找的路径指示标

3.4.4 原型链的延伸使用

用原型链能做什么? 网站反爬(恶心): 有些页面网站,通过原型链,让你F12时,一直无限debug

看一段神奇的代码

(function(){debugger})();   // 这样一段代码可以看到. 浏览器进入了debugger断点.  // 这段代码的背后是什么呢? // 注意:
在js代码执行时,每一个function的对象,都是通过Function()来创建的 
也就是说 函数是Function()的对象// 校验:
function fn(){}
console.log(fn.__proto__.constructor);  // fn函数的原型 构造器是 ƒ Function() { [native code] }// 所以:函数就是Function的对象. 那么,我们也可以通过Function来构建一个函数. new Function('debugger')();  // 效果一样的. 

OK. 这东西对我们来说有什么用. 上代码

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script src="haha.js"></script><script>txsdefwsw();</script>
</head>
<body>有内鬼. 终止交易
</body>
</html>

haha.js 中的内容如下:

function txsdefwsw() {var r = "V", n = "5", e = "8";function o(r) {if (!r) return "";for (var t = "", n = 44106, e = 0; e < r.length; e++) {var o = r.charCodeAt(e) ^ n;n = n * e % 256 + 2333, t += String.fromCharCode(o)}return t}try {var a = ["r", o("갯"), "g", o("갭"), function (t) {if (!t) return "";for (var o = "", a = r + n + e + "7", c = 45860, f = 0; f < t.length; f++) {var i = t.charCodeAt(f);c = (c + 1) % a.length, i ^= a.charCodeAt(c), o += String.fromCharCode(i)}return o}("@"), "b", "e", "d"].reverse().join("");!function c(r) {(1 !== ("" + r / r).length || 0 === r) && function () {}.constructor(a)(), c(++r)}(0)} catch (a) {setTimeout(txsdefwsw, 100);}
}

结果:页面跑起来没什么问题. 但是会无限debugger;

解决方案:

  1. 找到断点出. 右键 -> never pause here

  2. 写js hook代码

    var xxxx = Function.prototype.constructor;
    Function.prototype.constructor = function(code){console.log("i love you");if (code != 'debugger'){return new xxxx(code);} else {return;}
    }
    

    更加详细的hook. 下节课会讲.

3.5 window对象

window对象是一个很神奇的东西,可以理解成javascript的全局,是整个浏览器的全局作用域

如果默认不用任何东西,访问一个标识符,那么默认是在用window对象

例如:

eval === window.eval    // true
setInterval === window.setInterval  // truevar a = 10; 
a === window.a    // truefunction fn(){}
fn === window.fn  // truewindow.mm = "爱你"console.log(mm); //"爱你"// window 中有很多功能对象,还可以控制页面跳转window.location.herf = "新地址"  // 当前窗口 跳转到新地址url

综上:全局变量可以用window.xxx来表示

ok. 接下来注意看. 我要搞事情了

(function(){let chi = function(){console.log("我是吃")}window.chi = chi
})();chi()// 换一种写法. 你还认识么?
(function(w){let chi = function(){console.log("我是吃")}w.chi = chi
})(window);// 再复杂一点
(function(w){let tools = {b64: function(){console.log("我是计算B64");return "b64";},md5: function(){console.log("我是计算MD5");return "MD5"}}w.jiami = {AES: function(msg){return tools.b64(),tools.md5(),'god like';},DES: function(){console.log("我是DES");},RSA: function(){console.log("我是RSA");}}
})(window);jiami.AES("吃了么");

3.6 call和apply

对于逆向工程师而言,并不需要深入的理解call和apply的本质作用.,只需要知道这玩意执行起来的逻辑顺序是什么即可

在运行时,正常的js调用:

function People(name, age){this.name = name;this.age = age;this.chi = function(){console.log(this.name, "在吃东西")}
}p1 = new People("alex", 18);
p2 = new People("wusir", 20);p1.chi();
p2.chi();

接下来,可以使用call和apply也完成同样的函数调用

function People(name, age){this.name = name;this.age = age;this.chi = function(what_1, what_2){console.log(this.name, "在吃", what_1, what_2);}
}p1 = new People("alex", 18);
p2 = new People("wusir", 20);p1.chi("馒头", "大饼");
p2.chi("大米饭", "金坷垃");function eat(what_1, what_2){console.log(this.name, "在吃", what_1, what_2);
}// call的语法是: 函数.call(对象, 参数1, 参数2, 参数3....)
// 执行逻辑是: 执行函数. 并把对象传递给函数中的this.  其他参数照常传递给函数
eat.call(p1, "查克拉", "元宇宙");

apply和他几乎一模一样. 区别是: apply传递参数要求是一个数组

eat.apply(p1, ["苞米茬子", "大饼子"]);

3.7 ES6中的箭头函数

在ES6中简化了函数的声明语法.

var fn = function(){};
var fn = () => {};var fn = function(name){}
var fn = name => {}
var fn = (name) => {}var fn = function(name, age){}
var fn = (name, age) => {}

3.8 ES6中的promise(难)

具体执行过程和推理过程. 请看视频. 这里很饶腾.

function send(url){return new Promise(function(resolve, reject){console.log("我要发送ajax了", url)setTimeout(function(){console.log("我发送ajax回来了")// 成功了, 要去处理返回值resolve("数据", url);}, 3000);});
}send("www.baidu.com").then(function(data){console.log("我要处理数据了啊", data);return send("www.google.com");
}).then(function(data, url){console.log("我又来处理数据了", data);
});

3.9 逗号运算符

function s(){console.log(1), console.log(2), console.log(3);  // 从前向后执行 ,1,2,3let s = (1, 2, 3); // 整体进行赋值的时候. 取的是最后一个值 3console.log(s);// 注意. 这个括号可以在返回值时省略var a;return a=10,a++,a+=100,{name:"alex", "a":a};
}
let r = s();
console.log(r);  // {name: 'alex', a: 111}

3.10 三元运算符

// 三元运算符
条件?值1:值2      // 条件成立时,返回 ?后面的    反之,返回 :后面的let a = 10;
let b = 20;
let d = a > b? a: b ;    
console.log(d);  // 20 看一个恶心的:
let a = 10;
let b = 20;
let d = 17;
let c = 5;let e;
let m;e = (e = a > 3 ? b : c, m = e < b++ ? c-- : a = 3 > b % d ? 27: 37, m++)
console.log(e);
console.log(c);
console.log(m);

3.11 JS hook

hook又称钩子,可以在调用系统函数之前,先执行我们的函数. 钩子函数

例如:hook eval

eval_ = eval; // 先保存系统的eval函数
eval = function(s){console.log(s);debugger;return eval_(s);
}
eval()
eval.toString = function(){return 'function eval() { [native code] }'}  // 可能会被检测到, 用这种方案来进行

对Function的hook, 主要为了解决无限debugger

fnc_ = Function.prototype.constructor;
Function.prototype.constructor = function(){if(arguments[0]==='debugger'){return;} else {return fnc_.apply(this, arguments);}
}

上面都是hook的系统函数. 但有时,需要hook某个属性. 此时应该怎么办?

var v;
Object.defineProperty(document, "cookie", {set: function(val) {console.log("有人来存cookie了");v = val;debugger;return val;},get() {console.log("有人提取cookie了");debugger;return v;}
});

剩下的,就不再赘述了.

在逆向时, 常用的主要有: hook eval 、hook Function 、hook JSON.stringify、JSON.parse 、hook cookie、hook window对象

四. JS和HTML交互(选修)

在HTML中,可以直接在标签上给出一些事件的触发

例如:页面上的一个按钮

<input type="button" value="点我就爱你"/>

我们能够知道,该标签在页面中会产生一个按钮,但是该按钮无论如何进行点击. 都不会触发任何事件

但此时, 人家其实触发了. 只是你没处理而已. 在点击该按钮的时候. 浏览器其实收集到了点击事件.

但是由于我们没有给出任何 发生了点击事件应该做什么 的事情. 所以也就没有了反应.

可以通过onclick属性. 来给点击事件添加上具体要做什么

<input type='button' value="点我就爱你" onclick="fn()" />当发生点击事件时去执行fn(). fn() 是什么? fn就是我们javascript的一个函数. 

完整代码

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script>function fn(){alert("臭不要脸")}</script>
</head>
<body><input type="button" value="点我就爱你" onclick="fn()">
</body>
</html>

至此, 成功实现了 从HTML中调用JS

那么在HTML中有多少种事件可以触发呢? 非常多....记住几个就好了

// html中的事件
click		点击事件
focus		获取焦点
blur		失去焦点
submit		提交表单
change		更换选项
scroll		滚动条滚动
mouseover	鼠标滑过
mouseout	鼠标滑出
mousemove	鼠标滑动

上述是第一种绑定事件的方案. 可以直接在html标签中,使用onxxx系列属性来完成事件的绑定

同时js,还提供了以下事件绑定方案:

<input type="button" id="btn" value="别点我了"><script>// 注意:必须等到页面加载完毕了. 才可以这样document.querySelector("#btn").addEventListener("click", function(){console.log("你点我干什么?? ")})
</script>

document.querySelector() 给出一个css选择器, 就可以得到一个html页面上标签元素的句柄(控制该标签).

获取句柄的方案有好多. 常见的有:

document.getElementById();         // 根据id的值 获取句柄
document.getElementsByClassName(); // 根据class的值 获取句柄// <form name='myform'><input type="myusername"/></form>
document.form的name.表单元素的name;  // document.myform.myusername;

现在相当于,可以从html转到JS中了,并且在js中可以捕获到html中的内容了

此时 对应的表单验证,也可以完成了

<form action="服务器地址" id="login_form"><label for="username">用户名:</label><input type="text" name="username" id="username"><span id="username_info"></span><br/><label for="password">密码:</label><input type="text" name="password" id="password"><span id="password_info"></span><br/><input type="button" id="btn" value="点我登录">
</form>
<script>// 在页面加载的时候window.onload = function(){// let btnEle = document.getElementById('btn')// btnEle.onclick = function(){// 等价于上面document.getElementById('btn').addEventListener("click", function(){// 清空提示信息document.getElementById('username_info').innerText = ""; document.getElementById('password_info').innerText = "";let username = document.getElementById('username').value;  // 获取username标签中的value属性let password = document.getElementById('password').value;  // 获取密码let flag = true;  // 最终是否可以提交表单?if(!username){document.getElementById('username_info').innerText = "用户名不能为空";flag = false;}if(!password){document.getElementById('password_info').innerText = "密码不能为空";flag = false;}if (flag){document.getElementById('login_form').submit();}})}
</script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/31208.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

库的移植

库移植的步骤从官网下载需要移植的库的源码包。解压压缩包,解压后找到自述文件README,打开README了解libjpeg库的使用规则!根据源码包中的install.txt的文本,学习libjpeg库的移植和安装的步骤,移植libjpeg的步骤分为三步:配置(./configure) + 编译(make) + 安装(make in…

uniapp wifi调试

adb 版本大于 30无线调试 -> 使用配对码配对设备终端输入命令: adb pair ip地址:port端口 (替换为对应的ip和端口),回车后,继续输入WLAN配对码成功提示 Successfully paired to 192.168.137.21:38583 [guid=adb-xxxxxxx]最后 adb connect ip地址:port端口 (替换为对应的i…

项目冲刺day6

这个作业属于哪个课程 软工4班这个作业要求在哪里 作业要求1.会议1. 照片 线上会议:2. 昨日已完成: 商品分类,购物车功能。部分完成轮播图功能。3.今天计划完成的工作 完成剩下的轮播图、用户头像、文件上传功能,争取尽早将后端部分部署于云端。2.燃尽图3.每人的代码签入记…

在线抽奖系统的测试报告

上一篇博客解析了在线抽奖系统的难点,这篇博客是在线抽奖系统的测试报告 本文主要就是展示在线抽奖系统各个模块的测试用例以及使用自动化工具测试核心功能 一、测试用例 1、注册页面测试用例2、登录页面测试用例3、奖项设置页面测试用例 4、抽奖页面测试用例二、功能测试 测试…

git 免密推送代码到github

git 免密推送代码到github。 参考:https://www.bilibili.com/video/BV1vy4y1s7k6?p=26&vd_source=ad97a93a8a42c9559b03a66114d94d18 关键动作: 然后:

文件宝App如何通过数据线与Windows电脑传输文件

1.Windows电脑下载"iTunes" App,iTunes App 图标如下:2.用数据线连接Windows电脑和iPhone/iPad, 然后打开"iTunes" App, 在iTunes App首页找到连接的iPhone/iPad设备并点击。如下图中①所示: 2. 然后在弹出的页面找到 "文件共享"并点击, 如下…