对象详解,浅谈跨域以WebService对跨域的支持

浅谈跨域以WebService对跨域的支持

2015/04/03 · HTML5, JavaScript · WebService, 跨域

原文出处: 寒江独钓   

跨域问题来源于JavaScript的同源策略,即只有 协议+主机名+端口号 (如存在)相同,则允许相互访问。也就是说JavaScript只能访问和操作自己域下的资源,不能访问和操作其他域下的资源。

在以前,前端和后端混杂在一起, 比如JavaScript直接调用同系统里面的一个Httphandler,就不存在跨域的问题,但是随着现代的这种多种客户端的流行,比如一个应用通常会有Web端,App端,以及WebApp端,各种客户端通常会使用同一套的后台处理逻辑,即API, 前后端分离的开发策略流行起来,前端只关注展现,通常使用JavaScript,后端处理逻辑和数据通常使用WebService来提供json数据。一般的前端页面和后端的WebService API通常部署在不同的服务器或者域名上。这样,通过ajax请求WebService的时候,就会出现同源策略的问题。

需要说明的是,同源策略是JavaScript里面的限制,其他的编程语言,比如在C#,Java或者iOS等其他语言中是可以调用外部的WebService,也就是说,如果开发Native应用,是不存在这个问题的,但是如果开发Web或者Html5如WebApp,通常使用JavaScript ajax对WebService发起请求然后解析返回的值,这样就可能存在跨域的问题。

一般的,很容易想到,将外部的资源搬到同一个域上就能解决同源策略的限制的。即在Web网站上同时开发一个Http服务端页面,所有JavaScript的请求都发到这个页面上来,这个页面在内部使用其他语言去调用外部的WebService。即添加一个代理层。这种方式可以解决问题,但是不够直接和高效。

目前,比较常见的跨域解决方案包括JSONP (JSON with padding)和CORS (Cross-origin resource sharing )。一些解决方案需要客户端和服务端配合如JSOP,一些则只需要服务端配合处理比如CORS。下面分别介绍这两种跨域方案,以及服务端WebService如何支持这两种跨域方案。

H5 Crash 研究

2016/05/31 · HTML5 · Crash

原文出处: 小胡子哥(@Barret李靖)   

我们知道,支撑页面在 webview 上良好运转的前提是具备一个高效并且稳定的 webview 容器,而容器的高效稳定不仅仅由容器提供方来保障,也需要容器使用者遵守一些基本准则,否则就有可能出现页面 Crash 的情况,这些准则是什么?什么样的上层代码会引起容器异常退出?这是本文需要阐述的内容。

JavaScript arguments 对象详解

2016/10/24 · JavaScript · arguments

原文出处: CompileYouth   

JSONP以及WebService的支持

同源策略下,某个服务器是无法获取到服务器以外的数据,但是html里面的img,iframe和script等标签是个例外,这些标签可以通过src属性请求到其他服务器上的数据。而JSONP就是通过script节点src调用跨域的请求。

当我们向服务器提交一个JSONP的请求时,我们给服务传了一个特殊的参数,告诉服务端要对结果特殊处理一下。这样服务端返回的数据就会进行一点包装,客户端就可以处理。

举个例子,服务端和客户端约定要传一个名为callback的参数来使用JSONP功能。比如请求的参数如下:

JavaScript

1
http://www.example.net/sample.aspx?callback=mycallback

如果没有后面的callback参数,即不使用JSONP的模式,该服务的返回结果可能是一个单纯的json字符串,比如:

JavaScript

{ foo : 'bar' }

1
{ foo : 'bar' }

如果和服务端约定jsonp格式,那么服务端就会处理callback的参数,将返回结果进行一下处理,比如处理成:

JavaScript

mycallback({ foo : 'bar' })

1
mycallback({ foo : 'bar' })

可以看到,这其实是一个函数调用,比如可以实现在页面定义一个名为mycallback的回调函数:

JavaScript

mycallback = function(data) { alert(data.foo); };

1
2
3
4
mycallback = function(data)
         {
            alert(data.foo);
         };

现在,请求的返回值回去触发回调函数,这样就完了了跨域请求。

如果使用ServiceStack创建WebService的话,支持Jsonp方式的调用很简单,只需要在AppHost的Configure函数里面注册一下对响应结果进行过滤处理即可。

JavaScript

/// <summary> /// Application specific configuration /// This method should initialize any IoC resources utilized by your web service classes. /// </summary> /// <param name="container"></param> public override void Configure(Container container) { ResponseFilters.Add((req, res, dto) => { var func = req.QueryString.Get("callback"); if (!func.isNullOrEmpty()) { res.AddHeader("Content-Type", ContentType.Html); res.Write("<script type='text/javascript'>{0}({1});</script>" .FormatWith(func, dto.ToJson())); res.Close(); } }); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/// &lt;summary&gt;
        /// Application specific configuration
        /// This method should initialize any IoC resources utilized by your web service classes.
        /// &lt;/summary&gt;
        /// &lt;param name=&quot;container&quot;&gt;&lt;/param&gt;
        public override void Configure(Container container)
        {
            ResponseFilters.Add((req, res, dto) =&gt;
            {
                var func = req.QueryString.Get(&quot;callback&quot;);
                if (!func.isNullOrEmpty())
                {
                    res.AddHeader(&quot;Content-Type&quot;, ContentType.Html);
                    res.Write(&quot;&lt;script type=&#039;text/javascript&#039;&gt;{0}({1});&lt;/script&gt;&quot;
                        .FormatWith(func, dto.ToJson()));
                    res.Close();
                }
            });
        }

JSONP跨域方式比较方便,也支持各种较老的浏览器,但是缺点很明显,他只支持GET的方式提交,不支持其他Post的提交,Get方式对请求的参数长度有限制,在有些情况下可能不满足要求。所以下面就介绍一下CORS的跨域解决方案。

H5 Crash 问题概况

下图是 H5 Crash 的大致流程图:

图片 1

由于前端没办法捕捉到页面 Crash 的状态和堆栈,但是 H5 页面上发生的错误会传递到 Java 和更底层的 Native 直到容器异常退出,在退出的那一刻,容器会将堆栈写入到日志中,当下次打开容器时(也可能是定时上报)就会上报这些堆栈信息。

1. 什么是 arguments

MDN 上解释:

arguments 是一个类数组对象。代表传给一个function的参数列表。

我们先用一个例子直观了解下 JavaScript 中的 arguments 长什么样子。

JavaScript

function printArgs() { console.log(arguments); } printArgs("A", "a", 0, { foo: "Hello, arguments" });

1
2
3
4
5
function printArgs() {
    console.log(arguments);
}
 
printArgs("A", "a", 0, { foo: "Hello, arguments" });

执行结果是:

JavaScript

["A", "a", 0, Object]

1
["A", "a", 0, Object]

乍一看,结果是个数组,但并不是真正的数组,所以说 arguments 是一个类数组的对象(想了解真正数组与类数组对象的区别可以一直翻到最后)。

再看看 arguments 表示的内容,其表示了函数执行时传入函数的所有参数。在上面的例子中,代表了传入 printArgs 函数中的四个参数,可以分别用 arguments[0]arguments[1]… 来获取单个的参数。

CORS跨域及WebService的支持

先来看一个例子,我们新建一个基本的html页面,在里面编写一个简单的是否支持跨域的小脚本,如下:

XHTML

<html xmlns="; <head> <title>AJAX跨域请求测试</title> </head> <body> <input type='button' value='开始测试' onclick='crossDomainRequest()' /> <div id="content"></div> <script type="text/javascript"> //<![CDATA[ var xhr = new XMLHttpRequest(); var url = ''; function crossDomainRequest() { document.getElementById("content").innerHTML = "开始……"; if (xhr) { xhr.open('POST', url, true); xhr.onreadystatechange = handler; xhr.send(); } else { document.getElementById("content").innerHTML = "不能创建 XMLHttpRequest"; } } function handler(evtXHR) { if (xhr.readyState == 4) { if (xhr.status == 200) { var response = xhr.responseText; document.getElementById("content").innerHTML = "结果:" + response; } else { document.getElementById("content").innerHTML = "不允许跨域请求。"; } } else { document.getElementById("content").innerHTML += "<br/>执行状态 readyState:" + xhr.readyState; } } //]]> </script> </body> </html>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>AJAX跨域请求测试</title>
</head>
<body>
  <input type='button' value='开始测试' onclick='crossDomainRequest()' />
  <div id="content"></div>
 
  <script type="text/javascript">
    //<![CDATA[
    var xhr = new XMLHttpRequest();
    var url = 'http://localhost:8078/json/ShopUserLogin';
    function crossDomainRequest() {
      document.getElementById("content").innerHTML = "开始……";
      if (xhr) {
        xhr.open('POST', url, true);
        xhr.onreadystatechange = handler;
        xhr.send();
      } else {
        document.getElementById("content").innerHTML = "不能创建 XMLHttpRequest";
      }
    }
 
    function handler(evtXHR) {
      if (xhr.readyState == 4) {
        if (xhr.status == 200) {
          var response = xhr.responseText;
          document.getElementById("content").innerHTML = "结果:" + response;
        } else {
          document.getElementById("content").innerHTML = "不允许跨域请求。";
        }
      }
      else {
        document.getElementById("content").innerHTML += "<br/>执行状态 readyState:" + xhr.readyState;
      }
    }
    //]]>
  </script>
 
</body>
</html>

然后保存为本地html文件,可以看到,这个脚本中,对本地的服务 发起了一个请求, 如果使用chrome 直接打开,会看到输出的结果,不允许跨域请求。 在javascript控制台程序中同样可以看到错误提示:

图片 2

那么如果在返回响应头header中注入Access-Control-Allow-Origin,这样浏览器检测到header中的Access-Control-Allow-Origin,则就可以跨域操作了。

同样,如果使用ServcieStack,在很多地方可以支持CORS的跨域方式。最简单的还是在AppHost的Configure函数里面直接写入:

JavaScript

/// <summary> /// Application specific configuration /// This method should initialize any IoC resources utilized by your web service classes. /// </summary> /// <param name="container"></param> public override void Configure(Container container) { this.AddPlugin(new CorsFeature()); }

1
2
3
4
5
6
7
8
9
/// &lt;summary&gt;
/// Application specific configuration
/// This method should initialize any IoC resources utilized by your web service classes.
/// &lt;/summary&gt;
/// &lt;param name=&quot;container&quot;&gt;&lt;/param&gt;
public override void Configure(Container container)
{
    this.AddPlugin(new CorsFeature());
}

这样就可以了,相当于使用默认的CORS配置:

JavaScript

CorsFeature(allowedOrigins:"*", allowedMethods:"GET, POST, PUT, DELETE, OPTIONS", allowedHeaders:"Content-Type", allowCredentials:false);

1
2
3
4
CorsFeature(allowedOrigins:&quot;*&quot;,
allowedMethods:&quot;GET, POST, PUT, DELETE, OPTIONS&quot;,
allowedHeaders:&quot;Content-Type&quot;,
allowCredentials:false);

如果仅仅允许GET和POST的请求支持CORS,则只需要改为:

JavaScript

Plugins.Add(new CorsFeature(allowedMethods: "GET, POST"));

1
Plugins.Add(new CorsFeature(allowedMethods: &quot;GET, POST&quot;));

当然也可以在AppHost的Config里面设置全局的CORS,如下:

JavaScript

/// <summary> /// Application specific configuration /// This method should initialize any IoC resources utilized by your web service classes. /// </summary> /// <param name="container"></param> public override void Configure(Container container) { base.SetConfig(new EndpointHostConfig { GlobalResponseHeaders = { { "Access-Control-Allow-Origin", "*" }, { "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS" }, { "Access-Control-Allow-Headers", "Content-Type" }, }, }); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/// &lt;summary&gt;
/// Application specific configuration
/// This method should initialize any IoC resources utilized by your web service classes.
/// &lt;/summary&gt;
/// &lt;param name=&quot;container&quot;&gt;&lt;/param&gt;
public override void Configure(Container container)
{
 
    base.SetConfig(new EndpointHostConfig
    {
        GlobalResponseHeaders = {
            { &quot;Access-Control-Allow-Origin&quot;, &quot;*&quot; },
            { &quot;Access-Control-Allow-Methods&quot;, &quot;GET, POST, PUT, DELETE, OPTIONS&quot; },
            { &quot;Access-Control-Allow-Headers&quot;, &quot;Content-Type&quot; },
                },
    });
}

现在运行WebService,使用postman或者Chrome调用这个请求,可以看到返回的值头文件中,已经加上了响应头,并且可以正常显示返回结果了:

图片 3

CORS使用起来简单,不需要客户端的额外处理,而且支持Post的方式提交请求,但是CORS的唯一一个缺点是对客户端的浏览器版本有要求,支持CORS的浏览器机器版本如下:

图片 4

 

H5 Crash 原因初探

测试代码 仓库地址:

git clone ; cd demo;

1
2
git clone https://github.com/barretlee/h5crash.git;
cd demo;

注意: 代码需要在 Webview 容器中测试,PC 浏览器下不会出现异常。

H5 Crash 的原因不太明显,但是从经验上判断和摸索,大致归类为以下三种:

1. 内存问题

  • 测试方法:使用闭包,不断增加内存量,看看增加到哪个区间大小, webview 容器会出现异常
  • 测试地址:(微信、微博或者其他客户端打开该页面的用户,可以点进去测试下,选择 100M 内存,不出意外,你的客户端会闪退。)

XHTML

<script> var Closure = function() { var _cache = []; var cache = 0; var add = function(size) { cache += size; size = size * 1024 * 1024; _cache.push(new Array(size).join('x')); refresh(); }; var refresh = function() { r.innerHTML = '内存消耗: ' + cache + 'M'; }; return { cache: cache + 'M', add: add, refresh: refresh } }; var closure = Closure(); </script> <button onclick="closure.add(1)">增加 1M 内存消耗</button> <button onclick="closure.add(10)">增加 10M 内存消耗</button> <button onclick="closure.add(20)">增加 20M 内存消耗</button> <button onclick="closure.add(50)">增加 50M 内存消耗</button> <button onclick="closure.add(100)">增加 100M 内存消耗</button> <div id="r">内存消耗:0 M</div>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<script>
var Closure = function() {
  var _cache = [];
  var cache = 0;
  var add = function(size) {
    cache += size;
    size = size * 1024 * 1024;
    _cache.push(new Array(size).join('x'));
    refresh();
  };
  var refresh = function() {
    r.innerHTML = '内存消耗: ' + cache + 'M';
  };
  return {
    cache: cache + 'M',
    add: add,
    refresh: refresh
  }
};
var closure = Closure();
</script>
 
<button onclick="closure.add(1)">增加 1M 内存消耗</button>
<button onclick="closure.add(10)">增加 10M 内存消耗</button>
<button onclick="closure.add(20)">增加 20M 内存消耗</button>
<button onclick="closure.add(50)">增加 50M 内存消耗</button>
<button onclick="closure.add(100)">增加 100M 内存消耗</button>
 
<div id="r">内存消耗:0 M</div>

存在的干扰:这种测试存在比较多的干扰,比如设备类型、系统类型(iOS/Android)、和设备内存运行状态等。

2. Layers 数问题

Layers 数的获取比较麻烦,Chrome Driver 没有提供该数据的接口,目前也没有比较好的办法拿到这个数据。

  • 测试方法:通过不同的方式创建层,观察页面的 Crash 情况
  • 测试地址:

XHTML

<style>.transform { transform: translateZ(0); } .animation { width:100px; height:100px; background:red; position:relative; animation:move 5s infinite; } @keyframes move { from {left:0px;} to {left:200px;} } </style> <script> var Layer = function() { function getType() { return document.querySelector('input:checked').value; }; return { createOne: function(index) { var div = document.createElement('div'); div.appendChild(document.createTextNode(index)); switch(getType()) { case 'opacity': div.style.cssText = "opacity:" + (index / 1000); break; case 'transform': div.className = 'transform'; break; case 'animation': div.className = 'animation'; break; case 'zindex': div.style.cssText = "position:relative; z-index:" + index; break; } document.body.appendChild(div); }, create: function(num) { [].slice.call(document.querySelectorAll('div')).forEach(function(item) { item.parentNode && item.parentNode.removeChild(item); }); while(num--) { this.createOne(num); } } } }; var layer = Layer(); </script> <strong>层类型: </strong> <ul> <li><label><input type="radio" checked name="type" value="opacity"> <span>通过 opacity 创建层</span></label></li> <li><label><input type="radio" name="type" value="transform"> <span>通过 transforms 创建层</span></label></li> <li><label><input type="radio" name="type" value="animation"> <span>通过 animation 创建层</span></label></li> <li><label><input type="radio" name="type" value="zindex"> <span>通过绝对定位分层</span></label></li> </ul> <button onclick="layer.create(1)">创建 1 个层</button> <button onclick="layer.create(10)">创建 10 个层</button> <button onclick="layer.create(20)">创建 20 个层</button> <button onclick="layer.create(50)">创建 50 个层</button> <button onclick="layer.create(100)">创建 100 个层</button> <button onclick="layer.create(200)">创建 200 个层</button> <button onclick="layer.create(500)">创建 500 个层</button> <button onclick="layer.create(1000)">创建 1000 个层</button> <button onclick="layer.create(2000)">创建 2000 个层</button> <button onclick="layer.create(5000)">创建 5000 个层</button> <button onclick="layer.create(10000)">创建 10000 个层</button>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<style>.transform {
  transform: translateZ(0);
}
.animation {
  width:100px;
  height:100px;
  background:red;
  position:relative;
  animation:move 5s infinite;
}
 
@keyframes move {
  from {left:0px;}
  to {left:200px;}
}
</style>
<script>
var Layer = function() {
  function getType() {
    return document.querySelector('input:checked').value;
  };
  return {
    createOne: function(index) {
      var div = document.createElement('div');
      div.appendChild(document.createTextNode(index));
      switch(getType()) {
        case 'opacity':
          div.style.cssText = "opacity:" + (index / 1000);
          break;
        case  'transform':
          div.className = 'transform';
          break;
        case  'animation':
          div.className = 'animation';
          break;
        case  'zindex':
          div.style.cssText = "position:relative; z-index:" + index;
          break;
      }
      document.body.appendChild(div);
    },
    create: function(num) {
      [].slice.call(document.querySelectorAll('div')).forEach(function(item) {
        item.parentNode && item.parentNode.removeChild(item);
      });
      while(num--) {
        this.createOne(num);
      }
    }
  }
};
var layer = Layer();
</script>
 
<strong>层类型: </strong>
<ul>
  <li><label><input type="radio" checked name="type" value="opacity"> <span>通过 opacity 创建层</span></label></li>
  <li><label><input type="radio" name="type" value="transform"> <span>通过 transforms 创建层</span></label></li>
  <li><label><input type="radio" name="type" value="animation"> <span>通过 animation 创建层</span></label></li>
  <li><label><input type="radio" name="type" value="zindex"> <span>通过绝对定位分层</span></label></li>
</ul>
 
<button onclick="layer.create(1)">创建 1 个层</button>
<button onclick="layer.create(10)">创建 10 个层</button>
<button onclick="layer.create(20)">创建 20 个层</button>
<button onclick="layer.create(50)">创建 50 个层</button>
<button onclick="layer.create(100)">创建 100 个层</button>
<button onclick="layer.create(200)">创建 200 个层</button>
<button onclick="layer.create(500)">创建 500 个层</button>
<button onclick="layer.create(1000)">创建 1000 个层</button>
<button onclick="layer.create(2000)">创建 2000 个层</button>
<button onclick="layer.create(5000)">创建 5000 个层</button>
<button onclick="layer.create(10000)">创建 10000 个层</button>
  • 实际上,创建多个层,也是对内存的巨大消耗,页面 Crash 可能还是因为内存消耗过大

3. 并发过多问题

  • 测试方法:尝试并发发出多种不同的请求(Fetch请求、XHR 请求、Script/CSS 资源请求),观察页面 Crash 情况
  • 测试地址:

XHTML

<script> var Request = function() { function getType() { return document.querySelector('input:checked').value; }; function getResource() { var type = getType(); var resource = { fetch: '/', xhr: '/', script: '//g.alicdn.com/sd/data_sufei/1.5.1/aplus/index.js', css: '//g.alicdn.com/kg/global-util/1.0.3/index-min.css' }; return resource[type]; }; return { emitOne: function() { var url = getResource() + "?_t=" + (new Date * 1 + Math.random()); switch(getType()) { case 'fetch': return fetch('/'); case 'xhr': with(new XMLHttpRequest) { open('GET', url); send(); } return; case 'script': var s = document.createElement('script'); s.src = url; document.body.appendChild(s); return; case 'css': var s = document.createElement('link'); s.href = url; document.body.appendChild(s); } }, emit: function(num) { [].slice.call(document.querySelectorAll('script,link')).forEach(function(item) { item.parentNode && item.parentNode.removeChild(item); }); while(num--) { this.emitOne(); } } } }; var request = Request(); </script> <strong>请求类型: </strong> <ul> <li><label><input type="radio" checked name="type" value="fetch"> <span>使用 Fetch 发送请求</span></label></li> <li><label><input type="radio" name="type" value="xhr"> <span>使用 XHR 发送请求</span></label></li> <li><label><input type="radio" name="type" value="script"> <span>并发请求脚本资源</span></label></li> <li><label><input type="radio" name="type" value="css"> <span>并发请求样式资源</span></label></li> </ul> <button onclick="request.emit(1)">并发 1 个请求</button> <button onclick="request.emit(10)">并发 10 个请求</button> <button onclick="request.emit(20)">并发 20 个请求</button> <button onclick="request.emit(50)">并发 50 个请求</button> <button onclick="request.emit(100)">并发 100 个请求</button> <button onclick="request.emit(500)">并发 500 个请求</button> <button onclick="request.emit(1000)">并发 1000 个请求</button>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<script>
var Request = function() {
  function getType() {
    return document.querySelector('input:checked').value;
  };
  function getResource() {
    var type = getType();
    var resource = {
      fetch: '/',
      xhr: '/',
      script: '//g.alicdn.com/sd/data_sufei/1.5.1/aplus/index.js',
      css: '//g.alicdn.com/kg/global-util/1.0.3/index-min.css'
    };
    return resource[type];
  };
  return {
    emitOne: function() {
      var url = getResource() + "?_t=" + (new Date * 1 + Math.random());
      switch(getType()) {
        case 'fetch':
          return fetch('/');
        case 'xhr':
          with(new XMLHttpRequest) {
            open('GET', url);
            send();
          }
          return;
        case 'script':
          var s = document.createElement('script');
          s.src = url;
          document.body.appendChild(s);
          return;
        case 'css':
          var s = document.createElement('link');
          s.href = url;
          document.body.appendChild(s);
      }
    },
    emit: function(num) {
      [].slice.call(document.querySelectorAll('script,link')).forEach(function(item) {
        item.parentNode && item.parentNode.removeChild(item);
      });
      while(num--) {
        this.emitOne();
      }
    }
  }
};
var request = Request();
</script>
 
<strong>请求类型: </strong>
<ul>
  <li><label><input type="radio" checked name="type" value="fetch"> <span>使用 Fetch 发送请求</span></label></li>
  <li><label><input type="radio" name="type" value="xhr"> <span>使用 XHR 发送请求</span></label></li>
  <li><label><input type="radio" name="type" value="script"> <span>并发请求脚本资源</span></label></li>
  <li><label><input type="radio" name="type" value="css"> <span>并发请求样式资源</span></label></li>
</ul>
 
<button onclick="request.emit(1)">并发 1 个请求</button>
<button onclick="request.emit(10)">并发 10 个请求</button>
<button onclick="request.emit(20)">并发 20 个请求</button>
<button onclick="request.emit(50)">并发 50 个请求</button>
<button onclick="request.emit(100)">并发 100 个请求</button>
<button onclick="request.emit(500)">并发 500 个请求</button>
<button onclick="request.emit(1000)">并发 1000 个请求</button>
  • 存在的干扰:设备的种类、设备的 CPU 使用情况和网络状况等。

2. arguments 操作

总结

本文介绍了JavaScript中的跨域基本概念和产生的原因,以及如何解决跨域的两种方法,一种是JSONP 一种是 CORS,在客户端Javascript调用服务端接口的时候,如果需要支持跨域的话,需要服务端支持。JSONP的方式就是服务端对返回的值进行回调函数包装,他的优点是支持众多的浏览器, 缺点是仅支持Get的方式对服务端请求。另一种主流的跨域方案是CORS,他仅需要服务端在返回数据的时候在相应头中加入标识信息。这种方式非常简便。唯一的缺点是需要浏览器的支持,一些较老的浏览器可能不支持CORS特性。

跨域支持是创建WebService时应该考虑的一个功能点,希望本文对您在这边面有所帮助,文中是使用ServiceStack来演示跨域支持的,如果您用的WCF的话,知道跨域原理的前提下,实现跨域应该不难。

 

H5 Crash 测试结果

测试结果:

  • 通过 opacity、animation、positon 等方式创建层,即便是 1w 个,页面也没有明显变化;但是使用 transform 创建 2k~5k 个层,页面会卡顿几秒后立即闪退;
  • 内存是条红线,测试发现,一次性消耗 20M 的内存,会导致客户端立即闪退;
  • 并发请求也是存在响应问题的,Fetch API 和 CSS Resource 并发 1k 请求没有出现问题,但是 XHR 和 Script Resource 请求,问题特别明显,虽然没有导致页面闪退,但是页面已经进入了假死状态。

以上临界值还可以继续精确。

2.1 arguments length

arguments 是个类数组对象,其包含一个 length 属性,可以用 arguments.length 来获得传入函数的参数个数。

JavaScript

function func() { console.log("The number of parameters is " + arguments.length); } func(); func(1, 2); func(1, 2, 3);

1
2
3
4
5
6
7
function func() {
    console.log("The number of parameters is " + arguments.length);
}
 
func();
func(1, 2);
func(1, 2, 3);

执行结果如下:

JavaScript

The number of parameters is 0 The number of parameters is 2 The number of parameters is 3

1
2
3
The number of parameters is 0
The number of parameters is 2
The number of parameters is 3

参考资料:

赞 收藏 评论

图片 5

小结

本文主要是对 H5 Crash 做了一个预研,测试可能存在诸多误差,测试方法也需要改进,不过沿着这些的思路考究会比较容易找到结论。

后续会给出比较有意义的边界数据以及探测工具。

 

1 赞 3 收藏 评论

图片 6

2.2 arguments 转数组

通常使用下面的方法来将 arguments 转换成数组:

JavaScript

Array.prototype.slice.call(arguments);

1
Array.prototype.slice.call(arguments);

还有一个更简短的写法:

JavaScript

[].slice.call(arguments);

1
[].slice.call(arguments);

在这里,只是简单地调用了空数组的 slice 方法,而没有从 Array 的原型层面调用。

为什么上面两种方法可以转换呢?

首先,slice 方法得到的结果是一个数组,参数便是 arguments。事实上,满足一定条件的对象都能被 slice 方法转换成数组。看个例子:

JavaScript

const obj = { 0: "A", 1: "B", length: 2 }; const result = [].slice.call(obj); console.log(Array.isArray(result), result);

1
2
3
const obj = { 0: "A", 1: "B", length: 2 };
const result = [].slice.call(obj);
console.log(Array.isArray(result), result);

执行结果是:

JavaScript

true ["A", "B"]

1
true ["A", "B"]

从上面例子可以看出,条件就是: 1) 属性为 0,1,2…;2) 具有 length 属性;

另外,有一个需要注意的地方就是,不能将函数的 arguments 泄露或者传递出去。什么意思呢?看下面的几个泄露 arguments 的例子:

JavaScript

// Leaking arguments example1: function getArgs() { return arguments; } // Leaking arguments example2: function getArgs() { const args = [].slice.call(arguments); return args; } // Leaking arguments example3: function getArgs() { const args = arguments; return function() { return args; }; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Leaking arguments example1:
function getArgs() {
    return arguments;
}
 
// Leaking arguments example2:
function getArgs() {
    const args = [].slice.call(arguments);
    return args;
}
 
// Leaking arguments example3:
function getArgs() {
    const args = arguments;
    return function() {
        return args;
    };
}

上面的做法就直接将函数的 arguments 对象泄露出去了,最终的结果就是 V8 引擎将会跳过优化,导致相当大的性能损失。

你可以这么做:

JavaScript

function getArgs() { const args = new Array(arguments.length); for(let i = 0; i < args.length; ++i) { args[i] = arguments[i]; } return args; }

1
2
3
4
5
6
7
function getArgs() {
    const args = new Array(arguments.length);
    for(let i = 0; i < args.length; ++i) {
        args[i] = arguments[i];
    }
    return args;
}

那就很好奇了,我们每次使用 arguments 时通常第一步都会将其转换为数组,同时 arguments 使用不当还容易导致性能损失,那么为什么不将 arguments 直接设计成数组对象呢?

这需要从这门语言的一开始说起。arguments 在语言的早期就引入了,当时的 Array 对象具有 4 个方法: toString、 join、 reverse 和 sort。arguments 继承于 Object 的很大原因是不需要这四个方法。而现在,Array 添加了很多强大的方法,比如 forEach、map、filter 等等。那为什么现在不在新的版本里让 arguments 重新继承自 Array呢?其实 ES5 的草案中就包含这一点,但为了向前兼容,最终还是被委员会否决了。

本文由澳门新葡亰平台官网发布于web前端,转载请注明出处:对象详解,浅谈跨域以WebService对跨域的支持

TAG标签:
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。