在JS中,有两种设置定时器的方案
// 语法规则
t = setTimeout(函数, 时间)
// 经过xxx时间后, 执行xxx函数
// 5秒后打印我爱你
t = setTimeout(function(){
console.log("我爱你")
}, 5000);
window.clearTimeout(t) // 停止一个定时器
//语法规则
t = setInterval(函数, 时间)
// 每隔 xxx时间, 执行一次xxx函数
// 每隔5秒钟, 打印`我爱你`
t = setInterval(function(){
console.log("我爱你")
}, 5000)
window.clearInterval(t) // 停止一个定时器
关于第二种 setInterval
定时器来说,有一种很损的防爬措施,就是一旦浏览器检测到 F12
被按下后,就疯狂的执行下面这段代码
// 每隔1毫秒, 往缓存里写数据
t = setInterval(function(){
// 往缓存里写数据
}, 1)
这样会导致你的浏览器奇卡无比,最后只能强行关掉它,解决它的方案是
window.clearInterval(t) // 清理掉这个定时器
// 自定义的getTime函数,把时间整理成人看起来舒服的样子
function getTime() {
var now = new Date(); // 创建一个日期对象,默认情况就是系统时间
// 2022-2-26 18:32:xx
// var year = now.getYear(); // 从1900年开始计算
var year = now.getFullYear(); // 正常的年份
var month = now.getMonth() + 1; // 月份
var date = now.getDate(); // 日期
var hour = now.getHours(); // 小时
var minute = now.getMinutes(); // 分钟
var second = now.getSeconds(); // 秒
console.log(year + "-" + month + "-" + date + " " + hour + ":" + minute + ":" + second);
}
setInterval(getTime, 1000);
// 一般前端,如果不需要给用户展示的话,没有必要去格式化这个时间
// 时间戳 (各个语言通用)
var now = new Date();
console.log(now.getTime()); // 获取时间戳 1970-1-1 0:0:0
// Javascript中时间戳的单位是毫秒,Python中时间戳的单位是秒
时间戳的功能:
eval本身在js里面正常情况下使用的并不多,但是很多网站会利用eval的特性来完成反爬操作,我们来看看eval是个什么鬼?
从功能上讲,eval非常简单,它和python里面的eval是一样的,它可以动态的把字符串当成js代码进行运行
s = "console.log('我爱你')"
eval(s);
也就是说,eval里面传递的应该是即将要执行的代码(字符串),那么在页面中如果看到了eval加密该如何是好?其实只要记住了一个事儿,它里面不论多复杂,一定是个字符串,比如
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()里面的东西拷贝出来,执行一下,最终一定会得到一个字符串,要不然eval()执行不了的,对不?于是就有了下面的操作
如果上述操作后得到的javascript代码依然很复杂,则需要考验大家js语法功底了。
prototype是js里面给类增加功能扩展的一种模式
写个类看看
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();
prototype是一种不改变类原始代码的条件下,给类增加新功能的方法,有一点像python中的装饰器,但装饰器是给函数增加新的功能。
继承也是一种给类增加新功能的方式,但prototype不是,它只是把功能添加进了原始的类中,并没有产生新的类。
// 往Date类中增加一个格式化时间的功能
var now = new Date();
Date.prototype.format = function () {
var year = this.getFullYear(); // 正常的年份
var month = this.getMonth() + 1; // 月份
var date = this.getDate(); // 日期
var hour = this.getHours(); // 小时
var minute = this.getMinutes(); // 分钟
var second = this.getSeconds(); // 秒
return (year + "-" + month + "-" + date + " " + hour + ":" + minute + ":" + second);
}
console.log(now.format());
看以下代码,或多或少会有些问题的
function fn(){
console.log(name);
var name = '大马猴';
}
fn()
发现问题了么,这么写代码,在其他语言里,绝对是不允许的,但是在js里,不但允许,还能执行,为什么呢? 因为在js执行的时候,它会首先检测你的代码,发现在代码中会有name使用,OK,运行时就会变成这样的逻辑:
function fn(){
var name;
console.log(name);
name = '大马猴';
}
fn()
console.log(a);
看到了么,实际运行的时候和我们写代码的顺序可能会不一样…这种把变量提前到代码块第一部分运行的逻辑被称为变量提升。这在其他语言里是绝对没有的,并且也不是什么好事情,正常的逻辑不应该是这样的,那么怎么办?在新的ES6中,就明确了,这样使用变量是不完善的,es6提出,用let来声明变量,就不会出现该问题了。
function fn(){
console.log(name); // 直接报错, let变量不可以变量提升.
let name = '大马猴';
}
fn()
结论一, 用let声明变量是新版本javascript提倡的一种声明变量的方案.
let还有哪些作用呢?
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 = "王力宏";
console.log(name);
}
fn()
注意,报错是发生在代码检查阶段,所以,上述代码根本就执行不了。
结论二, 在同一个作用域内. let声明的变量只能声明一次. 其他使用上和var没有差别
先看一段代码
let name = "周杰伦"
function chi(){
name = "吃掉"
}
chi();
console.log(name);
发现没有,在函数内部想要修改外部的变量是十分容易的一件事。尤其是全局变量,这是非常危险的。
再看一个案例:
我准备两个工具人,来编写代码,分别是js01和js02
// 1号工具人.
var name = "alex"
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) // 一号工具人还以为是alex呢, 但是该变量是不安全的.
}, 5000);
})()
// 二号工具人
(function(){
var name = "周杰伦"
console.log("二号工具人", name);
})()
运行结果
这样虽然解决了变量的冲突问题,但是…我们想想。如果我需要函数内部的一些东西来帮我进行相关操作怎么办…比如,一号工具人要提供一个功能(加密),外界要调用,怎么办?
// 1号工具人.
let jiami = (function(){
let key = "10086" // 加装我是秘钥
// 我是一个加密函数
let mi = function(data){ // 数据
console.log("接下来, 我要加密了,rsa哦. 很厉害的")
console.log("秘钥:"+key);
console.log("数据:"+data);
// 返回密文
return "我要返回密文";
}
// 外面需要用到这个功能啊. 你得把这个东东返回啊. 返回加密函数
return mi;
})();
好像有点儿复杂了哈,别着急,注意了。我们如果封装一个加密js包的时候,好像还得准备出解密的功能,并且, 不可能一个js包就一个功能吧…那也太痛苦了(起名字). 那怎么办?我们可以返回一个对象,对象里面可以存放好多个功能,而一些不希望外界触碰的功能,就可以很好的保护起来。
// 1号工具人.
let jiami = (function(){
let key = "10086" // 加装我是秘钥
// 我是一个加密函数
let rsa_jiami = function(data){ // 数据
console.log("接下来, 我要加密了,rsa哦. 很厉害的")
console.log("秘钥:"+key);
console.log("数据:"+data);
// 返回密文
return "我要返回密文";
}
// 该函数只属于该模块内部. 外界无法访问.
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,至此,何为闭包? 上面这个就是闭包,相信你百度一下就会知道,什么内层函数使用外层函数变量,什么让一个变量常驻内存,等等。其实你细看,它之所以称之为闭包~,它是一个封闭的环境,在内部,自己和自己玩儿,避免了对该模块内部的冲击和改动,避免的变量之间的冲突问题。
闭包的特点:
这俩玩意就不解释了,和python的闭包是一个意思,不懂没关系,能看懂他的执行过程就好。
什么问题导致了需要有闭包?
在html中,引入许多js文件,这些js文件中定义在最外层的变量和函数都属于全局作用域,因此相互之间会存在干扰,由于不同的文件很有可能是不同的程序员开发的,因此当变量名字或函数名字产生冲突时,会相互覆盖而出问题,因此需要一种机制让每个程序员写的功能都独立起来,关起门来自己玩自己的。因此产生了闭包的概念。
如何解决变量名冲突问题?(闭包)
在C语言中,不同的C文件中的最外层变量和函数也都是全局作用域的,也会产生类似的问题,但是C语言在相同作用域声明两次同名函数或变量,编译器会直接报错,因此C语言通过在全局标识符前添加 static
关键字,把不同文件里的全局变量变成文件内部的静态全局变量,将全局作用域缩小到以文件为范围。
闭包要解决的问题和C语言中的 static
关键字类似,缩小全局变量的作用域。
由于Javascript中没有 staitc
关键字机制,但原理上只要缩小作用域就可以了,因此考虑到几乎任何编程语言都有的函数局部作用域的概念。将不希望被干扰到的变量名都打包进一个函数中,在函数中声明并使用,这样就相当于将全局作用域变成了函数局部作用域,和C语言中的变成文件作用域类似。
打包 (闭包) 之后,外部如何使用闭包内部的功能?
将打包函数内部需要被外部调用的功能,定义到一个 {}
对象中,并返回出去,由于打包这些功能的函数是全局作用域,因此调用一下该打包函数,并用一个变量来接收它内部返回的 {}
对象,通过 变量.功能()
即可调用闭包函数内部的功能了。
闭包特性第二点:让变量常驻与内存。
这也让我想到了C语言中,函数内部的 static
静态变量,C语言可以在函数内部通过定义静态变量的方式,让变量常驻内存。因此我觉得这里的闭包和C语言中的 staitc
关键字的引用有较深的联系。