原型和原型链的抽筋拔骨

755 阅读5分钟

一丶前言

最近的学习中总是时不时的遇到原型相关问题,不胜困扰,于是决定学学哪吒闹海,对“东海三太子”进行抽筋拔骨。希望你在看完本文之后感受是这样的:


二丶我有故事,君有酒否?

世人皆爱听故事,在听各种之乎者也之前不如容小生给大家讲个故事。在很久很久以前,js是孤独的,他没爹没妈,没妻没子。js转念一想,总不能叫孤儿吧,于是美其名曰:“null先生”。有一天,null先生捡到了一只鸡和一枚鸡蛋,很是高兴,给鸡蛋取名为Object由于他先看到鸡后看到蛋,于是他认为鸡是属于他的,这只鸡呢又是由鸡蛋孵化的,为了方便理清这层关系,他使用prototype__proto__来进行区分,并规定:

Object.prototype = 鸡;
鸡.__proto__ = null;

以图示之:


null先生并不安分,他认为鸡给他带来了很多乐趣,他得回报啊,于是他动手为鸡造了一个鸡窝,并规定,这个鸡窝是给鸡的,于是:

鸡窝.__proto__ = 鸡;

以图示之:


自从有了这个鸡窝,null先生从此和鸡过上了没羞没臊的生活。。。。。。额,不是,是有了这个鸡窝,鸡居然生小鸡了,但凡这只鸡走进鸡窝,没多久就能从里面产出小鸡来,amazing!null先生想这肯定是这个鸡窝的功劳,兴奋之余,他发现虽然都是鸡,但颜色,大小等都各有差异,大家都是鸡,你装什么高贵。null先生虽然这么想,但为了区分还是根据它们的特性进行命名:StringNumberBooleanArray。。。。。。这时null先生再望向那枚起始的鸡蛋,一拍大腿,鸡生蛋,蛋生鸡,这枚鸡蛋(Object)也可以归属到鸡窝才是!

Object.__proto__ = 鸡窝;

null先生以为日子总该平静了吧,但鸡就是鸡,一天不搞事浑身难受,有一种浑身彩色的小鸡我们简称为彩鸡,它干了什么呢?它居然可以自己造窝,不仅如此,它造出来的鸡窝和null先生造的鸡窝功能是一样的,也能产小鸡,null先生不禁叹服:好你个彩鸡,居然有如此之本领,这般放荡,要克制自己的本性才好。于是给这种彩鸡取名为“放克性”,彩鸡不服,兄弟姐妹们都是英文名凭啥我是中文名,便自我改名Function。有了这层关系,我们大胆得出结论:

String.__proto__ = 鸡窝;
Number.__proto__ = 鸡窝;
...
Function.__proto__ = 鸡窝;
Function.prototype = 鸡窝;

以图示之:


啊~null先生大叹一声,这日子什么时候是个头啊!其实,这才刚刚开始。。。。。。

好了,故事讲完了,根据以上信息我们得出以下结论:

Object.__proto__ === Function.prototype;
Function.prototype.__proto__ === Object.prototype;
Object.prototype.__proto__ === null;

三丶给对象划分三六九等

我们常说“万物皆对象”是因为javascript任何值或变量都是对象,但是对象也可以划分为三六九等。

公爵——Object

Javascript中所有的对象都是Object的实例,并继承Object.prototype的属性和方法,所有的对象都是间接或直接的通过Object衍生的。这个大哥Object当之无愧。

侯爵——Function

当我们定义一个对象时,可以用var obj = new Object(),我们都知道new是用来实例化一个对象的方法,但是我们都知道javascript中不存在真正的类的概念,所以只能用函数来模拟,那么既然可以有上面的做法也就印证了Object也是特殊的函数。  我们回到刚才的故事,Object是蛋,Function是鸡,那你觉得是鸡生蛋还是蛋生鸡呢?成年人没空思考,小孩又想不出答案。。。。。。

伯爵——String,Number,Boolean,Array...

子爵——其他

四丶原型和原型链

讲了这么多,那么到底何为原型,何为原型链呢???

4.1 原型

每个函数都有prototype(原型)属性,这个属性是一个指针,指向一个对象,这个对象的用途是包含特定类型的所有实例共享的属性和方法,我们称这种对象为原型对象,即这个原型对象是用来给实例共享属性和方法的。也就是说Function.prototype = 原型对象,现在再想想那群鸡和鸡窝是不是恍然大悟,鸡窝原来就是原型对象!

既然函数这么特殊,那么我们在此将对象分为函数对象普通对象,函数对象是通过使用function定义的对象,而普通对象是通过使用new操作符生成的对象。函数对象有一个prototype属性,普通对象就没有这个prototype属性,存在__proto__。我们来看几个例子:

var obj = new Object();
console.log(obj.__proto__);//Object原型对象
console.log(obj.prototype);//undefined
console.log(Object.prototype);//Object原型对象
console.log(obj.__proto__ === Object.prototype)//true


var Fn = function(){};
var fn = new Fn();
console.log(Fn.prototype);//Object {constructor: function}
console.log(fn.__proto__);//Object {constructor: function}
console.log(fn.__proto__ === Fn.prototype);//true

实际上每个实例内部都有一个指向原型对象的指针。因此:若var a = new A();则a.__proto__=A.prototype

4.2 原型链

谈到原型链就不得不谈到__proto__,上面我们有谈到,每个对象都会在其内部初始化一个属性,就是__proto__当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它就会去__proto__里找这个属性,这个__proto__又会有自己的__proto__,于是就这样 一直找下去,也就是我们平时所说的原型链的概念。举一个构造函数中经常遇到的实例:

var Fn = function(){};
Fn.prototype.sayLove = function(){
    console.log("I love XX");
}
var fn = new Fn();
fn.sayLove();//I love XX;

当我们调用fn.sayLove()时会输出"I love XX",那么fn是如何找到这个方法的呢?首先fn作为实例对象,本身并不存在sayLove()方法,从而它就会去找fn.__proto__,因为fn是Fn的实例,因此fn.__proto__ = Fn.prototype,而我们上边定义了Fn.prototype.sayLove = function(){},因此fn.sayLove()自然也就输出了"I love XX"。

初来贵宝地,有钱的捧个赞场,没钱的点个关注再走。

五丶参考资料

深入理解JavaScript原型链
Object.prototype原型和原型链
JS原型和原型链是什么?
JavaScript 世界万物诞生记