最新老婆准备找工作,找了一些js的面试题,有些不懂,其中有一个问啥是原型啥是原型链, 直接把我问懵逼了,接触js这么多年, 没有真正了解过某种用法或者技术的学名是啥。一帮所谓学者还真是取了漂亮名。 为此百度了一番,大部分博客写的一般般, 太过理论,我来点实际的
定一个类目前有两种方式:
函数式:
function A(){ this.a = 1; }
A.log = function (){}
A.prototype.log = function(){}
// 我刻意写成一样,都叫log
类式:
class A {
constructor() {this.a = 1;}
static log() {}
log() {}
}
第二种方式其实是第一种方式的语法糖,只是写法不同,类底层逻辑的其实还是第一种, 但是没有人证明过, 我决定证明一下:
两者类的定义方式我交换了顺序,看上面的图里错误信息堆栈一摸一样,编译器碰到class A, 使用compileFunction去编译的, 这一点就足以证明class写法是第一种的漂亮写法。
还没结束,再看这个样例:
通过这个例子,通过class 写的类,必须要使用new。 可以证明,class A也就仅仅只是function A 定义类的方式一种漂亮写法, 并不完全等价。事实上仅仅是在实现类方面一样,但是function并不仅限类的实现
再看这个样例:
function Animal(){
this.name = '动物';
this.speek = function (){
console.log('你是畜生');
}
return this;
}
Animal.speek = function(){
console.log('我是畜生-通过类名调用');
}
Animal.prototype.speek = function(){
console.log('我是畜生-通过实例调用');
}
new Animal().speek(); // 你是畜生
Animal().speek(); // 你是畜生
Animal.speek(); // 我是畜生-通过类名调用
Animal().name; // 动物
Animal() === Animal(); // true
Animal()函数里有一个this, 有的人可能不知道this是啥,this其实表示运行的当前环境。 记住一句话,谁在调用this就代表谁。
如果通过new Animal()写的, 这个this就是实例对象,new方式底层构造函数v8调用的
如果是通过Animal()写的,this表示全局对象,因为是在文件根调用的。
如何输出内容 我是畜生-通过类名调用?
要想输出它就必须清楚这个方法存放在哪个object中?
上面的代码其实设计有4种Object
第一种Object是 global
第二种Object是 new Animal()
第三种Object是 Animal.prototype
第四种Object是 Animal
new Animal().speek 没有走原型里的方法,因为它自己定义了speek。
通过对象.方法名去调用的时候,v8有一个查找路径,优先找自己实例的方式,没有就去原型里找,原型里还有原型,一层一层往上找, 直到Object对象为止,这个是一切对象的超类。
我自己定义了speek, 还想使用原型方法, 直接上干货
- 删除自己的定义,然后再调用
const anim = new Animal();
delete anim.speek;
anim.speek();// 我是畜生-通过实例调用
- 直接跳过自己的定义的方法
const anim = new Animal();
anim.__proto__.speek.call(anim); // 我是畜生-通过实例调用
- 以上两种方式,我破解某些app的时候都用过, 知道底层运行原理,就可以随意hook了
比如在原来的函数后输出一句话,你才是畜生
const anim = new Animal();
const speek = anim.__proto__.speek;
anim.__proto__.speek = function(){
speek.call(this);
console.log('你才是畜生');
}
anim.__proto__.speek.call(anim); // 我是畜生-通过实例调用\n你才是畜生
- 现在hook构造函数,之后不管怎么创建对象,都用原型方法一劳永逸
const oldAnimal = Animal;
Animal = function(){// hook
delete oldAnimal.call(this).speek;
return this;
}
Animal.prototype = oldAnimal.prototype;
const ani2 = new Animal();
ani2.speek();
- 超炫酷玩js
// 修改所有类的顶头上司
Object.prototype.log = function(...args){
console.log(`${this.name}`, ...args);
}
// 任何log方法, 都走到了超类, 这就是所谓的原型链查找
new Animal().log('你才是畜生'); // 动物 你是畜生
[].log('你才是畜生'); // undefined 你才是畜生
({}).log('你才是畜生'); // undefined 你才是畜生
this.log('你才是畜生'); // undefined 你才是畜生
- 输出任何对象的属性和方法, 包括不可遍历属性
function getKeys(obj){
let keys = {};
function set(args){
args.forEach((item) => {
keys[item] = 1;
});
}
function _(obj){
if(!obj ){
return;
}
set(Object.getOwnPropertyNames(obj))
if(obj.__proto__){
set(Object.getOwnPropertyNames(obj.__proto__));
_(obj.__proto__)
}
}
_(obj);
return Object.keys(keys);
}
-
js set get hook
https://www.cnblogs.com/dzqdzq/p/17031252.html -
闭包hook, 理论无法hook原始闭包里的局部变量,下面的代码hook,相当于重新创建了一个闭包。
有点脱了裤子放屁的感觉,因为完全可以直接hook,而不是使用proxy
function counter() {
var count = 0;
return function() {
return ++count;
}
}
function hookCounter() {
const handler = {
apply: function(target, thisArg, argumentsList) {
eval(target.toString().replace("++count;", "(count = arguments[0]), count"));
return counter.apply(thisArg, argumentsList);
},
};
return new Proxy(counter, handler);;
}
counter = hookCounter();
var count = counter();
console.log(count(5)); // 输出 5
console.log(count(6)); // 输出 6
console.log(count(7)); // 输出 7
console.log(counter()(10)); // 输出 10
标签:function,log,畜生,感想,js,hook,speek,Animal,new
From: https://www.cnblogs.com/dzqdzq/p/17658810.html