js 原型链、__proto__、prototype
本文由用途意义,进行脑测解析,从需求角度走一遍原型链的发展。
用对象模拟类的继承
js中没有类(没有类,没有类,重要的事情说3遍)只有对象,怎么才能做到继承的效果?
var a={x:1}
var b={};
b.__proto__=a;
接下来进行约定,当访问b.x
但不存在时,就自动去访问b.__proto__.x
。
逻辑上就这么一回事。不过需要注意,这里说的只是访问。b.x=2
这种是无法对a,也就是b.__proto__
造成影响的;同时这个为b赋予了x属性,b.x
将覆盖掉b.__proto__.x
。
通俗点总结,就是给对象挂一个父对象,当对象没有相应属性时,就去它父对象那里找。
__proto__指向构造函数的prototype
var A=function(){ }
var b = new A();
这个时候又该怎样用__proto__实现继承效果?
首先函数也是一个对象,除了A();
这样以函数调用,还能A.x=1;
这样把A当普通对象使用(下文中函数、函数对象,都是一回事)。知道这个后实现继承很简单,增加一个属性即可:
A.prototype={constructor: A};//提醒一下怕忘记了,这里相当于A增加了属性prototype
A.prototype.x=1;
b.__proto__ = A.prototype;
js会为每个这样new出来的对象做这个处理,自己不用写。
显然,这里的A.prototype
就类似与一开始例子中的a
,往A.prototype增加属性,那么所有new A()
出来的对象都能访问到这个新的属性。
为什么js要默认增加constructor: A
这个属性到prototype中?这里与new操作符有关,是为了解决另一个问题。另外常说的prototype的构造函数,就是指这个。
总结:
反正就是通过new 函数()
这样出来的对象,其__proto__默认指向构造函数(这里说的是函数对象本身,它跟在new后面也被称作构造函数)的prototype属性。
实际上我觉得一般不以这个方法进行有大量属性的继承,一是查找有无属性的效率问题,二是new时构造方法把this改为新建对象的指向就足以完成属性的添加和赋值,无需操作prototype进行继承。
值得注意的要点
__proto__、prototype是什么一回事相信已经了解,剩下的就是经常把人绕晕的Object.prototype、Function.prototype这些东西了。
先提一下,Object
、Function
都是一个函数对象,跟上面的A
差不多,既能new Object()
也能Object.xxx
这样用。
Object:
1.所以var b=new Object();
后,b.__proto__===Object.prototype
这个应该没有什么疑问。
2.新的标准中可用b=Object.creat(a)
,可当作是b=new Object(); b.__proto__=a
,还是这套操作,问题不大。
3.如果是var b={}
这种直接通过字面量创建对象,js会自动进行b.__proto__=Object.prototype
,知道后问题也不大。
4.就是默认情况下,你不手动搞__proto__、prototype的指向,最终__proto__都会去指向Object.prototype。
5.Object.prototype
本质上跟前面示例中的prototype没什么不同,只是这个prototype会被js自动增加一些属性
6.Object.prototype.__proto__===null
跟在c++/java中遍历链表一样,当__proto__为null时说明到头了。当作js自动设置上去的就行,没什么其他特殊
7.Object.__proto__===Function.prototype
下面再讲。
Function:
1.所有的函数对象都是Function的实例。
不是说没有类吗,这个实例又是什么意思?
emmm,习惯说法而已,具体什么操作没研究也不懂,或许当作js自动这样做:
var f=function(){}
f.__proto__=Function.prototype;
//或者这样理解
var f=new Function();
然后把你的代码放进去f
2.Function.__proto__===Function.prototype;
前面提过Object、Function都是一个函数对象,把它们代入第1点例子的f
就行。上面第6点同理。
按照理解,可能会这样的疑问:Function.__proto__指向构造函数(再次提醒,是一个对象)的prototype,所以Function这个对象的构造函数是它自己?自己创建自己么?
额。。。我觉得这种操作可能就是为了统一,只要记住“所有函数对象的__proto__都是Function.prototype”就行。
3.Function.prototype.__proto__===Object.prototype;
没什么特别的,把Function.prototype
代入上面Object第1点的b
就行,不要特殊看待,硬要说与b
不同的话,只是因为“函数对象的prototype默认会被增加一个constructor属性”而已,没什么大问题
总结:
1.我们在js中说的“类型”,可以说只是习惯用语,实际上仍旧是没有所谓的类概念的,只有用对象来“模拟类”。
2.prototype就是指向一个普通对象prototype=new Object()
,只不过这个对象被添加其他一些属性后,再被自动放到到函数对象的属性中。
3.__proto__就是 原型/原型对象,不断的找原型的原型最终到--->Object.prototype--->Object.prototype.__proto__ (null)。原型对象可能是一个普通的对象;也可能是js自动放入到函数对象的prototype
4.我觉得js这套东西根本目的是批量为对象赋予属性同时减少代码冗余,上面解析的Object
、Function
、例子中的A
都只是函数对象,不是像java一样的类,平时说对象的类型只是方便日常交流,instanceof也只是很粗暴的递归比较对象__proto__ === 函数对象.prototype
这种。把类的概念丢掉,保留对象、属性这概念,从这出发又回头去实现“类”、“继承”这东西,就出来这么套东西。