ECMAScript 报告(上)

阅读数:618 发布时间:2016-09-28 22:41:24

作者:w4gyc 标签: ECMAScript

ECMAScript 报告(上)

一、什么是ECMA

我们知道JavaScript是脚本语言,脚本语言又被称为动态语言,什么是动态语言呢?像Java,C,它们都是静态语言,需要经过编译器转化成计算机可以直接读懂的代码后才可以运行。但Javascript是没有这个过程,它是由JavaScript引擎直接解析运行的。以网页为例,这个引擎是由浏览器来提供的,比如Chrome的V8,IE的 TraceMonkey。

ECMA就是用来规范JS引擎的一套标准。

二、数据类型 Type

学习一门语言都是从数据开始。我们先看一下ECMA中的类型。

ECMA中的规定类型有三大类:

【原始类型】:原始类型是最基本的数据类型,ECMA规定了五种原始类型。Undefined,Null,Number,String,Boolean

【对象类型】:Object。函数 function也是一个对象,它是一个可调用的对象。

这两种构成了EMCA语言的类型,也就是我们通常所说的JavaScript的六种数据类型。

还有一类,【特定类型】:是JavaScript引擎进行底层操作时要用到的,无法直接访问。特定类型有以下几种

Reference、List、Completion、Property Descriptor(属性描述符)、 Property Identifier(属性标识符) 、Lexical Environment(词法环境)、 Environment Record(环境记录) 。 类型和值可以看作数学上的集合的关系,每个Type都是一系列value的集合。我们先来看一下五种原始类型,原始类型的值都叫做原始值。

1、Undefined 未定义类型

可以用集合表示为 { undefined } 。它有且只有一个唯一值,undefined。

当我们var声明一个变量,没有进行赋值的时候,它的值就是undefined。

举例:

img1

(注意 typeof 返回的并不是ECMA真正的类型,它返回的是人们眼中的“类型”有function,没有null。)

2、Null 空类型

有且只有一个唯一值 { null }

3、Number 数值类型

{ 1,2,3……+∞ , -∞,NaN}。以双精度浮点型表式,有3个特殊的value,正负无穷大,和NaN。正数/0是+∞,负数/0是 -∞,0/0是NaN

4、String 字符串类型

它的字面量表示方法是 'a' "abc"。这里出现了字面量,什么是字面量呢?字面量是固定的值,不是变量,让你从字面上理解。比如 '123',是一个字符串字面量,它表示一个值为123的字符串。

5、Boolean 布尔型

{ ture ,false }

6、Object 对象类型

对象类型就是由很多对象组成的集合。这些对象分为两种,一种是本地对象native object,一种是宿主对象host object。(在这里注意大小写,Object是类型,object是具体的一个对象)。

我们先来了解一下什么是宿主对象。前面说过,JavaScript是需要解释器才能运行的,提供这个解释器的东西就叫做宿主。通常,宿主就是我们所用的Web浏览器。JS在运行中,有两种环境,一种环境是由JS引擎提供的,另一种,是由宿主提供的,叫作宿主环境。因为JS本身没有对外通信的能力,比如说它没有输入输出接口,这些东西就是由宿主环境来提供。比如我们常用的document.write(),alert。它们都是window这个宿主对象的方法。

由JS引擎创建的对象,就是本地对象。ECMA已经帮我们定义好了很多对象,比如我们常用的Date。这些对象叫做内置对象,所有的内置对象,都是本地对象。

下面我们就来看什么对象吧。

三、Object

看代码:

 var c = {
    x : 4,
    y : 5,
    add : function(){
        console.log(this.x + this.y);
    }}

 function cf(){

 };

cf.prototype = {
    f1 : function(){
        console.log('i am cf');
    }
 }

  var c1 = new cf();

这里用字面量表示法声明了一个名为 c 的对象,这个对象有三个属性:x y add。add这个属性,它的值是一个函数,我们叫它方法(method)。

对象,就是由一系列的属性和方法组成的。

还有一种创建对象的方法,即用new表达式,它需要一个函数,这个函数就是我们平常说的构造器。

首先定义了一个名为cf的函数,这个函数默认有一个prototype的属性,这个属性就是原型。这个原型本身也是一个对象,我们可以给这个原型赋值,给它定义了一个方法 add。然后new了一个cf的实例c1。

我们来看一下这个c1

img2

c1继承了cf。它有一个隐式属性proto这个属性是无法通过ECMA语句访问到的,是内部属性。它指向了cf.prototype。这就是原型链。

我们展开可以看到这个cf.prototype它也有一个_proto_的隐式属性。(因为原型对象本身也是由构造器创建的一个对象,只不过创建过程在ECMA内部)。这两个原型链就构成了一个链表的结构。

在ECMA中,每个函数都有一个prototype的属性。每个对象都有一个内部属性proto( 位于原型链顶层的对象除外 ).

所谓构造器,就是具有prototype属性的东西,因为只有有了这个属性,你才能够创建一个实例。所以每个函数,都是一个构造器。每个由构造器创建出来的东西,都有原型链。

所以,我们可以总结如下:

prototype是函数的一个属性,可以用 . 访问到。

_proto_是一个内部属性,它是隐式的无法用 . 直接访问到。它是一个指针,指向了构造它的函数的prototype属性。这就是原型链

prototype 和_proto_的关系可以这么表示:

var a = new b();
a._proto_ == b.prototype;

这里涉及到比较多概念,我们来分别看一下。首先,了解一下ECMA的内置对象,因为,我们创建的所有对象都是继承自内置对象的,学习内置对象很有必要。

四、内置对象Object和Function

ECMA规定的内置对象分为 全局对象 和 异常对象。

全局对象包括:Object , Function , Array , String , Boolean ,Number , Math ,Date , RegExp , JSON

异常对象包括:Error, EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError.

重点看两个,Object 和 Function。

内置对象Object (注意和类型Object区分。一个是类型,一个是具体的对象。)

什么是Object 我们用typeof 看一下

img3

,这个内置对象Object 其实是个function。为什么Object会是个function呢?我们再来看看内置对象Function。

img4

它也是一个function.

也就是说,这些内置对象都是以一个个函数的形式定义的,也就是一个个构造器。

比如 var a = new Object; a 就是Object这个构造器的一个实例.

而我们写的函数 function 就是Function这个构造器的一个实例。这就解释了,为什么在ECMA中,function也是一个对象。

既然Function是一个构造器,那个我们同样可以new一个函数对象。如下,new出来的是一个匿名函数。 anonymous就是匿名的意思。

img5

我们甚至可以给这个对象传参。

img6

这就是创建函数的第二种方法, new 表达式 。而我们通常直接写的函数的方法,就是字面量表示法 。

理解了函数和对象的关系之后,我们再来深入一下内置对象Object。

既然Object是一个对象,那它就有一系列的属性和方法。 它有一个方法Object.getPrototypeof(O ),返回的是这个O 的 _proto_指向, 我们来尝试一下

Object.getPrototypeOf(c1)
    Object {f1: function}
Object.getPrototypeOf(cf.prototype)
Object {}
Object.getPrototypeOf(Object.prototype)
null

可以看到,c1的原型链指向的是cf.prototype,cf.prototype的原型链指向了一个空对象Object。这个空对象是什么呢,来思考一下。

显然cf.prototype是一个对象,那它就应该是内置对象Obejct的一个实例,也就是用Object的构造器构造出来的。那么它的原型链指向的应该是Object.prototype。也就是说这个空对象是Object.prototype。

Do It !

img7

没错,这个空对象就是Object.prototype。我们沿着这个原型链再往上

img8

null,是空的,没有。没错,我们已经爬到了这个原型链的最顶端 ,Object.protoype。这就是ECMA中的"基类"。所有的对象都是由Object.prototype创建出来的。

那么Object又是怎么创建出来的呢?

img9

Object._proto_指向了一个空方法,这个空方法其实就是Function.prototype

img10

这很容易理解。前面说过Object实质是一个构造器, 我们typeof它,返回的是function,既然它是一个函数,那它就是Function的实例,它的_proto_就会指向Function.prototype

现在,我们从原型链的角度来分析一下c1这个对象是如何一步步被创建出来的。

  1. 用Function创建了一个cf
  2. 将cf的原型链指向Function的原型属性 cf.proto = Function.prototype
  3. 用Object创建一个cf的原型属性 cf.prototype;
  4. 将cf.prototype的原型链指向Object的原型属性 cf.prototype.proto = Object.prototype
  5. 用cf创建了一个c1
  6. 将c1的原型链指向cf的原型属性 c1.proto = cf.prototype

这些原型链构成了链表的形式,表示如下。 用 ---> 表示proto

c1的原型链表: c1 ---> cf.prototype ---> Object.prototype

cf的原型链表: cf ---> Function.prototype ---> Object.prototype

这个链表就决定了在查找属性时的顺序。ECMA在查找c1.p1时,会先查找它本身,若没有p1这个属性,再沿着_proto_找到 cf.prototype,最终找到Object.prototype,若还是没有,则返回undefined.

既然所有对象都是以Object.prototype为原型来创建的,我们就来看一下Object.prototype这个东西。

相关文章推荐: