这是一个为你定制的从零基础到大师级的 JavaScript 原型链学习计划,特别结合了你感兴趣的 爬虫与逆向工程 场景。
📅 阶段一:筑基 —— 彻底搞懂 "三角关系"
目标:能够手画任意对象的原型链图,不再被 prototype、__proto__ 和 constructor 绕晕。
1.1 构造函数 vs 普通函数
知识点描述:在 JS 中,任何函数都可以被
new关键字调用,当被new调用时,它就是“构造函数”。构造函数通常首字母大写,用来创建对象(生成实例)。例子:
jsx// 普通函数定义 function Dog(name) { this.name = name; } // 当作普通函数调用(不推荐,this 会指向 window/undefined) Dog("旺财"); // 当作构造函数调用(推荐,创建新实例) const myDog = new Dog("旺财"); console.log(myDog.name); // "旺财"
1.2 prototype (显式原型)
知识点描述:这是函数独有的属性。你可以把它理解为一个“仓库”,用来存放所有实例共享的方法和属性。如果不放在这里,每个实例都会拷贝一份方法,浪费内存。
例子:
jsx// 往仓库里放一个 bark 方法 Dog.prototype.bark = function() { console.log(this.name + " 汪汪叫"); }; const dog1 = new Dog("小白"); const dog2 = new Dog("小黑"); dog1.bark(); // 都在用同一个仓库里的 bark dog2.bark(); console.log(dog1.bark === dog2.bark); // true,说明是同一个函数
1.3 __proto__ (隐式原型)
知识点描述:这是对象独有的属性(虽然函数也是对象)。它像一根绳子,连接着实例和构造函数的
prototype仓库。当你访问一个属性时,JS 引擎会先找对象自己,找不到就顺着__proto__这根绳子去仓库里找。例子:
jsx// 验证绳子是否连着仓库 console.log(dog1.__proto__ === Dog.prototype); // true
1.4 constructor (构造器)
知识点描述:这是仓库(
prototype)里的一个标签,指回构造函数本身,告诉别人“我是谁造出来的”。例子:
jsxconsole.log(Dog.prototype.constructor === Dog); // true console.log(dog1.constructor === Dog); // true (通过原型链找到的)
🚀 阶段二:进阶 —— 继承模式与 ES6 对照
目标:精通 JS 继承的发展史,理解 ES6 class 只是语法糖,能阅读转换后的 ES5 代码。
2.1 原型链继承的本质
知识点描述:让子类的原型对象等于父类的一个实例。这样子类实例顺着
__proto__就能找到父类的实例,再顺着父类实例的__proto__找到父类的原型。例子:
jsxfunction Animal() { this.type = "动物"; } function Cat() {} // 关键一步:Cat 的原型指向 Animal 的实例 Cat.prototype = new Animal(); const mimi = new Cat(); console.log(mimi.type); // "动物" (顺着链条找到的)
2.2 ES6 Class 语法糖
知识点描述:ES6 提供了更接近传统面向对象语言的写法,但底层依然是原型链。在逆向混淆代码时,你常会看到 Babel 将 class 编译回 ES5 的样子。
例子:
jsx// ES6 写法 class Cat extends Animal { constructor(name) { super(); // 相当于 Animal.call(this) this.name = name; } }
🛠️ 阶段三:深潜 —— 引擎视角与性能
目标:从 V8 引擎角度理解原型,编写高性能代码。
3.1 V8 的 Hidden Classes (形状)
知识点描述:V8 引擎为了快,会给对象赋予“形状”(Hidden Class)。如果你动态修改对象的原型(使用
__proto__或Object.setPrototypeOf),会破坏这个形状,导致 V8 放弃优化,性能大幅下降。例子:
jsxconst obj = { x: 1 }; // ❌ 性能杀手:动态修改原型 Object.setPrototypeOf(obj, { y: 2 }); // ✅ 推荐:在创建时就指定好原型 const objFast = Object.create({ y: 2 }); objFast.x = 1;
⚔️ 阶段四:大师 —— 逆向工程与 Hook 实战
目标:这是你作为爬虫工程师的核心竞争力。掌握利用原型链进行环境模拟和对抗检测。
4.1 原型链 Hook (Native Hooking)
知识点描述:在 JS 代码运行前,修改原生对象的原型方法。这样网页调用的所有原生方法都会先经过你的手,你可以查看参数、修改返回值。这被称为“插桩”。
场景:爬虫解密。很多加密库会调用
JSON.stringify或String.prototype.split。例子:
jsx// 保存原生的 split 方法 const originalSplit = String.prototype.split; // 覆盖(Hook)它 String.prototype.split = function(separator, limit) { // 在控制台偷偷打印数据 console.log('正在分割字符串:', this.toString()); console.log('分割符是:', separator); // 记得把活干完,调用原方法并返回结果 return originalSplit.apply(this, arguments); }; // 网页正常代码执行时: "abc|123".split("|"); // 控制台会输出你的 log!
4.2 检测与反检测 (Anti-Hooking)
知识点描述:网站会检测核心方法有没有被你 Hook。最常用的方法是打印
.toString(),看是不是输出[native code]。对抗思路:不仅 Hook 方法本身,还要 Hook 该方法的
toString方法,让它撒谎。例子:
jsx// 简单的反检测 Hook const myHook = function() { console.log("I am a hook"); }; // 让它的 toString 伪装成原生代码 myHook.toString = function() { return "function split() { [native code] }"; } String.prototype.split = myHook;