目录
抛出自定义错误
- html>
- <html>
- <body>
-
- <p>Please input a number between 5 and 10:p>
-
- <input id="demo" type="text">
- <button type="button" onclick="myFunction()">Test Inputbutton>
- <p id="message">p>
-
- <script>
- function myFunction() {
- var message, x;
- message = document.getElementById("message");
- message.innerHTML = "";
- x = document.getElementById("demo").value;
- try {
- if(x == "") throw "is Empty";
- if(isNaN(x)) throw "not a number";
- if(x > 10) throw "too high";
- if(x < 5) throw "too low";
- }
- catch(err) {
- message.innerHTML = "Input " + err;
- }
- }
- script>
-
- body>
- html>
try里面放可能会出错误的代码 catch是当发生错误时执行的,且有参数,代表错误本身,finally表示不管有没有错误都会执行
- <p>123p>
-
- <script>
- try{
- let ss = document.querySelector('.p');//Cannot read properties of null (reading 'style')
- ss.style.color = 'red'
- }catch(err){
- console.log(err.message);
-
- }finally{
- alert('sdasda')
- }
-
-
-
- script>
-
浅拷贝与深拷贝都只针对 引用类型
浅拷贝拷贝的是值(其实也是地址)
-
- const obj={
- age :18
- }
-
- let o = {...obj}
- let o1={}
- Object.assign(o1,obj)
- o1.age=20
- console.log(o1);//age =20
- console.log(obj);//age =18
-
-
但是这样有问题:
- const obj={
- age :18,
- obj1:{
- sex:1
-
- }
- }
-
- let o = {...obj}
- let o1={}
- Object.assign(o1,obj)
- o1.obj1.sex = 2
- console.log(o1);
- console.log(obj);//sex都为2
欠拷贝只直接拷贝最外一层的值,而对于obj1来说,最外层的值是他的地址,所以就直接把地址给了o1,改变o1时也会改变obj
直接拷贝对象 不是地址
设置函数,当originValue里面还有引用数据类型时,再次调用自己,只是将originValue改为里面的引用数据类型
-
- const obj = {
- uname: 'pink',
- age: 18,
- hobby: [{ name: 2222, age: 2222 }, '足球',new Set([123, 22, 333])],
- family: {
- baby: '小pink'
- },
- run: function () {
- console.log(1);
- },
- sete: new Set([123, 22, 333]),
- asas:Symbol('asd')
- }
- obj.self = obj
-
- function deepcopy1(originValue,map = new WeakMap()){
- if(typeof originValue === 'symobol'){
- return new Symbol(originValue.desciption)
- }
-
- if(!(originValue instanceof Object)){
- return originValue
- }
- if(typeof originValue === 'function'){//函数不需要深拷贝
- return originValue
- }
-
- if(originValue instanceof Set){
- const newSet = new Set()
- for(let item of originValue){
- newSet.add(deepcopy1(item,map))
- }
- return newSet
- }
-
- if(map.get(originValue)){//当我遍历到self,self需要我自身,也就是obj,这里直接把map中的obj拿给他
- return map.get(originValue)
- }
-
- let newobj = originValue instanceof Array?[]:{} //如果originValue是单纯的对象或数组
- map.set(originValue,newobj)
- for(let key in originValue){
- newobj[key] = deepcopy1(originValue[key],map)
- }
- return newobj
- }
- let ss = deepcopy1(obj)
- console.log(ss);
-
-
-
-
这里比较难的地方在于 obj.self = obj ,也就是在obj中创造了self指向自己
在深拷贝时,传入map = new WeakMap()用于记录已经生成的应用对象,当拷贝到self时,这个时候又需要创造一个obj对象,这时可以将map中存储好的obj直接拿出来赋值给它
先去下载库,并引入js
Lodash 简介 | Lodash 中文文档 | Lodash 中文网
调用cloneDeep()
- var oo = _.cloneDeep(obj)
- console.log(oo);
-
- const obj = {
- uname: 'pink',
- age: 18,
- hobby: ['乒乓球', '足球'],
- family: {
- baby: '小pink'
- }
- }
-
- // JSON.stringify(obj)
- //将对象转换为JSON字符串
- let o =JSON.parse(JSON.stringify(obj))//将JSON字符串转换为对象
-
-
-
-
但是JSON有个问题,如果 对象有 函数 或者 symbol 则无法解析
可以改变this指向,在调用的时候用,第一个值就是设置指向
- let obj={
-
- }
-
- function fn(x,y){
- console.log(this);
- }
- fn.call(obj,1,2)//obj
也是改变this指向 ,类似,传参必须为数组,这个用的比较多
- let obj={
- }
- function fn(x,y){
- console.log(this);//obj
- console.log(x+y);//3
- }
- fn.apply(obj,[1,2])
值得注意的是,当不匹配时:
- let obj={
-
- }
-
- function fn(x){
- console.log(this);//obj
- console.log(x);//1
- }
- fn.apply(obj,[1,2])
不会将数组全部传入
提供了另外一种求最大值的方式
- let aa = [1,4,5,8,9]
- //Math.max(1,4,7,98)
-
- console.log(Math.max(...aa));
- console.log(Math.max.apply(null,aa));
前面两种都必须要调用函数,这个不需要,返回值是函数,其实就是改变原函数的this得到了一个新函数
- let aa = [1,4,5,8,9]
- function fn(){
- console.log(this);
- }
- fn.bind(null)//无输出
-
- let fun = fn.bind(aa);
- fun()//a
小应用,点击按钮禁用,两秒开启
-
- <script>
- let btn = document.querySelector('button');
- btn.addEventListener('click',function(){
- this.disabled = true
- setTimeout(function(){
- this.disabled = false
- }.bind(this),2000)
- })
-
- script>
这个地方把定时器的this改为了btn,因为bind(this)里面这个this还是btn
在一定的时间内只能执行一次,不管触发了多少次,在这个时间内只执行一次
例子:div滑动时,200ms内只改变span一次
- var start = 0
-
- document.querySelector('div').addEventListener('mousemove',function(){
- let now = Date.now()
- if(now-start>=200){
- document.querySelector('span').innerHTML =i++
- start = now
- }
-
- })
每次调用函数时都将此时的时间赋给now,当now与start差别大于200毫秒时才会改变i,然后将现在的时间赋给start以进行下次函数
上面用的是时间差值的方法,也可以采用定时器的方法
这里再加了一个immediate 变量,具体原理参照 防抖
- <input type="text">
- <button>cancelbutton>
-
- <button class="throttle">throttlebutton>
-
- <script>
- let ipt = document.querySelector('input')
-
- let mythrottle = function(fn,interval,immediate =true){
- let timeout = null
- let immediate_done = false
- let _throttle = function(){
- if (!immediate_done && immediate) {
- fn.apply(this)
- immediate_done = true
- return
- }
-
- if(!timeout){
- timeout = setTimeout(()=>{
- fn.apply(this)
- timeout = null
- },interval)
- }
- }
- return _throttle
- }
-
- ipt.addEventListener('input',mythrottle(function(){
- console.log(this.value);
- },1000))
- script>
防抖
触发后n秒内只执行一次,但是如果这段时间内还触发了,则会重新计算时间
也就是说,在一定时间内我不停的进行高速的事件触发,但是只会在最后一次触发结束后进行执行
应用场景:输入框,一般是等我输入完了才会去匹配
例子:在div滑动时 停下来一秒钟才会改变span
- document.querySelector('div').addEventListener('mousemove',function(){
- if(timeget) clearTimeout(timeget)
- timeget = setTimeout(function(){
- document.querySelector('span').innerHTML =i++
-
- },1000)
- })
例子:再次采用函数实现,需求是对输入框,输入结束一段时间后反应,这里还提供了应该取消接口,可以在这段时间结束前 结束响应
- "text">
- <button>cancelbutton>
-
- <script>
- let mydebounce = function(fn,delay){
- let timeout = null
- let _debounce = function(){
- if(timeout) clearTimeout(timeout)
- timeout = setTimeout(()=>{
- fn.apply(this)
- },delay)
- }
- _debounce.cancel = function(){
- if(timeout) clearTimeout(timeout)
- }
- return _debounce
- }
-
- let ipt = document.querySelector('input')
- let btn = document.querySelector('button')
- let debounce = mydebounce(function(){
- console.log(this.value);
- },2000)
- ipt.addEventListener('input',debounce)
- btn.addEventListener('click',debounce.cancel)
- script>
再加一个立即执行功能,即在输入时,第一次输入马上响应,后面输入则等待一段时间,然后再次输入又马上响应
- let mydebounce = function(fn,delay,immediate=false){
- let timeout = null
- let immediate_done = false
- let _debounce = function(){
- if(timeout) clearTimeout(timeout)
- if(!immediate_done && immediate){
- fn.apply(this)
- immediate_done = true
- return
- }
- timeout = setTimeout(()=>{
- fn.apply(this)
- immediate_done = false
- },delay)
- }
- _debounce.cancel = function(){
- if(timeout) clearTimeout(timeout)
- immediate_done = false
- }
- return _debounce
- }
定义了 immediate表示是否需要这个功能,immediate_done判断是否完成,当输入时,immediate为1,immediate_done为0时,会马上执行fn函数,然后使得immediate_done为1。在执行完时间间隔后,又将immediate_done为0
还可以将_debounce函数返回为promise,好处是在fn传入时有返回值的情况下,可以拿到这个返回值
- let res = null
- let _debounce = function () {
- return new Promise((resolve, reject)=>{
- if (timeout) clearTimeout(timeout)
- if (!immediate_done && immediate) {
- res = fn.apply(this)
- resolve(res)
- immediate_done = true
- return
- }
-
- timeout = setTimeout(() => {
- res = fn.apply(this)
- resolve(res)
- immediate_done = false
- }, delay)
- })
- }
若fn有返回值,就可以通过.then得到
- let ipt = document.querySelector('input')
- let btn = document.querySelector('button')
-
- let debounce = mydebounce(function () {
- console.log(this.value);
- return(111)
- }, 1000)
-
-
- ipt.addEventListener('input', debounce)
- btn.addEventListener('click', debounce.cancel)
-
- debounce().then(res=>{
- console.log(res);
- })
还可以使用lodash
节流
_.throttle(fn,300)
在300ms内只能执行一次fn函数
- function fn(){
- document.querySelector('span').innerHTML =i++
- }
- document.querySelector('div').addEventListener('mousemove',_.throttle(fn,300))
-
防抖
_.debounce(fn,300)
停下来300ms后才执行fn函数
- function fn(){
- document.querySelector('span').innerHTML =i++
- }
- document.querySelector('div').addEventListener('mousemove',_.debounce(fn,300))
- const video = document.querySelector('video')
- video.onloadeddata = function(){
- video.currentTime = localStorage.getItem('time')||0
- }
- video.ontimeupdate = _.throttle(function(){
- localStorage.setItem('time',video.currentTime)
- },1000)
得到video视频
每次打开页面都把存储中的time拿出来,赋值给视频的currentTime,即跳转到对应的时间
视频的时间发生改变时,添加防抖,每隔1s更新存储中的time值
实现每次页面打开可以返回上次视频播放的地方
当项目中有很多组件,很多文件,当一个文件中的某一个事件触发,希望另外一个文件得到响应,就需要用到事件总线
这里我们自己用js实现
一般来说这个事件总线是一个对象,里面有emit函数,用于发送时间,也有on函数,用于接受事件
-
- <script>
- class EventBus{
- constructor(){
- this.eventMap = {}//这个map用于存储on绑定的函数
- }
- on(eventname,fn){
- let fns = this.eventMap[eventname]
- if(!fns){//第一次如果没有函数数组时,自己创造一个
- fns=[]
- this.eventMap[eventname] = fns
- }
- fns.push(fn)//将回调函数放入对应的函数数组
- }
-
- emit(eventname,...args){
- let fns = this.eventMap[eventname]
- if(!fns) return
- fns.forEach(fn => {
- fn(...args)
- });
- }
- }
- let eventBus = new EventBus()
-
- eventBus.on('btnclick',()=>{
- console.log(1111);
- })
-
- eventBus.on('btnclick',()=>{
- console.log(2222);
- })
-
- let btn = document.querySelector('button')
- btn.addEventListener('click',function(){
- console.log('自己得到');
- eventBus.emit('btnclick')
- })
- script>
因为on函数,对于一种事件的响应函数可能不止一个,所以需要eventMap来存储函数,其数据结构为{ eventname:[fn1,fn2]}
在emit时,先取得事件对应的函数数组,再以此执行
还可以写一个删除函数函数
- off(eventname,fn){
- let fns = this.eventMap[eventname]
- if (!fns) return
- for(let i=0;i
length;i++){ - if(fn === fns[i]){
- fns.splice(i,1)
- break
- }
- }
- //如果fns已经被清空了,那直接将事件删除
- if(fns.length==0){
- delete this.eventMap[eventname]
- }
- }