JavaScript数据类型
本文假设读者已经学习过类C/C++这样的编程语言,从这篇文章开始,博主讲以一个C/C++程序员的视角来学习JavaScript编程语言。学习一门新的编程语言,无非就是学习语法的以下属性:(1)基本数据类型,变量及扩展数据类型;(2)函数;(3)对象及其继承多态等机制。今天就让我们看看JavaScript数据类型与C/C++有和不同。
一、基本数据类型
1、 简单数据类型
数字(number),字符串(string),布尔值(boolean),Null,Undefinded。前三种都不是对象。但是Js中提供了将他们包装成对象的方法(构造器)。他们也称为原生数据类型。
2、复杂数据类型
数组,对象,函数属于复杂数据类型,对于数组我们不做过多介绍,我们只要知道它是一种特殊对象就可以了。这里我们需 要仔细探讨的是对象和函数。实际上在Js中函数也是对象。但是对象不一定是对象。只有函数对象才是函数,一般由内置构造器Function或者关键字function来声明。
(1)Function与Object
Function和Object是所有函数和所有对象的根构造器。在Js中类型和构造器是直接关联的,在Js中构造器充当C/C++中类的角色。且构造器本身是一个函数对象。也许刚从类C++语言阵营转向Js的朋友们感觉这非常困惑。其实不然,我们
只需要跳出以类为模板的对象构造的定式思维。先看一个例子:
var function_name = new Function(arg1,arg2, ..., argN, function_body)
对于声明函数我们看个例子:
//函数声明1
//add全局,但是在之前属于未定义状态,不可调用
var add = new Function(x,y,"return x+y;")
//函数声明2
//add1同add
var add1 = function(x,y){return x+y;};
//函数声明3
//全局可调用
function add3(x,y)
{
return x+y;
}
三种方法产生的函数对象都是Function(类似类的构造器)的实例。也许你会困惑,Function本身是对象,他如何能够构造其他对象呢,其实这一点也不起怪,想想你自己。你是你爸妈生的,但是你爸妈也是一个人,他们并不是一个虚幻的类。至于你要追根溯源,想找出你的根祖先就有点难以理解,其实Js中大抵如此。博主也有一点没有特别清晰:既然函数也是对象,而对象又需要构造器(函数)来构造,那么是谁生成了构造函数的根构造器呢?那么是先有函数还是先有对象呢?也就是Object是否由Function构造?粗浅的理解:在Js中有一个元祖先----Object.prototype------它是万物之源,所有对象都默认直接或间接继承自它。也就是说Function和Object都具有该属性对象。事实上Object应该是Function的一个实例。因为所有函数都来自与Function,而对象的构造器是函数,所以对象的根构造器Object同样来自于Function。Function自己则来自于自己(也就是自举性)。不过Object和Function等内建对象构造器是Js解释引擎内部实现的。可能实现的具体细节有差异,但语义应该都是一样的。
(2)函数
在Js中函数有一个根构造器Function(对象),它是一个特别的函数,是一个极其特别的对象,因为其他构造器都是用来构造普通对象的,但是Function是用来构造
构造器的(函数),所以为区分这个特殊构造器和普通构造器,我们可以将Function称为元构造器。它不可以作为普通函数来调用。
构造器:每一个函数都有两个作用,(1)普通函数;(2)构造器----使用new关键字调用时(当函数有return语句时,会把构造产生的对象丢掉,返回return返回的结果);
构造器分类:(1)元构造器(Function)-----用于生成普通构造器;(2)普通构造器------用于产生非函数对象;
函数的两个重要属性:(1)函数对象所属原型{Prototype}-----__proto__; (2)作为构造器时,构造出来的普通对象的原型prototype;(__proto__属于对象,而prototype属于构造器用于构造对象时作为原型,这样的话函数对象的__proto__属性是构造他的构造器的prototype的一个引用。prototype类似于模板的作用,用于构造对象时作为图纸,而__proto__用于对象查询自己来自哪个模板,或者说共享了哪个原型对象,也是用于查询原型链的中介,或者说是原型链的节点)。
由于Js是动态语言,所以可以随时给构造器或构造器的prototype(原型对象)添加新的方法和属性。本来想到下一篇文章才会讲到继承的,但是这里先提一点,Js中所谓的原型继承,其实只是共享一个prototype对象来实现。严格来讲这并非严格意义的继承,prototype并没有真正被子对象继承。prototype只是由构造器维护,而子对象只是通过设obj.__proto__= ConstructFuc.prototype。对象只是维护原型对象的一个引用。并没有真正拥有它,它类似于C/C++中类中的静态成员,属于类,所有类的对象共有。例如:
(3)对象
在Js中对象由构造器产生(var object ={code block;}形式调用同样调用了构造器Object),对象由以下结构组成:
(1)一个{Prototype}属性,在IE中不可直接访问,但是提供了方法访问。但是firefox等提供了一个__proto__属性。用于原型机制。默认情况下,对象的原型为自身(__proto__ = this)。
(2)构造器中定义的数据结构(属性+方法)。)对象自身的数据结构(方法+属性);
(3) 构造器行为:(a)根据构造器中的属性和方法构造对象;(b)将该对象的原型__proto__设置为自身;(c)返回生成的对象。
3、原始类型和引用类型
Js中变量分为原始类型和引用类型。数值,布尔值,null和未定义的值属于原始类型,对象,数组和函数属于引用类型。
原始类型:赋值是值传递,且内存大小固定;
引用类型:赋值是传递引用,且内存大小可变;
从更深层次来讲,声明一个变量var variable;如果赋值一个原始类型的数据给它,那么该变量所对应的内存存储的就是实实在在地数据。而如果给他赋值一个引用类型的数据,那么是将该数据的存放地址放入变量中,这样取数据其实是间接寻址。
有一个比较特殊的类型----字符串,在《JavaScript权威指南》中也没说明白它到底属于哪一种类型。这里就不深究。
二、数据类型实现模型
Build-in *** data structure: 指JS内部用于实现***类型的数据结构,这些结构我们基本上无法直接操作。
Build-in *** object:指JS内置的Number, String, Boolean等这些对象,这是JS将内部实现的数据类型暴露给开发者使用的接口。
Build-in *** constructor:指JS内置的一些构造器,用来构造相应类型的对象实例。它们被包装成函数对象暴露出来。
三、对象模型
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=newNumber(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属性之后,情况就不同了。
下一篇我们将讨论JavaScript的原型继承机制:JavaScript原型继承机制
最后再声明一下,任何添加属性和方法的操作都是向对象添加属性和方法。
引用:
声明函数的方法:
http://www.360doc.com/content/09/0703/15/16915_4124895.shtml
对象模型:
http://www.cnblogs.com/RicCC/archive/2008/02/15/JavaScript-Object-Model-Execution-Model.html(强烈推荐)
http://zeekat.nl/articles/constructors-considered-mildly-confusing.html#fn.1
https://developer.mozilla.org/zh-CN/docs/JavaScript/Guide/Details_of_the_Object_Model
函数对象与函数名的区别:
http://www.cnblogs.com/ArthurPatten/p/3297992.html
javascript函数声明、函数表达式和函数构造器:
http://openwares.net/js/javascript_function.html
this指针:
http://www.ruanyifeng.com/blog/2010/04/using_this_keyword_in_javascript.html(阮一峰博客,强烈推荐)
理解javascript原型:
构造器行为:
http://stackoverflow.com/questions/1646698/what-is-the-new-keyword-in-javascript
http://zeekat.nl/articles/constructors-considered-mildly-confusing.html