基础不牢地动山摇:JS逆向攻防对抗核心的博弈点在于对JS最基础部分的深刻理解和灵活应用——语法大全
JS逆向攻防对抗核心的博弈点在于对JS最基础部分的深刻理解和灵活应用,偏门基础用法语法知道的越多,理解的越深刻,运用的越灵活才能赢。
1. JavaScript 基础
1.1 语法和数据类型let
、var
和 const
的区别
JavaScript 的数据类型是理解变量和操作的基础。
let
、var
和 const
是 JavaScript 中用于变量声明的关键字,它们之间有几个关键的区别:
- 作用域(Scope):
var
是函数作用域或全局作用域的,即使在代码块(如for
循环或if
语句)内部声明,它的作用域也是整个函数或全局.如果同时声明两次var也会报错。let
和const
是块级作用域的,意味着它们只在声明它们的代码块(如for
循环、if
语句或任何其他类型的块)内可见。
function varTest() {
var a = 10;
if (true) {
var b = 20; // 同一个作用域
console.log(a); // 10
console.log(b); // 20
}
console.log(b); // ReferenceError: b is not defined
}
function letTest() {
let a = 10;
if (true) {
let b = 20; // 不同的作用域
console.log(a); // ReferenceError: a is not defined
console.log(b); // 20
}
console.log(a); // a is not defined
}
- 可变性(Mutability):
var
和let
允许变量重新赋值。const
声明的变量是不可变的,这意味着你不能重新分配新的值给这个变量,但如果你声明的是一个对象或数组,你可以修改对象的属性或数组的元素。
var message = 'Hello';
message = 'Hi'; // 允许重新赋值
let count = 1;
count = 2; // 允许重新赋值
const name = 'ln';
name = 'AI'; // TypeError: Assignment to constant variable.
const person = { name: 'ln' };
person.name = 'AI'; // 允许修改对象的属性
- 全局对象属性(Global Object Property):
- 使用
var
在全局作用域声明的变量会成为全局对象的属性(在浏览器中是window
对象)。 - 使用
let
和const
在全局作用域声明的变量不会成为全局对象的属性。
- 使用
var globalVar = 'I am a global variable';
console.log(window.globalVar); // 'I am a global variable'
let globalLet = 'I am not a global object property';
console.log(window.globalLet); // undefined
- 提升(Hoisting):
var
声明会被提升到它们所在作用域的顶部,但初始化不会被提升。let
和const
声明也会被提升,但它们不会被初始化,直到它们被明确声明。这意味着在声明之前访问这些变量会抛出一个ReferenceError
。
console.log(varTest); // undefined (只提升了声明)
var varTest = 10;
console.log(letTest); // ReferenceError: Cannot access 'letTest' before initialization
let letTest = 20;
console.log(constTest); // ReferenceError: Cannot access 'constTest' before initialization
const constTest = 30;
了解这些区别对于编写可维护和清晰的 JavaScript 代码至关重要。通常推荐使用 let
和 const
来声明变量,因为它们提供了块级作用域,有助于避免意外的作用域问题。使用 const
可以保证变量不可变性,有助于减少错误。
1.2 操作符和表达式
操作符是执行程序逻辑和计算的构建块。
// 算术运算
let total = 5 + 3;
// 字符串连接
let greeting = 'Hello, ' + name + '!';
// 逻辑运算
let isApproved = isDebugging && count > 5;
1.3 控制流
控制流语句控制程序的执行顺序。
// if...else 语句
if (count > 0) {
console.log('Count is positive.');
} else {
console.log('Count is not positive.');
}
// for 循环
for (let i = 0; i < numbers.length; i++) {
console.log(numbers[i]);
}
// break 和 continue
for (let i = 0; i < 10; i++) {
if (i === 5) break;
if (i % 2 === 0) continue;
console.log(i);
}
var value = 1;
switch (value) {
case 1: {
let value = 'Value is 1'; // 这里的let声明了一个块作用域的新变量
console.log(value);
break;
}
case 2:
console.log('Value is 2');
break;
default:
console.log('Value is not 1 or 2');
}
2. 函数和作用域
2.1 函数定义和调用
函数是封装代码以供重复使用的结构。
// 函数表达式
const sayGoodbye = function(name) {
console.log(`Goodbye, ${name}!`);
};
2.2 自执行函数
自执行函数(Immediately Invoked Function Expression,简称IIFE)是一种在定义后立即执行的JavaScript函数表达式。这种模式有几个关键特点:
- 匿名性:自执行函数通常是匿名的,这意味着它们没有名字。
- 立即执行:函数定义后会立即执行,不需要显式调用。
- 作用域限制:自执行函数提供了一个独立的作用域,有助于避免污染全局命名空间。
无参数的自执行函数
(function() {
console.log('This is an IIFE.');
})();
带参数的自执行函数
自执行函数也可以接受参数:
(function(greeting) {
console.log(greeting);
})('Hello, IIFE!');
为什么使用自执行函数
- 避免命名冲突:自执行函数是匿名的,可以避免命名冲突。
- 创建局部变量:自执行函数允许在其中声明局部变量,这些变量在函数外部不可访问。
- 模块模式:自执行函数可以用于JavaScript模块模式,以保持代码的封装性。
- 控制执行时机:立即执行的特性使得自执行函数非常适合用于初始化代码。
模块模式
自执行函数经常用于JavaScript模块模式,以提供私有作用域:
const myModule = (function() {
const privateVar = 'I am private';
return {
publicMethod: function() {
console.log('Accessing private variable:', privateVar);
}
};
})();
myModule.publicMethod(); // 'Accessing private variable: I am private'
在上面的例子中,privateVar
是一个私有变量,只能在自执行函数内部访问,而 publicMethod
是公共接口的一部分,可以从外部调用。
避免全局变量
自执行函数可以避免创建全局变量:
(function() {
let localVar = 'I am local to this IIFE';
})();
console.log(localVar); // ReferenceError: localVar is not defined
箭头函数
自执行函数同样可以使用箭头函数的语法:
// 箭头函数
const greet = (name) => `Hello, ${name}!`;
(() => {
console.log('Arrow function IIFE.');
})();
2.2 闭包和高阶函数
闭包提供了一种保护变量作用域的方式,而高阶函数可以接受或返回函数。
// 闭包
function makeAdder(x) {
return function(y) {
return x + y;
};
}
const addFive = makeAdder(5);
console.log(addFive(3)); // 8
// 高阶函数
function map(numbers, func) {
return numbers.map(n => func(n));
}
const squares = map(numbers, n => n * n);
console.log(squares);
3. 对象和原型链
3.1 对象字面量和原型
对象是属性和方法的集合,原型链是 JavaScript 继承的核心。
// 对象字面量
let car = {
brand: 'Tesla',
model: 'Model S',
start: function() {
console.log(`${this.model} is starting.`);
}
};
car.start(); // 'Model S is starting.'
// 原型链
function Vehicle(make, model) {
this.make = make;
this.model = model;
}
Vehicle.prototype.displayInfo = function() {
console.log(`Make: ${this.make}, Model: ${this.model}`);
};
let myCar = new Vehicle('Tesla', 'Model S');
myCar.displayInfo(); // 'Make: Tesla, Model: Model S'
3.2 构造函数和 new
构造函数用于创建特定类型的对象实例。
// 构造函数
function Animal(species, sound) {
this.species = species;
this.sound = sound;
}
Animal.prototype.makeSound = function() {
console.log(`${this.species} says ${this.sound}.`);
};
let dog = new Animal('Dog', 'Bark');
dog.makeSound(); // 'Dog says Bark.'
4. 异步编程
4.1 回调函数
回调函数是异步编程的基础,用于处理完成的异步操作。
// 回调函数
function onSuccess(data) {
console.log('Data received:', data);
}
function onDataReceived(error, data) {
if (error) throw error;
onSuccess(data);
}
// 模拟异步数据接收
setTimeout(() => onDataReceived(null, { info: 'asynchronous data' }), 1000);
4.2 Promises 和 Async/Await
Promises 提供了一种更优雅的异步编程方式,而 async/await 进一步简化了代码。
// Promises
const delayedData = new Promise((resolve) => {
setTimeout(() => resolve('Promise data'), 1000);
});
delayedData.then(onSuccess).catch(error => console.error(error));
// Async/Await
async function fetchData() {
try {
const data = await delayedData;
console.log('Fetched data:', data);
} catch (error) {
console.error(error);
}
}
fetchData();
5. 模块和作用域
5.1 CommonJS 和 ES6 Modules
模块化是现代 JavaScript 开发的关键部分,它帮助我们组织和管理代码。
// CommonJS 模块
const myUtils = require('./utils');
myUtils.doSomething();
// ES6 模块
import { doSomething } from './utils';
doSomething();
结论
JavaScript 逆向工程要求我们不仅要理解语言的表面语法,还要深入到语言的内部机制。掌握从基础语法到高级概念的知识,是每个逆向工程师的必备技能。通过不断学习和实践,我们可以提高自己的技术水平,更好地应对各种代码挑战。
注意
本文提供的代码示例仅用于教育目的,不应用于任何非法逆向工程活动。请确保在进行逆向工程时遵守相关法律法规。