沙箱模式,柯里化,防抖与节流,数据驱动视图,回调地狱

148 阅读9分钟

这一次的有点杂,也会用到一些闭包的知识,前一篇里面有说,感兴趣的话可以去翻看。主要讲沙箱模式,柯里化,防抖与节流,数据驱动视图,回调地狱的一些知识。

沙箱模式

利用间接返回一个函数,然后拿到外部函数内的私有变量。

function outer() {
    let a = 100;
    let str = 'hhhh';
    const obj = {
        getA: function() {
            return a;
        },
        getStr: function() {
            return str;
        },
        setA(val) {
            a = val;
        }
    }
    return obj;
}
let res = outer;//调用了外部函数outer,这个给函数内部回返回一个对象obj,然后将它存储到变量res中
let num = res.getA();

沙箱模式语法糖:

语法糖:再不影响功能的情况下,对我们的语法做一点简化操作;通过getter和setter帮助我们简化代码书写。

function outer() {
    let a = 1;
    let b = 100;
    const obj = {
        get a (){
            return a;
        },
        set a (val) {
            a = val;
        },
        get b (){
            return b;
        },
        set b (val) {
            b = val;
        }
    }
}
let res = outer();
//console.log(res.geta()),这样使用将会有问题
console.log(res.a);
res.a = 999

认识函数的柯里化

将一个接受多个参数的函数,更改为需要多次调用,每次只传一个参数的函数,利用了闭包,延长了外部函数的参数使用时间。例:

function sum(a,b) {
    console,log(a + b);
}
sum(10,20);
sum(10,30);
//更改后
function sum(a) {
    return function(b) {
        console.log(a+b);
    }
}
let res = sum(10);
res(20);
res(30);

函数的防抖与节流:

防抖:在一定时间内,快速触发同一事件,每次重新触发都顶掉前一次事件,以后一次事件为主;

节流:在一定时间内,快速触发同一事件,在规定时间内只能触发一次,下一次必须等规定时间结束以后才能执行。

(涉及自执行函数:第一个小括号内写函数体,第二个小括号内写实参,会传递给第一个小括号内部的函数)

const inp = document.querySelector('#inp');
//节流
inp.oninput = (function (flag) {
    return function(e) {
        if(flag === false) return;
        flag = false;
        console.log('搜索了${e.target.value}内容');
        setTimeout(()=> {
            flag = true;
        },300)//规定时间
    }
})(true)//自执行函数在第二个小括号里面传参//防抖
inp.oninput = (function(timer) {
    return function(e) {
        clearTimeout(timer);//每一次触发都清除上一次事件
        timer = setTimerout(function() {
            console.log('搜索了${e.target.value}内容');
        },300)//规定时间
    }
})(0)

数据驱动视图:

1,数据劫持:

将原始数据劫持出一份一模一样的,听起来很像浅拷贝,劫持出来的数据默认是不可以修改的。

语法:Object.defineProperty(存储的对象,对象的key,{配置项})。

配置项:
  • value,访问这个值之后,得到的结果;

  • writable,决定当前这个属性能否被修改,默认是false;

  • enumerable,决定当前这个属性能否被枚举,决定当前这个属性是否被遍历到;

  • getter,是一个函数,是一个获取器,当访问到这个属性时,会执行这个函数;getter不能和value writable一起使用;

  • setter,是一个函数,是一个设置器,当设置这个属性时,会执行这个函数。

    const obj = {};
    obj.name = 'zhangsan';
    Object.defineProperty(obj,'age', {
        //value:'123',
        //writable:true //这两项不能同下面的get一起使用;
        enumerable:true,
        get() {
            return '当你当前访问的age这个属性,触发执行了get函数';
        },
        set(val) {
            console.log('你当前想要修改age这个属性,修改的值是:',val);
        }
    })
    
基本写法:
Object.defineProperty(res,'name',{  //res,劫持的数据存储对象;name,属性名
    get() {
        return obj.name;//obj,劫持对象
    },
    set(val) {
        obj.name = val;//修改的值
    }
})
升级版语法:

语法: Object.defineProperties(到那个对象, { 属性1: 配置项, 属性2: 配置项 });

Object.defineProperties(res,{
    name:{
        get() {
            return obj.name
        },
        set(val) {
            obj.name = val;
        }
    }
})

数据代理:

Object.proxy,语法:new Proxy(参数一:代理哪个对象)

const res = new Proxy(obj,{
    get(target,p) { 
    //target,当前代理的对象,在当前案例中就是obj;p,proxy会自动遍历对象,拿到对象的每个key;
        return target[p];
    },
    set(target,p,val) {
        target[p] = val;//修改的值
        return true;//修改成功
    }
})

回调地狱:

回调函数:

把函数A通过参数的形式传递和函数B,在函数B内部以形参方式调用,函数A就叫函数B的回调函数,通常用的回调函数的场景都是在异步代码封装。

回调地狱:

并不是一个bug,而是一种代码格式,非常不利于我们阅读,通常用Promise(期约)来解决回调地狱代码。

Promise:

一个新的异步代码封装方案,它会有三种状态,持续状态:pending;成功状态:fulfilled;失败状态:rejected;Promise也只会发生两种状态转换:持续==>成功;持续==>失败。

const p = new Promise(function(reslove,reject) {
    const timer = Math.ceil(Math.random()*3000) + 2000;
    setTimeout(()=> {
        if(timer > 3500) {
            reject('失败')
        }else {
            reslove('成功')
        }
    },timer)
})
p.then(function(res) {
    console.log(res);
}).catch(function(res) {
    console.log(res)
})
async与await:

作用:能帮助我们把异步代码写的和同步代码一样

函数开头必须写async,表明内部可以书写await;await后面需要跟promise,表示等到的意思,执行到fn(),虽然是异步程序,但是因为用await关键字,此时不会继续往下执行,而是等到fn()执行完毕再往下执行。

function fn() {
    const p = new Promise(function(reslove,reject) {
        const timer = Math.ceil(Math.random()*3000) + 2000;
        setTimeout(()=> {
            if(timer > 3500) {
                reject('失败')
            }else {
                reslove('成功')
            }
        },timer)
    })
}
async function newFn() {
    let r1 = await fn()
    console.log(r1)
}
newFn()
async/await的问题:没有办法捕获到错误,只能接受promise的成功状态,如果报错,会中断程序执行。

解决方法:1,try...catch;首次执行的时候会走try这个分支,如果这个位置有报错,会结束执行try分支,然后走catch分支,如果再运行try分支时,没有报错,那么catch不会运行。

//使用上面一个函数fn()
async function newFn() {
    try {
        let r1 = await fn()
        console.log(r1)
    } catch (error) {
        console.log(error)
        console.log('如果失败了, 执行这行代码, 提示用户网络有问题')
    }
}
newFn()

解决方法2:更改promise的封装;让这个promise不管什么情况都返回resolve,让我们通过返回的参数,区分现在时成功还是失败;开发中,对象内的code如果为0,一般代表失败,对象内的code如果为1,一般代表成功。

function fn() {
    const p = new Promise(function (reslove, reject) {
        const timer = Math.ceil(Math.random() * 3000) + 1000
        setTimeout(() => {
            if (timer > 3500) {
                reslove({
                    code: 0,
                    msg: '失败'
                })
            } else {
                reslove({
                    code: 1,
                    msg: '成功'
                })
            }
        }, timer)
    })
    return p
}
async function newFn() {
    let r1 = await fn()
    if (r1.code === 0) {
        console.log('您的网络有问题')
    } else {
        console.log(r1.msg)
    }
}
newFn()

认识函数的柯里化

将一个接受多个参数的函数,更改为需要多次调用,每次只传一个参数的函数,利用了闭包,延长了外部函数的参数使用时间。例:

function sum(a,b) {
    console,log(a + b);
}
sum(10,20);
sum(10,30);
//更改后
function sum(a) {
    return function(b) {
        console.log(a+b);
    }
}
let res = sum(10);
res(20);
res(30);

函数的防抖与节流:

防抖:在一定时间内,快速触发同一事件,每次重新触发都顶掉前一次事件,以后一次事件为主;

节流:在一定时间内,快速触发同一事件,在规定时间内只能触发一次,下一次必须等规定时间结束以后才能执行。

(涉及自执行函数:第一个小括号内写函数体,第二个小括号内写实参,会传递给第一个小括号内部的函数)

const inp = document.querySelector('#inp');
//节流
inp.oninput = (function (flag) {
    return function(e) {
        if(flag === false) return;
        flag = false;
        console.log('搜索了${e.target.value}内容');
        setTimeout(()=> {
            flag = true;
        },300)//规定时间
    }
})(true)//自执行函数在第二个小括号里面传参//防抖
inp.oninput = (function(timer) {
    return function(e) {
        clearTimeout(timer);//每一次触发都清除上一次事件
        timer = setTimerout(function() {
            console.log('搜索了${e.target.value}内容');
        },300)//规定时间
    }
})(0)

数据驱动视图:

1,数据劫持:

将原始数据劫持出一份一模一样的,听起来很像浅拷贝,劫持出来的数据默认是不可以修改的。

语法:Object.defineProperty(存储的对象,对象的key,{配置项})。

配置项:
  • value,访问这个值之后,得到的结果;

  • writable,决定当前这个属性能否被修改,默认是false;

  • enumerable,决定当前这个属性能否被枚举,决定当前这个属性是否被遍历到;

  • getter,是一个函数,是一个获取器,当访问到这个属性时,会执行这个函数;getter不能和value writable一起使用;

  • setter,是一个函数,是一个设置器,当设置这个属性时,会执行这个函数。

    const obj = {};
    obj.name = 'zhangsan';
    Object.defineProperty(obj,'age', {
        //value:'123',
        //writable:true //这两项不能同下面的get一起使用;
        enumerable:true,
        get() {
            return '当你当前访问的age这个属性,触发执行了get函数';
        },
        set(val) {
            console.log('你当前想要修改age这个属性,修改的值是:',val);
        }
    })
    
基本写法:
Object.defineProperty(res,'name',{  //res,劫持的数据存储对象;name,属性名
    get() {
        return obj.name;//obj,劫持对象
    },
    set(val) {
        obj.name = val;//修改的值
    }
})
升级版语法:

语法: Object.defineProperties(到那个对象, { 属性1: 配置项, 属性2: 配置项 });

Object.defineProperties(res,{
    name:{
        get() {
            return obj.name
        },
        set(val) {
            obj.name = val;
        }
    }
})

数据代理:

Object.proxy,语法:new Proxy(参数一:代理哪个对象)

const res = new Proxy(obj,{
    get(target,p) { 
    //target,当前代理的对象,在当前案例中就是obj;p,proxy会自动遍历对象,拿到对象的每个key;
        return target[p];
    },
    set(target,p,val) {
        target[p] = val;//修改的值
        return true;//修改成功
    }
})

回调地狱:

回调函数:

把函数A通过参数的形式传递和函数B,在函数B内部以形参方式调用,函数A就叫函数B的回调函数,通常用的回调函数的场景都是在异步代码封装。

回调地狱:

并不是一个bug,而是一种代码格式,非常不利于我们阅读,通常用Promise(期约)来解决回调地狱代码。

Promise:

一个新的异步代码封装方案,它会有三种状态,持续状态:pending;成功状态:fulfilled;失败状态:rejected;Promise也只会发生两种状态转换:持续==>成功;持续==>失败。

const p = new Promise(function(reslove,reject) {
    const timer = Math.ceil(Math.random()*3000) + 2000;
    setTimeout(()=> {
        if(timer > 3500) {
            reject('失败')
        }else {
            reslove('成功')
        }
    },timer)
})
p.then(function(res) {
    console.log(res);
}).catch(function(res) {
    console.log(res)
})
async与await:

作用:能帮助我们把异步代码写的和同步代码一样

函数开头必须写async,表明内部可以书写await;await后面需要跟promise,表示等到的意思,执行到fn(),虽然是异步程序,但是因为用await关键字,此时不会继续往下执行,而是等到fn()执行完毕再往下执行。

function fn() {
    const p = new Promise(function(reslove,reject) {
        const timer = Math.ceil(Math.random()*3000) + 2000;
        setTimeout(()=> {
            if(timer > 3500) {
                reject('失败')
            }else {
                reslove('成功')
            }
        },timer)
    })
}
async function newFn() {
    let r1 = await fn()
    console.log(r1)
}
newFn()
async/await的问题:没有办法捕获到错误,只能接受promise的成功状态,如果报错,会中断程序执行。

解决方法:1,try...catch;首次执行的时候会走try这个分支,如果这个位置有报错,会结束执行try分支,然后走catch分支,如果再运行try分支时,没有报错,那么catch不会运行。

//使用上面一个函数fn()
async function newFn() {
    try {
        let r1 = await fn()
        console.log(r1)
    } catch (error) {
        console.log(error)
        console.log('如果失败了, 执行这行代码, 提示用户网络有问题')
    }
}
newFn()

解决方法2:更改promise的封装;让这个promise不管什么情况都返回resolve,让我们通过返回的参数,区分现在时成功还是失败;开发中,对象内的code如果为0,一般代表失败,对象内的code如果为1,一般代表成功。

function fn() {
    const p = new Promise(function (reslove, reject) {
        const timer = Math.ceil(Math.random() * 3000) + 1000
        setTimeout(() => {
            if (timer > 3500) {
                reslove({
                    code: 0,
                    msg: '失败'
                })
            } else {
                reslove({
                    code: 1,
                    msg: '成功'
                })
            }
        }, timer)
    })
    return p
}
async function newFn() {
    let r1 = await fn()
    if (r1.code === 0) {
        console.log('您的网络有问题')
    } else {
        console.log(r1.msg)
    }
}
newFn()