这一次的有点杂,也会用到一些闭包的知识,前一篇里面有说,感兴趣的话可以去翻看。主要讲沙箱模式,柯里化,防抖与节流,数据驱动视图,回调地狱的一些知识。
沙箱模式
利用间接返回一个函数,然后拿到外部函数内的私有变量。
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()