1.ajaxvar xhr = new XMLHttpRequest(); // 声明一个请求对象// 前端设置是否带cookiexhr.withCredentials = true;xhr.open(‘GET’, ‘xxxx’);//xhr.open(‘post’, ‘http://www.domain2.com:8080/login', true);// 如何设置请求头? xhr.setRequestHeader(header, value);xhr.setRequestHeader(‘Content-Type’, ‘application/json’);xhr.onreadystatechange = function(){ if(xhr.readyState === 4){ // readyState 4 代表已向服务器发送请求 if(xhr.status === 200){ // status 200 代表服务器返回成功 console.log(xhr.responseText); // 这是返回的文本 } else{ console.log(“Error: “+ xhr.status); // 连接失败的时候抛出错误 } }}xhr.send(null); //xhr.send(‘user=admin’);// get方法 send null(亦或者不传,则直接是传递 header) ,post 的 send 则是传递值2.jsonp1.)原生实现: <script> var script = document.createElement(‘script’); script.type = ’text/javascript’; // 传参并指定回调执行函数为onBack script.src = ‘http://www.domain2.com:8080/login?user=admin&callback=onBack'; document.head.appendChild(script); // 回调执行函数 function onBack(res) { alert(JSON.stringify(res)); } </script>服务端返回如下(返回时即执行全局函数):onBack({“status”: true, “user”: “admin”})2.)jquery ajax:$.ajax({ url: ‘http://www.domain2.com:8080/login', type: ‘get’, dataType: ‘jsonp’, // 请求方式为jsonp jsonpCallback: “onBack”, // 自定义回调函数名 data: {}});3.)vue.js:this.$http.jsonp(‘http://www.domain2.com:8080/login', { params: {}, jsonp: ‘onBack’}).then((res) => { console.log(res); })4.)npm包jsonp:npm install jsonp –saveimport originJSONP from ‘jsonp’ //引入jsonp//进行封装并exportexport default function jsonp(url,data,option) { url += (url.indexOf(’?’)<0? ‘?’ : ‘&’)+param(data) return new Promise((resolve,reject)=>{ originJSONP(url,option,(err,data)=>{ if(!err){ resolve(data) }else{ reject(err) } }) })}//对data进行处理,并encodeURIComponent()进行转码。function param(data) { let url = ’’ for(var k in data) { let value = data[k] !== undefined? data[k] : ’’ url += ‘&’ + k + ‘=’ + encodeURIComponent(value) } return url ? url.substring(1) : ‘’}本节参考文章: vue项目中jsonp跨域获取qq音乐首页推荐3.实现一个简单的PromisePromise对象调用let p =new Promise(function(resolve, reject){ if(/* 异步操作成功 /){ resolve(data) }else{ reject(err) }})p.then((res)=>{ console.log(res)},(err)=>{ console.log(err)})实现一个简单的Promisefunction Promise(fn){ var status = ‘pending’ function successNotify(){ status = ‘fulfilled’//状态变为fulfilled toDoThen.apply(undefined, arguments)//执行回调 } function failNotify(){ status = ‘rejected’//状态变为rejected toDoThen.apply(undefined, arguments)//执行回调 } function toDoThen(){ setTimeout(()=>{ // 保证回调是异步执行的 if(status === ‘fulfilled’){ for(let i =0; i< successArray.length;i ++) { successArray[i].apply(undefined, arguments)//执行then里面的回掉函数 } }else if(status === ‘rejected’){ for(let i =0; i< failArray.length;i ++) { failArray[i].apply(undefined, arguments)//执行then里面的回掉函数 } } }) } var successArray = [] var failArray = [] fn.call(undefined, successNotify, failNotify) return { then: function(successFn, failFn){ successArray.push(successFn) failArray.push(failFn) return undefined // 此处应该返回一个Promise } }}解题思路:Promise中的resolve和reject用于改变Promise的状态和传参,then中的参数必须是作为回调执行的函数。因此,当Promise改变状态之后会调用回调函数,根据状态的不同选择需要执行的回调函数。本节参考文章:面向面试题和实际使用谈promise示例2const PENDING = “pending”; //等待const FULFILLED = “fulfilled”; //已完成const REJECTED = “rejected”; // 已拒绝function Promise(executor) { let self = this; self.status = PENDING; self.value; self.reason; function resolve(value) { if (self.status === PENDING) { self.status = FULFILLED; self.value = value; } } function reject(reason) { if (self.status === PENDING) { self.status = REJECTED; self.reason = reason; } } try { // 规范提到,执行器抛异常会reject executor(resolve, reject); } catch(e) { reject(e) }}// then方法实现Promise.prototype.then = function (onFulfilled, onRjected) { let self = this; /* * onFulfilled 和 onRejected 都是可选参数。 * 如果 onFulfilled 不是函数,其必须被忽略 * 如果 onRejected 不是函数,其必须被忽略 / onFulfilled = typeof onFulfilled === ‘function’ ? onFulfilled : function(value) { return value; }; onRjected = typeof onRjected === ‘function’ ? onRjected : function(reason) { throw reason; } if (self.status === FULFILLED) { onFulfilled(self.value); } if (self.status === REJECTED) { onRjected(self.reason); }}本节参考文章:Javascript Promise学习过程总结4.闭包var fn = function() { var divs = document.querySelectorAll(‘div’); for (var i = 0; i < 3; i++) { divs[i].onclick = (function(i) { return function() { alert(i); }; })(i); }};fn();或者如下的写法:var fn = function() { var divs = document.querySelectorAll(‘div’); for (var i = 0; i < 3; i++) { (function(i) { divs[i].onclick = function() { alert(i); }; })(i); }};fn();for (var i = 0; i < 3; i++) { setTimeout((function(i) { return function() { console.log(i); }; })(i), 0); console.log(i);}5.事件代理事件代理(Event Delegation),又称之为事件委托。是 JavaScript 中常用绑定事件的常用技巧。“事件代理”即是把原本需要绑定的事件委托给父元素,让父元素担当事件监听的职务。事件代理的原理是DOM元素的事件冒泡。减少事件注册,节省内存占用,提高性能可以实现当新增子对象时无需再次对其绑定 <div class=“wrap” id=“wrap”> <div class=“btn” data-type=“btn” data-feat=“add”>添加</div> <div class=“btn” data-type=“btn” data-feat=“delete”>绘画</div> <div class=“btn” data-type=“btn” data-feat=“delete”>散步</div> <div class=“btn” data-type=“btn” data-feat=“delete”>静坐</div> </div> <script type=“text/javascript”> var n = 0 document.getElementById(‘wrap’).addEventListener(‘click’, function(e) { var target = e.target; var type = target.dataset.type; var feat = target.dataset.feat; if (type == ‘btn’) { switch (feat) { case ‘add’: this.innerHTML += &lt;div class="btn" data-type="btn" data-feat="delete"&gt;静坐${n}&lt;/div&gt; n++ return; case ‘delete’: target.parentNode.removeChild(target); return; } } }, false);</script> 6.封装dom查询(面向对象)function Elem(id){ this.elem = document.getElementById(id)} Elem.prototype.html = function(val){ var elem = this.elem if(val) { elem.innerHTML = val return this //链式 } else { return elem.innerHTML } } Elem.prototype.on = function(type,fn){ var elem = this.elem elem.addEventListener(type, fn) return this //链式 }//调用var div = new Elem(‘id’)div.html(’<p>hello</p>’).on(‘click’,function(){ console.log(‘suceess’)})7.DOM劫持 function nodeToFragment (node) { var flag = document.createDocumentFragment(); var child; // 首先,所有表达式必然会返回一个值,赋值表达式亦不例外 // 理解了上面这一点,就能理解 while (child = node.firstChild) 这种用法 // 其次,appendChild 调用以后 child 会从原来 DOM 中移除 // 所以,第二次循环时,node.firstChild 已经不再是之前的第一个子元素了 while (child = node.firstChild) { flag.appendChild(child); // 将子节点劫持到文档片段中 } return flag }8.添加calssName// 为元素添加类名export function addClass(el, className) { // 先判断一下元素是否含有需要添加的类名,有则直接 return if(hasClass(el, className)) { return } // 把该元素含有的类名以空格分割 let newClass = el.className.split(’ ‘) // 把需要的类名 push 进来 newClass.push(className) // 最后以空格拼接 el.className = newClass.join(’ ‘)}// 判断是否有要查看的 className,有则返回true,否则返回 falseexport function hasClass(el, className) { let reg = new RegExp(’(^|\s)’ + className + ‘(\s|$)’) return reg.test(el.className)}9.自动添加游览器前缀let elementStyle = document.createElement(‘div’).style// 主流浏览器内核let vendor = (() => { let transfromNames = { webkit: ‘webkitTransform’, Moz: ‘MozTransform’, ms: ‘msTransform’, O: ‘OTransform’, standard: ’transform’ } for(let key in transfromNames) { if(elementStyle[transfromNames[key]] !== undefined) { return key } } return false})()// 添加样式的浏览器前缀export function prefixStyle(style) { if(vendor === false) { return false } if(vendor === ‘standard’) { return style } return vendor + style.charAt(0).toUpperCase() + style.substr(1)}10.图片懒加载定义:延迟加载也称为惰性加载,即在长网页中延迟加载图像。用户滚动到它们之前,视口外的图像不会加载。这与图像预加载相反,在长网页上使用延迟加载将使网页加载更快。在某些情况下,它还可以帮助减少服务器负载。<!DOCTYPE html><html lang=“en”><head> <meta charset=“UTF-8”> <title>Lazyload 1</title> <style> img { display: block; margin-bottom: 50px; height: 200px; } </style></head><body> <img src=“images/loading.gif” data-src=“images/1.png”> <img src=“images/loading.gif” data-src=“images/2.png”> <img src=“images/loading.gif” data-src=“images/3.png”> <img src=“images/loading.gif” data-src=“images/4.png”> <img src=“images/loading.gif” data-src=“images/5.png”> <img src=“images/loading.gif” data-src=“images/6.png”> <img src=“images/loading.gif” data-src=“images/7.png”> <img src=“images/loading.gif” data-src=“images/8.png”> <img src=“images/loading.gif” data-src=“images/9.png”> <img src=“images/loading.gif” data-src=“images/10.png”> <img src=“images/loading.gif” data-src=“images/11.png”> <img src=“images/loading.gif” data-src=“images/12.png”> <!– <script> var num = document.getElementsByTagName(‘img’).length; var img = document.getElementsByTagName(“img”); var n = 0; //存储图片加载到的位置,避免每次都从第一张图片开始遍历 lazyload(); //页面载入完毕加载可是区域内的图片 window.onscroll = lazyload; function lazyload() { //监听页面滚动事件 var seeHeight = document.documentElement.clientHeight; //可见区域高度 var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; //滚动条距离顶部高度 for (var i = n; i < num; i++) { if (img[i].offsetTop < seeHeight + scrollTop) { if (img[i].getAttribute(“src”) == “images/loading.gif”) { img[i].src = img[i].getAttribute(“data-src”); } n = i + 1; } } } </script>–> //对比一下上下两种代码,一个变量是全局变量,一个是函数的局部作用域, <script> function lazyload() { var images = document.getElementsByTagName(‘img’); var len = images.length; var n = 0; //存储图片加载到的位置,避免每次都从第一张图片开始遍历 return function() { var seeHeight = document.documentElement.clientHeight; var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; for(var i = n; i < len; i++) { if(images[i].offsetTop < seeHeight + scrollTop) { if(images[i].getAttribute(‘src’) === ‘images/loading.gif’) { images[i].src = images[i].getAttribute(‘data-src’); } n = n + 1; } } } } var loadImages = lazyload(); loadImages(); //初始化首页的页面图片 window.addEventListener(‘scroll’, loadImages, false);</script></body></html>jQuery<script> var n = 0, imgNum = $(“img”).length, img = $(‘img’); lazyload(); $(window).scroll(lazyload); function lazyload(event) { for (var i = n; i < imgNum; i++) { if (img.eq(i).offset().top < parseInt($(window).height()) + parseInt($(window).scrollTop())) { if (img.eq(i).attr(“src”) == “default.jpg”) { var src = img.eq(i).attr(“data-src”); img.eq(i).attr(“src”, src); n = i + 1; } } } }</script>11.img加载图片失败时,使用默认图片img标签自带onError属性,当图片加载失败时,触发error事件:<img src=“image.png” onError=‘this.src=“http://ww.jpg”’ />jQuery的error事件$(‘img’).error(function(){ $(this).attr(‘src’,“http://ww.jpg”);});jQuery的one绑定使用onerror或者jQuery的error事件时,如果默认图片也发生加载失败,则会形成死循环,最好的办法是使用one绑定事件,只执行一次$(“img”).one(“error”, function(e){ $(this).attr(“src”, “http://ww.jpg”);});12.图片按比例响应式缩放、裁剪html部分:<div class=“zoomImage” style=“background-image:url(images/test1.jpg)"></div>css部分:.zoomImage{ width:100%; height:0; padding-bottom: 100%; overflow:hidden; //padding为百分比的时候根据他父层的宽度来进行计算 background-position: center center; background-repeat: no-repeat; -webkit-background-size:cover; -moz-background-size:cover; //把背景图像扩展至完全覆盖背景区域 background-size:cover;}总结:你所需要的比例,就是width与padding-bottom的比例 用的时候,直接把.zoomImage当成img标签来用就可以了。思维扩展很多轮播的插件本来是响应式的, 但可能有两个问题: 1.这个轮播图你必须要给他一个高度,但高度不是固定死的,是需要按比例的…2.轮播图里的图片不是需要的比例…所以我们可以用刚刚上面的padding方法拿swiper轮播图插件举例优化前优化后本节详细:如何让图片按比例响应式缩放、并自动裁剪的css技巧13.选项卡切换html的结构和样式:<style type=“text/css”> #div1 div{ width: 200px; height:200px; border: 1px #000 solid; display: none; } .active{ background: red; }</style><body> <div id=“div1”> <button class=“active”>1</button> <button>2</button> <button>3</button> <div style=“display: block;">111</div> <div>222</div> <div>333</div> </div></body>//过程式的编程思想window.onload=function(){ //获取元素 var oParent=document.getElementById(‘div1’); var btns=oParent.getElementsByTagName(‘button’); var divs=oParent.getElementsByTagName(‘div’); //通过循环给每个btn添加点击事件 for (var i = 0; i < btns.length; i++) { btns[i].index=i;//存储当前btn的下标 btns[i].onclick=function(){ for (var i = 0; i < btns.length; i++) { btns[i].className=’’; divs[i].style.display=‘none’; } this.className=‘active’; divs[this.index].style.display=‘block’;//让对应当前btn的div显示 } }}//面向对象window.onload = function(){ var t1=new Tab(); t1.init();}; function Tab() { this.btns=oParent.getElementsByTagName(‘button’); this.divs=oParent.getElementsByTagName(‘div’); } Tab.prototype.init=function(){ var This=this; for (var i = 0; i < this.btns.length; i++) { this.btns[i].index=i; this.btns[i].onclick=function(){ This.change(this); } }}Tab.prototype.change=function(btn) { for (var i = 0; i < this.btns.length; i++) { this.btns[i].className=’’; this.divs[i].style.display=‘none’; } btn.className=‘active’; this.divs[btn.index].style.display=‘block’;};14.元素拖拽#div1{ width: 100px; height: 100px; background: red; position: absolute;}<body> <div id=‘div1’></div></body>//过程式的编程思想window.onload=function(){ var oDiv=document.getElementById(‘div1’); var disX=0; var disY=0; oDiv.onmousedown=function(ev){ var ev=ev || window.event; disX=ev.clientX-oDiv.offsetLeft; disY=ev.clientY-oDiv.offsetTop; document.onmousemove=function(ev){ var ev=ev || window.event; oDiv.style.left=ev.clientX-disX+‘px’; oDiv.style.top=ev.clientY-disY+‘px’; }; document.onmouseup=function(){ document.onmousemove=null; document.onmouseup=null; } return false; }}//面向对象window.onload = function() { var d1 = new Drag(‘div1’); d1.init();};function Drag(id) { this.oDiv = document.getElementById(id); this.disX = 0; this.disY = 0;}Drag.prototype.init = function() { var This = this; this.oDiv.onmousedown = function(ev) { var ev = ev || window.event; This.fnDown(ev); return false; };};Drag.prototype.fnDown = function(ev) { var This = this; this.disX = ev.clientX - this.oDiv.offsetLeft; this.disY = ev.clientY - this.oDiv.offsetTop; document.onmousemove = function(ev) { var ev = ev || window.event; This.fnMove(ev); }; document.onmouseup = function() { This.fnUp(); }};Drag.prototype.fnMove = function(ev) { this.oDiv.style.left = ev.clientX - this.disX + ‘px’; this.oDiv.style.top = ev.clientY - this.disY + ‘px’;};Drag.prototype.fnUp = function() { document.onmousemove = null; document.onmouseup = null;};15.函数节流(throttle)//fn 要执行的函数//delay 延迟//atleast 在time时间内必须执行一次function throttle(fn, delay, atleast) { var timeout = null, startTime = new Date(); return function() { var curTime = new Date(); clearTimeout(timeout); // 如果达到了规定的触发时间间隔,触发 handler if(curTime - startTime >= atleast) { fn(); startTime = curTime; }else { // 没达到触发间隔,重新设定定时器 timeout = setTimeout(fn, delay); } }} // 实际想绑定在 scroll 事件上的 handlerfunction lazyload(event) { console.log(‘触发了’)}// 采用了节流函数window.addEventListener(‘scroll’,throttle(lazyload,500,1000));16.函数去抖(debounce)// debounce函数用来包裹我们的事件function debounce(fn, delay) { // 持久化一个定时器 timer let timer = null; return function() { // 如果事件被触发,清除 timer 并重新开始计时 clearTimeout(timer); timer = setTimeout(function() { fn(); }, delay); }}// 实际想绑定在 scroll 事件上的 handlerfunction lazyload(event) { console.log(‘触发了’)}// 采用了去抖函数window.addEventListener(‘scroll’,debounce(lazyload,500));17.分时函数如果一次获得了很多数据(比如有10W数据),然后在前端渲染的时候会卡到爆,所以在处理这么多数据的时候,我们可以选择分批进行。function timeChunk(data, fn, count = 1, wait) { let obj, timer; function start() { let len = Math.min(count, data.length); for (let i = 0; i < len; i++) { val = data.shift(); // 每次取出一个数据,传给fn当做值来用 fn(val); } } return function() { timer = setInterval(function() { if (data.length === 0) { // 如果数据为空了,就清空定时器 return clearInterval(timer); } start(); }, wait); // 分批执行的时间间隔 }}// 测试用例let arr = [];for (let i = 0; i < 100000; i++) { // 这里跑了10万数据 arr.push(i);}let render = timeChunk(arr, function(n) { // n为data.shift()取到的数据 let div = document.createElement(‘div’); div.innerHTML = n; document.body.appendChild(div);}, 8, 20);render();参考文章:高阶函数,你怎么那么漂亮呢!18.惰性载入函数假如你要写一个函数,里面有一些判断语句function foo(){ if(a != b){ console.log(‘aaa’) }else{ console.log(‘bbb’) }}如果你的a和b是不变的,那么这个函数不论执行多少次,结果都是不变的,但是每次执行还要进行if判断,这就造成了不必要的浪费。惰性载入表示函数执行的分支只会发生一次,这里有两种解决方式。// 常见的例子if (window.addEventListener) { ele.addEventListener(type, fn, false);} else if (window.attachEvent) { ele.attachEvent(‘on’ + type, fn);}在函数被调用时再处理函数function foo(){ if(a != b){ foo = function(){ console.log(‘aaa’) } }else{ foo = function(){ console.log(‘bbb’) } } return foo();}这样进入每个分支后都会对foo进行赋值,覆盖了之前的函数,之后每次调用foo就不会再执行if判断在声明函数时就指定适当的函数var foo = (function foo(){ if(a != b){ return function(){ console.log(‘aaa’) } }else{ return function(){ console.log(‘bbb’) } }})();本节参考文章:JS高级技巧(简洁版)19.实现once函数function test(){ alert(‘hello’);}var once = function (fn) { var isFirst = true; return function () { if (isFirst) { isFirst = !isFirst; fn(); } };};once(test);once(test);20.requirejs架子require.js的诞生,就是为了解决这两个问题:   实现js文件的异步加载,避免网页失去响应;管理模块之间的依赖性,便于代码的编写和维护。/* 网页中引入require.js及main.js /<script src=“js/require.js” data-main=“js/main”></script>/ main.js 入口文件/主模块 **/// 首先用config()指定各模块路径和引用名require.config({ baseUrl: “js/lib”, paths: { “jquery”: “jquery.min”, //实际路径为js/lib/jquery.min.js “underscore”: “underscore.min”, }});// 执行基本操作require([“jquery”,“underscore”],function($,){ // some code here});引用模块的时候,我们将模块名放在[]中作为reqiure()的第一参数;如果我们定义的模块本身也依赖其他模块,那就需要将它们放在[]中作为define()的第一参数。// 定义math.js模块define(function () { var basicNum = 0; var add = function (x, y) { return x + y; }; return { add: add, basicNum :basicNum };});// 定义一个依赖underscore.js的模块define([‘underscore’],function(){ var classify = function(list){ .countBy(list,function(num){ return num > 30 ? ‘old’ : ‘young’; }) }; return { classify :classify };})// 引用模块,将模块放在[]内require([‘jquery’, ‘math’],function($, math){ var sum = math.add(10,20); $("#sum”).html(sum);});加载非规范的模块理论上,require.js加载的模块,必须是按照AMD规范、用define()函数定义的模块。但是实际上,虽然已经有一部分流行的函数库(比如jQuery)符合AMD规范,更多的库并不符合。那么,require.js是否能够加载非规范的模块呢?这样的模块在用require()加载之前,要先用require.config()方法,定义它们的一些特征。举例来说,underscore和backbone这两个库,都没有采用AMD规范编写。如果要加载它们的话,必须先定义它们的特征。require.config({    shim: {      ‘underscore’:{        exports: ‘’      },      ‘backbone’: {        deps: [‘underscore’, ‘jquery’],        exports: ‘Backbone’      }    }});require.config()接受一个配置对象,这个对象除了有前面说过的paths属性之外,还有一个shim属性,专门用来配置不兼容的模块。具体来说,每个模块要定义(1)exports值(输出的变量名),表明这个模块外部调用时的名称;(2)deps数组,表明该模块的依赖性。比如,jQuery的插件可以这样定义:shim: {    ‘jquery.scroll’: {      deps: [‘jquery’],      exports: ‘jQuery.fn.scroll’    }}require.js插件require.js还提供一系列插件,实现一些特定的功能。domready插件,可以让回调函数在页面DOM结构加载完成后再运行。require([‘domready!’], function (doc){  // called once the DOM is ready});  text和image插件,则是允许require.js加载文本和图片文件。define([  ’text!review.txt’,  ‘image!cat.jpg’ ], function(review,cat){    console.log(review);    document.body.appendChild(cat);  } ); 类似的插件还有json和mdown,用于加载json文件和markdown文件。本节参考文章:require.js的用法