JavaScript基础知识

       JavaScript

       JavaScript由核心(ECMAScript),文档对象模型(DOM),浏览器对象模型(BOM)三部分组成。

  

       JavaScript是弱类型语言,表明该语言在表达式运算中不强制校验运算元的数据类型。

       JavaScript能识别6种数据类型:undefined,number,stirng,boolean,function,object;可以通过typeof来识别变量属于这6种类型中的哪一种。

       运算符与语法分隔符的区别。

       语法分隔符:表明语句结束的“;”。

       在显示声明并赋初始值的语句中出现的“=”也是语法分隔符,而非运算符。

       “变量声明语句”是一个有关键字的语句,并不是表达式语句;

       “赋值语句”是一个表达式语句。

       表达式由运算符与运算元构成。运算元除了包括变量、直接量,还包括函数(或方法)的返回值。

       表达式受到运算符的优先级的影响,具有缺省次序、优先级次序和强制优先级。因此说表达式既有值的含义,也有逻辑的含义。

       JavaScript中,赋值是一个运算,而不是一个语句。在赋值表达式中,运算符左右都是运算元。在赋值表达式中,至少要包括一个赋值运算符。根据赋值运算符的规则,其左侧一定是一个变量或对象属性,右侧可以是值或求值的表达式。

       变量声明语句与赋值表达式存在根本的不同。在声明语句中的”=“是语句的语法分隔符,而不是”赋值运算符“,因此也就不能替代成”+=“(或其它符合赋值运算符)。

       JavaScript不是严格意义上的面向对象语言,JavaScript中对象的概念有别于面向对象语言。可以把JavaScript中的对象理解为一个动态的属性与函数集合。属性是一对键值对,它的值可以是简单的数据类型,也可以是对象(object),还可以是函数(方法)。由于其动态性,所以在运行时可以对对象的属性进行增删改操作。

       一个对象就是一个属性集合,并拥有一个独立的prototype(原型)对象,这个prototype也可以是一个对象。可以理解为每个对象除了有自身定义的属性外,还隐含着一个__proto__属性,这个属性是对原型对象的引用。

       原型对象也是对象,并且可以拥有其自己的原型,以此类推形成了原型链。

       如果一个对象没有明确指定其原型,那么其将使用__proto__的默认值,指向Object.prototype。Object.prototype对象自身也有一个__proto__属性,这是原型链的终点,并且值为null。

       在通过构造函数创建一个对象时,构造函数也做了一些其他事情,它自动地为新创建的对象设置一个原型对象。这个原型对象存储在ConstructorFunction.prototype属性中。

       执行上下文堆栈

       有三种类型的ECMAScript代码:全局代码、函数代码和eval代码。每个代码在其执行上下文(Execution Context)中被求值。只有一个全局上下文,可以有多个函数执行上下文和eval执行上下文。对一个函数的每次调用,会进入到函数的执行上下文中,并对函数代码进行求值。每次对eval函数进行调用,会进入eval执行上下文并对其代码进行求值。

        一个函数可能会创建无数的上下文,因为对函数的每次调用(即使这个函数递归地调用自己)都会生成一个具有新状态的上下文。

        一个执行上下文可能会触发另一个上下文,如一个函数调用另外一个函数(或者在全局上下文中调用一个全局函数)。从逻辑上来说,这是以栈的形式实现的,它叫做执行上下文栈。

        一个触发其他上下文的上下文叫做caller,被触发的上下文叫做callee,callee在同一时间可能是一些其他callee的caller(如一个在全局上下文中被调用的函数,之后调用了一些内部函数)。

       当一个caller触发一个callee,这个caller会暂缓自身的执行,然后把控制权传递给callee。这个callee呗push到栈中,并成为一个运行中(活动的)执行上下文。在callee的上下文结束后,它会把控制权返回给caller,然后caller的上下文继续执行(它可能触发其他上下文)直到它结束,以此类推。callee可能简单的返回或者由于异常而推出。一个抛出的但是没有被捕获的异常可能退出(从栈中pop)一个或者多个上下文。

       换句话说,所有ECMAScript程序的运行时可以用执行上下文(EC)栈来表示,栈顶是当前活跃的(active)上下文。当程序开始的时候它会进入全局执行上下文,此上下文位于栈底并且是栈中的第一个元素。然后全局代码进行一些初始化,创建需要的对象和函数。在全局上下文的执行过程中,它的代码可能触发其它(已经创建完成的)函数,这些函数将会进入它们自己的执行上下文,向栈中push新的元素,以此类推。当初始化完成之后,运行时系统(runtime system)就会等待一些事件(如用户鼠标点击),这些事件将会触发一些函数,从而进入新的执行上下文中。

       一个执行上下文可以抽象的表示为一个简单的对象。每一个执行上下文拥有一些属性(可以叫做上下文状态)用来跟踪和它相关的代码的执行过程。三个重要的属性包括变量对象(Variable Object),this值以及作用域链(Scope Chain)。

       变量对象,变量对象是与执行上下文相关的数据作用域,它是一个与上下文相关的特殊对象,其中存储了在上下文中定义的变量和函数声明。函数表达式(与函数声明相对)不包含在变量对象之中。变量对象是一个抽象概念,对于不同的上下文类型,在物理上,使用不同的对象。如在全局上下文中变量对象就是全局对象本身(这就是为什么可以通过全局对象的属性名来关联全局变量)。

       当一个函数被caller所触发(被调用),一个特殊的对象——活动对象(Active Object)将会被创建。这个对象中包含形参和那个特殊的arguments对象(是对形参的一个映射,但是值是通过索引来获取)。活动对象之后会作为函数上下文的变量对象来使用。换句话说,函数的变量对象也是一个同样简单的变量对象,但是除了变量和函数声明之外,它还存储了形参和arguments对象,并叫做活动对象。

       作用域链

       作用域链是一个对象列表,上下文代码中出现的标识符在这个列表中进行查找。如果一个变量在函数自身的作用域(在自身的变量/活动对象)中没有找到,那么将会查找它父函数(外层函数)的变量对象,以此类推。就上下文而言,标识符指的是:变量名称,函数声明,形参,等等。当一个函数在其代码中引用一个不是局部变量(或者局部函数或者一个形参)的标识符,那么这个标识符就叫做自由变量。搜索这些自由变量(free variables)就要用到作用域链。

        在通常情况下,作用域链式一个包含所有父(函数)变量对象加上(在作用域链头部的)函数自身变量/活动对象的一个列表。但是,这个作用域链也可以包含任何其他对象,如在上下文执行过程中动态加入到作用域链中的对象,像with对象或者特殊的catch从句对象。

        在代码执行过程中,作用域链可以通过使用with语句和catch从句对象来增强,并且由于这些对象是简单的对象,它们可以拥有原型(和原型链)。这个事实导致作用域链查找变为两个维度:(1)首先是作用域链连接,然后(2)在每个作用域链连接上深入作用域链连接的原型链(如果此连接拥有原型)。换句话说,在沿着作用域链查找之前,首先查找对象的原型链。

        闭包

        在ECMAScript中,函数是第一级(first-class)对象,意味着函数可以作为参数传递给其他函数(此时参数叫做[函数类型参数])。接收[函数类型参数]的函数叫做高阶函数。同样,函数也可以作为一个函数的返回值。

        为了在即使父函数上下文结束的情况下也能访问其中的变量,内部函数在被创建的时候会在它的[[scope]]属性中保存父函数的作用域链。所以当函数被调用的时候,它上下文的作用域链会被格式化成活动对象与[[scope]]属性的和。确切地说是在创建时刻函数会保存父函数的作用域链,这个保存下来的作用域链将会在未来的函数调用时用来查找变量。

        这个类型的作用域叫做静态(或者词法)作用域。

        ECMAScript完全支持闭包,技术上是通过函数的[[scope]]属性实现的。

        闭包是一个代码块(在ECMAScript是一个函数)和以静态方式/词法方式进行存储的所有父作用域的一个集合体。所以,通过这些存储的作用域,函数可以很容易的找到自由变量。

        由于每个(标准的)函数都在创建的时候保存了[[scope]],所以理论上来讲,ECMAScript中的所有函数都是闭包。

        多个函数可能拥有相同的父作用域(如两个内部/全局函数)。这种情况下,[[scope]]属性中存储的变量是在拥有相同父作用域链的所有函数之间共享的。一个闭包对变量进行的修改会体现在另一个闭包对这些变量的读取上。

       JavaScript是一门基于对象,事件驱动的语言。

       JavaScript是通过基于原型链的方式来实现继承的。基于原型链的继承,所有的对象有一个原型作为模板,这意味着如果一个对象具有原型,那么这个对象也就具有了原型的属性和方法。

       构造函数都有一个prototype属性指向它的父对象。默认情况下该属性为undefined。当将一个对象赋值给该prototype属性时,由该构造函数实例化出来的对象均具有与其原型类似的行为和结构。

       变量相对于简单属性来说,变量有一个特性(attribute):{DontDelete},这个特性的含义就是不能用delete操作符直接删除变量属性。

a = 10;
alert(window.a); // 10
alert(delete a); // true
alert(window.a); // undefined
 
var b = 20;
alert(window.b); // 20
alert(delete b); // false
alert(window.b); // still 20。b is variable,not property!

       可以通过以下常用的4种方法来创建一个对象

      (1)使用对象字面量

      (2)使用new与构造函数

      (3)使用new与Object对象

      (4)使用Object.create函数

        Object.create函数是ECMAScript 5中新增的,且主流的现代浏览器都已支持,但IE 8不支持。在Object.create函数中可以定义对象原型链上的上级对象,以及对象自身的属性、属性描述符。

       this关键字

       JavaScript中的this出现在函数作用域,且其值是通过函数的调用方式来决定的,即this是在函数调用时才被确定的而不是定义的时候。

       函数中的this指的是执行的上下文环境,因此必须在其调用时决定它的取值或指向。

       可以通过call和apply方法来修改函数的执行上下文,也就是this的指向;call和apply的区别在于给函数传递参数的方式。

       除了call与apply外,在ECMAScript 5中还引入了bind方法来修改函数执行的上下文

      this是一个与执行上下文相关的特殊对象。因此,它可以叫做上下文对象(也就是用来指明执行上下文是在哪个上下文中被触发的对象)。this是执行上下文的一个属性,而不是变量对象的一个属性。这个特性非常重要,因为与变量相反,this从不会参与到标识符解析过程。换句话说,在代码中当访问this的时候,它的值是直接从执行上下文中获取的,并不需要任何作用域链查找。this的值只在进入上下文的时候进行一次确定。

      在全局上下文中,this就等于全局对象本身(这意味着,这是的this等于变量对象);

      在函数上下文的情况下,对函数的每次调用,其中的this值可能是不同的。这个this值是通过函数调用表达式(也就是函数被调用的方式)的形式由caller所提供的。

      在一个函数上下文中,this由调用者提供,由调用函数的方式来决定。如果调用括号()的左边是引用类型的值,this将设为引用类型值的base对象(base object),在其他情况下(与引用类型不同的任何其它属性),这个值为null。不过,实际不存在this的值为null的情况,因为当this的值为null的时候,其值会被隐式转换为全局对象。

       当一个外部函数返回一个内部函数,而这个内部函数能够对外部函数中定义的变量进行引用,这种现象称为JavaScript中的闭包。

      ECMASCript 5中重点新特性:

     (1)添加JSON序列化的支持,JSON.parse()与JSON.stringify();

     (2)针对对象的创建方面的更新:如Object.create(),defineProperty,getOwnPropertyDescriptor等;

     (3)针对Function对象的更新:如bind;

     (4)针对数组对象的更新:如添加新的方法,包括:forEach,map,filter等;

     (5)严格模式: "use strict"。

       严格模式下不能再使用:

      (1)with;

      (2)没有八进制(以0开始的数字);

      原型模式:

function Construction(<params>) {
    //构造函数
}

Construction.prototype = {
    //原型链上的内容
}
var Module = function () {
    // 私有或共有变量
    // 私有或共有方法
};

Module.prototype = (function () {
    // private implementation
   
    return {
        // public API
    }
})();

      模块模式:

var Module = function () {
    //私有变量
    //私有方法

    return {
        //共有成员和方法
    }
}
var Module = (function () {
    //私有变量
    //私有方法

    return {
        //共有成员与方法
    }
})();

       怪异的JavaScript

var obj = {'': 100};
obj.['']; // 输出 100;

相关推荐