Javascript之昨是今非

3,313 views
3,220 views

Published on

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,313
On SlideShare
0
From Embeds
0
Number of Embeds
13
Actions
Shares
0
Downloads
49
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Javascript之昨是今非

  1. 1. JavaScript 之昨是今非
  2. 2. Javascript 0 javascript 和 ECMAScript 的关系 1 闭包的作用域困扰和自执行函数 2 call 和 apply 重定义执行 Context 3 面向类继承的 js 4 动态构建和 eval 函数 5 ajax 并不神秘 6 js 事件和分离式脚本编程 7 ajax 跨域 8 reverse ajax 和服务器推技术 9 js 对象技术之背后的实现 --google 的 v8 js 引擎 10 js 的未来
  3. 3. 0 javascript 和 ECMAScript 的关系 (1) <ul><li>ECMA 是欧洲计算机制造商协会的缩写 </li></ul><ul><li>javascript 最初是由 netscape 公司创造,由其员工 Brendan eich 在 1995 年的时候写了一个叫 LiveScript ,当时由于 java 的风头正劲,所以正式发布前改名为 JavaScript 。最终 Netscape 公司赌赢了。 js 从此成为因特网必备组件 </li></ul><ul><li>当 Netscape 的浏览器取得成功后,微软进入了这个领域,并且也推出了一个叫 JScript 的脚本语言。同一时期还有其他公司推出不同版本的 js ,这些脚本语言并没有统一的语法和特性,为了解决浏览器不兼容性,在 1997 年网景公司将 JavaScript1.1 版本提交给 ECMA 的第 39 技术委员会( TC39 ),这个委员会被委派来“标准化一个通用的,跨平台的,中立于厂商的脚本语言的语法和语义”。 </li></ul><ul><li>关系: </li></ul><ul><li>尽管 ECMAScript 是一个重要的标准,但它并不是 JavaScript 的唯一部分,当然也不是唯一被标准化的部分,一个完整的 JavaScript 实现由 3 个部分组成。 </li></ul><ul><li>1 核心( ECMAscript ) </li></ul><ul><li>2 文档对象模型( DOM ) </li></ul><ul><li>3 浏览器对象模型( BOM ) </li></ul>
  4. 4. 0 javascript 和 ECMAScript 的关系 (2) <ul><li>BOM 是 IE3.0 和 Netscape Navigator 3.0 提供的一种特性。可以对浏览器窗口进行访问和操作,使用 BOM ,开发者可以移动窗口,改变状态栏中的文本以及其他和页面内容不直接相关的内容。使 BOM 独树一帜且又常常令人怀疑的地方,他只是 JavaScript 的一部分并没有相关的标准。 </li></ul><ul><li>BOM 主要处理浏览器窗口和框架( frame )包括 </li></ul><ul><li>1 弹出新的浏览器窗口 </li></ul><ul><li>2 移动、关闭浏览器详细以及调整窗口大小 </li></ul><ul><li>3 提供 web 浏览器中详细信息的导航对象 </li></ul><ul><li>4 提供装载到浏览器中页面的详细信息的定位对象 </li></ul><ul><li>5 对 cookie 的支持 </li></ul><ul><li>6 最后也是非常重要的是 IE 扩展了 BOM 加入了 ActiveXObject 类 , 可以通过 JavaScript 实例化 ActiveX 对象 正式这一特性后来成为 ajax 的点睛之笔,提供异步交互的可能。 </li></ul>
  5. 5. 1: 闭包的作用域困扰和自执行函数 (1) <ul><li>(1) 作用域解析和闭包。 </li></ul><ul><li>首先作用域是指对某一属性(变量)或方法(函数)具有访问权限的代码空间。在 js 中 </li></ul><ul><li>作用域是在函数中进行维护的。闭包是与作用域相关的一个概念,它指的是“内部函数即使在外部函数执行完毕并终止以后,仍然可以访问外部函数的属性“。当引用一个变量或方法时, js 会沿着由对象执行的路径构成的作用域链对作用域进行解析”。 </li></ul><ul><li>例子 1: </li></ul><ul><li>function initAnchors(){ </li></ul><ul><ul><li>for(var i=1;i<=3;i++){ </li></ul></ul><ul><ul><ul><li>var anchor=document.getElementById(”anchor“+i); </li></ul></ul></ul><ul><ul><ul><li>anchor.addEventListener('click',function(){ </li></ul></ul></ul><ul><ul><ul><ul><li>alert(&quot;My id is anchor&quot;+i); </li></ul></ul></ul></ul><ul><ul><ul><li>}); </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>} </li></ul><ul><li>单击该 dom 对象 onclick 事件时, i 都会是 3 而不是期望的分别为 1,2,3, </li></ul>
  6. 6. 1: 闭包的作用域困扰和自执行函数 (2) <ul><li>具体来说,当点击 click 事件侦听器被调用并在它的内部作用域中查找 i 的值时,结果没有找到(因为 I 的值没有在匿名函数中定义) </li></ul><ul><li>解决:可以用一个方法包装事件注册,用一个方法来激活一个作用域。 </li></ul><ul><li>function registerListener(anchor,i){ </li></ul><ul><ul><li>anchohr.addListener('click',function()){ </li></ul></ul><ul><ul><ul><li>alert('My id is anchor'+i); </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>} </li></ul><ul><li>这样原来的代码改写为 </li></ul><ul><li>function initAnchors(w3cEvent){ </li></ul><ul><ul><li>for(var i=1;i<=3;i++){ </li></ul></ul><ul><ul><ul><li>var anchor=document.getElementById(”anchor“+i); </li></ul></ul></ul><ul><ul><ul><li>registerListener(anchor,i); </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>} </li></ul>
  7. 7. 1: 闭包的作用域困扰和自执行函数 (3) <ul><li>2 自执行函数。 </li></ul><ul><li>原型: (function(){})(); </li></ul><ul><li>(function(){})() , (function(){});// 强制其理解为函数,“函数 ()” 表示执行该函数,即声明后立即执行。 </li></ul><ul><li>function initAnchors(){ </li></ul><ul><ul><li>for(var i=1;i<=3;i++){ </li></ul></ul><ul><ul><ul><li>var anchor=document.getElementById('anchor'+i); </li></ul></ul></ul><ul><ul><ul><li>,(function(anchor,i){ </li></ul></ul></ul><ul><ul><ul><ul><li>anchor.addListener('click',function(){ </li></ul></ul></ul></ul><ul><ul><ul><ul><ul><li>alert('My id is anchor'+i); </li></ul></ul></ul></ul></ul><ul><ul><ul><ul><li>}) ; </li></ul></ul></ul></ul><ul><ul><ul><li>})(anchor,i); </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>} </li></ul>
  8. 8. 2:call 和 apply 重定义执行 Context(1) <ul><li>在函数的执行环境改变后怎么重新定义 Context </li></ul><ul><li>function doubleCheck(){ </li></ul><ul><ul><li>this.message=&quot;ar you sure you want to leave?&quot;; </li></ul></ul><ul><li>} </li></ul><ul><li>docubleCheck.prototype.sayGoodbye=function(){ </li></ul><ul><ul><li>return confirm(this.message); </li></ul></ul><ul><li>} </li></ul><ul><li>initPage(){ </li></ul><ul><ul><li>var clickLink=new doubleCheck(); </li></ul></ul><ul><ul><li>var links=document.getElementsByTagName('a'); </li></ul></ul><ul><ul><li>for(var i=0;i<links.length;i++){ </li></ul></ul><ul><ul><ul><li>links[i].addListener('click',clickedLink.sayGoodbye); </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>} </li></ul>
  9. 9. 2:call 和 apply 重定义执行 Context(2) <ul><li>这个例子中绑定了 clickedLink 对象 sayGoodbye 方法, 但是在实际执行时该方法的 this 对象指向了当前的 dom 元素对象,而不是我们创建的 clickedLink 对象。 </li></ul><ul><li>怎么解决这个问题呢?这就要用到两个 Function 对象的 call 和 apply 方法了。 </li></ul><ul><li>通过这两个方法可以重新定义方法的执行环境, </li></ul><ul><li>调用语法: </li></ul><ul><ul><li>functionReference.sayGoodbye.call(object,argment1,argments2,...); </li></ul></ul><ul><ul><li>functionReference.sayGoodbye.apply(object,argments); </li></ul></ul><ul><ul><li>apply 方法可以利用 Argments 对象(注: Argments 对象是 js 的特殊对象,具有部分数组的功能 </li></ul></ul><ul><ul><li>例如可以通过下标访问语法,去访问各个参数,但是它不具备一些数组的方法,比如 push(), </li></ul></ul><ul><ul><li>concat(),slice() 等等); </li></ul></ul><ul><ul><li>当为了调整方法的执行环境而生成匿名包装函数时, apply() 方法特别有用,可以通过联合闭包功能。 </li></ul></ul><ul><ul><li>例子: </li></ul></ul><ul><ul><li>function bindFunction(obj,func){ </li></ul></ul><ul><ul><ul><li>reutrn function(){func.apply(obj,argmengs);} </li></ul></ul></ul><ul><ul><li>} </li></ul></ul>
  10. 10. 三、面向类继承的 js(1) <ul><li>1:“ 对 js 来说面向对象这个词有些多余,因为 js 这么语言是完全面向对象的,也不可能以非面向对象的方法来使用。” -----John Resig 。 </li></ul><ul><li>大多数编程新手的常见弱点在于按照功能编写代码,而不考虑任何上下文或者结构。 </li></ul><ul><li>对象是 js 的基础(因为 dom 就是 js 一种对象)但是 js 的对象不同样传统语言里的对象,比如 java 和 c++ c# 等, js 的对象更像是散列表。它具有多重性 . </li></ul><ul><li>例子: </li></ul><ul><li>var obj=new Object; </li></ul><ul><li>obj.val=5; </li></ul><ul><li>obj.click=funciton(){alert(&quot;hello&quot;);}; </li></ul><ul><li>// 下面一段 json 方式定义 </li></ul><ul><li>var obj={ </li></ul><ul><li>val:5; </li></ul><ul><li>click:function(){alert(&quot;hello&quot;);} </li></ul><ul><li>} </li></ul><ul><li>一:原型式继承 </li></ul><ul><li>原型式继承不同传统的类 / 对象继承,和大部分的其他面向对象语言不同的是, js </li></ul>
  11. 11. 三、面向类继承的 js(2) <ul><li>并没有类( class )的概念。其他面向对象语言中大多需要实例化具体类实例,但是 js 中不用, js 里对象本身可以用来创建新对象,而对象也可以继承自其他对象。这个概念称之为“原型化继承”。 </li></ul><ul><li>不过 js 使用何种对象方案,首先还是应该有一种创建新对象的方法。 js 的做法是任何函数都可以被实例化一个对象。 </li></ul><ul><li>例子: </li></ul><ul><li>function User(){} </li></ul><ul><li>var me=new User(); </li></ul><ul><li>var you=new me.constructor(); </li></ul><ul><li>alert(me.constructor=you.constructor); </li></ul><ul><li>constructor 这个属性的使用,它在每个对象中都存在,并一直指向创建它的函数,这样一来可以有效的复制对象了。用同一个基类创建并赋予不同的属性。 </li></ul><ul><li>2 :创建可重用的代码 </li></ul><ul><li>例子: </li></ul>
  12. 12. 三、面向类继承的 js(3) <ul><li>function Person(name){ </li></ul><ul><ul><li>this.name=name; </li></ul></ul><ul><li>} </li></ul><ul><li>// 给 Person 对象添加一个新方法 </li></ul><ul><li>Person.prototype.getName=function(){ </li></ul><ul><li>return this.name; </li></ul><ul><li>}; </li></ul><ul><li>// 创建一个新的 User 对象的构造函数 </li></ul><ul><li>function User(name,password){ </li></ul><ul><li>this.name=name; </li></ul><ul><li>this.password=password; </li></ul><ul><li>}; </li></ul><ul><li>//User 对象继承所有 Person 对象的方法 </li></ul><ul><li>User.prototype=new Person(); </li></ul><ul><li>User.prototype.getPassword=function(){return this.password;}; </li></ul>
  13. 13. 三、面向类继承的 js(4) <ul><li>类式继承对于大部分开发者来说都已经熟悉,只要有了带方法的类就可以把它实例化为对象。 </li></ul><ul><li>先看一段代码 </li></ul><ul><ul><li>Function.prototype.method = function (name, func) { </li></ul></ul><ul><ul><li>this.prototype[name] = func; </li></ul></ul><ul><ul><li>return this; </li></ul></ul><ul><ul><li>}; </li></ul></ul><ul><ul><li>这一段代码扩展了 Function 类,动态绑定新函数到对象的 prototype 上,这段函数还是比较好理解的。它把函数和构造函数的原型关联起来,之所以有效,是因为所有的构造函数本身都是函数, </li></ul></ul><ul><ul><li>所以能获得” method“ 这个新方法。 </li></ul></ul><ul><ul><li>但是下面这个函数就相当复杂,它实现了子类继承父类时,当子类 override 父类方法时 </li></ul></ul><ul><ul><li>用类似 super 的方式访问父类被覆盖的方法,这是 js 大师 Douglas Crockford 的作品 值得我们再三拜读。 O(∩_∩)O~ </li></ul></ul>
  14. 14. 三、面向类继承的 js(5) <ul><li>Function.method('inherits', function (parent) { </li></ul><ul><li>var depth=0; </li></ul><ul><li>var p = (this.prototype = new parent()); </li></ul><ul><li>this.method('uber', function uber(name) { </li></ul><ul><li>var f, r, t = depth, v = parent.prototype; </li></ul><ul><li>if (t) { </li></ul><ul><li> for(t=depth;t>0; t -= 1){ </li></ul><ul><li> v = v.constructor.prototype; </li></ul><ul><li> } </li></ul><ul><li>f = v[name]; </li></ul><ul><li>} else { </li></ul><ul><li>f = p[name]; </li></ul><ul><li>if (f == this[name]) { </li></ul><ul><li>f = v[name]; </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>depth += 1; </li></ul><ul><li>r = f.apply(this, Array.prototype.slice.apply(arguments, [1])); </li></ul><ul><li>depth -= 1; </li></ul><ul><li>return r; </li></ul><ul><li>}); </li></ul><ul><li>return this; </li></ul><ul><li>}); </li></ul>
  15. 15. 三、面向类继承的 js(6) <ul><li>这个函数定义了一个继承的方法,同时也定义了一个 uber 方法,该方法可以让子类访问到父类的被 override 的方法。 </li></ul><ul><li>depth 定义对象继承链的深度,如果 uber 函数内还存在 uber 函数的话,就会递归调用 uber 的闭包函数,注意此时 this 上下文是相同的,使用 depth 来定位 prototype 链回溯的深度。 </li></ul><ul><li>Function.method('swiss', function (parent) { </li></ul><ul><li>for (var i = 1; i < arguments.length; i += 1) { </li></ul><ul><li>var name = arguments[i]; </li></ul><ul><li>this.prototype[name] = parent.prototype[name]; </li></ul><ul><li>} </li></ul><ul><li>return this; </li></ul><ul><li>}); </li></ul>
  16. 16. 三、面向类继承的 js(7) <ul><li>swiss 这个 method 函数的增强版本,可以用于单一父对象获取多个函数,如果用在多个父对象上就能获得可用的多对象继承。 </li></ul><ul><li>当我们了解我这三个函数后,但是当实际使用时还是使用真正最流行的面向对象的框架 </li></ul><ul><li>Base.js 和 Base2.js 现今几乎所有的主流 js 框架在面向对象部分都是采取这个框架的思想, </li></ul><ul><li>包括我们熟悉的 ProtoType,Ext JS JQuery 等等。 </li></ul><ul><li>使用的例子: </li></ul><ul><li>// 创建一个 Person 类 </li></ul><ul><li>var Person=Base.extend({ </li></ul><ul><li>//Person 构造函数 </li></ul><ul><li>constructor:function(name){ </li></ul><ul><li>this.name=name} </li></ul><ul><li>getName:function(){ </li></ul><ul><li>return this.name; </li></ul><ul><li>} </li></ul><ul><li>}); </li></ul>
  17. 17. 三、面向类继承的 js(8) <ul><li>// 创建新的继承自 person 类的 user </li></ul><ul><li>var User=Base.extend({ </li></ul><ul><ul><li>constructor:function(name,password){ </li></ul></ul><ul><ul><ul><li>this.base(name); </li></ul></ul></ul><ul><ul><ul><li>this.password=password; </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><ul><li>getPassword:function(){ </li></ul></ul><ul><ul><ul><li>return this.password; </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>}); </li></ul><ul><li>this.base(name); 访问父类的构造方法, </li></ul><ul><li>extend 函数的两种版本 一种色 Base 直接调用 创建基本构造函数对象,而另外一种方式是扩展一个已有的对象,并赋值给一个新的对象。 </li></ul>
  18. 18. 四:动态解析和 eval 函数 <ul><li>脚本的动态解析: </li></ul><ul><li>Js 的 eval 函数是一个神奇的东西,它可以将字符串作为脚本来执行,这样,为我们设计和实现动态解析器提供了可能。 </li></ul><ul><li>最简单的解析器是按照 js 的语法规则原样解释代码,例如: </li></ul><ul><li>eval(&quot;alert('hello world!');&quot;);// 相当于直接执行 alert(&quot;hello world&quot;); </li></ul><ul><li>然而,除了原样解释代码之外,我们可以通过技巧拼接字符串生成要执行的脚本,例如: </li></ul><ul><li>var name=&quot;akira&quot;; </li></ul><ul><li>eval(&quot;alert('Hello '+name+'!')&quot;); </li></ul><ul><li>注意到上面的例子通过闭包也能够实现,然而用动态解析的方式更加灵活和方便(但是用闭包的方式执行效率更高)。下面是用闭包来实现的对比例子: </li></ul><ul><li>var foo=(function(name){ </li></ul><ul><ul><li>return function(){alert(&quot;Hello &quot;+name+&quot;!&quot;)} </li></ul></ul><ul><li>})(&quot;akira&quot;); </li></ul><ul><li>下面再来看它令人迷惑的一面: </li></ul>
  19. 19. 四:动态构建和 eval 函数 (1) <ul><li>var s='function test(){return 1;}'; // 一个函数定义语句 </li></ul><ul><li>function demo(){ </li></ul><ul><li>eval(s); </li></ul><ul><li>} </li></ul><ul><li>demo(); </li></ul><ul><li>alert(test()); //->error:test is not defined </li></ul><ul><li>这是因为 test 函数在局部空间定义, demo2 函数内可以访问到,外面( alert )就访问不到了。 </li></ul><ul><li>将 var s='function test(){return 1;}'; 这句改为 </li></ul><ul><li>var s=' var test=function(){return 1;}'; 这样的变量型的定义 </li></ul><ul><li>方式 结果是一样的 因为 test 执行环境在函数 demo 中,所以 外面也访问到 </li></ul><ul><li>我们将 其改为 var s=' test=function(){return 1;}'; 就可以访问了 </li></ul><ul><li>因为 test 是全局范围内的变量(因为前面没有 var )。 </li></ul>
  20. 20. 四:动态构建和 eval 函数 (2) <ul><li>var X2={} // 定义一个对像 </li></ul><ul><li>X2.Eval=function(code){ </li></ul><ul><li>if(window.execScript){ </li></ul><ul><li>//ie </li></ul><ul><li>execScript(code); </li></ul><ul><li>}else{ </li></ul><ul><li>//not ie </li></ul><ul><li>window.eval(code); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>注意 eval 方法其实是有调用者环境的,如果前面加上 window 对象调用,它的执行上 </li></ul><ul><li>下文是可以重新定义的。 </li></ul><ul><li>在各种 js 框架中 一般都只在动态转化 json 对象时使用 eval ,在动态执行脚本的时候 </li></ul><ul><li>一般使用 ScriptTag 技术,也就是创建一个 script 标签。然后追加其内容,在下面摘取 </li></ul><ul><li>的 jquery 的源代码 globalEval 方法中即是使用了这种方法。 </li></ul>
  21. 21. 四:动态构建和 eval 函数( 3 ) <ul><li>// Evalulates a script in a global context </li></ul><ul><li>globalEval: function( data ) { </li></ul><ul><ul><li>data = jQuery.trim( data ); </li></ul></ul><ul><ul><li>if ( data ) { </li></ul></ul><ul><ul><li>// Inspired by code by Andrea Giammarchi </li></ul></ul><ul><ul><li>// http://webreflection.blogspot.com/2007/08/global-scope-evaluation-dom.html </li></ul></ul><ul><ul><li>var head = document.getElementsByTagName(&quot;head&quot;)[0] || document.documentElement, </li></ul></ul><ul><ul><li>if ( jQuery.browser.msie ) </li></ul></ul><ul><ul><ul><li>script.text = data; </li></ul></ul></ul><ul><ul><ul><li>else </li></ul></ul></ul><ul><ul><ul><li>// Use insertBefore instead of appendChild to circumvent an IE6 bug. </li></ul></ul></ul><ul><ul><ul><li>// This arises when a base node is used (#2709). </li></ul></ul></ul><ul><ul><ul><li>head.insertBefore( script, head.firstChild ); </li></ul></ul></ul><ul><ul><ul><li>} </li></ul></ul></ul><ul><ul><ul><li>}, </li></ul></ul></ul><ul><ul><ul><li>head.removeChild( script ); </li></ul></ul></ul><ul><ul><ul><li>script.appendChild( document.createTextNode( data ) ); </li></ul></ul></ul><ul><ul><li>script = document.createElement(&quot;script&quot;); </li></ul></ul><ul><ul><li>script.type = &quot;text/javascript&quot;; </li></ul></ul>
  22. 22. 四:动态构建和 eval 函数( 4 ) <ul><li>这段代码摘自 jquery1.4.2 源码。其中不但能找到动态执行的信息而且还可以觉察出跨域执行的影子。 </li></ul>
  23. 23. ajax 并不神秘 (1) <ul><li>ajax 并不是一种新的技术,它只是几种技术的综合应用。它包括: </li></ul><ul><li>1 : HTML 和 Css 样式表 </li></ul><ul><li>2 : Dynamic HTML </li></ul><ul><li>3 :文档对象模型 DOM </li></ul><ul><li>4 : JavaScript </li></ul><ul><li>还有最最重要画龙点睛之笔的 XMLHttpRequest 对象,没有它其他那些技术并不会被激活,不会得到主流开发世界的接纳, js 仍然被认为是玩具语言。 </li></ul><ul><li>就是它提供的异步交互的能力才使纯浏览器环境无刷新访问成为可能, ajax 技术在整个 RIA 解决方案中式最有前途的方案,并随着 HTML5 和 ECMAScript4 的到来 更加重要。基于插件的 flex , slivelight , javafx 等都是 RIA 重要的解决方案而存在,但是 ajax 是最轻量级的。 </li></ul><ul><li>对于 ajax 的原理不深入分析,可以分成如下的几步: </li></ul><ul><li>1 : 通过 new XMLHttpRequest 或其它的形式(指 IE )生成 ajax 的 xhr </li></ul>
  24. 24. ajax 并不神秘 (2) <ul><li>2 通过 xhr.open(type,url,async,username,password) 的形式建立一个连接 </li></ul><ul><li>3 通过 setRequestHeader 设定 xhr 的请求头部 </li></ul><ul><li>4 通过 send(data) 向服务器发送数据 </li></ul><ul><li>5 执行在 xhr 注册的 onreadystatechange 回调处理返回数据。 </li></ul><ul><li>可以说所有的 js 框架在 ajax 这部分的处理都是围绕这个步骤进行包装,在这几步中 url 涉及到跨域问题(将会在下面的 ajax 跨域一节进行说明) . </li></ul><ul><li>对于 ajax 的应用,不管对返回的数据进行如何的处理,其最终的目的还是要落实到页面的显示上,也就是某一个或一批 dom 上,返回的数据可以是 json 字符串 或者 xml 文本, ajax 分别有处理这两方面的方式,其中 json 可以用 eval 方法去执行或者用框架 JSON2 来解析成为 js 对象。因为 eval 的性能稍差。推荐使用框架方式的解析。 </li></ul><ul><li>下面列出一段 jquery 的 ajax 部分的源代码代码来说明,一个成熟的 ajax 框架是怎么样来包装和处理这个细节的,由于 ajax 的 xhr 具有平台实现的差异性,需要编写跨平台的代码需要耐心和高超的水平。所以我们只需了解其原理然后用的时候呢 就直接拿来主义吧! </li></ul>
  25. 25. ajax 并不神秘 (3) <ul><li>// 通过 get 的 type 方式进行 ajax 的请求 </li></ul><ul><li>get : function(url, data, callback, type) { </li></ul><ul><li>// 前移 arguments 如 data 参数省略 </li></ul><ul><li>if (jQuery.isFunction(data)) { </li></ul><ul><li>callback = data; </li></ul><ul><li>data = null; </li></ul><ul><li>} </li></ul><ul><li>return jQuery.ajax( { </li></ul><ul><li>type : &quot;GET&quot;, </li></ul><ul><li>url : url, </li></ul><ul><li>data : data, </li></ul><ul><li>success : callback, </li></ul><ul><li>dataType : type </li></ul><ul><li>}); </li></ul><ul><li>}, </li></ul><ul><li>// 取得返回的 script </li></ul><ul><li>getScript : function(url, callback) { </li></ul><ul><li>return jQuery.get(url, null, callback, &quot;script&quot;); </li></ul><ul><li>}, </li></ul><ul><li>// 取得 json </li></ul><ul><li>getJSON : function(url, data, callback) { </li></ul><ul><li>return jQuery.get(url, data, callback, &quot;json&quot;); </li></ul><ul><li>}, </li></ul>
  26. 26. ajax 并不神秘 (4) <ul><li>这是 jquery get 方法的部分,发现最终还是调用最底层的方法 ajax 方法,注意在一般用户不需要同步执行的时候,可以使用 load (), get (), post () 三个方法, </li></ul><ul><li>如果需要同步执行需要直接调用 ajax 方法 设定 async:false 。 </li></ul><ul><li>下面看看 post 请求的源码: </li></ul><ul><li>// 以 post 方式进行 ajax 请求 </li></ul><ul><li>post : function(url, data, callback, type) { </li></ul><ul><li>if (jQuery.isFunction(data)) { </li></ul><ul><li>callback = data; </li></ul><ul><li>data = {}; </li></ul><ul><li>} </li></ul><ul><li>return jQuery.ajax( { </li></ul><ul><li>type : &quot;POST&quot;, </li></ul><ul><li>url : url, </li></ul><ul><li>data : data, </li></ul><ul><li>success : callback, </li></ul><ul><li>dataType : type </li></ul><ul><li>}); </li></ul><ul><li>}, </li></ul>
  27. 27. ajax 并不神秘 (5) <ul><li>post 和 get 方法都会去调用底层的方法 ajax 区别在于如果 data 参数是函数的话的说明用户向服务器发出的请求不需要传递数据,所有 data=null; 而 post 是需要这个步骤的 </li></ul><ul><li>所有的前期的铺垫都已经完成了, get , post , getScript 和 getJSON 的方法都去调用最终的 ajax ()方法,在分析它之前,我们先看一下默认的 setting 对象 </li></ul><ul><li>// 默认的 ajax 的请求参数 </li></ul><ul><li>ajaxSettings : { </li></ul><ul><li>url : location.href,// 默认是地址栏中 url </li></ul><ul><li>global : true,// 默认支持全局的 ajax 事件 </li></ul><ul><li>type : &quot;GET&quot;, </li></ul><ul><li>timeout : 0, </li></ul><ul><li>contentType : &quot;application/x-www-form-urlencoded&quot;,//data 的内容的形式 </li></ul><ul><li>processData : true, </li></ul><ul><li>async : true,// 注意这个值 这是同步还是异步的开关 </li></ul><ul><li>data : {name}, </li></ul><ul><li>username : null, </li></ul><ul><li>password : null, </li></ul><ul><li>accepts : { </li></ul><ul><li>xml : &quot;application/xml, text/xml&quot;, </li></ul><ul><li>html : &quot;text/html&quot;, </li></ul><ul><li>script : &quot;text/javascript, application/javascript&quot;, </li></ul><ul><li>json : &quot;application/json, text/javascript&quot;, </li></ul><ul><li>text : &quot;text/plain&quot;, </li></ul><ul><li>_default : &quot;*/*&quot; </li></ul><ul><li>} </li></ul><ul><li>}, </li></ul>
  28. 28. Ajax 跨域问题探讨 (1) <ul><li>首先需要说明的一点是 ajax 不支持跨域,也就是其实跨域访问的问题跟 ajax 没有直接的关系。 </li></ul><ul><li>为什么这么说呢,其实这些都是浏览器的同源策略,为了安全性而让脚本只能读取与它位于同一个域和同一个端口的窗口或文档的属性。 </li></ul><ul><li>从 ajax 诞生那天起, xmlHttprequest 对象在 firefox 下不能跨域请求的问题就一直存在,等待浏览器们去解决这个问题显然不太现实,聪明的 web 开发人员们早就想了一系列的方法来解决这个问题,下面列举两个比较不错的方法: </li></ul><ul><li>1. 使用中间层过渡的方式:中间过渡,很明显,就是在 ajax 与不同域的服务器进行通讯的中间加一层过渡,这一层过渡可以是 php 、 jsp 、 c++ 等任何具备网络通讯功能的语言,由中间层向不同域的服务器进行读取数据的操作。拿 php 做一个例子,如果需要对不同域的某一个 php 进行通讯,现在客户端的 xmlhttprequest 先 query 本域的一个 php ,然后由本域的这个 php 去和不同域的 php 进行通讯,然后由本域的 php 输出 response ; </li></ul><ul><li>2 : 2. 使用 <script> 标签这个方法是利用 <script> 标签中的 src 来 query 一个 php 获得 response ,因为 <script> 标签的 src 属性不存在跨域的问题。举个例子来让大家看得更清楚一点吧: </li></ul><ul><li><script LANGUAGE=&quot;Javascript&quot; src=&quot;&quot; id=&quot;get&quot;> </li></ul><ul><li></script> </li></ul><ul><li><script LANGUAGE=&quot;Javascript&quot;> </li></ul>
  29. 29. Ajax 跨域问题探讨 (2) <ul><li><!-- </li></ul><ul><li>function get(url) </li></ul><ul><li>    { </li></ul><ul><li>        var obj = document.getElementById(&quot;get&quot;); </li></ul><ul><li>        obj.src = url; </li></ul><ul><li>        (obj.readStatus == 200) </li></ul><ul><li>        { </li></ul><ul><li>            alert(param); </li></ul><ul><li>        } </li></ul><ul><li>    } </li></ul><ul><li>function query() </li></ul><ul><li>    { </li></ul><ul><li>get(get.php); </li></ul><ul><li>    } </li></ul><ul><li>//--> </li></ul><ul><li></script> </li></ul><ul><li><BODY> </li></ul><ul><li><INPUT TYPE=&quot;button&quot; value=&quot;CLICK ME&quot; onclick=&quot;query()&quot;> </li></ul><ul><li></BODY> </li></ul><ul><li></HTML> </li></ul><ul><li>其中 get.php 的代码是 </li></ul><ul><li><?php </li></ul><ul><li>echo &quot;var param = 'www.achome.cn'&quot;; </li></ul><ul><li>?> </li></ul>
  30. 30. Ajax 跨域问题探讨 (3) <ul><li>最后的运行结果是,当你点击那个 button ,它会出现一个内容为” www.achome.cn” 的对话框。这个方法又叫做 ajaj 或者 ajax without xmlHttprequest ,把 x 换成了 j ,是因为使用了 <script> 标签而没有用到 xml 和 xmlHttprequest 的缘故。 </li></ul><ul><li>怎么样,很简单吧,我看到过很多人不愿意去正视 ajax 所存在的技术瓶颈,其实 ajax 更应该是 Ajax 而不是 AJAX ,突出第一个 A 是想强调其实 ajax 发扬的是一种异步传输的方法,而不是具体到底使用了哪种技术。 </li></ul><ul><li>说了这些我们再来看看成熟的 js 框架怎么样来解决跨域问题,其实在前面我们也说明了 </li></ul><ul><li>那就 jquery 得源码中的 ajax 方法内已经内置了跨域的支持 我们来分析一下: </li></ul><ul><li>if (s.dataType == &quot;script&quot;&& type == &quot;GET&quot;&& parts </li></ul><ul><li>&& (parts[1] && parts[1] != location.protocol || parts[2] != location.host)) { </li></ul><ul><li>// 在 head 中加上 <script src=&quot;&quot;></script> </li></ul><ul><li>var head = document.getElementsByTagName(&quot;head&quot;)[0]; </li></ul><ul><li>var script = document.createElement(&quot;script&quot;); </li></ul><ul><li>script.src = s.url; </li></ul><ul><li>if (s.scriptCharset) </li></ul><ul><li>script.charset = s.scriptCharset; </li></ul>
  31. 31. Ajax 跨域问题探讨 (4) <ul><li>// 如果 datatype 不是 jsonp ,但是 url 却是跨域的。采用 scriptr 的 </li></ul><ul><li>//onload 或 onreadystatechange 事件来触发回调函数。 </li></ul><ul><li>if (!jsonp) { </li></ul><ul><li>var done = false; </li></ul><ul><li>// 对所有浏览器都加上处理器 </li></ul><ul><li>script.onload = script.onreadystatechange = function() { </li></ul><ul><li>if (!done&& (!this.readyState || this.readyState == &quot;loaded&quot; || this.readyState == &quot;complete&quot;)) { </li></ul><ul><li>done = true; </li></ul><ul><li>success(); </li></ul><ul><li>complete(); </li></ul><ul><li>head.removeChild(script); </li></ul><ul><li>} </li></ul><ul><li>}; </li></ul><ul><li>} </li></ul><ul><li>head.appendChild(script); </li></ul><ul><li>// 已经使用了 script 元素注射来处理所有的事情 </li></ul><ul><li>return undefined; </li></ul><ul><li>} </li></ul>
  32. 32. Ajax 跨域问题探讨 (5) <ul><li>我们可以看出 jquery 使用的就是 scriptTag 的方式来完成的。先在页面的 <head> 中添加一个 <script src=url/> 的标签。 因为在 <head> 中,浏览器会自动载入并运行请求返回的 script. </li></ul><ul><li>我们没有讲到 jsonp 的方式 而只说了 dataType==&quot;script&quot; 形式的跨域,所以我通过 script.onload 或者 script.onreadystatechange 事件来触发回调。这里我们可以 script 代码: var data=xxx 来传递参数给 s 在 setting 里设置的回调方法 . </li></ul><ul><li>下面作为结尾给出两个方法的代码: </li></ul><ul><ul><ul><li>function success() { </li></ul></ul></ul><ul><li>// 调用构建请求对象时指定的 success 回调。 </li></ul><ul><li>if (s.success) </li></ul><ul><li>s.success(data, status); </li></ul><ul><li>// 执行全局的回调 </li></ul><ul><li>if (s.global) </li></ul><ul><li>jQuery.event.trigger(&quot;ajaxSuccess&quot;, [xhr, s]); </li></ul><ul><li>} </li></ul>
  33. 33. Ajax 跨域问题探讨 (6) <ul><li>function complete() { </li></ul><ul><li>// 本地的回调 </li></ul><ul><li>if (s.complete) </li></ul><ul><li>s.complete(xhr, status); </li></ul><ul><li>// 执行全局的回调 </li></ul><ul><li>if (s.global) </li></ul><ul><li>jQuery.event.trigger(&quot;ajaxComplete&quot;, [xhr, s]); </li></ul><ul><li>// 全局的 ajax 计数器 </li></ul><ul><li>if (s.global && !--jQuery.active) </li></ul><ul><li>jQuery.event.trigger(&quot;ajaxStop&quot;); </li></ul><ul><li>} </li></ul>
  34. 34. reverse ajax 和服务器推技术 (1) <ul><li>反向 ajax 的概念是:客户端不必重服务器获取信息,服务器会把相关的信息直接推送到客户端。这样做的目的是为了解决传统 web 模型所带来的一个限制:实时信息很难从技术上解决。原因是客户端必须联系服务器,主动询问是否存在变更,如果有变更就会刷心页面。虽然可以非常快速完成这个操作。让人感觉好像是实时的。但是实际上不是实时的,我们需要的是服务器联系查看其页面的所有浏览器,并通告所发生的变更。 </li></ul>
  35. 35. reverse ajax 和服务器推技术 (2) <ul><li>就像 ajax 技术一样, reverse ajax 也不是一门专门的技术,而是按照不寻常方式组合使用已有的技术达到不同的效果。 </li></ul><ul><li>众所周知,传统的 web 模式(也就是非 ajax 方式) </li></ul><ul><li>有一系列鲜明的事件。简单来说用户在客户端的一个动作会导致向服务器发出一个请求 (request) ,然后服务器按照请求进行相应的处理,并把处理结果作为响应 (response) 传回客户端,这个响应通常是一个完整的新的 ui 视图。这种处理过程反复循环,直到用户决定离开当前的 web 站点。(上图左) </li></ul><ul><li>基于 ajax 的应用程序的事件序列稍微有所差别,如上右图,某个用户动作会导致对某个客户端 ajax 引擎的调用,不论它是 js 代码,还是其他库。这个引擎会向服务器发出一个请求,服务器按照非 ajax 模式进行处理,然后返回响应。注意响应内容首先被 ajax 引擎处理,然后调用某些客户端代码更新页面。 </li></ul><ul><li>而 reverse ajax 表示另外一种事件序列 ( 如右图 ) </li></ul>
  36. 36. reverse ajax 和服务器推技术 (3) <ul><li>其中有一个双向的信息流,即通常的客户端发起的信息流和新的服务器发起的信息流。 </li></ul><ul><li>其实在 http 技术的限制下,根本找不到真正的把信息从服务器推送的到客户端的方式,因为这个协议时无状态的。并且客户端发起的链接一旦断开,服务器就不可能了解客户端,不管到底哪一方发起这个链接,都不存在从客户端到服务器的“持续”链接。 </li></ul><ul><li>不过,虽然没有真正的推送技术,但是可以完美的模拟它! </li></ul><ul><li>reverse ajax 必须同时由客户端和服务器端做出努力才能达到;到目前为止有几种框架很好的实现了反向 ajax 。 </li></ul><ul><li>DWR/ dojo/flash XMLSocket/pushlet/ </li></ul><ul><li>DWR 对反向 ajax 支持 dwr 支持三种模式 1: 轮询, 2 : comet , 3 : piggybacking </li></ul><ul><li>dojo 和 jetty6/tomcat6 联合使用,支持 comet </li></ul><ul><li>pushlet 实现自身的 comet </li></ul><ul><li>flash XMLSocket 是另外一种实现 </li></ul><ul><li># 因为 XMLSocket 没有 HTTP 隧道功能, XMLSocket 类不能自动穿过防火墙; </li></ul><ul><li># 因为是使用套接口,需要设置一个通信端口,防火墙、代理服务器也可能对非 HTTP 通道端口进行限制; </li></ul>
  37. 37. reverse ajax 和服务器推技术 (4) <ul><li>在 Web 早期,这一点常使用 meta 刷新实现。这将自动指示浏览器在指定秒数之后重新装载页面,从而支持简陋的轮询( polling )。这不仅是一种糟糕的用户体验,而且通常效率非常低下。如果没有新的数据要显示在页面上呢?这时不得不重新呈现同样的页面。如果对页面的更改很少,并且页面的大部分没有变化呢?同样,不管是否有必要,都得重新请求和获取页面上的一切内容。 </li></ul><ul><li>Ajax 的发明和流行改变了上述状况。现在,服务器可以异步通信,因此不必重新请求整个页面。现在可以进行增量式的更新。只需使用 XMLHttpRequest 轮询服务器。这项技术通常被称作 Comet 。这项技术存在一些变体,每种变体具有不同的性能和可伸缩性。我们来看看这些不同风格的 Comet 。 </li></ul><ul><li>客户端轮询技术我们不会陌生,我们主要了解 comet 技术。 </li></ul><ul><li>comet 技术其实包含两种风格:一是长轮询( long polling ),二是 流( streaming ) . </li></ul><ul><li>注意轮询和长轮询有着很大区别,轮询不属于 comet 风格,就像任何其他 Ajax 请求一样。在请求之间必须有一段暂停。否则,连续不断的请求会冲垮服务器,并且这种情况下显然不具有可伸缩性。这段暂停使应用程序产生一个延时。暂停的时间越长,服务器上的新数据就需要越多的时间才能到达客户机。如果缩短暂停时间,又将重新面临冲垮服务器的风险。 </li></ul>
  38. 38. reverse ajax 和服务器推技术 (5) <ul><li>轮询与长轮询之间的主要区别在于服务器花多长的时间作出响应。长轮询通常将连接保持一段较长的时间 — 通常是数秒钟,但是也可能是一分钟甚至更长。当服务器上发生某个事件时,响应被发送并随即关闭,轮询立即重新开始。 </li></ul><ul><li>长轮询相对于一般轮询的优点在于,数据一旦可用,便立即从服务器发送到客户机。请求可能等待较长的时间,期间没有任何数据返回,但是一旦有了新的数据,它将立即被发送到客户机。因此没有延时。如果您使用过基于 Web 的聊天程序,或者声称 “实时” 的任何程序,那么它很可能就是使用了这种技术。 </li></ul><ul><li>长轮询有一种变体,这是第三种风格的 Comet 。这通常被称为流( streaming )。按照这种风格,服务器将数据推回客户机,但是不关闭连接。连接将一直保持开启,直到过期,并导致重新发出请求。 XMLHttpRequest 规范表明,可以检查 readyState 的值是否为 3 或 Receiving (而不是 4 或 Loaded ),并获取正从服务器 “流出” 的数据。和长轮询一样,这种方式也没有延时。当服务器上的数据就绪时,该数据被发送到客户机。这种方式的另一个优点是可以大大减少发送到服务器的请求,从而避免了与设置服务器连接相关的开销和延时。不幸的是, XMLHttpRequest 在不同的浏览器中有很多不同的实现。这项技术只能在较新版本的 Mozilla Firefox 中可靠地使用。对于 Internet Explorer 或 Safari ,仍需使用长轮询。 </li></ul>
  39. 39. reverse ajax 和服务器推技术 (6) <ul><li>基于 DWR 反向 ajax 的开发 : </li></ul><ul><li>使用 dwr 实现反向的 ajax 的第一步是,使用一些新的配置元素,首先在 web.xml 中要添加 dwr servlet 的一些新的初始化参数。 </li></ul><ul><li><servlet-name>dwr-invoker</servlet-name> </li></ul><ul><li><servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class> </li></ul><ul><li><init-param> </li></ul><ul><li><param-name>debug</param-name> </li></ul><ul><li><param-value>true</param-value> </li></ul><ul><li></init-param> </li></ul><ul><li><init-param> </li></ul><ul><li><!-- 是否跨域 --> </li></ul><ul><li><param-name> crossDomainSessionSecurity </param-name> </li></ul><ul><li><param-value>false</param-value> </li></ul><ul><li></init-param> </li></ul><ul><li><init-param> </li></ul><ul><li><!-- 开启反向 ajax--> </li></ul><ul><li><param-name> activeReverseAjaxEnabled </param-name> </li></ul><ul><li><param-value>true</param-value> </li></ul><ul><li></init-param> </li></ul><ul><li><init-param> </li></ul><ul><li><!-- 轮询方式 如果去掉此配置则是 comet 方式 --> </li></ul><ul><li><param-name>org.directwebremoting.extend.ServerLoadMonitor</param-name> </li></ul><ul><li><param-value>org.directwebremoting.impl.PollingServerLoadMonitor</param-value> </li></ul><ul><li></init-param> </li></ul><ul><li><init-param> </li></ul>
  40. 40. reverse ajax 和服务器推技术 (7) <ul><li>实际上只要一个参数,也就是 activeReverseAjaxEnabled 就可以了 , 除了上述的配置 </li></ul><ul><li>为了启用反向 ajax ,页面上还需要一些 js 代码。即只要在页面上 dwr.engine.setActiveReverseAjax(true); 就可以了。 </li></ul><ul><li>再来看看后台 java 代码示例。 </li></ul><ul><li>String currentPage = wContext.getCurrentPage(); </li></ul><ul><li>Collection sessions =wContext.getScriptSessionsByPage(currentPage); </li></ul><ul><li>Util utilAll = new Util(sessions); </li></ul><ul><li>utilAll.setValue(&quot;divTest&quot;, d.toString(), true); </li></ul><ul><li>dwr 会记录与之联系的每个客户端,分别存储每个客户端的会话,这一点和通常的 http 会话不同,一旦获得当前页面名称,就可以获取当前链接到这个页面的所有会话的列表,然后。可以获取 util 类的一个实例。这个实例是 dwr 中自己的 java 代码和客户端 js 代码之间的主要交互点,给这个实例传入一个会话列表,就可以与各个会话进行交互。 </li></ul>
  41. 41. v8 js 引擎介绍 <ul><li>V8 的性能提升主要来自三个关键部分: </li></ul><ul><li>* 快速属性访问 </li></ul><ul><li>* 动态机器码生成 </li></ul><ul><li>* 高效的垃圾收集 </li></ul>
  42. 42. v8 js 引擎介绍 -- 快速属性访问 (1) <ul><li>JavaScript 是一门动态语言,属性可以在运行时添加到或从对象中删除。这意味着对 </li></ul><ul><li>象的属性经常会发生变化。大部分 JavaScript 引擎都使用一个类似于字典的数据结构 </li></ul><ul><li>来存储对象的属性,这样每次访问对象的属性都需要进行一次动态的字典查找来获取 </li></ul><ul><li>属性在内存中的位置。这种实现方式让 JavaScript 中属性的访问比诸如 Java 和 这样 </li></ul><ul><li>的语言中的成员变量的访问慢了许多。成员变量在内存中的位置离对象的地址的距离 </li></ul><ul><li>是固定的,这个偏移量由编译器在编译的时候根据对象的类的定义决定下来。因此对 </li></ul><ul><li>成员变量的访问只是一个简单的内存读取或写入的操作,通常只需要一条指令即可。 </li></ul><ul><li>为了减少 JavaScript 中访问属性所花的时间, V8 采用了和动态查找完全不同的技术 </li></ul><ul><li>来实现属性的访问:动态地为对象创建隐藏类在 V8 里,当一个新的属性被添加到对 </li></ul><ul><li>象中时,对象所对应的隐藏类会随之改变。 </li></ul><ul><li>下面我们用一个简单的 JavaScript 函数来加以说明: </li></ul><ul><li>function Point(x, y) { </li></ul><ul><li>this.x = x; </li></ul><ul><li>this.y = y; </li></ul><ul><li>} </li></ul>
  43. 43. v8 js 引擎介绍 -- 快速属性访问 (2) <ul><li>当 new Point(x, y) 执行的时候,一个新的 Point 对象会被创建出来。如果这是 Point 对象第一次被创建, V8 会为它初始化一个隐藏类,不妨称作 C0 。因为这个对象还没有定义任何属性,所以这个初始类是一个空类。到这个时候为止,对象 Point 的隐藏类是 C0 。 </li></ul><ul><li>执行函数 Point 中的第一条语句( this.x = x; )会为对象 Point 创建一个新的属性 x 。此时, V8 会: </li></ul><ul><li>在 C0 的基础上创建另一个隐藏类 C1 ,并将属性 x 的信息添加到 C1 中:这个属性的值会被存储在距 Point 对象的偏移量为 0 的地方。 </li></ul><ul><li>在 C0 中添加适当的类转移信息,使得当有另外的以其为隐藏类的对象在添加了属性 x 之后能够找到 C1 作为新的隐藏类。此时对象 Point 的隐藏类被更新为 C1 。 </li></ul>
  44. 44. v8 js 引擎介绍 -- 快速属性访问 (3) <ul><li>执行函数 Point 中的第二条语句( this.y = y; )会添加一个新的属性 y 到对象 Point 中。同理,此时 V8 会: </li></ul><ul><li>在 C1 的基础上创建另一个隐藏类 C2 ,并在 C2 中添加关于属性 y 的信息:这个属 </li></ul><ul><li>性将被存储在内存中离 Point 对象的偏移量为 1 的地方。 </li></ul><ul><li>在 C1 中添加适当的类转移信息,使得当有另外的以其为隐藏类的对象在添加了属性 y 之后能够找到 C2 作为新的隐藏类。此时对象 Point 的隐藏类被更新为 C2 。 </li></ul>
  45. 45. v8 js 引擎介绍 -- 快速属性访问 (4)
  46. 46. v8 js 引擎介绍 -- 快速属性访问 (5) <ul><li>咋一看似乎每次添加一个属性都创建一个新的隐藏类非常低效。实际上,利用类转移信息,隐藏类可以被重用。下次创建一个 Point 对象的时候,就可以直接共享由最初那个 Point 对象所创建出来的隐藏类。例如,如果又一个 Point 对象被创建出来了: </li></ul><ul><li>* 一开始 Point 对象没有任何属性,它的隐藏类将会被设置为 C0 。 </li></ul><ul><li>* 当属性 x 被添加到对象中的时候, V8 通过 C0 到 C1 的类转移信息将对象的隐藏类更新为 C1 ,并直接将 x 的属性值写入到由 C1 所指定的位置(偏移量 0 )。 </li></ul><ul><li>* 当属性 y 被添加到对象中的时候, V8 又通过 C1 到 C2 的类转移信息将对象的隐藏类更新为 C2 ,并直接将 y 的属性值写入到由 C2 所指定的位置(偏移量 1 )。 </li></ul><ul><li>尽管 JavaScript 比通常的面向对象的编程语言都要更加动态一些,然而大部分的 JavaScript 程序都会表现出像上述描述的那样的运行时高度结构重用的行为特征来。使用隐藏类主要有两个好处:属性访问不再需要动态字典查找了;为 V8 使用经典的基于类的优化和内联缓存技术创造了条件 </li></ul>
  47. 47. v8 js 引擎介绍 -- 动态机器码生成 (1) <ul><li>V8 在第一次执行 JavaScript 代码的时候会将其直接编译为本地机器码,而不是使用中间字节码的形式,因此也没有解释器的存在。属性访问由内联缓存代码来完成,这些代码通常会在运行时由 V8 修改为合适的机器指令。 </li></ul><ul><li>在第一次执行到访问某个对象的属性的代码时, V8 会找出对象当前的隐藏类。同时, V8 会假设在相同代码段里的其他所有对象的属性访问都由这个隐藏类进行描述,并修改相应的内联代码让他们直接使用这个隐藏类。当 V8 预测正确的时候,属性值的存取仅需一条指令即可完成。如果预测失败了, V8 会再次修改内联代码并移除刚才加入的内联优化。 </li></ul><ul><li>例如,访问一个 Point 对象的 x 属性的代码如下: </li></ul><ul><li>point.x </li></ul><ul><li>在 V8 中,对应生成的机器码如下: </li></ul>
  48. 48. v8 js 引擎介绍 -- 动态机器码生成 (2) <ul><li>; ebx = the point object </li></ul><ul><li>cmp [ebx, < hidden class offset >], <cached hidden class> </li></ul><ul><li>jne <inline cache miss> </li></ul><ul><li>mov eax, [ebx, < cached x offset >] </li></ul><ul><li>如果对象的隐藏类和缓存的隐藏类不一样,执行会跳转到 V8 运行系统中处理内联缓存预测失败的地方,在那里原来的内联代码会被修改以移除相应的内联缓存优化。如果预测成功了,属性 x 的值会被直接读出来。 </li></ul><ul><li>当有许多对象共享同一个隐藏类的时候,这样的实现方式下属性的访问速度可以接近大多数动态语言。使用内联缓存代码和隐藏类实现属性访问的方式和动态代码生成和优化的方式结合起来,让大部分 JavaScript 代码的运行效率得以大幅提升。 </li></ul>
  49. 49. 多谢各位大拿!

×