Coder Social home page Coder Social logo

blog's Introduction

  • 👋 Hi, I’m @ahaow
  • 👀 I’m interested in ...
  • 🌱 I’m currently learning ...
  • 💞️ I’m looking to collaborate on ...
  • 📫 How to reach me ...

blog's People

Contributors

ahaow avatar

Stargazers

 avatar  avatar  avatar

Forkers

aihao1024

blog's Issues

js 动态加载 / 移除 script 与 link 文件

js 动态加载 / 移除 script 与 link 文件

使用场景

在 知乎日报 获取 新闻详情的 数据中,例如:https://news-at.zhihu.com/api/4/news/9710258

数据格式:

css样式表是给以链接的方式给前端的,这就需要我们动态创建script 或者 link标签来引入 css 或者 js

css动态引入/移除

// 引入

function loadCss(href) {

	var link = document.createElement("link");
	link.setAttribute("rel","stylesheet");
	link.setAttribute("type","text/css");
	link.setAttribute("href",href);

	var heads = document.getElementsByTagName("head");

	if(heads.length) {
		heads[0].appendChild(link);
	} else {
		document.documentElement.appendChild(link);
	}
}


// 移除

function removeCss(href) {
	var links = document.getElementByTagName("link");
	for(var i = 0; i < links.length; i++ ) {
		if(links[i] && links[i].href && links[i].href.indexOf(href) != -1) {
			links[i].parentNode.removeChild(links[i]);
		}
	}
}

动态创建style方式

function addCssByStyle(cssString){ 
    var doc=document; 
    var style=doc.createElement("style"); 
    style.setAttribute("type", "text/css"); 
   
    if(style.styleSheet){// IE 
        style.styleSheet.cssText = cssString; 
    } else {// w3c 
        var cssText = doc.createTextNode(cssString); 
        style.appendChild(cssText); 
    } 
   
    var heads = doc.getElementsByTagName("head"); 
    if(heads.length) 
        heads[0].appendChild(style); 
    else 
        doc.documentElement.appendChild(style); 
} 

js动态引入/移除

// 引入

function loadScript(src) {

	var addSign = true;

	var script = document.getElementsByTagName("script");

	for(var i = 0; i < script.length; i++) {
		if(script[i] && script[i].src && script[i].src.indexOf(src) != -1) {
			return addSign = false;
		}
	}
	if(addSign) {
		var newScript = document.createElement("script");
		newScript.setAttribute("type","text/javascript");
		newScript.setAttribute("src",src);
		document.getElementsByTagName("head")[0].appendChild(newScript);
	}
}

// 移除

function removeScript(src) {

	var scripts = document.getElementsByTagName("script");
	for (var i = 0; i < scripts.length; i++) {
        if (scripts[i] && scripts[i].src && scripts[i].src.indexOf(src) != -1) {
            scripts[i].parentNode.removeChild(scripts[i]);
        }
    }

}

js clientHeight offsetHieght scrollHeight 以及 scrollTop offsetTop clientTop 的区别

  • clientHeight 表示的是可视区域的高度,不包含border和滚动条;
  • offsetHeight 表示的是可视区域的高度,包含了border和滚动条;
  • scrollHeight 表示了所有区域的高度,包含了滚动条隐藏的部分;
  • clientTop 表示边框border的厚度,包含了因为滚动条被隐藏的部分;
  • offsetTop 表示距离父盒子的高度,没有父盒子,最高等级就是body;
  • scrollTop 滚动后被隐藏的高度,获取对象相对于由offsetParent属性指定的父坐标(css定位的元素或者body元素)距离顶端的高度;
<style>
    div {
        width: 200px;
        height: 200px;
        padding: 10px;
        border: 2px solid #e0e0e0;
        background: red;
    }
</style>
<div></div>

div.clientHeight  // 220;  因为box-sizing: content-box; 
div.offsetHeight  // 224 宽度 + padding + border
div.scrollHeight  // 根据滚动条

div.clientTop // 2 border的厚度
div.offsetTop // 8 根据父盒子来,没有父盒子 就是body
div.scrollTop // 0 

HTML/CSS小技巧

HTML/CSS小技巧

清除浮动

.clearfix {
	zoom: 1;
}

.clearfix::after {
	content: "";
	clear: both;
	display: block;
	visibilty: hidden;
	height: 0
}

控制文本大小

text-transform:

	none // 默认
	caoitailze // 文本中的每一单词以大写字母开头
	lowercase  // 定义无大写字母,仅有大写字母
	uppercase // 定义仅有大写字母
	inherit // 规定从父元素继承text-transform属性的值

文本溢出 出现省略号

单行文本:

text-overflow: ellipsis; // 出现省略号
overflow: hidden;
white-space: nowrap; // 禁止换行

第一种多行文本:

overflow: hidden;
text-overflow: ellipsis; 
display: -webkit-box; // 必须结合的属性,将对象作为弹性伸缩盒子模型显示;
-webkit-line-clamp: 2; // 可以用来多行文本的情况下,用省略号“...”隐藏超出范围的文本;
-webkit-box-orient: vertical; // 必须结合的属性,设置或检索伸缩对象的子元素的排列方式;

第二种多行文本:

p {
	
	position: relative;
	line-height: 1.4em;
	height: 4.2em;
	overflow: hidden;
}

p::after {
	content: "...";
	font-weight: bold;
	position: absolute;
	bottom: 0;
	right: 0;
	padding:0 20px 1px 45px;
    background:url(http://newimg88.b0.upaiyun.com/newimg88/2014/09/ellipsis_bg.png) repeat-y;
}

第三种多行文本:

  • 使用split + 正则表达式将单词与单个文字切割出来为一个数组
  • 加上'...'
  • 判断scrollHeight和clientHeight,超过的话就将数组pop一个出来
<style>
  #box {
     width: 200px;
     height: 80px;
     background: pink;
  }
</style>

<div id='box'>
扭扭捏捏扭扭捏捏扭扭捏捏扭扭捏捏扭扭捏捏扭扭捏捏扭扭捏捏扭扭捏捏男男女女你爱上大搜活动请问我核对
</div>

<script>
     var box = document.getElementById("box");
     var words = box.innerHTML.solit(/(?<=[\u4e00-\u9fa5])|(?<=\w*?\b)/g)
     while(box.scrollHeight > box.clientHeight) {
           words.pop;
           box.innerHTML = words.join('') + '...';
     }
</script>

第四种多行文本:

clamp.js 地址

css改变滚动条样式

/*定义滚动条宽高及背景,宽高分别对应横竖滚动条的尺寸*/
::-webkit-scrollbar {
    width: 5px;
    height: 5px;
    background-color: rgba(245, 245, 245, 0.47);
}

/*定义滚动条的轨道,内阴影及圆角*/
::-webkit-scrollbar-track {
    -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .3);
    border-radius: 10px;
    background-color: #f5f5f5;
}

/*定义滑块,内阴影及圆角*/
::-webkit-scrollbar-thumb {
    /*width: 10px;*/
    height: 20px;
    border-radius: 10px;
    -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .3);
    background-color: rgba(85, 85, 85, 0.25);
}

clipboard

clipboard.js 是一个在浏览器上 拷贝文字 的一个库

ios上,绑定 属性,方法的元素 不能是 div , 要是用 button 标签

js 滚动底部

判断内容滚动到底部,需要知道的信息;

  • 1、内容区域的真实(也就是滚动区域)scrollHeight;
  • 2、滚动条距离顶部的位置 scrollTop;
  • 3、内容区域的可见高度 clientHeight;
  • 4、判断元素是否滚动到底部 e.clientHeight + e.scrollTop = scrollHeight;

兼容写法

// 获取scrollTop  

function getScrollTop () {
  var scrollTop = 0,bodyScrollTop = 0, documentScrollTop = 0
  if(document.body) {
    bodyScrollTop = document.body.scrollTop;
  }
  if(document.documentElement) {
    documentScrollTop = document.documentElement.scrollTop;
  }

  scrollTop = (bodyScrollTop - documentScrollTop  >  0)  ?  bodyScrollTop :  documentScrollTop;
  return scrollTop;
}

文档的总高度

function getScrollHeight() {
  let scrollHeight = 0,bodyScrollHeight = 0,documentScrollHeight = 0;
  if(document.body) {
     bodyScrollHeight = document.body.scrollHeight;
  }
  if(documentScrollHeight) {
     documentScrollHeight = document.documentElement.scrollHeight;
  }
  scrollHeight = (bodyScrollHeight - documentScrollHeight > 0) ? bodyScrollHeight : 
  documentScrollHeight;
  return scrollHeight;
}

内容区域可见高度

function getWindowHeight() {
    let windowHeight = 0;
    if(document.compatMode == "CSS1Compat") {
        windowHeight = document.documentElement.clientHeight;
    } else {
        windowHeight = document.body.clientHeight;
    }
    return windowHeight;
}

回到底部

dom.addeventlistener('click',function() {
  window.scrollTo(0,document.documentElement.scrollHeight);
})

回到顶部

// 定时器又有 setInterval、setTimeout 和 requestAnimationFrme 这三种使用
// 下面使用性能最好的定时器requestAnimationFrame来实现

let timer = null;

btn.addEventlistener('click',function() {
   cancelAnimationFrame(timer);
   timer = requestAnimationFrame(function fn() {
      let oTop = document.body.scrollTop  ||  document.documentElement.scrollTop;
      if(oTop > 0) {
         /* 三种方法 */
        document.body.scrollTop = document.documentElement.scrollTop = oTop - 50;
        // window.scrollTo(0,oTop - 50)
        // window.scrollBy(0,-50)
        timer = requestAnimationFrame(fn);
      } else {
         cancelAnimationFrame(timer)
      }
   })
})

判断是否底部

window.onscroll = function () {
   if(Math.ceil(getScrollTop) + getWindowHeight() === getScrollHeight()) {
        console.log('到底了')
    }
}

关于自己使用React开发微信公众号的一些坑和解决方法

最近在工作中,接连到两个项目都是微信公众号网页开发,起初觉得没有太大难度,因为也就是一些微信页面而已,可是在实际的开发中还是遇到了几个让自己抓狂的坑。

1.关于授权

在react中我自己结合项目的做法是把授权做到了加载这个组件中(顾名思义是显示加载的页面),附上代码‘

`

getUserinfo() {
    const url = localStorage.getItem(“url”)
    if(url) {
        const getcode = GetRequ(window.location.search);
        const _props = JSON.parse(localStorage.getItem('_ props'));
        let code = getcode.code;
        axios.get(https: //www.xxx.com/xxx/xxx/xxx?code=${code})。then(res => { 
        window.localStorage.setItem('userinfo',JSON.stringify(res.data.data))window.localStorage.removeItem('url')alert(res.data);
        setTimeout(() = >{
            if(_props.location.state.pathname === '/ single') {
                this.props.history.replace('/ single')
            } else if(_props.location.state.pathname = =='/ setmeal') {
                this.props。
            } else if(_props.location.state.pathname === '/ user') {
                this.props.history.replace('/ user')
            } else if(_props.location.state.pathname === '/ activity ') {
                this.props.history.replace('/ activity')
            }
        },1000)
    }).
    catch(err = >{
        if(err) {
            alert('登录失效,重新登录');
            localStorage.removeItem('userinfo');
            localStorage.removeItem('_ props');
            localStorage.removeItem('url');
            this.props.history.replace('/ user');
        }
    })
} else {
    window.location.href = ' https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxea312fb8dd46a9f8&redirect_uri=http://www.xxx.com/xxx/xxxx&RESPONSE_TYPE =代码&范围= snsapi_userinfo&状态=状态#wechat_redirect'localStorage.setItem('_ props',JSON.stringify(this.props))localStorage.setItem(“url”,window.location.href);
}

`

  1. 首先在url地址中redirect_uri对应的地址必须要是你反打打包后放上去的地址。
  2. 然后授权后,出现xxx.com?code=xxx获取到了代码,并将url存储到本地
  3. 我是根据本地存储是否有url来进行判断用户是否授权登录了的
  4. 我还存储了一个react的道具,这个主要是用来根据用户第一次进入页面是从哪个页面进来的(结合自己项目,比如是:单品页面,套餐页面,用户页面); 之所以要在其他,也就是授权链接那里存,是因为我调试的时候,点击登录刷新页面在if if判断里面打印props的location.state是undefined;
  5. 然后根据后台给的接口,我将代码发送过去,得到用户信息userinfo,并且存在本地。
  6. !!这个问题是很久就出现了,但一直没有太重视的问题。后台如果将我在用户列表中删去后,苹果手机打开后用户就会一直在载页面,然后没有反应。但安卓可以直接进入,然后我在授权的请求中警报(错误); 打印错误的参数给我报了错误500; 之所以会这样是我自己认为存储的那个url中的代码过期了,而一直请求获取用户信息的是我存在本地的url中的代码,所以在err的回调函数中,我才会删除url这个本地存储;删除之后重新走了一次授权登录,就可以了 - - (但我认为这不是最好的解决办法);

2.关于使用js-jdk

`

let href = window.location.href;
let url2 = encodeURIComponent(href.split('#') [0]);
axios.get(https: //www.xxx.com/index/access/access?url=${url2})。then(res => { 
	console.log(res)window.wx.config( {
    	debug:false,appId:res.data.appId,timestamp:res.data.timestamp,nonceStr:res.data.nonceStr,signature:res.data.signature,jsApiList: ['checkJsApi','scanQRCode','chooseWXPay','getLocation']
	})
}).
catch(err = >{
    console.log(err);
})

`

  1. 使用hashRouter。
  2. 使用微信js-jdk,什么步骤啊,引入什么的,不需要写了,文档已经很详细了
  3. 在获取权限验证配置的时候,ajax要发送一个当前的url地址给后台,结果老是报错,获取签名失效,试了很多次都不行,后面才发现需要的是动态地址,让url = window.location.href; 在发给后台就获取成功了
  4. 使用hashRouter我们发送的url必须encodeURIComponent(href.split('#')[0]); 进行处理
  5. 老问题:就是调用微信支付或者其他微信方法时,也会报错误500; 也是通过让用户重新登录一次就可以继续使用了(但我认为这不是最好的解决办法)。

小结:其他微信就是授权和jdk这里比较麻烦,其他的都和我们平时正在开发一样..

js 编写原生插件方式

js 编写原生插件

1、

整个插件写在一个立即执行函数里,也可以称为是一个闭包;保证里面的变量不会与外界互相影响,头部的win,doc都是底部window,document的映射,也可以将win,doc理解为函数的形参,window,document为实参,后面的undefined可以不写,写了的话表示保证再出现的undefined是未定义的意思;不被其他赋值;分号则是为了防止js文件合并,可能会发生语法混淆;

;(function(win,doc,undefined){


} (window,document))

2、

unction前的 +(加号或者 ! ~);可以把function 直接转换为可执行的;和用括号括起来一样的作用, 因为!+-()这些符号的运算符是最高的,所以会把其后或者其中的当做表达式处理;故函数会自执行,而直接写function(){}() 会报错,function(){}只是匿名函数声明;而写 var b = function(){}; b()是可以的

+function(){   }()
!function(){   }()
~function(){   }()
viod function(){   }()
(function(){   })()
(function(){   }())

需求

滚动到 页面高度 1400px时,显示出文本 “返回顶部” 的div,并且点击之后滚动到底部;

; (function(win, doc) {
    //开启严格模式,规范代码,提高浏览器运行效率
    "use strict";

    //定义一个类,通常首字母大写
    var Render = function(dom) {

        // 插件名,调用的时候直接new一下插件名就行了并传参数或者传对象
        //(一般这个函数名手写字母大写比较好,构造函数嘛,其实也是函数)        
        //很明显我要传id名;这里传什么都可以的其实; 
        this.dom = document.getElementById(dom);

        //为什么把获取的id传给this.div呢?this的指向为调用的实例;
        //我们此时姑且认为this就指向这个函数;因为这样我们之后再想获取这个
        //div就可以直接用this.div了好吗;而不是在document.getElementById(。。。。) 

        this.dom.style.width = "50px";
        this.dom.style.height = "50px";
        this.dom.style.backgroundColor = "red";
        this.dom.style.position = "fixed";
        this.dom.style.right = "20px";
        this.dom.style.bottom = "20px";
        this.dom.innerHTML = "返回顶部";
        this.dom.style.fontSize = "12px";
        this.dom.style.display = "none";

        //你也可以写一些其他的默认的东西;比如默认的变量啦;方便下面调用;
        //这里写了什么都不会报错;只是有用没用的问题这行可以忽略    
        this.num = 0;
        this.author = "lianxiaozhuang";

        // 执行的函数
        this.scroll();
        this.goTop();
    }

    // 给构造函数render对象原型里添加属性(方法)   
    render.prototype = {
        //给函数写方法;这里可能不止一个函数;你还记得你在全局里写一个个
        //的function吗;贼乱找也不好找;把一个个函数都写到对象的属性里;
        //调用函数就直接调用对象的属性; 
        constructor: render,

        //构造器指向构造函数;这行其实不写没啥毛病;不过防止构造器
        //指向Object的情况;你还是装逼写上吧; 因为当 addHtml.prototype = {}
        //的时候;把一个字面量的对象付给了他的原型上,而{}是由Object产生;
        //故此时addHtml的prototype指向了Object,所以要从新指向

        // 获取scrollTop的兼容写法
        getScrollTop: function() {
            let scrollTop = document.documentElement ? document.documentElement.scrollTop: document.body.scrollTop;
            console.log(scrollTop);
            return scrollTop;
        },

        // scroll滚动监听
        scroll: function() {
            let _self = this; // 把this保存下来防止在局部函数内部取不到(局部函数内部取得this可能
            是别的)window.onscroll = function() {
                let scrollTop = _self.getScrollTop();
                if (scrollTop > 1200) {
                    _self.dom.style.display = "block";
                } else {
                    _self.dom.style.display = "none";
                }
            }

        },

        // 点击返回顶部
        goTop: function() {
            let _self = this;
            let timer = null;
            _self.dom.addEventListener("click",
            function() {
                console.log(66) cancelAnimationFrame(timer);
                timer = requestAnimationFrame(function fn() {
                    let oTop = document.body.scrollTop || document.documentElement.scrollTop;
                    if (oTop > 0) {
                        document.body.scrollTop = document.documentElement.scrollTop = oTop - 100;
                        timer = requestAnimationFrame(fn);
                    } else {
                        cancelAnimationFrame(timer);
                    }
                })
            })
        }

    }

    window.Render = Render ; //把这个对象附给window底下的 名字叫Render 的对象;要不你调用的时候 ,new Render () 怕在window的环境下找不到; 

} (window, document))
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
        <style>
            body {
                height: 2000px;
            }
        
        </style>
    </head>
    <body>
        <div id="scrollTop"></div>
        <script src="./2.js"></script>
        <script>
            let a = new render("scrollTop");
			//这里是实例调用插件的代码    
			//是不是明白为什么要写插件了;要封装;两个相同组件即使有相同的class名在dom操作的时候
			//也不会相互冲突;因为他们都new出来了个自的实例;有自己的this;有自己的一套方法了
			//(其实方法都在原型里是公用的;操作各自的dom)
        </script>

    </body>
</html>
	

3、具有安全作用域的构造函数

function Fn(name){
   this.a = name;
}
Fn.prototype = {
    constructor:Fn,
    getF:function(){
        console.log(1);
    }
}
new Fn();
//new 出来的Fn就是一个构造函数
//倘若有人忘记写new,直接调用Fn();此时var p = Fn();可见实例p没有getF的方法;
//为了解决这种问题;引入了安全作用域的构造函数概念

function Fn(name){
  if(!(this instanceof Fn)){
      //只要不是new的,用Fn()直接调用的,这里的this绝对不指向Fn;让它从
      //新new一下;直到下一次代码走else里的内容,故把this.a 放到了else里
      return new Fn(name)
  }else{
      this.a = name;
  }
}
Fn.prototype = {
    constructor:Fn,
    getF:function(){
        console.log(1);
    }
}

4、默认参数

//我们用过一些插件,是可以传一些配置参数的,当然如果你不传的话它有默认的值,
 function Fn(params){
    var defaults = {
        width:100,
        color:"#000"
    };
    var params = params||{}; 
    for (var x in defaults) {
        if (typeof params[x] === 'undefined') {
            params[x] = defaults[x];
            //对于使用时,没有设置的参数;用默认参数代替
        }  
    }
    this.params= params;//得到的this.params,在方法中调用;
   
 }
 Fn.prototype = {

 }

5、方法到底写到this里还是prototype 里

function Fn(){
    this.getC = function(){
        //...
    }
}

function Fn(){}
Fn.prototype.getC = function(){}
//大家可能都会有这样的纠结,getC到底写到this里还是原型里
//这要从内存说起;写到原型上,每执行一个实例,getC不需要开辟新的内存
//故,可以把一些纯计算的方法,写原型上,如果方法和实例本身有关,应该写道this中

6、方法名防止冲突处理

//如果在引入你的插件之前,window下已经有Fn的变量;怎么办,你如果这么搞; 岂不是把别人写的Fn搞掉了
//此时应该把Fn的控制权交出,自己用Fn2输出
(function(){
    var Fn=function(){
            console.log(000)
        }
        Fn.prototype =  {
            
        }

        if(window.Fn){
           // throw Error("Fn已经存在,请使用Fn2")
            window.Fn2 = Fn	;
           
        }else{
                window.Fn2 = Fn	;
        }
}())

7、兼容 CommonJS 和 AMD/CMD 规范

//兼容CommonJs规范
if (typeof module === 'object' && module && module.exports) { module.exports =Render }

//兼容AMD/CMD规范
if (typeof define === 'function') define(function() { return MyPlugin; });


//注册全局变量,兼容直接使用script标签引入该插件
global.MyPlugin = MyPlugin;
 
//this,在浏览器环境指window,在nodejs环境指global
//使用this而不直接用window/global是为了兼容浏览器端和服务端
//将this传进函数体,使全局变量变为局部变量,可缩短函数访问全局变量的时间

8、完整写法

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
        <style>
            body {
                height: 2000px;
            }
        
        </style>
    </head>
    <body>
        <div id="scrollTop"></div>
        <script src="./2.js"></script>
        <script>
            let a = Render("scrollTop", {
                width: "50px",
                height: "50px",
                backgroundColor: "pink"
            });
        </script>

    </body>
</html>
;(function (global, doc) {
    //开启严格模式,规范代码,提高浏览器运行效率
    "use strict";

    var Render = function (dom,params) {
        // 具有安全作用域的构造函数
        if(!(this instanceof Render)) {
            return new Render(dom,params)
        } else {
            // 默认参数
            var defaults = {
                width: "100px",
                height: "100px",
                backgroundColor: "green"
            }
            var params = params || {}; 
            for(var x in defaults) {
                if(typeof params[x] === 'undefined') {
                    params[x] = defaults[x];
                }
            }
            this.params = params;
            this.dom = document.getElementById(dom);
            this.dom.style.width = this.params.width;
            this.dom.style.height = this.params.height;
            this.dom.style.backgroundColor = this.params.backgroundColor;
            this.dom.style.position = "fixed";
            this.dom.style.right = "20px";
            this.dom.style.bottom = "20px";
            this.dom.innerHTML = "返回顶部";
            this.dom.style.fontSize = "12px";
            this.dom.style.display = "none";
            this.scroll();
            this.goTop();
        }
        
    }

    Render.prototype = {
        constructor: Render,

        getScrollTop: function () {
            let scrollTop = document.documentElement ? document.documentElement.scrollTop : document.body.scrollTop;
            console.log(scrollTop);          
            return scrollTop;
        },

        scroll: function () {
            let _self = this;
            window.onscroll = function () {
                let scrollTop = _self.getScrollTop();
                if(scrollTop > 1200) {
                    _self.dom.style.display = "block";
                } else {
                    _self.dom.style.display = "none";
                }
            }
        },
        aa() {
            console.log(6666666)
        },

        goTop: function() {
            let _self = this;
            let timer = null;
            _self.dom.addEventListener("click",function() {
                console.log(66)
                cancelAnimationFrame(timer);
                timer = requestAnimationFrame(function fn() {
                    let oTop = document.body.scrollTop || document.documentElement.scrollTop;
                    if(oTop > 0) {
                        document.body.scrollTop = document.documentElement.scrollTop = oTop - 100;
                        timer = requestAnimationFrame(fn);
                    } else {
                        cancelAnimationFrame(timer);
                    }
                })
            })
        }
    }
    // 兼容 CommonJs
    if (typeof module === 'object' && module && module.exports) { module.exports = Render }
    // 兼容 AMD/CMD
    if (typeof define === 'function') define(function() { return MyPlugin; });
    // 防止命名冲突
    if(global.Render) {
        global.Render2 = Render;
    } else {
        global.Render = Render;
    }

    // this
}(this, document))

懒加载与预加载

懒加载

1.1 什么懒加载

懒加载就是延迟加载

当访问一个页面的时候,先把img元素或是其他元素的背景图片路径替换成一张大小为1*1px图片的路径(这样就只需请求一次,"占位图"), 只有当图片出现在浏览器的可视区域内时,才设置图片真正的路径,让图片显示出来

1.2 为什么使用懒加载

很多页面,内容很丰富,页面很长,图片较多。比如说各种商场页面,这些页面图片数量多,而且比较大,要是页面载入就一次性加载完毕,会花费很多时间

1.3 懒加载原理

页面中的img元素,如果没有src属性,浏览器就不会发出请求去下载图片,只有通过JavaScript设置了图片路径,浏览器才会发送请求

懒加载的原理就是先在页面中把所有的图片统一使用一张占位图进行占位,把真正的路径存在元素的 data-url (这个名字起个自己认识好记的就行了)属性中,要用的时候就取出来,再设置

1.4 懒加载实现步骤

  • 首先,不要将图片地址放在src属性中,而是放在其他属性(data-origin)中
  • 页面加载完成后,根据scrollTop判断图片是否在用户的视野内,如果在,则将data-original属性中的值取出来存放在src属性中
  • 在滚动事件中重复判断图片是否进入视野,如果进入,则将data-origin属性的值取出来存放在src属性中

1.5 懒加载的优点

页面加载速度快,可以减轻服务器的压力,节约了流量,用户体验好

1.6 代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style type="text/css" media="all">
        body,
        ul,
        li {
            padding: 0;
            margin: 0;
        }

        body {
            font-size: 12px;
            -webkit-user-select: none;
            -webkit-text-size-adjust: none;
            font-family: helvetica;
        }

        .lazyLoad ul li {
            height: 200px;
            border: 1px solid red;
            margin-bottom: 2px;
            overflow: hidden;
            text-align: center;
        }
    </style>
</head>

<body>
    <div class="lazyLoad">
        <ul>
            <li><img data-src="http://img0.imgtn.bdimg.com/it/u=2873269578,797009742&fm=26&gp=0.jpg" alt=""></li>
            <li><img data-src="http://img0.imgtn.bdimg.com/it/u=2873269578,797009742&fm=26&gp=0.jpg" alt=""></li>
            <li><img data-src="http://img0.imgtn.bdimg.com/it/u=2873269578,797009742&fm=26&gp=0.jpg" alt=""></li>
            <li><img data-src="http://img0.imgtn.bdimg.com/it/u=2873269578,797009742&fm=26&gp=0.jpg" alt=""></li>
            <li><img data-src="http://img0.imgtn.bdimg.com/it/u=2873269578,797009742&fm=26&gp=0.jpg" alt=""></li>
            <li><img data-src="http://img0.imgtn.bdimg.com/it/u=2873269578,797009742&fm=26&gp=0.jpg" alt=""></li>
            <li><img data-src="http://img0.imgtn.bdimg.com/it/u=2873269578,797009742&fm=26&gp=0.jpg" alt=""></li>
            <li><img data-src="http://img0.imgtn.bdimg.com/it/u=2873269578,797009742&fm=26&gp=0.jpg" alt=""></li>
            <li><img data-src="http://img0.imgtn.bdimg.com/it/u=2873269578,797009742&fm=26&gp=0.jpg" alt=""></li>
            <li><img data-src="http://img0.imgtn.bdimg.com/it/u=2873269578,797009742&fm=26&gp=0.jpg" alt=""></li>
            <li><img data-src="http://img0.imgtn.bdimg.com/it/u=2873269578,797009742&fm=26&gp=0.jpg" alt=""></li>
            <li><img data-src="http://img0.imgtn.bdimg.com/it/u=2873269578,797009742&fm=26&gp=0.jpg" alt=""></li>
            <li><img data-src="http://img0.imgtn.bdimg.com/it/u=2873269578,797009742&fm=26&gp=0.jpg" alt=""></li>
            <li><img data-src="http://img0.imgtn.bdimg.com/it/u=2873269578,797009742&fm=26&gp=0.jpg" alt=""></li>
            <li><img data-src="http://img0.imgtn.bdimg.com/it/u=2873269578,797009742&fm=26&gp=0.jpg" alt=""></li>
            <li><img data-src="http://img0.imgtn.bdimg.com/it/u=2873269578,797009742&fm=26&gp=0.jpg" alt=""></li>
            <li><img data-src="http://img0.imgtn.bdimg.com/it/u=2873269578,797009742&fm=26&gp=0.jpg" alt=""></li>
            <li><img data-src="http://img0.imgtn.bdimg.com/it/u=2873269578,797009742&fm=26&gp=0.jpg" alt=""></li>
            <li><img data-src="http://img0.imgtn.bdimg.com/it/u=2873269578,797009742&fm=26&gp=0.jpg" alt=""></li>
        </ul>
    </div>

    <script>
        
        function lazyLoad(obj, option) {
            this.init.apply(this, arguments)
        }

        lazyLoad.prototype = {
            init: function (el, param) {
                let opt = {
                    dataSrc: 'lazyImg',
                    dataImg: './11.jpg', // 占位图
                    contens: typeof el === 'string' ? this.getElement(el) : el || document.body, // 判断 el 传进来的是 dom对象 还是 string
                    cb: null 
                }

                if (param && param instanceof Object) {
                    for (let key in param) {
                        if (param.hasOwnProperty(key)) {
                            opt[key] = param[key]
                        }
                    }
                }
                this.opt = opt
                console.log(this.opt)
                this.render()
                this.bindUI()
            },
            render: function () {
                let _opt = this.opt,
                    _imgArr = [],
                    _dom = _opt.contens,
                    // 获取到传进来的dom对象 里面所有的 img 元素
                    _img = _dom.querySelectorAll('['+ _opt.dataSrc +']');
                if(_img.length) {
                    let _tagName
                    Array.prototype.forEach.call(_img,function(item,i) {
                        console.log(item.tagName);
                        _tagName = item.tagName.toLowerCase() // 将 元素 的 tagName 小写化
                        if(_tagName === 'img' && !item.getAttribute('src')) {
                            item.setAttribute('src',this.opt.dataImg); // 将img元素的src的值 设为 传进来的 占位图
                        }
                        _imgArr.push({
                            obj: item,
                            tagName: item.tagName.toLowerCase(),
                            src: item.getAttribute(_opt.dataSrc)
                        })

                    }.bind(this));
                }
                this.imgArr = _imgArr
                this.loadImg()
            },
            bindUI: function() {
                window.addEventListener('scroll',function() {
                    this.loadImg()
                }.bind(this),false)
            },
            loadImg: function() {
                let _imgArr = this.imgArr;
                let _dataSrc , _src;
                if(_imgArr.length) {
                    _imgArr.forEach(function(item,i) {
                        // console.log(item)

                        if(this.isLoad(item.obj)) {  // 判断 该图片是否进入了 可视区域
                            // 如果是图片
                            if(item.tagName === 'img') {
                                _dataSrc = this.opt.dataSrc
                                _src = item.obj.getAttribute(_dataSrc)

                                console.log(_dataSrc);
                                console.log(_src); // img 下 data-src 属性下的值

                                if(_src) {
                                    console.log(item)
                                    item.obj.setAttribute('src',_src); // 将 data-src 的值 赋值给 img 下的 src 属性中
                                    item.obj.removeAttribute(_dataSrc); // 删除 data-src 属性
                                    if(typeof this.opt.cb === 'function') {
                                        return this.opt.cb.call(this,item.obj)
                                    }
                                }
                            } else {
                                // 加载文件
                                if(typeof this.opt.cb === 'function') {
                                    if(_imgArr[i].isLoad) {
                                        return false
                                    }
                                }
                                _imgArr[i].isLoad = 1;
                                return this.opt.cb.call(this,item.obj)
                            }
                        }
                    }.bind(this))
                }
            },
            isLoad: function(img) {
                let _scrollTop = document.body.scrollTop || document.documentElement.scrollTop
                let _winHeight = window.innerHeight
                let _offsetTop = img.offsetTop
                let _height = img.offsetHeight
                if(_offsetTop + _height >= _scrollTop && _offsetTop <= _winHeight + _scrollTop) {
                    /**
                        _offsetTop + _height >= _scrollTop 
                        img图片的距离可视区域的高度 + 图片的高度 大于等于 浏览器窗口顶部与文档顶部之间的距离 也就是滚动条滚动的距离

                        _offsetTop <= _winHeight + _scrollTop
                        img图片的距离可视区域的高度 小于等于  屏幕可视窗口大小 + 浏览器窗口顶部与文档顶部之间的距离 也就是滚动条滚动的距离
                    */
                        return true
                }
                return false
            },
            getElement: function(o) {
                return document.querySelector(o)
            } 
        }

        let loadPic = function (obj, option) {
            if (!obj) return
            return new lazyLoad(obj, option)
        }
        

        var t = loadPic('.lazyLoad',{
            dataSrc : 'data-src',
            cb : function(item){
                // console.log(item)
            }
        })
    </script>
</body>
</html>

预加载

1.1 什么是预加载

提前加载图片,当用户查看时可以直接从本地缓存中渲染

1.2 为什么要使用预加载

图片预先加载到浏览器中,访问者便可顺利地在你的网站上冲浪,并享受到极快的加载速度。这对图片画廊及图片占据很大比例的网站来说十分有利,它保证了图片快速、无缝地发布,也可帮助用户在浏览你网站内容时获得更好的用户体验。

2.3 实现预加载

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        #preload_01 img {
            width: 100px;
            height: 100px;
            margin: 8px;
        }
    </style>
</head>

<body>
    <div id="preload_01"></div>
    <script>
        function preload() {
            let images = new Array()
            for (let i = 0; i < preload.arguments.length; i++) {
                images[i] = new Image()
                images[i].src = preload.arguments[i]
                document.getElementById('preload_01').appendChild(images[i]);
            }
        }
		// 使用addLoadEvent()函数来延迟preloader()函数的加载时间,直到页面加载完毕。
        function addLoadEvent(func) {
            var oldonload = window.onload;
            if (typeof window.onload != 'function') {
                window.onload = func;
            } else {
                window.onload = function () {
                    if (oldonload) {
                        oldonload();
                    }
                    func();
                }
            }
        }
        addLoadEvent(preload(
            "http://img4.imgtn.bdimg.com/it/u=1688026885,2773767715&fm=26&gp=0.jpg",
            "http://img4.imgtn.bdimg.com/it/u=1688026885,2773767715&fm=26&gp=0.jpg",
            "http://img4.imgtn.bdimg.com/it/u=1688026885,2773767715&fm=26&gp=0.jpg"
        ))
    </script>
</body>
</html>

懒加载和预加载的对比

  1. 概念
    1. 懒加载也叫延迟加载:js图片延迟加载,延迟加载图片或符合某些条件时才加载某些图片
    2. 预加载:提前加载图片,当用户需要查看时可以直接从本地缓冲中渲染
  2. 区别:
    1. 两种技术的本质:两者行为是相反的,一个是提前加载,一个是迟缓甚至不加载
    2. 懒加载对服务器端有一定的缓解压力,预加载则会增加服务器端压力
  3. 意义:
    1. 懒加载的主要目的是作为服务器端的优化,减少请求或延迟请求数
    2. 预加载可以说是牺牲服务器前端性能,换取更好的用户体验,这样可以使用户的操作得到最快的反映。

music-app 关于 audio

music-app 关于 audio

关于audio获取播放源时长

let audio = document.getElememtById("audio");
// load() 方法用于在更改来源或其他设置后对音频/视频元素进行更新。

audio.load();

// 更改后需要重新加载 audio 元素,在 audio 元素加载完成后 (oncanplay),此时才能获取到正确的 duration 值
// 当浏览器可以播放音频/视频时
audio.oncanplay = () => {
	 console.log(audio.duration);
}

js实现播放器与歌词同步

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<style>
		.lyric_area{/*歌词显示区域*/
			height: 400px; /*歌词区域高度*/
			overflow: hidden; /*隐藏超出部分*/
			margin-top: 15px;
		}
		#lyricdom {/*歌词列表*/
			line-height: 30px;/*行高,这个值要用在歌词滚动距离上*/
			transition-duration: 600ms;/*滚动速度*/
			list-style: none;
		 }
		#lyricdom .lineHigh{/*高亮行*/
			color: red;
		}


	</style>
</head>
<body>
	<!-- 播放器 -->
	<audio id="audio" src="http://m10.music.126.net/20190522143350/fc2178e33d6b4078e91fd1e15620ec82/ymusic/e7c5/84f9/897e/a897fda63f7e9f788eac7abbc0bf8602.mp3" controls preload="auto"></audio>
	<!-- 歌词 -->
	<div class="lyric_area">
		<ul id="lyricdom"></ul>
	</div>
	<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
	<script>
		window.onload = function() {
			var lyric = "" // 歌词
			var audio = document.getElementById("audio");
			var lyricdom = document.getElementById("lyricdom");
			var medisArray = [];

			var lineNo = 0, //当前行
			C_pos = 6, //C位
			offset = -20; //滚动距离(应等于行高)

			// 获取歌词
			axios.get("http://localhost:4000/lyric?id=32507038").then(res => {
				lyric = res.data.lrc.lyric
				console.log(lyricdom);
				var medises = lyric.split("\n");
				medises.forEach(item => {
					var t = item.substring(item.indexOf("[") + 1, item.indexOf("]"));
					 medisArray.push({
                        t: (t.split(":")[0] * 60 + parseFloat(t.split(":")[1])).toFixed(3),
                        c: item.substring(item.indexOf("]") + 1, item.length)
                    });
				})
				for(let i = 0; i < medisArray.length; i++) {
					let li = document.createElement("li");
					console.log(li);
					li.innerHTML = medisArray[i].c;
					lyricdom.appendChild(li);
				}
			})

			axios.get("http://localhost:4000/song/url?id=32507038").then(res => {
				audio.src = res.data.data[0].url;
			}).catch(err => {
				console.log(err)
			})

			//高亮显示歌词当前行及文字滚动控制,行号为lineNo
			function lineHigh() {
				var lis = lyricdom.getElementsByTagName("li");//歌词数组
				if(lineNo>0){
			 		lis[lineNo-1].removeAttribute("class");//去掉上一行的高亮样式
				}
				lis[lineNo].className = "lineHigh";//高亮显示当前行
				//文字滚动
				if(lineNo>C_pos){
					lyricdom.style.transform = "translateY("+(lineNo-C_pos)*offset+"px)"; //整体向上滚动一行高度
				}
			}

			//监听播放器的timeupdate事件,实现文字与音频播放同步
			audio.ontimeupdate = function () {
			    if(lineNo == medisArray.length)
					return;
				var curTime = audio.currentTime; //播放器时间
				if(parseFloat(medisArray[lineNo].t)<=curTime){
					lineHigh();//高亮当前行
					lineNo++;
				}
			};

			//滚回到开头,用于播放结束时
			function goback() {
				document.querySelector("#lyricdom .lineHigh").removeAttribute("class");
				lyricdom.style.transform = "translateY(0)";
				lineNo = 0;
			}

			//监听播放器的ended事件,播放结束时回滚歌词
			audio.onended = function () {
			    goback(); //回滚歌词
			};
		}
	</script>
</body>
</html>

关于自定义播放进度条

  • currentTime 播放的当前时间
  • duration 播放的时长

currentTime / duration 得出 播放进度

结合 music-app 案例:

let progressBar = document.getElementById("progressBar");
let bar = progressBar.children[0]
let progressBtn = progressBar.children[1];
let time = audio.currentTime / audio.duration;
bar.style.width = (time * 100) + "%";
progressBtn.style.left = (time * 100) + "%";

js 深拷贝与浅拷贝

JavaScript 深拷贝与浅拷贝

简单问题

var name = "hanzo";
var a = name;
name = '111';
console.log(a); // hanzo
console.log(name); // 111

var b = {
      name: 'genji',
      age: 32
 }
 var c = b;
 console.log(c); // {name: "genji", age: 32}
 b.name = 'hanzo';
 console.log(b)  //  {name: "hanzo", age: 32}
 console.log(c); //   {name: "hanzo", age: 32}

js数据类型

1. 简单理解栈堆

在计算机领域中,栈堆是两种数据结构,它们只能在一端(称为栈顶(top))对数据项进行插入和删除

  • 堆 队列优先, 先进先出, 由操作系统自动分配释放, 存放函数的参数值, 局部变量的值等。 其操作方式类似于数据结构中的栈。

  • 栈 先进后出;动态分配的空间 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。

2. js基本类型和引用类型与堆栈有什么联系?

我们都知道javascript中的基本数据类型有:string number boolean null undefined symbol, 引用类型有:Array Object

  • 简单数据类型是存在栈内存中的

  • 引用类型是保存在堆内存中的,然后再栈内存中保存一个对堆内存中实际对象的引用。所以,javascript中引用数据类型的操作都是操作对象的引用而不是实际的对象, 可以理解为:栈内存中保存了一个地址,这个地址和堆内存中的实际值是相关的。

var name = 'hanzo';
var age = 25;
var job;
var arr = [1,2,3];
var obj = {age: 25};
// 可以通过下图来表示数据类型在内存中的存储情况:

此时name,age,job三种基本数据类型是直接存在栈内存中的,而arr,obj在栈内存中只是存了一个地址来表示对堆内存中的引用。

复制

基本数据类型

对于基本数据类型,如果进行复制,系统会自动为新的变量在栈内存中分配一个新值,很容易理解

引用数据类型

如果对于数组、对象这样的引用数据类型而言,复制的时候就会有所区别了:
系统也会自动为新的变量在栈内存中分配一个值,但这个值仅仅是一个地址。也就是说,复制出来的变量和原有的变量具有相同的地址值,指向堆内存中的同一个对象。

如果所示,执行了var objCopy=obj之后,obj和objCopy具有相同的地址值,执行堆内存中的同一个实际对象。

这有什么不同呢?

当我修改obj或objCopy时,都会引起另一个变量的改变。

为什么基础数据类型存在栈中,而引用数据类型存在堆中呢?

  • 堆比栈大,栈比对速度快。
  • 基础数据类型比较稳定,而且相对来说占用的内存小。
  • 引用数据类型大小是动态的,而且是无限的。
  • 堆内存是无序存储,可以根据引用直接获取。

深拷贝/浅拷贝

对于基本数据类型的拷贝,并没有深浅拷贝的区别,我们所说的深浅拷贝都是对于引用数据类型而言。

浅拷贝

浅拷贝的意思就是只复制引用,而未复制真正的值。

const originArray = [1,2,3,4,5];
const originObj = {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
 
const cloneArray = originArray;
const cloneObj = originObj;
 
console.log(cloneArray); // [1,2,3,4,5]
console.log(originObj); // {a:'a',b:'b',c:Array[3],d:{dd:'dd'}}
 
cloneArray.push(6);
cloneObj.a = {aa:'aa'};
 
console.log(cloneArray); // [1,2,3,4,5,6]
console.log(originArray); // [1,2,3,4,5,6]
 
console.log(cloneObj); // {a:{aa:'aa'},b:'b',c:Array[3],d:{dd:'dd'}}
console.log(originArray); // {a:{aa:'aa'},b:'b',c:Array[3],d:{dd:'dd'}}


// 上面的代码是最简单的利用 `=` 赋值操作符实现了一个浅拷贝,可以很清楚的看到,随着 `cloneArray` 和 `cloneObj` 改变,`originArray` 和 `originObj` 也随着发生了变化。

深拷贝

深拷贝就是对目标的完全拷贝,不像浅拷贝那样只是复制了一层引用,就连值也都复制了。只要进行了深拷贝,它们老死不相往来,谁也不会影响谁。

1. 利用JSON.parse 和 JSON.stringify 方法

// 1.实现简单的深拷贝
const hanzo = [1,2,3,4];
const genji = JSON.parse(JSON.stringify(hanzo));
console.log(hanzo === genji); // false
hanzo.push(5);
genji.splice(0,1);
console.log(hanzo); // [1,2,3,4,5]
console.log(genji); // [2,3,4]
// 缺陷:只能实现一些简单的深拷贝。但是下面这种情况就不适合了。

// 2. 
const obj = {
    name: 'hanzo',
    age: 38,
    sayHello: function() {
        console.log('半藏')
    }
};
console.log(obj); // {name: "hanzo", age: 38, sayHello: ƒ}
const obj6 = JSON.parse(JSON.stringify(obj));
console.log(obj6); // {name: "hanzo", age: 38}
// sayHello 这个funciton 并没有没拷贝下来
// 原因:undefined、function、symbol 会在转换过程中被忽略, 
// 所以如果对象中含有一个函数时(很常见),就不能用这个方法进行深拷贝。

2. 递归

递归的**就很简单了,就是对每一层的数据都实现一次 创建对象->对象赋值 的操作,

function deepCopy(source) {
    const targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
    for(let keys in source) {
        if(source[keys] && typeof source[keys] === 'object') { // 如果值是对象,就递归一下
            targetObj[keys] = source.constructor === Array ? [] : {};
            targetObj[keys] = deepClone(source[keys]);
        } else {
            targetObj[keys] = source[keys];
        }
    }
    return targetObj
}


const hanzo = {
    name: 'hanzo',
    age: 38,
    sayHello: function() {
        console.log('我是半藏')
    }
}

const genji = deepCopy(hanzo);
genji.name = 'genji';
genji.age = 35;
genji.sayHello = function() {
    console.log('我是源氏')
};
console.log(hanzo); // {name: "hanzo", age: 38, sayHello: ƒ}
console.log(genji); // {name: "genji", age: 35, sayHello: ƒ}
console.log(hanzo === genji); // false
hanzo.sayHello(); // 我是半藏
genji.sayHello(); // 我是源氏

3. javascript的其他拷贝方法

我们知道在 JavaScript 中,数组有两个方法 concat 和 slice 是可以实现对原数组的拷贝的,这两个方法都不会修改原数组,而是返回一个修改后的新数组。

同时,ES6 中 引入了 Object.assgn 方法和 ... 展开运算符也能实现对对象的拷贝。

那它们是浅拷贝还是深拷贝呢?

4. concat slice

const arr1 = [1,2,3,4,5];
const arr2 = arr1.concat();
arr1.push(6);
arr2.splice(0,1);
console.log(arr2 === arr1); // false;
console.log(arr1); // [1, 2, 3, 4, 5, 6]
console.log(arr2); // [2, 3, 4, 5]
// 看上去像深拷贝

// 但如果是多层呢?
const arr1 =[1,[1,2,3],{a:1}];
const arr2 = arr1.concat();
arr2[1].push(4);
arr2[2].a = 666;
console.log(arr1 === arr2); // false
console.log(arr1); // [1,[1,2,3,4],{a:666}];
console.log(arr2); // [1,[1,2,3,4],{a:666}];

arr1 中含有数组 [1,2,3] 和对象 {a:1},如果我们直接修改数组和对象,不会影响 arr1 ,但是我们修改数组[1,2,3] 或对象 {a:1} 时,发现 arr1 也发生了变化。

结论:concat 只是对数组的第一层进行深拷贝。

slice 和 concat 一样 所以不举例

Object.assign()

const hanzo = {
    name: 'hanzo',
    xage: 38,
    children: [1,2,3,4],
    sayHello() {
        xconsole.log(666)
    }
};

const genji = Object.assign({}, hanzo);
console.log(genji === hanzo); // false;
genji.children.push(666);
console.log(hanzo); //  {name: "hanzo", age: 38, children: [1,2,3,4,666], sayHello: ƒ}
console.log(genji); //  {name: "hanzo", age: 38, children: [1,2,3,4,666], sayHello: ƒ}

Object.assign() 也是首层浅拷贝

...展开运算符

const Array1 = [1,2,3,4,[5,6],7,8];
    const Obj1 = {
        a: 1,
        b: {
            bb: 233
        }
    };
const cloneArray = [...Array1];
cloneArray[4].push(666);
console.log(cloneArray === Array1); // false
console.log(cloneArray); // [1,2,3,4,[5,6,666],7,8];
console.log(Array1); // [1,2,3,4,[5,6,666],7,8];
const cloneObj = {...Obj1};
console.log(cloneObj === Obj1);
cloneObj.b.bb = 666;
console.log(cloneObj);//  {a:1,b:{bb:666}}
console.log(Obj1); //  {a:1,b:{bb:666}}

...展开运算符也是首层浅拷贝

首层浅拷贝

function shallowClone(source) {
    xconst targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
    for (let keys in source) { // 遍历目标
        if (source.hasOwnProperty(keys)) {
            targetObj[keys] = source[keys];
        }
    }
    return targetObj;
}       

总结

  • 赋值运算符 = 实现的是浅拷贝,只拷贝对象的引用值;
  • javascript中数组和对象自带的拷贝方法都是“首层浅拷贝”;
  • JSON.stringify 实现的深拷贝,但是对目标对象有要求;
  • 若想真正意思上的深拷贝,请递归。

代码改变360浏览器模式

代码改变360浏览器模式

从js判断ie版本

判断IE版本主要的是获取两个属性,一是当前浏览器名称,二是当前浏览器版本

		window.onload = function() {
			var head = document.getElementsByTagName("head")[0];
            var meat1 = document.createElement("meat");

			// 获取 浏览器 信息
           	var appName = navigator.appName; // Netscape 网景 || Microsoft Internet Explorer
           	var appVersion = navigator.appVersion; // 获取当前浏览器的平台和版本信息
           	var version_arr = appVersion.split(";");  //将平台版本信息按照;切割成数组
            var version = version_arr[1].replace(/\s/g,""); //取上一步数组下标为1的元素,去空格

            if(appName === "Microsoft Internet Explorer") {
                
                if (version == "MSIE5.0" || version == "MSIE6.0" || version == "MSIE7.0" || version == "MSIE8.0" || version == "MSIE9.0" || version == "MSIE10.0") {
                    alert("为了您更好的体验,请下载最新的浏览器");
				}
            }
        }

对于appName属性(浏览器名称),最直观感觉可能是,使用谷歌浏览器获取的就是chrome,使用QQ浏览器获取的名称就是qqbrowser,不过很可惜,目前appName获取的值只存在两种,一是**"Netscape" (网景),二是“Microsoft Internet Explorer” (IE)**。

在IE11中


在IE11以下

可以看出,版本低于11的IE版本,appName属性均为Microsoft Internet Explorer。

360浏览器内核切换

360安全浏览器现有两种内核模式,即“极速模式”和“兼容模式”。

极速模式

“极速模式”是以Blink(Webkit)为内核的浏览模式,Blink内核具有更高的网页浏览速度和更好网页渲染效果。但由于少部分网银、政府、税务、办公系统等网站对Blink的兼容性不佳,若打开此类网站发现异常,请手动切换到“兼容模式”下继续浏览。

兼容模式

“兼容模式”下,360安全浏览器调用Trident内核(IE内核),Trident内核具有网页兼容性好、页面适用性广的特征,对于部分在默认“极速模式”下出现问题的网页(如:使用了Activex控件、页面代码只支持Trident内核、网页中注明使用IE浏览器)的情况下,切换“兼容模式”就可以正常使用网站功能。

内核控制标签meta

开发者可以通过在页面标签中添加代码,来指定网站的模式;

若页面需默认用极速模式,增加标签:<meta name=”renderer” content=”webkit”>

若页面需默认用IE兼容模式,增加标签:<meta name=”renderer” content=”ie-comp”>

若页面需默认用IE标准模式,增加标签:<meta name=”renderer” content=”ie-stand”>

content的取值分别为webkit,ie-comp,ie-stand,区分大小写,分别代表用极速模式,IE兼容模式(低版本IE模式),IE标准模式(高版本IE模式)

代码改变360浏览器模式

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<body>
	<script>
		window.onload = function() {
			var head = document.getElementsByTagName("head")[0];
            var meat1 = document.createElement("meat");

			// 获取 浏览器 信息
           	var appName = navigator.appName; // Netscape 网景 || Microsoft Internet Explorer
           	var appVersion = navigator.appVersion; // 获取当前浏览器的平台和版本信息
           	var version_arr = appVersion.split(";");
            var version = version_arr[1].replace(/\s/g,"");
            console.log(appName);
            console.log(appVersion);
            console.log(version_arr);
            console.log(version);

            if(appName === "Microsoft Internet Explorer") {
                meat1.name = 'render';
                meat1.content = 'webkit';
                head.appendChild(meat1);
                if (version == "MSIE5.0" || version == "MSIE6.0" || version == "MSIE7.0" || version == "MSIE8.0" || version == "MSIE9.0" || version == "MSIE10.0") {
                    alert("为了您更好的体验,请下载最新的浏览器");
				}
            } else if (appName === "Netscape") {
                var m = document.createElement("meta");
                m.name = 'render';
                m.content = 'webkit';
                head.appendChild(m);
            }
        }

	</script>
</body>
</html>

git使用

本地库和远程库

1、团队内部协作

2、跨团队协作

git命令行操作

1、本地库操作

1.1、git init 生成一个.git文件夹

1.2、设置签名
		
	形式:
		用户名:tom,
		Email地址:[email protected]
	作用:
		区分不同开发人员的身份
	辨析:这里设置的签名和登录远程库(代码托管中心)的账号,密码没有任何关系
	命令:
		项目级别:仅在当前本地库范围内有效;
			git config user.name hanzo
			git config user.email [email protected]
			查看:cat .git/config	

		系统用户级别:登录当前操作系统的用户范围

			git config --global user.name hanzo
			git config --global user.email [email protected]	

1.3、git status // 查看状态

1.4、git add [文件名]  // 添加某个文件到暂存区 / git add --all 添加所有文件

1.5、git rm --cached [文件名]  // 从暂存区移该文件

1.6、git commit -m ""我提交了xx文件,将XX功能做好了" [文件名]  // 将该文件从暂存区提交到本地仓库, 也可以不跟文件名

实现版本的前进和后退

1、版本的历史记录 

	git log
	git log --pretty=oneline
	git log --oneline
	git reflog

2、前进后退

	2.1、基于索引符号操作[推荐]

		git reset --hard [07e942a索引符号]

	2.2、使用^符号,只能后退;
		
		git reset --hard HEAD^ // 几个^符号就可以回退几个版本

	2.3 使用~符号,只能回退

		git reset --herd HEAD~3 // 回退3个版本 

3、reset命令三个参数对比
	
	--soft: 仅仅在本地库移动HEAD指针
	--mixed: 仅仅在本地库移动HEAD指针,重置暂存区
	--hard: 仅仅在本地库移动HEAD指针,重置暂存区,重置工作区

删除文件并找回

1、通过回退版本找回文件; 

	首先我们在工作区删除了一个文件后,应该再add将新的文件目录的文件重新放在暂存区,然后commit到本地仓库;
	当我们想找回文件时,通过log历史记录来查找上一次我提交“删除文件”的历史记录,通过版本回退来进入之前的那个版本,然后在本地又会有刚刚删除的那个文件了;、

2、当你没有将文件commit到本地仓库的时候(还在暂存区),将文件删除了,可以通过 git reset --hard HEAD

比较文件差异

当你在工作区修改了文件后,通过 git diff 命令可以进行比较

git diff [文件名]:将工作区和暂存区的文件进行比较;

git diff [本地库历史版本] [文件名]:将工作区的文件和本地仓库的文件进行比较;

git diff 不带文件名比较多个文件

分支

在版本控制过程中,使用多条线同时推进多个任务

分支的作用

  1. 同时并行推进多个功能开发,提高开发效率;
  2. 各个分支在开发过程中,如果某一个分支开发失败,不会对其他分支有任何影响,失败的分支删除重新开始即可;

分支操作

  1. git branch -v // 查看分支
  2. git branch [分支名] // 创建分支
  3. git checkout [分支名] // 切换分支
  4. 合并分支:
    1. // 例如我在 创建了一个xxx分支 并修改内容然后commit在这个分支上,然后我想将该分支的内容 和 主分支(master)进行合并;
    2. 第一步:切换到接受修改的分支上(被合并的分支名);
    3. 第二步:执行merge命令; git merge [有新内容的分支名];

  1. 解决冲突
    1. 编辑文件,删除特殊符号;
    2. 把文件修改到满意的程度,保存退出;
    3. git add [文件名]
    4. git commit -m "日志信息";

// 合并分支失败,需要手动合并;

// git 并没有给我们创建一个新的文件,而是以这种形式来区分的;

// 具体协商将冲突的部分,删除协商的那一段;

// 然后git status

// 继续 git add 该文件

// 冲突解决

本地库与远程库交互

1、创建目录,git init 初始化git仓库,创建一个文件,git add 添加到暂存区,git commit 添加到本地仓库;

2、创建远程库

3、推送操作 git push origin[别名] master[分支名]

4、克隆操作 git clone [远程地址]

命令效果

- 完整的把远程库下载到本地;
- 创建origin远程地址别名;
- 初始化本地库;

5、邀请别人进入团队

5.1 当别人同意之后,他clone远程库,修改文件内容,再add commit , 然后push 就不会有错误了;

5.2 我要通过 git pull从远程库拉取,从而在本地获取最新内容;

5.3 拉取 git pull origin master

		pull = fetch + merge
		git fetch [远程库地址别名] [远程分支]
		git merge [远程库地址名 / 远程库分支名]

5.4 解决冲突

	1、如果不是基于github远程库的最新版所做的修改了,不能推送,必须先拉取;
	2、拉取下来后如果进入冲突状态,按照“分支冲突解决”操作解决即可;

6、跨团队协作

  1. 先从“小明”远程库 Fork一份,到自己的远程库;
  2. 在自己的远程库 通过clone到本地,然后修改,add,commit ,push到自己的远程库;
  3. 通过pull request

  1. 最后 git pull origin master 拉取到本地 获取最新内容;

简单学习了解React Hooks

react hooks

为什么需要 class 类组件

  • 需要状态(state)
  • 需要组件的生命周期函数
  • 需要一些副作用的操作

Hooks

  • useState 状态管理
  • useEffect componentDidUpdate,componentDidMount
  • useContext

useState

useState 有两个值 一个是 初始值,一个是 function

const [count,setCount] = useState(0);

// useState(0) 中的 0 是 count的初始值 0;

/*

this.state = {
  count: 0
}

*/


// setCount则是一个function函数

React为每一次useState调用分配一个“空间”,然后通过顺序辨别各个“空间”。

useEffect

  • componentDidMount用于在mount过程结束时的副作用;
  • componentDidUpdate用于在update过程结束时的副作用;
  • useEffect = componentDidMount + componentDidUpdate;

useEffect模拟componentDidMount

useEffect第一个参数是一个函数,第二个参数是一个数组(依赖的数据)

useEffect(() => {
	// 每次mount或者update都会调用在这里;

})


useEffect(() => {

	// 只有 mount时调用到这里

},[])

useEffect模拟componentDidUnmount

useEffect(() => {

	// 只有在mount时调用这里

	return () => {
		// 只有unmount时调用这里
	}

},[])

useEffect模拟componentDidUpdate

const mounted = useRef();
useEffect(() => {

	if(!mounted.current) {
		mounted.current = true;
	} else {
		// do compoentDidUpdate logic
	}

})

怎么跳过一些不必要的副作用函数

每次重新渲染都要执行一遍这些副作用函数,显然是不经济的。怎么跳过一些不必要的计算呢?我们只需要给useEffect传第二个参数即可。用第二个参数来告诉react只有当这个参数的值发生改变时,才执行我们传的副作用函数(第一个参数)

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // 只有当count的值发生变化时,才会重新执行`document.title`这一句

hooks优点

  • 降低组件复杂度
    • 完全使用函数组件
    • 无需生命周期函数
    • 更好的状态管理
  • 更好的代码重用性

定制hooks

const useBeacon = () => {
  const [renderCount,serRenderCount] = useState(100);
  const sendBeacon = () => {
    console.log(7677)
  }
  useEffect(() => {
    sendBeacon()
  })
}


const App = (props) => {
  useBeacon();
  useEffect(() => {
    return () => {
      console.log('unmount');
    }
  })

  return (
    <div className='App' ref={mounted}>
     
    </div>
  )

}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.