javascript之原型和原型链

聊到面向对象编程就不得不说到面向对象的编程语言的三大基础特征(封装,继承,多态),js作为一门面向对象的编程语言,当然也是具备上面的几个特征,今天我们就一起认识下js如何实现其中的一个基本特征:继承,对,就是我们常说的prototype,原型及由原型组成的一个链,原型链
一直想分析为什么利用prototype就可以实现js的继承了!!!,接下来小伙伴们就和我一起来探讨这个比较深奥的问题哈

一.第一步还是先来看原型的概念:

1、Prototype:
每个函数就是一个对象(Function),函数对象都有一个子对象 prototype对象,类是以函数的形式来定义的。prototype表示该函数的原型,也表示一个类的成员的集合。(来自百度百科)
这个看的有的晕,把句子拆开一句一句分析:(1)明确了Prototype是一个引用。(2)这个引用指向的就是原型对象
2.proto
针对js对象提供的一个指向原型的指针,该属性并不标准,实际工作中并不会用到,所以称其为隐藏的原型

二、prototype的作用

针对上面的概念,我们用代码来演示一遍,来进行进一步的理解

function Demo(){
   
}
Demo.prototype.name = "xiaoming";
var demo = new Dem0();
demo.name //xiaoming
demo.__proto__;//{name: "xiaoming", constructor: ƒ}

如上我们定义了一个构造函数Demo(也就是js中的类,为了区分普通函数我们一般将构造函数首字母进行大写),每一个函数在定义的时候都会拥有自己的一个prototype属性(除Function.prototype.bind),而该属性指向的是自己的原型对象,即Demo.prototype;
我们首先定义了一个构造函数Demo,然后在其原型对象上定义了一个属性name并且赋值为xiaoming(注意:在一个原型对象中会默认有两个属性,一个是construct,一个是proto,后面会针对这两个属性来细讲)。
然后执行new操作进行对象的实例化(接下来也会对new这个过程进行拆分),结果demo.name可以访问到在构造函数原型上定义的属性,如何访问到的了?
我们输出demo.proto结果指向了一个对象,该对象和Demo.prototype对象一样,然后我们来证明一下,两者是否真的为同一个对象,执行demo.proto === Demo.prototype 结果返回 true,看来两者是一样的,即实列化的对象demo可以通过proto来访问Demo构造函数上的原型对象,而prototype和proto就是所谓的原型指针(原型),他们所指向的就是原型对象。
解释prototype后,我们来看看prototype在的作用,看如下代码:

function Car(color,size){
  this.color = color;
  this.size = size;
}
Car.prototype.action = function(){//共用的属性放到原型上
   console.log('run....')
}
var bmw = new Car('blue',200);//定义了一辆车
var audi = new Car('white',230);//定义了另一辆车
bmw.action  === audi.action //true
bmw.__proto__ === audi.__proto__ //true
audi.__proto__ === Car.prototype // true
为什么要把共用的东西放到prototype上面了?

1.节省内存
在进行实列化的时候都会将构造函数里的属性都执行一边,意思就是每进行一次new操作,就会对属性进行一次初始化,这样就会占用相应的内存空间,而将公共的属性放到prototype上面,就只在函数初始话的时候开辟了一个内存,在new的过程中不会重新开辟新的地址(题外话:那么这个构造函数在实例话后算不算是形成了一个闭包了?)
2.实列话的对象可以天然的继承(哈哈,不需要你去进行任何操作)
因为通过构造函数实列话后的对象,它的proto指向的就是该构造函数的原型对象,所以无论你实列化多少个 他们的proto都是指向该构造函数的原型对象,即它们都继承了该属性
为了加强理解,我们在看一个例子

 function OldCar(){
     this.color = "black";
  }
function Car(color,size){
  this.color = color;
  this.size = size;
}
Car.prototype = new OldCar();
Car.prototype.action = function(){//共用的属性放到原型上
   console.log('run....')
}
var car1 = new Car();
car1._proto_ === Car.prototype //true

//看下car1._proto指向的具体内容

1.png

好奇怪,怎么这个原型指向的构造函数变成了OldCar ,不应该是Car吗?
Car.prototype = new OldCar();这个操作后,就会Car原型指针指向了OldCar实列化后的对象上,所以访问car1._proto就相当于访问OldCar的实列,所以car1._proto.constructor 就是OldCar了

聊聊new、constructor .原型链

上面说的也许听起来不是很明白,接下来,我们将上面出现的几个名词进行解释,这样就能对原型有一个深入的理解,所以接下来将会解释 construct 、proto和prototype的关系 ,以及new的过程中内部具体发生了什么,然后看一个代码实列。
(1)constructor
w3c定义:constructor 属性返回对创建此对象的数组函数的引用。
还是一如既往的不好理解,不要担心,还是老办法拆开一句一句理解
首先constructor 是一个属性(即原型对象中存在的一个属性),然后这个属性指向的是创建该对象的函数。

function Person(){
}
Person.prototype.age = 38;
var person = new Person();
person._proto_.constructor ;//返回的是Person这个函数(因为person._proto_===Person.prototype所以两者的原理是一样的的)
//看看下面的构造函数指向谁
function Father(){
}
Father.prototype.name = "jhon";
function Son(){
}
Son.prototype = new Father();
var son = new Son();
son.__proto__.constructor//  function Father(){}

上面的列子很好的说明了constructor指向的是创建该对象的函数,因为Son函数的原型指向的是 father这个对象实列,所以返回的constructor即为在father上查找constructor 所以返回值为Father函数
(2)new的过程中发生了啥
1、在每次我们定义对象的时候,我们会使用一个new操作符去实现一个对象的实列化,即使我们平时使用 var demo = {},这种使用字面量来声明一个对象时。其实它内部仍然使用了new操作符

var demo = {};
//相当于 var demo = new Object();

2、接下来我们来剖析剖析new的过程中到底发生了 什么,看如下代码

function Demo(){
 this.name = "xiaoming";
}
//直接调用
var demo = Demo();
     demo//undefined,这个很好理解,如果一个函数中没有返回值时默认返回的为undefined
//进行实例化
var demo = new Demo();//注意两者直接仅仅是多了一个new操作符
demo//Demo {name: "xiaoming"};返回的是一个对象

//将上面的函数进行更改,给他一个返回值看看有什么变化
function Demo(){
 this.name = "xiaoming";
 return age = 3
}
//直接调用
var demo = Demo();
demo// 3
//实例化
var demo = new Demo();
demo//Demo {name: "xiaoming"};返回的仍然是一个对象
//在把返回值变为一个对象 看看有木有变化
function Demo(){
      this.name = "xiaoming";
     return{
       age:3
     } 
}
//直接调用
var demo = Demo();
demo// {age: 3}返回的是一个对象,木有问题
//实例化
var demo = new Demo();//这个会返回啥,是仍然不变还是已经变化了?
demo//{age: 3}返回的对象发生了变化
通过上面的几个例子我们可以得出如下结论:

(1)、如果构造函数没有返回值时,直接执行时,返回的为undefined,进行new操作符后返回的为一个对象,相当于在函数体内隐士执行了如下操作
function Demo(){
var this = {proto:Demo.prototype }
return this;
}
(2)、如果构造函数返回值为一个原始值时,进行new操作符后,返回值和(1)中返回的一样
(3)、如果构造函数返回值为一个引用值是(比如对象,数组)进行new操作符后,返回值和直接执行这个函数返回的值一样
3、看看new的过程中this的指向

function Demo(){
    this.name = "jhon";
    this.age =18;
   console.log("当前的this指向的是"+this);
 }
//直接执行函数
Demo();//当前的this指向的是[object Window],指向的为window
//new实例化
var demo = new Demo();
demo//指向的为当前的实例化后返回的对象 Demo {name: "jhon"}

也就是说在new的过程中,构造函数将this指向调用者。。。这个new就先聊这么多,嘎嘎,也只是提到了一些基本的。

(3)原型链
1、在上面我们已经知道了这两个都是原型的引用,只不过prototype是针对函数,而proto指的是对象(除null,undefined.和自定义创建的空对象,因为以上对象是没有proto属性的)
在js中,如果在访问一个对象属性的时候,在当前实列下没有找到该属性,那么他就会沿着proto原型去到上一层去查找

2.png

我们看到father这个实列是没有age属性的,但是我们却可以访问到age这个属性,这是因为当没有在当前实列下该属性,就会通过proto去向上进行查找。
说道这里就可以说说什么是原型链了,如上图所示,在father.proto对像中还存在一个proto,而这个proto指向的就是最原始的对象Object,而这个Object.proto指向的为null
3.png

这样就形成了一个原型查找的链,即所谓的原型链,这就是js对象在进行属性和方法进行查找时所采用的一个方法,沿着原型链从底层一层一层向上查找。

如何使用原型来实现继承
function Father(){
}
Father.prototype.firstName = 'gong';
function Son(){
  this.lastName = 'cheng'
}
Son.prototype = Father.prototype;
var son = new Son();
son.firstName // gong
//如果改变son的原型对象会发生什么
Son.prototype.age = 18;
son.age // 18
//看看father对象会有什么变化
var father = new Father();
father.age// 18
//此时的不足就是如果给son的原型对象上增加属性会改变father的原型对象(两者属于同一引用)
//如何优化,优化重点即想办法让son的原型引用和father的原型引用不是指向同一个地址
//定义一个构造继承的函数
function inherit(Target,Orgin){
    //定义一个中间函数
     function F(){};
     F.prototype = Orgin.prototype;
     Target.prototype = new F();
}
//执行下上面的列子
Father.prototype.firstName = 'gong';
function Father(){
}
function Son(){
  this.lastName = 'cheng'
}
inherit(Son,Father);
var son = new Son();
son.firstName//gong
Son.prototype.age = 18;
son.age//18
var father = new Father();
father.age//undefined
//如果把上述的inherit内部的代码  F.prototype = Orgin.prototype; Target.prototype = new F();)的顺序换下,结果会怎样呢
//把上面的代码在优化下
let inherit = (function(){
    var F = function(){};
    return function(Target,Orgin){
       F.prototype = Orgin.prototype;
       Target.prototype = new F();
       Target.prototype.constructor= Target;//是否有疑问?为什么要重新构造函数
    }
}(Target,Orgin))
//为何要重新构造函数
//构造函数:每创建一个原型对象,该原型对象就会根据一定的规则生成 一个construct,上面我们默认重写了Target的原型对象,所以construct属性也会变化(F),为了让construct保持原来的,我们可以重新他的construct属性

最后看一个题目

//考察原型相关的知识
 function F(){
 }
var person = new F();
F.prototype.name = 'jack';
F.prototype = {
 name:'jack',
 age:12,
say:function(){console.log(this)}
}
var person1 = new F();
//问题
person.name 
person.say()
person1.name
person1.say()

这是我的理解,也许理解的不到位,说实话要将这个讲清楚,不是区区这点内容就可以的,这里相当于自己的一个总结,互相学习,互相促进~~~

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

推荐阅读更多精彩内容

  • 原型链是一种机制,指的是 JavaScript 每个对象都有一个内置的 __proto__ 属性指向创建它的构造函...
    劼哥stone阅读 4,408评论 15 80
  • 典型的面向对象编程语言(比如C++和Java),存在“类”(class)这个概念。所谓“类”就是对象的模板,对象就...
    萨默塞特酱阅读 325评论 0 0
  • 在JavaScript中,原型链作为一个基础,老生长谈,今天我们就来深入的解读一下原型链。 本章主要讲的是下面几点...
    Devinnn阅读 1,398评论 1 6
  • 我出生那天,是金陵城的七夕节,我生就丰额骈齿、一目双瞳,父亲抱着我十分喜悦,他们说,我这是天生富贵相,周武王和项羽...
    呢喃雪阅读 646评论 0 11
  • 也没有什么好说的,反正开学嘛也已经有两个礼拜了。也不知道自己最近学得了多少???又懂得多少呢??? 时间在慢慢流逝...
    sayuuuuuu阅读 122评论 0 0