第20章 Day24 原型链
原型链
EventTarget 不是 Window 的"构造函数"
EventTarget 也是一个构造函数,Window 也是一个构造函数。它们之间是构造函数的继承关系。
正确的理解
// 两个构造函数之间的继承
Object.setPrototypeOf(Window, EventTarget)
// 结果:Window.__proto__ === EventTarget
// 意思:Window 构造函数继承自 EventTarget 构造函数
这不是说 EventTarget 是 Window 的构造函数,而是说:
Window是一个构造函数EventTarget也是一个构造函数Window构造函数继承自EventTarget构造函数
类比 ES6 class
// 相当于:
class EventTarget {
addEventListener() {}
}
class Window extends EventTarget { // Window 继承自 EventTarget
// Window 的方法
}
// 在 ES6 中:
console.log(Window.__proto__ === EventTarget); // true
完整的继承链
// 构造函数继承链
Window.__proto__ === EventTarget // Window 构造函数继承自 EventTarget 构造函数
EventTarget.__proto__ === Function.prototype // EventTarget 构造函数继承自 Function.prototype
// 原型链
Window.prototype.__proto__ === WindowProperties.prototype
WindowProperties.prototype.__proto__ === EventTarget.prototype
EventTarget.prototype.__proto__ === Object.prototype
// 实例链
window.__proto__ === Window.prototype // window 是 Window 的实例
总结
- EventTarget 是构造函数
- Window 也是构造函数,但它继承自 EventTarget 构造函数
- window 是 Window 构造函数的实例
所以更准确的说法是:"Window 构造函数继承自 EventTarget 构造函数",而不是 "EventTarget 是 Window 的构造函数"。
构造函数判定
// 在浏览器中
console.log(typeof Window); // "function"
console.log(Window.prototype); // Window.prototype 对象
console.log(Window.prototype.constructor === Window); // true
console.log(window instanceof Window); // true
console.log(window.constructor === Window); // true
总结 构造函数之间的继承就是 Object.setPrototypeOf(Window, EventTarget);
完整的继承结构
// 1. 构造函数之间的继承
Window.__proto__ === EventTarget // 构造函数继承
// 2. 原型对象之间的继承
Window.prototype.__proto__ === WindowProperties.prototype // 实例方法继承
WindowProperties.prototype.__proto__ === EventTarget.prototype // 实例方法继承
// 3. 实例的继承链
window.__proto__ === Window.prototype // window 是 Window 的实例
基本对! 但有一个小错误,让我修正一下:
✅ 正确的三种继承
1. 构造函数继承
Object.setPrototypeOf(x, y)
// 结果:x.__proto__ === y
// 例子:Object.setPrototypeOf(Window, EventTarget)
2. 实例方法继承(原型对象之间的继承)
Object.setPrototypeOf(x.prototype, y.prototype)
// 结果:x.prototype.__proto__ === y.prototype
// 例子:Object.setPrototypeOf(Window.prototype, WindowProperties.prototype)
3. 实例的继承链 ❌
// ❌ 错误
Object.setPrototypeOf(x, y.prototype)
// ✅ 正确
Object.setPrototypeOf(instance, Constructor.prototype)
// 结果:instance.__proto__ === Constructor.prototype
// 例子:Object.setPrototypeOf(window, Window.prototype)
为什么第3个不对?
第3种是让一个对象成为某个构造函数的实例,格式应该是:
// 让对象 obj 成为构造函数 Constructor 的实例
Object.setPrototypeOf(obj, Constructor.prototype)
// 而不是:
Object.setPrototypeOf(Constructor, OtherConstructor.prototype) // 这个没意义
完整示例
// 1. 构造函数继承
Object.setPrototypeOf(Window, EventTarget);
console.log(Window.__proto__ === EventTarget); // true
// 2. 原型对象继承(实例方法继承)
Object.setPrototypeOf(Window.prototype, WindowProperties.prototype);
console.log(Window.prototype.__proto__ === WindowProperties.prototype); // true
// 3. 实例的继承链
Object.setPrototypeOf(window, Window.prototype);
console.log(window.__proto__ === Window.prototype); // true
console.log(window instanceof Window); // true
总结
实例的继承链:Object.setPrototypeOf(实例对象, 构造函数.prototype)
Navigator
function analyzeNavigatorGetters() {
const proto = Navigator.prototype;
const descriptors = Object.getOwnPropertyDescriptors(proto);
const analysis = {
getters: {},
regularProperties: {}, // 添加常规属性收集
summary: {
totalProperties: 0,
getterProperties: 0,
regularProperties: 0,
errors: []
}
};
for (const [key, descriptor] of Object.entries(descriptors)) {
analysis.summary.totalProperties++;
if (descriptor.get) {
// 处理getter属性
analysis.summary.getterProperties++;
try {
analysis.getters[key] = {
value: descriptor.get.call(navigator),
enumerable: descriptor.enumerable,
configurable: descriptor.configurable,
hasGetter: true,
hasSetter: !!descriptor.set,
getterFunction: descriptor.get,
descriptor: descriptor
};
} catch (e) {
analysis.summary.errors.push(`${key}: ${e.message}`);
analysis.getters[key] = {
value: null,
error: e.message,
enumerable: descriptor.enumerable,
configurable: descriptor.configurable,
hasGetter: true,
hasSetter: !!descriptor.set,
getterFunction: descriptor.get,
descriptor: descriptor
};
}
} else {
// 处理常规属性
analysis.summary.regularProperties++;
try {
// 尝试获取属性值
let value = navigator[key];
analysis.regularProperties[key] = {
value: value,
enumerable: descriptor.enumerable,
configurable: descriptor.configurable,
writable: descriptor.writable,
hasGetter: false,
hasSetter: false,
descriptor: descriptor
};
} catch (e) {
analysis.summary.errors.push(`Regular property ${key}: ${e.message}`);
analysis.regularProperties[key] = {
value: null,
error: e.message,
enumerable: descriptor.enumerable,
configurable: descriptor.configurable,
writable: descriptor.writable,
hasGetter: false,
hasSetter: false,
descriptor: descriptor
};
}
}
}
return analysis;
}
// 使用
const analysis = analyzeNavigatorGetters();
console.log('分析结果:', analysis);
console.log('总属性数:', analysis.summary.totalProperties);
console.log('Getter 属性数:', analysis.summary.getterProperties);
console.log('常规属性数:', analysis.summary.regularProperties);
console.log('常规属性:', analysis.regularProperties);
console.log('错误:', analysis.summary.errors);
拿到toStirng
const desc = Object.getOwnPropertyDescriptor(window, 'location');
console.log(desc.get.toString());
待测试 toStirng保护
(() => {
'use strict';
// 取原型链上的toString
const $toString = Function.toString;
// 取方法名 symbol
const myFunction_toString_symbol = Symbol('('.concat('', ')_', (Math.random() + '').toString(36)));
const myToString = function () {
return typeof this == 'function' && this[myFunction_toString_symbol] || $toString.call(this);
};
function set_native(func, key, value) {
Object.defineProperty(func, key, {
"enumerable": false,
"configurable": true,
"writable": true,
"value": value
})
}
// 保护 getter/setter 的函数
function protect_getter_setter(obj, prop, customNames = {}) {
const descriptor = Object.getOwnPropertyDescriptor(obj, prop);
if (!descriptor) return;
// 保护 getter
if (descriptor.get) {
const propName = customNames.getter || prop;
const getterStr = `function get ${propName}() { [native code] }`;
set_native(descriptor.get, myFunction_toString_symbol, getterStr);
}
// 保护 setter
if (descriptor.set) {
const propName = customNames.setter || prop;
const setterStr = `function set ${propName}() { [native code] }`;
set_native(descriptor.set, myFunction_toString_symbol, setterStr);
}
}
// 批量保护对象的所有 getter/setter
function protect_all_getters_setters(obj, customNames = {}) {
const descriptors = Object.getOwnPropertyDescriptors(obj);
for (const [prop, desc] of Object.entries(descriptors)) {
if (desc.get || desc.set) {
const names = customNames[prop] || {};
protect_getter_setter(obj, prop, names);
}
}
}
delete Function.prototype['toString'];
set_native(Function.prototype, "toString", myToString);
set_native(Function.prototype.toString, myFunction_toString_symbol, "function toString() { [native code] }");
// 扩展的全局函数
globalThis.safefunction = (func, name) => {
const funcName = name || func.name || '';
set_native(func, myFunction_toString_symbol, `function ${funcName}() { [native code] }`);
};
// 保护 getter/setter
globalThis.safeGetter = (obj, prop, customNames = {}) => {
protect_getter_setter(obj, prop, customNames);
};
// 批量保护
globalThis.safeAllGetters = (obj, customNames) => {
protect_all_getters_setters(obj, customNames);
};
// 创建安全的属性定义
globalThis.safeDefineProperty = (obj, prop, descriptor, customNames = {}) => {
Object.defineProperty(obj, prop, descriptor);
if (descriptor.get) {
const propName = customNames.getter || prop;
const getterStr = `function get ${propName}() { [native code] }`;
set_native(descriptor.get, myFunction_toString_symbol, getterStr);
}
if (descriptor.set) {
const propName = customNames.setter || prop;
const setterStr = `function set ${propName}() { [native code] }`;
set_native(descriptor.set, myFunction_toString_symbol, setterStr);
}
};
}).call(globalThis);
提示:
// 在控制台创建一个 div 元素
const div = document.createElement('div');
div.className = 'foo bar baz'; // 给它设置一些类名
console.log(div.classList); // 这会返回一个 DOMTokenList 对象
参数:
{
"_method": "GET",
"_url": "/aweme/v1/web/solution/resource/list/?spot_keys=7359502129541449780_douyin_spec_theme&app_id=6383&update_version_code=170400&pc_client_type=1&pc_libra_divert=Mac&support_h265=1&support_dash=0&cpu_core_num=4&version_code=170400&version_name=17.4.0&cookie_enabled=true&screen_width=1440&screen_height=900&browser_language=zh-CN&browser_platform=MacIntel&browser_name=Chrome&browser_version=137.0.0.0&browser_online=true&engine_name=Blink&engine_version=137.0.0.0&os_name=Mac+OS&os_version=10.15.7&device_memory=8&platform=PC&downlink=10&effective_type=4g&round_trip_time=50&webid=7517282755928573449&uifid=163eead721bc91ca6f3a3cb4766a73c0638fabced1012e02c28bcbf3169aca5c44c51fa6beff8f1f2615be624638d06d91c6d0ff5636b21eb7b8a473d4d2e7e8aeb97f1b374c801db9850467ee095467",
"_vc_status": 1,
"_vc_actionList": [
{
"fnName": "open",
"arguments": {
"0": "GET",
"1": "/aweme/v1/web/solution/resource/list/?spot_keys=7359502129541449780_douyin_spec_theme&app_id=6383&update_version_code=170400&pc_client_type=1&pc_libra_divert=Mac&support_h265=1&support_dash=0&cpu_core_num=4&version_code=170400&version_name=17.4.0&cookie_enabled=true&screen_width=1440&screen_height=900&browser_language=zh-CN&browser_platform=MacIntel&browser_name=Chrome&browser_version=137.0.0.0&browser_online=true&engine_name=Blink&engine_version=137.0.0.0&os_name=Mac+OS&os_version=10.15.7&device_memory=8&platform=PC&downlink=10&effective_type=4g&round_trip_time=50&webid=7517282755928573449&uifid=163eead721bc91ca6f3a3cb4766a73c0638fabced1012e02c28bcbf3169aca5c44c51fa6beff8f1f2615be624638d06d91c6d0ff5636b21eb7b8a473d4d2e7e8aeb97f1b374c801db9850467ee095467",
"2": true
}
},
{
"fnName": "setRequestHeader",
"arguments": {
"0": "Accept",
"1": "application/json, text/plain, /"
}
},
{
"fnName": "setRequestHeader",
"arguments": {
"0": "uifid",
"1": "163eead721bc91ca6f3a3cb4766a73c0638fabced1012e02c28bcbf3169aca5c44c51fa6beff8f1f2615be624638d06d91c6d0ff5636b21eb7b8a473d4d2e7e8aeb97f1b374c801db9850467ee095467"
}
},
{
"fnName": "send",
"arguments": {
"0": null
}
}
],
"_reqHeaders": {
"Accept": "application/json, text/plain, /",
"uifid": "163eead721bc91ca6f3a3cb4766a73c0638fabced1012e02c28bcbf3169aca5c44c51fa6beff8f1f2615be624638d06d91c6d0ff5636b21eb7b8a473d4d2e7e8aeb97f1b374c801db9850467ee095467"
},
"_start": 1750254595745,
"_data": null,
"bdmsInvokeList": [
{
"args": [
"GET",
"/aweme/v1/web/solution/resource/list/?spot_keys=7359502129541449780_douyin_spec_theme&app_id=6383&update_version_code=170400&pc_client_type=1&pc_libra_divert=Mac&support_h265=1&support_dash=0&cpu_core_num=4&version_code=170400&version_name=17.4.0&cookie_enabled=true&screen_width=1440&screen_height=900&browser_language=zh-CN&browser_platform=MacIntel&browser_name=Chrome&browser_version=137.0.0.0&browser_online=true&engine_name=Blink&engine_version=137.0.0.0&os_name=Mac+OS&os_version=10.15.7&device_memory=8&platform=PC&downlink=10&effective_type=4g&round_trip_time=50&webid=7517282755928573449&uifid=163eead721bc91ca6f3a3cb4766a73c0638fabced1012e02c28bcbf3169aca5c44c51fa6beff8f1f2615be624638d06d91c6d0ff5636b21eb7b8a473d4d2e7e8aeb97f1b374c801db9850467ee095467&verifyFp=verify_mc1zx67l_pnrg8Q4K_uzh0_4Oms_BLHt_TpjFC31zVsH2&fp=verify_mc1zx67l_pnrg8Q4K_uzh0_4Oms_BLHt_TpjFC31zVsH2",
true
]
},
{
"args": [
"Accept",
"application/json, text/plain, /"
]
},
{
"args": [
"uifid",
"163eead721bc91ca6f3a3cb4766a73c0638fabced1012e02c28bcbf3169aca5c44c51fa6beff8f1f2615be624638d06d91c6d0ff5636b21eb7b8a473d4d2e7e8aeb97f1b374c801db9850467ee095467"
]
}
]
}
[null]
提醒:
t.origin.apply(e, u);
m._url.indexOf("/aweme/v1/web/aweme/post") !==-1
e._url.indexOf("/aweme/v1/web/aweme/post") !==-1
e._vc_actionList[0].arguments[1].indexOf('v1/web/aweme/post')!==-1
m._tnc_request_url.indexOf("/aweme/v1/web/aweme/post") !==-1
e._cmp_data.url.indexOf("aweme/v1/web/aweme/post") !==-1

浙公网安备 33010602011771号