Javascript原型和原型链

JavaScript在ES6之前没有类似class,extend的继承机制,JavaScript的继承主要是通过原型和原型链实现的。

在说原型和原型链之前 ,我们首先要了解以下几个概念

一.私有变量和私有函数

在函数内部定义的变量和函数,如果不对外提供接口,那么外部是无法访问到的。这个被定义的变量和函数就叫做该函数的私有变量和私有函数。

function Foo() {

var name = "yiMu";  //私有变量

var fn = function() {   //私有函数

console.log("hello word");

};

}

var bar = new Foo();

console.log(bar.name);  //undefined

console.log(bar.fn);    //undefined

因为变量name和函数fn都是在Foo函数内部定义局部变量,所以在函数外部新创建的实例无法访问;

二、静态变量和静态函数

当定义一个函数后通过"."的方式为其添加属性和函数,通过对象本身可以访问到,但是其实例却无法访问到,这样的变量和函数叫做静态变量和静态函数。

function Foo(){}

Foo.num = 10;  //静态变量

Foo.fn = function() {   //静态函数

console.log("hello word");

};

console.log(Foo.num);  //10

console.log(typeof Foo.fn); //function

var bar = new Foo();

console.log(bar.num);  //undefined

console.log(typeof bar.fn); //undefined
三、实例属性和实例方法

在面向对象编程中除了一些库函数,我们还是希望在定义一个对象的时候同时定义一些属性和方法并在实例化后能够访问,这些添加的属性和方法就叫做实例属性和实例方法。

function Foo() {

this.num = [];  //实例属性

this.fn = function() {  //实例方法

console.log("hello word");

};

}

console.log(Foo.num);   //undefined

console.log(typeof Foo.fn); //undefined

var bar = new Foo();

console.log(bar.num);   //[]

console.log(typeof bar.fn); //function

我们也可以为实例属性和实例方法添加属性和方法:

function Foo() {

this.num = [];  //实例属性

this.fn = function() {  //实例方法

console.log("hello word");

}

}

var oneBar = new Foo();

oneBar.num.push(1);

oneBar.fn = {};

console.log(oneBar.num);    //[1]

console.log(typeof oneBar.fn);  //Object

var twoBar = new Foo();

console.log(twoBar.num);   //[]

console.log(typeof twoBar.fn);  //function

从上面的代码可以看到,当我们在oneBar中修改了num的值和fn的类型,但是在twoBar中却没有发生改变,这是由于数组和函数都是对象,属于引用类型。oneBar和twoBar中的属性和方法名称虽然相同但是却不是同一个引用,它们只是对Foo对象定义的属性和方法的一个复制。

如果一个构造函数对象有上千的实例方法,那么它的每个实例都要对这个构造函数的上千个实例方法进行复制,这显然是不科学的,那么这种情况下我们就必须使用prototype了。好,接下来我们正式说一下什么是原型,什么是原型链?

首先,什么是原型?

JavaScript 中,万物皆对象!但对象也是有区别的。分为普通对象和函数对象,Object ,Function 是JS自带的函数对象。每个对象都有原型(null和undefined除外),你可以把它理解为对象的默认属性和方法。
普通对象和函数对象
var o1 = {}; 
var o2 =new Object();
var o3 = new f1();

function f1(){}; 
var f2 = function(){};
var f3 = new Function('str','console.log(str)');

console.log(typeof Object); //function 
console.log(typeof Function); //function  

console.log(typeof f1); //function 
console.log(typeof f2); //function 
console.log(typeof f3); //function   

console.log(typeof o1); //object 
console.log(typeof o2); //object 
console.log(typeof o3); //object

在上面的例子中 o1 o2 o3 为普通对象,f1 f2 f3 为函数对象。怎么区分,其实很简单,凡是通过 new Function() 创建的对象都是函数对象,其他的都是普通对象。f1,f2,归根结底都是通过 new Function()的方式进行创建的。Function Object 也都是通过 New Function()创建的。

  • Object:Object是一个函数对象,Object的原型就是一个Object对象,它里面存在着一些对象的方法和属性,例如最常见的toString方法。
  • 新建对象:用new Object或者{}建的对象是普通对象,它没有prototype属性,只有proto属性,它指向Object.prototype。
  • Array Array也是一个函数对象,它的原型就是Array.prototype,它里面存在着一些数组的方法和属性,例如常见的push,pop等方法。
  • Function:Function也是一个函数对象,但它有点特殊,它的原型就是一个function空函数。
  • 自定义函数:它的原型就是你给它指定的那个东西。如果你不指定,那它的原型就是一个Object.prototype。

实例就是通过构造函数创建的。实例一创建出来就具有constructor属性(指向构造函数)和proto属性(指向原型对象)。构造函数中有一个prototype属性,这个属性是一个指针,指向它的原型对象。原型对象内部也有一个指针(constructor属性),指向构造函数Person.prototype.constructor = Person。实例可以访问原型对象上定义的属性和方法。

普通对象的proto指向这个对象(this)的构造函数的prototype;
函数对象的proto全部都是指向Function的prototype。这是一个空函数
然后无论是Function的prototype还是构造器的prototype的proto都指向object.prototype,然后最终object.prototype指向null,原型链结束;

总的来说:

在Javascript中,万物皆对象,然而对象又分为普通对象和函数对象,至于怎么区分普通对象和函数对象,很简单,没有通过new Function()创建的都为普通对象,Object ,Function 是JS自带的函数对象,每个对象都具有原型属性prototype指向它的原型对象,每个实例都是通过构造函数创建的,一旦创建就拥有constructor属性,指向它的构造函数,和_ proto _属性,指向原型对象,也就是说

实例对象._ proto _属性===构造函数.prototype

所有普通对象的_ proto 属性等于它构造器的prototype;
所有函数对象的
proto 属性等于Function的prototype;
无论是FUnction还是构造器,构造函数的prototype的
proto _都等于object.prototype;

Function.prototype._ proto _ === Object.prototype
Object.prototype._ proto _===null
则原型链完成

function Person(name) {
    this.name = name
}
// 重写原型
Person.prototype = {
    getName: function() {}
}
var p = new Person('jack')
console.log(p.__proto__ === Person.prototype) // true
console.log(p.__proto__ === p.constructor.prototype) // false
这里直接重写了 Person.prototype。输出结果可以看出p._ proto 仍然指向的是Person.prototype,而不是p.constructor.prototype。这也很好理解,给Person.prototype赋值的是一个对象直接量{getName: function(){}},使用对象直接量方式定义的对象其构造器(constructor)指向的是根构造器Object_,Object.prototype是一个空对象{},{}自然与{getName: function(){}}不等。

什么是原型链

在JavaScript 中,每个对象都有一个指向它的原型(prototype)对象的内部链接。这个原型对象又有自己的原型,直到某个对象的原型为 null 为止(也就是不再有原型指向),组成这条链的最后一环。这种一级一级的链结构就称为原型链(prototype chain)。
JavaScript 对象是动态的属性“包”(指其自己的属性)。JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依此层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。


原型链.png
var A = function(){};

var a = new A();

console.log(a.__proto__);   //A{}(即构造器function A 的原型对象)

console.log(a.__proto__.__proto__); //Object{}(即构造器function Object 的原型对象)

console.log(a.__proto__.__proto__.__proto__);   //null
参考:

作者:Yi罐可乐
最详尽的 JS 原型与原型链终极详解,没有「可能是」。(一)
最详尽的 JS 原型与原型链终极详解,没有「可能是」。(二)
最详尽的 JS 原型与原型链终极详解,没有「可能是」。(三)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容