Skip to content

这是一个为你定制的从零基础到大师级的 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)里的一个标签,指回构造函数本身,告诉别人“我是谁造出来的”。

  • 例子

    jsx
    console.log(Dog.prototype.constructor === Dog); // true
    console.log(dog1.constructor === Dog); // true (通过原型链找到的)

🚀 阶段二:进阶 —— 继承模式与 ES6 对照

目标:精通 JS 继承的发展史,理解 ES6 class 只是语法糖,能阅读转换后的 ES5 代码。

2.1 原型链继承的本质

  • 知识点描述:让子类的原型对象等于父类的一个实例。这样子类实例顺着 __proto__ 就能找到父类的实例,再顺着父类实例的 __proto__ 找到父类的原型。

  • 例子

    jsx
    function 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 放弃优化,性能大幅下降。

  • 例子

    jsx
    const 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.stringifyString.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;
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v3.7.1