一、Object
An object is a collection of properties and has a single prototype object. The prototype may be either an object or the null
value.
Object是有属性集合以及单例的原型对象组成的。原型可以为object和null。
Object的原型对象是通过对象内部的[[Prototype]]属性来引用的。下图我们将用__<internal-property>__ 替代[[Prototype]] (在FF的JS引擎中使用的是__proto__).
var foo = {
x: 10,
y: 20
};
上面的代码段,我们将有2个显示属性和一个隐式__proto__ 属性,该隐式属性是foo原型的引用。
这些属性有什么用,下面就来看看原型链。
二、A prototype chain
原型对象也是简单的对象也可以有他自己的原型,如果一个原型有不为null的引用指向到他的原型(If a prototype has a non-null reference to its prototype),如此反复,我们就成为原型链。原型链是有限的对象链(能有终点的),用来继承和共享属性。
下面我们就来使用基于原型链的继承写几个类。
var a = {
x: 10,
calculate: function (z) {
return this.x + this.y + z
}
};
var b = {
y: 20,
__proto__: a
};
var c = {
y: 30,
__proto__: a
};
// call the inherited method
b.calculate(30); // 60
c.calculate(40); // 80
对象b和c都能使用a的方法,这就是原型链起的作用。
规则很简单:如果一个方法或者属性在当前对象找不到,他会尝试着去原型链中查找,如果原型中没找到,则会从原型的原型中查找,直到整个原型链。最先找到的那个属性和方法将会被使用,如果遍历了整个原型链都未找到则会返回undefined。
注意,this的取值(that this
value in using an inherited method is set to the original object,
but not to the (prototype) object in which the method is found.)也是从原型链中取得的,比如this.y 是b和c中的值而不是a,但是this.x却是从a中取得的。
如果一个原型没有直接指明一个object,那么将使用Object.prototype.作为__proto__,而对象Object.prototype 自己的__proto__最终会指向null。
下图清晰的描绘了原型链
在FF中运行的结果:
通常我们创建对象的时候希望他会有相同的状态和属性值,这时我们就需要利用构造函数(constructor function )
三、constructor
构造函数除了能够创建一个特定模式的对象之外,还有另一个用途:为新建的对象自动设置一个原型对象,这个原型对象存储在ConstructorFunction.prototype 属性中。例如:我们利用构造函数重写之前的代码
// a constructor function
function Foo(y) {
// which may create objects
// by specified pattern: they have after
// creation own "y" property
this.y = y;
}
// also "Foo.prototype" stores reference
// to the prototype of newly created objects,
// so we may use it to define shared/inherited
// properties or methods, so the same as in
// previous example we have:
// inherited property "x"
Foo.prototype.x = 10;
// and inherited method "calculate"
Foo.prototype.calculate = function (z) {
return this.x + this.y + z;
};
// now create our "b" and "c"
// objects using "pattern" Foo
var b = new Foo(20);
var c = new Foo(30);
// call the inherited method
b.calculate(30); // 60
c.calculate(40); // 80
// let's show that we reference
// properties we expect
console.log(
b.__proto__ === Foo.prototype, // true
c.__proto__ === Foo.prototype, // true
// also "Foo.prototype" automatically creates
// a special property "constructor", which is a
// reference to the constructor function itself;
// instances "b" and "c" may found it via
// delegation and use to check their constructor
b.constructor === Foo, // true
c.constructor === Foo, // true
Foo.prototype.constructor === Foo // true
b.calculate === b.__proto__.calculate, // true
b.__proto__.calculate === Foo.prototype.calculate // true
);
四、execution context stack
ECMAScript代码分为三种:全局代码、函数代码、eval代码。每个代码都会在执行上下文中运行。全局上下文只有一个,而函数和eval执行上下文却有很多实例。每调用一次函数都会进入函数执行上下文,每次调用eval函数,都会进入eval执行上下文执行代码。由于每次调用函数都会产生一个新的执行状态的上下文,所以每个函数都可能产生无限个上下文。一个执行上下文会激活另一个上下文,比如一个函数调用另一个函数,最终会产生一个执行上下文堆栈。一个上下文激活另一个上下文则被称为caller,被激活的则称为callee。一个callee可能同时又是caller(比如一个函数被全局函数调用之后,可能会调用内部函数)。
当调用者(caller)激活(calls)被调用者(callee),会中断当前的执行,同时将控制流转向被调用者。被调用者压入栈成为正在执行的执行上下文,待到调用结束,将控制转交给调用者,继续执行调用者的部分直到结束。被调用者如果由于未捕捉的异常直接返回或者退出,则会终止一个或多个上下文。
执行上下文堆栈由program runtime负责,栈的最顶端即是active context。
当程序进入全局执行上下文(global execution context),即stack中最低端的的第一个那个元素。首先全局代码提供初始化,创建必须的对象和函数。在执行全局上下文的时候会激活一些已经创建好的函数,进入函数的执行上下文,同时将一些新的元素压入堆栈。等到初始化结束的时候,系统会进入等待阶段,等待一些事件,比如用户鼠标单击事件,以便激活新的函数,进入新的执行上下文。
堆栈中的每一个执行上下文都可以被描述成一个对象。接下来,我们看看这个对象的结构,以及执行它的代码的时候都需要哪些具体的属性。
五、Execution context
每一个执行上下文对象都有一个属性集合,我们称之为上下文状态(context’s state),这些属性集合用来跟踪执行进度以及关联到相关执行代码。结构图如下:
除了(a variable object, a this
value and
a scope chain)这三个属性之外,在具体的过程中执行上下文还需要其他一些附加的属性。
接下来详细讨论下这三个属性。