关于原型链,原来这么简单?—————终结__proto__和prototype的那些事
今天,一个技术群里小朋友提出一个问题:
object.prototype.a = function () { console.log('a') } function.prototype.b = function () { console.log('b') } function f(){} var f = new f(); f.a(); f.b(); f.a(); f.b();
我默念心中的万能公式,答案一下就出来了:
a;
报错(f.b is not a function);
a;
b;
这下子出题人产生了疑惑,你是控制台敲出来的吧!
但其实,原型链真的很简单。话不多说,开始表演!
首先,我们简单介绍一下,实例化一个对象(new)到底做了什么?
function base() { this.a = function () { console.log('我是base中的a') } } base.prototype.b = function () { console.log('我是base prototype上的b') } var obj = new base(); // 实际上做了以下几件事 // var obj = {}; // obj.__proto__ = base.prototype; // base.call(obj); // 第一行,我们创建了一个空对象obj // 第二行,我们将这个空对象的__proto__成员指向了base函数对象prototype成员对象 // 第三行,我们将base函数中this上的成员赋值给obj
这就去控制台检验一下:
好,可以开始解题,回顾一下小朋友提出的问题:
object.prototype.a = function () { console.log('a') } function.prototype.b = function () { console.log('b') } function f(){} var f = new f(); f.a();f.b();f.a();f.b();
我们展开f的原型链:
f.__proto__ === f.prototype; // 因为prototype本质也是对象,继承自object,所以f.prototype.__proto__ === object.prototype f.__proto__.__proto__ === object.prototype; // 按上一条,会得出object.prototype.__proto__ = object.prototype,那样会永无止境,因此js中把object.prototype.__proto_直接赋值成null f.__proto__.__proto__.__proto__ === null;
我们会发现,f的原型链中,根本没有function.prototype什么事,所以答案出来了,f.a()输出a,f.b会报错;
我们再展开f的原型链:
f.__proto__ === function.prototype; // 因为prototype本质也是对象,继承自object,所以function.prototype.__proto__ === object.prototype f.__proto__.__proto__ === object.prototype; f.__proto__.__proto__.__proto__ === null;
ok,function.prototype和object.prototype都在原型链上,都会有输出,答案揭晓,f.a()输出a,f.b()输出b;
前文我提到了万能公式,吃瓜群众表示,难道公式就是这个?当然不是,请听我细细道来;
刚才的故事还没结束,我心血来潮回问了小朋友一个问题,就刚才你的条件object.a();object.b();function.a();function.b();会输出什么?
小朋友答曰:a; b; a; b;
不错不错,但他接了一句(他的1和4其实答错了,后续会告诉原因):
1. object.a(); 直接prototype取值;
2. object是function.prototype的实例,可以继承b;
3. function.__proto__ === function.prototype;
4. function.b();直接prototype取值;
???object.a第一步就直接到object.prototype上找?这可能也是大家弄不清原型链的一大问题,话不多说,请摊开object的原型链:
// object是函数,继承自function object.__proto__ === function.prototype; object.__proto__.__proto__ === object.prototype; object.__proto__.__proto__.__proto__ === null;
因此,细心的朋友已经发现他的错误了,object.a其实是访问到object.__proto__.__proto__时才从object.prototype上找到相应的a();
同样展开function的原型链,就不再赘述了:
// function自身也是个函数,因此继承自己 function.__proto__ === function.prototype; function.__proto__.__proto__ === object.prototype; function.__proto__.__proto__.__proto__ === null;
为了引导出万能公式,我再次发出了灵魂拷问:
// 在刚才的前提下 var c = 1; console.a();// a console.b();// console.b is not a function c.a();// a c.b();// c.b is not a function console.log.a();// a console.log.b();// b
这时,学会展开原型链的同学已经明白答案了,我就直接说出我的万能公式:
在js中,万物皆对象,只要在object.prototype上写的方法,万物都能访问到;而function.prototype上的方法,只有能通过()方式调用的函数,才能访问到;
因此我只需要查看这个对象可不可以通过函数()的形式调用,就能确定他是否能访问function.prototype。再次回顾出题人的问题,f仅仅是个对象,f()是会报not is a function错误的,而f()是可以调用的函数,一下子得出 a 报错 a b的答案,学到了是不是感觉自己也棒棒哒~~~
最终cober老师还是给出了自己的杀手锏面试题,毕竟原型链还有另一层面试方法:
object.prototype.a = function () { console.log('我是object中的a') } object.prototype.b = function(){ console.log('我是object中的b') } function.prototype.a = function () { console.log('我是function中的a') } function.prototype.b = function () { console.log('我是function中的b') } function f(){ this.a = function () { console.log('我是f中的a') } } f.prototype.a = function () { console.log('我是f的prototype中的a') } var f = new f(); f.a();f.b();f.a();f.b();object.a();object.b();function.a();function.b();
(提示:原型链调用顺序是f.a -> f.__proto__.a -> f.__proto__.__proto__.a,直到访问到null)