关于javascript中的原型和原型链

关于javascript中的原型和原型链

我GitHub上的菜鸟仓库地址: 点击跳转查看其余相关文章
文章在个人博客上的地址: 点击跳转

        关于javascript中的原型和原型链,可能都会想到一个词“prototype”,而实际里面藏的是什么东西,才是你们应该要掌握的。javascript

        看过一些文章,将原型和原型链说得很枯燥难懂,其实抽丝剥茧以后,理顺思路,其实原型和原型链没有想象中的那么难以理解。我一直崇尚的是类比生活去理解,因此我的仍是不太喜欢纯叙述性的解释。java

        其实不少讲解的人,都是从自身角度出发的,解释的都是理所固然的,他们没法感觉咱们这些菜鸟的角度,不知道咱们不少个为何。固然,当咱们了解理解以后,再从新看他们的文章,说的也是头头是道的。git

        关于原型这个词,其实很好理解,能够说成是“原来的模型”。好比说,“儿子长得就像是爸爸一个模子出来同样”,那爸爸就是儿子的原型,儿子继承了爸爸的一些特征,固然,儿子也会有本身的特征,这些特征,就是属性。而有时候儿子有些特征没有,能够在儿子的爸爸那里找到,甚至儿子爸爸那里找不到的特征,能够在爸爸的爸爸那里找到,而彼此之间维系着的,就是血缘关系,DNA传递,而这个关系链,就是咱们说的原型链,固然,往上找祖先,找到最后确定是炎帝黄帝了,他们就是人类始祖了,若是他们身上还找不到,再往上找,就是空了,由于往上就没有祖先了,原本无一物,何处惹尘埃。github

        好了,开始来代码了。网络

        先来一个构造函数:函数

//构造一我的类
function Mankind(name){
    this.name = name;
}

//实例化一个Dad对象
var Dad = new Mankind('BaBa');

//看看Dad的名字是什么
console.log(Dad.name);

//打印结果
BaBa

        先说一个前提:this

        只要是函数,就会有一个 prototype 属性,能够理解为子代的原型(遗传基因);只要是对象,就会有一个__proto__方法,能够理解为向上寻找原型的方法。spa

        因此上面的构造函数中,Mankind这个构造函数,就会有一个prototype属性(不是函数没有),能够这样访问:Mankind.prototype,固然也能够给传统基因添加其余特征:prototype

//仍是上面的构造函数
function Mankind(name){
    this.name = name;
}

//仍是实例化一个Dad对象
var Dad = new Mankind('BaBa');

//而后给构造函数添加特征
Mankind.prototype.sayHello = 'HaHaHa';

//看看Dad有没有sayHello特征
console.log(Dad.sayHello);

//打印结果
HaHaHa

        从结果能够看出,Dad原本没有的sayHello特征,你给Dad的祖先添加了,Dad也会拥有这个特征了,其实这就是从原型链上找到这个属性了。code

        Dad对象这个实例的原型,就是Mankind.prototype这个遗传基因。

        而向上找原型,就是经过__proto__这个方法,因此:

Dad.__proto__ === Mankind.prototype  //true

        固然,Mankind.prototype也是一个对象,固然也有一个__proto__方法,经过这个方法,也是能够找到他再上一级的原型,因此:

Mankind.prototype.__proto__ === Object.prototype //true

        这也是对的。由于函数的祖先是Object,因此就是指向Object.prototype这个原型 。

固然,再往上找,就是空了。
Object.prototype.__proto__  === null  //true

        因此各个原型组织起来,就是一条原型链了:

        Dad ---> Mankind.prototype ---> Object.prototype ---> null   能够看到从对象开始的原型链的规律

        回过头来,其实Mankind.prototype这个对象除了__proto__这个方法外,还有一个constructor的方法,由于Mankind是函数,因此有这个方法,因此经过这个方法,能够访问到自身这个函数:

//打印一下Mankind.prototype.constructor
console.log(Mankind.prototype.constructor);

//打印结果
function Mankind(name){
    this.name = name;
}

        说到这里,相信已经类比得很清楚了。而后又会有一个疑问:

        既然说函数是对象(函数对象Function,普通对象Object,Function是继承于Object的),那么前面的构造函数Mankind能够有prototype属性,也应该有__proto__这个方法?

        没错,因此咱们也能够有Mankind.__proto__这个方法访问原型:

Mankind.__proto__ === Function.prototype  //true

        固然,Function.prototype 也是能够经过__proto__方法访问原型:

Function.prototype.__proto__ === Object.prototype //true

        因此也有这样的原型链:

        Mankind ---> Function.prototype ---> Object.prototype ---> null   能够看到从函数开始的原型链的规律

        固然了,咱们既然有一个实例的对象Dad,固然也能够再延生下去,生一个Son来继承Dad的啦:

//从Dad那里继承,建立一个son对象,下面两种方法均可以:
var Son = new Object(Dad);
var Son = Object.create(Dad);

//修改一下儿子的name
Son.name = 'ErZi';

//打印一下儿子的name和原型链上父亲的name
console.log(Son.name);
console.log(Son.__proto__.name);//经过__proto__方法找到父亲Dad

//打印结果
ErZi
BaBa

        因此这条原型链是这样的:

        Son ---> Dad ---> Mankind.prototype ---> Object.prototype ---> null   对照从对象开始的原型链的规律

        经过上面的一大顿啰嗦,相信已经很清楚了,最后再说一下鸡和鸡蛋的问题:

        上面既然说到有Object.prototype,并且prototype是函数才有的,因此能够访问到Object这个构造函数,能够用Object.prototype.constructor这个方法,固然构造函数是继承于函数对象的,因此构造函数原型又是Function.prototype,因此也有这样的一条原型链:

        Object ---> Function.prototype ---> Object.prototype ---> null   对照从函数开始的原型链的规律(这里的Object是构造函数)

        或者表示为:

        Object.prototype.constructor---> Function.prototype ---> Object.prototype ---> null

        这就是鸡和鸡蛋的问题了。

        最最后,放上一张网络上解释很清楚的原型链图,再结合我上面的啰嗦,相信就很清楚容易明白了。
        952839-20160807171533231-172025675.png

相关文章
相关标签/搜索