tip 由于js逆向中需要对js有一定的了解,所以在正式尝试案例前需要对js的一些基础有一定了解,所以会用一定的篇幅去解释一些基础概念语句。

object对象

object对象的基本操作

Object 的实例不具备多少功能,但对于在应用程序中存储和传输数据而言,它们确实是非常理想的选择。
`创建 Object 实例的方式有两种。

1
2
3
4
5
6
7
8
9
10
11
12
//
var person = new Object()
person.name = "cyt"
person.age = 19
console.log(person)

//第二种方式是使用对象字面量表示法。对象字面量是对象定义的一种简写形式,目的在于简化创建包含大量属性的对象的过程。
var people = {
name : "zyj",
age : 19
}
console.log(people)
  • object可以通过. 和 []来访问。
1
2
console.log(people.name)  
console.log(person['age'])
  • object可以通过for循环遍历
1
2
3
for (var attr in person){
console.log(attr,person[attr]);
}

通过构造函数和 new 关键字实现面向对象编程,展示对象的实例化、属性赋值和方法调用。尽管在 ES6+ 中更推荐使用 class,但理解构造函数模式仍是掌握 JS 面向对象的基础。

1
2
3
4
5
6
7
8
9
function person(name,age) {  
this.name = name
this.age = age
this.eat = function(){
console.log(this.name+"正在吃饭")
}
}
var p1 = new person("jyh",20)
p1.eat()

json序列化和反序列化

JSON:JavaScript 对象表示法,是一种轻量级的数据交换格式。易于人阅读和编写。

  • json是一种数据格式, 语法一般是{}或者[]包含起来
  • 内部成员以英文逗号隔开,最后一个成员不能使用逗号!
  • 可以是键值对,也可以是列表成员
  • json中的成员如果是键值对,则键名必须是字符串.而json中的字符串必须使用双引号圈起来
    // json数据也可以保存到文件中,一般以”.json”结尾.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    var data = {
    name: "xiaoming",
    age: 22,
    say: function(){
    alert(123);
    }
    };

    // 把对象转换成json字符串
    var ret = JSON.stringify(data);
    console.log(ret ); // {"name":"xiaoming","age":22}

    // 把json字符串转换成json对象
    var ret2 = JSON.parse(ret);
    console.log(ret2);

Date对象

  • 创建Date对象
1
2
3
4
5
6
7
8
9
//方法1:不指定参数
var nowd1=new Date(); //获取当前时间
console.log(nowd1);
console.log(nowd1.toLocaleString( ));
//方法2:参数为日期字符串
var d2=new Date("2004/3/20 11:12");
console.log(d2.toLocaleString( ));
var d3=new Date("04/03/20 11:12");
console.log(d3.toLocaleString( ));
  • 获取时间信息
1
2
3
4
5
6
7
8
9
10
11
12
获取日期和时间
var date=new Date();
date.getDate()();

getDate() 获取日
getDay () 获取星期
getMonth () 获取月(0-11
getFullYear () 获取完整年份
getHours () 获取小时
getMinutes () 获取分钟
getSeconds () 获取秒
getMilliseconds () 获取毫秒

Math对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Math对象的内置方法
// abs(x) 返回数值的绝对值
var num = -10;
console.log( Math.abs(num) ); // 10

// ceil(x) 向上取整
var num = 10.3;
console.log( Math.ceil(num) ); // 11

// floor(x) 向下取整
var num = 10.3;
console.log( Math.floor(num) ); // 10

// max(x,y,z,...,n)
console.log( Math.max(3,56,3) ); // 56
// min(x,y,z,...,n)


// random() 生成0-1随机数
console.log( Math.random() );

// 生成0-10之间的数值
console.log( Math.random() * 10 );

// round(x) 四舍五入
// 生成0-10之间的整数
console.log( Math.round( Math.random() * 10 ) );

JS中的函数(重点)

声明函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

// 函数的定义方式1
function 函数名 (参数){
函数体;
return 返回值;
}
/*功能说明:
可以使用变量、常量或表达式作为函数调用的参数
函数由关键字function定义
函数名的定义规则与标识符一致,大小写是敏感的
返回值必须使用return
*/
// 函数的定义方式2

//用 Function 类直接创建函数的语法如下:
var 函数名 = new Function("参数1","参数n","function_body");

//虽然由于字符串的关系,第二种形式写起来有些困难,但有助于理解函数只不过是一种引用类型

函数调用

1
2
3
4
5
6
7
//f(); --->OK
function f(){
console.log("hello")

}
f() //----->OK

不同于python,js代码在运行时,会分为两大部分———检查装载 和 执行阶段。

  • 检查装载阶段:会先检测代码的语法错误,进行变量、函数的声明
  • 执行阶段:变量的赋值、函数的调用等,都属于执行阶段。

函数参数

(1) 参数基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 位置参数
function add(a,b){

console.log(a);
console.log(b);
}
add(1,2)
// 会自动忽略第三个
add(1,2,3)
//会只输出1
add(1)


// 默认参数
// 默认参数
function stu_info(name,gender){
console.log("姓名:"+name+" 性别:"+gender)
}

stu_info("bobo","男")

函数返回值

在函数体内,使用 return 语句可以设置函数的返回值。一旦执行 return 语句,将停止函数的运行,并运算和返回 return 后面的表达式的值。如果函数不包含 return 语句,则执行完函数体内每条语句后,返回 undefined 值。

1
2
3
4
5
6
function add(x,y) {
return x,y
}

var ret = add(2,5);
console.log(ret)

1、在函数体内可以包含多条 return 语句,但是仅能执行一条 return 语句

2、函数的参数没有限制,但是返回值只能是一个;如果要输出多个值,可以通过数组或对象进行设计。

作用域

作用域是JavaScript最重要的概念之一。

JavaScript中,变量的作用域有全局作用域和局部作用域两种。

  • 局部变量,是在函数内部声明,它的生命周期在当前函数被调用的时候, 当函数调用完毕以后,则内存中自动销毁当前变量
  • 全局变量,是在函数外部声明,它的生命周期在当前文件中被声明以后就保存在内存中,直到当前文件执行完毕以后,才会被内存销毁掉

首先熟悉下var

1
2
3
4
5
6
7
var name = "bobo"; // 声明一个全局变量 name并赋值”bobo“
name = "张三"; // 对已经存在的变量name重新赋值 ”张三“
console.log(name);

var gender = "male"
var gender = "female" // 原内存释放与新内存开辟,指针指向新开辟的内存
console.log(gender)

作用域案例:

1
2
3
4
5
6
7
 var num = 10; // 在函数外部声明的变量, 全局变量
function func(){
//千万不要再函数内部存在和全局变量同名的变量
num = 20; // 函数内部直接使用变量,则默认调用了全局的变量,
}
func();
console.log("全局num:",num);

匿名函数

匿名函数,即没有变量名的函数。在实际开发中使用的频率非常高!也是学好JS的重点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
   // 匿名函数赋值变量
var foo = function () {
console.log("这是一个匿名函数!")
};
foo() //调用匿名函数


// 匿名函数的自执行
(function (x,y) {
console.log(x+y);
})(2,3)


// 匿名函数作为一个高阶函数使用
function bar() {

return function () {
console.log("inner函数!")
}
}
bar()()

闭包函数

我们先看一段代码.

1
2
3
4
5
6
let name = "周杰伦";
function chi(){
name = "吃掉";
}
chi();
console.log(name);

发现没有, 在函数内部想要修改外部的变量是十分容易的一件事. 尤其是全局变量. 这是非常危险的. 试想, 我写了一个函数. 要用到name, 结果被别人写的某个函数给修改掉了. 多难受.

接下来. 我们来看一个案例:

同时运行下面两组代码:

1
2
3
4
5
6
7
// 1号工具人.
var name = "猪猪"

setTimeout(function(){
console.log("一号工具人:"+name) // 一号工具人还以为是猪猪呢, 但是该变量是不安全的.
}, 5000);

1
2
3
// 2号工具人
var name = "小林子"
console.log("二号工具人", name);

两组代码是在同一个空间内执行的. 他们拥有相同的作用域. 此时的变量势必是非常非常不安全的. 那么如何来解决呢? 注意, 在js里. 变量是有作用域的. 也就是说一个变量的声明和使用是有范围的. 不是无限的. 这一点, 很容易验证.

1
2
3
4
5
function fn(){
let love = "爱呀"
}
fn()
console.log(love)

直接就报错了. 也就是说. 在js里是有全局和局部的概念的.

直接声明在最外层的变量就是全局变量. 所有函数, 所有代码块都可以共享的. 但是反过来就不是了. 在函数内和代码块内声明的变量. 它是一个局部变量. 外界是无法进行访问的. 我们就可以利用这一点来给每个工具人创建一个局部空间. 就像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 1号工具人.
(function(){
var name = "猪猪";
setTimeout(function(){
console.log("一号工具人:"+name) // 一号工具人还以为是alex呢, 但是该变量是不安全的.
}, 5000);
})();

// 二号工具人
(function(){
var name = "小林子"
console.log("二号工具人", name);
})();

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 1号工具人.
let encrypt_tool = (function(){
let log_msg = '开始加密......\n'
// 我是一个加密函数
let encrypt = function(data){ // 数据
console.log(log_msg) //打印日志信息(访问外部变量)
// 返回密文
return atob(data);
}
// 外面需要用到这个功能啊. 你得把这个东东返回啊. 返回加密函数
return encrypt;
})();

//外部调用
console.log(encrypt_tool('i love you'));

注意了. 我们如果封装一个加密js包的时候. 好像还得准备出解密的功能. 并且, 不可能一个js包就一个功能吧. 那怎么办? 我们可以返回一个对象. 对象里面可以存放好多个功能. 而一些不希望外界触碰的功能. 就可以很好的保护起来.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 1号工具人.
let encrypt_tool = (function(){
let log_msg_1 = '开始加密......'
let log_msg_2 = '开始解密......'

// 我是一个加密函数
let encrypt = function(data){ // 被加密数据
console.log(log_msg_1) //打印日志信息(访问外部变量)
// 返回密文
return atob(data);
};
//解密函数
let decrypt = function(en_data){ // 加密后的数据
console.log(log_msg_2) //打印日志信息(访问外部变量)
// 返回解密后的原数据
return btoa(en_data);
};
// 外面需要用到这个功能啊. 你得把这个东东返回啊. 返回加密函数
return {'encrypt':encrypt,'decrypt':decrypt};
})();

//外部调用
en_data = encrypt_tool.encrypt('i love you');
de_data = encrypt_tool.decrypt(en_data)
console.log(en_data);
console.log(de_data);

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

闭包的特点:

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

变量提升(不正常现象)

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

1
2
3
4
5
function fn(){
console.log(name);
var name = 'cyt';
}
fn()

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

1
2
3
4
5
6
7
function fn(){
var name;
console.log(name);
name = 'cyt';
}
fn()
console.log(a);

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

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

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

let还有哪些作用呢?

1
2
3
4
5
6
7
8
function fn(){
// console.log(name); // 直接报错, let变量不可以变量提升.
// let name = '大马猴';
var name = "周杰伦";
var name = "王力宏";
console.log(name);
}
fn()

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

1
2
3
4
5
6
7
8
9
function fn(){
// console.log(name); // 直接报错, let变量不可以变量提升.
// let name = '大马猴';
let name = "周杰伦";
console.log(name);
let name = "王力宏";
console.log(name);
}
fn()

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

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

js包的导入(exports)

类似Python中的模块导入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// functions.js文件

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

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

// 导出函数
exports.add = add;
exports.multiply = multiply;
1
2
3
4
5
6
7
8
// main.js

// 导入 functions 模块
const functions = require('./functions');

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