前文
超星更新后的网页
这次在beforesetup和setup的钩都给强制return了
导致无法再次使用钩子
所以我观察源码搞了一个极限注入的操作
本质上学习观察源码的过程也是提升自我的一个过程
只要忍过第一个阵痛期,后面其实能看懂源码很爽的
很多时候你比只会看文档的程序员更了解执行原理!
开始
之前的hooks已经失效了,那我们在初始化阶段几乎没有什么可以插手的地方了
hooks('beforesetup').forEach(function (hookFunction) {
var opts = hookFunction(el, mergeOptions$3(options));
if (!isObject$1(opts) || Array.isArray(opts)) {
log$1.error('please return an object in beforesetup hooks');
return;
}
options = mergeOptions$3(options, opts);
}); // We get the current "Player" component here in case an integration has
// replaced it with a custom player.
var PlayerComponent = Component$1.getComponent('Player');
player = new PlayerComponent(el, options, ready);
hooks('setup').forEach(function (hookFunction) {
return hookFunction(player);
});
hooks钩的地方可以全部排除了
那我们唯一能下手的地方就是更核心的地方
也就是Component$1.getComponent函数
我们先观察一下他的源码
Component.getComponent = function getComponent(name) {
if (!name || !Component.components_) {
return;
}
return Component.components_[name];
};
这里判断是否为空,或者保存组件的位置是否为空,如果都不为空,则返回对应的名字
也就是说Player保存在Component.components_['Player']中
那我们需要设置的,就是对components_进行设置
全局搜索Component.components_以及查阅官方文档
可以找到这里
查阅官方文档可以发现,这里是注册组件的地方
那么思路来了,我们可不可以注册Player函数?
阅读源码开始!
if (typeof name !== 'string' || !name) {
throw new Error("Illegal component name, \"" + name + "\"; must be a non-empty string.");
}
判断名字异常,可以跳过
var Tech = Component.getComponent('Tech'); // We need to make sure this check is only done if Tech has been registered.
var isTech = Tech && Tech.isTech(ComponentToRegister);
var isComp = Component === ComponentToRegister || Component.prototype.isPrototypeOf(ComponentToRegister.prototype);
if (isTech || !isComp) {
var reason;
if (isTech) {
reason = 'techs must be registered using Tech.registerTech()';
} else {
reason = 'must be a Component subclass';
}
throw new Error("Illegal component, \"" + name + "\"; " + reason + ".");
}
这里通过原型链判断是否继承与Comp组件或者继承于Tech组件
name = toTitleCase$1(name);
if (!Component.components_) {
Component.components_ = {};
}
name函数经过某些处理,然后对components_如果不存在则进行初始化
var Player = Component.getComponent('Player');
获取Player组件
if (name === 'Player' && Player && Player.players) {
var players = Player.players;
var playerNames = Object.keys(players); // If we have players that were disposed, then their name will still be
// in Players.players. So, we must loop through and verify that the value
// for each item is not null. This allows registration of the Player component
// after all players have been disposed or before any were created.
if (players && playerNames.length > 0 && playerNames.map(function (pname) {
return players[pname];
}).every(Boolean)) {
throw new Error('Can not register Player component after player has been created.');
}
}
这里判断了Player地方进行了处理,可以看到首先判断名字是否是Player
然后判断Player是否为空,如果不为空,则继续判断players是否存在
三者都存在的时候才禁止替换Player组件
也就是说我们可以对其进行组件替换
这里可以看出来Vidoejs的设计非常优秀,对大部分的功能做了一个组件化的抽离和替换。
Component.components_[name] = ComponentToRegister;
Component.components_[toLowerCase(name)] = ComponentToRegister;
return ComponentToRegister;
然后非常简单,对其组件进行赋值了
理论建立完毕,开始实战
let OriginPlayer = _videojs.getComponent('Player')
let woailiyinhe=function(tag, options, ready){这里做原OriginPlayer的对象生成,返回,处理}
woailiyinhe.prototype=Object.create(OriginPlayer.prototype)
videojs.registerComponent('Player',woailiyinhe)
这里因为是tm脚本,所以我直接上了一个prototype替换,按道理其实官方更推荐用class类,但是那就要做语法转换了,所以hook偷了个懒
劫持完毕之后我们就可以完美的劫持Player初始化前后的操作了!
结语
其实阅读源码对个人提升帮助真的很大,所以偶尔会带领大家一起拆解一些流行的框架或者库的源码
至于给我爱李银河改成我爱破解,真的蛮无聊的
写李银河只是因为我个人非常崇尚王小波
我经常翻读王小波的著作,其中面朝大海,春暖花开那一段非常经典,
我看了不下十遍,至今仍能倒背如流,所以建议如果不懂李银河瞎改建议先提高文学素养
我认为写代码不仅仅是写代码,也是一个表达自我精神的一个机会
代码上的喜好能直接体现出人的性格,希望大家写出属于自己的代码
李银河
李银河(1952年2月4日-),女,北京人,中国大陆社会学家、社会活动家,性学家、LGBT权利活动家,已故中国当代作家王小波的遗孀。