请选择 进入手机版 | 继续访问电脑版
查看: 130|回复: 0

【JavaScript框架封装】深入理解原型链中的prototype、__proto__和constructor的关系

[复制链接]

699

主题

740

帖子

5977

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
5977
发表于 2018-6-29 18:59:43 | 显示全部楼层 |阅读模式
JavaScript中的原型链也算是JavaScript中的一个高级话题,一直以来对这块的知识理解地不是特别深刻,现再记录一下这块的学习笔记:
1.从最简单的代码开始:三行代码引出js中的高级话题
  1. <font size="4">function Foo(){};
  2. var foo = new Foo();

  3. </font>
复制代码
2.我们首先看一下这两行代码在JavaScript中的关系:
0.jpg


3.深入剖析:
这两行代码实际上包含了以下三个过程:
3.1:当我们创建一个自定义的函数Foo,该函数就会自动创建一个prototype属性,这个属性指向函数的原型对象;
3.2:原型对象会默认去取得constructor属性,指向构造函数。


第一行代码实际上发生了以上两个过程,我的理解如下:
父类(原型对象)的constructor属性指向子类对象(函数),子类的prototye属性指向了父类(原型对象),这里的原型对象可以表示为Foo.prototype。


3.3:当调用构造函数创建一个新实例foo后,该实例的内部将包含一个指针__proto__,指向构造函数的原型对象。

第二行代码执行后,实际上发生了第三个过程,我的理解如下:
每一个实例对象都有一个__proto__属性(指针),这个属性指向了父类(原型对象),而父类(原型对象)的constructor属性指向了子类(函数),而子类(函数)的prototye属性指向了父类对象。


4.现在来梳理任意一个实例,函数与Object这个对象的关系:
我的理解如下:
只要创建了一个Object,这个就会默认有一个protype属性,这个属性指向了Object的原型对象,而Object的原型对象默认会有一个constructor属性,这个属性指向了Object,但是Object的原型对象已经是原型链的最根部了,所以它没有__proto__属性。因此Object的原型对象的__proto__指针指向为null.

函数看做是一个对象以后,其构造函数就是Function(),原型对象就是Function的原型对象,Object函数看作实例对象的话,其构造函数就是Function(),原型对象是Function的原型对象
也就是函数的原型对象是:Function.prototype, Object的原型对象是:Object.protype


其他核心测试代码如下:
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <title>Title</title>
  6. </head>
  7. <body>

  8. <script>
  9.     /*如何识别一个对象是什么类型的呢?*/


  10.     // 每一个原型都有自己的prototype都有自己的constructor和toString()方法
  11.     console.log(Object.prototype.toString.call("jerry"));//[object String]
  12.     console.log(Object.prototype.toString.call(12));//[object Number]
  13.     console.log(Object.prototype.toString.call(true));//[object Boolean]
  14.     console.log(Object.prototype.toString.call(undefined));//[object Undefined]
  15.     console.log(Object.prototype.toString.call(null));//[object Null]
  16.     console.log(Object.prototype.toString.call({name: "jerry"}));//[object Object]
  17.     console.log(Object.prototype.toString.call(function () {
  18.     }));//[object Function]
  19.     console.log(Object.prototype.toString.call([]));//[object Array]
  20.     console.log(Object.prototype.toString.call(new Date));//[object Date]
  21.     console.log(Object.prototype.toString.call(/\d/));//[object RegExp]
  22.     /**
  23.      * 构造函数
  24.      * @constructor
  25.      */
  26.     function Person() {
  27.     };
  28.     console.log(Object.prototype.toString.call(new Person));//[object Object]


  29.     console.log("jerry".toString());//jerry
  30.     console.log((1).toString());//1
  31.     console.log([1, 2].toString());//1,2
  32.     console.log(new Date().toString());//Wed Dec 21 2016 20:35:48 GMT+0800 (中国标准时间)
  33.     console.log(function () {
  34.     }.toString());//function (){}
  35.     //console.log(null.toString());//error
  36.     //console.log(undefined.toString());//error


  37.     /*
  38.     *  这是因为toString为Object的原型方法,
  39.     *  而Array ,function等类型作为Object的实例,都重写了toString方法。
  40.     *  不同的对象类型调用toString方法时,
  41.     *  根据原型链的知识,调用的是对应的重写之后的toString方法(function类型返回内容为函数体的字符串,Array类型返回元素组成的字符串.....),
  42.     *  而不会去调用Object上原型toString方法(返回对象的具体类型),
  43.     *  所以采用obj.toString()不能得到其对象类型,只能将obj转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用Object上原型toString方法。
  44.     *
  45.     * */

  46.     obj = new Array();
  47.     // 所有的对象实际上是没有自己的toString()方法的,但是由于js中的原型链,导致了每一个对象都有自己的toString()方法,如果直接调用obj.toString()方法
  48.     // 会直接把数组里面的所有元素打印出来,如果要检测一个对象是不是数组类型, 需要使用Object的toString()犯法来检测
  49.     console.log(Object.prototype.toString.call(obj)) === '[object Array]';



  50.     var arr = [1, 2, 3];
  51.     console.log(Array.prototype.hasOwnProperty("toString"));//true
  52.     console.log(arr.toString());//1,2,3
  53.     delete Array.prototype.toString;//delete操作符可以删除实例属性
  54.     console.log(Array.prototype.hasOwnProperty("toString"));//false
  55.     console.log(arr.toString());//"[object Array]"


  56.     // 通过原型链判断是不是数组类型【原理?】
  57.     function Foo() {

  58.     }

  59.     console.log(Foo.prototype.constructor); // prototype指向了他的原型对象,这个原型对象有一个constructor属性,这个属性指向了他的构造函数

  60.     console.log(Foo.prototype);     // 每一个Foo都有一个prototype属性,这个属性指向了Foo的原型对象


  61.     var foo = new Foo();
  62.     console.log(foo.constructor == Foo);        // true: foo.constructor指向了他的构造函数(其实就是Foo这个函数)
  63.     console.log(foo.hasOwnProperty('constructor'));  // false: foo本身是没有constructor这个属性的,这个属性实际上是继承于父类的
  64.     console.log(foo.__proto__);             // 每一个foo的实例都有一个__proto__属性,这个属性指向了他的原型对象(Object)


  65.     /*function Object() {

  66.     }
  67.     obj = new Object();
  68.     console.log(obj.__proto__);*/             // 指向了他的原型对象

  69.     console.log(Object.prototype.constructor);
  70.     console.log(Object.prototype.__proto__);   // null, 由于Object是js内置的根部, __proto__指向了他的原型对象,他的原型对象实际上为null
  71.     console.log(Object.prototype);    // 这是Object的原型对象,但是这个原型对象里面已经没有__proto__属性了


  72.     // Valueof() 和 toString()的使用详解-------------------------------------------------
  73.     // valueOf()方法会将对象转换为基本类型,如果无法转换为基本类型,则返回原对象
  74.     var obj = new Boolean(true);
  75.     console.log(obj.valueOf());//true
  76.     console.log(typeof obj.valueOf());//boolean
  77.     //如果是包装类型的基本类型,则返回原基本类型值
  78.     var a = true;
  79.     console.log(a.valueOf());//true
  80.     console.log(typeof a.valueOf());//boolean

  81.     /*
  82.     * valueOf()用法小结:
  83.         1)undefined和null没有此方法(基本类型肯定没有方法,String、Number和Boolean是因为有对应的基本包装类型,才可以调用方法);
  84.         2)基本包装类型和对应的基本类型,调用valueOf()返回对应的基本类型值;
  85.         3)对象类型(除Date类型)返回原对象;
  86.         4)Date类型返回表示日期的毫秒数
  87.     * */


  88.     // oSting()方法返回返回对象的字符串表现-----------------------------------------
  89.     /*
  90.     * 如果是基本包装类型对应的基本类型,会返回原值。但这并不代表基本类型拥有toString()方法(基本类型不是对象,不拥有任何方法),而是在读取一个基本类型值时,后台会创建一个对应的基本包装类型的对象,从而调用一些方法。所以,基本类型“调用”toString()方法时,实际上是先创建了一个对应的基本包装类型,由此基本包装类型调用toString()最后返回了其对应的字符串,看起来就好像是基本类型调用了toString()方法而得到了对应的字符串。
  91.     *
  92.     *
  93.     *
  94.     *
  95.     * 【小结】:

  96.         1)undefined和null没有此方法(基本类型肯定没有方法,String、Number和Boolean是因为有对应的基本包装类型,才可以调用方法);
  97.         2)Date类型返回表示时间的字符串;
  98.         3)Object类型返回字符串“[object Object]”。



  99.         【与valueOf()对比】
  100.         1)toString()和valueOf()的主要不同点在于,toString()返回的是字符串,而valueOf()返回的是原对象
  101.         2)由于undefined和null不是对象,所以它们toString()和valueOf()两个方法都没有
  102.         3)数值Number类型的toString()方法可以接收转换基数,返回不同进制的字符串形式的数值;而valueOf()方法无法接受转换基数
  103.         4)时间Date类型的toString()方法返回的表示时间的字符串表示;而valueOf()方法返回的是现在到1970年1月1日00:00:00的数值类型的毫秒数
  104.         5)包装对象的valueOf()方法返回该包装对象对应的原始值



  105.         【与转型函数String()函数的对比】
  106.         toString()和String()都是将数据转换为对应的字符串,有如下区别:
  107.         1)String()可以将任何类型的值转换为字符串,包括undefined和null;
  108.         console.log(String(null));//"null"
  109.         console.log(String(undefined));//"undefined"
  110.         2)String()不能接受数值基数作为参数
  111.     *
  112.     * */




  113.     // call和apply的使用方法总结-----------------------------------------------------------------
  114.     window.color = 'red';
  115.     document.color = 'yellow';

  116.     var s1 = {color: 'blue' };
  117.     function changeColor(){
  118.         console.log(this.color);
  119.     }


  120.     // 注意call()里面传递的参数,就会把这个参数传给调用的函数,此时调用函数的内部里面的this实际上指向了传递过来的这个参数对象
  121.     changeColor.call();         //red (默认传递参数)
  122.     changeColor.call(window);   //red
  123.     changeColor.call(document); //yellow
  124.     changeColor.call(this);     //red
  125.     changeColor.call(s1);       //blue



  126.     window.firstName = "Cynthia";
  127.     window.lastName = "_xie";

  128.     var myObject = {firstName:'my', lastName:'Object'};

  129.     function getName(){
  130.         // 这个函数内部的this默认指向的是window,通过call和apply可以间接修改这个this的指向
  131.         console.log(this.firstName + this.lastName);
  132.     }

  133.     function getMessage(sex,age){
  134.         console.log(this.firstName + this.lastName + " 性别: " + sex + " age: " + age );
  135.     }

  136.     getName.call(window); // Cynthia_xie
  137.     getName.call(myObject); // myObject

  138.     getName.apply(window); // Cynthia_xie
  139.     getName.apply(myObject);// myObject

  140.     getMessage.call(window,"女",21); //Cynthia_xie 性别: 女 age: 21
  141.     getMessage.apply(window,["女",21]); // Cynthia_xie 性别: 女 age: 21

  142.     getMessage.call(myObject,"未知",22); //myObject 性别: 未知 age: 22
  143.     getMessage.apply(myObject,["未知",22]); // myObject 性别: 未知 age: 22


  144.     /*apply()方法 接收两个参数,一个是函数运行的作用域(this),另一个是参数数组。
  145.         语法:apply([thisObj [,argArray] ]);,调用一个对象的一个方法,2另一个对象替换当前对象。

  146.         说明:如果argArray不是一个有效数组或不是arguments对象,那么将导致一个
  147.         TypeError,如果没有提供argArray和thisObj任何一个参数,那么Global对象将用作thisObj。

  148.         call()方法 第一个参数和apply()方法的一样,但是传递给函数的参数必须列举出来。
  149.         语法:call([thisObject[,arg1 [,arg2 [,...,argn]]]]);,应用某一对象的一个方法,用另一个对象替换当前对象。

  150.         说明: call方法可以用来代替另一个对象调用一个方法,call方法可以将一个函数的对象上下文从初始的上下文改变为thisObj指定的新对象,如果没有提供thisObj参数,那么Global对象被用于thisObj。
  151.     *
  152.     *
  153.     * */


  154.     function isArray(obj) {
  155.         // 注意这里的object是小写的哈,否则是检测不出来的哈
  156.         return Object.prototype.toString.call(obj) === '[object Array]'
  157.     }

  158.     obj2 = new Array();

  159.     console.log(isArray(obj2));         // false
  160.     console.log(Object.prototype.toString.apply(obj2));         // 使用apply方法同样是可以检测的哈




  161.     //数据类型的检测测试--------------------------------------------------------------
  162.     /**
  163.      * 数据类型检测工具包
  164.      * @constructor
  165.      */
  166.     MyTools = function () {

  167.     }

  168.     MyTools.prototype = {
  169.         isStringByApply : function (obj) {
  170.             return Object.prototype.toString.apply(obj) === '[object String]'
  171.         },
  172.         isArrayByCall : function (obj) {
  173.             return Object.prototype.toString.call(obj) === '[object Array]'
  174.         },
  175.         isStringByConstructor : function (obj) {
  176.             return obj.constructor === String
  177.         },
  178.         isBooleanByTypeOf : function (obj) {
  179.             return typeof obj === 'boolean'
  180.         }

  181.     }


  182.     // 对json格式的深入理解
  183.     // 测试结果:json中存储数据的时候,key必须是唯一的,否则后面的会覆盖掉前面的内容哈
  184.     var jsonObject = {
  185.         name : 'xiuxiu',
  186.         age : 15,
  187.         name : 'xiugang'
  188.     }
  189.     console.log(jsonObject);
  190. </script>
  191. </body>
  192. </html>
复制代码








上一篇:杨强主讲Linux环境高级编程视频教程资料分享
下一篇:使用移动安全自动渗透测试框架Mobile Security Framework检测APK漏洞
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案; 如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子分类或者标题加上【已解决】。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

微信扫一扫

我爱科技论坛(www.52tech.tech)旨在打造全网最大的免费资源共享平台。目前论坛包括考研资料、编程学习、黑科技/科学上网、开源软件等资源模块,竭力服务于正在学习道路上的每一个人。我爱科技论坛,爱科技,更爱分享。致力于营造一个资源丰富、内容完善的大型网络学习交流资源共享平台!

QQ|Archiver|手机版|小黑屋|我爱科技论坛 快乐学习交流

(请勿发布违反中华人民共和国法律法规的言论,会员观点不代表我爱科技论坛的官方立场)

Powered by Discuz! X3.4© 2001-2013 Technology Inc.

返回顶部 返回列表