一.从一个小例子到原型链

  • 豆豆家是一个商业世家,豆豆的爷爷创建了世界上最大的商业中心
  • 豆豆的爸爸创建了世界上最大的餐饮连锁店
  • 而豆豆开建了豆豆爸和豆豆爷都没有创建的电竞城
  • 但是,豆豆爸没有告诉豆豆爷他创建的餐饮连锁店,豆豆也没有告诉豆豆爸他创建的店
  • 在豆豆想要带朋友去玩免费电竞的时候,想起自己有建一个,他就会去自己的电竞城
  • 在豆豆想带朋友去免费吃饭的时侯,想到自己没有建餐厅,就会去找豆豆爸问,豆豆爸想到自己有餐厅,就让豆豆去这个地方
  • 在豆豆想要去免费商业城shopping的时候,想到自己没有建,他就会去问豆豆爸哪里有,豆豆爸想到自己没有建,就会去问豆豆爷,豆豆爷想到自己有,就会让豆豆去他的商业中心
  • 在豆豆去完爷爷的商业中心,发现爷爷商场的衣服一点也不适合自己还有朋友,所以他建了自己的时尚服装店,所以下次去买衣服只需要去自己的店就可以了
  • 有一天,他的朋友想去海洋馆,豆豆想到自己没有,问了豆豆爸,豆豆爸也无,然后问豆豆爷,豆豆爷也无,所以告诉豆豆没有

二.用代码模拟一下这个小例子

//爷爷
let GrandFather = function(){this.shopping= function(){console.log("免费购物");}
}
let grandFather = new GrandFather();
//爸爸
let Dad = function(){this.restaurant = function(){console.log("免费吃饭");}
}
let dad = new Dad();
//儿子
let Son = function () {this.Gaming = function () {console.log("免费游戏");}
}
//此处代码暂时不说明
// 形成继承关系(头)
Dad.prototype = new GrandFather();
Dad.prototype.constructor = Dad;
Son.prototype = new Dad();
Son.prototype.constructor = Son;
// 形成继承关系(尾)
let son = new Son();
son.Gaming();//豆豆去玩电竞
son.restaurant();//豆豆去吃饭
son.shopping();//豆豆去购物
Son.prototype.shopping = function(){console.log("免费时尚购物");
}
son.shopping();//豆豆创建了一个新的商店后去购物

三.原型的一般规则

  1. 构造函数有其对应的显式原型的,该显示原型有一个属性constructor指向构造函数本身

    • 此处以豆豆为例,其构造函数Son()有一个对应的显示原型Son.prototype,这个原型有一个属性constructor指向Son()这个构造函数,即:
      Son.prototype.constructor === Son
    • 所以 let son = new Son() 和 let son = new Son.prototype.constructor() 是一样的
  2. 实例对象的隐式原型属性__proto__都对应其构造函数的原型(Object例外)
    • 依旧以豆豆为例,那么豆豆的实例son的隐式原型属性就和其构造函数Son()的隐式原型Son.prototype相等,即

      son.__proto__===Son.prototype
      
    • 这里再聊一些特殊的东东去加深这个,经过测试可以发现所有构造函数的隐形原型属性都是Function.prototype,也就是说所有的构造函数都是Function()的实例(包括它自己),即:
        console.log(Son.__proto__===Function.prototype)//trueconsole.log(Dad.__proto__===Function.prototype)//trueconsole.log(GrandFather.__proto__===Function.prototype)//trueconsole.log(Function.__proto__===Function.prototype)//true
    
  3. 构造函数的原型的隐式原型属性默认是Object的实例
    • 依旧以豆豆为例,那么在豆豆Son()这个构造函数创建的时候,就有:
    • Son.prototype = new Object()
    • 同时需要注意的是,Object.prototype = null,这是为了防止原型链无限循环设计的

四.关于原型链

首先,一个实例对象在调用其方法属性的时候,先会从自身寻找属性方法,如

son.say = function(){console.log("豆豆会说话");
}
son.say();//豆豆会说话

我们在实例Son对象后,在它的实例son上添加了新的方法say(),那么我们就可以去调用它,同时我们也知道son的构造函数中有Gaming()方法,也是可以直接调用的,这是我们在未学习原型和原型链知识前就知道的。而回到我们刚开始的例子中,我们或者会疑惑,为什么son这个实例可以使用Dad对象和Grandfather对象的方法呢?而答案就在我们未解释的代码之上,现在让我们来讲解它:

  • 当一个对象访问其属性时,会先从自身的属性列表上寻找,如果没有找到,就会通过其隐式原型属性到其原型上寻找,循环(前面说到的:对象原型是某个对象的实例,实例对象都有隐式原型属性),直至找到,或者
    未找到返回undefined(调用方法就返回什么什么不是一个方法)。

这里建议回去看开篇的那个小故事再理解一下,然后以豆豆实例son要去自家免费3d体验馆Experience3d()为例,

  1. 豆豆会先查看自身的属性方法:say()、Gaming(),没有Experience3d()方法,
  2. 然后就到其隐式原型上查找,Son.prototype,即Dad对象的实例,拥有所有的Dad对象的方法,restaurant(),以及添加在Son原型即Dad对象实例上的方法(就是下面这个代码):shopping(),也没有想要的方法,
    Son.prototype.shopping = function(){console.log("免费时尚购物");
    }
    
  3. 这个时候就会到Son原型的隐式原型,也就是Dad对象实例的隐式原型即Dad.prototype上查找,Dad.prototype是Grandfather对象的实例对象,拥有所有的Grandfather对象的属性和方法,即shopping(),也没有找到,
  4. 然后就会去Dad.prototype也就是Grandfather对象的实例的隐式原型即Grandfather.prototype上查找,Grandfather.prototype默认是Object对象的实例,拥有所有Object对象的方法,如toString()等,但是也没有想要的方法Experience3d(),
  5. 然后就会去查找Grandfather.prototype也就是Object对象实例的隐式原型属性,即null,也就找不到这个方法,返回undefined,调用它就会报错“Experience3d不是一个方法”(因为它是undefined)
  • 而这个查找的过程就是链式的,我们就将其称为原型链。(建议自己推演一下上面那个过程,如果有疑问的地方,回去再查看一下“原型的一般规律”)

这里放个丑丑的图:

五.了解原型链的内部

这里放个图,其实就是讲一下设计者的最初设计:

图中可以看到一些有意思的点:

  1. Object()构造函数的隐式原型是Function.prototype,而Function.prototype的隐式原型是Object.prototype
  2. Function()构造函数是自己的实例

六. 原型链的使用

1. 形成继承关系

这里继续使用上面的代码

//爷爷
let GrandFather = function(){this.shopping= function(){console.log("免费购物");}
}
let grandFather = new GrandFather();
//爸爸
let Dad = function(){this.restaurant = function(){console.log("免费吃饭");}
}
let dad = new Dad();
//儿子
let Son = function () {this.Gaming = function () {console.log("免费游戏");}
}
// 形成继承关系
Dad.prototype = new GrandFather();
Dad.prototype.constructor = Dad;
Son.prototype = new Dad();
Son.prototype.constructor = Son;

通过修改构造函数原型的指向,再修改构造函数原型的构造函数(不修改会指向与原型相同的那个对象的构造函数,如Son.prototype.constructor = Son若不加,那么Son.prototype.constructor===Dad),就可以形成继承关系。

2. 实例共享方法

再次看向我们之前的代码:

Son.prototype.shopping = function(){console.log("免费时尚购物");
}

这里我们向Son的原型新增了一个方法shopping(),那么son的实例均可以使用这个shopping()方法,但是,这和直接在Son的构造函数里加shopping()方法有什么区别呢?区别在于:

使用Son创建的shopping()方法会在每个Son()构造函数实例创建一个shopping()方法,而由构造函数原型所创建的shopping()只会创建一次,被所有Son()构造函数所创建的实例共享

这个实际上也很容易理解,因为Son.prototype = new Dad();也就是说Son.prototype指向一个对象实例,那么这个实例是一次性创造的,而对他添加方法,就是实例对象添加方法,而每个Son对象实例通过原型链访问的都是同一个Dad实例,那shopping()方法肯定是同一个。
即:

let son2 = new Son()//son2就当作是豆豆弟弟吧
console.log(son.__proto__===son2.__proto__)//true

总之,通过这个方法实现实例间共享方法,可以节省内存空间

七. 总结

放一下上面用到的全部代码吧,如果有疑问,可以选择带着怀疑的态度去验证,代码也最好自己试试哦!(大佬除外)

//爷爷
let GrandFather = function(){this.shopping= function(){console.log("免费购物");}
}
let grandFather = new GrandFather();
//爸爸
let Dad = function(){this.restaurant = function(){console.log("免费吃饭");}
}
let dad = new Dad();
//儿子
let Son = function () {this.Gaming = function () {console.log("免费游戏");}
}
//此处代码暂时不说明
// 形成继承关系(头)
Dad.prototype = new GrandFather();
Dad.prototype.constructor = Dad;
Son.prototype = new Dad();
Son.prototype.constructor = Son;
// 形成继承关系(尾)
let son = new Son();
son.Gaming();//豆豆去玩电竞
son.restaurant();//豆豆去吃饭
son.shopping();//豆豆去购物
Son.prototype.shopping = function(){console.log("免费时尚购物");
}
son.shopping();//豆豆创建了一个新的商店后去购物console.log(son.__proto__===Son.prototype)console.log(Son.__proto__===Function.prototype)//true
console.log(Dad.__proto__===Function.prototype)//true
console.log(GrandFather.__proto__===Function.prototype)//true
console.log(Function.__proto__===Function.prototype)//trueson.say = function(){console.log("豆豆会说话");
}son.say();//豆豆会说话
console.log(Son.prototype)let son2 = new Son()//son2就当作是豆豆弟弟吧
console.log(son.__proto__===son2.__proto__)//true

我觉得我这篇文章还是不错的吧,可以的话给我点个赞吧,球球了!球球了!

最新文章

  1. 模仿SDWebImage实现异步加载图片
  2. Sql Server相关报错解决
  3. bzoj1297 [SCOI2009]迷路——拆点+矩阵快速幂
  4. 让AMD在中国发声 APU14技术创新大会首次在华召开
  5. JavaScript中错误正确处理方式,你用对了吗? 1
  6. 知道答案吗?知道为什么是这个答案吗?
  7. stm32单片机屏幕一直闪_用STM32做一个微型掌上示波器项目——终于鼓起在大牛前献丑的无比勇气...
  8. uniapp 如何给搜索框设值_uni-app搜索功能前后端开发(页面)
  9. Linux下DIR,dirent,stat等结构体详解
  10. 28留数及其应用(四)
  11. 面向对象淡入淡出轮播图(附带面向过程)
  12. Permission denied (publickey) 解决方案
  13. Pandas系列(十)Merge语法
  14. RFC chinese
  15. 上海海事大学c语言题库,上海海事大学,C语言试卷6
  16. 基于web的仿Steam游戏网
  17. 微信联盟链接不到服务器怎么,LOL微信登不上去怎么办?微信登不上解决方法推荐...
  18. 何时需要对时间序列进行Log Transformation
  19. oracle 数据占百分比,占总数比例怎么算_占总金额百分比公式
  20. zznu 2054 : 油田

热门文章

  1. 水星路由器wan口ip显示0_水星路由器WAN口获取不到ip地址如何解决
  2. 又是一年的高峰跳槽季,你到底是跳还是留呢?
  3. python网络爬虫原理
  4. Python网络爬虫-原理及基础知识
  5. 简单实现美团手机版应用源码
  6. 【誉天教育】11月28日,云计算HCIE直通车周末班,邹老师带班
  7. warning C4003: not enough arguments for function-like macro invocation ‘min‘
  8. mq mysql复制_activemq持久化配置,设置为主从模式(带复制的主从模式,应用mysql数据库)...
  9. 基于KepServer实现与S7-1200PLC之间的通信
  10. python哪个方向最容易上手的英雄_王者荣耀操作最简单十大英雄有谁?用好了快乐上分不用愁...