资讯专栏INFORMATION COLUMN

前端基本功-示例代码(一)

wapeyang / 1559人阅读

摘要:惰性载入表示函数执行的分支只会发生一次,这里有两种解决方式。插件,可以让回调函数在页面结构加载完成后再运行。

前端基本功-示例代码 (一) 点这里
前端基本功-示例代码 (二) 点这里

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.)原生实现:
 

服务端返回如下(返回时即执行全局函数):

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元素的事件冒泡

减少事件注册,节省内存占用,提高性能

可以实现当新增子对象时无需再次对其绑定

  
添加
绘画
散步
静坐
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("

hello

").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.图片懒加载
定义:延迟加载也称为惰性加载,即在长网页中延迟加载图像。用户滚动到它们之前,视口外的图像不会加载。这与图像预加载相反,在长网页上使用延迟加载将使网页加载更快。在某些情况下,它还可以帮助减少服务器负载。



    
    Lazyload 1
    


    

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部分:

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的结构和样式:



    
111
222
333
//过程式的编程思想
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;
}

    
//过程式的编程思想
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 **/


/** 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) 内推,备注来意,简历请甩 weihongjie@huami.com

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/117229.html

相关文章

  • 学习笔记 | HTML 基本结构和基本标签 ——前端学习第步!

    摘要:基本结构语言中,一个页面是由四个部分组成文档声明标签对标签对标签对图示文档声明这是一个文档声明,表示这是一个页面。标签标签表示页面内容的范围。 HTML HTML ...

    sPeng 评论0 收藏0
  • 前端修炼の道:第个 HTML 页面

    摘要:可见对一个页面正确渲染很重要。和标签对用于标识页面的头部区域,和之间的内容都属于头部区域中的内容。是一个辅助性标签,对页面可以进行很多方面的特性的设置。当页面没有设置字符集时,浏览器会使用默认的字符编码显示。 showImg(https://segmentfault.com/img/bVbjNkI?w=900&h=383); 往期回顾 在 1.2 节介绍 HTML 语言时讲到:HTML...

    JasonZhang 评论0 收藏0
  • 前端本功-示例代码 (二)

    摘要:前端基本功示例代码一点这里前端基本功示例代码二点这里一像素伪类实现对于老项目,有没有什么办法能兼容的尴尬问题了,个人认为伪类是比较完美的方法了。 前端基本功-示例代码 (一) 点这里前端基本功-示例代码 (二) 点这里 1.一像素 伪类 + transform 实现对于老项目,有没有什么办法能兼容1px的尴尬问题了,个人认为伪类+transform是比较完美的方法了。 原理是把原先元素...

    Java3y 评论0 收藏0
  • 前端本功-示例代码 (二)

    摘要:前端基本功示例代码一点这里前端基本功示例代码二点这里一像素伪类实现对于老项目,有没有什么办法能兼容的尴尬问题了,个人认为伪类是比较完美的方法了。 前端基本功-示例代码 (一) 点这里前端基本功-示例代码 (二) 点这里 1.一像素 伪类 + transform 实现对于老项目,有没有什么办法能兼容1px的尴尬问题了,个人认为伪类+transform是比较完美的方法了。 原理是把原先元素...

    wpw 评论0 收藏0
  • 前端修炼の道 | 你知道哪些复合选择器?

    摘要:后代选择器后代选择器又称包含选择器,用于选择指定元素的后代元素。这些选择器既可以是基本选择器,也可以是一个复合选择器。注意元素选择器及和属性选择器之间没有空格。 showImg(https://segmentfault.com/img/bVblJEJ?w=900&h=383); 复合选择器是通过基本选择器进行组合后构成的,常用的复合选择器有: 交集选择器 并集选持器 后代选择器 子元...

    Java_oldboy 评论0 收藏0

发表评论

0条评论

wapeyang

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<