热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

从零构建JavaScript的对象系统

一、正统的类与继承类是对象的定义,而对象是类的实例(Instance)。类不可直接使用,要想使用就必须在内存上生成该类的副本

 一、正统的类与继承

       类是对象的定义,而对象是类的实例(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项目”,怎么也扯不到哲学上去,这就是外行学前端的一个写照。原型链系统中每个对象都必须关联到另一个对象,并且不能循环引用,那么原型链就只能无限延伸下去,这显然是不可能的,因此只好把个不伦不类的 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

转:https://www.cnblogs.com/kidney/p/5966988.html



推荐阅读
  • 本文介绍了绕过WAF的XSS检测机制的方法,包括确定payload结构、测试和混淆。同时提出了一种构建XSS payload的方法,该payload与安全机制使用的正则表达式不匹配。通过清理用户输入、转义输出、使用文档对象模型(DOM)接收器和源、实施适当的跨域资源共享(CORS)策略和其他安全策略,可以有效阻止XSS漏洞。但是,WAF或自定义过滤器仍然被广泛使用来增加安全性。本文的方法可以绕过这种安全机制,构建与正则表达式不匹配的XSS payload。 ... [详细]
  • Html5-Canvas实现简易的抽奖转盘效果
    本文介绍了如何使用Html5和Canvas标签来实现简易的抽奖转盘效果,同时使用了jQueryRotate.js旋转插件。文章中给出了主要的html和css代码,并展示了实现的基本效果。 ... [详细]
  • 如何在HTML中获取鼠标的当前位置
    本文介绍了在HTML中获取鼠标当前位置的三种方法,分别是相对于屏幕的位置、相对于窗口的位置以及考虑了页面滚动因素的位置。通过这些方法可以准确获取鼠标的坐标信息。 ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
  • position属性absolute与relative的区别和用法详解
    本文详细解读了CSS中的position属性absolute和relative的区别和用法。通过解释绝对定位和相对定位的含义,以及配合TOP、RIGHT、BOTTOM、LEFT进行定位的方式,说明了它们的特性和能够实现的效果。同时指出了在网页居中时使用Absolute可能会出错的原因,即以浏览器左上角为原始点进行定位,不会随着分辨率的变化而变化位置。最后总结了一些使用这两个属性的技巧。 ... [详细]
  • 【shell】网络处理:判断IP是否在网段、两个ip是否同网段、IP地址范围、网段包含关系
    本文介绍了使用shell脚本判断IP是否在同一网段、判断IP地址是否在某个范围内、计算IP地址范围、判断网段之间的包含关系的方法和原理。通过对IP和掩码进行与计算,可以判断两个IP是否在同一网段。同时,还提供了一段用于验证IP地址的正则表达式和判断特殊IP地址的方法。 ... [详细]
  • JavaScript和HTML之间的交互是经由过程事宜完成的。事宜:文档或浏览器窗口中发作的一些特定的交互霎时。能够运用侦听器(或处置惩罚递次来预订事宜),以便事宜发作时实行相应的 ... [详细]
author-avatar
莪鈈稀罕rn
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有