1.1 理解原型链

JavaScript中几乎所有的东西都是对象,我们说数组是对象、DOM节点是对象、函数等也是对象,创建对象的Object也是对象(本身是构造函数),那么有一个重要的问题:对象从哪里来?

这是一句废话,对象当然是通过一定方式创建出来的,根据实际类型不同,对象的创建方式也千差万别。比如函数,我们可以声明函数、使用Function构造函数创建等,比如数组,我们可以直接通过var arr = [] 的方式创建空数组,也可以通过new Array的方式创建,比如普通的对象,我们可以字面量创建、使用内置构造函数创建等等,花样太多了,以至于我们学习的时候头昏脑涨、不得要领。

其实,归根结底所有“类型”的对象都可以认为是由相应构造函数创建出来的。 函数由Function构造函数实例化而来,普通对象由Object构造函数实例化而来,数组对象由Array构造函数实例化而来,至于Object | Array | Function等他们本身是函数,当然也有自己的构造函数。

理解了上面一点,那么接下来我们在理解原型链的时候就会容易得多。

请看刺激的推导过程

前提 所有对象都由构造函数实例化而来,构造函数默认拥有与之相关联的原型对象
❒ ① 构造函数的原型对象也是对象,因此也有自己的构造函数
❒ ② 构造函数原型对象的构造函数,也有与之相关连的原型对象
❒ ③ 构造函数原型对象的原型对象(__proto__)也有自己的构造函数,其也拥有关联的原型对象
☞ 以上就形成了一种链式的访问结构,是为原型链

其实构造函数也是对象,所以构造函数本身作为对象而言也有自己的构造函数,而这个构造函数也拥有与之相关联的原型对象,以此类推。那么,这就是另一条原型链了。综上,我们可以得出原型链并不孤单的结论。

 

1.2 原型链结构

现在我们基本上把原型链的由来说清楚了,那么接下来通过具体的代码来分析原型链的整体结构。

示例代码

  1. 1 //01 自定义构造函数Person和Animal
  2. 2 function Person() {}
  3. 3 function Animal() {}
  4. 4 //02 使用构造函数创建实例对象
  5. 5 var p1 = new Person();
  6. 6 var p2 = new Person();
  7. 7 var a = new Animal();
  8. 8 //03 创建数组对象
  9. 9 var arrM = ["demoA","demoB"];

上面的代码非常简单,其中p1,p2和a它们是自定义构造函数的实例化对象。其次,我们采用快捷方式创建了arrM数组,arrM其实是内置构造函数Array的实例化对象。另外,Person和Animal这两个构造函数其实是Function构造函数的实例对象。理解以上几点后,我们就可以来看一下这几行代码对应的原型链结构图了。

原型链结构图说明:

① 因为复杂度关系,arrM对象的原型链结构图单独给出。
② Object.prototype是所有原型链的顶端,终点为null。

验证原型链相关的代码

  1. 1 //[1] 验证p1、p2的原型对象为Person.prototype
  2. 2 // 验证a 的原型对象为Animal.prototype
  3. 3 console.log(p1.__proto__ == Person.prototype); //true
  4. 4 console.log(p2.__proto__ == Person.prototype); //true
  5. 5 console.log(a.__proto__ == Animal.prototype); //true
  6. 6 //[2] 获取Person.prototype|Animal.prototype构造函数
  7. 7 // 验证Person.prototype|Animal.prototype原型对象为Object.prototype
  8. 8 // 先删除实例成员,通过原型成员访问
  9. 9 delete Person.prototype.constructor;
  10. 10 delete Animal.prototype.constructor;
  11. 11 console.log(Person.prototype.constructor == Object); //true
  12. 12 console.log(Animal.prototype.constructor == Object); //true
  13. 13 console.log(Person.prototype.__proto__ == Object.prototype); //true
  14. 14 console.log(Animal.prototype.__proto__ == Object.prototype); //true
  15. 15 //[3] 验证Person和Animal的构造函数为Function
  16. 16 // 验证Person和Animal构造函数的原型对象为空函数
  17. 17 console.log(Person.constructor == Function); //true
  18. 18 console.log(Animal.constructor == Function); //true
  19. 19 console.log(Person.__proto__ == Function.prototype); //true
  20. 20 console.log(Animal.__proto__ == Function.prototype); //true
  21. 21 //[4] 验证Function.prototype的构造函数为Function
  22. 22 console.log(Function.prototype.constructor == Function); //true
  23. 23 //[5] 验证Function和Object的构造函数为Function
  24. 24 console.log(Function.constructor == Function); //true
  25. 25 console.log(Object.constructor == Function); //true
  26. 26 //[6] 验证Function.prototype的原型对象为Object.prototype而不是它自己
  27. 27 console.log(Function.prototype.__proto__ == Object.prototype);//true
  28. 28 //[7] 获取原型链的终点
  29. 29 console.log(Object.prototype.__proto__); //null

下面贴出数组对象的原型链结构图

验证数组对象原型链结构的代码示例

  1. 1 //[1] 验证arrM的构造函数为Array
  2. 2 //方法1
  3. 3 console.log(arrM.constructor == Array); //true
  4. 4 //方法2
  5. 5 console.log(Object.prototype.toString.call(arrM)); //[object Array]
  6. 6 //[2] 验证Array的构造函数为Function
  7. 7 console.log(Array.constructor == Function); //true
  8. 8 //[3] 验证Array构造函数的原型对象为Function.prototype(空函数)
  9. 9 console.log(Array.__proto__ == Function.prototype); //true
  10. 10 //[4] 验证Array.prototype的构造函数为Object,原型对象为Object.prototype
  11. 11 delete Array.prototype.constructor;
  12. 12 console.log(Array.prototype.constructor == Object); //true
  13. 13 console.log(Array.prototype.__proto__ == Object.prototype); //true

1.3 原型链的访问

原型链的访问规则

对象在访问属性或方法的时候,先检查自己的实例成员,如果存在那么就直接使用,如果不存在那么找到该对象的原型对象,查找原型对象上面是否有对应的成员,如果有那么就直接使用,如果没有那么就顺着原型链一直向上查找,如果找到则使用,找不到就重复该过程直到原型链的顶端,此时如果访问的是属性就返回undefined,方法则报错。

  1. 1 function Person() {
  2. 2 this.name = "wendingding";
  3. 3 }
  4. 4 Person.prototype = {
  5. 5 constructor:Person,
  6. 6 name:"自来熟",
  7. 7 showName:function () {
  8. 8 this.name.lastIndexOf()
  9. 9 }
  10. 10 };
  11. 11 var p = new Person();
  12. 12 console.log(p.name); //访问的是实例成员上面的name属性:wendingding
  13. 13 p.showName(); //打印wendingding
  14. 14 console.log(p.age); //该属性原型链中并不存在,返回undefined
  15. 15 p.showAge(); //该属性原型链中并不存在,报错

概念和访问原则说明
❐ 实例成员:实例对象的属性或者是方法
❐ 原型成员:实例对象的原型对象的属性或者是方法
❐ 访问原则:就近原则

1.4 getPrototypeOf、isPrototypeOf和instanceof

Object.getPrototypeOf方法用于获取指定实例对象的原型对象,用法非常简单,只需要把实例对象作为参数传递,该方法就会把当前实例对象的原型对象返回给我们。说白了,Object的这个静态方法其作用就是返回实例对象__proto__属性指向的原型prototype。

  1. 1 //01 声明构造函数F
  2. 2 function F() {}
  3. 3 //02 使用构造函数F获取实例对象f
  4. 4 var f = new F();
  5. 5 //03 测试getPrototypeOf方法的使用
  6. 6 console.log(Object.getPrototypeOf(f)); //打印的结果为一个对象,该对象是F相关联的原型对象
  7. 7 console.log(Object.getPrototypeOf(f) === F.prototype); //true
  8. 8 console.log(Object.getPrototypeOf(f) === f.__proto__); //true

isPrototypeOf方法用于检查某对象是否在指定对象的原型链中,如果在,那么返回结果true,否则返回结果false。 

  1. 1 //01 声明构造函数Person
  2. 2 function Person() {}
  3. 3 //02 获取实例化对象p
  4. 4 var p = new Person();
  5. 5 //03 测试isPrototypeOf的使用
  6. 6 console.log(Person.prototype.isPrototypeOf(p)); //true
  7. 7 console.log(Object.prototype.isPrototypeOf(p)); //true
  8. 8 var arr = [1,2,3];
  9. 9 console.log(Array.prototype.isPrototypeOf(arr)); //true
  10. 10 console.log(Object.prototype.isPrototypeOf(arr)); //true
  11. 11 console.log(Object.prototype.isPrototypeOf(Person));//true

上述代码的原型链
① p–>Person.prototype –>Object.prototype –>null
② arr–>Array.prototype –>Object.prototype –>null
Object.prototype因处于所有原型链的顶端,故所有实例对象都继承于Object.prototype

instanceof运算符的作用跟isPrototypeOf方法类似,左操作数是待检测的实例对象,右操作数是用于检测的构造函数。如果右操作数指定构造函数的原型对象在左操作数实例对象的原型链上面,则返回结果true,否则返回结果false。

  1. 1 //01 声明构造函数Person
  2. 2 function Person() {}
  3. 3 //02 获取实例化对象p
  4. 4 var p = new Person();
  5. 5 //03 测试isPrototypeOf的使用
  6. 6 console.log(p instanceof Person); //true
  7. 7 console.log(p instanceof Object); //true
  8. 8 //04 Object构造函数的原型对象在Function这个实例对象的原型链中
  9. 9 console.log(Function instanceof Object); //true
  10. 10 //05 Function构造函数的原型对象在Object这个实例对象的原型链中
  11. 11 console.log(Object instanceof Function); //true
注意:不要错误的认为instanceof检查的是该实例对象是否从当前构造函数实例化创建的,其实它检查的是实例对象是否从当前指定构造函数的原型对象继承属性。

我们可以通过下面给出的代码示例来进一步理解

  1. 1 //01 声明构造函数Person
  2. 2 function Person() {}
  3. 3 //02 获取实例化对象p
  4. 4 var p1 = new Person();
  5. 5 //03 测试isPrototypeOf的使用
  6. 6 console.log(p1 instanceof Person); //true
  7. 7 //04 替换Person默认的原型对象
  8. 8 Person.prototype = {
  9. 9 constructor:Person,
  10. 10 showInfo:function () {
  11. 11 console.log("xxx");
  12. 12 }
  13. 13 };
  14. 14 //05 重置了构造函数原型对象之后,因为Person
  15. 15 console.log(p1 instanceof Person); //false
  16. 16 //06 在Person构造函数重置了原型对象后重新创建实例化对象
  17. 17 var p2 = new Person();
  18. 18 console.log(p2 instanceof Person); //true
  19. 19 //==> 建议开发中,总是先设置构造函数的原型对象,之后在创建实例化对象

贴出上面代码的原型链结构图(部分)

1.5 原型链相关的继承

继承是面向对象编程的基本特征之一,JavaScript支持面向对象编程,在实现继承的时候,有多种可行方案。接下来,我们分别来认识下原型式继承、原型链继承以及在此基础上演变出来的组合继承

原型式继承基本写法

  1. 1 //01 提供超类型|父类型构造函数
  2. 2 function SuperClass() {}
  3. 3 //02 设置父类型的原型属性和原型方法
  4. 4 SuperClass.prototype.info = 'SuperClass的信息';
  5. 5 SuperClass.prototype.showInfo = function () {
  6. 6 console.log(this.info);
  7. 7 };
  8. 8 //03 提供子类型
  9. 9 function SubClass() {}
  10. 10 //04 设置继承(原型对象继承)
  11. 11 SubClass.prototype = SuperClass.prototype;
  12. 12 SubClass.prototype.constructor = SubClass;
  13. 13 var sub = new SubClass();
  14. 14 console.log(sub.info); //SuperClass的信息
  15. 15 sub.showInfo(); //SuperClass的信息

贴出原型式继承结构图

提示 该方式可以继承超类型中的原型成员,但是存在和超类型原型对象共享的问题

 

原型链继承

实现思想

核心:把父类的实例对象设置为子类的原型对象 SubClass.prototype = new SuperClass();
问题:无法为父构造函数(SuperClass)传递参数

原型链继承基本写法

  1. 1 //01 提供超类型|父类型
  2. 2 function SuperClass() {
  3. 3 this.name = 'SuperClass的名称';
  4. 4 this.showName = function () {
  5. 5 console.log(this.name);
  6. 6 }
  7. 7 }
  8. 8 //02 设置父类型的原型属性和原型方法
  9. 9 SuperClass.prototype.info = 'SuperClass的信息';
  10. 10 SuperClass.prototype.showInfo = function () {
  11. 11 console.log(this.info);
  12. 12 };
  13. 13 //03 提供子类型
  14. 14 function SubClass() {}
  15. 15 //04 设置继承(原型对象继承)
  16. 16 var sup = new SuperClass();
  17. 17 SubClass.prototype = sup;
  18. 18 SubClass.prototype.constructor = SubClass;
  19. 19 var sub = new SubClass();
  20. 20 console.log(sub.name); //SuperClass的名称
  21. 21 console.log(sub.info); //SuperClass的信息
  22. 22 sub.showInfo(); //SuperClass的信息
  23. 23 sub.showName(); //SuperClass的名称

贴出原型链继承结构图

组合继承

实现思想

① 使用原型链实现对原型属性和方法的继承
② 通过伪造(冒充)构造函数来实现对实例成员的继承,并且解决了父构造函数传参问题

组合继承基本写法

  1. 1 //01 提供超类型|父类型
  2. 2 function SuperClass(name) {
  3. 3 this.name = name;
  4. 4 this.showName = function () {
  5. 5 console.log(this.name);
  6. 6 }
  7. 7 }
  8. 8 //02 设置父类型的原型属性和原型方法
  9. 9 SuperClass.prototype.info = 'SuperClass的信息';
  10. 10 SuperClass.prototype.showInfo = function () {
  11. 11 console.log(this.info);
  12. 12 };
  13. 13 //03 提供子类型
  14. 14 function SubClass(name) {
  15. 15 SuperClass.call(this,name);
  16. 16 }
  17. 17 //(1)获取父构造函数的实例成员 Person.call(this,name);
  18. 18 //(2)获取父构造函数的原型成员 SubClass.prototype = SuperClass.prototype;
  19. 19 SubClass.prototype = SuperClass.prototype;
  20. 20 SubClass.prototype.constructor = SubClass;
  21. 21 var sub_one = new SubClass("zhangsan");
  22. 22 var sub_two = new SubClass("lisi");
  23. 23 console.log(sub_one);
  24. 24 console.log(sub_two);

最后,贴出实例对象sub_one和sub_two的打印结果


js 原型链(转)的更多相关文章

  1. JS原型链

    JS作为发展了多年了对象语言,支持继承,和完全面向对象语言不同的是,JS依赖原型链来实现对象的继承. 首先JS的对象分两大类,函数对象和普通对象,每个对象均内置__proto__属性,在不人为赋值__ ...

  2. 深入分析JS原型链以及为什么不能在原型链上使用对象

    在刚刚接触JS原型链的时候都会接触到一个熟悉的名词:prototype:如果你曾经深入过prototype,你会接触到另一个名词:__proto__(注意:两边各有两条下划线,不是一条).以下将会围绕 ...

  3. js原型链与继承(初体验)

    js原型链与继承是js中的重点,所以我们通过以下三个例子来进行详细的讲解. 首先定义一个对象obj,该对象的原型为obj._proto_,我们可以用ES5中的getPrototypeOf这一方法来查询 ...

  4. JS 原型链图形详解

    JS原型链 这篇文章是「深入ECMA-262-3」系列的一个概览和摘要.每个部分都包含了对应章节的链接,所以你可以阅读它们以便对其有更深的理解. 对象 ECMAScript做为一个高度抽象的面向对象语 ...

  5. 深入理解JS原型链与继承

    我 觉得阅读精彩的文章是提升自己最快的方法,而且我发现人在不同阶段看待同样的东西都会有不同的收获,有一天你看到一本好书或者好的文章,请记得收藏起来, 隔断时间再去看看,我想应该会有很大的收获.其实今天 ...

  6. js 原型链和继承(转)

    在理解继承之前,需要知道 js 的三个东西: 什么是 JS 原型链 this 的值到底是什么 JS 的 new 到底是干什么的 1. 什么是 JS 原型链? 我们知道 JS 有对象,比如 var ob ...

  7. 一张图看懂 JS 原型链

    JS 原型链,画了张图,终于理清楚各种关系有木有 写在最后: __proto__是每个对象都有的一个属性,而prototype是函数才会有的属性!!! function Person() { } 是函 ...

  8. 简单粗暴地理解js原型链–js面向对象编程

    简单粗暴地理解js原型链–js面向对象编程 作者:茄果 链接:http://www.cnblogs.com/qieguo/archive/2016/05/03/5451626.html 原型链理解起来 ...

  9. JS原型链与继承别再被问倒了

    原文:详解JS原型链与继承 摘自JavaScript高级程序设计: 继承是OO语言中的一个最为人津津乐道的概念.许多OO语言都支持两种继承方式: 接口继承 和 实现继承 .接口继承只继承方法签名,而实 ...

  10. 02 js原型链

    1 js原型链是一个绕不开的话题.直接上说吧. /** * 1. js里的原型链是怎么样的? 带class 和不带class的原型链的不同. */ const util = require('util ...

随机推荐

  1. EXCEL行倒叙

  2. 分页用到的子查询sql语句

    说明(2017-8-31 23:30:22): 1. 分页用到的子查询sql语句 select * from(select *,ROW_NUMBER() over(order by id)as num ...

  3. C#学习笔记(17)——C#中接口的作用

    说明(2017-7-17 23:50:48): 原文: 接口的作用 接口的作用(C#)(另一篇) C#接口是一个让很多初学C#者容易迷糊的东西,用起来好像很简单,定义接口,里面包含方法,但没有方法具体 ...

  4. iOS增加pch预加载文件

    1.)  打开你的Xcode工程. 在Supporting Files目录下,选择 File > New > File > iOS > Other > PCH File ...

  5. 基于jQuery实现的腾讯互动娱乐网站特效

    分享一款基于jQuery实现的腾讯互动娱乐网站特效.腾讯互动娱乐网站jQuery特效是一款右侧带伸缩选项卡,支持鼠标滚轮滚动切换特效代码.效果图如下: 在线预览   源码下载 实现的代码. html代 ...

  6. bootstarp-table 设置隐藏列

    在做隐藏列的时候会发现一个问题. var settings = { url: ctx + "rollapply/list", pageSize: 10, queryParams: ...

  7. JAVA传入一个字符串,返回一个字符串中的大写字母

    /**      *       * @param 传入一个字符串      * @return 返回一个字符串中的大写字母      */     private static String str ...

  8. C语言 · x的x次幂结果为10

    如果x的x次幂结果为10(参见[图1.png]),你能计算出x的近似值吗? 显然,这个值是介于2和3之间的一个数字. 请把x的值计算到小数后6位(四舍五入),并填写这个小数值. 注意:只填写一个小数, ...

  9. 微信JSSDK接口,previewImage

    原文:https://www.hackhp.com/801.html 在微信里看过文章的应该知道,文章里的图片点击后可以放大.分享和保存. 然而自己在微信里开发的网页,里面的图片点击后没办法实现这个效 ...

  10. [openssl]openssl特定版本安装

    卸载旧版本 OpenSSL1. apt-get purge openssl2. rm -rf /etc/ssl #删除配置文件编译与安装 OpenSSLprefix 是安装目录,openssldir ...