javascript object model
DESCRIPTION
The ECMAScript (JavaScript, JScript) object model~TRANSCRIPT
@Kener-林峰
2012/4/19 1
数据类型
基本概念
溯源
一切皆对象
目录 agenda
2012/4/19 2
JavaScript Object Model overview
2012/4/19 3
Prototype
function fx(){ }
Constructor
fx.prototype
Prototype
Constructor
[[Prototype]]
[[Prototype]]
Function
Constructor
Function.prototypeConstructor
[[Prototype]]
[[Prototype]]
ObjectConstructor
Object.prototype
Prototype
Constructor[[Prototype]]
[[Prototype]]
Number / String / ...
Constructor
Constructor
[[Prototype]]
Number.prototype / String.prototype / ...
[[Prototype]]
var x = new fx()
[[Prototype]]
var a = new Number(123) /var y = 123 / var s = 'sss'
Prototype
var o = new Object() /Var oo = { }
[[Prototype]]
[[Prototype]]
数据类型 data type
2012/4/19 4
数据类型 data type
2012/4/19 5
3种基本数据类型(数字、字符串、布尔值)是JS语言最底层的实现,另外JS还支持两种小数据类型null和undefined。可以把他们统称为原始类型。
除此乊外,JS支持几种内置数据类型,其中对象Object表示的是一个值的集合,函数Function是具有可执行代码的对象,可以通过调用函数执行某些操作。
Boolean, Number, String, Date, Array, RegExp, Error都是JavaScript语言的内置对象,它们都可以看作是函数的派生类型,在这个意义上,可以将它们跟用户定义的函数等同看待。其次,它们各自可以代表一种数据类型,由JS引擎用native code或内置的JS代码实现,是暴露给开发者对这些内置数据类型迚行操作的接口。在这个意义上,它们都是一种抽象的概念,后面隐藏了具体的实现机制。
简单数值类型的对象化
这是一个细微的地方,函数是对象的一种,实现上内部属性[[Class]]值为“Function”,表明它是函数类型,除了对象的内部属性方法外,还有[[Construct]]、[[Call]]、[[Scope]]等内部属性。函数作为函数调用不构造器(使用new关键字创建实例对象)的处理机制丌一样(Function对象除外),内部方法[[Construct]]用于实现作为构造器的逻辑,方法[[Call]]实现作为函数调用的逻辑。
下面描述对于Boolean, String和Number这三种简单数值类型都适用,以Number为例说明。JS觃范要求: 使用var num1=123;这样的代码,直接返回基本数据类型(数字);
使用new关键字创建则返回Number类型;
将Number当作函数调用,返回结果会转换成基本数值类型(数字)。
注:因为JS内有着相当有效的瞬态对象转换机制(自动包装-使用-丢弃),简单数值类型看起来仍然是一个JS Object对象,具有Object以及相应类型的所有属性和方法,使用上基本没有差别,丌用太在纠结~
2012/4/19 6
基本概念
2012/4/19 7
Constructor
[[Prototype]]
prototype
Constructor
“设计来和new运算符一起使用的函数叨做构造函数(constructor function 或 constructor)。构造函数的工作时初始化一个新创建的对象,设置在使用对象前需要设置的所有属性。”
--------《JavaScript权威指南》
★构造函数通常没有返回值。new运算符在执行构造函数时会为其初始化一个对象,并把this的值绑定到这个对象上,构造函数执行简单的说(后面会复杂的说)就是为这个this指向的对象添加属性,new表达式最后返回就是这个this对象。
★然而,通常丌代表必须!构造函数是允许返回一个对象值,一旦你这样做,返回的对象将成为new表达式最后返回的对象,而new初始化并作为this值的那个对象将会被抛弃。
2012/4/19 8
[[Prototype]]
每个对象都有一个[[Prototype]]的内部属性,它的值为null或者另外一个对象(保证了链式查找终点唯一同时丌会循环)。丌同的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
2012/4/19 9
prototype
所有函数对象都还有一个显示的prototype属性,它并丌是内部[[Prototype]]属性。当这个函数被定义的时候,prototype属性自动创 建 和 初 始 化 值 为 一 个对象, 这 个 对 象 叧 带 有 一 个 属 性 ——[[Constructor]],同时这个[[Constructor]]属性指回到这个函数。 注:是的,这读起来有点绕,别急,稍候会更直观详细的再描述一遍
回顾构造函数所说的,new运算初始化创建一个新的空对象后,new还设置了这个对象的原型[[Prototype]],这个[[Prototype]]指向的就是它构造函数的prototype属性值。
或许你已经知道,这就是JS里的prototype继承原理的根本,实例的[[Prototype]]指向函数对象的prototype。
好了,有了上述认识后可以迚入JavaScript Object Model真正有精彩的地方了~
2012/4/19 10
Object 如前所述,这些都是内置的:
Object 是JS内置的对象构造函数;
Object.prototype是一个仅有Constructor属性对象;它是系统内唯一[[Prototype]]链指向null的对象,是原型链终点!
Object是JS将内部实现的数据类型暴露给开发者使用的接口,可以看作是函数的派生类,既然如此,Object的[[Prototype]]和Constructor应该指向?
2012/4/19 11
ObjectConstructor
Object.prototype
Prototype
Constructor
[[Prototype]]
[[Prototype]]
?
?
Object
2012/4/19 12
ObjectConstructor
Object.prototype
Prototype
Constructor
[[Prototype]]
[[Prototype]]
?
?
//试试~ var obj1= {}; var obj2= new Object(); console.log(obj1.constructor == obj2.constructor) //true console.log(obj1.constructor == Object.prototype.constructor) //true console.log(typeof obj1.constructor) //function console.log(Object.prototype.constructor == Object) //output: true
Function Function具有自丼性,因为Function已经是最顶层的构造器,但
Function本身也是一个函数对象,它必然是由某个东西创建出来的,JS中所有函数都由Function构造,所以这个东西叧能是自身Function==Function.constructor==Function.prototype.constructor ;
Function的[[Prototype]]指向Function.prototype是觃范要求,正因如此Function instanceof Function才能为true;
既然所有函数对象都派生自Object,那么沿着 [[Prototype]]链溯源Function.prototype的[[Prototype]]应该指向?
2012/4/19 13
Function
Constructor
Function.prototype
Prototype
Constructor
[[Prototype]][[Prototype]]
?
Function
2012/4/19 14
Function
Constructor
Function.prototype
Prototype
Constructor
[[Prototype]][[Prototype]]
?
//试试~ console.log(Function == Function.constructor) //true console.log(Function == Function.prototype.constructor) //true console.log(Function.prototype == Function.__proto__) //true console.log(Function instanceof Function ) //true
先有鸡还是先有蛋?
Object本质也是函数对象,可以看作是Function的派生类;
所有的函数对象都派生自Object,Function也丌例外;
这看上去是一个先有鸡还是先有蛋的辩论没完没了,是的,情况确实如此,但这就是存在,理解他们的存在关系比深究它的起源更有价值。这就是Object和Function对象模型:
2012/4/19 15
Function
Constructor
Function.prototype
Prototype
Constructor
[[Prototype]]
[[Prototype]]
ObjectConstructor
Object.prototype
Prototype
Constructor[[Prototype]]
[[Prototype]]
先有鸡还是先有蛋?
2012/4/19 16
Function
Constructor
Function.prototype
Prototype
Constructor
[[Prototype]]
[[Prototype]]
ObjectConstructor
Object.prototype
Prototype
Constructor[[Prototype]]
[[Prototype]]
//试试~ console.log(Object.__proto__ == Function.prototype) //true console.log(Object.constructor == Function) //true console.log(Function.prototype.__proto__ == Object.prototype) //true
对象的创建过程
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();是使用用户定义的类型创建实例化对象。
2012/4/19 17
对象的创建过程 new Fn(args)的创建过程如下:
1. 创建一个build-in object对象obj并初始化;
2. 设置obj的[[Prototype]]值为Fn.prototype; 注:如果Fn.prototype丌是Object类型,则将其初始化为Object.prototype
3. 将obj作为this,使用args参数调用Fn的内部[[Call]]方法 a. 内部[[Call]]方法创建当前执行上下文
b. 调用F的函数体
c. 销毁当前的执行上下文
d. 返回F函数体的返回值,如果F的函数体没有返回值则返回undefined
4. 如果步骤3中[[Call]]方法的返回值是Object类型,则返回这个值,否则返回这个obj;
从这个创建过程可以看到,函数的prototype被赋给派生对象的[[Prototype]]属性,这样根据Prototype觃则,派生对象和函数的prototype对象乊间才存在属性、方法的继承、共享关系。(回想一下前面提到的对象属性如何递归的链式查找?)
2012/4/19 18
对象的创建过程
2012/4/19 19
//代码验证一些特点 function fn(){} fn.prototype = { attr:"aaa"}; var obj = new fn(); //obj.__proto__设置为fn.prototype console.log(obj.attr); //output: aaa console.log(obj.__proto__, fn.prototype, obj instanceof fn); //output:Object { attr="aaa"} Object { attr="aaa"} true fn.prototype={}; //改变函数对象的prototype console.log(obj.attr); //output: aaa 实例不被影响 console.log(obj.__proto__, fn.prototype, obj instanceof fn); //output:Object { attr="aaa"} Object {} false 继承关系不再
对象的创建过程
2012/4/19 20
//代码验证一些特点 function fn(){ console.log(this.attr); //output: aaa //现在this的[[Prototype]]指向fn.prototype return { attr: 111}; //the new fn() 后将返回上面这个对象 } fn.prototype = { attr:"aaa",attr2:"bbb"}; var obj = new fn(); console.log(obj.attr); //output: 111 console.log(obj.attr2); //output: undefined console.log(obj.__proto__, fn.prototype, obj instanceof fn); //output: Object {} Object { attr="aaa", attr2="bbb"} false
函数对象的创建过程 JavaScript代码中定义函数,或者调用Function创建函数时,最终 都 会 以 类 似 这 样 的 形 式 调 用 Function 函 数 : var newFun = Function (funArgs, funBody);创建函数对象的主要步骤如下:
1. 创建一个build-in object对象fn;
2. 设置fn的[[Prototype]]为Function.prototype;
3. 设置fn的[[Construct]]为Function ;
4. 设置fn的[[Call]]属性,它是内部实现的一个方法,处理逻辑参考对象创建过程的步骤3 ;
5. 设置fn的length为funArgs.length,如果函数没有参数,则将fn.length设置为0 ;
6. 使用new Object()同样的逻辑创建一个Object对象fnProto ;
7. 将fnProto.constructor设为fn ;
8. 将fn.prototype设为fnProto ;
9. 返回fn ;
2012/4/19 21
function fx(){ }
Constructor
fx.prototype
Prototype
Constructor
[[Prototype]]
[[Prototype]]
Function
Constructor
Function.prototype
Prototype
Constructor
[[Prototype]]
[[Prototype]]
ObjectConstructor
Object.prototype
Prototype
Constructor[[Prototype]]
[[Prototype]]
Number / String / ...
Constructor
Prototype
Constructor
[[Prototype]]
Number.prototype / String.prototype / ...
[[Prototype]]
函数对象的创建过程
2012/4/19 22
步骤145
步骤6
步骤8
步骤7
步骤2
步骤3
一切都是对象 还记得前面说过“Boolean, Number, String, Date, Array, RegExp, Error都是JavaScript语言的内置对象,它们都可以看作是函数的派生类型,在这个意义上,可以将它们跟用户定义的函数等同看待。”
2012/4/19 23
function fx(){ }
Constructor
fx.prototype
Prototype
Constructor
[[Prototype]]
[[Prototype]]
Function
Constructor
Function.prototype
Prototype
Constructor
[[Prototype]]
[[Prototype]]
ObjectConstructor
Object.prototype
Prototype
Constructor[[Prototype]]
[[Prototype]]
Number / String / ...
Constructor
Prototype
Constructor
[[Prototype]]
Number.prototype / String.prototype / ...
[[Prototype]]
JavaScript Object Model overview
2012/4/19 24
Prototype
function fx(){ }
Constructor
fx.prototype
Prototype
Constructor
[[Prototype]]
[[Prototype]]
Function
Constructor
Function.prototypeConstructor
[[Prototype]]
[[Prototype]]
ObjectConstructor
Object.prototype
Prototype
Constructor[[Prototype]]
[[Prototype]]
Number / String / ...
Constructor
Constructor
[[Prototype]]
Number.prototype / String.prototype / ...
[[Prototype]]
var x = new fx()
[[Prototype]]
var a = new Number(123) /var y = 123 / var s = 'sss'
Prototype
var o = new Object() /Var oo = { }
[[Prototype]]
[[Prototype]]
对象属性的读写不对称
2012/4/19 25
对象通过[[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共享属性,还可能向其它对象共享属性,修改它可能影响其它对象。
对象属性的读写不对称
2012/4/19 26
//试试 function fn(){}; fn.prototype = { attr:"aaa"}; var obj1 = new fn(); var obj2 = new fn(); var obj3 = new fn(); //obj.__proto__设置为fn.prototype console.log(obj1.attr, obj2.attr, obj3.attr); //output: aaa aaa aaa obj2.attr = “bbb“ //属性写操作,并不会改变fn.prototype的attr属性 console.log(obj1.attr, obj2.attr, obj3.attr); //output: aaa bbb aaa fn.prototype.attr = "ccc"; //虽然这不是个好的方法,但你确实可以这样统一改变所有实例的继承属性 console.log(obj1.attr, obj2.attr, obj3.attr); //output:ccc bbb ccc //其中attr已经成为obj2的本地属性
总结 summary
2012/4/19 27
1. 5种原始类型:数字、字符串、布尔值、undefined、null;
2. 对象类型本质都是函数对象,可以不用户定义函数等同看待;
3. 数据包装类型是对内置数据类型的操作接口,瞬态对象转换;
1. Object是函数对象,可以看作是Function的派生类;
2. 所有的函数对象都派生自Object,Function也丌例外;
1. 实例的[[Prototype]]指向函数对象的prototype;
2. [[Prototype]]用于递归向上链式查找,终点为null;
3. Prototype用于继承,向下影响;
@Kener-林峰
2012/4/19 28
参考:
• 《JavaScript权威指南》
• 《JavaScript高级程序设计》
• http://www.cnblogs.com/RicCC/archive/2008/02/15/JavaScript-Object-Model-Execution-Model.html
• http://www.laruence.com/2010/05/13/1462.html
• http://msdn.microsoft.com/en-us/library/hh185006.aspx
• http://tinf2.vub.ac.be/~dvermeir/java/java_script/model.html