【笔记】web前端琐碎代码学习片段及知识点整理

代码片段

JS设计模式片段

Function.prototype.addMethod = function (name,fn) {
   this.prototype[name] = fn;
   return this;
};
var Methods = function(){};
Methods.addMethod('checkName',function () {
   //验证姓名
   console.log('姓名');
}).addMethod('checkEmail',function () {
   //验证邮箱
   console.log('邮箱');
});
var m = new Methods();
m.checkName();
m.checkEmail();

canvas图片预加载及进度条的实现

/*star
 *loading模块
 *实现图片的预加载,并显示进度条
 *参数:图片数组对象,加载完成的回调函数
 */
function loadImages(sources,callback){    
    var loadedImages = 0;    
    var numImages = 0;  
    ctx.font='14px bold';
    ctx.lineWidth=5;
    var clearWidth=canvas.width;
    var clearHeight=canvas.height;
    // get num of sources    
    for (var src in sources) {    
        numImages++;    
    }    
    for (var src in sources) {    
        images[src] = new Image();
        //当一张图片加载完成时执行    
        images[src].onload = function(){ 
            //重绘一个进度条
            ctx.clearRect(0,0,clearWidth,clearHeight);
            ctx.fillText('Loading:'+loadedImages+'/'+numImages,200,280);
            ctx.save();
            ctx.strokeStyle='#555';
            ctx.beginPath();
            ctx.moveTo(200,300);
            ctx.lineTo(600,300);
            ctx.stroke();
            ctx.beginPath();
            ctx.restore();
            ctx.moveTo(200,300);
            ctx.lineTo(loadedImages/numImages*400+200,300);  
            ctx.stroke();
            //当所有图片加载完成时,执行回调函数callback
            if (++loadedImages >= numImages) {    
                callback();    
            }    
        };  
        //把sources中的图片信息导入images数组  
        images[src].src = sources[src];    
    }    
}    
//定义预加载图片数组对象,执行loading模块
window.onload = function(){    
    var sources = {    
        PaperBoy1: "images/run/PaperBoy1.png",    
        PaperBoy2: "images/run/PaperBoy2.png", 
        PaperBoy3: "images/run/PaperBoy3.png",    
        PaperBoy4: "images/run/PaperBoy4.png" 
    };    
    //执行图片预加载,加载完成后执行main
    loadImages(sources,main);    
};   
/*end*/

JS实现跨浏览器添加事件与移除事件怎样做才最优?

一般的兼容做法,如下:

跨浏览器添加事件

//跨浏览器添加事件
    function addEvent(obj,type,fn){
        if(obj.addEventListener){
            obj.addEventListener(type,fn,false);
        }else if(obj.attachEvent){//IE
            obj.attchEvent('on'+type,fn);
        }
    }

跨浏览器移除事件

//跨浏览器移除事件
function removeEvent(obj,type,fn){
    if(obj.removeEventListener){
        obj.removeEventListener(type,fn,false);
    }else if(obj.detachEvent){//兼容IE
        obj.detachEvent('on'+type,fn);
    }
}

推荐写法

    function addEvent( obj, type, fn ) {
      if ( obj.attachEvent ) {
        obj['e'+type+fn] = fn;
        obj[type+fn] = function(){obj['e'+type+fn]( window.event );}
        obj.attachEvent( 'on'+type, obj[type+fn] );
      } else
        obj.addEventListener( type, fn, false );
    }
    function removeEvent( obj, type, fn ) {
      if ( obj.detachEvent ) {
        obj.detachEvent( 'on'+type, obj[type+fn] );
        obj[type+fn] = null;
      } else
        obj.removeEventListener( type, fn, false );
    }

参考地址:

addEvent() recoding contest entry

addEvent() – Follow Up

Ajax用jsonp方式跨域发送请求小实例

众所周知,Ajax是通过创建XMLHttpRequest对象或ActiveXObject来连接服务器、发送请求以及响应数据,但它却不能跨域。而在分布式系统中我们又需要跨域发送接受数据,于是jsonp出现了...

它是一种跨域请求方式,主要利用了script标签里的src属性,该属性可以跨域发送请求,然后服务器返回js代码,网页端便响应其信息,然后我们可以对其传过来的js代码做处理提取其中的信息。

jsonp发送请求只需在src后面添加“?callback=函数名”就可以,例如“http://www.item.com/list?callback=myfunction",则只需在服务端接受参数myfunction并将函数名与想要返回的数据拼接就可以例如在java中响应该请求,可以获取参数callback的值myfunction,再拼接成myfunction+"("+data+")"格式返回就行,在前端写同名函数接受data并处理就可以了。但在jquery中对jsonp进行了封装,返回函数就是success,数据也用success接受。

例如:

前端代码:

//发送请求  
$.ajax({  
   //url:"http://localhost:8081/rest/itemcat/list?callback=getMessage",  
    url:"http://localhost:8081/rest/itemcat/message",      
    type:"get",  
    cache:false,  
    dataType:"jsonp",  
    jsonp:"callback", //这里定义了callback的参数名称,以便服务获取callback的函数名即getMessage  
    jsonpCallback:"getMessage", //这里定义了jsonp的回调函数  
    success:function(data){  
        alert("success:"+data);  
    },  
    error:function(){  
        alert("发生异常");  
    }  
});  

function getMessage(jsonp){  
    alert("message:"+jsonp);  
}

这样发出的请求为:http://localhost:8081/rest/itemcat/message?callback=getMessage
jsonp:"callback",
jsonpCallback:"getMessage",
这两个参数的值会自动拼接在url后面,所以用jquery的$.ajax方法发出的url可以不用在后面添加callback=getMessag,返回函数则变为了success而不是getMessage

使用高德地图API创建地图以及获取当前地址经纬度

创建API地图带有点标记

<script src="http://webapi.amap.com/maps?v=1.4.1&key=bcf87f3263f98cc37309298bca20c622"></script>
<script type="text/javascript">
    // 实例化点标记
 function addMarker() {
        marker = new AMap.Marker({
            icon: "http://webapi.amap.com/theme/v1.3/markers/n/mark_b.png",
            position: [116.41, 39.91]
        });
        marker.setMap(map);
    }

 var map = new AMap.Map('container', {
        resizeEnable: true,
        center: [116.40, 39.91],
        zoom: 13
    });
 addMarker();
</script>

根据浏览器定位获取当前经纬度

<div id="container_display"></div>
<script src="https://webapi.amap.com/maps?v=1.4.1&key=bcf87f3263f98cc37309298bca20c622"></script>
<script>
function loadingCurrentPosition(callback){
    
    document.getElementById('container_display').innerHTML = '';
      //加载地图,调用浏览器定位服务
    map = new AMap.Map('container_display', {
        resizeEnable: true
    });
    map.plugin('AMap.Geolocation', function() {
        geolocation = new AMap.Geolocation({
            enableHighAccuracy: true,//是否使用高精度定位,默认:true
            timeout: 10000,          //超过10秒后停止定位,默认:无穷大
            buttonOffset: new AMap.Pixel(10, 20),//定位按钮与设置的停靠位置的偏移量,默认:Pixel(10, 20)
            zoomToAccuracy: true,      //定位成功后调整地图视野范围使定位位置及精度范围视野内可见,默认:false
            buttonPosition:'RB'
        });
        map.addControl(geolocation);
        geolocation.getCurrentPosition();
        AMap.event.addListener(geolocation, 'complete', onComplete);//返回定位信息
        AMap.event.addListener(geolocation, 'error', onError);      //返回定位出错信息
    });
    //解析定位结果
    function onComplete(data) {
        var getLngdata = data.position.getLng();
        var getLatdata = data.position.getLat();
        if(callback){
            callback(getLngdata,getLatdata); //回调参数中传递经度与纬度
        }
    }
    //解析定位错误信息
    function onError(data) {
        alert('定位失败');
    }
}

$(function(){
    var getLng = ''; //存经度
    var getLat = ''; //存纬度
    //默认加载一次自动获取当前人的位置
    loadingCurrentPosition(function(lngdata,Latdata){
        getLng = lngdata;
        getLat = Latdata;
    });
    $(".getCurrentPosition").on('click',function(){
        loadingCurrentPosition(function(lngdata,Latdata){
            getLng = lngdata;
            getLat = Latdata;
        });
    });
})

</script>

高德地图根据浏览器定位获取当前经纬度API案例地址:http://lbs.amap.com/api/javascript-api/example/location/browser-location
高德开放平台:http://lbs.amap.com/api

JS创建canvas学习小例代码

1.HTML5中的Canvas标签的创建

window.onload = function(){
     createCanvas();
}
function createCanvas(){
    var canvas_width= 200, canvas_height = 200;
    document.body.innerHTML = "<canvas id=\"canvas\" width=\""+canvas_width+"\" height=\""+canvas_height+"\"></canvas>";
}

2.HTML5Canvas标签绘制图形

var canvas_width= 500, canvas_height = 500;
var mycanvas, context;
window.onload = function(){
    createCanvas();
    drawRect();
}
function createCanvas(){
   document.body.innerHTML = "<canvas id=\"mycanvas\" width=\""+canvas_width+"\" height=\""+canvas_height+"\"></canvas>";
   mycanvas = document.getElementById("mycanvas");
   context = mycanvas.getContext("2d");
}
 
function drawRect(){
    context.fillStyle ="#FF0000";
    //context.rotate(45);//旋转45度
    //context.translate(200,200);//移动
    //context.scale(2,0.5);//缩放
    context.fillRect(0,0,200,200);
 }

3.HTML5Canvas标签绘制图片

var canvas_width= 500, canvas_height = 500;
var mycanvas, context;
window.onload = function(){
    createCanvas();
    drawImage();
}
function createCanvas(){
    document.body.innerHTML = "<canvas id=\"mycanvas\" width=\""+canvas_width+"\" height=\""+canvas_height+"\"></canvas>";
    mycanvas = document.getElementById("mycanvas");
    context = mycanvas.getContext("2d");
}
 
function drawImage(){
    var img = new Image();
    img.onload = function(){
        context.drawImage(img,0,0);
    }
     img.src = "1.png";
}

正则匹配设置电话号码格式为XXX-XXXX-XXXX

var _formatType = 'XXX-XXXX-XXXX';
var i= 0;
var phone = '15510900909';
var result = _formatType.replace(/X/g,function() {
    return phone[i++];
});

语音播报

语音播报:在项目中需要对ajax请求返回的消息进行语音播报,str 为返回的data(可以在浏览器控制台尝试哟~)

function  voiceAnnouncements(str){
//百度
    var  url = "http://tts.baidu.com/text2audio?lan=zh&ie=UTF-8&text=" + encodeURI(str); // baidu
    var  n = new  Audio(url);
    n.src = url;
    n.play();
}
voiceAnnouncements('你好,今天吃的什么?')

数组展开的N种方法

  • 循环加递归
  • flat
  • flatMap
  • toString后split 
  • join后split
var arr1 = [[1,2],[3,4,[5,6,4]]];
var arr2 = [[1,2,{a:1}],[3,4,[5,6,4]]];
console.log(arr1.toString().split(','))
console.log(arr2.toString().split(','))

console.log(arr1.join(',').split(','))
console.log(arr2.join(',').split(','))

// ["1", "2", "3", "4", "5", "6", "4"]
// ["1", "2", "[object Object]", "3", "4", "5", "6", "4"]
// ["1", "2", "3", "4", "5", "6", "4"]
// ["1", "2", "[object Object]", "3", "4", "5", "6", "4"]

使用instanceof判断原始类型

class PrimitiveString {
  static [Symbol.hasInstance](x) {
    return typeof x === 'string'
  }
}
console.log('hello world' instanceof PrimitiveString) // true

使用 Proxy 来实现一个数据绑定和监听

Proxy简介:

let  p = new  Proxy(target, handler);
// `target` 代表需要添加代理的对象
// `handler` 用来自定义对象中的操作
let  onWatch = (obj, setBind, getLogger) => {
    let  handler = {
        get(target, property, receiver) {
            getLogger(target, property)
            return  Reflect.get(target, property, receiver);
        },

        set(target, property, value, receiver) {
            setBind(value);
            return  Reflect.set(target, property, value);
        }
    };
    return  new  Proxy(obj, handler);
};


let  obj = { a:  1 }
let  value
let  p = onWatch(obj, (v) => {
    value = v
}, (target, property) => {
    console.log(`Get '${property}' = ${target[property]}`);
})
p.a = 2  // bind `value` to `2`
p.a  // -> Get 'a' = 2

 定时器

requestAnimationFrame实现循环定时器

function setInterval(callback, interval) {
  let timer
  const now = Date.now
  let startTime = now()
  let endTime = startTime
  const loop = () => {
    endTime = now()
    if (endTime - startTime >= interval) {
      startTime = endTime = now()
      callback(timer)
    }
    timer = window.requestAnimationFrame(loop)
  }
  timer = window.requestAnimationFrame(loop)
  return timer
}

let a = 0
setInterval(timer => {
  console.log(1)
  a++
  if (a === 3) cancelAnimationFrame(timer)
}, 1000)

 修正setTimeout定时器

let period = 60 * 1000 * 60 * 2
let startTime = new Date().getTime()
let count = 0
let end = new Date().getTime() + period
let interval = 1000
let currentInterval = interval

function loop() {
  count++
  // 代码执行所消耗的时间
  let offset = new Date().getTime() - (startTime + count * interval);
  let diff = end - new Date().getTime()
  let h = Math.floor(diff / (60 * 1000 * 60))
  let hdiff = diff % (60 * 1000 * 60)
  let m = Math.floor(hdiff / (60 * 1000))
  let mdiff = hdiff % (60 * 1000)
  let s = mdiff / (1000)
  let sCeil = Math.ceil(s)
  let sFloor = Math.floor(s)
  // 得到下一次循环所消耗的时间
  currentInterval = interval - offset 
  console.log('时:'+h, '分:'+m, '毫秒:'+s, '秒向上取整:'+sCeil, '代码执行时间:'+offset, '下次循环间隔'+currentInterval) 

  setTimeout(loop, currentInterval)
}

setTimeout(loop, currentInterval)

 知识点

新特性:async/await

async/await 是 ES2016 新加入的特性,它提供了用 for、if 等代码结构来编写异步的方式。它的运行时基础是 Promise,面对这种比较新的特性,我们先来看一下基本用法。

async 函数必定返回 Promise,我们把所有返回 Promise 的函数都可以认为是异步函数。

async 函数是一种特殊语法,特征是在 function 关键字之前加上 async 关键字,这样,就定义了一个 async 函数,我们可以在其中使用 await 来等待一个 Promise。

function sleep(duration) {
    return new Promise(function(resolve, reject) {
        setTimeout(resolve,duration);
    })
}
async function foo(){
    console.log("a")
    await sleep(2000)
    console.log("b")
}

foo();  //a b

这段代码利用了我们之前定义的 sleep 函数。在异步函数 foo 中,我们调用 sleep。

async 函数强大之处在于,它是可以嵌套的。我们在定义了一批原子操作的情况下,可以利用 async 函数组合出新的 async 函数。

function sleep(duration) {
    return new Promise(function(resolve, reject) {
        setTimeout(resolve,duration);
    })
}
async function foo(name){
    await sleep(2000)
    console.log(name);
}

async function foo2(){
    await foo('a');
    await foo('b');
}

foo2(); //a b

 练习:我们现在要实现一个红绿灯,把一个圆形 div 按照绿色 3 秒,黄色 1 秒,红色 2 秒循环改变背景色。

(function (){
    var btn = document.getElementById('status');
    function sleep(duration){
        return new Promise(function(resolve,reject){
            setTimeout(resolve,duration);
        })
    }
    
    async function changeColor(duration,color){
        btn.style.background = color;
        await sleep(duration);
    };
    async function fn(){
        while(true){
            await changeColor(3000,'green');
            await changeColor(1000,'yellow');
            await changeColor(2000,'red');
        }
    } 
    fn();
})();

Promise里的代码为什么比setTimeout先执行实例

Promise和setTimeout实例

我们把宿主发起的任务称为宏观任务,把 JavaScript 引擎发起的任务称为微观任务。许多的微观任务的队列组成了宏观任务。

为了理解微任务始终先于宏任务,我们设计一个实验:执行一个耗时 1 秒的 Promise。

setTimeout(()=> console.log('d'),0);
var r = new Promise(function(resolve,reject){
    resolve();
});

r.then(() => {
    var begin = Date.now();
    while(Date.now() - begin < 1000);
    console.log("c1");
    
    new Promise(function(resolve,reject){
        resolve();
    }).then(() => console.log("c2"));
    
})
//c1 c2 d

这里我们强制了 1 秒的执行耗时,这样,我们可以确保任务 c2 是在 d 之后被添加到任务队列。

我们可以看到,即使耗时一秒的 c1 执行完毕,再 enque 的 c2,仍然先于 d 执行了,这很好地解释了微任务优先的原理。

再看一个复杂点的例子:

function sleep(duration) {
    return new Promise(function(resolve, reject) {
        console.log("b");
        setTimeout(resolve,duration);
    })
}
console.log("a");
sleep(5000).then(()=>console.log("c"));

//a b c

这是一段非常常用的封装方法,利用 Promise 把 setTimeout 封装成可以用于异步的函数。

我们首先来看,setTimeout 把整个代码分割成了 2 个宏观任务,这里不论是 5 秒还是 0 秒,都是一样的。

第一个宏观任务中,包含了先后同步执行的 console.log(“a”); 和 console.log(“b”);。

setTimeout 后,第二个宏观任务执行调用了 resolve,然后 then 中的代码异步得到执行,所以调用了 console.log(“c”),最终输出的顺序才是: a b c。

Promise 是 JavaScript 中的一个定义,但是实际编写代码时,我们可以发现,它似乎并不比回调的方式书写更简单,但是从 ES6 开始,我们有了 async/await,这个语法改进跟 Promise 配合,能够有效地改善代码结构。

JavaScript同步任务、异步任务(宏任务和微任务)

同步任务: 当前主线程将要消化执行的任务,这些任务一起形成执行栈。
异步任务: 不进入主线程,而是进入任务队列,即不会马上进行的任务。

任务队列的异步任务又分为宏任务与微任务,也就是说宏任务和微任务虽然都是异步任务,都在任务队列中,但是它们在不同的队列中。

一般情况下,宏任务包含以下内容:script,setTimeout setInterval, I/O, 事件,postMessage,setImmediate, requestAnimationFrame, UI渲染。

微任务包含以下内容: Promise.then, MutationObserver, process.nextTick

示例:

console.log('start here');

setTimeout(() => {
    console.log('setTimeout');
}, 0);

new Promise((resolve, reject) => {
    resolve('promise result');
}).then(value => {
    console.log(value);
});

console.log('end here');

输出结果:
start here // 同步任务
end here // 同步任务
promise result // 微任务
setTimeout // 宏任务

可以看出是先执行同步任务,然后执行微任务,然后执行宏任务。但是由于先执行script,浏览器会先执行一个宏任务,再执行同步任务,如果有异步代码的话才会先执行微任务。

 地址:《【Javascript基础】宏任务,微任务解析》

posted @ 2018-10-29 20:22  风雨后见彩虹  阅读(775)  评论(0编辑  收藏  举报