ZBLOG

使用Proxy监听window.location的调用次数

由于 window.location 是一个特殊的只读对象,直接使用 Proxy 代理它会抛出错误(如 TypeError: Cannot create proxy with a non-configurable property)。但我们可以通过 间接监听 的方式统计它的访问次数。以下是几种实现方案:


方法1:通过 Object.defineProperty 劫持部分属性

虽然无法代理整个 location,但可以劫持它的某些属性(如 href)的 getter/setter:

let accessCount = 0;

const originalHref = Object.getOwnPropertyDescriptor(window.location, 'href').get;

Object.defineProperty(window.location, 'href', {
    get: function() {
        accessCount++;
        console.log(`location.href accessed (count: ${accessCount})`);
        return originalHref.call(window.location);
    },
    configurable: true  // 必须设置为 true
});

// 测试
console.log(window.location.href); // 触发计数

限制:只能监听已明确劫持的属性(如 hrefhash等)。


方法2:包装 window.location 的引用

通过中间变量间接监听:

let accessCount = 0;
const wrappedLocation = new Proxy({}, {
    get(target, prop) {
        accessCount++;
        console.log(`location.${prop} accessed (count: ${accessCount})`);
        return window.location[prop]; // 返回真实值
    }
});

// 重定向全局引用(需确保在目标代码之前执行)
window._originalLocation = window.location;
window.location = wrappedLocation;

// 测试
console.log(window.location.href); // 触发计数

注意:此方法可能被严格模式或某些安全策略阻止。


方法3:AST + Hook(推荐用于生产环境)

如果是自己的项目,可以通过 Babel/Webpack 插件在编译时注入统计代码:

// Babel插件示例:将所有 `window.location.x` 调用替换为统计函数
function replaceLocationAccess(babel) {
    const { types: t } = babel;
    return {
        visitor: {
            MemberExpression(path) {
                if (path.get('object').matchesPattern('window.location')) {
                    path.replaceWith(
                        t.callExpression(
                            t.identifier('__countLocationAccess'),
                            [t.stringLiteral(path.node.property.name)]
                        )
                    );
                }
            }
        }
    };
}

// 全局统计函数
window.__countLocationAccess = (prop) => {
    console.log(`location.${prop} accessed`);
    return window.location[prop];
};

方法4:Chrome DevTools Overrides

  1. 保存 Overrides

    • Chrome DevTools → Sources → Overrides
    • 复制页面脚本,手动添加统计代码后保存。
  2. 示例修改

// Find all location accesses in the original script and wrap them:
const originalCode = 'console.log(window.location.href);';
const instrumentedCode = originalCode.replace(
    /(window\.location\.[a-zA-Z]+)/g,
    '(window.__countAccess($1), $1)'
);

// Inject counter:
window.__accessCounts = {};
window.__countAccess = (prop) => {
    window.__accessCounts[prop] = (window.__accessCounts[prop] || 0) + 1;
};

注意事项

  1. 执行时机:所有 Hook 必须在目标脚本访问 location 之前执行。
  2. 沙箱环境:某些网站使用 iframe sandboxing,会阻止属性修改。
  3. 备选方案:如果目标是调试而非生产代码,直接用 DevTools 的 Event Listener Breakpoints (监听 hashchange/popstate)更可靠。

如果需要针对具体场景优化方案,可以提供更多细节(如是否允许修改原始代码、目标运行环境等)。


内容由零声教学AI助手提供,问题来源于学员提问

本站部分文章来源于网络,版权归原作者所有,如有侵权请联系站长删除。
转载请注明出处:https://golang.0voice.com/?id=24053

分享:
扫描分享到社交APP
上一篇
下一篇
发表列表
游客 游客
此处应有掌声~
评论列表

还没有评论,快来说点什么吧~

联系我们

在线咨询: 点击这里给我发消息

微信号:3007537140

上班时间: 10:30-22:30

关注我们
x

注册

已经有帐号?