JavaScript是一种动态的、基于对象的浏览器端脚本语言,具有JAVA、C++静态语言所没有的灵活程度。JavaScript的写法变化多端,且各种写法带来的执行性能也会有所不同,所以在此进行总结。
JavaScript早期分为服务端和浏览器端两种脚本,服务器端已基本被jsp和asp两大阵营淘汰。现在JavaScript遵循ECMA-262标准(也称为ECMAScript3),ECMAScript4正在激烈讨论中。
JavaScript认可两类对象:原生(Native)对象和宿主(Host)对象,其中宿主对象包含一个被称为内置对象的原生对象的子类。原生对象属于语言,而宿主对象由环境提供,比如说可能是文档对象、DOM 等类似的对象。
具体来看,Javascript主要由二块构成:Core(核心部分)+DOM实现部分。(DOM是w3c定义的针对HTML、XML文档编程的一系列接口。通过这些接口,我们可以改变文档结点的结构、样式、内容。实现这些接口的语言可以是java、Python,javascript等。)
定义了语法规则,及内置的全局对象(Date、Function等),全局方法(parseFloat),全局属性(NaN,undefined等) |
针对HTML文档与XML文档操作的API |
面向对象语言包括几个主要特性:抽象,继承,封装和多态。Javascript本质上不是面向对象的,而是基于对象的。基于对象的语言对上面四个特性支持很弱。
JavaScript只有类,没有接口和抽象类;继承则通过prototype实现,Function的apply或call方法间接实现;封装访问特性仅有private和public两个级别;不支持多态的特性。但是,JavaScript通过闭包和函数式编程以及弱类型来克服这些缺点,使得其具有很大的灵活性。
JavaScript的变量分为全局变量(global variable)和局部变量(local variable)。局部变量的声明需要var,如果存在同名的全局变量而不用var,则解释器不会认为在声明局部变量。另外,一旦声明了局部变量以后,它将在整个作用域都有定义(无论位置在作用域的哪里),在此作用域内不会调用同名的全局变量。
每段JavaScript的代码都运行在与之对应的执行环境中,作用域(scope)是组成执行环境的一部分,它表示了某段代码所在的上下文环境(对象列表或对象链),通常它是以链表的形式存在,也称为作用域链。在JavaScript解释器启动的时候,它会创建一个全局对象(global object不属于任何函数的顶层),全局变量就是这个全局对象的一个属性,所有的预定义函数和属性也都是这个全局对象的属性。接着解释器会创建一个执行环境,这个执行环境包含了作用域链的定义;在这个环境下,解释器会为唤醒的function创建call object(也称为active object),并将它放置在作用域链的前面。call object持有该function的Arguments object的引用以及在此function里定义的Argument和局部变量的引用。
对于非嵌套的function来说,它的作用域链通常就是call object--global object,解释器会沿着作用域链查找变量的定义。
许多动态语言特别强调闭包的重要性。简言之,闭包就是函数的作用域,一个函数的主体(可执行的代码段)和它的定义作用域的组合称为闭包。闭包产生的原因是JavaScript允许创建内部函数,当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。也就是说,内部函数会在外部函数返回后被执行。而当这个内部函数执行时,它仍然必需访问其外部函数的局部变量、参数以及其他内部函数。这些局部变量、参数和函数声明(最初时)的值是外部函数返回时的值,但也会受到内部函数的影响。
闭包有两个特点:
2、一个闭包就是当一个函数返回时,一个没有释放资源的栈区。
这两个特点也就是一个意思:如果存在闭包,JavaScript解释器的GC不会销毁已返回的外部函数占用的堆栈(因为内部函数持有了外部函数中变量的引用)。这样便可以继续使用内部函数,即使内部函数持有外部函数中变量的引用。
内存泄露的根本原因是因为浏览器的GC机制不够完善,当JavaScript和DOM对象互相引用的时候,内存泄露就会发生。GC是由对象引用计数器来控制的,当解释器发现对象的引用计数为零的时候,GC就发生了。但是,当发生DOM和JavaScript循环引用的时候,他们的引用计数都不为零,所以GC也就无法进行。一个解决内存泄露的方法就是在循环对象不再使用的时候,将null赋给它,这样它们的引用都会为零。
另外,使用闭包也要注意,因为内部函数持有外部函数的引用,所以如果DOM对象引用了内部函数,而外部函数恰巧也引用这个DOM对象,内存泄露也会发生。参考内存泄露的类型会有更多信息。
每个JavaScript对象都有一个prototype(原型)属性,该属性引用另外一个对象,且可以编程赋予。每个原型对象有个constructor属性反过来引用函数本身。
|
原型属性也是一个对象,因此其本身也有prototype属性引用另一个对象,这样就可能形成一个原型链。这个原型链会终止于链中原型为 null 的对象,而Object 构造函数的默认原型正好有一个 null 原型。
更为重要的是,JavaScript解释器会自动顺着原型链查找属性。下图说明了在Dog中查找toString(),实际该方法在Object.prototype中。
|
将字符串作为javascript脚本执行
setTimeout用来推迟一个函数的执行,其特殊之处在于将所推迟的函数脱离现有的调用栈(call stack),例如:
类似的,浏览器还有个onkeyup事件也可以实现我们的需求
对Javascript调用堆栈和setTimeout用法的深入研究
本质上,匿名函数和直接函数量是同样的事物,匿名函数是无法被外部调用的。使用匿名函数的优势是可以直接进行值调用(函数式编程的特点),只将匿名函数作为中间过程而不用担心会与其他外部变量冲突。
1、在
2、
最后的括号中的x作为匿名函数的参数
用字符串做变量名可以使程序更灵活
可以利用.和[]]来引用成员
call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。
apply是与call类似的用于切换上下文的语句,与call不同的是apply能把参数包裹进一个数组再传递给呼叫函数。