JavaScript的原型系统是怎样构建起来的

  和传统的面向对象语言通过类实现继承的方式不同,JavaScript中不存在传统意义的"类",JavaScript是通过构造函数来实现继承的。JavaScript的构造函数常常被混淆为"类",只是因为它们承担着同样的功能,然而它们实现继承的方式完全不同。类继承通过生成一个类的副本实现继承,构造函数通过原型关联实现继承。类继承的机制是"复制、拷贝",原型继承的机制是"引用、关联"。

一、原型是什么

JavaScript中所有的对象(包括函数)都有一个内部指针[[prototype]]指向对另一个对象的引用。

如果说对象A的内部特性[[prototype]]指向对象B,那么B就是A的原型。

同样的,对象B也有一个内部特性[[prototype]],指向另一个对象C,C也有一个内部特性[[prototype]]指向另一个对象D……这就是原型链。

JavaScript的继承是基于原型链实现的。

[[prototype]]特性属于内部实现,是不可访问的,但也有浏览器暴露出__proto__属性用于访问原型对象。

二、关联继承的本质

当调用对象的某个属性时,如果对象的自有属性中不存在该属性,那么JavaScript引擎就会沿着该对象的原型链向上查找,直到找到第一个匹配的属性名为止。

这就是关联继承,与作用域链的工作机制非常类似。基于原型链一脉相承的继承模式是JavaScript对象系统的根本特征。

三、函数的prototype属性

所有的函数(也只有函数)都有一个内置属性prototype,这个属性指向对另一个对象的引用,这个对象也被称为原型。

这体现了函数作为一等公民的特殊性:函数拥有一个可访问的内部属性prototype,可以显式的设置其原型对象,从而影响原型链的构建形态。对象是没有prototype属性的,但有一个constructor属性,指向其构造函数(JavaScript所有的对象都是由函数构造的)。

必须严格区分函数的内部属性prototype和对象的内部特性[[prototype]]。原型继承是基于[[prototype]]的,而不是prototype。

JavaScript整个对象系统都是建立在构造函数的基础之上的,JavaScript提供了九个内置的构造函数,同时我们还可以使用自定义函数作为构造函数。

内置的构造函数,其原型是JavaScript预设好的,例如:

Object.getOwnPropertyNames(Object.prototype)返回结果如下:

["__defineGetter__", "__defineSetter__", "hasOwnProperty", "__lookupGetter__", "__lookupSetter__", "constructor", "toString", "toLocaleString", "valueOf", "isPrototypeOf", "propertyIsEnumerable", "__proto__"]

自定义构造函数的原型对象默认是一个仅包含两个属性(constructor和__proto__)的对象,这个原型对象的[[prototype]]指向Object.prototype。

所有构造函数的原型都可以自定义设置,但一般不去修改内置构造函数。

四、关联是如何建立的

1、对象的原型链

对象的[[prototype]]具体指向谁,取决于对象的创建方式。

对象直接量的[[prototype]]指向的原型对象是Object.prototype;

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

通过Object.create( )创建的对象,其[[prototype]]特性指向由第一个参数指定的对象;

通过构造函数创建的对象,其 [[prototype]] 特性指向构造函数的原型对象。

2、函数的原型链

函数的原型链非常简单,所有函数的[[prototype]]都指向Function.prototype

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

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

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

5、对象系统的构建

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

JavaScript的初始环境提供了三个纯粹的独立的对象(Global&Math&JSON)和九个构造函数。

Global是全局环境,暂且不谈。

九个构造函数中,Object是所有对象的始祖,其余八个构造函数都是Object的实例。

Date instanceof Object // true

……

Math和JSON都是对象,也都是Object的实例。

Math.constructor === Object // true

Math instanceof Object // true

因此构建JavaScript对象系统的第一步就是创造一个构造函数Object,以及Object的原型对象Object.prototype。

然后再创建其余八个构造函数,将它们的原型对象的[[prototype]]指向Object.prototype。为什么不直接指向Object?因为[[prototype]]只能指向对象,而不是构造函数。

检验一下:

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

现在原型对象之间的关联有了,函数之间还没有关联,将函数的[[prototype]]都指向Function.prototype后就得到了一个初始的对象系统。

   自定义的构造函数在创建之初和内置构造函数是同级的,因为自定义构造函数的原型对象的[[prototype]]同样指向Object.prototype,但是我们可以修改构造函数的原型,来改变一下原型链的纵深。

var obj = new Array; // obj的[[prototype]]指向Array.prototype

var f = function() {}; // 创建一个函数作为构造函数

f.prototype = obj; // 修改构造函数的原型

var o = new f(); // 创建一个f的实例对象

那么o的原型链是怎么样的呢?

对象o的原型的[[prototype]]指向构造函数f的prototype对象;

构造函数f的prototype对象(即对象obj)的[[prototype]]指向Array.prototype;

Array.prototype的[[prototype]]指向Object.prototype;

由此可得:

可以检测一下:

console.log(o instanceof f); // true

console.log(o instanceof Array); // true

console.log(o instanceof Object); // true

注: object instanceof constructor

instanceof 运算符用来检测 constructor.prototype 是否存在于object 的原型链上。

6、道生于无

所有原型链都汇聚到同一个源头,就是Object.prototype,它也是一个对象,也有[[prototype]]属性,那么Object.prototype.__proto__ === ?答案是null。

   看上去有些玄妙的样子,正所谓天地肇始,化分阴阳:

   

  九个构造函数其实就是九尾:

   由此看来,火影的创作者也学过JavaScript,而JavaScript的创作者学过《易经》。

时间: 2024-08-24 22:12:38

JavaScript的原型系统是怎样构建起来的的相关文章

深刻理解JavaScript基于原型的面向对象

主题一.原型 一.基于原型的语言的特点 1 只有对象,没有类;对象继承对象,而不是类继承类. 2  "原型对象"是基于原型语言的核心概念.原型对象是新对象的模板,它将自身的属性共享给新对象.一个对象不但可以享有自己创建时和运行时定义的属性,而且可以享有原型对象的属性. 3 除了语言原生的顶级对象,每一个对象都有自己的原型对象,所有对象构成一个树状的层级系统.root节点的顶层对象是一个语言原生的对象,其他所有对象都直接或间接继承它的属性. 显然,基于原型的语言比基于类的语言简单得多,我

JavaScript的原型继承

JavaScript是一门面向对象的语言.在JavaScript中有一句很经典的话,万物皆对象.既然是面向对象的,那就有面向对象的三大特征:封装.继承.多态.这里讲的是JavaScript的继承,其他两个容后再讲. JavaScript的继承和C++的继承不大一样,C++的继承是基于类的,而JavaScript的继承是基于原型的. 现在问题来了. 原型是什么? 原型我们可以参照C++里的类,同样的保存了对象的属性和方法.例如我们写一个简单的对象 function Animal(name) { t

[C++]现行的试卷封面并获取学生题目得分信息以及学号信息的原型系统

大二的时候写的一个CV小玩意,最终决定还是把它放出来,也许会帮助到很多人,代码写的很丑,大家多多包涵.附加实验报告主要部分.大家会给这个课设打多少分呢? 课题背景及意义: 本项目主要目标是设计一套能自动分析我校现行的试卷封面并获取学生题目得分信息以及学号信息的原型系统. 本项目的实现有助于提升我校成绩管理的自动化程度以及试卷分析的量化程度,分担一部分期末教师阅卷的工作. 课题相关研究情况概述: 本项目进行至今已经完成了单个数字的识别,并且准确率高达98.74%.完成了试卷卷面的基本分析工作,可以

浅谈javascript的原型及原型链

浅谈javascript的原型及原型链 这里,我们列出原型的几个概念,如下: prototype属性 [[prototype]] __proto__ prototype属性 只要创建了一个函数,就会为该函数创建一个prototype属性,指向该函数的原型对象.实例对象是不会拥有该属性的.默认情况下,该原型对象也会获得一个constructor属性,该属性包含一个指针,指向prototype属性所在的函数. Person.prototype.constructor===Person [[proto

javascript简单原型

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getS

史上最清晰的JavaScript的原型讲解

一说起JavaScript就要谈的几个问题,原型就是其中的一个.说了句大话,史上最清晰.本来是想按照大纲式的行文写一下,但写到后边感觉其实就一个概念,没有什么条理性,所以下面就简单按照概念解释的模式谈下这个问题. 1.JavaScript的原型是什么? 原型,首先他是个对象.和在以对象为核心的JavaScript这门语言中的其他普通对象来说一样,只不过他的角色有点特殊.但首先要明白他就是一个对象,是一个无序的属性和值的序列对. 2.谁会具有原型这个对象? 所有的对象(包括函数这个对象)在默认的情

理解JavaScript 的原型属性

1.原型继承 面向对象编程可以通过很多途径实现.其他的语言,比如 Java,使用基于类的模型实现: 类及对象实例区别对待.但在 JavaScript 中没有类的概念,取而代之的是一切皆对象.JavaScript 中的继承通过原型继承实现:一个对象直接从另一对象继承.对象中包含其继承体系中祖先的引用——对象的 prototype 属性. 2. JavaScript 实现继承的语言特性 当尝试访问 JavaScript 对象中不存在的属性时,解析器会查找匹配的对象原型.例如调用 car.toStri

javascript数组原型方法

1.javascript数组原型方法. 1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>jstest</title> 6 </head> 7 <body> 8 <script> 9 var arr = ["1","2&q

javascript动手写日历组件(1)——构建日历逻辑 (by vczero)

一.分析日历的组成部分和交互要素 (1)组成部分:选择年月部分.星期显示.包含本月(或者有前月和下一个月部分日子) (2)根据选择的年和月份,动态绘制日历面板. (3)一个日历 7(天) * 5(周) = 35格表格. (4)一个月份是统一的一个面板:一个月的头一天一定在日历面板的第一行,根据该天的“星期几”确定位置. (5)第一格子是星期一,最后一个格子是星期日,为5周的日历面板. 二.确定逻辑设计 日历上面的日历,8月1号建军节为什么会出现在这一格?因为一个月的天数是小于5周(35天)的,因