javascript原型和原型链是js中的重点也是难点,理论上来说应该是属于面向对象编程的基础知识,那么我们今天为什么要来讲这个呢?(因为我也忘了,最近看资料才揭开面纱……  哈哈哈)

  

  好了,直接进入正文。在js的编程世界中,万物皆对象;不管你是数组还是函数还是对象,都是属于对象类型;那么这么多对象,如何进行管理呢?js中把对象分为实例对象、函数对象、原型对象三大类;

  实例对象:

    通过构造函数(所谓构造函数我们可以简单理解为进行new操作的函数就是构造函数)所创建的对象都是实例对象;

var people = new Student();
console.log(people)

  上面代码中的people就是一个实例对象,Student 就是一个构造函数;

  

  函数对象:

    函数对象我们可以简单的理解为函数,因为在js中函数本身就属于一个对象;而上述代码中的Student是一个构造函数,构造函数是一种特殊的函数,构造函数往往都是在实例对象创建后才进行调用,作用是对实例对象进行初始化的操作;构造函数和普通函数的区分仅仅只是功能区分的一个称呼,体现在JS代码中的区别就是new和不new的区别;有new关键字就是新建一个构造函数,没有new关键字就是新建一个普通函数;

  原型对象:

    原型对象我们可以简单的理解为原型对象是实例对象和函数对象的父对象,俗称:爸爸,并且通过实例对象和函数对象都能找到原型对象;

  

  我们先来看看实例对象和函数对象的关系:

  

function people(name){
this.name=name;
console.log(this.name);
} var Student=new people("咸鱼");

  上面代码中我们可以看到people是构造函数,Student是实例对象,二者之间的关系体现在constructor属性中;

  

Student.constructor==people;//true

  我们的实例对象对象中是可以通过constructor属性来访问到构造函数的,那么反过来可以实现吗?

  答案是:不行!!!  但是我们可以变通一下通过instanceof方法来进行检查

  

Student instanceof people  //true

  通过这种方式,我们可以间接的检查一个对象是否是构造函数的实例对象

  new关键字做了什么?

    上面讲了构造函数和普通函数在JS中没有什么太大的区别,所以我们可以想到那么直接使用函数的话,this的指向肯定是浏览器全局对象window,那么我们new一下之后,this的指向将会改变为新的实例对象,并且还会将这个新的实例对象返回回来;

    所以我们可以总结new做了什么:

       1.新建了一个空的函数对象;

       2.改变this的指向,将this的指向改为接收新函数对象的实例对象

          3.返回这个新的实例对象

  

   大家来看一个小demo:

    

function people(name){
this.name=name;
this.say=function(){
console.log(this.name);
} } var Student=new people("咸鱼");
var man=new people("张三")
Student.say();  //咸鱼
man.say(); //张三
console.log(Student.say === man.say); //false

  从上面代码中我们可以看到我们有两个实例对象分别是Student、man;有两个构造函数people;我们可以看到最后两个结果进行比较是不相等的,这就说明两个函数对象不是同一个,这就验证了上面的new关键字,每new一下,就会新建一个空白的函数对象;如果我们要做的事情都一样,每一次都需要去重新new一个对象,如果是大量重复的做100次同样的事情呢?难道我们要去new100次吗?这会给内存带来极大的浪费;

  那现在怎么解决呢?JS官方都已经替我们想好了办法,我们可以把这个方法放到原型对象上,每次我们需要用的时候去调用就可以了;那么我们怎么放到原型对象上呢?

  JS中每一个构造函数都有一个prototype属性,这个属性指向的是另一个对象,这个对象就是原型对象;通过这个属性我们可以直接找到原型对象,当然原型对象也是一个对象,毕竟万物皆对象嘛;同时原型对象中也有一个constructor属性,这个属性也是指向构造函数;

  对了,JS中每一个对象都有一个__proto__属性(注意是左右两边各两个下划线),我们可以通过这个对象直接访问到原型对象;构造函数可以直接通过prototype直接访问到原型对象,实例对象可以通过__proto__直接访问到原型对象,这之间就有了联系;所以得到结论:实例对象.__proto__===函数对象.prototype,因为这两者都是访问到原型对象,共同的爸爸了;

   我们可以将上面的代码改造一下:

function people(name){
this.name=name;
people.prototype.say=function(){
console.log(this.name);
} } var Student=new people("咸鱼");
var man=new people("张三")
Student.say();  //咸鱼
man.say(); //张三
console.log(Student.say === man.say); //true

  这样我们就可以直接将say方法直接挂到原型对象上面了;

  

  这里有个图例可以对照着看,更方便理解:

  

  

function Student(name){
this.name=name;
}
var xianyu = new Student('xianyu');
console.log(xianyu.__proto__ === Student.prototype); // true
console.log(xianyu.constructor === Student); // true
console.log(xianyu.constructor === Student.prototype.constructor); // true

    得出的结果如下:

  所以我们可以总结一下结论:

       1.实例对象通过__proto__和函数对象通过prototype都是能够访问到原型对象的;

       2.实例对象通过constructor属性可以访问到构造函数,因为实例对象的constructor属性直接指向构造函数;

               3.原型对象上面的constructor属性跟实例对象一样,都是指向其构造函数

  原型链:

    对象可以通过“.”操作获取到一个属性的值,首先会在对象自身开始查找,如果查不到会到原型对象(__proto__)中去查找,如果原型对象中还没有就会把当前得到的原型对象当作实例对象,继续通过(__proto__)去查找当前原型对象的原型对象中去找,也就是去爸爸的爸爸那里找,直到__proto__为null时停止;

    

    上面可能有点拗口,我们换个生活中的例子来理解:就是你有个对象,你丈母娘问你要20W彩礼,你自己拿不出来你没有(对象本身没有属性),丈母娘让你问你爸爸(原型对象)要,如果你爸爸也没有,你爸爸就得问你爸爸的爸爸也就是你爷爷要(原型对象的原型对象,这里的第一个原型对象实质上成了一个实例对象,因为你爸爸在你爷爷那里永远是儿子),如果你爷爷也没有就继续往上要……如此反复,实在拿不出来(null),丈母娘大手一挥,拿不出来就不嫁了(直到为null时停止);

  原型链实现继承:

    我们知道所有的对象都有个toString()方法,上述代码中实例对象xianyu其实也是一个对象,这就是JS中的万物皆对象的道理,我们没有给xianyu加任何toString()方法,它是哪里来的?继承来的!因为xianyu.__proto__最终指向的是Function原型对象(Function函数对象一直往上查找原型对象最终是Function原型对象,Object函数对象一直往上查找原型对象最终是null),因为Function原型对象中有,所以xianyu作为它的实例会继承上面的方法,这就是JS继承的本质;

    

    也就是说你爹(原型对象)那有20W,没花在银行存着,你(实例对象)也可以理解为你有20W在银行存着,因为你爹的钱迟早会给你,你爹的钱间接就是你的钱,你要是还存着,你的儿子(实例对象的实例对象)也就间接有了20W;

    总结:

      1.实例对象可以通过__proto__访问原型对象,函数对象可以通过prototype访问原型对象;

      2.原型对象上面的方法一定会继承给下属的实例对象,反之如果要给原型对象上添加方法需要通过   函数对象.prototype.方法名  或  实例对象.__proto__.方法名  进行添加;

         3.原型对象实质上也是一个对象,原型对象上面也会有__proto__、constructor等属性,所以原型对象可以通过  原型对象.__proto__来访问原型对象的原型对象,也可以通过constructor来知道自己是属于哪个构造函数上面的实例对象;

大白话通俗易懂的讲解javascript原型与原型链(__proto__、prototype、constructor的区别)的更多相关文章

  1. JavaScript原型,原型链 !

    js原型 问题:什么是js原型? js每声明一个function,都有prototype原型,prototype原型是函数的一个默认属性,在函数的创建过程中由js编译器自动添加. 也就是说:当生产一个 ...

  2. javascript构造函数及原型对象

    /** @ javascript中没有类的概念,所以基在对象创建方面与面向对象语言有所不同* @ 对象创建的常用方法及各自的局限性* @ 使用Object或对象字面量创建对象* @ 工厂模式创建对象* ...

  3. javascript系列--认识并理解构造函数,原型和原型链

    一.前言 介绍构造函数,原型,原型链.比如说经常会被问道:symbol是不是构造函数:constructor属性是否只读:prototype.[[Prototype]]和__proto__的区别:什么 ...

  4. 深入总结Javascript原型及原型链

    本篇文章给大家详细分析了javascript原型及原型链的相关知识点以及用法分享,具有一定的参考价值,对此有需要的朋友可以参考学习下.如有不足之处,欢迎批评指正. 我们创建的每个函数都有一个 prot ...

  5. javascript 原型及原型链详解

    我们创建的每个函数都有一个 prototype (原型)属性,这个属性是一个指针,指向一个原型对象,而这个原型对象中拥有的属性和方法可以被所以实例共享. function Person(){ } Pe ...

  6. 总结一下js的原型和原型链

    最近学习了js的面向对象编程,原型和原型链这块是个难点,理解的不是很透彻,这里搜集了一些这方面的资料,以备复习所用 一. 原型与构造函数 Js所有的函数都有一个prototype属性,这个属性引用了一 ...

  7. 原型prototype、原型链__proto__、构造器constructor

    创建函数时,会有原型prototype,有原型链__proto__,有constructor.(构造函数除外,没有原型) . prototype原型:是对象的一个属性(也是对象),使你有能力向对象添加 ...

  8. JavaScript中的原型继承原理

    在JavaScript当中,对象A如果要继承对象B的属性和方法,那么只要将对象B放到对象A的原型链上即可.而某个对象的原型链,就是由该对象开始,通过__proto__属性连接起来的一串对象.__pro ...

  9. 第一百零九节,JavaScript面向对象与原型

    JavaScript面向对象与原型 学习要点: 1.学习条件 2.创建对象 3.原型 4.继承 ECMAScript有两种开发模式:1.函数式(过程化),2.面向对象(OOP).面向对象的语言有一个标 ...

随机推荐

  1. Js面向对象原型~构造函数

    脑袋一团浆糊,但希望写点啥,所有就有了这篇博文了,抱歉哦....开始吧!!!!  什么是构造函数??   所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量.对构造 ...

  2. Apple放大绝进行反取证

    取证说穿了其实就是攻防,这本是正义与邪恶的对决,亦即执法单位与嫌疑犯两者之间的事,但现实生活中要比这复杂多了. 怎么说呢?举个例子大家便理解了.取证人员费尽心思,用尽各种手法,努力地想要自手机上提取重 ...

  3. Oracle DBLink跨数据库访问SQL server数据同步 踩坑实录

    项目需求:这里暂且叫A公司吧,A公司有一套人事管理软件,需要与我们公司的软件做人员信息同步,A公司用的是SQL server数据库,我们公司用的Oracle,接口都不会开发(一万句"fuck ...

  4. Java编程基础阶段笔记 day 07 面向对象编程(上)

    ​ 面向对象编程 笔记Notes 面向对象三条学习主线 面向过程 VS 面向对象 类和对象 创建对象例子 面向对象的内存分析 类的属性:成员变量 成员变量 VS 局部变量 类的方法 方法的重载 可变个 ...

  5. Flink 源码解析 —— 源码编译运行

    更新一篇知识星球里面的源码分析文章,去年写的,周末自己录了个视频,大家看下效果好吗?如果好的话,后面补录发在知识星球里面的其他源码解析文章. 前言 之前自己本地 clone 了 Flink 的源码,编 ...

  6. Caddy 源码全解析

    caddy源码全解析 Caddy 源码全解析 Preface Caddy 是 Go 语言构建的轻量配置化服务器.同时代码结构由于 Go 语言的轻便简洁,比较易读,推荐学弟学妹学习 Go 的时候也去查看 ...

  7. 使用top查看进程和系统负载信息

    引言      使用top命令,可以查看正在运行的进程和系统负载信息,包括cpu负载.内存使用.各个进程所占系统资源等,top可以以一定频率更新这些统计信息.下面我们来学习top命令的具体使用方法. ...

  8. 阿里架构师浅析Java设计模式之虚拟代理模式

    虚拟代理模式(Virtual Proxy)是一种节省内存的技术,它建议创建那些占用大量内存或处理复杂的对象时,把创建这类对象推迟到使用它的时候.在特定的应用中,不同部分的功能由不同的对象组成,应用启动 ...

  9. Markdown的最常用标记符号

    Markdown是一种可以使用普通文本编辑器编写的标记语言,通过简单的标记语法,它可以使普通文本内容具有一定的格式. md就是markdown 如果你要把这段文字定义成标题,只需要在前面加上一个#号, ...

  10. Java 操作Word书签(一):添加、删除、读取书签

    Word中,书签功能常用于查找.定位.标记特定字符或段落,对于篇幅较大的文档,此功能非常实用.下面,将介绍通过Java程序来添加及删除Word书签的方法.示例要点包括: 1. 添加书签 1.1 给指定 ...