木灵鱼儿 - javascript高性能
type: channel
link: https://www.mulingyuer.com/category/javascript%E9%AB%98%E6%80%A7%E8%83%BD/
items: 编程实践
items: Ajax
items: 字符和正则表达式
items: 算法和流程控制
items: DOM编程
items: 数据存取
items: 加载和执行

编程实践
type: item
date: 2019-06-17T13:08:22+08:00
description: 避免双重求值其实就是字符类型的js代码被运行时所产生的第二次运行求值我们都知道eval()方法是可以将字符类型的js代码运行的，但是除了它还有其他三种方法也支持。new Function()setTimeout()setInterval()这三个方法也都支持传入一个字符串类型的js代码，然后当这个函数运行后会识别这个字符并运行传入的js。这样就会产生两次求值，其中eval和Function这两种方法用的很少，甚至不使用了，而定时器确是比较常用的手段，所以为了规避这个问题，定时器里面第一个参数不要传字符，而是用函数封装。避免重复的工作在事件函数上，我们要做两个判断，要判断这个浏览器是支持dom 2级的事件还是ie的事件，所以要做判断，常用的方式如下：function addEvent(element,type,fn) { if(element.addEventListener) { element.addEventListener(type,fn,false); }else { element.attachEvent('on'+type,fn); } } function removeEvent(element,type,fn) { if(element.removeEventListener) { element.removeEventListener(type,fn,false); }else { element.detachEvent('on'+type,fn); } }这样写好像已经优化的很好了，自动判断浏览器支持，但是，事实上当我们每次运行一次addEvent函数时，都会if判断一次，然而事实上并不需要每次运行判断一次，因为用户不可能中途突然更改浏览器，所以，这个判断只需要运行一次即可。延迟加载function addEvent(element,type,fn) { if(element.addEventListener) { addEvent = function(element,type,fn){ element.addEventListener(type,fn,false); } }else { addEvent = function(element,type,fn){ element.attachEvent('on'+type,fn); } } addEvent(element,type,fn); } function removeEvent(element,type,fn) { if(element.removeEventListener) { removeEvent = function(element,type,fn){ element.removeEventListener(type,fn,false); } }else { removeEvent = function(element,type,fn){ element.detachEvent('on'+type,fn); } } removeEvent(element,type,fn); }第一次判断完重写整个函数，然后再次调用，下一次运行的时候已经是判断好的函数了，这里就可以加快运行速度。使用这种方法会在第一次调用函数时消耗较长的时间，还有另一种方式可以更快的运行。条件预加载var addEvent = document.body.addEventListener ? function(element,type,fn) { element.addEventListener(type,fn,false); } : function(element,type,fn){ element.attachEvent('on'+type,fn); }; var removeEvent = document.body.removeEventListener ? function(element,type,fn){ element.removeEventListener(type,fn,false); } : function(element,type,fn){ element.detachEvent('on'+type,fn); }; 我们知道，当js代码被下载完毕后是会被自动执行一次的，那么在这个自动执行的时候执行了三元运算，此时将判断好的函数赋值给实例，从而第一调用时就是正确的函数，没有了第一次判断所用的耗时。

Ajax
type: item
date: 2019-06-16T16:44:22+08:00
description: 异步监听readyState == 3当readyState的值等于3时，此时服务器正在与浏览器传输数据，这个时候其实是可以进行获取到responseText值得，但是这个值并不完整，只有当readyState == 4时，值才是完整的。但是如果我们需要获取的内容是极其长的字符，那么就可以通过监听这个状态，然后轮询的方式，可以在内容没有全部下载来之前就可以提前更新内容了，这样就可以避免在下载的过程中产生的等待时间。var req = new XMLHttpRequest(); var getLastInterval =null, lastLength = 0; req.open('GET','xxx.php',true); req.onreadystatechange = readStateHandler; req.send(null); function readStateHandler() { if(red.readyStare == 3 && getLastInterval == null) { getLastInterval = setInterval(function(){ getLastePacket(); },15) } if(red.readyStare == 4) { clearInterval(getLastInterval); getLastePacket(); } } function getLastePacket() { var length = req.responseText.length; var packet = req.responseText.substring(lastLength,length); callback(packet); lastLength = length; } 当readyState等于3的时候，创建一个定时器，15ms调用一次getLastePacket函数，这个函数每次运行先得到当前req.responseText.length的字符长度，然后通过string的substring方法截取这段内容，然后下次运行的时候又从上一次的最后一个位置开始，然后回调函数callback，将packet字符串传入，这个回调函数还要根据你传入的值做判断，毕竟你需要判断目前的字符最后一项是否完整，如果不完整可以创建一个全局变量来保存，下次再续上。通过这个方法可以在较大的内容时减少等待时间，增加用户体验。multipart XHR说白了就是通过php将多个地址合并成一个字符串返回给浏览器，浏览器通过js将返回的字符串解析成需要的值。比如php返回三个img图片的地址，通过;分号连接。if(req.readyState == 4) { splitImage(req.responsText); } function splitImage(text) { var imgelement = null, imgdata = text.split(';'); for(var i =0,len = imgdata.length;i<len;i++){ imgelement = document.createElement('img'); imgelement.src = imgdata[i]; document.getElementsByTagName('body')[0].appendChild(imgelement); } }以此类推，我们还可以增加更多的值，比如添加css地址，js文件，通过用不同的字符分隔，就可以判断到不同值，然后对应的获取。发送数据通过ajax发送数据其实有点奢侈，但是ajax支持pos方式发送，但是如果只是单方面的发送，且数据不会太大就可以使用get方式，但是ie对get方式的请求是有字符限制的，也就是说url的字符不能超过2048个字符，为此我要可以根据实际的使用情况使用对应的方法。发送数据我们甚至可以使用动态插入script的方式，更深层的讲，我们是通过元素的src属性进行发送的。这里暂时先不讲src的方式，这里完善一下ajax的方式事实上我们还要考虑如果发送错误的话怎么处理，这里就要使用到error事件。req.onerror = functuon() { //发生错误，我们在这个函数了再次调用封装的ajax函数，重新再运行一次，也就是递归 }我们甚至可以使用setTimeout来增加延时机制，如果获取的数据是要等一段时间才能获取到，然后再发送给服务器，那么就可以onerror事件里面使用setTimeout。get的方式会比post的方式发送更快，get只有一个数据包，而post至少会有两个，一个是头信息，一个是post数据。Beacons上面讲到发送数据使用的是…

字符和正则表达式
type: item
date: 2019-06-14T07:23:36+08:00
description: 字符串连接标准的方式都是A+B通过+进行连接，如果是下面这种写法：str += 'one' + 'two';这段代码会经历四个步骤：先在内存中创建一个新的临时字符串（内容为空）one和two先进行连接并赋值给临时字符串临时字符串与str进行连接返回的值赋值给str如果我们这样写可以避免创建临时字符串str += 'one'; str += 'two';因为只有一个字符串需要添加，然后str本身就是字符串，所以只对str进行了操作，没有再去创建新的临时字符。但是这样写太过麻烦，于是可以这样写：str = str + 'one' + 'two';因为字符的操作是从=符号开始，从左到右操作，此时str本身是一个占据在内存中的字符串，所以无需创建新的临时字符，然后依次往右连接，最后赋值，为此可以加快速度。但是在ie7及更早的版本，这种方式反倒没有加速的效果，反而会增加用时，原因是ie每次连接的时候都会每进行一步就复制一次，先将str和one连接的结果复制一次，复制一次后再复制后面的two，然后再将这两个连接的结果复制一次，依次 重复，所以在旧版的ie中，使用+=反倒更快了。但是+=也不能说明就是ie的最佳方案，在大量文字连接中，+=也是很慢，但是我们可以通过数组项合并的方式提高速度。数组项合并Array.prototype.join方法是将数组中的分隔符改变成其他的值，我们可以通过去除这个分隔符达到合并数组的效果，但是这个方法实际上在现在浏览器中是一个比较慢的方法，在旧版的ie反倒可以达到意料之外的效果。var str = 'I', strs = [], num = 5000, newStr = null; while(num--) { strs.push(str); } newStr = strs.join('');这样写反倒快于用for循环str+ ‘I’的方法。当然也仅限于旧版的ie。正则表达式正则表达式的优化要首先明白他的工作原理，最主要的就是理解回溯。回溯就是当正则匹配到一定的值后会继续查找正则后面的值，比如/.*\s+/；首先正则会贪婪的将所有字符匹配到，然后进行回溯，也就是从最后一个字符往前查找，当找到一个\s是并不会立即放弃，它还会继续回溯，直到找不到\s字符了。如果是惰性模式/.*?\s+/，它的方式又不一样了，惰性可以理解为最少次数，所以他会从最开始一个一个字符回溯，找到一个\s后并不会立即停止，它还会继续查找，直到后面的字符不是\s后才会停止。使用不明确的正则容易造成回溯失控，你可以把回溯理解为路口，当你到了一个站点后，就会根据实际情况产生多个不同的选择，每个选择为一个路口，当正则在第一个路口找不到的时候就会返回到第二个路口，然后无限重复，直到找到了值或者全部路口都找不到才会停下，所以说，能找到的正则时间最短，找不到的正则是最慢的，因为他要不断的回溯。为此宽泛的正则性能往往不如更具体的选择，.*往往不如[white]这种实际指出的值，然后我们还可以通过一些位置符减少回溯的次数，比如\b、\B这种。var str = 'AAAAAAAAAAAAAAAAAAAAAAAAAA'; var reg = /A+A+B/g; var time = +new Date(); alert(reg.test(str) +' ' + (+new Date() - time))这段代码中，如果str没有最后的B字符，正则依旧会不断的重复回溯很多次，A+A+会产生众多的路口，为此我们要考虑到一种情况，就是当str中没有B的时候该怎么办？这里我们就可以使用预查?!var str = 'AAAAAAAAAAAAAAAAAAAAAAAAAA'; var reg = /A+A+(?=B)B/g; var time = +new Date(); alert(reg.test(str) +' ' + (+new Date() - time))通过这个预查可以减少回溯的点，因为预查已经判断到了A+A+的部分，后面是要有B的，那么正则就不会再一个个判断，为此减少了时间，alert输出的时候，你会发现使用之前的正则返回1毫秒的时间，而使用预查的只用了0毫秒，速度非常之快，如果在大量文本内容的时候，预查的优势也会很高，但是不能滥用，预查也是可以进行一定的优化的。捕获正则中的捕获()也就是分组会将里面的内容额外的保存起来，如果大量不用的捕获，会浪费内存的空间，为此可以使用非捕获来减少空间占用。/^[a-z]+(?:.*)/通过非捕获?:来达到减少占用。事实上预查虽然是一个()，但是他是非捕获性的，所以他不能被回调所调用。何时不使用正则其实这里主要是因为正则只能从开始匹配到末尾，没办法从末尾匹配到开头，哪怕你使用了$字符申明，但是他依旧是从前到后，所以如果是针…

算法和流程控制
type: item
date: 2019-06-11T02:00:27+08:00
description: 循环for循环是常见的编程模式之一，也是提升性能必须要关注的要点之一。js的循环有四种：for循环while循环do--while循环for--in循环其中在浏览器中，while的循环会快于for循环，而for--in循环是最慢的，因为他会先从对象实例属性开始到从原型链继承而来的属性一一枚举。如何提高循环的性能？首先我们要知道，除了for--in，其他的循环性能都差不多，深究那种循环最快其实没有什么意义，循环的方式要看你环境的需求，那么抛开最快来讲，我们怎么才能提高循环的性能？无非就两点：每次循环处理的事物循环的次数通过减少两者中的一个，或者全部的时间开销，就可以提升整体的性能。减少循环处理的事物首先看下平时使用的循环：var item = [1,2,3] for(var i = 0;i<item.length;i++){ get(item[i]); } var j = 0; while(j<item.length){ get(item[j++]); } var k = 0; do { get(item[k++]); }while(k<item.length) function get(num){ alert(num); }上面循环每次循环时都会产生一下操作：判断循环条件时先会查找一次item元素的length属性（item.length）然后进行一次数值比较（i<item.length）比较的计算结果然后转换为布尔值（i<item.length == true）一次自增操作（++）一次数组查找（item[i]）一次函数调用（get(item[i])）这些循环虽然代码不多，但是每次循环都会产生以上的步骤，我们可以通过减少以上的步骤来达到效果之前讲过一次，局部变量的访问时比访问全局的要快的，然后将需要重复访问的值保存为一个局部变量，就可以减少访问次数。var item = [1,2,3] for(var i = 0，len = item.length;i < len;i++){ get(item[i]); } 这样写就可以减少访问item元素的次数，其他循环也是一样的写，定义一个变量保存length值即可达到一样的效果。我们还可以通过倒序的方式来提高循环性能。var item = [1,2,3]; for(var i = item.length;i--;){ get(item[i]); } 在js中，倒序循环会略微提升性能，通过倒序循环，并把后执行体（i--）放在前测条件中，这样就将两次对比变为了一次，直接判断数字是否为0；任何非0值都会自动转化为true，而0则等于false，原来是先判断小于length吗？是否为true，现在就直接就是是否为true，每次循环处理的事件又减少了，提升了性能。for(var i = item.length;i--;){ get(item[i]); } var j = item.length; while(j--){ get(item[j]); } var k = item.length -1; do { get(item[k]); }while(k--) 相对于之前的循环，这里只产生四个步骤：一次前测条件判断（i== true）一次自减操作（i--）一次数组查找（item[i]）一次函数运行（ get(item[i])）如何减少循环的次数？最广为人知的一种方式是“达夫设备 deff’s device”；它可以在一次循环中运行多次循环操作，为此‘jeff Greenberg’也被认为是将 “deff’s device”从原始的c实现移植到js的第一人。典型实现如下：var iterations = Math.floor(item.length/8), startAt = item.length % 8, i = 0; do { switch(startAt) { case 0 : get(item[i++]); case 7 : get(item[i++]); case 6 : get(item[i++]); case 5 : get(item[i++]); case 4 : get(item[i++]); case 3 : get(item[i++]); case 2 : get(item[i++]); case 1 : get(item[i++]); }; startAt = 0; }while(--iterations)iterations 判断当前元素能执行几个8次，使用Math.floor()去除小数点后的数。startAt 通过求余判断余几次。do -- while运行，先循环，switch判断余数，因为没有使用break跳出循环，所以，当case 0 判断为true的时候，它以及他…

DOM编程
type: item
date: 2019-05-27T17:26:35+08:00
description: dom是浏览器中非常重要的一部分，他其实相对于js是一个独立的语言，我们通过js去操作其实只是利用api沟通，并不是直接操作的，也就是说dom和js是两个部分，他们之间通过api进行沟通，那么这个沟通的过程自然就会产生性能的损耗，那么你沟通的越多，网页的响应速度就越慢。dom的访问与修改function innerHtmlLoop() { for(var i =0;i<15000;i++) { document.getElementById('box').innerHtml += 'a'; } }这里我们对元素box添加内容，添加15000个a字符，这里就进行了15000次api的沟通，这是非常消耗资源的，实际上还有效率更高的写法：function innerHtmlLoop() { var content = ''; for(var i =0;i<15000;i++) { content += 'a'; } document.getElementById('box').innerHTML += content; }我们先获取到完整的需要添加的内容，然后再一次性传入，这样就只产生了一次沟通，性能提升非常大。innerHTML 对比 dom方法通过innerHTML 创建元素速度快呢还是createElement速度快呢？目前以现在的浏览器来说其实速度已经是相差无几了，甚至dom的方法速度会快过innerHTML，但是innerHTML长盛不衰的秘诀自然是他的方便程度了，创建一个p元素我们使用innerHTML可以用极少量的代码完成，而dom则需要重复好几步，更复杂一点来说，如果是大量的内容，innerHTML无疑是完胜dom。//innerHTML document.body.innerHTML += ' 这是一个p元素 '；//dom var p = document.createElement('p'); var text = document.createTextNode('这是一个p元素'); p.appendChild(text); document.body.appendChild(p)；为了减少dom的重复性，js还提供了html dom的专门属性，比如表格的专门属性，表单的属性，所以使用这两个方法可以根据需求自行判断使用。节点克隆如果你只是想添加一个相同元素，只是内容不同，那么使用节点克隆会更有优势，虽然这个优势并不明显。使用cloneNode(Boolean);使用布尔值来判断，是否克隆该元素的子元素：true 克隆该元素内的所有子元素false 只克隆该元素本身var p = document.getElementsByTagName('p')[0]; var p2 = p.cloneNode(true); alert(p == p2) // trueHTML集合HTML集合是包含了DOM节点引用的类数组对象，他并没有数组的push()或者slice()方法，但提供了一个length属性，并且还可以通过数字索引的方式访问集合中的元素。HTML集合事实上会与文档保持连接，当文档内容发生改变时，集合也会发生改变，哪怕你只是获取length属性，这也是HTML集合低效率的源头。var div = documetn.getElementsByTagName('div'); for(var i = 0;i<div.length;i++) { document.body.appendChild(document.createElement('div')); }这段代码看上去没什么问题，实际上却是一个死循环，因为div.length是即时性的获取，当我们对文档进行添加div时，length也会+1，然后就会进入无限死循环。然而解决这个问题也很简单，我们只需要将length作为局部的缓存来进行使用即可。var div = documetn.getElementsByTagName('div'); var length = div.length； for(var i = 0;i<length ;i++) { document.body.appendChild(document.createElement('div')); }length成为了一个固定的值，不会影响到for语句的循环。事实上，如果我们多次循环引用div集合中的元素，性能损耗也会加重，办法也是一样，将里面的元素变为一个数组保存。var div = documetn.getElementsByTagName('div'); toArray(div); function toArray(arr) { for(var i = 0,a = [],length = arr…

数据存取
type: item
date: 2019-05-26T01:37:33+08:00
description: JavaScript中，不同存储位置，他的读取速度是不一样的，就好像一个距离你只有一米的饮料和一个距离你十米的饮料，当然是一米的你拿起来喝的速度最快。js中有四种基本的数据存取位置：1.字面量字面量只代表自身，不存储在特定的位置，js的字面量有：字符串、数字、布尔值、对象、数组、函数、正则表达式、及特殊的null和undefined值。你可以这么理解，if(true)语句中的true布尔值就是字面量，他就是一个值，不需要命名什么的就可以用的那种。2.本地变量使用var定义的数据存储单元，被作用域影响读取快慢。3.数组元素存储在JavaScript数组中的，以数字作为索引。4.对象成员存储在JavaScript对象内部，已字符串作为索引。其中字面量和本地变量读取最快，而数组和对象读取最慢，因为浏览器需要一个个遍历直到找到对应的值。虽然现在js的运行已经很快了，但是如果是大型的项目，他的代码量非常庞大，运行的效率也会被积少成多的小延迟影响到。作用域如果一个变量是在window下的，那么他是一个全局作用域下的变量，而如果他是一个function下的变量，那么他就是这个function作用域下的变量，那么function如何访问到他的作用域下的变量，又如何访问到全局的变量，这里就要了解到作用域链了。首先肯定很多人都会以为这还不简单，既然这个变量在function里面，那么自然就可以获取到里面的变量啊：function ------> 变量然后实际的原理并不是这样，function创建后，他会有一个内部属性，这个内部属性不能通过代码访问，只能通过浏览器的JavaScript引擎访问，这个属性就是[scope].scope属性他包含一个该函数被创建的作用域中的对象的集合，也就是全局对象的集合，该集合包含了window、navigator、document...等等可访问的数据对象，函数的作用域中的每个对象也被称之为可变对象，每个可变对象都是以‘键值对’的形式存在（变量名 = 值），这种存在的形式在被查找获取的时候，称之为‘标识符解析’。当function运行的时候，会创建一个称为执行环境的内部对象，每个执行环境都是独一无二的，多次调用同一个函数就会创建多个执行环境，这里是不是有点想法，比如：函数的独立属性为啥会独立？当执行环境被创建后，他的作用域链被链接到对应的运行的函数scope属性下，当这个过程完成后，一个被称之为‘活动对象’的新对象也就创建好了，活动对象作为函数运行时的变量对象，包含了函数内所有的局部变量、命名参数、参数集合及this，也就可以理解为函数内所有的变量集合了，这也就是所谓的独立属性了。这个活动对象会被推至顶端，当函数运行完毕后，这个活动对象也随之销毁。也就是说，scope属性里的顺序从原来只有一个全局对象集合到现在的：活动对象、全局对象，活动对象顺序高于全局对象，那么这也造成了局部的属性访问速度高于全局的，也就是为什么函数里面有一个name变量，window下也有一个name变量，当你在函数里面调用的时候，会是函数里的name被输出，而不是window下的，因为函数作用域下的变量所在的对象集合优先级高于全局的。标识符解析在函数执行的过程中，每遇到一个变量，都会经历一次标识符解析，从而决定从哪里获取或存储数据，该过程搜索执行环境的作用域链，查找同名的标识符，从头部开始依次查询，如果找到运行函数所调用的标识符（可以理解为变量名），就使用对应的变量，如果没有找到，就继续搜索下一个作用域链，若是无法找到匹配的对象，那么这个标识符就会被视为未定义（浏览器报错not defined），也正是这个不断搜索的过程，影响了代码的性能。标识符解析的性能标识符解析是有代价的，事实上没有哪种计算机操作可以不产生性能开销。在执行环境的作用域链中，一个标识符他的位置越深，他的读写速度就越慢，因此，在局部变量中读写总是最快的，而读写全局变量通常是最慢的，但是现在浏览器优化的js引擎，其实影响已经不那么明显了，但是不管浏览器如何优化他的引擎，总的趋势还是这样，一个标识符所在的位置越深，他的读写就越慢。来个例子：function initUI() { var bd = document.body, links = document.getElementsByTagName('a'), i = 0, len = links.length; while(i<len) { update(links[i++]); } document.getElementById('go-bth').onclick = function(event) { start(); } bd.className = 'active'; }上面这段代码，重复调用了全局作用域中docume…

加载和执行
type: item
date: 2019-05-24T21:21:00+08:00
description: js的加载会阻塞页面的加载和渲染，那么常用的方式就是将script元素放置在body元素里面最底下的位置，但是这样还是会有阻塞，但是不会阻塞下载，阻塞的是渲染，也就是说，js运行长时间的话，页面也会长时间显示空白，哪怕所有的资源都已经下载完毕了。延迟脚本为此便有了无阻塞的模式，那么最简单的就是给script添加defer和async属性了，但是这两个属性当初也只有ie支持，其他的浏览器并不支持，但是目前为止，如果都是新版本的浏览器，这两个属性已经全面支持了。defer表示当前的js文件不需要修改dom，所以他会在dom加载完运行，async则是异步加载，谁先加载谁先运行，但是如果js文件有前后顺序的话，就不怎么实用了。动态脚本既然defer和async不能满足我们的需求，那么只能另寻它法，于是就有了动态脚本！动态脚本原理：在浏览器加载html的时候只有一个script元素，这个元素里的js代码非常少，代码则创建script元素并插入head中，通过代码插入的js文件并不会影响页面的加载和解析。 //js文件中的代码 function loadScript(url) { var script = document.createElement('script'); script.type = 'text/javascript'; script.src = url; document.getElementsByTagName('head')[0].appendChild(script); } loadScript('js/load.js');这样通过动态加载的js可以避开阻塞带来的问题，但是也会产生一个新的问题，如果动态加载的js文件包含其他js文件调用的接口，那么就需要判断，当这个js文件加载完毕后再运行这个接口的代码，不然浏览器就会报错。新版的浏览器支持load事件判断script元素是否加载完成，旧ie不支持，但是支持readystatechange事件，通过readyState属性返回的值判断是否加载完成。readyState的值有5个，分别为：‘uninitialized’(初始状态)、‘loading’(开始下载)、‘loaded’(下载完成)、‘interactive’(数据完成下载但还不可用)、‘complete’(所有数据准备就绪);一般来说我们只需要判断是complete即可，但是ie有时候会返回complete，有时候会返回loaded，这两个状态返回后，代码就已经可以使用了，所以我们还要检查这两种状态，只要其中任意一个出现了，就可以触发了。function loadScript(url,callback) { var script = document.createElement('script'); script.type = 'text/javascript'; if(script.readyState) { script.onreadystatechange = function() { if(script.readyState == 'loaded' || script.readyState == 'complete') { script.onreadystatechange = null； callback(); } } }else { script.onload = function() { callback(); } } script.src = url; document.getElementsByTagName('head')[0].appendChild(script); } loadScript('js/load.js');如果我们要求js1加载完再加载js2，这样顺序加载的话，可以使用callback函数，写法如下：loadScript('js/load1.js',function(){ loadScript('js/load2.js',function(){ loadScript('js/load3.js',function(){ //无限嵌套 }); }); });ajax获取通过ajax获取，可以控制代码什么时候运行，因为通过ajax获取的是js代码本身，但它并不会自动执行，只有当我们插入的时候才会运行，但是也有一个坏处，因为同源策略的限制，你无法通过js去获取cdn等资源上的js文件，当然你也可以通过跨域的方式去解决。 var xhr = new XHRHttpRequest(); xhr.open('get','js/load.js',true); xhr.onreadystatechange = function() { if(xhr.readyState == 4) { …