JavaScript相关

Javascript基础

​ JavaScript, 是一门能够运行在浏览器上的脚本语言. 简称JS. 首先, Javascript这个名字的由来就很有意思, 不少人认为Javascript和Java貌似很像. 容易想象成Java的脚本. 但其实不然, 两者之间没有任何关系. 纯粹是商业碰瓷.

​ 那么既然JS是可以运行在浏览器上的脚本. 并且, 我们知道本质上, 浏览器是执行HTML程序的. 那么如何在HTML中引入JS呢?

​ 方案一, 直接在标签中引入编写js代码

image-20210826150049916

​ 方案二, 将js代码写在js文件中, 然后通过script标签的src属性进行引入

image-20210826150217635

两种方式运行出的效果是一致的. 但是需要各位注意一点, HTML程序在执行的时候是从上到下进行渲染的.

那么如果我把脚本放在下面和放在上面是有一些不同的.

image-20210826150358678

image-20210826150452740

js基础语法大全
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference

一. Javascript基本数据类型

JS虽然是一个脚本语言. 麻雀虽小, 五脏俱全. 在js中也是可以像其他编程语言一样. 声明变量, 条件判断, 流程控制等等. 我们先看一下JS中的数据类型

在js中主要有这么几种数据类型(基本)

number  数字, 不论是整数还是小数, 数据类型都是number
string  字符串, 这个没啥可聊的. 就是很单纯的字符串
boolean  布尔值, 只有两个, true和false. 注意不是大写T和F. 
object 对象, 这个比较特殊. 你可以理解为所有被new出来的东西都是对象  
undefined, 这个表示未定义. 所有没有被定义过的东西默认都是该类型 类似像空一样的东西

在js中声明变量用var来声明

在js中使用// 来表示单行注释. 使用/* */表示多行注释.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        // // 声明变量
        // var x;  // 声明一个变量叫x, x没有值. 此时打印x的内容是undefined(没有被定义)
        //
        // // 对x进行打印看看效果.
        // // 在控制台记录xxxx => python中的print()
        // // 查看打印的内容在F12 -> Console -> 可以看到打印的内容
        // // console.log("樵夫的湿地是乔丹..");
        // console.log(x);

        // 数字 => number类型(整数,小数)
        // var x = 123.123; // 声明变量x, 并且给x一个初始值. 123
        // console.log(typeof x); // 可以查看数据类型. 但是只能看基础数据类型.

        // var x = "我要上天";
        // console.log(typeof x); // string 字符串

        // // json就是js的一个概念. 是符合js语法的...
        // var x = true;
        // var x2 = false; // 布尔值. python里面是大写的. js里面是小写.
        //
        // console.log(typeof x); // boolean 布尔. python简化成bool

        // var x = []; // 数组. => python的列表.
        // console.log(typeof x);  // 类型叫object

        // var x = {}; // 对象 => python的字典.
        // console.log(typeof x); // 也是object

        // // 两个特殊值
        // var x; // undefined
        // console.log(typeof x);  // undefined类型... 不占用内存空间的
        //
        // var x = null; // 空.... 是python的None, 要使用堆内存的....
        // // 在咱们的视角里. 它俩都是空. 啥都干不了...
        // console.log(typeof x); // object类型

        // var x = function(){};  // 函数
        // console.log(typeof x);  // 所有的函数的数据类型都是function

        // 函数,具体数据 => 逻辑都是一样的. 都当成普通变量来看...重要...

        // // 一次性可以声明多个变量
        // var x = 1, y = 2, z = 3;  // 一次性声明三个变量. 并且都有值.
        // console.log(x);
        // console.log(y);
        // console.log(z);
        //
        // var a, b, c=3, d, e = 5; // 从左到右,声明5个变量, 其中, c=3, e=5


    </script>
</head>
<body>

</body>
</html>

JS中的运算符和Python几乎一致. 但有些特殊的.

and, or, not 到了js中注意,换成了&&, ||, !, 其含义和概念是一致的.

var a = 10, b = 20, c = 30 ;
console.log(a > b && b > c);  // false
console.log(!(a > b)) // 注意括号

== 和 ===,

​ == 只是判断值是否一致,
​ === 会判断数据类型和数据是否都一致.

var a = "123";
var b = 123;
console,log(a == b);  // true
console.log(a === b); // false

数据类型转换:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        // 在js中. 所有的数据和字符串相加. 结果都是字符串拼接.(重点)
        // var a = 123;
        // var b = "我爱你";
        // console.log(a + b);
        // console.log(1+1); // 2

        // var a = "123"; // 字符串
        // // 字符串转化成整数
        // // parse 转换
        // // Int 整数
        // a = parseInt(a);
        // console.log(a + 3); // 126 数学上的加法. 证明转化成功了.

        // var a = 123; // 转化成字符串
        // var b = a.toString(); // 把数字, 转化成字符串
        // console.log(b + 3); //"1233"

        // // 一般的程序员不用toString()的.
        // var a = 123;
        // var b = a + ""; // 在逆向的过程中. 这种写法是最多的.
        // console.log(b+333); // "123333" 字符串


        // var a = 123;
        // // 数字的toString()可以指定进制
        // var b = a.toString(16);
        // console.log(b);

        // var m = '7b'; // 眼睛看着是字符串. 但是我们知道你它应该是一个十六进制的字符串.
        // // 字符串, 转化成 整数   parseInt
        // var n = parseInt(m, 16);
        // console.log(n)
    </script>
</head>
<body>

</body>
</html>

遇见的第一个难点.....关于++

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        // var a = 10;
        // a ++; // 让a自增1  => a = a + 1
        // console.log(a);

        // ++ a; // 让a自增1  => a = a + 1
        // console.log(a);

        // // 在单独使用a++和++a是没有区别的. 都是让a自增1
        // var b = a ++;  // 先赋值, 后运算...
        //
        // console.log(a); // 11
        // console.log(b); // 10

        // var b = ++ a; // 先计算, 后赋值
        // console.log(a);  // 11
        // console.log(b);  // 11

        // 总结:++在前. 先算+, ++在后, 先赋值.  可能会有坑. 大概率遇不到这个坑.

        var a = 20;
        var b = a ++; // b 20
        var c = ++ a;  //a 22   c 22

        console.log(a); // 22
        console.log(b); // 20
        console.log(c); // 22

    </script>
</head>
<body>

</body>
</html>

字符串操作:

s.split()  字符串切割
s.substr(start, len)  字符串切割, 从start开始切, 切len个字符
s.substring(start, end)  字符串切割, 从start切割到end
s.length  字符串长度
s.charAt(i) 第i索引位置的字符  s[i]
s.indexOf('xxx')  返回xxx的索引位置, 如果没有xxx. 则返回-1
s.lastIndexOf("xxx") 返回xxx的最后一次出现的索引位置,如果没有xxx. 则返回-1
s.toUpperCase() 转换成大写字母
s.startsWith("xxx")  判断是否以xxx开头
s.charCodeAt(i) 某个位置的字符的ascii
String.fromCharCode(ascii) 给出ascii 还原成正常字符
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        // var s = "樵夫是一}个美}男子特}别特别帅";
        // console.log(s.split("美}男子")); // 切出来的结果是数组.

        // 字符串截取. 去浏览器跑一下就知道他们是什么意思了...
        // console.log(s.substr(3, 4)); // 34组合  从3开始, 截取4个内容
        // console.log(s.substring(3, 4)); // 34组合 从3开始截取, 截取到4.

        // 字符串操作.记不住的时候. 去浏览器console跑...
        // console.log(s.length);

        // 有用...
        // console.log(s.charAt(3)); // 取索引是3的位置的字符
        // console.log(s[3]); // 取索引是3的位置的字符

        // console.log(s.indexOf("美男")); // `美男`在字符串s中出现的索引位置
        // 如果`美男`没有出现在s中. 此时返回-1
        // if s.indexOf("xxxxx") == -1: 如果不包含.

        // console.log(s.lastIndexOf("}"));  // 获取到最后一个}出现的位置.

        // console.log("aBc".toUpperCase());// 转化成大写
        // console.log("aBc".toLowerCase());// 转化成小写

        // console.log(s.startsWith("樵夫")); // `樵夫`是否是字符串s的开头

        // // 去掉左右两端的空白
        // var s = "abc"
        // console.log(s.trim());
        // s = s.concat("胡辣汤").concat(123).concat(789).concat("哈哈哈"); // 字符串拼接. 相当于+
        // console.log(s);

        // var s = "123456"
        // console.log(s.includes("45"));

        // "a" => ascii => 码位(十进制数字) 97
        // CharCode 字符编码
        // charCodeAt()
        // fromCharCode()
        // console.log('0'.charCodeAt(0)); // 65

        // 汉字
        // var m = "中".charCodeAt(0);
        // console.log(m); // 20013  中文. unicode码
        // 你用的字符串是可以被转化成数字的.
        // 可以被转化成数字. 意味着. 可以进行加密了.

        // // 万恶之源....
        // var s = "我叫樵夫"; // 明文
        // var a = s.charCodeAt(0) + 10086 * 1; // 把字母转化成数字
        // var b = s.charCodeAt(1) + 10086 * 2; // 把字母转化成数字
        // var c = s.charCodeAt(2) + 10086 * 3; // 把字母转化成数字
        // var d = s.charCodeAt(3) + 10086 * 4; // 把字母转化成数字
        //
        // var mi = ""+a+"|"+b+"|"+c+"|"+d;
        // console.log(mi);

        // 上面这个东西如果是服务器返回的
        var s = "35191|41655|57447|63171";
        console.log("mi", s);
        // 在它的js里面百分之百要有一个功能. 该功能负责把上述字符串还原成正常的字符串
        var arr = s.split("|");
        var a = parseInt(arr[0]) - 10086 * 1;
        var b = parseInt(arr[1]) - 10086 * 2;
        var c = parseInt(arr[2]) - 10086 * 3;
        var d = parseInt(arr[3]) - 10086 * 4;

        console.log(a, b, c, d);
        // 把数字, 转化回 字符
        a = String.fromCharCode(a);
        b = String.fromCharCode(b);
        c = String.fromCharCode(c);
        d = String.fromCharCode(d);

        console.log("明文", a+b+c+d);

        // 什么叫逆向?
        // 在js里一定有加密或者解密的过程.
        // 我们只需要阅读它的代码找出加密或者解密的逻辑. 在你的程序里. 重现该逻辑
        // 上述过程叫逆向...

    </script>
</head>
<body>

</body>
</html>

关于null和undefined. 这两个会很容易混. 你可以这样来记. null就是空对象. undefined就是空变量. 两者都可以表示空. 啥也没有. 本质其实是一样的. 都啥也干不了. 两者都可以当做false来看待就好了.

二. JS条件分支

​ 除了HTML以外. 几乎所有的编程语言都有条件判断的功能. 比如, python, 我们用if语句来做条件判断. 到了javascript中也是一样的, 也使用javascript来做条件上的判断.

// 语法
if(条件1){
    代码块1    
}

// 解读: 当`条件1`成立时, 执行代`码块1`中的内容, 如果`条件1`不成立. 则不执行该`代码块1`中的内容
// 注, 如果代`码块1`中的内容只有一行. 则可以省略外面的大括号(一些逆向工程里会有)
// 语法
if(条件1){
    代码块1
} else {
    代码块2
}
// 解读: 当`条件1`成立时, 执行`代码块1`中的内容, 如果`条件1`不成立. 则执行`代码块2`中的内容
// 语法
if(条件1){
    代码块1
} else if(条件2) {
    代码块2
} else if(条件3) {
    代码块3
} ... {
	代码块n
} else {
    代码块else
}
// 解读: 当`条件1`成立时, 执行`代码块1`中的内容, 如果`条件2`不成立. 则执行`代码块2`中的内容...如果都不成立, 最终执行`代码块else`中的内容. 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        // var a = 100;
        // var b = 20;
        // 条件要使用小括号括起来
        // 边界是以{}为准的.
        // 坑: 在js中. 如果没有{}, 边界还可以使用;来描述.
        // if(a > b){
        //     console.log("a比b大");
        // }

        // if(a > b)
        //     console.log("a比b小");
        //     // 该代码的执行和if没有关系....
        //     console.log("我要上天");

        // // 逗号表达式.
        // // 从左到右执行. 到分号结束
        // if(a<b) console.log("我要上天"),
        //     console.log('我要下地狱');
        // // 上述代码改写成正常的代码:
        // if(a<b){
        //     console.log("我要上天");
        //     console.log('我要下地狱');
        // }



        // var a = 100;
        // var b = 20;
        //
        // if (a > b)console.log("a比b大");else console.log("a比b小");

        // var score = 45;
        // if (score >= 90){
        //     console.log("A");
        // } else if(score >= 80){
        //     console.log("B");
        // } else if(score >= 70){
        //     console.log("C");
        // } else if(score >= 60){
        //     console.log("D");
        // } else {
        //     console.log("E");
        // }



    </script>
</head>
<body>

</body>
</html>

switch语句. 该语句是python中不存在的. 但是在Java和C, 以及JS中依然会有使用

switch(变量){
    case 值1:
        代码块1
        break  // 可选
    case 值2:
      	代码块2
        break  // 可选
    case 值3:
        代码块3
        break  // 可选
        
    default:   // 可选
        default代码块
}

/*
	解读: 
		执行时, 
		switch会判断变量的值是否是`值1`, 
		如果是, 则执行代码块1以及代码块1中的break, 
		如果不是, 则继续判断`值2`...
		如果前面的`值`都没有和`变量`相等的.则执行`default代码块`. 
		
	注意, 
		每一个`case`中都可以选择`break`, 也可以不选择`break`, 需要注意的是, 如果不写`break`. 
		那么就会形成`case穿透`现象. 
		
	例, `变量`的值如果和`值1` 相等. 并且case1中没有写`break`, 
	则在执行的时候. 会执行完`case1`中的代码. 
	然后会自动穿透到`case2`中去执行里面的代码, 而不经过case2中的数据的验证. 
*/

案例1

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        // var week = 3;
        // switch(week){
        //     case 1:
        //         console.log("星期一");
        //         break;
        //     case 2:
        //         console.log("星期二");
        //         break;
        //     case 3: // 某一个case匹配成功. 那么后面的case就不判断了, 直接被执行.
        //         console.log("星期三");
        //         break;
        //     case 4:
        //         console.log("星期四");
        //         break;
        //     case 5:
        //         console.log("星期五");
        //         break;
        //     case 6:
        //         console.log("星期六");
        //         break;
        //     case 7:
        //         console.log("星期天");
        //         break;
        //     default:
        //         console.log("啥也不是!");
        //         break;
        // }
        // case穿透现象:
        // 某一个case匹配成功. 那么后面的case就不判断了, 直接被执行.
        // 它是现象. 不是bug. 人家语法就是这么设计的.
        // 可以使用break来解决穿透现象. 跳过后面的代码. 离开switch


        // // case穿透使用场景
        // // 输入月份. 输出第几季度
        // var month = 7;
        // switch(month){
        //     case 1:
        //     case 2:
        //     case 3:
        //         console.log("第一季度");
        //         break;
        //     case 4:
        //     case 5:
        //     case 6:
        //         console.log("第二季度");
        //         break;
        //     case 7:
        //     case 8:
        //     case 9:
        //         console.log("第三季度");
        //         break;
        //     case 10:
        //     case 11:
        //     case 12:
        //         console.log("第四季度");
        //         break;
        // }



    </script>
</head>
<body>

</body>
</html>

案例2

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        // 能不能打乱代码的书写顺序..
        // 代码的运行的顺序是不能乱的..

        // 需要一个数组. 来描述代码的正确执行顺序
        // 流程平坦化.... 平坦流...
        var arr = [5,1,3,2,4];
        var flag = true;
        var a = 0; // 4
        while(flag){
            switch (arr[a++]){
                case 1:
                    console.log("2. 挽起袖子");
                    break;
                case 2:
                    console.log("4. 兄弟们上");
                    break;
                case 3:
                    console.log("3. 大吼一声");
                    break;
                case 4:
                    console.log("5. 上车. 跑路");
                    flag = false;
                    break;
                case 5:
                    console.log("1. 怒目而视");
                    break;
            }
        }

        // 上述逻辑执行的真正的过程是:
        // console.log("1. 怒目而视");
        // console.log("2. 挽起袖子");
        // console.log("3. 大吼一声");
        // console.log("4. 兄弟们上");
        // console.log("5. 上车. 跑路");

    </script>
</head>
<body>

</body>
</html>

案例2修改版

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>

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

        var i = 0;
        var flag = true;
        while(flag){
            var m = arr[i++];
            if(m > 5){
                if(m > 7){
                    if (m > 9){
                        console.log("6. 上车. 跑路");
                    } else {
                        if (m > 8){
                            console.log("10. 上车. 跑路");
                            flag=false;
                        } else {
                             console.log("1. 怒目而视");
                        }
                    }
                } else {
                    if (m > 6){
                        console.log("8. 上车. 跑路");
                    } else {
                        console.log("9. 上车. 跑路");
                    }
                }

            } else {
                if (m > 3){
                    if(m > 4){
                        console.log("4. 兄弟们上");
                    } else {
                        console.log("5. 上车. 跑路");
                    }
                } else{
                    if (m > 2){
                        console.log("3. 大吼一声");
                    } else {
                        if (m > 1){
                            console.log("7. 上车. 跑路");
                        } else {
                            console.log("2. 挽起袖子");
                        }
                    }
                }
            }
        }

        // console.log("1. 怒目而视");
        // console.log("2. 挽起袖子");
        // console.log("3. 大吼一声");
        // console.log("4. 兄弟们上");
        // console.log("5. 上车. 跑路");
        // console.log("6. 上车. 跑路");
        // console.log("7. 上车. 跑路");

    </script>
</head>
<body>

</body>
</html>

三. JS中的循环语句

在js中有三种循环语句. 首先是while循环. 它的逻辑和咱们python中的while几乎一模一样, 就是符号上有些许的区别.

// 语法
while(条件){
    循环体 ->  里面可以有break和continue等关键字
}


/*
	判断`条件`是否为真, 如果`真`, 则执行`循环体`.执行完`循环体`, 会再次判断`条件`....
	并且在循环中也可以使用`break`和`continue`等关键字来控制循环的走向. 
*/
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        var a = 0; // 循环变量. 用来记录循环次数的一个变量...
        while(a < 100){  // 判断循环是否正常进行...
            console.log("我爱你");  // 业务需求...循环内容...
            a ++;  // 改变循环变量.  必须放在业务代码执行之后.
        }

    </script>
</head>
<body>

</body>
</html>
// 语法
do{
    循环体
} while(条件);

/*
	解读:
		先执行`循环体`, 然后判断`条件`是否成立, 如果成立.在来一次. 
	注意, 由于do..while是先执行的`循环体`. 所以, 不论条件如何, 至少执行一次`循环体`
*/


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        // var i = 0;
        // do {
        //     console.log(i);
        //     i ++;
        // } while(false);
        // // 和while的区别. 由于do.while是先执行循环体. 然后判断条件.
        // // 至少会执行一次.

    </script>
</head>
<body>

</body>
</html>
// 语法: for的第一种语法
for(表达式1; 表达式2; 表达式3){
	循环体
}
/*
	解读: 
		for循环和我们python中的循环是完全不一样的. 解读起来会有点儿麻烦. 
		首先, 在执行的时候, 先执行`表达式1`, 
		然后, 判断`表达式2`得到的结果是否真, 如果`真`, 则执行循环体, 
		再然后, 执行`表达式3`, 
		再然后, 判断`表达式2`执行的结果是否为`真`, 如果`真`, 则执行`循环体`
        再然后, 执行`表达式3`
        .....
        直到, `表达式2`得到的结果是`假`, 则跳出循环
*/
// 看起来很绕. 我们用for循环来跑一个1~99
for(var i = 1; i < 100; i++){
    console.log(i);
}
/*
	首先, i = 1, 
	然后, 判断 i < 100 成立
		打印i
	在然后, i++, i变成2
	再然后, 判断 i < 100 还是成立
		打印i
	再然后, i++, i变成3
	再然后, 判断 i< 100 还是成立
		打印3....
	....
	当i = 100了. i < 100不成立. 程序结束 
*/

// for循环的固定逻辑也就这样了
for(变量声明; 条件判断; 改变变量){
 	循环体
}

// for的第二种用法
var a = [11,22,33,44,55,66]
for(let i in a){
    console.log(i + "_" + a[i])
}
// 这种写法非常类似python中的for循环. 但是要注意. 这里的`i`拿到的仅仅是 `数组a`的索引信息. 
// 如果需要数据 a[i]
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        // 一个更纯粹的循环逻辑
        /**
        for(对循环变量的初始化;循环条件;当循环体执行之后你要做什么事儿){
            你的循环体, 你的业务逻辑
        }
         */
        for(var i = 0; i < 10; i++){
            console.log(i);
        }

        /**
         *
         * for(语句1;表达式2;表达式3){
         *     循环体
         * }
         *
         * 1. 首先, 会执行语句1, 通常会在语句1里完成对循环变量的初始化
         * 2. 然后, 判断表达式2计算的结果是否是真, 如果是真, 则执行循环体, 如果是假, 结束循环
         * 3. 执行完循环体后. 执行表达式3
         * 然后回到第二步...
         *
         */

        /**
         *
        for (C = S[R--],
        z = v(b, O),
        A = "",
        P = i.q[z][0]; P < i.q[z][1]; P++)
            A += String.fromCharCode(r ^ i.p[P]);
        */

        // var C = S[R--];
        // var z = v(b, O);
        // var A = "";
        //
        // // 偏移量....
        //
        // for (var P = 10; P < 20; P++){
        //     A += String.fromCharCode(r ^ i.p[P]);
        // }

    </script>
</head>
<body>

</body>
</html>

四.JS中的数组和对象

数组

在JS中创建数组非常简单. 直接[ ]即可. 也可以用正规军的new Array(). 不过效果都是一样的.

var as = [11,22,33,44,55];
var bs = new Array(11,22,33,44,55);

数组的常用操作

arr.length;  // 数组长度
arr.push(data);  // 添加数据
arr.pop();  // 删除数据, 从后面删除, 并返回被删除的内容
arr.shift()  // 删除数据, 从前面删除, 并返回被删除的内容 

// arr中的每一项循环出来. 分别去调用function函数, 会自动的将`数据`传递给函数的第一个参数
arr.forEach(function(e, i){  // 第二个参数是可选的
    console.log(i+"__"+e);
});
arr.join("连接符");  // 使用`连接符`将arr中的每一项拼接起来. 和python中的 "".join()雷同
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        // 注意, 前端js里面的数组. 相当于python的列表, java的List
        // java, c的数组.不是一回事儿...

        // 创建数组的方法;
        // 1. 直接赋值一个数组
        // var arr = [11,22,33];
        // console.log(arr);

        // // new表示创建对象.  理解成分配内存.
        // var arr2 = new Array();
        // arr2.push("樵夫");// 给数组添加数据
        // arr2.push("于谦");// 给数组添加数据
        // arr2.push("老郭");// 给数组添加数据
        // console.log(arr2);

        // var arr3 = new Array; // 不标准的写法..
        // arr3.push("1111");
        // arr3.push("1111");
        // arr3.push("1111");
        // arr3.push("1111");
        // arr3.push("1111");
        // console.log(arr3);

        // // 1. push 在数组的末尾添加数据...
        // // 2. pop 在数组的末尾删除数据
        // var arr = [11,22,33,44];
        // var item = arr.pop();
        // console.log(item);
        //
        // console.log(arr);
        // 这两个操作组合在一起. 就是数据结构 => 栈...
        // 栈的特点:  先进后出...
        // 计算机底层逻辑.
        // 计算机在进行函数调用的时候. 一定需要记录调用关系. 否则,
        // 返回值怎么返回? 返回到哪儿?

        // 通过出栈和入栈可以维护计算机最底层的函数调用关系.
        // // 头 .
        // var arr = [11,22,33];
        // // arr.unshift('喝酒'); // 在数组的前面插入数据
        // // console.log(arr);
        // var item = arr.shift();
        // console.log(item);
        // console.log(arr);

        // js中的数组, 既可以在开头增加和删除, 也可以在末尾增加和删除

        // var arr = [111,22,33, "樵夫", "lao baby"];
        // console.log(arr.join("_")); // 把数组转化成字符串

        // 循环和遍历
        //  key       0         1       2       3       4         5
        var arr = ["朱元璋", "朱允炆", "朱棣", "朱高炽", "朱高煦", "朱瞻基"];

        // for(var i = 0; i < arr.length; i++){
        //     console.log(arr[i]);
        // }

        // for-in
        // for(var x in arr){ // 此时拿到的是数组的索引(key)
        //     console.log(x);
        // }
        // // for-of
        // for (var x of arr){ // 此时拿到的是数组的元素(value)
        //     console.log(x);
        // }

        // // arr中的每一个元素执行里面传递进去的函数
        // // 回调函数的参数是 元素和索引
        var ret = arr.forEach(function(value, index){
            console.log(value);
            console.log(index);
            return 1;
        });
        console.log(ret);   // undefined

        // map和forEach的区别: map可以回收每一次函数调用的结果. 最终组合成一个新的数组.
        // var ret = arr.map(function(value, index){
        //     console.log(value);
        //     console.log(index);
        //     return 1;
        // });
        // console.log(ret);    // [1,1,1,1,1]

    </script>
</head>
<body>

</body>
</html>

对象

在JS中创建一个对象非常容易. 和python中的字典几乎一样{ }

var p = {
    name: "汪峰",
    age: 18,
    wife: "章子怡",
    
    chi: function(){
        console.log("吃饭")
    }
};

使用对象

p.name
p.age
p['wife']
p.chi()
p['chi']()

从上述内容中几乎可以看到. JS对象的使用几乎是没有门槛的. 十分灵活

for(var n in p){
    if(typeof(p[n]) != 'function'){
        console.log(p[n])
    }
}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        // var obj = {
        //     "name": "汪峰",
        //     age: 18,
        //     wife:{
        //         name: "子怡",
        //         age: 28
        //     }
        // }

        // 当他是python的字典, 无缝衔接的. `[]` 也可以翻译成`的`
        // console.log(obj['wife']['name']);
        // // 当他是对象... 对象.属性  `.`可以翻译成`的`
        // console.log(obj.name)
        // console.log(obj.wife.name)

        // // 它俩可以混着来.
        // var w = "wife"
        // console.log(obj[w].name);
        // console.log(obj.wife['name']);
        // // js的对象在使用的时候. 既可以通过`[]`来获取属性的值. 也可以通过`.`来获取属性的值
        // // 区别: []里面必须放字符串.
        // //       .后面放的必须是属性名(不可以是字符串)
        // console.log(obj.wife.name); // 68

        // 如果想要对代码进行伪装,混淆. 就不能用`.`
        // 万事万物皆为对象.

        // var i = 9967;
        // var j = 9985;
        // console.log(obj['wife']['name']); // 子怡
        // console.log(obj.wife.name); // 子怡

        // 综上. 我们未来见到的代码里. 更多的是 obj['xxxx']
        // 不论多恶心. 最终在浏览器上一定是要能运行的....
        // 所有的`.`后面的东西都可以换成[字符串]


        var obj = {};
        obj.name = "樵夫";  // 对象.属性 = 值. 可以给对象增加新的属性(设置一个新的属性)
        obj['age'] = 28;

        console.log(obj);




    </script>

</head>
<body>

</body>
</html>

关于对象的一个小补充

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        var obj = {
            name: "汪峰",
            age: 18,

            sing: function(song){
                // 需要当前对象的信息
                // 在前端当前对象是this => self
                console.log(this.name, "准备开始唱", song);
            }
        }

        obj.sing("春天里");
        var $ = console.log;
        $('哈哈')

    </script>
</head>
<body>

</body>
</html>

五. JS中的函数(重点)

Python中的函数调用关系为

def func1():
    func2()


def func2():
    ret2 = func3()
    print(ret2)  # 222


def func3():
    ret3 = func4()
    print(ret3)  # 333
    return 222


def func4():
    print(123)
    return 333


func1()
func1()

"""
123
333
222
123
333
222
"""

# 栈:先进后出

​ 在JS中声明函数和python差不多. 也要有一个关键字顶在前面. python是def, 到了JS里换成了function, 只不过在JS中没有像python那么死板, 必须def后面必须跟上函数名. 这也为我们未来做逆向提供了第一个超大的伏笔.

// 语法
// 声明函数
function 函数名(形参1, 形参2, 形参3....){
    函数体
    return 返回值
}
// 调用函数
函数名(实参1, 实参2, 实参3....)

// 除了写法换了一丢丢. 其他的东西和python完全一致,

简单来个案例看看

function an(a, b){
    return a + b
}

ret = an(1, 2)
console.log(ret);  // 3

很简单不是么? 那为什么我们在网页上看到一些网站的JS是如此的复杂呢?

注意了, JS其实没有一个非常非常严格的语法规则. 只要能够形成 xxx() 并且xxx是一个函数的话就可以执行.

例如:

var an = function(a, b){
    return a + b 
}
an(1, 2)  // an虽然是var声明的, 但是它的指向是一个函数. 那就可以执行

var $ = function(a, b){
    
}
$(1, 2) // $没有被JS使用. 我就可以用啊. _也OK


// 这个也很过分. 这个东东要拆开来看 第一个括号里面放的就是一个函数啊. 所以依然可以执行. 
(function(a, b){
    return a + b
})(1, 2)

c = (function(){
    var m = {
        name:'alex',
        age:'18',
        xijiao: function(a){
            console.log(a+"来帮我洗脚");
        }
    }
    return m
})

//  还有最后一个问题. 未来我们也会遇到的. 就是它这个return
var an = function(){
    return console.log("我爱你"), console.log("爱你妹"), "哈哈"
}
// 注意我们发现js会把return后的每一个,都执行一次. 但是最终真正的返回值其实是最后的那个"哈哈"

js里的函数

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        /**
         * def fn():
         *  pass
         */
      // function fn(){
      //     console.log("我爱你")
      // }
      //
      // fn();

        // // 参数, 是可以不固定的.
        // function gn(a, b){
        //     console.log(a, b); // 简单的使用一下参数
        //     console.log(arguments); // 每个函数内部都有一个arguments,它接受的是所有参数
        // }
        // // gn(1, 2);
        // // gn(1, 2, 3); // 参数如果多了. 是可以的
        // // gn(1); // 参数如果少了. 也是可以的. 但是接不到值的形参是undefined
        // gn(1,2,3,4,5,6,7)

        // 逗号运算符(表达式)的运算结果是最后一个表达式的结果
        // 逗号运算符的逻辑是:
        // 表达式1;
        // 表达式2;
        // return 表达式3;
        // 返回值
        // function en(){
        //     123;
        //     456;
        //     return 789;
        // }
        // var ret = en();
        // console.log(ret);

        // function en(){
        //     return;
        // }
        // console.log(en()); // 默认没有返回值的时候. 得到的是undefined



    </script>
</head>
<body>

</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        // function fn(){
        //     console.log("哈哈哈哈哈")}
        // var gn = [1,22,33];
        // // 变量也好, 函数名也好. 最终操纵的都是他们背后的那个东西
        // console.log(fn);
        // console.log(gn);

        // var mn = 123;
        // var nn = function(){
        //     console.log(4444444)
        // };
        // // 把一个函数彻底的当成一个值(特殊)来看待了.
        // console.log(mn);
        // console.log(nn);


        var a = "哈哈";
        var b = function(){
            console.log("我是一个可怜的函数, 我被别人运行了", "樵夫夫妇付付费")
        }

        // 100

        function fn(mmm){
            if(typeof mmm === 'function'){ // 判断是否是函数
                mmm(); // 37   9
                console.log("你传递进来的是一个函数");
            } else {
                console.log("你传递进来的是一个普通变量的值", mmm);
            }
        }
        // fn(a);
        // fn("哈哈");
        // 这个函数有个特殊的名字, 叫回调函数.
        fn(function(){console.log("我是一个可怜的匿名函数. 完全没有名字. ")});
        // fn(b);
        // 在js里. 函数+() 就可以被运行.
        // 发请求 = function(e, o, n) {  // o()
        //     $.ajax({
        //         url: "/apis/" + e.url,
        //         成功: o(n)
        //     })
        // }
        //
        //
        // 发请求(params, function(mn){ // 回调函数
        //     return 123456;
        // })

        // 函数是可以被作为参数传递的..

        // function fn(){
        //     return function(){
        //         console.log(123);
        //     }
        // }
        //
        // fn()();
        // 函数+() 就可以运行函数
        // 基本上真的快把一个函数当成普通值了...

        var arr = [
            function(){console.log("我是1")},
            function(){console.log("我是2")},
            function(){console.log("我是3")}
        ];

        arr.push(function(){});

        for(var i = 0 ; i < arr.length; i++){
            arr[i]();
        }

    </script>
</head>
<body>

</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
  <script>
      // () 括号运算符
      // 先产生一个函数. 然后运行它
      // (function(){
      //     console.log(123);
      // })(); // 7

      // 自运行函数
      // 后续的代码中不可能重新运行它.

      // +function(a){console.log('新', a)}(123);
      // ~function(a){console.log('新', a)}(456);
      // !function(a){console.log('新', a)}(789);
      // (function(a){console.log('新', a)})(0);
      // (function(a){console.log('新', a)}(111));

  </script>
</head>
<body>

</body>
</html>

Javascript进阶

一. 变量提升(不正常现象)

看以下代码, 或多或少会有些问题的.

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没有差别

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        // // 使用变量的过程应该是, 先声明, 然后再使用..
        // function fn(){
        //     var qiao = "乔丹";
        //     console.log(qiao);
        // }
        //
        // fn();

        // function fn(){
        //     // 在js里面打印出来的是undefined
        //     // 放在其他语言里面, 这里应该是要报错的...
        //     // js里面的变量提升.只针对var... 本来应该在后面创建的东西. 提前创建出来..
        //     // 只能提升变量的声明, 不能提升变量的值..
        //     // 真正的执行逻辑:
        //     // var qiao;
        //     // console.log(qiao); // 1  undefined
        //     //
        //     // var qiao = "乔丹";  // 2
        //     // // var qiao = "乔丹";
        //
        //     // 在新版本的js语法规范上. 对变量的提升做了明确的规定.
        //     // es6中, 规定使用let声明出来的变量.不允许变量提升...
        //
        //     // // var a = 123;
        //     // // 如果我们自己写代码, 首选let.
        //     // console.log(qiao); // 此时是报错的. 报错才是对的..
        //     // let qiao = "樵夫";
        // }
        // fn()

        // 作用域
        // 每个函数自己会产生一个独立的内存区域.
        // 外界是无法进行访问的.

        // function fn(){
        //     // var ha = "樵夫"; // 局部变量
        //     // xxxxxx   108000
        //     // var ha = "lucky";
        //
        //     // Identifier 'ha' has already been declared
        //     // 标识符 'ha' 已经 被声明了.
        //     let ha = "樵夫";
        //     // let ha = "lucky"; //....
        //     console.log(ha);
        // }
        //
        // let ga = "吼吼"; // 这么写是没问题的.
        // // let ga = "大大大";
        //
        // fn();
        // 在es6中, 规范了很多之前的语法的逻辑错误...

        // // 约定俗成的写法. 变量的字母都大写. 表示常量.
        // var WIDTH = 1440;
        // var HEIGHT = 1220;
        //
        // WIDTH = 1300; // 依然是可以被修改的.
        // console.log(WIDTH);
        // 新版本es6中, 使用const来描述一个常量的声明.
        // 被它声明的东西是不可以被重新赋值的.

        // const WIDTH = 1440;
        // console.log(WIDTH);
        // WIDTH = 1300;

        // 在我们的视角里. let, const和var没区别.
        // let, const: 在同一个作用域里不允许声明两个一样的变量
        // 解决了变量提升.
        // const表示常量
        // let表示变量

    </script>
</head>
<body>

</body>
</html>

二. 闭包函数

我们先看一段代码.

let name = "周杰伦";
function chi(){
    name = "吃掉";
}
chi();
console.log(name);

发现没有, 在函数内部想要修改外部的变量是十分容易的一件事. 尤其是全局变量. 这是非常危险的. 试想, 我写了一个函数. 要用到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>

此时运行的结果:

image-20210828145001335

很明显, 虽然各自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);
})()

运行结果

image-20230429194640109

这样虽然解决了变量的冲突问题. 但是...我们想想. 如果在外面需要函数内部的一些东西来帮我进行相关操作怎么办...比如, 一号工具人要提供一个功能(加密). 外界要调用. 怎么办?

// 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. 至此. 何为闭包? 上面这个就是闭包. 相信你百度一下就会知道. 什么内层函数使用外层函数变量. 什么让一个变量常驻内存.等等. 其实你细看. 它之所以称之为闭包~. 它是一个封闭的环境. 在内部. 自己和自己玩儿. 避免了对该模块内部的冲击和改动. 避免的变量之间的冲突问题.

闭包的特点:

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

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

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>自己开发一个淘宝</title>
    <script src="樵夫.js"></script>
    <script src="lucky.js"></script>
    <script>
        // // 使用lucky和樵夫封装好的代码
        // // 文档:
        // // qiaofu_jiami()樵夫的加密, 可以对xxxx. 进行xxxxx
        // // 文档:
        // // lucky_jiami()lucky的加密, 可以对xxxx. 进行xxxxx
        //
        // qiaofu_jiami();
        // lucky_jiami();
        // 调用樵夫的加密
        qiaofu.jiami();
        qiaofu.jiemi();
        lucky.jiami();
        lucky.jiemi();

    </script>

</head>
<body>

</body>
</html>
// fn函数为了解决变量冲突被迫产生的.
qiaofu = (function(){
    // 樵夫的代码里需要用到加密和解密
    var key = "樵夫的秘钥";

    function shangdi() {
        console.log("樵夫使用", key, "来加密");
    }
    // 这个return是干啥的?
    // 把闭包内部的东西. 提供给外界.
    // 让外界能够使用到该闭包内的东西.
    return {
        jiami: shangdi,
        jiemi: function(){
            console.log("樵夫的解密");
        },
    }
})();
// 16 + 15 = 31
lucky = (function(){
    // lucky的代码里需要用到加密和解密
    var key = "lucky的秘钥";

    function shangdi(){
        console.log("lucky使用", key, "来加密");
    }
    return {
        jiami: shangdi,
        jiemi: function(){
            console.log("lucky提供的解密")
        }
    };
})();

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

3.1 定时器

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

// 语法规则
t = setTimeout(函数, 时间)
// 经过xxx时间后, 执行xxx函数

// 5秒后打印我爱你
t = setTimeout(function(){
    console.log("我爱你")
}, 5);

window.clearTimeout(t)  // 停止一个定时器
//语法规则
t = setInterval(函数, 时间)
// 每隔 xxx时间, 执行一次xxx函数

// 每隔5秒钟, 打印`我爱你`
t = setInterval(function(){
    console.log("我爱你")
}, 5000)

window.clearInterval(t)  // 停止一个定时器

for(let i = 0; i <= 9999; i++)window.clearInterval(i); // 清理掉所有定时器
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        // // 定时, 多久之后, 自动执行某函数
        // setTimeout(function(){
        //     console.log("我是周润发");
        // }, 2000); // 时间单位是毫秒
        // 这玩意一般和无限debugger一起出现 .

        var username = "樵夫";
        // while(true){ // 这个是不能做无限debugger的.
        //     debugger; // 在不打开F12的情况下是没有意义的.
        // }
        // 无限debugger
        // 1. 最好用的解决方案: 在行号位置, 右键 => never pause here, 然后释放断点就可以了
        // 2. 置空法(hook的逻辑), 可以对无限debugger的入口进行置空, 可以对setTimeout或者setInterval进行置空
        //              该方案需要找到进入无限debugger的入口. 或者定时器的入口. 需要打断点, 然后置空.
        // 3. hook(后面讲)

        // 注意, 无限debugger不止这一种方案.

        function fn(){
            debugger;
            setTimeout(fn, 300);
        }
        fn()
        console.log(username);
        alert("执行完毕");


        // 每隔3000毫秒执行一次函数
        // setInterval(function(){
        //     console.log('我要上天');
        // }, 3000)

        // 无限debugger
        // setInterval(function(){
        //     debugger;
        // }, 200)


    </script>
</head>
<body>

</body>
</html>

3.2 关于时间

http://www.baidu.com/s?word=jfdsaf&_t=1640090719637

var d = new Date(); // 获取系统时间
var d = new Date("2018-12-01 15:32:48"); // 得到一个具体时间

// 时间格式化
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;
d.getTime()  // 时间戳. 表示从1970-1-1 00:00:00 到现在一共经过了多少毫秒
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        // var now = new Date();// 当期系统时间
        // console.log(now);

        // var n = Date.now();
        // console.log(n); // 1680875906884 时间戳  python => time.time()

        // // 了解时间的格式化
        // var d = new Date();
        //
        // var year = d.getFullYear();
        // var month = d.getMonth()+1;
        // var day = d.getDate();
        //
        // var hour = d.getHours();
        // var min = d.getMinutes();
        // var sec = d.getSeconds();
        //
        // // 格式化, 在js里格式化字符串用的是模板字符串(``)
        // // %Y-%m-%d %H:%M:%D
        // var result = `${year}-${month}-${day} ${hour}:${min}:${sec}`;
        // console.log(result);

        // // 获取时间戳的方案
        // // 方案一.
        // console.log(Date.now());
        // // 方案二.
        // // var d = new Date();
        // var d = new Date;
        // console.log(d.getTime());

    </script>
</head>
<body>

</body>
</html>

3.3 eval函数(必须会. 隔壁村老太太都会.)

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()执行不了的. 对不...于是就有了下面的操作.

image-20230429194948666

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

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
  <script>

    // // var c = 1+1;
    // // console.log(c);
    //
    // var code = "1+1"; // 把代码存入字符串了
    // var ret = eval(code); // eval的工作是把一个字符串当做代码进行运行. 返回运行的结果
    // console.log(ret);
    // // 1. eval可以运行js代码
    // // 2. 参数, 必须是字符串..注意, 不管参数写成什么样子, 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,{}))
  </script>
</head>
<body>

</body>
</html>

3.4 prototype是个什么鬼 (超重点)

image-20230429194730486

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        // var obj = {
        //     name: "樵夫",
        //     sing:function(){
        //         // 注意, 在前端. 用this来表示当前对象. 和python里的self是一样的
        //         // 不用传递this
        //         console.log(this.name, "樵夫喜欢唱歌");
        //     }
        // }
        //
        // // console.log(obj.name);
        // // console.log(obj['name']);
        //
        // obj.sing();

        // // // 在前端new表示创建, 开辟内存
        // // // new后面的东西给对象进行初始化操作
        // // var d = new Date();
        // // var m = new Array(); // 创建一个数组
        //
        // // 该函数在被new的时候. 会在新开辟的内存中给对象进行初始化操作
        // // 该函数被称为构造函数(constructor)
        // // 类比就是python中的__init__
        // function Person(name, age, address){
        //     // this表示当前对象
        //     this.name = name;
        //     this.age = age;
        //     this.address = address;
        //     // 这样写和类是没有关系的.
        //     // 这相当于每个对象里面有了一个特殊的属性...
        //     this.nakele = function(){
        //         console.log(this.name, "帮我拿可乐");
        //     }
        // }
        //
        // // new constructor()
        // // var p1 = new Person("林志玲", 18, "沙河"); //直接执行该函数. 和对象没关系
        // // // console.log(p1);
        // // console.log(p1.name);
        // // console.log(p1.address);
        // //
        // // p1.nakele();//
        // //
        // // 注意, 在es6出现之前. 是没有类的..
        // // 在老版本的js里面,实用原型来表示类...
        // // 在js中, 所有的函数在创建的时候.  js就会产生一个该函数的原型对象
        // // 该原型对象. 我们可以看成是 该函数创建出来的对象的类.
        // //
        // // console.log(Person.prototype);  // {xxxxx}
        // //
        // // 在类中定义的函数, 叫方法, 该类的对象都可以执行.
        // //
        // // 类里面多了个新功能
        // Person.prototype.chi = function(){
        //     console.log("我要吃东西")
        // }
        //
        // var p2 = new Person("林志玲", 18, "沙河");
        // p2.chi = function(){
        //     console.log("吃你妹");
        // }
        // // 我们发现. 可以正常运行. 为什么呢?
        // // 原因是. 我们把Person.prototype当成类来看. 这件事儿就成立了.
        // p2.chi();

        // prototype被称为原型. 原型的工作原理是什么?
        // 前端的对象. 是如何查找方法的.
        // 在前端. 所有的对象在被创建的时候. 都会有一个默认的属性. 该属性叫__proto__ 表示该对象的原型对象.
        // 默认的原型对象指向的是 构造函数的原型

        // 前端js的方法的查找路径(MRO)
        // 在执行`对象.方法()`的时候. 先去找`对象`中是否含有该`方法`, 如果有, 就执行.
        // 如果没有, 就会自动查找 `对象.__proto__`, 如果`对象.__proto__`中找到了该方法就执行.
        // 如果没有, 就会自动查找 `对象.__proto__.__proto__`, 如果有, 就执行,
        // 如果没有, 就会自动查找 `对象.__proto__.__proto__.__proto__`, 如果有, 就执行,
        // 直到找到object. 在js中, 只有object的__proto__是null, 所有的原型对象的默认原型对象是object
        // 此时程序报错....

        // 上述逻辑叫: 原型链....

        // object 里面的东西. 在js所有的对象中都可以执行...

        // console.log(p2.toString());

        // // 通过原型链, 我们能做什么
        // // 可以实现继承关系
        //
        function Fu(){}
        function Zi(){}

        // console.log(Zi.prototype.__proto__);
        // Zi.prototype.__proto__  = Fu.prototype; // 改变了原型链的指向
        Object.setPrototypeOf(Zi.prototype, Fu.prototype); // 和上面一样

        // 在查找方法的时候. 首先会查找Zi的对象, 对象里面没有会找Zi.prototype/对象.__proto__
        // 如果没有怎么办?
        // 找 对象.__proto__.__proto__, 此时就是Fu.prototype
        // console.log(Zi.prototype.__proto__);

        Fu.prototype.fu_chi = function(){
            console.log("爹的吃");
        }

        var zi = new Zi();
        zi.fu_chi();

    </script>
</head>
<body>

</body>
</html>

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给我们的对象增加一些功能.

接下来. 聊几个重要的概念.

  1. 构造器

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

    function People(){  //这个东西就是构造器 constractor
        
    }
    
    var p = new People(); // 调用构造器
    p.constractor == People; // true
    
  2. 原型对象

    每一个js对象中. 都有一个隐藏属性__proto__指向该对象的原型对象. 在执行该对象的方法或者查找属性时. 首先, 对象自己是否存在该属性或者方法. 如果存在, 就执行自己的. 如果自己不存在. 就去找原型对象.

    function Friend(){
        this.chi = function(){
            console.log("我的朋友在吃");
        }
    }
    Friend.prototype.chi = function(){
        console.log("我的原型在吃")
    }
    
    f = new Friend();
    f.chi(); // 此时. 该对象中. 有chi这个方法.  同时, 它的原型对象上, 也有chi这个方法.
    // 运行结果:
    // 我的朋友在吃
    
  3. prototype__proto__有什么关系?

    在js中. 构造器的prototype属性和对象的__proto__是一个东西. 都是指向这个原型对象.

    f.__proto__ === Friend.prototype   // true
    
  4. 原型链

    这个比较绕了. 我们从前面的学习中, 了解到. 每个对象身体里. 都隐藏着__proto__也就是它的原型对象. 那么我们看哈, 原型对象也是对象啊, 那么也就是说. 原型对象也有__proto__属性.

    类似于.....这样:

    f.__proto__.__proto__
    

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

    image-20230429194754068

    此时. 又出现一堆看不懂的玩意. 这些玩意是什么? 这些其实是Object的原型.

    f.__proto__.__proto__ === Object.prototype
    

    所以, 我们在执行f.toString()的时候不会报错. 反而可以正常运行. 原因就在这里.

    执行过程: 先找f对象中是否有toString. 没有, 找它的原型对象.原型对象中没有, 继续找原型对象的原型对象. 直至你找到Object的原型为止. 如果还是没有. 就报错了.

    f.hahahahahahah()  // 报错. 
    

    综上, 原型链是js 方法查找的路径指示标.

  5. 我们用原型链能做什么?(每日一恶心)

    我们来看一段神奇的代码.

    (function(){debugger})();
    

    这样一段代码可以看到. 浏览器进入了debugger断点.

    那么这段代码的背后是什么呢? 注意. 在js代码执行时. 每一个function的对象都是通过Function()来创建的. 也就是说. 函数是Function()的对象.

    function fn(){}
    console.log(fn.__proto__.constructor); // ƒ 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 x = Function; // 保留原来的Function
      Function = function(arg){
          arg = arg.replace("debugger", "");
          return new x(arg);
      }
      
      var qiaofu_function_constructor = (function(){}).__proto__.constructor;
      (function(){}).__proto__.constructor = function(arg){
          console.log("我爱你大大");
          if(arg ==='debugger'){
              return function(){}
          } else {
              return new qiaofu_function_constructor(arg);
          }
      }
      

    image-20230429194816913

image-20230429194835928

关于Function

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        // var s = [];
        // console.log(s.__proto__ === Array.prototype);
        // console.log("".__proto__ === String.prototype);

        function fn(){

        }

        // 每个函数都有prototype => 给该函数的对象使用的.   即对象的__proto__==函数的prototype  但是函数也有__proto__
        var m = new fn();
        console.log(m.__proto__ === fn.prototype);

        // fn.qiao = "樵夫";
        // console.log(fn.qiao);
        // 上述逻辑证明函数也有对象的一面

        // 函数比较特殊.
        // 1. 函数是有prototype属性. 是给它的对象使用的
        // 2. 函数是有__proto__属性的. 它是把函数当成对象来看的.

        // console.log(fn.__proto__ === Function.prototype);
        // // 前端所有的函数的原型是Function
        //
        // Function.prototype.chi = function(){
        //     console.log("大Function也会吃");
        // }
        // fn.chi(); //会不会报错....
        //
        // fn.chi.chi(); //  ???
        // fn.chi.chi.chi(); //
        // fn.chi.chi.chi.chi.chi();

        // Function决定了函数对象能执行哪些东西.
        // 如果改变了Function.prototype 中的内容, 那么整个js中所有函数的执行逻辑都将改变


        // 这个函数, Function的一个对象.
        function fn(){}

        console.log(fn.constructor === Function); // 能不能点出来?  报不报错..
        console.log(fn.constructor === Function.prototype.constructor); // 能不能点出来?  报不报错..
        console.log(fn.__proto__.constructor === Function.prototype.constructor); // 能不能点出来?  报不报错..


        // 对象.xxxx  永远都是先找对象自身. 自身没有就去找__proto__

        // 总结:
        // 1. 所有函数都是Function的对象
        // 2. 所有函数.constructor. 通过__proto__可以找到Function.prototype.constructor

        // 3. 可以使用Function 来构建一个函数

        // /**
        //  * function func(){
        //  *     console.log('上帝');
        //  * }
        //  */
        // var func = new Function("console.log('上帝');");
        //
        // func(); // 函数() 运行该函数

        // var gn = new Function.prototype.constructor("console.log('上帝');");// Function
        // gn();

        // var mn = (function(){}).__proto__.constructor("console.log('上帝');");
        // mn();

        // // 默认就是Function
        // // 函数对象.__proto__.constructor
        // // 通过该方案可以拦截: 函数.constructor
        Function.prototype.constructor = function(){
            console.log("我叫周润发");
            return "麻瓜";
        }
        // var en = (function(){}).constructor("console.log('上帝');");
        // console.log(en); //

        Function = function(){
            console.log("我叫周润发222222");
            return "大麻瓜";
        }
        var enen = Function("console.log('上帝');");
        console.log(enen);


        // 777777777     9999




    </script>
</head>
<body>

</body>
</html>

无限debugger

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="jjjj.js"></script>
    <script>
        txsdefwsw();
    </script>
</head>
<body>
    这里是天堂.
</body>
</html>
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 = 'debugger';
        !function c(r) { // r = 0
            if (1 !== ("" + r / r).length || 0 === r){
                // 在r = 0的时候. 这里是真. 会执行下面的代码
                // Function.prototype.constructor(a) => 构建了一个函数
                // function(){debugger}()  debugger

                // 修改Function.prototype.constructor
                (function (){}.__proto__.constructor('debugger')()); // 运行debugger, Function.prototype.constructor(a)()
                Function(a)();//  => 修改Function
            }
            // console.log(r);
            c(++r)  // 递归死循环. 内存溢出...会报错.
        }(0)
    } catch (a) { // 报错就执行这里的catch
        setTimeout(txsdefwsw, 100); // 0.1秒又重新启动该函数
    }
}

/***
 *
 * 无限debugger:
 * 1. 右键-> never pause here
 * 2. 找到入口 ->  置空 setTimeout,或者入口函数
 *
 * 3. hook方案. 半成品.下节课继续聊
 * Function.prototype.constructor = function(body){
 *     if ("debugger".indexOf(body) != -1){
 *         return function(){}
 *     }
 * }
 */


3.5 神奇的window

window对象是一个很神奇的东西. 你可以把这东西理解成javascript的全局. 如果我们默认不用任何东西访问一个标识符. 那么默认认为是在用window对象.

例如:

eval === window.eval    // true
setInterval === window.setInterval  // true
var a = 10; 
a === window.a  // true
function fn(){}
fn === window.fn  // true

window.mm = "爱你"

console.log(mm); //"爱你"

综上, 我们可以得出一个结论. 全局变量可以用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("吃了么");

window是整个浏览器的全局作用域.

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, ["苞米茬子", "大饼子"]);
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>

        function Person(name){
            this.name = name;
        }

        Person.prototype.shangtian = function(){
            console.log(this.name, "要上天");
        }


        function rudi(){
            // this是谁??? 如果this没有指向的话. 就是window
            console.log("谁呀?", this.qiao, "要入地");
        }

        // var p = new Person("我来也");
        // p.rudi();

        rudi(); // 相当于window.rudi();

        // function fn(){
        //     // 默认情况下. this是window
        //     function gn(){
        //         console.log(this);
        //     }
        //     gn(); // 默认执行一个函数. this就是window
        // }
        //
        // fn(); // 此时this是window

        // 默认执行一个函数. this就是window
        // 例外.
        //      在`对象.方法()`, 此时, 方法中的this是对象. 这个验证过的....
        //      函数可以执行`call`和`apply`的时候. 可以指定this对象.

        // // 关于call 和 apply
        // function Person(qiao){
        //     this.qiao = qiao;
        // }
        //
        // var p = new Person("樵夫");
        //
        // function fn(a, b){
        //     // this? 对象p
        //     console.log(this.qiao, "在上天");
        //     console.log(a + b);
        // }
        //
        // // fn(p); // 内部的this是
        // // 在js中. 所有的函数都有一个call的方法, 可以帮你完成对函数的调用
        // // call的第一个参数表示的就是 给函数内部的this传递的对象.
        // // call的从第二个参数开始到结束, 表示的是给`目标函数`传递的参数.
        // // fn()
        // fn.call(p, 33,22); // 用对象p去执行fn函数.
        //
        // // // 某个函数共享给多个对象的时候
        // // //
        // // function Person(){
        // // }
        // //
        // // function Dog(){
        // //
        // // }
        // // function Cat(){
        // //
        // // }
        // //
        // // function chi(){
        // //     console.log(this.name, "在吃东西");
        // // }
        // //
        // // // VUE, REACT
        // // // 框架设计里
        // // chi.call(new Person());
        // // chi.call(new Dog());
        // // chi.call(new Cat());
        // // 唱歌.call(汪峰); // 汪峰.唱歌()
        // // 唱歌.call(王力宏)
        //
        // // apply和call是一样的.
        // // 区别: apply在给`目标函数`传递参数的时候. 需要传递的是(类似)列表.
        // fn.apply(p, [33, 22]);
        //
        // // call, apply都是执行`某个函数`


        // console.log(String.prototype.charAt.call("樵夫", 1));
        // // "樵夫".charAt(1);
        // console.log("樵夫".charAt(1));
        //
        // var arr = [];
        //
        // // A  -> 在RS里面就有大量的这种东西..
        // var mmm = Array["prototype"]['push'];
        // mmm['call'](arr, "哈哈");
        //
        // // B
        // arr['push']("哈哈");
        //
        // console.log(arr); // 数组 ['哈哈']
        //
        // // charAt['call']('xxxxxxxxx', 数字);
        // // 'xxxxxxxxx'.charAt(数字)
        //
        // // 777777  9999

        //
        function Person(name){
            this.name = name;
            this.chi = function(){
                console.log(this.name, "在吃東西");
            }
        }

        var p1 = new Person("胡辣汤");
        var p2 = new Person("疙瘩汤");

        p1.chi.call(p2); // 疙瘩汤 在吃東西
        p2.chi.call(p1); // 胡辣汤 在吃東西


    </script>
</head>
<body>

</body>
</html>

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) => {}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
  <script>
      // var mm = function(a, b){return a + b}

      // es6可以简化成箭头函数
      // var mm = (a, b) => {
      //     return a + b;
      // }
      //
      // console.log(mm(1,2)); // 在使用上没有区别

      // 注意事项:
      // 1. 箭头函数如果只有1个参数, 小括号可以省略
      // 2. this逻辑和普通的function不一样.(我们作为爬虫工程师, 这里不用管)

      // var fn = () => {
      //     console.log("这是一个函数, 参数是", a);
      // }
      //
      // fn(123);

      // var username = "qiaofu";
      // var age = 18;
      // var password = 123456;
      //
      // var chi = function(){
      //     console.log("吃饭睡觉, 打豆豆")
      // }
      // var person = {
      //     // username: username,
      //     // age: age,
      //     // password: password,
      //     // chi: chi
      //     // 在es6中, 简化了上述逻辑
      //     // 如果属性名和某一个变量名完全一样, 并且, 你需要用该变量来表示该属性
      //     username, // username: username,
      //     age,
      //     password,
      //     chi,
      //     he: function(){
      //         console.log("我要喝东西")
      //     },
      //     // 上面这个he也可以进行简化
      //     la(){
      //         console.log("我要ladongxi")
      //     }
      //     // la: function(){}
      // }
      // person.la();

       var obj = {
            "name": "汪峰",
            age: 18,
            wife:{
                name: "子怡",
                age: 28
            },
            sing(){
                console.log("汪峰会唱歌")
            }
       }
       // 需求如下, 需要从对象中获取某个属性. 刚刚好, 该属性需要设置成变量, 名字一样
       // var age = obj.age;
       // var wife = obj.wife;
       // var name = obj.name;
       // 前端的解构
      var {age, wife, name, sing} = obj;

       // 拆分出wife中的name , 赋值给sb_name变量
       var {name:sb_name} = wife;

      // console.log(age);
      // console.log(wife);
      // console.log(name);
      // sing();

      console.log(sb_name);

  </script>
</head>
<body>

</body>
</html>

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,3
    let s = (1, 2, 3); // 整体进行赋值的时候. 取的是最后一个值 3
    console.log(s);
    // 注意. 这个括号可以在返回值时省略
    var a;
    return a=10,
    a++,
    a+=100,
    {name:"alex", "a":a};
}
let r = s();
console.log(r);  // {name: 'alex', a: 111}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        // // 从左到右...一次执行逗号之间的内容. 表达式的结果是最后一项内容.
        // var a;
        // // 逗号运算
        // a = (1,2,3);
        // console.log(a);  // 3
        //
        // a = (console.log(1), console.log(2), console.log(3), 4);
        // console.log(a);

        function fn(){
            var a = 10;
            var b = 20;
            a+=3;      // a=13
            a--; // a=12
            a++; // a=13
            a+=b;    // a=a+b=13+20=33
            return a;
        }

        console.log(fn());  // 33 
    </script>
</head>
<body>

</body>
</html>

3.10 三元运算符(折磨)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        // var a = 100;
        // var b = 20;
        // var c = a > b ? a: b;
        // console.log(c);
        // // js中的三元运算
        // // 条件? 成立: 不成立
        // 平坦流..switch   if
        // 三元运算可以做平坦流...
        // 阿里的平坦流: switch+if+三元+递归...

        let a = 10;
        let b = 20;
        let c = 5;
        let d = 17;

        let e;
        let m;

        //
        e = (e = a > 3 ? b : c, m = e < b++ ? c-- : a = 3 > b % d ? 27: 37, m++);
        // //
        // e = a > 3 ? b : c; // e = 20
        // //      b = 21 , m 和 a都是37
        // m = e < b++ ? c-- : a = 3 > b % d ? 27: 37;      // 20<20,假,得a = 3 > b % d ? 27: 37 ;    3>4,假,得a=37   注意:b++先赋值,再++
        // e = m++; // e 37  m   38    a 37

        console.log(a); //   37
        console.log(b); //   21
        console.log(d); //   17
        //
        console.log(e); //   37
        console.log(c); //   5
        console.log(m); //  38

    </script>
</head>
<body>

</body>
</html>

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

// hook代码, 这样可以解决掉Function() 这种无限debugger
var Function_ = Function;
Function = function(){
    // if (code.indexOf("debugger") === -1){
    //     // 没有debugger代码
    //     // 放行.
    //     return Function_.apply(this, arguments);
    // } else {
    //     return function(){}; // 返回一个新的函数. 无限debugger被干掉
    // }

    // 最后一个参数(最后一个参数才是函数体)
    var last_arg = arguments[arguments.length-1];
    last_arg = last_arg.replaceAll("debugger", "");

    var arr = [];
    for(var i = 0 ; i < arguments.length-1; i++){
        arr.push(arguments[i]);
    }
    arr.push(last_arg);
    console.log(arr);
    return Function_.apply(this, arr);
}



// 此时需要hook Function.prototype.constructor
Function.prototype.constructor_ = Function.prototype.constructor;

Function.prototype.constructor = function(){
    // 最后一个参数(最后一个参数才是函数体)
    var last_arg = arguments[arguments.length-1];
    last_arg = last_arg.replaceAll("debugger", "");

    var arr = [];
    for(var i = 0 ; i < arguments.length-1; i++){
        arr.push(arguments[i]);
    }
    arr.push(last_arg);
    // 原函数调用
    return Function.prototype.constructor_.apply(this, arr);
}

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

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

剩下的咱就不再赘述了. 在逆向时, 常用的主要有: hook eval 、hook Function 、hook JSON.stringify、JSON.parse 、setInterval, setTimeout, hook cookie

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        function fn(a, b, c){
            console.log("我叫樵夫", a, b, c);
        }
    </script>

    <script>
        // 植入我的代码, 负责在执行目标函数的时候. 打断点. 找到函数的对应的位置.
        // 可以使用hook的方案来完成
        // 在中间插一杠子...目的是. 在执行fn()函数的时候. 换成我这个.

        // 记录原函数
        var _gn = fn; // 从这里开始, 以后, gn就是原来的fn

        // 核心思路就是, 重新定义该函数
        fn = function(){
            // arguments // 所有的参数

            console.log("我拦截到你了");
            // debugger;
            // 在这里. 得知道原来的函数是谁呀....
            // _gn(arguments); // 执行原函数

            // 如果你能确定参数是什么. 就可以正常调用.
            // 如果参数不能确定. 对原函数的调用. 只能用apply
            _gn.apply(this, arguments); // 这里用apply是最合适的.
            console.log("目标函数执行完毕");
        }
        // 上面的逻辑就叫做hook.

    </script>

    <script>
        // 它是人家网站写好的代码
        // 这里在调用函数
        // 分析网站的时候. 发现. 这里是一个关键的地方.
        fn(11, 22, 33);
        fn(11, 22, 33, 44, 55, 66);
        fn(11, 22, 33, 44, 55);
        fn();
        fn(11,22, 11, 22, 2, 1, 1, 1, 1, 1, 11);
    </script>

</head>
<body>

</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        // 处理无限debugger

        Function("debugger;")()
             // 拦截Function
        setInterval(function(){
            new Function("arg0", "debugger;eval(arg0);console.log('正常的代码'); console.log('args', arg0, arg1);")("debugger");
        }, 100);
        // hook代码, 这样可以解决掉Function() 这种无限debugger
        var Function_ = Function;
        Function = function(){
            // if (code.indexOf("debugger") === -1){
            //     // 没有debugger代码
            //     // 放行.
            //     return Function_.apply(this, arguments);
            // } else {
            //     return function(){}; // 返回一个新的函数. 无限debugger被干掉
            // }

            // 最后一个参数(最后一个参数才是函数体)
            var last_arg = arguments[arguments.length-1];
            last_arg = last_arg.replaceAll("debugger", "");

            var arr = [];
            for(var i = 0 ; i < arguments.length-1; i++){
                arr.push(arguments[i]);
            }
            arr.push(last_arg);
            console.log(arr);
            return Function_.apply(this, arr);
        }



        // // (function(){}).constructor("debugger;")()
        // setInterval(function(){
        //     (function(){}).constructor("debugger;")();
        // }, 100);

        // // 这样写的无限debugger. 用户就完了.
        // // 网站不论多恶心. 最终一定要保证用户可以正常访问
        // while (true){
        //     (function(){}).constructor("debugger;")();
        // }
        //
        // // 此时需要hook Function.prototype.constructor
        // Function.prototype.constructor_ = Function.prototype.constructor;
        //
        // Function.prototype.constructor = function(){
        //     // 最后一个参数(最后一个参数才是函数体)
        //     var last_arg = arguments[arguments.length-1];
        //     last_arg = last_arg.replaceAll("debugger", "");
        //
        //     var arr = [];
        //     for(var i = 0 ; i < arguments.length-1; i++){
        //         arr.push(arguments[i]);
        //     }
        //     arr.push(last_arg);
        //     // 原函数调用
        //     return Function.prototype.constructor_.apply(this, arr);
        // }


    </script>
</head>
<body>
    我叫周润发
</body>
</html>

关于JSON

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        var data = {
            username: "qiaofu",
            password: "123456",
            verify_code: "acef"
        }

        // 所有的数据加密都要围绕着字符串展开.
        // 在js中. 可以使用JSON.stringify() 把一个对象, 转化成(json)字符串
        console.log(data); // 对象
        // 很有用...
        console.log(JSON.stringify(data)); // json字符串

        var s = '{"code":9,"msg":"请填写正确图形码","data":null,"operation_date":"2023-04-12 22:39:16"}'
        // 把(json)字符串转化成js的对象
        var d = JSON.parse(s);
        console.log(d['msg']);

        // JSON <=> json模块

        // 1. 通过某个事件触发一个函数的执行 =>   点击按钮
        // 2. 在函数中可以收集到一些数据(object) =>  用户输入的周杰伦
        // 3. 发送数据到服务器(发请求) =>
        // 4. 服务器收集  => 准备好`周杰伦的信息`
        // 5. 服务器返回数据 => 接收到`周杰伦的信息`
        // 6. js处理成html, 然后展示给用户.

        // 准备数据(普通对象) => JSON.stringify(普通对象) => 加密(请求参数, 请求头) => 发请求
        // 响应 => 解密 => JSON.parse(解密好的字符串) => 获取数据

        // JSON.stringify_ = JSON.stringify;
        // JSON.stringify = function(){
        //     debugger; // 断下来. 为了去找它前面或者后面做了什么
        //     return JSON.stringify_.apply(this, arguments);
        // }

        JSON.parse_ = JSON.parse;
        JSON.parse = function(){
            debugger; // 断下来. 为了去找它前面或者后面做了什么
            return JSON.parse_.apply(this, arguments);
        }
        // 混淆是为了让你看不明白.
        // 混淆绝对不能改变程序逻辑.
    </script>
</head>
<body>

</body>
</html>

关于cookie

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
      // 在js中可以直接document.cookie给cookie增加新的数据

      // 对cookie进行hook
      // 半成品
      (function(){
          // 弄个局部变量
          var val = document.cookie; // 拿到当前cookie的信息
          Object.defineProperty(document,"cookie", {
              // get, 在从document.cookie中获取数据的时候. 自动被执行
              get(){  // function get(){}
                  // debugger;
                  return val;
              },
              // set, 在向document.cookie中设置数据的时候. 自动被执行
              set(data){
                  debugger;
                  val += data+"; ";
              }
          })
      })();

        // 不要求会写...建议捋捋逻辑...
        (function(){
          // 弄个局部变量
          var val = {}; // 拿到当前cookie的信息

          var tmp = document.cookie;
          var arr = tmp.split("; ");
          for (var i = 0 ; i < arr.length; i++ ){
              var cook = arr[i].split("=")
              var cook_0 = cook[0];
              var cook_1 = cook[1];

              val[cook_0] = cook_1;
          }
          Object.defineProperty(document, "cookie", {
              // get, 在从document.cookie中获取数据的时候. 自动被执行
              get(){  // function get(){}
                  debugger;
                  let s = [];
                  for(let k in val){
                      let v = val[k];
                      s.push(`${k}=${v}`)
                  }
                  return s.join("; ");
              },
              // set, 在向document.cookie中设置数据的时候. 自动被执行
              set(data){
                  debugger;
                  let d = data.substring(0, data.indexOf(";")).split("=");
                  let k = d[0];
                  let v = d[1];
                  val[k] = v;
              }
          })
      })();
    </script>
</head>
<body>

</body>
</html>

3.12 localStorage和sessionStorage

本地存储. 存储在浏览器端的数据. 可以理解成一个小型的非关系型数据库.

存储, 获取. 删除.

这俩玩意使用上是一样的. 区别在于. 一个是永久存储一个是临时存储.

localStorage 永久存储

sessionStorage 临时存储, 浏览器关闭后. 数据就没了.

document.cookie也是本地存储.

cookie的本质是一个超大号字符串. 有失效时间的.

都继承自Storage常见的操作:

setItem(key, value);  // 设置key = value, 如果是相同的key, 会把数据覆盖
removeItem(key); // 根据key删除数据
getItem(key); // 根据key来获取数据 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
      <script>
          // 存储数据的时候. 如果把浏览器关掉. 重新打开. 数据依然存在
          // window.localStorage.setItem('qiao', "樵夫很厉害!");
          // 存储数据的时候. 如果把浏览器关掉. 重新打开. 数据就不在了
          // sessionStorage.setItem("qiao", "樵夫很伟大");
          // localStorage.setItem("qiao","哈哈");

          // localStorage.removeItem("qiao");

          // 删除数据
          localStorage.removeItem("qiao");
          // 存储数据
          localStorage.setItem('qiao', "123");
          //获取数据
          localStorage.getItem("qiao");

      </script>


</head>
<body>

</body>
</html>

3.13 math数学逻辑

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        // 如果服务器可以接受随机值. ok. 我们就可以固定该值..
        // console.log(Math.random()); // 随机数.

        function a() { // 随机获得一个字符串. 该字符串长度是a
            // var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c = "";
            // for (d = 0; d < 16; d++) // 循环16次
            //     // random随机出来的玩意. (0,1) * 62  [0, 61]
            //     e = Math.random() * b.length,
            //     e = Math.floor(e), // 取整.
            //     c += b.charAt(e); // 从b里面获取某个字符  c: "Fx1SfAAGB"
            return "nB1WuA48FZPHuBVv" // 当函数内出现random这种东西的时候. 直接锁死数据即可.
        }

        console.log(a(16));
    </script>
</head>
<body>

</body>
</html>

3.14 关于promise

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        // 死亡回调.
        // 需求:  完成页面登陆功能
        // 1. 获取用户填写的...用户名, 密码, 验证码, 发送请求到服务器.
        // 2. 服务器返回登陆状态. 1. 登陆成功, 2. 登陆失败
        // 3. 登陆成功了. 页面跳转->拿到首页的内容进行渲染.
        // 4. 加载菜单
        // 5. 加载用户信息
        // 6. 加载统计信息

        // // 登陆成功了 ->  页面跳转  -> 加载菜单 -> 加载用户信息 -> 统计信息 -> 呈现给用户
        //
        // // 获取用户名. 密码. 验证码
        // username = "admin";
        // password = "123456";
        // verify_code = "abcd";
        // // 发请求到服务器. 来验证登陆是否成功
        // // 网络请求. 1. 有延迟. 2. 可能这辈子都回不来了...
        //
        // setTimeout(function(){ // 回调函数
        //     console.log("我叫樵夫");
        // }, 1000); // 这玩意的优点是. 不会让程序卡死在这.
        // // 在js中使用的是异步请求.(协程), 等到服务器返回了数据之后. 自动触发某个函数的运行
        //
        // console.log("123456");
        //
        // // 后面的代码将无法运行...
        // // 前端js是单线程....

        // 利用setTimeout来模拟上述逻辑

        var username = "admin";
        var password = "123456";
        var verify_code = "abcd";

        // console.log("1.我要开始发送请求了.....")
        // setTimeout(function(){
        //     // 这里接收到服务器返回的内容
        //     console.log("2.接收到服务器返回的内容....");
        //     var resp = "成功"; // 服务器返回的数据. 成功或者失败
        //     if (resp === "成功"){
        //         // 模拟....
        //         console.log("3. 恭喜你, 登陆成功了. 接下来要发送新的请求.跳转到新的页面");
        //         setTimeout(function(){
        //             console.log("4. 进入主页面.....");
        //             console.log("5. 开始加载菜单")
        //             setTimeout(function(){
        //                 console.log("6. 菜单加载完毕...");
        //                 setTimeout(function(){
        //                     console.log("7.加载用户信息成功");
        //                     setTimeout(function(){ // 等待服务器返回
        //                         console.log("8. 加载统计信息成功!");
        //                     }, 500);
        //                 }, 500);
        //             }, 1000)
        //         }, 1000)
        //     } else {
        //         console.log("登陆失败了. 请检查你的用户名和密码");
        //     }
        // }, 1000);
        // // 前面的一件事儿. 完成了之后. 才能开始后面的一件事儿...
        // // 这里的每一件事儿, 什么时候结束. 没人知道...

        // es6提供了promise解决了死亡回调的问题

        // 负责发请求
        function send(url){
            // Promise 保证.会给你一个结果
            // 世界任何事情就三个状态
            // 进行中...
            // 结果:
            //  resolve: 成了
            //  reject: 没成.
            //    resolve和reject对应的是两个函数....
            return new Promise(function(resolve, reject){
                // 发请求
                console.log("发请求", url);
                setTimeout(function(){
                    console.log("请求回来了");
                    // 我保证你的这件事儿. 成了. 成了之后. 你要干啥???
                    resp = "正确的响应结果";
                    // 如何把信息传递出去
                    if (true){ // 成功还是不成功
                        resolve(resp); // 成了
                    } else {
                        reject("没成."); // 没成
                    }
                }, 1000);
            });
        }

        // promise对象在执行resolve和reject之前本身是出于`运行中`状态的.
        // 得到一个promise对象之后. 会对应两个逻辑, 一个是这件事儿成了, 一个是这件事儿没成.
        //  then表示, 在promise对象执行结束之后. 得到结果了. 之后. 要干啥 .
        // then中的两个函数对应的就是resolve和reject
        // catch表示. 前面的操作中, 如果出现了错误. 处理异常的逻辑就是catch
        // 语法:
        // send("login").then(function(data){
        //     console.log("成了,,,,,接收到返回数据.", data);
        // }).catch(function(){
        //     console.log("这里是报错信息处理. 你能看到这条打印, 说明前面的逻辑中出现了错误. ");
        // });
        // then中的第二个函数可以没有...可以换成catch(), 也是一样的.. catch可以代替then中的第二个函数.


        // 能否解决死亡回调的问题.
        send("login").then(function(data){
            console.log("成了,,,,,接收到返回数据.", data);
            // 在promise的then中. 可以返回一个新的promise
            return send("页面跳转")
        }).then(function(){
            return send("加载菜单")
        }).then(function(){
            return send("加载用户信息")
        }).then(function(){
            console.log("用户信息加载完毕!!!!!!")
        }).catch(function(){ // 前面的promise任何一个出现了问题. 都会执行最后的这个catch()
            console.log("这里是报错信息处理. 你能看到这条打印, 说明前面的逻辑中出现了错误. ");
            console.log("服务器出错,请练习管理员.");
        });

        // promise().then(function(){
        //     return new Promise();
        // }).then(function(){
        //     return new Promise();
        // }).then(function(){})
        //
        // new Promise().then().then().then().then()

        // 注意点.
        // 1. Promise(function(a, b){
        //      a(); // ok
        //      b(); // 不ok
        // })
        // 2. then()里面可以有1个函数,也可以有2个函数.
        //      如果只有1个函数, 那么reject对应的就是catch
        //      如果有2个函数, 那么第二个函数对应的是reject
        // 3. 当你遇到了promise的时候. 怎么办?
        //  在promise后面可以.then(function(data){console.log("阿拉丁神灯:", data);});
        //  然后一定要释放断点. 等待运行完毕. 一定会执行你设置的then

    </script>
</head>
<body>

</body>
</html>

四. JS和HTML交互

在HTML中可以直接在标签上给出一些事件的触发. 例如, 页面上的一个按钮.

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

我们能够知道此时在页面中会产生一个按钮. 但是该按钮无论如何进行点击. 都不会触发任何事件. 但, 此时我要告诉你, 人家其实触发了. 只是你没处理而已. 在我们点击该按钮的时候. 浏览器其实收集到了点击事件. 但是由于我们没有给出任何发生了点击事件应该做什么的事情. 所以也就没有了反应. 我们可以通过onclick属性. 来给点击事件添加上具体要做什么

<input type='button' value="点我就爱你" onclick="fn()" />

看到了吧. 多了个onclick, 其含义是, 当发生点击事件时. 去执行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>

image-20230429194912064

有效果了. 发现了么. 至此, 我们成功的实现了. 从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(){
        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>

发现了么. 不经意间, 我们通过js可以改变html中的内容了.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        // 在窗口(html)中的内容加载完毕的时候
        window.onload = function(){
            // console.log("窗口加载完毕...");
            // 获取到按钮标签. 动态添加事件监听
            var btn = document.querySelector("#btn");
            //                    点击      干啥
            btn.addEventListener("click", function(){
                console.log("你居然敢点我");
            });
        }
    </script>

</head>
<body>
    <input type="button" id="btn" value="新button">

    <input type="text" id="myinp" onblur="gn()" onfocus="mn()" value="xxxx">
    <input type="button" onclick="fn()" value="点我呀">
</body>

<script>
    function fn(){
        // 在js脚本中如何获取到页面标签
        // document.getElement系列的操作
        // var inp = document.getElementById("myinp");
        // document.querySelector(); // 使用css选择器来获取页面元素
        // var inp = document.querySelector("#myinp"); // 获取第一个
        var inp = document.querySelectorAll("#myinp")[0]; // 获取所有符合该选择器的内容

        // 操纵它.
        // inp.value = "周润发";
        // 可以看到通过js代码可以动态改变html标签的显示效果.
        // 自由的获取某个属性中的数据
        console.log(inp.value); // 自由的获取文本框的内容  23
    }

    // blur: 失去焦点的时候自动触发
    function gn(){
        console.log("我是gn");
    }
    // focus: 获取焦点的时候自动触发
    function mn(){
        console.log("我是mn");
    }

</script>
</html>

表单验证

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        // 在页面加载的时候
        window.onload = function(){
            document.getElementById('btn').addEventListener("click", function(){

                //<标签><a>123</a></标签>
                // 清空提示信息
                //  innerText处理的是纯文本
                //  innerHTML处理的是HTML代码片段
                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;
                }

                // js中如何使用正则
                // ^开始
                // $结束
                // var obj = /^\w+@\w+\.\w+$/;
                var obj = new RegExp('\\w+@\\w+\\.\\w+$');

                // re.search(r"", "", re.S); // re.S表示让.可以匹配\n

                // 验证测试
                if(!obj.test(username)){ // 是否符合该规则
                    document.getElementById('username_info').innerText = "有户名必须要填写邮箱格式";
                    flag = false;
                }


                if (flag){
                    alert("可以提交数据了")
                }
            })
        }
    </script>
</head>
<body>

    <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>

</body>
</html>

jQuery和Ajax

jQuery是一个曾经火遍大江南北的一个Javascript的第三方库. jQuery的理念: write less do more. 其含义就是让前端程序员从繁琐的js代码中解脱出来. 我们来看看是否真的能解脱出来.

python => 基础语法 => 系统模块 => 第三方的库

关于jQuery的版本: 这里有必要说一下, jQuery一共提出过3个大版本. 分别是1.x, 2.x和3.x. 这里注意, 虽然目前最新的是3.x. 但各个公司都不约而同的选择了1.x. 说明jQuery1.x在编程界的地位是非常非常高的. 而且从其执行效率, 代码兼容性以及代码可靠性上讲. 1.x确实做到了极致. 所以, 我们学习的版本自然也是1.x了. 我们选择和腾讯课堂同一个版本. 1.9.1

jQuery的下载, 推荐直接去cdn下载即可. 他本质就是一个js文件. 去哪儿都一样.

字节cdn: https://cdn.bytedance.com/

jquery: https://cdn.bytedance.com/?query=jquery&version=1.9.1

只需要把上面这个jquery下载的网址复制到浏览器上, 然后保存(ctrl+s)成js文件就可以了.

一. jQuery初使用

我们用jQuery来完成一个按钮的基本点击效果. 当然, 得和传统的js对比一下

先准备好html. 页面结构. 这里复制粘贴就好

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .div-out{
            width: 400px;
            height: content-box;
            margin: 0 auto;
            padding: 0;

        }
        .mydiv{
            width: 400px;
            height: 200px;
            line-height: 200px;
            text-align: center;
            background: pink;
            margin:0;
            padding: 0;
            font-size: 18px;
        }
        .btn{
            width: 400px;
            height: 50px;
            padding: 0;
            border: none;
            margin: 0;
            box-sizing: content-box;
            font-size: 18px;
            color: #000;

            background: #999999;
        }
        .btn:hover{
            cursor: pointer;
            color: pink;
        }
        .btn:active{
            color: #666;
            background-color: #eee;
        }
    </style>
</head>
<body>
    <div class="div-out">
        <input type="button" class="btn" value="我是按钮. 你怕不怕">
        <div class="mydiv">我怕死了...</div>
    </div>
</body>
</html>

需求: 点击按钮. 更改mydiv中的内容.

// 传统js
// 版本1
window.addEventListener('load', function(){
    document.getElementsByClassName("btn")[0].addEventListener('click', function(){
        document.getElementsByClassName('mydiv')[0].innerText = "真的不怕啊";
    });
})

// 版本2
window.onload = function(){
    document.querySelector(".btn").onclick = function(){
        document.querySelector('.mydiv').innerText = "我好啪啪啊";
    };
}
// jQuery
$(function(){  // $(document).ready(function(){
    $(".btn").click(function(){
        $(".mydiv").text('我要上天');
    })
})

除了$外, 其他东西貌似都挺容易理解的. 而且代码简洁. 异常舒爽.

$是什么鬼, 在jQuery中, $可以认为是jQuery最明显的一个标志了. $()可以把一个普通的js对象转化成jQuery对象. 并且在jquery中 $的含义就是jQuery.

课上笔记

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="jquery.js"></script>

    <script>
        // 猜测jquery的内部大概是这样的:
        // window.jQuery = xxxxx
        // window .$= window.jQuery
        // 等待html加载完成
        // window.onload = function(){
        //     console.log("hello world");
        // }
        //
        //
        // // 等待html加载完成
        // $(function(){
        //     console.log("hello jquery");
        // })
        // // $ === jQuery
        // console.log($ === jQuery);
        // $() jquery的选择器, css选择器,
        // document.querySelector() => $()
        // $()  返回的是jquery对象. 进而可以使用jquery提供的各种功能


        // 事件绑定
        $(function(){
            // // 原生js代码
            // document.querySelector("#btn").addEventListener("click", function(){
            //     var v = document.querySelector("#myinput").value;
            //     alert(v);
            // });

            // jquery代码
            $("#btn").click(function(){
                // var v = $("#myinput").val(); // 拿到某个文本框的值, 获取value属性的值
                // alert(v);
                // $("#myinput").val("周润发是我兄弟");

                // 操纵标签上的属性(大概率是自定义)
                // $("#myinput").attr("jay", "周杰伦"); // 设置属性
                // var v = $("#myinput").attr("jay"); // 获取属性
                // console.log(v);

                // $(".bug").text("你好呀");  // 向某个标签插入文本 => innerText
                // $(".bug").html('<a href="http://www.baidu.com">去百度</a>');  // ==> innerHTML

                // filter 在被选择的内容之间, 做进一步的查找
                // console.log($("div").filter(".bug").text());
                // find 在被选择的内容 里面(节点的内部), 进一步的查找
                // console.log($("div").find(".bug").text());
            });
        })
    </script>
    <style>
        .bug{width: 100px; height:100px; background: pink;}
    </style>
</head>
<body>
<input type="button" id="btn" value="点我呀">
<!--<input type="text" id="myinput" jay="马化腾">-->
<div class="bug">
    <span> 哈哈 </span>
</div>
<div>
    <span class="bug">呵呵</span>
</div>

</body>
</html>

二. jQuery选择器

jQuery的逻辑和css选择器的逻辑是一样的.

// 语法:
$(选择器)

可以使用jQuery选择器快速的对页面结构进行操作.

案例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="jquery.min.js"></script>
    <script>

        $(function(){
            $(".btn").on('click', function(){
                $(".info").text("");
                let username = $("#username").val();
                let password = $("#password").val();
                let gender = $("input:radio[name='gender']:checked").val();  // input标签中radio 并且name是gender的. 并且被选择的.
                let city = $("#city").val();

                let flag = true;
                if(!username){
                    $("#username_info").text('用户名不能为空!');
                    flag = false;
                }

                if(!password){
                    $("#password_info").text('密码不能为空!');
                    flag = false;
                }

                if(!gender){
                    $("#gender_info").text('请选择性别!');
                    flag = false;
                }

                if(!city){
                    $("#city_info").text('请选择城市!');
                    flag = false;
                }

                if(flag){
                    $("#login_form").submit();
                } else {
                    return;
                }
            })
        })

    </script>
</head>
<body>
    <form id="login_form">
        <label for="username">用户名: </label><input type="text" id="username" name="username"><span class="info" id="username_info"></span><br/>
        <label for="password">密码: </label><input type="password" id="password" name="password"><span class="info" id="password_info"></span><br/>
        <label>性别: </label>
            <input type="radio" id="gender_men" name="gender" value="men"><label for="gender_men">男</label>
            <input type="radio" id="gender_women" name="gender" value="women"><label for="gender_women">女</label>
            <span class="info" id="gender_info"></span>
        <br/>

        <label for="city">城市: </label>
            <select id="city" name="city">
                <option value="">请选择</option>
                <option value="bj">北京</option>
                <option value="sh">上海</option>
                <option value="gz">广州</option>
                <option value="sz">深圳</option>
            </select>
            <span class="info" id="city_info"></span>
        <br/>

        <input type="button" class="btn" value="登录">
    </form>
</body>
</html>

三. 属性控制

属性相关的控制主要有以下几个功能

val()  => 处理value属性
text()  => 处理innerText
html()  => 处理innerHTML

attr()  => 处理所有属性的
css()  => 处理所有css样式的

一起看吧. 简单的很

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="jquery.min.js"></script>
    <script>
        $(function(){
            // 如果给参数, 就是设置值, 如果没参数, 就是获取值
            $("#text_1").val("我是谁?");
            console.log($("#text_1").val());
            // attr() 如果给一个参数. 就是获取值. 如果给2个参数就是设置属性值
            $("#text_2").attr("type", "button").val("god");
            console.log($("#text_2").attr("type"));
            // css() 如果一个参数, 取值, 如果2个参数, 设置值
            $("#mydiv").css("background", "#eee");
            console.log($("#mydiv").css("background"))
            
            // text()和html()很像. 
            console.log($("#mydiv_2").text())  // 拿到纯文本
            console.log($("#mydiv_2").html())  // 拿到html标签
            // 如果传参. 则text(xxx)把xxx作为文本放入标签内.  
            //          则html(xxx)把xxx作为html放入标签.
        })
    </script>
</head>
<body>
    <input type="text" name="" id="text_1">
    <input type="text" name="" id="text_2">
    <div id="mydiv" style="width: 200px;height:100px; background:pink;"></div>
    <div id="mydiv_2" >
        <span>哈哈</span>
        <span>呵呵</span>
    </div>
</body>
</html>

四. 遍历器

如果jquery一次性选择了很多元素节点. 而我们又希望能拿到每一个元素中的相关信息. 此时可以考虑用jQuery的遍历器来完成对元素的循环遍历

例,

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="jquery.min.js"></script>
    <script>
        $(function(){
            // 此时确实能拿到所有li的内容, 但是我想一个一个拿. 分别处理
            // li_text = $('li').text();
            // console.log(li_text);

            // 分别处理
            $('li').each(function(i, el){
                console.log(i);
                //console.log(el.text()); // 注意, 遍历器拿到的东西是JS的DOM对象, 并不是jquery对象
                console.log($(el).text()); // 此时el和this是同一个东西.
                console.log($(this).text());
            })
        })
    </script>
</head>
<body>
    <ul>
        <li>吃饭</li>
        <li>睡觉</li>
        <li>打豆豆</li>
        <li>吹牛</li>
    </ul>
</body>
</html>

五. 发送ajax请求(扩展, 延伸)

首先, 我们用Flask创建一个后台服务器(自己做网站了哈)

目录结构:

image-20210831163129374

服务端

from flask import Flask, render_template, request  # pip install Flask


app = Flask(__name__)


@app.route("/")
def index():
    # 跳转到首页
    print("你曾经来过服务器")
    name = "alex"
    # 数据是在这里渲染后, 返回个客户端的html
    return render_template("index.html", name=name)  


if __name__ == '__main__':
    app.run()

html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="/static/js/jquery.js"></script>
</head>
<body>
    我就是一个传统的html页面, 我的名字是{{name}}
</body>

5.1 发送get请求

接下来. 我们使用jquery来发送ajax.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="/static/js/jquery.js"></script>
    <script>
        function setCookie(name, value) {
            let Days = 30;
            let exp = new Date();
            exp.setTime(exp.getTime() + Days * 24 * 60 * 60 * 1000);
            document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString();
        }
        $(function(){
            // 可以在js任意位置设置cookie信息
            setCookie("name", "i-have-a-dream")
            $(".get_btn").click(function(){
                $.ajax({
                    url:"/ajax_get", // 服务器地址: 域名+url
                    method:'get',  // 发送get请求
                    headers:{  // 添加请求头信息
                        "token":"mememmememe",
                    },
                    data:{   // 传递参数
                        name:'alex',
                        _: new Date().getTime()
                    },
                    contentType:'application/json;charset=utf8',  
                    beforeSend: function(req){  // 也可以这样添加请求头信息
                        req.setRequestHeader("tokken", "i-can-do-it-haha");
                    },
                    success: function(back){  // 请求成功后. 返回数据了. 要做什么?
                        console.log(back);
                    }
                });
            })
        })
    </script>
</head>
<body>
    我就是一个传统的html页面, 我的名字是{{name}}
    <br/>
    <input type="button" class="get_btn" value="点我发送get_请求">
    <hr/>
    <a href="javascript:void(0);" class="post_btn" >点我发送post_请求</a>
</body>
</html>

服务器处理ajax_get请求

@app.route("/ajax_get")
def ajax_get_req():
    # 接收cookie中的信息
    n = request.cookies.get('name')
    if not n:
        return "没有cookie就不要来了."
    # 接收header中的信息
    token = request.headers.get('token')
    if not token:
        return "没token还想来?"

    # Flask接收get请求的参数
    name = request.args.get('name')
    _ = request.args.get('_')
    if name and _:
        # 返回json
        return {"name":'alex', "id": 10086, "isMen": True}
    else:
        return "回家去吧"

5.2 发送post请求(json)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="/static/js/jquery.js"></script>
    <style>
        #mask{
            position:fixed;
            top:0;
            left:0;
            right:0;
            bottom: 0;
            background-color: rgba(0,0,0, .3);
            color: #fff;
            font-size:30px;
            text-align: center;
            padding-top:300px;
            display:none;
        }

    </style>
    <script>
        $(function(){
            $(".post_btn").click(function(){
                $('#mask').css("display","block");
                $("#data_tbody").remove();
                $.ajax({
                    url:'/ajax_post',
                    method:'post',
                    data: JSON.stringify({
                        name:'alex',
                        id:'10086'
                    }),
                    headers: {  // 发送json数据. 要换这个头, 否则服务器收不到数据
                        "Content-Type": "application/json;charset=utf-8"
                    },
                    dataType:"text",
                    success:function(d){
                        $('#mask').css("display","none"); // 设置不遮罩
                        let data = JSON.parse(d);
                        let tbody = $("<tbody id='data_tbody'></tbody>")
                        data.forEach(function(item){
                            let tr = `<tr><td>${item.id}</td><td>${item.name}</td><td>${item.age}</td></tr>`;
                            tbody.append(tr);
                        });
                        $('table').append(tbody);
                    }
                })
            });
        })
    </script>
</head>
<body>
    我就是一个传统的html页面, 我的名字是{{name}}
    <br/>
    <input type="button" class="get_btn" value="点我发送get_请求">
    <hr/>
    <a href="javascript:void(0);" class="post_btn" >点我发送post_请求_加载一个表格试试</a>
    <hr/>
    <table width="80%" border="1">
        <thead>
        <tr>
            <td>id</td>
            <td>name</td>
            <td>age</td>
        </tr>
        </thead>

    </table>
    <div id="mask"><span>正在加载中......</span></div>
</body>
</html>

python服务器:

from flask import Flask, render_template, request  # pip install Flask
import time
import json

app = Flask(__name__)

@app.route("/")
def index():
    # 跳转到首页
    print("你曾经来过服务器")
    name = "alex"
    return render_template("index.html", name=name)  # 数据是在这里渲染后, 返回个客户端的html


@app.route("/ajax_post", methods=['POST'])
def ajax_get_post():

    # time.sleep(3)
    # 接收JSON数据
    print(request.json)

    lst = [
        {"id": 1, "name": "张飞", "age": 16},
        {"id": 2, "name": "孙斌", "age": 16},
        {"id": 3, "name": "樵夫", "age": 16},
        {"id": 4, "name": "大佬", "age": 16},
    ]

    return json.dumps(lst)

if __name__ == '__main__':
    app.run()

课上笔记

myapp.py

from flask import Flask, render_template, request, json

app = Flask("qiaofu")


# 127.0.0.1 你这台计算机.
# http://127.0.0.1:5000/

#  用http协议连接127.0.0.1机器上的5000端口
#  / 表示该应用的根目录(虚拟的)

# 有人用浏览器访问我的服务器了. 我得处理人家的请求
# 分开来编写不同的url请求过来之后应该干什么
# @app.route("/")  # 前端访问的如果是/ 就这样处理
# def func1111():
#     # 数据库查询. name
#     # ci = "周杰伦"
#     movie = "饮食男女"
#     # f = open("index.html", mode="r", encoding="utf-8")
#     # page_content = f.read()
#     # # 把name替换到html字符串中
#     # page_content = page_content.replace("{{ movie }}", movie)
#     # return page_content   # 你看到的页面源代码里 是有数据的.
#
#     # 在flask中提供了 模板引擎来做这件事儿,
#     # 模板需要放在templates文件夹中
#
#     # 直接requests 然后. 用xpath, bs4, re解析出你需要的数据
#     return render_template("index.html", movie=movie)


@app.route("/abc")  # 前端访问的如果是/ 就这样处理
def func11112():
    val = request.args.get("name")
    if val:
        val = "参数是: " + val
        # 渲染:
        #   把数据怼到页面
        #   服务器端.
        #   浏览器端.
        # 数据在这里塞入的html# 这里返回的html是有数据的.
        # 服务器端渲染,
        #     发请求. 得到页面源代码, xpath, re, bs4
        return render_template("index.html", val=val)
    else:
        return render_template("index.html", val="你传了个毛线")


@app.route("/haha.daxigua", methods=['GET', "POST"])
def daxigua():
    # print("====>", request.args.get("uname"))  # args接受的是get
    # print("====>", request.form.get("uname"))  # form接受的是post 参数(Form Data)
    print("====>", request.json.get("uname"))  # 获取浏览器传递过来的json数据

    ua = request.headers.get("User-Agent")
    if "PYTHON" in ua.upper():
        return json.dumps({"msg": "滚犊子!!!!"})

    data = {"name": "周杰伦", "age": 18}
    return json.dumps(data)


if __name__ == '__main__':
    # 运行服务器
    app.run(debug=True, port=5000)
    # s = "樵夫是个大可爱"
    # s = s.replace("可爱", "sb")
    # print(s)

# 服务器渲染
# 浏览器发请求 ->
# 到百度 -> 得到参数周杰伦 -> 查询周杰伦相关的数据 -> 把周杰伦的数据组织成html代码
# 返回给浏览器
# 浏览器负责显示

# 客户端渲染
# 浏览器发送请求.
#    返回一段html代码(没有数据),
# 浏览器会开始工作. 执行js的时候.
# 由于某些事件的触发, 通过ajax再次发送一个请求
# 服务器接收到请求. 查询周杰伦相关的数据, 组织需要返回的数据. 直接返回json数据
# 数据返回给js. 然后, js把数据塞入页面当中.
# 浏览器负责显示

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="/static/jquery.js"></script>
    <script>
        $(function(){
            $("#qiaofu").html("<a href='http://www.baidu.com'>去百度</a>");
            // 这里可以发请求. 加载数据
            // ajax -> js专门用来发请求的.
            // // 原生的js代码
            // var xhr = new XMLHttpRequest();
            // xhr.open("get", "/haha.daxigua?name=abc"); // open的意思是打开. 但是这里可没有发请求. 挖坑
            //
            // // 在readystate状态改变的时候
            // xhr.onreadystatechange = function(){
            //     // 只有在返回的数据接收完毕之后. 才开始工作
            //     if (xhr.readyState === 4){
            //         if (xhr.status === 200){
            //             // 确认好返回的数据已经接收完毕了. 返回状态码也OK
            //             // 获取到返回数据
            //             var x = xhr.responseText;
            //             // 直接把数据塞入html代码中
            //             $("#qiaofu").html(x);
            //         }
            //     }
            // }
            // xhr.send(); // 跑路了

            // 瑞数加密参数的入口就是这里.
            // 对数据进行加密. 而且不让你看到在哪里加密的
            var xx = XMLHttpRequest.prototype.open;
            XMLHttpRequest.prototype.open = function(){
                if(arguments[1].includes("?")){
                    arguments[1] += "&t=" + Date.now();
                } else {
                    arguments[1] += "?&t=" + Date.now();
                }
                return xx.apply(this,arguments);
            }


            // jquery ajax
            // $.get()  $.post()
            $.ajax({
                url: "/haha.daxigua",
                method: "post",
                data:JSON.stringify({ // 如果是get, 会写入url,  如果是post, 会写入form data
                    uname: "樵夫"
                }),
                contentType:"application/json",
                dataType: "json", // 返回的数据如果是json, 自动处理成js对象 -> JSON.parse()
                success: function(flkjasdlkfjadskljfdslk){
                    console.log(flkjasdlkfjadskljfdslk);
                }
            });
        });
    </script>

</head>
<body>
  樵夫是一个小可爱
<Br>
{{ val }}
<hr />
下面的东西准备动态加载
<div id="qiaofu"></div>
</body>
</html>

JavaScript补充

一. jsonp

为了解决浏览器跨域问题. jquery提供了jsonp请求.

在网页端如果见到了服务器返回的数据是:

​ xxxxxxxxxxdjsfkldasjfkldasjklfjadsklfjasdlkj({json数据})

​ 在Preview里面可以像看到json一样去调试

這就是jsonp。 这东西依然是ajax.

jsonp的逻辑是. 在发送请求的时候. 带上一个callback字符串. 该字符串自动发送给服务器. 服务器返回数据的时候. 会带上该callback字符串. 我们在抓包中看到的就是这样的效果:

image-20220609173715291

在Python中. 接下来, 我们还原一下该效果.

首先, 在flask中. 必须接收到前端返回的callback, 然后在返回数据的时候. 需要用前端返回的callback字符串. 将数据包裹

from flask import Flask, request, json
from flask_cors import CORS


app = Flask(__name__)
CORS(app)	# 处理cors跨域问题

@app.route("/abc")
def func():
    cb = request.args.get("callback")  # callback_fkldsajfklajsklf
    data = {"msg": "i love you"}
    return f'{cb}({data})'

@app.route("/haha", methods=["GET", "POST"])
def haha():
    print("接受到请求了")
    data = {"msg": "i love you"}
    return json.dumps(data)


if __name__ == '__main__':
    app.run(debug=True)

在发送ajax的时候. 需要指定dataType为jsonp, 以及自由配置回调函数的参数名

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/3.6.0/jquery.js"></script>
    <!-- 我要加载一段js
    var m = {"msg": "i love you"}
    -->

<!--    <script>-->
<!--        // // 你再所有的jsonp里面都能看到返回的东西都长这样:-->
<!--        // // xxxxxxxx({数据})-->
<!--        // // 回调函数...回来了. 调用该函数.-->
<!--        // function callback(arg){-->
<!--        //     // 这里可以监听到数据回来了.-->
<!--        //     console.log("我的arg是", arg);-->
<!--        //     //-->
<!--        // }-->


<!--    </script>-->
<!--    &lt;!&ndash; callback({'msg':'i love you'}) &ndash;&gt;-->
<!--    <script src="http://127.0.0.1:5000/abc"></script>-->

    <script>
        $(function(){
            $("#btn").click(function(){
                // 发个请求
                $.ajax({ // 默认发送的是xhr请求...不发送xhr能不能请求到服务器
                    // 报错了.
                    // CORS policy: No 'Access-Control-Allow-Origin'
                    // 跨域.. 你从一个域名访问另一个域名的东西
                    // 图片, js, css, 视频 -> 静态资源. 没有跨域的问题的

                    // 1. 用jsonp, 可以用于跨域请求.

                    // 2. 在服务器加上CORS的允许
                    // 浏览器的xhr默认是不允许的.
                    url: "http://127.0.0.1:5000/abc",
                    dataType: "jsonp", // 为了解决跨域, 加上它就是jsonp了
                    jsonp:"cb", // 传递给服务器的时候. 自动带上cb=xxxxxx  服务器端接收cb即可
                    success: function(data){
                        console.log("从后台服务器返回的数据是:",data);
                        // 加载完毕之后需要干活的....
                    }
                });
            });
        });
    </script>
</head>
<body>
    <button id="btn">点我</button>
    <!-- http://localhost:63342/%E5%85%AB%E6%9C%9F%E4%B8%93%E7%94%A8/2023_04_19_js%E8%AF%AD%E6%B3%95_08/index.html?_ijt=aov75u38gs7bbpnmsc38q36rjk -->
<!--<img src="https://img14.360buyimg.com/pop/jfs/t1/120075/7/20217/54241/63f47824F7b660785/db424b5a932c2a8b.jpg"/>-->
</body>
</html>

抓包效果:

image-20220609174324528

服务器处理cb时的效果:

image-20220609174352852

抓包中. 看到的服务器返回的数据

image-20220609174419304

success中接收到的数据效果

image-20220609174441431

我们以后见到这种网站. 如何处理?

首先, 固定好callback的值. 如上述案例. 我们就可以直接给出一个固定的cb值. 如果原网站就是固定的值. 此步骤可以忽略

http://127.0.0.1:5000/process_jsonp?cb=haha&_=1654767783595

然后, 得到返回值后. 用正则. 或者字符串操作. 即可处理.

import requests
import re

url = "http://127.0.0.1:5000/abc"
params = {
    "callback": "jQuery36002530792718956838_1681908950457",
    "_": "1681908950461"  # 它是为了防止浏览器缓存的. 实际上和服务器无关
}

resp = requests.get(url, params=params)
# 自己的服务器随便搞

print(resp.text)
# 如何提取jsonp中有用的数据
obj = re.compile(r'\((?P<code>.*)\)')  # 1
code = obj.search(resp.text).group("code")
print(code)
# 后续处理用, json或者execjs来处理成python的dict. 

二. axios

​ 由于jquery有严重的地狱回调逻辑. 再加上jquery的性能逐年跟不上市场节奏. 很多前端工程师采用axios来发送ajax. 相比jquery. axios更加灵活. 且容易使用. 更加美丽的是. 这玩意是用promise搞的. 所以更加贴合大前端的项目需求. 来吧. 上手试试吧

<script src="/static/axios.min.js"></script>
<script>
    window.onload = function(){
        axios.post("/movies", {"page": 10086}).then(function(resp){
            console.log(resp.data);
        })
    }
</script>

看到没. 这玩意比jquery简单n多倍. 而且, axios为了更加适应大前端. 它默认发送和接收的数据就是json. 所以, 我们在浏览器抓包时.

image-20220609175541058

直接就是request payload. 这对于前端工程师而言. 爽爆了.

三. axios拦截器

在前端, 我们能看到有些网站会对每次请求都添加加密信息. 或者每次返回数据的时候, 都有解密逻辑. 那此时. 你思考. 不可能每次请求都要程序员去手动写加密逻辑. 例如:

window.onload = function(){
    // 加密数据
    axios.post("/movies", {"page": 10086}).then(function(resp){
        明文 = 解密(resp.data);
        console.log(明文);
    })

    // 加密数据
    axios.post("/movies", {"page": 10086}).then(function(resp){
        明文 = 解密(resp.data);
        console.log(明文);
    })
}

这样很麻烦. 也很蛋疼. axios想到过类似的问题. 它提供了拦截器. 一次性处理好这种问题

axios.interceptors.request.use(function(config){  // 拦截所有请求
    console.log("我是拦截器. 我可以对数据进行加密");
    console.log(config)
    return config;
}, function(error){
    return Promise.reject(error);
});  

axios.interceptors.response.use(function(response){  // 拦截所有响应
    console.log("我是响应回来之后拦截器. 我可以对数据进行解密")
    return response.data;  
}, function(error){
    return Promise.reject(error);
});

这样. 对于业务层的代码而言就简单很多了

window.onload = function(){
    // 加密的逻辑拦截器帮我完成了
    axios.post("/movies", {"page": 10086}).then(function(data){
        // 解密的逻辑拦截器帮我完成了
        console.log(data);
    })
    // 加密的逻辑拦截器帮我完成了
    axios.post("/movies", {"page": 10086}).then(function(data){
        // 解密的逻辑拦截器帮我完成了
        console.log(data);
    })
}

课上笔记

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="axios.js"></script>

    <script>
        // axios.get("http://127.0.0.1:5000/haha?name=abc").then(function(resp){
        //     // 这个data是一个对象
        //     // resp:
        //     //      config: 配置信息
        //     //            headers 请求头
        //     //      data: 返回的数据
        //     //      headers: 响应头
        //     //      request: 请求对象, 就是xhr对象
        //     //
        //     // console.log("接受到的参数是: ", resp);
        //     var data = resp.data;
        //     console.log(data);
        // });

        // 拦截器
        // scrapy 中间件

        // 我的网站需要对数据进行加密之后发送给服务器
        // 我的网站服务器返回的数据需要解密
        // 给axios所有的请求添加拦截器
        axios.interceptors.request.use(function(config){
            console.log("这里是请求拦截器==========");
            console.log(config);
            // 给所有的请求都添加一个请求头.
            config['headers']['qiao'] = "qiaofu";
            let m = config.method
            if (m === 'post'){
                console.log("请求数据", config.data);
            }

            config.data = {"haha": "fjkldsajklfjsdaklfjadlksjfkasdljfasdlkjfldkasjflksdajfklasdjkf"}
            console.log("这里是请求拦截器==========")
            return config;
        }, function(error){
            return Promise.reject(error);
        });


        // // 给axios所有的响应添加拦截器
        axios.interceptors.response.use(function(resp){
            let data = resp.data; // 拿到响应数据
            return {"解密后": "flkdasjlkfjadslkfjdaslkfjadslkfjadslfjdaslkjfdasklfjadlskjfadslkfjadskl"};
        }, function(error){
            return Promise.reject(error);
        });

        axios.post("http://127.0.0.1:5000/haha?name=abc", {"name": "qiaofu"}).then(function(data){
            // 这个data是一个对象
            // resp:
            //      config: 配置信息
            //            headers 请求头
            //      data: 返回的数据
            //      headers: 响应头
            //      request: 请求对象, 就是xhr对象
            //
            // console.log("接受到的参数是: ", resp);  直接就是request payload,处理成JSON字符串
            // var data = resp.data;
            console.log(data);
        });

        // axios的拦截器的关键词是: interceptors
        // 发现, 调用栈中, 有Promise, 并且距离发送请求很近.
        // 网站中大多数数据的返回都很相似..
        // 盲狙, interceptors

        // 如果看到了
        // 这是它的源码.  目前还无法调试. 后面会讲解如何调试.
        /**
         *  function l(e) {
 *             this.defaults = e,
 *             this.interceptors = {
 *                 request: new o,
 *                 response: new o
 *             }
 *         }
 *         l.prototype.request = function(e) {
         *             "string" === typeof e ? (e = arguments[1] || {},
         *             e.url = arguments[0]) : e = e || {},
         *             e = a(this.defaults, e),
         *             e.method ? e.method = e.method.toLowerCase() : this.defaults.method ? e.method = this.defaults.method.toLowerCase() : e.method = "get";
         *             var t = [s, void 0]
         *               , n = Promise.resolve(e);
         *             this.interceptors.request.forEach((function(e) {
         *                 t.unshift(e.fulfilled, e.rejected)
         *             }
         *             )),
         *             this.interceptors.response.forEach((function(e) {
         *                 t.push(e.fulfilled, e.rejected)
         *             }
         *             ));
         *             while (t.length)
         *                 n = n.then(t.shift(), t.shift());
         *             return n
         *         }
         *
         */

    </script>

</head>
<body>

</body>
</html>
posted @ 2023-05-01 09:16  凫弥  阅读(51)  评论(0编辑  收藏  举报