学习JavaScript过程中,原型问题一直让我疑惑许久,那时候捧着那本著名的红皮书,看到有关原型的讲解时,总是心存疑虑。
当在JavaScript世界中走过不少旅程之后,再次萌发起研究这部分知识的欲望,翻阅了不少书籍和资料,才搞懂__proto__prototype的概念。
故以作此笔记,日后忘了可以回来看看。
如果你看的过程中觉得理解有些困难,把例子在代码中跑一跑,亲手试一试也许能解决不少疑惑。

一切皆为对象

殊不知,JavaScript的世界中的对象,追根溯源来自于一个 null

「一切皆为对象」,这句着实是一手好营销,易记,易上口,印象深刻。
万物初生时,一个null对象,凭空而生,接着ObjectFunction学着null的模样塑造了自己,并且它们彼此之间喜结连理,提供了prototypeconstructor,一个给子孙提供了基因,一个则制造万千子子孙孙。
在JavaScript中,null也是作为一个对象存在,基于它继承的子子孙孙,当属对象。
乍一看,null像是上帝,而ObjectFunction犹如JavaScript世界中的亚当夏娃

原型指针 __proto__

在JavaScript中,每个对象都拥有一个原型对象,而指向该原型对象的内部指针则是__proto__,通过它可以从中继承原型对象的属性,原型是JavaScript中的基因链接,有了这个,才能知道这个对象的祖祖辈辈。从对象中的__proto__可以访问到他所继承的原型对象。

var a = new Array();
a.__proto__ === Array.prototype // true

上面代码中,创建了一个Array的实例a,该实例的原型指向了Array.prototype
Array.prototype本身也是一个对象,也有继承的原型:

a.__proto__.__proto__ === Object.prototype  // true
// 等同于 Array.prototype.__proto__ === Object.prototype

这就说了明了,Array本身也是继承自Object的,那么Object的原型指向的是谁呢?

a.__proto__.__proto__.__proto__ === null  // true
// 等同于 Object.prototype.__proto__ === null

prototype.png

所以说,JavaScript中的对象,追根溯源都是来自一个null对象。佛曰:万物皆空,善哉善哉。

除了使用.__proto__方式访问对象的原型,还可以通过Object.getPrototypeOf方法来获取对象的原型,以及通过Object.setPrototypeOf方法来重写对象的原型

值得注意的是,按照语言标准,__proto__属性只有浏览器才需要部署,其他环境可以没有这个属性,而且前后的两根下划线,表示它本质是一个内部属性,不应该对使用者暴露。因此,应该尽量少用这个属性,而是用Object.getPrototypeofObject.setPrototypeOf,进行原型对象的读写操作。
这里用__proto__属性来描述对象中的原型,是因为这样来得更加形象,且容易理解。

原型对象 prototype

函数作为JavaScript中的一等公民,它既是函数又是对象,函数的原型指向的是Function.prototype

var Foo = function() {}
Foo.__proto__ === Function.prototype // true

函数实例除了拥有__proto__属性之外,还拥有prototype属性。
通过该函数构造的新的实例对象,其原型指针__proto__会指向该函数的prototype属性。

var a = new Foo();
a.__proto__ === Foo.prototype; // true

而函数的prototype属性,本身是一个由Object构造的实例对象。

Foo.prototype.__proto__ === Object.prototype; // true

prototype属性很特殊,它还有一个隐式的constructor,指向了构造函数本身。

Foo.prototype.constructor === Foo; // true
a.constructor === Foo; // true
a.constructor === Foo.prototype.constructor; // true

prototype2.png

原型链

概念

原型链作为实现继承的主要方法,其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。
每个构造函数都有一个原型对象(prototype),原型对象都包含一个指向构造函数的指针(constructor),而实例都包含一个指向原型对象的内部指针(__proto__)。
那么,假如我们让原型对象等于另一个类型的实例,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立。
如此层层递进,就构造了实例与原型的链条,这就是原型链的基本概念。

意义

“原型链”的作用在于,当读取对象的某个属性时,JavaScript引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。以此类推,如果直到最顶层的Object.prototype还是找不到,则返回undefine

亲子鉴定

在JavaScript中,也存在鉴定亲子之间DNA关系的方法:

  1. instanceof
    运算符返回一个布尔值,表示一个对象是否由某个构造函数创建。
  2. Object.isPrototypeOf()
    只要某个对象处在原型链上,isProtypeOf都返回true
var Bar = function() {}
var b = new Bar();
b instanceof Bar // true
Bar.prototype.isPrototypeOf(b) // true
Object.prototype.isPrototypeOf(Bar) // true

要注意,实例b的原型是Bar.prototype而不是Bar

一张历史悠久的图

jsobj_full.jpg

这是一张描述了ObjectFunction以及一个函数实例Foo他们之间原型之间联系。如果理解了上面的概念,这张图是不难读懂。

从上图中,能看到一个有趣的地方。
Function.prototype.__proto__ 指向了 Object.prototype,这说明Function.prototype 是一个 Object实例,那么应当是先有的Object再有Function
但是Object.prototype.constructor.__proto__ 又指向了 Function.prototype。这样看来,没有FunctionObject也不能创建实例。
这就产生了一种类「先有鸡还是先有蛋」的经典问题,到底是先有的Object还是先有的Function呢?
这么哲学向的问题,留给你思考了。
我只是感慨:
越往JavaScript的深处探索,越发觉得这一门语言很哲学。

文/JC_Huang(简书作者)
原文链接:http://www.jianshu.com/p/3bb6f208e459
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

JavaScript 原型中的哲学思想的更多相关文章

  1. js中的原型哲学思想

    https://segmentfault.com/a/1190000005824449 记得当年初试前端的时候,学习JavaScript过程中,原型问题一直让我疑惑许久,那时候捧着那本著名的红皮书,看 ...

  2. javascript原型链中 this 的指向

    为了弄清楚Javascript原型链中的this指向问题,我写了个代码来测试: var d = { d: 40 }; var a = { x: 10, calculate: function (z) ...

  3. C# -- 等待异步操作执行完成的方式 C# -- 使用委托 delegate 执行异步操作 JavaScript -- 原型:prototype的使用 DBHelper类连接数据库 MVC View中获取action、controller、area名称、参数

    C# -- 等待异步操作执行完成的方式 C# -- 等待异步操作执行完成的方式 1. 等待异步操作的完成,代码实现: class Program { static void Main(string[] ...

  4. javascript --- 将共享属性迁移到原型中去

    当我们用一个构造函数创建对象时,其属性就会被添加到this中去.并且被添加到this中的属性实际上不会随着实体发生改变,这时,我们这种做法显得会很没有效率.例如: function her(){ th ...

  5. [Effective JavaScript 笔记]第34条:在原型中存储方法

    js中完全有可能不借助原型进行编程.不用在其原型中定义任何的方法. 创建对象 构造函数法 所有属性和方法都在构造函数中定义 function User(name,pwd){ this.name=nam ...

  6. javascript继承机制的设计思想(ryf)

    我一直很难理解Javascript语言的继承机制. 它没有"子类"和"父类"的概念,也没有"类"(class)和"实例" ...

  7. javascript原型对象

    先来做个复习,ES5中有有几种数据类型呢? 5种基本数据类型 Undefined Null Boolean Number String 1种复杂数据类型 Object 除了基本数据类型,万物皆对象,记 ...

  8. 从mixin到new和prototype:Javascript原型机制详解

    从mixin到new和prototype:Javascript原型机制详解   这是一篇markdown格式的文章,更好的阅读体验请访问我的github,移动端请访问我的博客 继承是为了实现方法的复用 ...

  9. 【转载】Javascript原型继承-学习笔记

    阮一峰这篇文章写的很好 http://www.ruanyifeng.com/blog/2011/06/designing_ideas_of_inheritance_mechanism_in_javas ...

随机推荐

  1. MSIL实用指南-局部变量的声明、保存和加载

    这一篇讲解方法内的局部变量是怎么声明.怎样保存.怎样加载的. 声明局部变量声明用ILGenerator的DeclareLocal方法,参数是局部变量的数据类型,得到一个局部变量对应的创建类LocalB ...

  2. syskey——让你的电脑更加安全

    我之前介绍过一个绕过系统登录密码的工具kon-boot,今天介绍的就是可以防止这个工具的方法,也能让你的电脑更加的安全. 这个方法也是我在Youtube上看见的一个方法,还是不错. 方法: win+R ...

  3. 筛法求素数Java

    输出:一个集合S,表示1~n以内所有的素数 import java.util.Scanner; public class 筛法求素数 { public static void main(String[ ...

  4. ASP.NET Core 2.0 : 八.图说管道

    本文通过一张GIF动图来继续聊一下ASP.NET Core的请求处理管道,从管道的配置.构建以及请求处理流程等方面做一下详细的研究.(ASP.NET Core系列目录) 一.概述 上文说到,请求是经过 ...

  5. 【Linux】 环境变量与shell配置&执行

    ■ 变量与环境变量 shell环境通常存在很多变量,变量可以通过echo $VAR或${VAR}的方式查看.set命令可以查看当前环境中的所有变量(包括一般的自定义变量和环境变量) 变量的设置通过简单 ...

  6. 【JS】 Javascript 入门

    javascript **********本章大量示例和内容引用自w3cschool的javascript教程************** 本来已经快写完90%左右了,结果不小心跑了个js,不小心把浏 ...

  7. SQL语法语句总结

    一.SQL语句语法 ALTER TABLE ALTER TABLE 用来更新已存在表的结构. ALTER TABLE tablename (ADD|DROP column datatype [NULL ...

  8. [mysql] MySQL解压缩安装步骤

    以前装的MySQL出问题了,只好卸载了. 又下载了一个mysql-5.6.24-win32.1432006610.zip.msi文件直接安装就行了.这里需要解压到指定目录,配置后可使用. 环境变量配置 ...

  9. java程序性能调优---------------性能概述

    一.程序的性能通过哪几个方面表现 1.执行速度(程序反应反应是否迅速.响应时间是否足够短) 2.分配内存 (分配内存是否合理,是否过多的消耗内存或者内存溢出) 3.启动时间(程序从运行到可以正常处理业 ...

  10. 浅谈-RMQ

    浅谈RMQ Today,我get到了一个新算法,开心....RMQ. 今天主要说一下RMQ里的ST算法(Sparse Table). RMQ(Range Minimum/Maximum Query), ...