Ajax在浏览器上具体是如何执行的(1)

Ajax是目前web开发中使用很基本的技术了,搞web的程序员一定都搞过Ajax,即使没有自己用javascript写过原生Ajax,也一定使用过javascript框架中封装好的Ajax,比如jquery中,就有对Ajax的很好封装。

不过,用过,不见得就对Ajax真的了解的透彻,比如,Ajax技术是如何实现的?使用Ajax的时候,javascript扮演的角色是什么?浏览器时候是如何工作的?外部数据又是怎样返回到你调用的javascipt程序内?…诸如此类的问题,还是有很多开发人员没有想过,即使想过,也没有深入了解过这个问题。

没关系,下面我将对Ajax的实现做一次透彻的分析。

我以下所讲内容,全部基于webkit内核浏览器,所以有些内容可能不适用于Trident,Gecko或者其他内核

要弄透彻Ajax的工作方式,就要首先了解一下什么是Ajax

Ajax 即“Asynchronous Javascript And XML”(异步 JavaScript 和 XML),是指一种创建交互式网页应用的网页开发技术。

从描述上可以看出,Ajax并非一种javascript的方法。而是javascript的一个应用方式
。所以基于上述分析,及应该可以了解到,Ajax并非依靠javascript代码就能实现,它必须依靠浏览器更底层工作才能实现,然后作为一个应用,由javascript调用,从而实现后台运行获取数据,而不用通过刷新页面获取数据

下面是一段标准的javascript实现Ajax的过程

var xmlhttp;
xmlhttp=new XMLHttpRequest();
xmlhttp.onreadystatechange=function()
{
    if (xmlhttp.readyState==4 && xmlhttp.status==200)
    {
        xmlhttp.responseText;
    }
}
xmlhttp.open("GET","http://www.google.com",true);
xmlhttp.send();

下面通过上面这个一段简单的脚本来说明Ajax的具体工作方式

1.JavaScript的角色

脚本很简单,不用多解释,先说明一点,关于XMLHttpRequest的所有属性方法都定义webkit源码的JSXMLHttpRequest.cpp中。

在webkit中,javascript是通过javascriptcore(javascript引擎)实现的,javascriptcore是很好的JS引擎,自带JIT。具体可查看webkit源码,Source\JavaScriptCore文件夹下便是全部的jsc的实现代码。

webkit通过桥接程序,将javascriptcore API与dom 经行连接,让javascript可以操作htmldom,具体可见Source\WebCore\bridge

以上是一些基本的webkit知识,现在看我们上面写的代码,第一句不用看,谁都知道啥意思.

现在主要看第二句
new XMLHttpRequest,

XMLHttpRequest方法定义在Source\WebCore\xml\XMLHttpRequest.cpp

这个是创建一个XMLHttpRequest对象

XMLHttpRequest::XMLHttpRequest(ScriptExecutionContext* context, PassRefPtr<SecurityOrigin> securityOrigin):ActiveDOMObject(context, this)

当你调用onreadystatechange实际是调用了

setJSXMLHttpRequestOnreadystatechange

void setJSXMLHttpRequestOnreadystatechange(ExecState* exec, JSObject* thisObject, JSValue value)
{
UNUSED_PARAM(exec);
JSXMLHttpRequest* castedThis = static_cast<JSXMLHttpRequest*>(thisObject);
XMLHttpRequest* impl = static_cast<XMLHttpRequest*>(castedThis->impl());
impl->setOnreadystatechange(createJSAttributeEventListener(exec, value, thisObject));
}

impl->setOnreadystatechange(createJSAttributeEventListener(exec, value, thisObject))

这句话至关重要,这个就是你定义的无名函数,在数据返回时,程序将调用你刚才定义的代码,接下来

xmlhttp.open(“GET”,”www.google.com",true);

这句话会调用Source\WebCore\bindings\js\JSXMLHttpRequestCustom.cpp,目的是通过参数进行调用不同的函数重载,有一些参数,比如user,async这些都是判断时候同步,是否有用户名,密码KURL是webkit处理url时的统一类,主要记录url地址jsUndefined() 是每一个jsc函数扩展的返回类型,如果没有js返回,则返回jsUndefined(),就是js代码中的undefied;

JSValue JSXMLHttpRequest::open(ExecState* exec)
{
    if (exec->argumentCount() < 2)
    return throwError(exec, createSyntaxError(exec, "Not enough arguments"));

    const KURL& url = impl()->scriptExecutionContext()->completeURL(ustringToString(exec->argument(1).toString(exec)->value(exec)));
    String method = ustringToString(exec->argument(0).toString(exec)->value(exec));

    ExceptionCode ec = 0;
    if (exec->argumentCount() >= 3) {
    bool async = exec->argument(2).toBoolean(exec);

    if (exec->argumentCount() >= 4 && !exec->argument(3).isUndefined()) {
    String user = valueToStringWithNullCheck(exec, exec->argument(3));

    if (exec->argumentCount() >= 5 && !exec->argument(4).isUndefined()) {
    String password = valueToStringWithNullCheck(exec, exec->argument(4));
    impl()->open(method, url, async, user, password, ec);
    } else
    impl()->open(method, url, async, user, ec);
    } else
    impl()->open(method, url, async, ec);
    } else
    impl()->open(method, url, ec);

    setDOMException(exec, ec);
    return jsUndefined();
}

以上全是为要发起请求所做的工作,执行xmlhttp.send()才会真正的发送请求,一直到createRequest(ec)以前,还是为发起请求做一个必要的需求,比如判断发送方式、定义httphead、设置字符集。createRequest(ec)是真的开始请求了

void XMLHttpRequest::send(const String& body, ExceptionCode& ec)
{
    if (!initSend(ec))
    return;

    if (!body.isNull() && m_method != "GET" && m_method != "HEAD" && m_url.protocolInHTTPFamily()) {
    String contentType = getRequestHeader("Content-Type");
    if (contentType.isEmpty()) {
    #if ENABLE(DASHBOARD_SUPPORT)
    if (usesDashboardBackwardCompatibilityMode())
    setRequestHeaderInternal("Content-Type", "application/x-www-form-urlencoded");
    else
    #endif
    setRequestHeaderInternal("Content-Type", "application/xml");
    } else {
    replaceCharsetInMediaType(contentType, "UTF-8");
    m_requestHeaders.set("Content-Type", contentType);
    }

    m_requestEntityBody = FormData::create(UTF8Encoding().encode(body.characters(), body.length(), EntitiesForUnencodables));
    if (m_upload)
    m_requestEntityBody->setAlwaysStream(true);
    }

    createRequest(ec);
}

当然,webkit代码中通过了好多层调用才到了我们上面看的代码,并不是仅仅只有上面一点,不过上面就是代码就是JS操作的基本方式,在javascript代码中,他能做的就是以上这些工作

总结一下,javascript在ajax是所做的工作

有了以上的工作,javascript端就是完成了,具体是如何开是调用数据的,又如何使用异步调用的,请看一下节