一、正统的类与继承

类是对象的定义,而对象是类的实例(Instance)。类不可直接使用,要想使用就必须在内存上生成该类的副本,这个副本就是对象。

以Java为例:

public class Group { } // 创建一个类

Group a = new Group(); // 实例化一个对象

通过继承,子类可以直接从父类获得其所有的属性和方法,继承的实现机制是"复制、拷贝"。

public class Child extends Parent { } // 创建一个子类,继承父类的方法

二、原型与继承 

和正统的面向对象语言不同,JavaScript中不存在正统意义的"类"。原因是Brendan Eich在设计JavaScript的时候,不希望又重新设计一门面向对象的语言(因为已经有C++和Java了),并且他希望把JavaScript设计得更简单,因此他借鉴了Java的语法,用构造函数代替类,所以JavaScript中的对象是通过构造函数创建的。

这并不奇怪,JavaScript本身就是一个借鉴多种语言,仓促交媾的产物。

严格的讲,JavaScript中既不存在类,也不存在实例,尽管这些概念是如此的深入人心,以致于误用起来是那么顺其自然,但我们还是需要明白这一点。

没有类,那么JavaScript怎么实现继承呢? Brendan Eich为构造函数设置了一个prototype属性。这个属性包含一个对象(即"原型对象"),所有实例对象需要共享的属性和方法,都放在这个对象里面;不需要共享的属性和方法,就放在构造函数里面。

与类继承的"复制、拷贝"不同,原型继承的机制是"引用、关联"。JavaScript中所有的对象都是由构造函数生成的,每个对象都共同继承构造函数的原型对象中的属性和方法。

在对象内部有两种方式访问它继承的原型:

1、obj.constructor.prototype

2、obj.__proto__

第一种方法,源于每个对象都拥有一个内置属性constructor指向其构造函数;第二种方法源于每个对象都有一个内部指针[[prototype]],直接指向其原型,这个内部指针是不可访问的,但是有些浏览器暴露出一个__proto__属性,用于直接访问原型对象。

三、原型链与继承

因为所有对象都是由构造函数生成的,也就是说所有对象,都必然继承某个原型,而原型本身也是对象,它也会继承别的原型,如此环环相扣就形成了原型链。

JavaScript的对象系统是一个类似族谱的树状结构,每一个分支都通过原型链一脉相承。

   基于原型链的继承机制和作用域链的工作机制非常类似:当调用对象的某个属性时,如果对象的自有属性中不存在该属性,那么JavaScript引擎就会沿着该对象的原型链向上查找,直到找到第一个匹配的属性名为止。

原型链总有个尽头吧,就好像DOM只有唯一的根元素一样,所有原型链都汇聚到同一个源头,那就是Object.prototype,它也是一个对象,也有[[prototype]]属性,那么Object.prototype.__proto__ === ?答案是null。

有人特别擅长发挥,就根据这个大谈“JavaScript原型设计的哲学思想”,什么道生于无,一生二,二生三,三生万物……这个真的想多了,一个花两周时间搞出来的“KPI项目”,怎么也扯不到哲学上去,玄学也扯不上。现实很可能是:Brendan Eich设计的这套原型链系统走到根源处发现逻辑走不通了,每个对象都必须关联到另一个对象,并且不能循环引用,那么原型链就只能无限延伸下去,这显然是不可能的,因此只好生编硬造一个不伦不类的null作为原型链的终结点,这也就是为啥null明明不是对象,但是它的数据类型却是对象。

四、原型的关联规则

每个对象的[[prototype]]具体指向谁,或者说每个对象的原型究竟是谁,取决于对象的创建方式。

1、对象直接量的原型对象是Object.prototype;

var obj = { }; obj.__proto__ === Object.prototype // true

2、通过Object.create( )创建的对象,其原型是由第一个参数指定的对象;

  对象直接量等价于Object.create(Object.prototype)

3、通过构造函数创建的对象,其原型即构造函数的原型对象。

   内置的构造函数的prototype原型是预设好的,我们可以修改。内置构造函数的prototype原型并不都是普通的对象,例如:

typeof Function.prototype // "function"

Array.isArray( Array.prototype ) // true

但这不重要。

   自定义的构造函数,它的原型默认是一个仅含constructor属性的对象。

   function f () {}; Object.getOwnPropertyNames(f.prototype) // "constructor"

比较特殊的是函数也有继承的原型。注意,这里很容易混淆“函数的继承原型”与“函数的prototype原型”,它们是两回事,函数不会从自身的prototype指向的原型对象中继承任何属性或方法,所有的函数都共同继承一个原型对象,那就是Function.prototype。

Array.__proto__ === Function.prototype // true

Function.__proto__ === Function.prototype // true

   Object.__proto__ === Function.prototype // true

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

为什么函数也有__proto__属性?这不奇怪,因为函数也是对象(可调用的对象)。

五、从零构建JavaScript对象系统

根据以上规则,我们试着从零开始构建JavaScript的对象系统。

首先,创造一个构造函数Object,为JavaScript空空如也的对象世界播下第一颗种子。然后给它添一些属性和方法。在Chrome控制台输入Object.getOwnPropertyNames(Object),可以看到Object有二十五个属性,其中就有prototype。

prototype几乎就是JavaScript的繁育后代的生殖系统,所以它很关键,我们需要传给它一些公共属性和方法:

   Object.prototype = {/* properties */}

    然后需要给Object定义私有属性,虽然它是函数,但毕竟函数也是对象,所以能够定义属性:

Object.create = function ( ) { }; Object.keys = function ( ) { } ……

   同样的我们需要给Function设置原型,但是这个原型不是普通对象,而是函数,因此不能用对象直接量:

Function.prototype = {/* properties */} // wrong

Function.prototype = function () { } // right

至于为什么这么做,规范里只有规定,没有解释。

(参考:http://stackoverflow.com/questions/39698919/why-typeoffunction-prototype-is-function)

然后将Function.prototype.__proto__ 设置为 Object.prototype。

如法炮制,于是我们有了九大构造函数:Object、Function、Array、String、Number、Boolean、Date、Error、RegExp。

再来两个单体内置对象:Math和JSON

var Math = new Object(); // 这里采用new“实例化”了一个对象

Math.random = ……

Math.max = ……

……

现在原型对象之间的关联有了,函数是不是也效仿着弄一个继承呢,这样就免得给每个函数都定义相同的方法了,简单粗暴,直接将函数的[[prototype]]都指向Function.prototype完事儿。

       

    JavaScript内置的原型系统基本上就完成了,其它的构造函数和对象就留给程序员自定义设置吧。

参考:阮一峰《JavaScript继承机制的设计思想》

http://www.ruanyifeng.com/blog/2011/06/designing_ideas_of_inheritance_mechanism_in_javascript.html

从零构建JavaScript的对象系统的更多相关文章

  1. 基于类(Java)和基于原理(JavaScript)的对象系统的比较

    Java:面向对象编程语言,吸收了C++语言的各种优点,丢掉了C++让人头疼的多继承.指针等概念.具有功能强大和简单易用的两大特征.Java具有简单性.面向对象.分布式.健壮性.安全性.平台独立与可移 ...

  2. 面向对象的JavaScript --- 原型模式和基于原型继承的JavaScript对象系统

    面向对象的JavaScript --- 原型模式和基于原型继承的JavaScript对象系统 原型模式和基于原型继承的JavaScript对象系统 在 Brendan Eich 为 JavaScrip ...

  3. javascript中的null,对象系统还是非对象系统?

    1.一直以来的认知 在我学习js的过程中,爱民老师的绿皮书里将js的类型系统分成了两类: 其一是元类型系统:由typeof运算来检测 其二是对象类型系统:是元类型的object的一个分支 而null这 ...

  4. JavaScript的对象——灵活与危险

    转:http://www.cnblogs.com/tolg/p/4873000.html 没有哪种数据结构比JavaScript的对象更简单灵活了.作为一个弱动态类型语言,JavaScript对对象的 ...

  5. javaScript系列 [03]-javaScript原型对象

    [03]-javaScript原型对象 引用: javaScript是一门基于原型的语言,它允许对象通过原型链引用另一个对象来构建对象中的复杂性,JavaScript使用原型链这种机制来实现动态代理. ...

  6. 1、JavaScript 基础一 (从零学习JavaScript)

    1:定义:javascript是一种弱类型.动态类型.解释型的脚本语言. 弱类型:类型检查不严格,偏向于容忍隐式类型转换. 强类型:类型检查严格,偏向于不容忍隐式类型转换. 动态类型:运行的时候执行类 ...

  7. Javascript常用对象的属性和方法

    javascript为我们提供了一些非常有用的常用内部对象和方法.用户不需要用脚本来实现这些功能.这正是基于对象编程的真正目的. 在javascript提供了string(字符串).math(数值计算 ...

  8. 关于javascript自定义对象(来自网络)(最近几天不会的)

    javascript定义对象的几种简单方法 1.构造函数方式,全部属性及对象的方法都放在构造方法里面定义 优点:动态的传递参数 缺点:每创建一个对象就会创建相同的方法函数对象,占用大量内存 funct ...

  9. JavaScript常用对象的方法和属性

    ---恢复内容开始--- 本文将简单介绍JavaScript中一些常用对象的属性和方法,以及几个有用的系统函数. 一.串方法 JavaScript有强大的串处理功能,有了这些串方法,才能编写出丰富多彩 ...

随机推荐

  1. (原)android的alertdialog中加入edittext但是不弹出软键盘等问题的解决与原因

    摘要:alertdialog中加入edittext但是不弹出软键盘等问题网上有很多不管用的解决方案, 本文意在给出更有效的解决办法,并初步探究其原因 正文 在对话框中插入文本框是十分常见的需求 通常我 ...

  2. 未备案域名打开国内服务器上的网站(绑定国外空间并判断url后跳转引用)

    场景:由于域名没备案不能绑定国内服务器,通过先绑定国外空间,在空间着陆页判断当前url,打开不同的页面.页面上通过iframe引用国内服务器上的目标网站. 实现:未备案域名打开国内服务器上的网站. 国 ...

  3. 0ctf – mobile – boomshakalaka writeup

    作为一个web狗,一道web都没做出来Orz...做出来一道apk,纪念一下在ctf中做出的第一道apk... 首先在模拟器或者真机中安装一下apk看到是一个cocos2dx的打飞机游戏 根据题目提示 ...

  4. .NET LINQ Set 运算

    Set 运算      LINQ 中的 Set 操作是指根据相同或不同集合(或集)中是否存在等效元素来生成结果集的查询操作. 方法 方法名 说明 C# 查询表达式语法 Visual Basic 查询表 ...

  5. (2016弱校联盟十一专场10.3) D Parentheses

    题目链接 把左括号看成A右括号看成B,推一下就行了.好久之前写的,推到最后发现是一个有规律的序列. #include <bits/stdc++.h> using namespace std ...

  6. JS面试题-算法台阶问题

    有100格台阶,可以跨1步可以跨2步,那么一个有多少种走法: 今天电话面试.遇到一道算法问题,然后瞬间一脸懵逼: 然后机智的我,自作聪明的想到如果一个人每次都走1步,那么最多步,每次走2步最少步:然后 ...

  7. SQL入门语句之ORDER BY 和GROUP BY

    一.SQL入门语句之ORDER BY ORDER BY 是用来基于一个或多个列按升序或降序顺序排列数据 1.从数据库表获取全部数据按字段A的升序排列 select *from table_name o ...

  8. kettle系列-3.kettle读取数据库资源库很慢的优化

    环境:windows7,jvm内存设置14G,kettle5.1后来升级到5.4,oracle作为资源库. 问题背景:我们通过web页面管理kettle的job运行,这只是一个管理界面,即使web项目 ...

  9. MySQL 相关

    Innodb引擎 Innodb引擎提供了对数据库ACID事务的支持,并且实现了SQL标准的四种隔离级别.该引擎还提供了行级锁和外键约束,它的设计目标是处理大容量数据库系统. 但是该引擎不支持FULLT ...

  10. C#delegate委托

    类似函数,却没有语句体. using System; using System.Collections.Generic; using System.Linq; using System.Text; u ...