一、webpack技术是什么?
(1)定义
Webpack 是一个前端资源模块化管理和打包工具,Webpack技术的出现主要是为了解决现代前端开发中JS代码的模块化组织问题(当然还有别的原因,这里只重点讲和逆向有关的)。
随着JavaScript生态的发展,特别是CommonJS和ES6模块等规范的引入,项目中代码需要以模块化的方式组织。然而,浏览器原生并不直接支持这些模块加载机制,WebPack通过解析、编译和打包模块,使得开发者可以利用模块化的优点编写可维护性更强的代码。
(2)webpack的格式
简单的webpack格式:
javascript
// 简单
!function (e) {
加载器
}(接收的参数)
详细的webpack格式:
javascript
// 详细
!function (e) {
// 存放加载器
var c = {}
// 加载器
function n(t) {
// 创建a对象
var a = {
i: t, // 表示模块的标识符
l: !1, // 布尔值,初试为false,用来表示当前模块是否被加载
exports: {} // 空对象,用于讲模块导出,存储
};
c[t] = {
i: t, // 表示模块的标识符
l: !1, // 布尔值,初试为false,用来表示当前模块是否被加载
exports: {} // 空对象,用于讲模块导出,存储
};
// 执行函数里面的代码
return e[t].call(a.exports, a, a.exports, n),
a.l = !0,
a.exports
}
n.m = e // n.m 用来存所有的模块
n(2) // 执行当前选中的模块,下标2就是第三个模块
}([
function () {...},
function () {...},
function () {...},
function () {...},
])
(3)模块化
比如下面代码中有三个模块,他们其实就是三个函数,假设这三个模块都有着不同的功能,这里演示代码中显示的是打印不同字符。
javascript
function ModuleA() {console.log("a")}
function ModuleB() {console.log("b")}
function ModuleC() {console.log("c")}
但如果你的写的功能模块有几百上千个,那么这样的书写方式必须放在同一个JS文件中就会非常臃肿,于是出现了这样的代码结构:
javascript
!function(i) {
function n(t) {
return i[t].call(a, b, c, d)
}
}([
function(t, e, n) {},
function(t, e, r) {},
function(t, e, o) {},
function(t, e) {},
]);
由于在扣JS加密代码时会经常遇到,所以理解这种语法对于逆向来说是非常重要的。
这种代码组织相当于进行了模块化编程,因此被称为 webpack。上面的代码看起来很费劲,其中的一个重要原因就是函数的参数和函数名都是简单的字母没有含义,都不知道这是干什么。于是我们可以写成下面这种方式便于理解:
javascript
!function (allModule) {
// 定义执行模块的函数
function useModule(whichModule) {
// 用call方法来执行函数
allModule[whichModule].call(null, "hello world!");
}
useModule(0) // 这里指定加载哪一个模块
}([
function ModuleA(param) {console.log("A" + param)}, // 用数组装所有的模块,这是第一个
function ModuleB(param) {console.log("B" + param)},
function ModuleC(param) {console.log("C" + param)},
]);
二、技术拆解
(1)JS的自执行函数
IIFE 全称 Immediately-invoked Function Expressions,译为立即调用函数表达式,也称为自执行函数、立即执行函数、自执行匿名函数等,IIFE 是一种语法,这种模式本质上就是函数表达式(命名的或者匿名的)在创建后立即执行。当函数变成立即执行的函数表达式时,表达式中的变量不能从外部访问。IIFE 主要用来隔离作用域,避免污染。它长这个样子(我只介绍最常见的形式):
javascript
// 第一种形式:用感叹号表示
!function () {
console.log("I AM IIFE")
};
!function () {
console.log(A)
}(A); // 后面紧跟的小括号里面可以传入参数
// 第二种形式:用括号表示
(function () {
console.log("I AM IIFE")
}());
(() => {
console.log("I AM IIFE")
})();
(2)call方法apply方法
JavaScript 中的 call() 和 apply() 方法都是函数对象(Function.prototype)上的方法,它们的主要功能是改变函数调用时的上下文环境(即函数内部 this 的指向),同时允许你传递参数给该函数。
下面是call方法的使用例子:
javascript
function sayHello(name) {
console.log('Hello, ' + this.name + ', ' + name);
}
let user = { name: 'John' };
sayHello.call(user, 'Doe'); // 输出 "Hello, John, Doe"
下面是apply方法的使用例子:
javascript
function sum(a, b) {
return a + b;
}
let numbers = [3, 5];
let result = sum.apply(null, numbers); // 输出 8,等同于 sum(3, 5)
总结起来就是,call和apply第一个参数都是函数运行时的上下文对象thisArg。后面的参数不同,对于call来说,参数是一个个传入;对于apply来说,参数是通过一个数组传入。
- 当需要以逗号分隔的形式传递参数时,可以使用
call()
方法。 - 当需要以数组形式传递可变数量的参数时,应该使用
apply()
方法。