JavaScript基础

数据类型

数值型:number(凡是数字都是数值型,不区分整数和小数)
字符串:string(凡是引号包裹起来的内容全部都是字符串)
布尔:bool(true、false)
对象类型:object(特殊取值null)
未定义型:undefined
javascript一切皆对象

object对象

Date对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var now=new Date();
console.log(now.toLocaleString( ));

/*
获取日期和时间
now.getDate() 获取日
now.getDay () 获取星期
now.getMonth () 获取月(0-11)
now.getFullYear () 获取完整年份
now.getYear () 获取年
now.getHours () 获取小时
now.getMinutes () 获取分钟
now.getSeconds () 获取秒
now.getMilliseconds () 获取毫秒
now.getTime () 返回累计毫秒数(从1970/1/1午夜)
*/

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
29
30
31
32
33
34
35
36
37
 //  Number对象的内置方法
// toFixed(x) 保留小数位
var num = 100.3;
var ret = num.toFixed(2);
console.log(num); // 100.3
console.log(ret); // 100.30

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

// pow(x,y)
console.log(Math.pow(3, 2)); // 相等于 3**2
console.log( 3**2 ); // 使用这个,上面废弃

// 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 ) );

Function对象

  • 函数作用域

任何程序设计语言都有作用域的概念,简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。在JavaScript中,变量的作用域有全局作用域和局部作用域两种。

局部变量,是在函数内部声明,它的生命周期在当前函数被调用的时候, 当函数调用完毕以后,则内存中自动销毁当前变量.

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

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

age = 18 // 之前不存在age变量,这里等同于var age = 19 即声明全局变量age并赋值为18

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

函数内部直接使用变量,则默认调用了全局的变量

使用变量的时候,解释器会在当前花括号范围值搜索是否有关键字var 或者 let 声明了变量,如果没有,则一层一层往外查找,如最终查找不到,则直接报错!

变量名 is not define!

1
2
3
4
5
6
7
8
9
var num = 10; // 在函数外部声明的变量, 全局变量
function func(){
num = 20; // 函数内部直接使用变量,则默认调用了全局的变量,
//var num = 20; // 函数内部使用var 或者 let声明的变量则是局部变量

console.log("函数内部num:",num)
}
func();
console.log("全局num:",num);

匿名函数

匿名函数,即没有变量名的函数

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

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


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

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

bar()()

闭包函数

闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量(外部非全局)的函数。

一个函数定义中引用了函数外定义的变量,并且该函数可以在其定义环境外被执行。

分别编写两个js脚本,在运行时, 在同一html内执行。他们拥有相同的作用域。 此时的变量不安全,造成全局变量污染。

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

var key = "123456"

function foo(){
console.log(key)
}

</script>

<script>

var key = "abcdefg"

function bar(){
console.log(key)
}

</script>
1
2
<script src="test01.js"></script>
<script src="test02.js"></script>

在函数内和代码块内声明的变量,尤其是函数内。

声明出来的变量它是一个局部变量. 外界是无法进行访问的,即函数是可以开辟独立的作用域环境的。

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

var f = (function () {
let key = "123456"

function foo() {
return key
}

return foo
})()

</script>

<script>

var b = (function () {
let key = "abcdefg"

function bar() {
return key
}

return bar
})()

</script>
1
2
3
4
5
6
<script src="test01.js"></script>
<script src="test02.js"></script>
<script>
console.log(f())
console.log(b())
</script>

prototype 原型对象

prototype(原型对象)就是一个容器,存放公共的方法给对象使用,对象可以直接访问原型对象中的方法和属性,类似Python的类对象

原型对象和函数之间的关系

每个函数都会有一个prototype属性,指向原型对象.

每个原型对象都会有一个constructor属性,指向函数.

总结:每个函数与原型对象之间的关系是互相引用.

对象和原型对象和函数之间的关系

call和apply方法

call,apply都属于Function.prototype的一个方法,它是JavaScript引擎内在实现的,因为属于Function.prototype,所以每个Function对象实例(就是每个方法)都有call,apply属性。

1
2
3
foo.call(this, arg1,arg2,arg3)
foo.apply(this, arguments)
this.foo(arg1, arg2, arg3);

window对象

window 是客户端浏览器对象模型的基类,window 对象是客户端 JavaScript 的全局对象。一个 window 对象实际上就是一个独立的窗口,对于框架页面来说,浏览器窗口每个框架都包含一个 window 对象。

  • 全局作用域

在客户端浏览器中,window 对象是访问 BOM 的接口,如引用 document 对象的 document 属性,引用自身的 window 和 self 属性等。同时 window 也为客户端 JavaScript 提供全局作用域。

1
2
3
4
5
6
7
8
9
10
11
<script>

var username = "yuanhao"; //全局变量
function f() { //全局函数
console.log(username);
}

console.log(window.username); //返回字符串“yuanhao”
window.f(); //返回字符串“yuanhao”

</script>
  • 使用系统对话框

window 对象定义了 3 个人机交互的方法

1
2
3
alert()  # 确定提示框
confirm() # 选择提示框
prompt() # 输入提示框
  • 访问客户端对象

使用 window 对象可以访问客户端其他对象

  • window:客户端 JavaScript 顶层对象。每当 或 标签出现时,window 对象就会被自动创建。
  • navigator:包含客户端有关浏览器信息。
  • screen:包含客户端屏幕的信息。
  • history:包含浏览器窗口访问过的 URL 信息。
  • location:包含当前网页文档的 URL 信息。
  • document:包含整个 HTML 文档,可被用来访问文档内容及其所有页面元素。
  • 使用定时器

window 对象包含 4 个定时器专用方法

方法 说明
setInterval() 按照执行的周期(单位为毫秒)调用函数或计算表达式
setTimeout() 在指定的毫秒数后调用函数或计算表达式
clearInterval() 取消由 setInterval() 方法生成的定时器
clearTimeout() 取消由 setTimeout() 方法生成的定时器

ES6新语法

var、let以及const

ES6 中引入了关键字 let 和 const 作为 var 的替代。与关键字 var 不同,这两个关键字具有块作用域。在块中声明它们时,它们只能在该块 {} 内访问。

1
2
3
4
5
6
7
8
9
10
// for (var i=0;i<10;i++){
// console.log(i)
// }
// console.log(i)


for (let i=0;i<10;i++){
console.log(i)
}
console.log(i)
  • 变量提升

实际运行的时候和我们写代码的顺序可能会不一样,把变量提前到代码块第一部分运行的逻辑被称为变量提升

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

ES6新语法用let就可以避免此问题,用let声明变量是新版本javascript提倡的一种声明变量的方案

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

箭头函数

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

  • 新写法
1
2
3
4
5
6
7
8
9
10
11
12
var f = v => v
var f = () => 5

var sum = (num1, num2) => num1 + num2
// 如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。
var sum = (num1, num2) => {return num1 + num2}

// 大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号
// 报错
let getUser = id => {id: id, name: "zhang"}
// 不报错
let getUser = id => ({id: id, name: "zhang"})
  • 普通写法
1
2
3
4
5
6
7
8
9
10
11
var f = function(v) {
return v
}

var f = function() {
return 5
}

var sum = function(num1, num2) {
return num1 + num2
}

对象简写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var name = "zhang"
var age = 22

function run() {
console.log(this.name + " running")
}

p1 = {
name,
age,
eat: function () {
console.log(this.name + " is eating")
},
run: run,
}

p1.eat()
p1.run()

ES6格式化

S6中允许使用反引号 ` 来创建字符串,
此种方法创建的字符串里面可以包含由美元符号加花括号包裹的变量 ${vraible}。

1
2
var num = Math.random()
console.log(`生成一个随机数:${num}`)

eval

eval() 函数计算 JavaScript 字符串,并把它作为脚本代码来执行。

如果参数是一个表达式,eval() 函数将执行表达式。如果参数是Javascript语句,eval()将执行 Javascript 语句。

1
2
3
eval(string)
//
eval('[1,2,3,4,5].map(x=>x*x)')

http://tools.jb51.net/password/evalencode

Hook函数

在 JS 逆向中,我们通常把替换原函数的过程都称为 Hook。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function foo() {
console.log("foo功能...")
return 123
}
foo()

var _foo = foo

foo = function () {
console.log("截断开始...")
debugger;
_foo()
console.log("截断结束...")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function foo(a, b, c, d, e, f) {
console.log("foo功能...")
console.log(a, b, c, d, e, f)
return 123
}

var _foo = foo

foo = function () {
console.log("截断开始...")
// _foo(arguments)
_foo.apply(this,arguments)
console.log("截断结束...")
}

foo(1,2,3,4,5,6,7)

案例1: Hook eval

1
2
3
4
console.log("程序开始")
// eval("console.log('yuan')")
window["e"+"v"+"a"+"l"]("console.log('eval yuan')")
console.log("程序结束")
1
2
3
4
5
6
7
8
var _eval = eval

eval = function (src) {
console.log("eval截断开始...")
debugger;
_eval.apply(this, src)
console.log("eval截断结束...")
}

案例2: Hook JSON.stringify

JSON.stringify() 方法用于将 JavaScript 值转换为 JSON 字符串,在某些站点的加密过程中可能会遇到,以下代码演示了遇到 JSON.stringify() 时,则插入断点:

1
2
3
4
5
6
7
8
(function() {
var stringify = JSON.stringify;
JSON.stringify = function(params) {
console.log("Hook JSON.stringif:::", params);
debugger;
return stringify(params);
}
})();

案例3: Hook JSON.parse

JSON.parse() 方法用于将一个 JSON 字符串转换为对象,在某些站点的加密过程中可能会遇到,以下代码演示了遇到 JSON.parse() 时,则插入断点:

1
2
3
4
5
6
7
8
(function() {
var parse = JSON.parse;
JSON.parse = function(params) {
console.log("Hook JSON.parse::: ", params);
debugger;
return parse(params);
}
})();

一般使用Object.defineProperty()来进行属性操作的hook。那么我们了解一下该方法的使用。

1
2
3
4
5
6
Object.defineProperty(obj, prop, descriptor)

// 参数
obj:对象;
prop:对象的属性名;
descriptor:属性描述符;

一般hook使用的是get和set方法,下边简单演示一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var people = {
name: '张三',
};

Object.defineProperty(people, 'age', {
get: function () {
console.log('获取值!');
return count;
},
set: function (val) {
console.log('设置值!');
count = val + 1;
},
});

people.age = 18;
console.log(people.age);

通过这样的方法,我们就可以在设置某个值的时候,添加一些代码,比如 debugger;,让其断下,然后利用调用栈进行调试,找到参数加密、或者参数生成的地方,需要注意的是,网站加载时首先要运行我们的 Hook 代码,再运行网站自己的代码,才能够成功断下,这个过程我们可以称之为 Hook 代码的注入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(function(){
'use strict'
var _cookie = "";
Object.defineProperty(document, 'cookie', {
set: function(val) {
console.log(val);
debugger
_cookie = val;
return val;
},
get: function() {
return _cookie;
},
});
})()