在深入细节之前,有几点需要明确一下
1、JavaScript没有类的概念。所有自定义的类都是通过function模拟的,包括其原生的对象,Number、String、Array等。因此创建自定义的类的实例时,也是在执行一个函数,只是执行的方式不同。转载文章里有详细说明。
2、对象内部私有的prototype属性和Function公开的prototype属性。在使用new的方式调用一个function时,两者是通过怎样一个过程将相应的属性和方法附加到新创建的对象上的。
3、每个对象都有construct属性,该属性保存着用来创建对象的函数的引用。比如说 var objA = new FnA();那么objA.construct就等于FnA。
Build-in *** data structure: 指JS内部用于实现***类型的数据结构,这些结构我们基本上无法直接操作。
Build-in *** object: 指JS内置的Number, String, Boolean等这些对象,这是JS将内部实现的数据类型暴露给开发者使用的接口。
Build-in *** constructor: 指JS内置的一些构造器,用来构造相应类型的对象实例。它们被包装成函数对象暴露出来,例如我们可以使用下面的方法访问到这些函数对象:
//Passed in FF2.0, IE7, Opera9.25, Safari3.0.4 //access the build-in number constructor var number = new Number(123); var numConstructor1 = number.constructor; //or var numConstructor2 = new Object(123).constructor; //both numConstructor1 and numConstructor2 are the build-in Number constructor numConstructor1 == numConstructor2 //result: true //access the build-in object constructor var objConstructor1 = {}.constructor; //or var objConstructor2 = new Object().constructor; //both objConstructor1 and objConstructor2 are the build-in Object constructor objConstructor1==objConstructor2 //result: true
每个对象都有一个[[Prototype]]的内部属性,它的值为null或者另外一个对象。函数对象都有一个显示的prototype属性,它并不是内部[[Prototype]]属性。不同的JS引擎实现者可以将内部[[Prototype]]属性命名为任何名字,并且设置它的可见性,只在JS引擎内部使用。虽然无法在JS代码中访问到内部[[Prototype]](FireFox中可以,名字为__proto__,因为Mozilla将它公开了),但可以使用对象的isPrototypeOf()方法进行测试,注意这个方法会在整个Prototype链上进行判断。
使用obj.propName访问一个对象的属性时,按照下面的步骤进行处理(假设obj的内部[[Prototype]]属性名为__proto__):
1. 如果obj存在propName属性,返回属性的值,否则
2. 如果obj.__proto__为null,返回undefined,否则
3. 返回obj.__proto__.propName
调用对象的方法跟访问属性搜索过程一样,因为方法的函数对象就是对象的一个属性值。
提示: 上面步骤中隐含了一个递归过程,步骤3中obj.__proto__是另外一个对象,同样将采用1, 2, 3这样的步骤来搜索propName属性。
object1将具备属性prop1, prop2, prop3以及方法fn1, fn2, fn3。图中虚线箭头表示prototype链
这就是基于Prototype的继承和共享。其中object1的方法fn2来自object2,概念上即object2重写了object3的方法fn2。
JavaScript对象应当都通过prototype链关联起来,最顶层是Object,即对象都派生自Object类型。
对象创建过程
JS中只有函数对象具备类的概念,因此要创建一个对象,必须使用函数对象。函数对象内部有[[Construct]]方法和[[Call]]方法,[[Construct]]用于构造对象,[[Call]]用于函数调用,只有使用new操作符时才触发[[Construct]]逻辑。
var obj=new Object(); 是使用内置的Object这个函数对象创建实例化对象obj。var obj={};和var obj=[];这种代码将由JS引擎触发Object和Array的构造过程。function fn(){}; var myObj=new fn();是使用用户定义的类型创建实例化对象。
new Fn(args)的创建过程如下(即函数对象的[[Construct]]方法处理逻辑,对象的创建过程)。另外函数对象本身的创建过程(指定义函数或者用Function创建一个函数对象等方式)虽然也使用了下面的处理逻辑,但有特殊的地方,后面再描述。
1. 创建一个build-in object对象obj并初始化
2. 如果Fn.prototype是Object类型,则将obj的内部[[Prototype]]设置为Fn.prototype,否则obj的[[Prototype]]将为其初始化值(即Object.prototype)
3. 将obj作为this,使用args参数调用Fn的内部[[Call]]方法
3.1 内部[[Call]]方法创建当前执行上下文
3.2 调用F的函数体
3.3 销毁当前的执行上下文
3.4 返回F函数体的返回值,如果F的函数体没有返回值则返回undefined
4. 如果[[Call]]的返回值是Object类型,则返回这个值,否则返回obj(这就是为什么当我们是用这个function来模拟类时,通常都不给它任何返回值,我们需要的就是这个新创建的对象作为返回值)
注意步骤2中, prototype指对象显示的prototype属性,而[[Prototype]]则代表对象内部Prototype属性(隐式的)。
构成对象Prototype链的是内部隐式的[[Prototype]],而并非对象显示的prototype属性。显示的prototype只有在函数对象上才有意义,从上面的创建过程可以看到,函数的prototype被赋给派生对象隐式[[Prototype]]属性,这样根据Prototype规则,派生对象和函数的prototype对象之间才存在属性、方法的继承/共享关系。
//Passed in FF2.0, IE7, Opera9.25, Safari3.0.4 function fn(){} //the value of implicit [[Prototype]] property of those objects derived from fn will be assigned to fn.prototype fn.prototype={ attr1:"aaa", attr2:"bbb"}; var obj=new fn(); document.write(obj.attr1 + "<br />"); //result: aaa document.write(obj.attr2 + "<br />"); //result: bbb document.write(obj instanceof fn); //result: true document.write("<br />"); //I change the prototype of fn here, so by the algorithm of Prototype the obj is no longer the instance of fn, //but this won't affect the obj and its [[Prototype]] property, and the obj still has attr1 and attr2 properties fn.prototype={}; document.write(obj.attr1 + "<br />"); //result: aaa document.write(obj.attr2 + "<br />"); //result: bbb document.write(obj instanceof fn); //result: false
//prototye is shared by all instances fn.prototype = {attr1: "aaa"}; var obj1 = new fn(); var obj2 = new fn(); document.write(obj1.attr1); //result aaa document.write(obj2.attr1); //result aaa fn.prototype.attr1 ="bbb"; document.write(obj1.attr1); //result bbb document.write(obj2.attr1); //result bbb
关于创建过程返回值的验证
//Passed in FF2.0, IE7, Opera9.25, Safari3.0.4 function fn(){ //according to step 4 described above, //the new fn() operation will return the object { attr1: 111, attr2: 222 }, it's not an instance of fn! return { attr1: 111, attr2: 222 }; } fn.prototype={ attr1:"aaa", attr2:"bbb"}; var obj=new fn(); document.write(obj.attr1 + "<br />"); //result: 111 document.write(obj.attr2 + "<br />"); //result: 222 document.write(obj instanceof fn); //result: false
本地属性与继承属性
对象通过隐式Prototype链能够实现属性和方法的继承,但prototype也是一个普通对象,就是说它是一个普通的实例化的对象,而不是纯粹抽象的数据结构描述。所以就有了这个本地属性与继承属性的问题。
首先看一下设置对象属性时的处理过程。JS定义了一组attribute,用来描述对象的属性property,以表明属性property是否可以在JavaScript代码中设值、被for in枚举等。
obj.propName=value的赋值语句处理步骤如下:
1. 如果propName的attribute设置为不能设值,则返回
2. 如果obj.propName不存在,则为obj创建一个属性,名称为propName
3. 将obj.propName的值设为value
可以看到,设值过程并不会考虑Prototype链,道理很明显,obj的内部[[Prototype]]是一个实例化的对象,它不仅仅向obj共享属性,还可能向其它对象共享属性,修改它可能影响其它对象。
用上面CF, Cfp的示例来说明,实例对象cf1具有本地属性q1, q2以及继承属性CFP1,如果执行cf1.CFP1="",那么cf1就具有本地属性CFP1了,测试结果如下:
//Passed in FF2.0, IE7, Opera9.25, Safari3.0.4 var cf1=new CF("aaa", "bbb"); var cf2=new CF(111, 222); document.write(cf1.CFP1 + "<br />"); //result: CFP1 in Cfp document.write(cf2.CFP1 + "<br />"); //result: CFP1 in Cfp //it will result in a local property in cf1 cf1.CFP1="new value for cf1"; //changes on CF.prototype.CFP1 will affect cf2 but not cf1, because there's already a local property with //the name CFP1 in cf1, but no such one in cf2 CF.prototype.CFP1="new value for Cfp"; document.write(cf1.CFP1 + "<br />"); //result: new value for cf1 document.write(cf2.CFP1 + "<br />"); //result: new value for Cfp
对象模型
1. 了解了JavaScript的数据类型,清楚了象Number这样的系统内置对象具有多重身份: a)它们本身是一个函数对象,只是由引擎内部实现而已,b)它们代表一种数据类型,我们可以用它们定义、操作相应类型的数据,c)在它们背后隐藏了引擎的内部实现机制,例如内部的数据结构、各种被包装成了JavaScript对象的构造器等。
2. 了解了Prototype机制,知道对象是如何通过它们继承属性和方法,知道了在创建对象过程中JS引擎内部是如何设置Prototype关系的。
JavaScript代码中定义函数,或者调用Function创建函数时,最终都会以类似这样的形式调用Function函数:var newFun=Function(funArgs, funBody); 。创建函数对象的主要步骤如下:
1. 创建一个build-in object对象fn
2. 将fn的内部[[Prototype]]设为Function.prototype
3. 设置内部的[[Call]]属性,它是内部实现的一个方法,处理逻辑参考对象创建过程的步骤3
4. 设置内部的[[Construct]]属性,它是内部实现的一个方法,处理逻辑参考对象创建过程的步骤1,2,3,4
5. 设置fn.length为funArgs.length,如果函数没有参数,则将fn.length设置为0
6. 使用new Object()同样的逻辑创建一个Object对象fnProto
7. 将fnProto.constructor设为fn
8. 将fn.prototype设为fnProto
9. 返回fn
步骤1跟步骤6的区别为,步骤1只是创建内部用来实现Object对象的数据结构(build-in object structure),并完成内部必要的初始化工作,但它的[[Prototype]]、[[Call]]、[[Construct]]等属性应当为null或者内部初始化值,即我们可以理解为不指向任何对象(对[[Prototype]]这样的属性而言),或者不包含任何处理(对[[Call]]、[[Construct]]这样的方法而言)。步骤6则将按照前面描述的对象创建过程创建一个新的对象,它的[[Prototype]]等被设置了。
从上面的处理步骤可以了解,任何时候我们定义一个函数,它的prototype是一个Object实例,这样默认情况下我们创建自定义函数的实例对象时,它们的Prototype链将指向Object.prototype。
另外,Function一个特殊的地方,是它的[[Call]]和[[Construct]]处理逻辑一样。
红色虚线表示隐式Prototype链。
这张对象模型图中包含了太多东西,不少地方需要仔细体会,可以写些测试代码进行验证。彻底理解了这张图,对JavaScript语言的了解也就差不多了。下面是一些补充说明:
1. 图中有好几个地方提到build-in Function constructor,这是同一个对象,可以测试验证:
//Passed in FF2.0, IE7, Opera9.25, Safari3.0.4 Function==Function.constructor //result: true Function==Function.prototype.constructor //result: true Function==Object.constructor //result: true //Function also equals to Number.constructor, String.constructor, Array.constructor, RegExp.constructor, etc. function fn(){} Function==fn.constructor //result: true
这说明了几个问题: Function指向系统内置的函数构造器(build-in Function constructor);Function具有自举性;系统中所有函数都是由Function构造。
2. 左下角的obj1, obj2...objn范指用类似这样的代码创建的对象: function fn1(){}; var obj1=new fn1();
这些对象没有本地constructor方法,但它们将从Prototype链上得到一个继承的constructor方法,即fn.prototype.constructor,从函数对象的构造过程可以知道,它就是fn本身了。
右下角的obj1, obj2...objn范指用类似这样的代码创建的对象: var obj1=new Object();或var obj1={};或var obj1=new Number(123);或obj1=/\w+/;等等。所以这些对象Prototype链的指向、从Prototype链继承而来的constructor的值(指它们的constructor是build-in Number constructor还是build-in Object constructor等)等依赖于具体的对象类型。另外注意的是,var obj=new Object(123);这样创建的对象,它的类型仍然是Number,即同样需要根据参数值的类型来确定。
同样它们也没有本地constructor,而是从Prototype链上获得继承的constructor方法,即build-in *** constructor,具体是哪一个由数据类型确定。
3. 关于图中Prototype链的补充说明:
Object.prototype是整个链的终结点,它的内部[[Prototype]]为null。
所有函数的Prototype链都指向Function.prototype。
Function的Prototype链指向Function.prototype,这是规范要求的,因为设计者将Function设计为具有自举性。Function的Prototype链这样设计之后,Function.constructor==Function, Function instanceOf Function都为true。另外Function已经是最顶层的构造器,但Function本身也是一个函数对象,它必然是由某个东西创建出来的,这样自举在语义上合情合理。
Function.prototype的Prototype链指向Object.prototype,这也是规范强制要求的。首先Function.prototype是Function的一个实例对象(typeof Function.prototype可以知道它是一个Function,instanceOf无法通过测试,因为Prototype链在内部被额外设置了),所以按照Prototype的规则,Function.prototype的内部[[Prototype]]值应当为Function.prototype这个对象,即它的Prototype链指向自己本身。这样一方面在Prototype链上造成一个死循环,另一方面它本身成为了一个终结点,结果就是所有函数对象将不是派生自Object了。加上这个强制要求之后,Prototype链只有唯一的一个终结点。
4. 因为Function.prototype是一个函数对象,所以它应当具有显示的prototype属性,即Function.prototype.prototype,但只有FireFox中可以访问到,IE、Opera、Safari都无法访问。所以图中用了个表示不存在的符号。
5. 用户自定义函数(user defined functions)默认情况下[[Prototype]]值是Object.prototype,即它的隐式Prototype链指向Object.prototype,所以图中就这样表示了,但并不代表总是这样,当用户设置了自定义函数的prototype属性之后,情况就不同了。
发表评论
-
Apache中设置自动清除浏览器缓存
2009-10-26 10:26 7966打开Apache的httpd.conf文件 1 将 Load ... -
JavaScript执行环境
2009-09-08 11:25 0http://www.jibbering.com/faq/fa ... -
Eclipse + JSEclipse + PDT
2009-07-13 00:43 1627需要用到的几个插件:JSEclipse PDT SVN。可直 ... -
JavaScript Inheritance
2009-03-23 23:52 825先上代码说明 KevLinDev.extend = func ... -
Douglas Crockford - JavaScript function 3 of 4
2008-11-29 21:36 724function能像其他类型的 ...
相关推荐
### 深入浅出JavaScript对象模型 #### JavaScript对象的本质 根据ECMA262规范,ECMAScript被定义为一种基于对象的语言而非传统的面向对象语言。这意味着在JavaScript中,对象被视为存储数据的一种大型数组形式,...
### JavaScript对象模型与执行模型详解 #### 一、引言 JavaScript作为一种强大的脚本语言,在Web开发领域占据了举足轻重的地位。其独特的对象模型和执行模型为开发者提供了灵活多变的功能,使得JavaScript能够轻松...
JavaScript对象模型(Object Model)和执行模型是理解JavaScript工作原理的关键概念。JavaScript是一种基于原型的动态类型语言,其对象模型是其核心特性之一。本文将深入探讨JavaScript的对象模型和执行模型,以及...
JavaScript 对象模型与事件处理 JavaScript 对象模型是指在 JavaScript 中描述对象之间的层次关系的模型。该模型可以将对象分为核心部分、浏览器对象模型和文档对象模型三个组成部分。核心部分主要包括 JavaScript ...
- 对象模型描述了JavaScript对象间的层次结构,提供了一种接口来处理这些对象及其行为。在JavaScript中,对象模型分为核心部分和特定于环境的部分。 - **核心部分** 包括基本数据类型、运算符、表达式,以及全局...
本文深入浅出地探讨了JavaScript对象模型与function对象的概念与用法,并通过示例代码展示了JavaScript函数的特性。接下来将详细阐述本文提到的关键知识点。 首先,JavaScript中的函数也是对象,这意味着函数可以像...
Javascript对象模型。 1Kb压缩 价值转换 值格式 值类型验证 价值内容验证(必填,自定义规则) 默认值 虚拟财产 现场直播活动 正确的文档^^ 体面的测试^^ 安装 安装npm软件包 npm install @jaysalvat/...
8.3.1 JavaScript对象模型 JavaScript有全局对象、内置对象、宿主对象等层次结构,如DOM(文档对象模型)和BOM(浏览器对象模型)。 8.3.2 客户端对象层次介绍 客户端对象层次主要涉及浏览器提供的对象,如window、...
原型链、构造函数、实例化和原型对象的概念是JavaScript对象模型的重要组成部分,手册会详细阐述这些内容。 在函数部分,手册可能涵盖函数的定义、匿名函数、箭头函数、函数参数(默认值、剩余参数和解构赋值)、...
僵化 一个简单JavaScript对象模型。 Klassified提供了一个基类object ,可以使用其subclass类类方法对其进行subclass类化,如以下示例所示: var animal = object . subclass ( function ( that , my ) { my . ...
使用SharePoint JavaScript对象模型(JSOM)对SharePoint加载项中的网站属性,列表和列表项执行创建,读取,更新和删除操作。 适用于 SharePoint Online和本地SharePoint 2013及更高版本 先决条件 此示例需要满
理解JavaScript对象模型对于深入学习和使用JavaScript至关重要。 JavaScript对象基于原型(Prototype)体系,这意味着每个对象都有一个原型对象,可以通过`__proto__`属性或`Object.getPrototypeOf`方法访问。通过...
本压缩包文件“Javascript API for ArcGIS Server对象模型图.rar”包含了关于这个API的详细对象模型图,对于理解和学习JavaScript API的使用非常有帮助。 1. **对象模型图**:对象模型图是API中各种对象、类和方法...
用于FHIR标准的Typescript / Javascript对象模型Model遵循FHIR R4规范。 定义是基于io-ts的接口。 有关更多详细信息,请参见。 这意味着您可以在运行时检查类型。 安装 npm i -S @ahryman40k/ts-fhir-types 或者 ...
本文将深入探讨JavaScript的基础语法以及DOM对象模型。 一、JavaScript基础语法 1. 变量:在JavaScript中,我们使用`var`、`let`或`const`来声明变量。`var`在全局或函数作用域内有效,而`let`和`const`则在块级...