爬虫&逆向--Day07--Javascript进阶

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

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

function fn(){
    console.log(name);          // undefined
    var name = '大马猴';
}
fn()

发现问题了么. 这么写代码, 在其他语言里. 绝对是不允许的. 但是在js里. 不但允许, 还能执行. 为什么呢? 因为在js执行的时候. 它会首先检测你的代码. 发现在代码中会有name使用. OK. 运行时就会变成这样的逻辑:

function fn(){
    var name;
    console.log(name);          // undefined
    name = '大马猴';
}
fn()

看到了么. 实际运行的时候和我们写代码的顺序可能会不一样....这种把变量提前到代码块第一部分运行的逻辑被称为变量提升. 这在其他语言里是绝对没有的. 并且也不是什么好事情. 正常的逻辑不应该是这样的. 那么怎么办? 在新的ES6中. 就明确了, 这样使用变量是不完善的. ES6提出. 用let来声明变量. 就不会出现该问题了.

function fn(){
    console.log(name);      // 直接报错, let变量不可以变量提升.
    let name = '大马猴';
}
fn()

结论一: 用let声明变量是新版本javascript提倡的一种声明变量的方案,不会进行变量提升操作

let还有哪些作用呢?

function fn(){
    var name = "周杰伦";
    var name = "王力宏";
    console.log(name);      // 王力宏
}
fn()

显然一个变量被声明了两次. 这样也是不合理的. var本意是声明变量. 同一个东西. 被声明两次. 所以ES6规定. let声明的变量. 在同一个作用域内. 只能声明一次.

function fn(){
    let name = "周杰伦";       // 直接报错, let变量不允许声明两个相同的变量
    console.log(name);
    let name = "王力宏";
    console.log(name);
}
fn()

 

注意, 报错是发生在代码检查阶段. 所以. 上述代码根本就执行不了.

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

 

注意:直接进行变量声明,不使用let和var的含义是什么?

不使用`var或者let`直接赋值的变量,如果没有提前声明,它会成为全局变量。这意味着它可以在整个程序的任何地方访问和修改,这可能导致意外的副作用和难以调试的错误。

function fn(){
    name = '大马猴';
}
fn()
console.log(name)  //大马猴   正确


function fn(){
    var name = '大马猴';
}
fn()
console.log(name)   //报错

二、JS中的各种操作(非交互)

2.1、定时器

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

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

// 正常函数的延迟调用  示例:3秒后打印执行
function f(){
    console.log("函数被执行了--正常函数")      // 函数被执行了--正常函数
}
setTimeout(f,3000)

// 匿名函数的延迟调用
setTimeout(function (){
    console.log("函数被执行了--匿名函数")     // 函数被执行了--匿名函数
},3000)

2.2、eval函数(必须会)

eval本身在js里面正常情况下使用的并不多. 但是很多网站会利用eval的特性来完成反爬操作. 我们来看看eval是个什么鬼?

从功能上讲, eval非常简单. 它和python里面 的eval是一样的. 它可以动态的把字符串当成js代码进行运行.

let s = "console.log('我爱你')";
eval(s);       // 我爱你

也就是说. eval里面传递的应该是即将要执行的代码(字符串). 那么在页面中如果看到了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()执行不了的. 对不...于是就有了下面的操作.

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

2.3、prototype和__proto__(重点)

逆向代码示例

//原型
function ct(t) {
    pv['$_BCAO'] = t || [];
} 

ct.prototype = {
    '$_CAE':function(f) {
               f();
            }
} 
//[]表示,调用对象成员
var H = new ct(t)['$_CAE'](function(t) {
        var e = function(t) {
            for (var e = [[1, 0], [2, 0], [1, -1], [1, 1], [0, 1], [0, -1], [3, 0], [2, -1], [2, 1]], r = e['length'],n = 0; n < r; n++){
                var b = 'stuvwxyz~';
            }   
            return b;
        };
        return e;
    })
  • 原型

    • 概念:原型本身其实是一个function函数,可以将其理解成Python中的class类。

    • 创建一个原型:

  • function User(name, pwd) {
        this.userName = name;                   // 属性成员
        this.pwd = pwd;
        this.regist = function () {               // 方法成员
            console.log(this.userName + "在注册")
        }
    }
  • 实例对象

    • 概念:通过new关键字创建的对象称为实例对象。

    • 创建实例对象:

  • let u1 = new User('jay', '123');
    let u2 = new User('tom', '456');
  • 原型对象

    • 概念:原型对象用于存储所有实例对象共享的属性和方法,以减少每个实例对象重复存储相同属性和方法的开销。

    • 原型对象存储所有实例对象共享的属性和方法

  • // 获取/创建原型对象   如何获取原型对象  User.prototype
    // 类似于类属性
    User.prototype.address = "BJ";
    User.prototype.gender = "male";
    //类似于类方法
    User.prototype.login = function login(username, password) {
        console.log(`${username}在登录`);
    }
    
    console.log(u1.address, u2.address)          // BerJing BerJing
    console.log(u1.gender, u2.gender)            // male male
    console.log(u1.login('jay', '123'), u2.login('tom', '456'));
    // jay在登录   tom在登录
    /*
    原型对象可以存储或者可以创建被所有实例对象共享的成员
    原型对象是用于给所有的实例对象增加可共享的成员的
    */
    • 获取原型对象:

  • User.prototype;     //类似于访问所有的类成员  通过原型获取原型对象
    u1.__proto__;       // 通过实例对象获取原型对象
    User.prototype === u1.__proto__ //true
    //可以通过原型名访问原型对象或者使用实例名访问原型对象
    console.log(User.prototype)     // { address: 'BJ', gender: 'male', login: [Function: login]
    console.log(u1.__proto__)       // { address: 'BJ', gender: 'male', login: [Function: login] }
  • 原型链

    • 原型链是JavaScript中对象继承属性和方法的一种方式。具体介绍如下:

      原型链是JavaScript中对象继承属性和方法的一种方式。当访问一个对象的属性或方法时,如果该对象本身没有这个属性或方法,它会通过原型链去它的原型对象中查找,如果原型对象也没有,会继续在其原型的原型中查找,这样逐级向上,直到找到属性或方法或者达到原型链的末端。

      原型对象本身也是一个对象,它也可以使用__proto__访问它的原型对象,类似于:

      u1.__proto__.__proto__
    • 原型链的成员访问:

      • 实例对象可以访问其原型内的成员和其原型链上所有原型对象内的成员

    • u1.toString()
  • 浏览器环境navigator分析

练习测试:理解下属代码

//原型
function ct(t) {
    pv['$_BCAO'] = t || [];
}

//[]表示.调用对象成员
var H = new ct(t)['$_CAE'](function(t) {
        var e = function(t) {
            for (var e = [[1, 0], [2, 0], [1, -1], [1, 1], [0, 1], [0, -1], [3, 0], [2, -1], [2, 1]], r = e['length'],n = 0; n < r; n++){
                var b = 'stuvwxyz~';
            }   
            return b;
        };
        return e;
    })

ct.prototype = {
    '$_CAE':function(f) {
               f();
            }
}

2.4、神奇的window(浏览器环境,在console中执行代码)

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

例如:

var a = 10; 
a === window.a  // true

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

综上, 我们可以得出一个结论. 全局变量可以用window.xxx来表示.

注意:window对象实际上表示的是浏览器的窗口。浏览器的窗口只有浏览器有。我们在浏览器中的开发者工具的Console选项卡中是可以直接使用window对象。

但是:在pycharm中直接使用window对象会报错:ReferenceError: window is not defined。why?就是因为在pycharm中是没有浏览器窗口。

所以切记:window对象是专属于浏览器环境下的一个内置对象。就意味着window只可以在浏览器环境下无需声明直接被使用。

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

//如果想在外部调用下面函数的中的chi函数如何实现?
(function(){
    let chi = function(){
        console.log("我是吃")
    }
})();
chi() //会报错,因为chi是一个局部变量

//正确写法:
(function(){
    let chi = function(){
        console.log("我是吃")
    }
    window.chi = chi //全局的
})();
chi();

//换一种写法. 你还认识么?
(function(w){
    let chi = function(){
        console.log("我是吃")
    }
    w.chi = chi
})(window);

如何在node环境下执行上述代码?

//globalThis在浏览器和node中都可以使用。在Pycharm中就可以使用globalThis代替window用于全局变量的声明

//全局变量的声明
globalThis.a = 10;
console.log(a);

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

使用 window 对象也可以访问客户端其他对象,这种关系构成浏览器对象模型,window 对象代表根节点,浏览器对象关系的关系如图所示,每个对象说明如下。

  • window:客户端 JavaScript 顶层对象。每当 <body><frameset> 标签出现时,window 对象就会被自动创建。

  • navigator:包含客户端有关浏览器信息。

  • screen:包含客户端屏幕的信息。

  • history:包含浏览器窗口访问过的 URL 信息。

  • location:包含当前网页文档的 URL 信息。

  • document:包含整个 HTML 文档,可被用来访问文档内容及其所有页面元素。

2.5、call和apply

对于咱们逆向工程师而言. 并不需要深入的理解call和apply的本质作用. 只需要知道这玩意执行起来的逻辑顺序是什么即可(外部函数关联对象的内部成员)

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

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

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

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

//外部函数关联对象的内部函数:
//想要让hahaha吃:"馒头", "大饼",如何调用下面的函数?
function eat(what_1, what_2){
    console.log(this.name, "在吃", what_1, what_2);    // hahaha 在吃 馒头 大饼
}
// call的语法是: 函数.call(对象, 参数1, 参数2, 参数3....)
// 执行逻辑是: 执行函数. 并把对象传递给函数中的this.  其他参数照常传递给函数
eat.call(p1, "馒头", "大饼");
// 将eat这样的一个外部函数,关联p1对象访问p1对象的内容成员

eat.apply(p1, ["馒头", "大饼"]);

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

eat.apply(p1, ["馒头", "大饼"]);

2.6、ES6中的箭头函数

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

// 定义了一个匿名函数,并用fn指向该匿名函数
var fn = function () {};
// 如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。
var fn = () => {};   // var fn = 参数 => 函数体;

var fn = function (name) {}
var fn = name => {}
var fn = (name) => {}

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

// 一个参数可以省略小括号,一行可以省略大括号
var f = v => v * v
// 等同于
var f = function (v) {
    return v * v
}

2.7、exports

类似Python中的模块导入

// functions.js文件

// 加法函数
function add(a, b) {
    return a + b;
}

// 乘法函数
function multiply(a, b) {
    return a * b;
}

// 把内部函数向外公开一下
exports.add = add;
exports.multiply = multiply;
// main.js
// 导入 functions 模块
let functions = require('./functions');

// 使用导入的函数
console.log(functions.add(2, 3));       // 输出: 5
console.log(functions.multiply(4, 5));  // 输出: 20

三、DOM对象(了解)

DOM document Object Model 文档对象模型

// 整个html文档,会保存一个文档对象document
// console.log( document ); // 获取当前文档的对象

3.1、查找标签

  • 直接查找标签

document.getElementsByTagName("标签名")
document.getElementById("id值")
document.getElementsByClassName("类名")
//返回dom对象,就是标签对象或者数组
  • CSS选择器查找

document.querySelector("css选择器")  //根据css选择符来获取查找到的第一个元素,返回标签对象(dom对象)
document.querySelectorAll("css选择器"); // 根据css选择符来获取查找到的所有元素,返回数组
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>


<div id="i1">DIV1</div>

<div class="c1">DIV</div>
<div class="c1">DIV</div>
<div class="c1">DIV</div>


<div class="outer">
    <div class="c1">item</div>
</div>


<div class="c2">
    <div class="c3">
        <ul class="c4">
            <li class="c5" id="i2">111</li>
            <li>222</li>
            <li>333</li>
        </ul>
    </div>
</div>

<script>

   // 直接查找
   var ele = document.getElementById("i1");   // ele就是一个dom对象
   ele.innerText = 'hello hahaha';            //将该标签内容修改成了hello bobo
   console.log(ele);

   var eles = document.getElementsByClassName("c1"); // eles是一个数组 [dom1,dom2,...]
   console.log(eles);

   var eles2 = document.getElementsByTagName("div"); // eles2是一个数组 [dom1,dom2,...]
   console.log(eles2);

  //定位outer下的c1对应的标签
   var outer = document.getElementsByClassName("outer")[0];
   var te = outer.getElementsByClassName("c1");
   console.log(te);

    // css选择器
        //层级定位(空格可以表示一个或多个层级)  . 表示class 将class=c2 clss=c3 class=c5
    var dom = document.querySelector(".c2 .c3 .c5");
    console.log("::: 1 1 1",dom);
        //层级定位
    var doms = document.querySelectorAll("ul li");
    console.log(":::",doms);

</script>

</body>
</html>

3.2、绑定事件

  • 静态绑定 :直接把事件写在标签元素中

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

<div id="div" onclick="foo()">click</div>

<script>
    function foo() {
        console.log("foo函数");
    }
</script>


</body>
</html>

  • 动态绑定:在js中通过代码获取元素对象,然后给这个对象进行后续绑定

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

<p id="i1">试一试!</p>

<script>

    var ele=document.getElementById("i1");

    ele.onclick=function(){
        console.log("ok");

    };

</script>

</body>
</html>

四、jQuery和Ajax(了解)

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

jQuery的下载:

jquery:https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/1.9.1/jquery.js

https://code.jquery.com/jquery-3.6.0.min.js

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

<head>
    <meta charset="UTF-8">
    <title>Title</title>
    // 在代码中引入jQuery编程,只需要在代码中加入下方的这行代码即可
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>

4.1、jQuery初使用

导入jQuery

<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>

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

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

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    // 在代码中引入jQuery编程,只需要在代码中加入下方的这行代码即可
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body> <div class="div-out"> <input type="button" class="btn" value="我是按钮. 你怕不怕"> <div class="mydiv">我怕死了...</div> </div>

</body> </html>

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

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

汇总代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    // 在代码中引入jQuery编程,只需要在代码中加入下方的这行代码即可
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body>
<div class="div-out">
    <input type="button" class="btn" value="我是按钮. 你怕不怕">
    <div class="mydiv">我怕死了...</div>
</div>

<script>
    // 传统js
    // window.onload = function () {
    //     document.querySelector(".btn").onclick = function () {
    //         document.querySelector('.mydiv').innerText = "我好啪啪啊";
    //     };
    // }


    // jQuery
    $(function () {  // $(document).ready(function(){
        $(".btn").click(function () {
            $(".mydiv").text('我要上天');
        })
    })

</script>

</body>
</html>

4.2、jQuery选择器

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

// 语法:
$(选择器)

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

案例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://code.jquery.com/jquery-3.6.0.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>

4.3、发送ajax请求

  • 什么是ajax?

  • ajax是异步的网络请求,ajax是在保证整体页面不做刷新的情况下,页面 内容的局部数据进行刷新

    • AJAX = 异步的javascript和XML(Asynchronous Javascript and XML)

    • 它不是一门编程语言,而是利用JavaScript在保证页面不被刷新、页面链接不改变的情况下与服务器交换数据并更新部分网页的技术。

    • 对于传统的网页,如果想更新内容,那么必须要刷新整个页面,但有了Ajax,便可以在页面不被全部刷新的情况下更新其内容。在这个过程中,页面实际上是在后台与服务器进行了数据交互,获得数据之后,再利用JavaScript改变页面,这样页面内容就会更新了。

    • 简言之,在不重载整个网页的情况下,AJAX通过后台加载数据,并在网页上进行显示。

    • 通过 jQuery AJAX 方法,您能够使用 HTTP Get 和 HTTP Post 从远程服务器上请求文本、HTML、XML 或 JSON - 同时您能够把这些外部数据直接载入网页的被选元素中。

  • 方式一:通过jQuery进行发送ajax请求
//get()方式
$.ajax({
    url: './data/index.txt',    // 异步请求的地址
    type: 'get',                // 请求方式
    //当请求成功后,执行下面的匿名函数,函数参数data就是请求到的数据
    success: function (data) {
        // 请求成功
        $('p').html(data);
    },
    error: function (error) {
        // 请求失败
        console.log(error);
    }
})
//post()方式
$.ajax({
    url: '/index',
    type: 'post',
    data: {name: '张三', age: 12},//post的请求参数
    success: function (dd) {
        $('p').html(dd);
    },
    error: function (error) {
        console.log(error)
    }
})
  • 方式二:通过原生JS进行发送ajax请求
// 原生js如何做
// 创建一个新的XMLHttpRequest对象
let xhr = new XMLHttpRequest();

// 设置请求方法和URL
xhr.open('GET', 'https://xxx.com', true);

// 设置请求完成时的回调函数  请求成功
xhr.onload = function () {
    if (xhr.status >= 200 && xhr.status < 400) {
        // 请求成功,处理返回的数据
        let data = JSON.parse(xhr.responseText);
        console.log(data);
    } else {
        // 请求失败,处理错误
        console.error('请求失败,状态码:' + xhr.status);
    }
};

// 设置请求过程中发生错误的回调函数  如:网络阻塞等
xhr.onerror = function () {
    console.error('请求过程中发生错误');
};

// 发送请求
xhr.send();

 

posted @ 2025-07-03 14:41  L遇上J  阅读(47)  评论(0)    收藏  举报