前端基本功-示例代码(一)
前端基本功-示例代码 (一) 点这里
前端基本功-示例代码 (二) 点这里
1.ajax
var xhr = new XMLHttpRequest(); // 声明一个请求对象 // 前端设置是否带cookie xhr.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.jsonp
1.)原生实现:
<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 --save
import originJSONP from 'jsonp' //引入jsonp //进行封装并export export 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.实现一个简单的Promise
Promise对象调用
let p =new Promise(function(resolve, reject){ if(/* 异步操作成功 */){ resolve(data) }else{ reject(err) } })
p.then((res)=>{ console.log(res) },(err)=>{ console.log(err) })
实现一个简单的Promise
function 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
示例2
const 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 += `<div class="btn" data-type="btn" data-feat="delete">静坐${n}</div>` 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,否则返回 false export 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 事件上的 handler function 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 事件上的 handler function 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的用法
华米科技 招前端
联系: 于梦中(wx:tsw0618) 内推,备注来意,简历请甩 [email protected]