`

javascript

    博客分类:
  • java
阅读更多

javascript中的数据类型、Object与Function

文章分类:Web前端

1. 数据类型

javascript中包含6种数据类型:undefined、null、string、number、boolean和object。其中,前5 种是原始数据类型,object是对象类型。

object类型中包括Object、Function、String、Number、Boolean、Array、Regexp、Date、 Globel、Math、Error,以及宿主环境提供的object类型。

2. 类型判断

通常在javascript中进行类型判断主要通过3种方式:typeof、instanceof、constructor。

2.1 typeof

typeof操作可能返回的类型为undefined、object、number、string、function、boolean。但是会有一些情况并不能完全判断准确。比如typeof new String('')的值为object。

2.2 constructor

有时候我们可能会很偷懒的使用 a.constructor == String进行类型判断,但是constructor其实是不靠谱的东西。因为当我们调用a.constructor的时候,内部操作其实是 ToObject(a).prototype.constructor(ToObject是什么,看下文分解)。

看下面一段代码就能明白:

String

.

prototype

.

constructor

 =

 Number

;

 
alert

(

'test'

.

constructor

 ==

 String

);

 //Result:false

 

或者

function

 MyClass

()

 {

 
}

 
MyClass

.

prototype

 =

 {};

 
alert

((

new

 MyClass

).

constructor

 ==

 MyClass

);

 //Result:false

 

而且,通过constructor并不能判断出对象实例类型的继承关系。因为javascript的继承其实是通过原型链实现的(原型链是什么,看下文分解)。

另外,null.constructor会抛出运行时的TypeError,所以使用constructor除了不靠谱外,还可能伴随着异常的风险。

2.3 instanceof

例子:a instanceof String

关于object类型的判断,使用 instanceof判断是比较靠谱的方法。instanceof所做的事情是,先取出类型对象(String) 的prototype成员(String.prototype),然后和要判断类型的对象(a)的原型链中的对象逐个比较。当发现是一个对象的时候返回 true,原型链中当前节点是null的时候返回false。

类型判断示例:判断一个变量是否是字符串类型

function

 isString

(

str

)

 {

 
    return

 (

typeof

 str

 ==

 'string'

 ||

 str

 instanceof

 String

);

 
}

 

3. 类型转换

ecma262中描述了以下几种类型转换的操作:(还有其他的比如ToInt32等,这里就不列了)

  • ToNumber:转换成number型
  • ToString:转换成string型
  • ToBoolean:转换成boolean型
  • ToObject:转换成object型
  • ToPrimitive:转换成原始类型

每种操作都描述了从什么类型转换成该类型的映射。比如上文的'a'.constructor中,就包含解析器使用ToObject将‘a’转换成 object的一个隐式操作。

这里想要主要说的是ToPrimitive。 ToPrimitive用于转换成原始数据类型。当要转换的量已经是原始类型时,会直接返回。如果要转换的是一个Object,那会调用 [[DefaultValue]]方法做转换。([[DefaultValue]]是什么,下文分解)该方法可以传入一个hint参数,说明需要将 Object转换成字符串或数字。如果要转换成字符串,则调用Object的toString方法,如果要转换成数字,则调用 Object的valueOf方法。具体在运行时什么时候应该转换成什么类型,请参考ecma262中关于expression的描述部分。

------------------- 切割线:写累了,喝点水去 ----------------------

4. Object

除了5种原始类型外,一切都是Object,包括Object、Function、Array等等,他们的实例和构造器,都是Object。那 Object是一个什么东西呢?

Object是一个:无序的成员集合

它是一个集合,说明它包含0-n个成员。而它是无序的。

每一个成员由以下3个部分组成:名称、值、特征集合

下面的代码中:

var

 obj

 =

 {

'key'

:

 'value'

};

 

key就是成员名称,value就是值,obj这个Object从代码上看起来包含了一个成员,注意,是从代码上看而已。这里我们不去深究它先。

那特征集合是个什么东西呢?

javascript的对象成员可能包含下面几种特征的0个或多个:ReadOnly、DontEnum、DontDelete、 Internal。

  • ReadOnly:拥有这个特征的成员是不能被程序修改的。
  • DontEnum:拥有这个特征的成员是不能被for in遍历的。
  • DontDelete:拥有这个特征的成员是不能被delete操作删除的。
  • Internal:代表这个成员是内部成员。通常内部成员不能被程序以任何方式访问,但是有些javascript的引擎实现将它以特殊方式暴露,使得可以访问对象的某些内部成员。

一个对象的Internal成员以[[xxxx]]的方式来表示。

下面列一些和本博有关的的Object可能包含的internal成员。

  • [[Class]]:表示该对象的类型。比如function Object的[[Class]]成员的值是"Function"
  • [[Get]](PropertyName):获取对象的属性值。
  • [[DefaultValue]] (Hint):用于ToPrimitive进行类型转换时调用。hint参数可能的值为"string"或"number"
  • [[Prototype]]:[[Prototype]]成员 实现了javascript中所谓的“原型链”。一个对象的[[Prototype]]成员可能是object对象,或者是null。只有Object. [[prototype]]为null,其他任何对象的[[Prototype]]成员都是一个Object
  • [[Call]]:function Object特有的成员,在函数被调用的时候,就是调用的[[Call]]。
  • [[Construct]]:function Object特有的成员,在函数作为构造器,被new操作符用于创建对象的时候,就是调用的[[Construct]]。
  • [[Scope]]:[[Prototype]]成员实现了javascript中所谓的“作用域链”。

------------------- 切割线:手开始酸了 ----------------------

5. function Object的创建过程

解析器在遇到function declaration或者function expression的时候,会创建一个function Object。步骤大致如下:

  1. 解析形参和函数体
  2. 创建一个native ECMAScript Object:F
  3. 设置F的[[Class]]、[[Prototype]]、[[Call]]、[[Construct]]、[[Scope]]、length属性
  4. 创建一个new Object():O
  5. 设置O的constructor属性为F
  6. 设置F的prototype属性为O

在这个创建过程里,要说明的几点是:

  1. 步骤3中F的[[Prototype]]被设置为Function.prototype
  2. 用户自定义的function,都会同时具有[[Call]]和[[Construct]]这两个internal属性
  3. 解析器会自动给每一个function Object初始化一个prototype成员。而F.prototype.constructor == F,所以,当我们没有重新定义这个F的prototype成员的时候,F的实例的constructor成员是靠谱的。因为(new F).constructor其实方位的是F.prototype.constructor,而解析器默认初始化给你的 F.prototype.constructor就是F。
  4. 关于[[scope]]和作用域链的问题,下文分解

还要提的一点是,function declaration和function expression是不一样的

function declaration:

function

 fn

()

 {}

 

function expression:

var

 a

 =

 function

 ()

 {};

 
function

 ()

 {};

 

------------------- 切割线:坐着怎么那么热呢 ----------------------

6. 原型链

首先要澄清的一点是,我们通常会使用 myfunction.prototype的方式进行原型扩展,所以我们在听到“原型链”这个词的时候,会觉得这里的“原型”指的是 myfunction.prototype。其实不是,“原型”指的是对象的[[Prototype]]。当然,对象的 [[Prototype]]就是其真实构造器当前的prototype成员对象。

上文中有提过,一个我们通过程序创建的function Object,一定会包含[[Call]]和[[Construct]]这2个internal成员。它们做了什么事情呢?

[[Call]]:

  1. Establish a new execution context using F's FormalParameterList, the passed arguments list, and the this value as described in 10.2.3.
  2. Evaluate F's FunctionBody.
  3. Exit the execution context established in step 1, restoring the previous execution context.
  4. If Result(2). type is throw then throw Result(2). value.
  5. If Result(2). type is return then return Result(2). value.
  6. (Result(2). type must be normal.) Return undefined.

[[Construct]]:

  1. Create a new native ECMAScript object.
  2. Set the [[Class]] property of Result(1) to "Object".
  3. Get the value of the prototype property of F.
  4. If Result(3) is an object, set the [[Prototype]] property of Result(1) to Result(3).
  5. If Result(3) is not an object, set the [[Prototype]] property of Result(1) to the original Object prototype object as described in 15.2.3.1.
  6. Invoke the [[Call]] property of F, providing Result(1) as the this value and providing the argument list passed into [[Construct]] as the argument values.
  7. If Type(Result(6)) is Object then return Result(6).
  8. Return Result(1).

一切都很清楚了。当我们创建一个对象,也就是我们new的时候,调用的是function Object的[[Construct]]成员方法。在上面的描述中,3、4步描述了[[Prototype]]成员的创建过程,就是构造器的 prototype成员。

好的,那回到之前,我们使用obj.property来获取obj对象的属性的时候,其实调用的是obj对象的internal方法 [[Get]]。那我们看看[[Get]]方法调用做了哪些事情:

  1. If O doesn't have a property with name P, go to step 4.
  2. Get the value of the property.
  3. Return Result(2).
  4. If the [[Prototype]] of O is null, return undefined.
  5. Call the [[Get]] method of [[Prototype]] with property name P.
  6. Return Result(5).

可以看出来,当我们获取对象obj的某个成员的时 候,会在obj对象自身成员里查找是否存在该成员。如果不包含,则到obj. [[Prototype]]这个对象中查找名字成员,如果还不存在,则到obj.[[Prototype]].[[Prototype]]这个对象里找, 直到某个[[Prototype]]是null为止。查找的过程就是一个顺藤摸瓜的事情,这个藤就是我们所谓的“原型链”。

我不想说太多原型链和继承之间的关系与实现,这方面的资料在网络上已经太多太多。我只想把原型链脱光了告诉大家,原型链是什么。

------------------- 切割线:脑子发胀中 ----------------------

7. 函数调用过程与作用域链

讲到作用域链,就要扯到函数的调用。当我们有一个函数

function

 fn

(

param

)

 {}

 

我们去调用它

fn

(

1

);

 

这个时候解析器为我们做了什么呢?

有一定经验的javascript工程师也许会用过arguments、用过闭包、知道作用域,这一切的一切,都和execution context有关。

当我们进入一个函数调用的时候,解析器会为我们创建一个活动对象(Activation Object ),假设这里把这个活动对象叫做ac(为什么不叫ao呢,因为喜欢c)。然后做下面的事情:

  1. 初始化arguments对象,并将它添加到这个ac中。这个时候,对象ac就拥有了一个name为arguments的成员。这里arguments初始化过程就不具体说了,感兴趣的可以看ecma262的章节10.1.8。
  2. 解析形参,并使用函数调用时传递的参数初始化。在上面的调用例子fn(1)中,这个时候,ac就拥有了一个name为param的成员,这个成员的值为 1。
  3. 对function declaration进行初始化,为所有FunctionBody中的function declaration,创建function Object,并添加到对象ac中作为ac的成员。在这一步,假设ac中已经包含了同名属性,会被覆盖掉。
  4. 对var声明进行初始化,为所有var声明,在对象ac中创建同名成员,并初始化为undefined。在这一步,假设ac中已经包含了同名属性,不会被覆盖掉。
  5. 初始化作用域链,并将这个作用域链与当前的执行上下文相关联。 这个作用域链是一个链式列表,最前段是进入函数调用时初始化出来的活动对象ac,然后后面跟着的是该函数的[[scope]]的成员。[[scope]] 是个什么东西呢,就是这个链。假如函数体中有创建function Object,叫做innerFn,那innerFn的[[scope]]成员,就是这个作用域链。当innerFn被调用时,会初始化新的活动对象,新 的作用域链。新的作用域链就是初始化自这个新的活动对象和innerFn的[[scope]]。

那scope chain是什么作用呢?看下面的描述,来自10.1.4

During execution, the syntactic production PrimaryExpression : Identifier is evaluated using the following algorithm:

  1. Get the next object in the scope chain. If there isn't one, go to step 5.
  2. Call the [[HasProperty]] method of Result(1), passing the Identifier as the property name.
  3. If Result(2) is true, return a value of type Reference whose base object is Result(1) and whose property name is the Identifier.
  4. Go to step 1.
  5. Return a value of type Reference whose base object is null and whose property name is the Identifier. 可以看出,我们在访问一个变量的时候,其实是从和当前执行上下文相关的作用域链中查找成员。

在程序正常在全局下的函数,其[[scope]]成员的值是global object,所以无论任何调用,在作用域链的尾端,一定会是global object。在浏览器宿主环境下,就是window。

------------------- 切割线:感叹中,怎么还没写完,唠唠叨叨的,受不了自己了 ----------------------

8. 函数调用过程中的this

在函数的调用中,this是个什么东西,又是由什么决定的呢?在ecma262中,这是个比较绕的东西,其描述散落在世界各地。

首先,在10.2.3中告诉我们: The caller provides the this value. If the this value provided by the caller is not an object (note that null is not an object), then the this value is the global object. 我们可以知道,caller可以提供给我们this。如果没有提供,则this为global object。问题又来了,caller是怎么提供this的?

在11.2.3中,找到如下关于Function calls的描述:The production CallExpression : MemberExpression Arguments is evaluated as follows:

  1. Evaluate MemberExpression.
  2. Evaluate Arguments, producing an internal list of argument values (see 11.2.4).
  3. Call GetValue(Result(1)).
  4. If Type(Result(3)) is not Object, throw a TypeError exception.
  5. If Result(3) does not implement the internal [[Call]] method, throw a TypeError exception.
  6. If Type(Result(1)) is Reference, Result(6) is GetBase(Result(1)). Otherwise, Result(6) is null.
  7. If Result(6) is an activation object, Result(7) is null. Otherwise, Result(7) is the same as Result(6).
  8. Call the [[Call]] method on Result(3), providing Result(7) as the this value and providing the list Result(2) as the argument values.
  9. Return Result(8).

从步骤6、7中可以看出来,如果 MemberExpression的结果是一个Reference的话,提供的this应该是 GetBase(Reference),否则是空。步骤7中还有描述了6的结果是活动对象的情况,我们这里忽略。 又有疑问了,Reference?Reference是什么,GetBase又是什么?

我们在8.7中,找到了Reference的答案。 这里的描述比较长,我只摘了可以满足我们需要的一段: A Reference is a reference to a property of an object. A Reference consists of two components, the base object and the property name.

The following abstract operations are used in this specification to access the components of references:

GetBase(V). Returns the base object component of the reference V.

GetPropertyName(V). Returns the property name component of the reference V.

已经很明显了,一个Reference必须引用一个 对象的一个属性。所以我们通过obj.method()来调用的时候,obj.method这个表达式生成了一个中间态的Reference,这个 Reference的base object就是obj,所以GetBase的结果就是obj,于是obj被caller提供作this

我曾经看到很多文章,举了类似obj.method()这样的调用例子,认为obj就是caller,来解释这番话:

The caller provides the this value. If the this value provided by the caller is not an object (note that null is not an object), then the this value is the global object.

这其实是说不通的。

caller绝不可能是obj,否则被attachEvent的函数或对象方法,他们运行时的this就解释不通了。 所以,通过我们自己代码调用的函数,caller由脚本引擎执行控制所决定;在浏览器宿主环境通过事件触发的,caller由浏览器控制的行为所决定。

------------------- 切割线:坚持坚持,好不容易想写点正经东西,快了 ----------------------

9. 关于原型链的补充——原型链会不会是圆形链

这个问题是telei同学提出的。答案是:不会

回头看看[[Construct]]的步骤,我们可 以发现,创建一个对象obj时,obj.[[prototype]]成员被赋予其构造器的 prototype成员。但是当构造器的prototype成员被指向为另外一个对象的引用时,obj.[[prototype]]依然是其构造器的前 prototype对象。

描述代码如下:(注释里是说明)

function

 A

(){

 
        this

.

testA

 =

 new

 Function

();

 
}

 
function

 B

(){

 
        this

.

testB

 =

 new

 Function

();

 
}

 
 
var

 a

 =

 new

 A

();

 
 
B

.

prototype

 =

 a

;

 
//a.[[prototype]] == {};(不是真的等,{}表示的是Function A初始的prototype object。下同)

 
 
var

 b

 =

 new

 B

();

 
//b.[[prototype]] == a;

 
//b.[[prototype]].[[prototype]] == a.[[prototype]] == {};

 
 
A

.

prototype

 =

 b

;

 
 
var

 a2

 =

 new

 A

();

 
//a2.[[prototype]] == b;

 
//a2.[[prototype]].[[prototype]] == b.[[prototype]] == a;

 
//a2.[[prototype]].[[prototype]].[[prototype]] == b.[[prototype]].[[prototype]] == a.[[prototype]] == {};

 
 
//最后测试一下,很搞笑的

 
alert

(

a

 instanceof

 A

);

 

最后特殊的解释:好吧,上面代码的最后出现了很搞笑 的事情,合乎语言的实现,但不合乎正常以及不正常地球人的逻辑。 我们知道,a对象是被A构造器创建出来的,所以a是A的实例。 但是,上面类型判断那里有讲,instanceof是通过构造器prototype成员与对象原型链的比较来判断的。所以当对象a被创建后,如果创建它的 构造器的prototype发生了变化,a就和他妈(构造器)没任何关系了。 看到这里,你确定你还想要在实例化对象后,修改构造器的prototype成另外一个对象吗?

------------------- 切割线:我是结束前 ----------------------

好了,就写这么多吧,好久不码那么多字了…………

渴了,有没有人请我喝饮料~~

分享到:
评论

相关推荐

    深入理解JavaScript系列

    本书是一本全面、深入介绍JavaScript语言的学习指南。本书共分四个部分,第1部分帮助读者快速入手,掌握基本的JavaScript编程要点;第2部分介绍JavaScript的发展和技术背景;第3部分深入探索JavaScript,介绍了语法...

    head first JavaScript源码

    《Head First JavaScript源码》是2010年出版的一本中文版JavaScript学习书籍的源码,这本书以其独特的视觉设计和易理解的方式深入浅出地介绍了JavaScript编程语言。JavaScript是一种广泛应用于网页和网络应用的脚本...

    JavaScript语言精粹完整版

    《JavaScript语言精粹》作为一本深入浅出讲解JavaScript编程语言的经典之作,对于想要提升自己JavaScript技能的开发者来说,无疑是一份宝贵的资源。本书由Douglas Crockford所著,他不仅是JSON格式的创始人,也是...

    Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript[EPUB版]

    Author David Herman, with his years of experience on Ecma’s JavaScript standardization committee, illuminates the language’s inner workings as never before—helping you take full advantage of ...

    VS2015安装证书,JavaScript_ProjectSystem.msi,JavaScript_LanguageService.msi

    在这个场景中,我们关注的是VS2015的安装过程中涉及到的证书问题以及两个特定的组件:JavaScript_ProjectSystem.msi和JavaScript_LanguageService.msi。 首先,关于“VS2015安装证书”,这通常是指安装过程中需要...

    《JavaScript程序设计案例教程》课件(共13章)第1章 JavaScript程序设计案例教程.pdf

    《JavaScript程序设计案例教程》课件(共13章)第1章 JavaScript程序设计案例教程.pdf《JavaScript程序设计案例教程》课件(共13章)第1章 JavaScript程序设计案例教程.pdf《JavaScript程序设计案例教程》课件(共13章)第...

    面向对象JavaScript精要(英文原版pdf)

    ### 面向对象JavaScript精要 #### 一、书籍概览 本书《面向对象JavaScript精要》由Nicholas C. Zakas编写,是面向对象编程领域中的权威指南之一,尤其针对JavaScript这门语言。作者深入浅出地介绍了面向对象编程的...

    ArcGis-for-javaScript最全中文API.pdf

    ArcGIS JavaScript API 介绍与示例 ArcGIS JavaScript API 是 ESRI 提供的一种基于 JavaScript 的开发工具,允许开发者创建交互式的 Web 地图应用程序。本文将对 ArcGIS JavaScript API 进行详细的介绍,并提供多个...

    jQuery JavaScript与CSS开发入门经典

    jQuery将JavaScript编程量精简为寥寥数行代码,使JavaScript变得更直观,更富魅力。iQuery还允许同时为一个或多个元素设置样式,使得通过JavaScript操纵CSS变得分外轻松。 在这本面向项目的精品书籍的指引下,即使是...

    JavaScript结课大作业.zip

    JavaScript是一种广泛应用于网页和网络应用开发的脚本语言,它主要负责实现客户端的交互效果、动态更新内容以及处理用户输入。在"JavaScript结课大作业.zip"这个压缩包中,我们可以推测这是一份关于JavaScript编程的...

    JavaScript基础语法(ppt)

    JavaScript基础语法(ppt)JavaScript基础语法(ppt)JavaScript基础语法(ppt)JavaScript基础语法(ppt)JavaScript基础语法(ppt)JavaScript基础语法(ppt)JavaScript基础语法(ppt)JavaScript基础语法(ppt)...

    JavaScript前端开发案例教程-源代码.rar

    JavaScript,作为全球最广泛使用的编程语言之一,是前端开发的核心技术。这个名为"JavaScript前端开发案例教程-源代码.rar"的压缩包文件提供了一系列实践案例,帮助开发者深入理解和掌握JavaScript在网页开发中的...

    Javascript 高级程序设计(第3版)超清中文PDF

    JavaScript,作为一种广泛应用于Web开发的脚本语言,已经成为现代互联网技术不可或缺的一部分。《JavaScript高级程序设计》(第3版)是一本深入探讨该语言精髓的权威书籍,它为读者提供了全面且深入的JavaScript知识...

    JavaScript DOM编程艺术【第2版&高清】.pdf

    但是,根据标题《JavaScript DOM编程艺术【第2版&高清】.pdf》和描述“JavaScript DOM编程艺术,高清资源,无比经典,值得拥有”,我们可以推断这本书主要讲述了JavaScript中DOM(文档对象模型)的相关编程技术。...

    JavaScript&jQuery;交互式Web前端开发

    通过将编程理论与用来演示JavaScript和jQuery如何被应用于流行站点之上的示例相结合,《JavaScript&jQuery;交互式Web前端开发》将教会您如何让网站更具交互性、吸引性、可用性。很快,您就能够像一名程序员那样去...

    [JavaScript] Effective JavaScript 编写高质量JavaScript代码的68个有效方法 (英文版)

    [Addison-Wesley Professional] Effective JavaScript 编写高质量JavaScript代码的68个有效方法 (英文版) [Addison-Wesley Professional] Effective JavaScript 68 Specific Ways to Harness the Power of ...

    JavaScript手册 JavaScript手册

    JavaScript是一种广泛应用于网页和网络应用的编程语言,尤其在客户端脚本方面扮演着核心角色。JavaScript手册是学习和掌握这门语言的重要参考资料。手册通常包含了语言的基础语法、数据类型、控制结构、函数、对象、...

    JavaScript入门经典(第3版)高清PDF—3

    《JavaScript入门经典(第3版)》首先介绍了JavaScript的基本语法,并介绍了如何发挥JavaScript中对象的威力。《JavaScript入门经典(第3版)》还介绍了如何操纵最新版本浏览器所提供的BOM对象。在《JavaScript入门经典...

    JavaScript函数(源代码)

    JavaScript函数(源代码)JavaScript函数(源代码)JavaScript函数(源代码)JavaScript函数(源代码)JavaScript函数(源代码)JavaScript函数(源代码)JavaScript函数(源代码)JavaScript函数(源代码)...

    Free JavaScript Editor 4.7 javascript编辑器 天涯浪子

    Free JavaScript Editor编辑器是一个先进的Java脚本编辑器,验证器和调试器对于初学者和专家。 免费的JavaScript编辑器是用于专业编辑JavaScript代码和创建动画和其他特殊效果网页使用DOM中,使用DOM, DHTML, CSS, ...

Global site tag (gtag.js) - Google Analytics