[转] Javascript 原型链
1. 类
在C或者Java里,int a;定义了一个int类型的变量a。其中int是类型的名字,a是具体的变量。
Javascript 模仿自 Java, 有一部分面向对象编程的部分。在面向对象的编程中,类就是像int那样是类型,对象就是像a那样的变量。他们的区别是:int是编译器预先定义好的类型,但是类是我们自定义的类型,需要自行写代码创建这个类以及这个类型的变量。比如:
| 1 | class Point { | 
可是 Javascript 是弱类型,写出了Point类型又有什么意义呢,又不能写Point p = xx。于是在JS里面把类型和函数合二为一。当定义了一个函数,那么这个变量名称既是一个函数也是一个类型的名称,并且这个函数是这个类型的构造函数(也是一个普通的函数)。
| 1 | function Point(x,y) | 
这样Point有三个意义:1. Point类型;2. Point 是Point类的构造函数;3. Point 是一个普通的函数。这样做不会出现歧义,因为没有Point p这种写法,所以在p instanceof Point中Point表示一个类型,在new Point()中表示构造函数,其他情况下Point就是一个普通的函数。不过也没有太大的必要去区分Point是构造函数,还是普通函数。一般情况下构造函数中要给this添加属性。普通函数调用里对this的操作是不建议的。但是这样做存在一个问题:
| 1 | var p1 = new Point(1,2); | 
即p1.show和p2.show是不同的函数,但是他们却有着相同的代码。这样在内存中是一种浪费,尤其是类中的方法和该类创建的变量都很多的时候。(p1.x和p2.x各自占用一个内存是必要的,因为他们需要保存不同的值) 下面是一种解决方法:
| 1 | var PointMethod = {  // 把所有方法放到一个额外的对象中 | 
这样有点儿麻烦,但是 Javascript 正是使用了这种方法。解释器帮我们处理了一部分工作,所以我们方便的写出和上面工作原理一样的如下代码:

p1p2都可以通过自己的__proto__读取show方法
| 1 | function Point(x,y) | 
在 javascript 里面有两个特殊的类:Object,Function。所有对象都是Object类的,所有函数都是Function类的,再加上函数也是对象,于是他们之间有着微妙的关系。
| 1 | var obj = {}; // 等价于 var obj = new Object(); | 
2. 继承
面向对象的语言还有一个精髓是继承。
| 1 | // NamedPoint 继承 Point | 
其实要实现这样的方案也是不是很难,用之前面说过的知识就可以了。前面说过,当读取np.show的时候其实解释器读取的是以np.show, np.__proto__.show, np.__proto__.__proto__.show …的顺序找的的第一个值。在NamedPoint里面没有show 方法,即np.show, np.__proto__.show都是不存在的。那么我们让np.__proto__.__proto__ 等于 Point.prototype 那么np.show读取的就是Point中定义的方法show了!即np.__proto__(NamedPoint.prototype) 是Point类的变量。即实现如图所示的结构:

解决方案就现而易见了:
| 1 | function NamePoint(name,x,y){ | 
虽然可以,但是比较麻烦,已经是javascript(ES5.1)里面最好的解决方案了。那么Point类继承了什么类?在上面的代码中,无论是默认的还是自定义的Point.prototype都是Object类。所以Point继承了Object类。这就是说np.__proto__.__proto__.__proto__是Object.prototype。所以:
| 1 | p.toString === Object.prototype.toString; // true | 

Point类继承了Object类,所以p可以通过上述方式访问Object类的方法toString
可见,子类继承了父类的所有方法,所以如果一个地方可以写下p.show()那么np.show()同样是可以的。所以一个父类变量出现的地方,出现子类是没有问题的(当然结果可能不是你想要的)。如果把 np 当作Point 类也是没有问题的,只是这样用浪费了部分内存。np instanceof Point 可以返回 true。于是,instanceof 就是检查Point.prototype是否在np的原型链(__proto__形成的链表)里面了。
任何对象通过递归的访问__proto__,最终的结果都是Object.prototype,所以说,所有的对象都继承了Object类的方法,所有对象都是Object类的,Object类是所有类的父类。仔细研究一下下图还是大有裨益的:

Object,Function,Point都是类名即也是函数,所以都是Function类的对象。p是Point类的对象,Function.prototype 和Point.prototype 都是Object类的对象,所以他们的__proto__分别是什么已经很明确了。
3. 重载
NamedPoint.prototype.show 是 NamedPoint.prototype.__proto__.show,即Point.prototype.show。这仅仅是在读取的时候是这样的,但是写入的时候就不是这样了,写入仅仅会在NamedPoint.prototype中添加/改变这个属性,而不会去原型链中去修改这个属性。
| 1 | //写入时 Namepoint.prototype.show 不存在就创建新的,而不会去原型链中去查找 | 
这样可以实现类中方法的重载,即NamedPoint 继承了Point中的方法,同时也重新定义了show方法。当然也可以给每个对象重载新的方法。

4. 其他
Point.prototype 可以被赋值为基本类型。此时执行,var p = new Point(),p.__proto__会被赋值为Object.prototype
不建议直接操作一个对象的__proto__属性,虽然在某些nodejs的代码里经常见到。
Point.prototype 可以不只是函数(方法),也可以是变量。只是如果只能从原型链上读取的话,那么就是一个常量。而一个类的常量一般作为类的一个属性,比如Number.MAX_VALUE。如果当作类内的静态变量,为什么不用闭包呢?
把所有的Point类型变量替换为NamedPoint是语法上不会出错,但逻辑上结果是否相同要靠自己保证。但是有一种设计模式叫做”里氏替换原则”,就是说要保证这种替换不会改变程序的运行结果。
P.__proto__ = Point.prototype 是在p = new Point()的时候发生的:
| 1 | function Point() {} | 
由于 Point 既是一般函数,又是构造函数,区别就是有没有new,可人总是要犯错误的,有些时候想把其当作构造函数却忘记写new就不好了。下面是一种还可以的解决方法,虽然不完美,但是足够了。
| 1 | function Point(x,y) | 
一个方便继承的函数,随着ES2015的到来,可能用处不大了,仅供参考
| 1 | var hasOwn = Object.hasOwnProperty; | 
[转] Javascript 原型链的更多相关文章
- JavaScript学习总结(十七)——Javascript原型链的原理
		一.JavaScript原型链 ECMAScript中描述了原型链的概念,并将原型链作为实现继承的主要方法.其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法.在JavaScript中, ... 
- javascript原型链中 this 的指向
		为了弄清楚Javascript原型链中的this指向问题,我写了个代码来测试: var d = { d: 40 }; var a = { x: 10, calculate: function (z) ... 
- 明白JavaScript原型链和JavaScrip继承
		原型链是JavaScript的基础性内容之一.其本质是JavaScript内部的设计逻辑. 首先看一组代码: <script type="text/javascript"&g ... 
- Javascript 原型链资料收集
		Javascript 原型链资料收集 先收集,后理解. 理解JavaScript的原型链和继承 https://blog.oyanglul.us/javascript/understand-proto ... 
- 资料--JavaScript原型链
		JavaScript原型链 原文出处:https://www.cnblogs.com/chengzp/p/prototype.html 目录 创建对象有几种方法 原型.构造函数.实例.原型链 inst ... 
- JavaScript原型链:prototype与__proto__
		title: 'JavaScript原型链:prototype与__proto__' toc: false date: 2018-09-04 11:16:54 主要看了这一篇,讲解的很清晰,最主要的一 ... 
- JavaScript原型链及其污染
		JavaScript原型链及其污染 一.什么是原型链? 1.JavaScript中,我们如果要define一个类,需要以define"构造函数"的方式来define: functi ... 
- 图解Javascript原型链
		本文尝试阐述Js中原型(prototype).原型链(prototype chain)等概念及其作用机制.上一篇文章(图解Javascript上下文与作用域)介绍了Js中变量作用域的相关概念,实际上关 ... 
- 画一画javascript原型链
		在javascript中,几种数据类型String,Number,Boolean,Object,Function都是函数,可称之为函数对象. 可以说拥有prototype属性的都是函数. 所有对象都拥 ... 
- JavaScript原型链和instanceof运算符的暧昧关系
		时间回到两个月前,简单地理了理原型链.prototype以及__proto__之间的乱七八糟的关系,同时也简单了解了下typeof和instanceof两个运算符,但是,anyway,试试以下两题: ... 
随机推荐
- Windows PowerShell 入門(10)-デバッグ編
			対象読者 Windows PowerShellでコマンドレット操作ができる方 何らかのプログラミング経験があればなお良い 必要環境 Windows PowerShell デバッグメッセージの出力 Po ... 
- vue生命周期学习(watch跟computed)
			1.watch钩子函数监听数据的变化 watch 的一个特点是,最初绑定的时候是不会执行的,要等到 firstName 改变时才执行监听计算. <div> <p>FullNam ... 
- Windows登录类型及安全日志解析
			Windows登录类型及安全日志解析 一.Windows登录类型 如果你留意Windows系统的安全日志,在那些事件描述中你将会发现里面的“登录类型”并非全部相同,难道除了在键盘上进行交互式登录(登录 ... 
- WM_COMMAND消息
			原文地址:https://blog.csdn.net/whm243149796/article/details/78966065 当用户点击菜单.按钮.下拉列表框等控件时候,会触发WM_COMMAND ... 
- 使用python调用淘宝的ip地址库查询接口结合zabbix判断dnspod域名解析是否正确
			#encoding:utf-8 import socket import requests import json ''' 使用python结合zabbix判断dnspod域名解析是否正确 服务器分国 ... 
- FS 日志空间限定
			一.说明: FS默认安装的log文件,仅仅的限制了每个文件的大小,但是没有限制文件的个数.这种情况下,在FS运行很长时间之后,会出现物理空间不够的情况,导致FS或者mysql 或者其他应用没有空间使用 ... 
- ASP.NET MVC5高级编程 之 视图
			1.1理解视图约定 当创建一个项目模版时,可以注意到,项目以一种非常具体的方式包含了一个结构化的Views目录.在每一个控制器的View文件夹中,每一个操作方法都有一个同名的视图文件与其对应.这就提供 ... 
- Cocos2d-x中文显示乱码
			Cocos2d-x 引擎编码格式默认为utf8,而VS开发环境默认为gbk2312,所以把代码文件保存为utf8格式就能解决. VS->文件->高级保存选项->Unicode(UTF ... 
- android系统下消息推送机制
			一.推送方式简介: 当前随着移动互联网的不断加速,消息推送的功能越来越普遍,不仅仅是应用在邮件推送上了,更多的体现在手机的APP上.当我们开发需要和服务器交互的应用程序时,基本上都需要获取服务器端的数 ... 
- Android:图解四种启动模式 及 实际应用场景解说
			在一个项目中会包括着多个Activity,系统中使用任务栈来存储创建的Activity实例,任务栈是一种“后进先出”的栈结构.举个栗子,若我们多次启动同一个Activity.系统会创建多个实例依次放入 ... 
