命令式和声明式
从范式上来看,视图层框架通常分为命令式和声明式,它们各有优缺点。作为框架设计者,应该对两种范式都有足够的认知,这样才能做出正确的选择,甚至想办法汲取两者的优点并将其捏合。
命令式和声明式各有优缺点,在框架设计方面,则体现在性能与可维护性之间的权衡。这里我们先抛出一个结论:声明式代码的性能不优于命令式代码的性能。 命令式。
早年间流行的 jQuery 就是典型的命令式框架。命令式框架的一大特点就是关注过程。例如,我们把下面这段话翻译成对应的代码:
从范式上来看,视图层框架通常分为命令式和声明式,它们各有优缺点。作为框架设计者,应该对两种范式都有足够的认知,这样才能做出正确的选择,甚至想办法汲取两者的优点并将其捏合。
命令式和声明式各有优缺点,在框架设计方面,则体现在性能与可维护性之间的权衡。这里我们先抛出一个结论:声明式代码的性能不优于命令式代码的性能。 命令式。
早年间流行的 jQuery 就是典型的命令式框架。命令式框架的一大特点就是关注过程。例如,我们把下面这段话翻译成对应的代码:
Array.prototype.selfReduce = function (callback, initValue) {
// 获取源数组
const originArray = this;
// 判断源数组是否为空,如果为空,抛出异常
if (!originArray.length) {
throw new Error('selfReduce of empty array with no initial value');
}
// 声明累计器
let accumulator
// 是否有初始值情况
// 设置累计器初始值(如果有初始值,第一次调用`callback`时,`callback`的第一个参数的值为初始值,否则为源数组的第一项)
if (initValue === undefined) {
accumulator = originArray[0];
} else {
accumulator = initValue;
}
// 遍历数组,执行`callback`函数
for (let i = 0; i < originArray.length; i++) {
// 如果没有初始值且是最后一次循环,不再执行callback
if (initValue === undefined && (i + 1) === originArray.length) break;
// 循环执行 `callback`
// 这里判断一下`currentValue`
// 因为有初始值时,`currentValue`是`originArray[i]`
// 没有初始值时`currentValue`是`originArray[i + 1]`
accumulator = callback(accumulator, initValue === undefined ? originArray[i + 1] : originArray[i], i, originArray);
}
// 把累计器返回出去
return accumulator
}
// 找出最大值
const r = [2, 4, 8, 1].selfReduce((a, b) => Math.max(a, b))
console.log(r) // 输出: 8
// 数组元素求和
const arr = [1, 2, 3, 4];
const initVal = 0;
const total = arr.reduce((acc, cur, index, array) => {
console.log(acc, cur);
// 0 1
// 1 2
// 3 3
// 6 4
return acc + cur;
}, initVal);
console.log(total); // 输出:10
一般情况下,我们或许会使用instanceof,是没有问题的。
async function back(){
return 1;
}
const res = back();
console.log(res instanceof Promise); //true
但是问题来了,Promise是一个规范而不只是一个类。
遵循Promise规范的库包含了ES6默认Promise、bluebird Promise、Q Promise等,那么我们使用bluebird Promise生成的Promise去instanceofES6的默认Promise会不会有问题呢? 显然,要出错。
所以这引出了React官方使用的方式是通过判断条件:
typeof destroy.then === 'function'
来判断一个对象是否是异步返回对象。
这样子的好处是,对于所有实现了Promise规范的异步库,这样的判断方式都是有效的。虽然这有产生误报的风险,但这是所有Promise库都必须遵循的规范。
同样的Promise判断方式并不只是React在使用,可以试试在F12运行这行代码,这将不会有任何输出。
await {then:()=>1};
原因无他,await的语法糖里判断Promise对象也是通过promise.then==='function',这源于Promise A+最基本的定义:
为什么要有这些模式,目的:职责划分、分层(将Model层、View层进行分类)借鉴后端思想,对于前端而已,就是如何将数据同步到页面上
MVVM模式 映射关系的简化,隐藏了controller
MVVM是Model-View-ViewModel缩写,也就是把MVC中的Controller演变成ViewModel。Model层代表数据模型,View代表UI组件,ViewModel是View和Model层的桥梁,数据会绑定到viewModel层并自动将数据渲染到页面中,视图变化的时候会通知viewModel层更新数据。
总结: MVVM模式简化了界面与业务的依赖,解决了数据频繁更新。MVVM 在使用当中,利用双向绑定技术,使得 Model 变化时,ViewModel 会自动更新,而 ViewModel 变化时,View 也会自动变化。
1.Beacon API
用于将少量数据通过 post 请求发送到服务器。
2.Beacon
是非阻塞请求,不需要响应
完美的解决性能缺陷问题:
1.浏览器将Beacon
请求排队让它再空闲的时候执行并立即返回
2.它再unload
状态下也可以异步发送,不阻塞页面刷新/跳转等操作
使用 navigator.sendBeacon()
const blob = new Blob(['room_id=123'], {
type: 'application/x-www-form-urlencoded',
})
const result = navigator.sendBeacon('http://127.0.0.1:8686', blob)
result
是一个布尔值,代表这次发送请求的结果
浏览器接受并把请求排队了 返回true
如果过程中出现了问题 返回false
navigator.sendBeacon
接受两个参数:
url
:请求的url
,请求是post
请求
data
:要发送的数据
Beacon
其他相关
Beacon
更多的情况是用于做前端埋点,监控用户活动
客户端优化:可以将 Beacon
请求合并到其他请求上,一同处理, 尤其在移动环境下。
函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性就是计算机科学文献中称的 “闭包”。这个术语非常古老,是指函数变量可以被隐藏于作用链之内,因此看起来是和函数将变量“包裹”起来。
在 JavaScript 中,只有函数内部的局部变量不能直接访问到,而函数中的子函数能读取局部变量,因此可以把闭包简单理解成 "定义在一个函数内部的函数"。
《JS 高级程序设计》里面的定义是:一个可以访问另一个函数里局部变量的函数既闭包。
阮一峰的网络日志:在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
闭包是一种形式,它是以函数的方式表现出来。
function A() {
var num = 100;
return function B() {
// 返回值函数B
var num1 = num; // 在函数B中访问函数A的局部变量
console.log(num1);
return num1;
};
}
var fn1 = A(); // 此处调用函数A,执行的结果就是函数B
var fn2 = fn1(); // 此处调用函数fn1(),执行结果就是引用函数B
闭包会使得函数中的变量都被保存在内存中,每个父函数调用完,就会形成新的闭包。父函数中的变量始终会在内存中,相当于缓存。内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,尤其是IE 在我们使用完闭包之后,依然回收不了闭包里面引用的变量。
但是我认为: 使用了闭包那里面的变量明明就是我们需要的变量。不能说是内存泄漏,内存泄露是指: 你用不到(访问不到)的变量,依然占居着内存空间,不能被再次利用起来。
IE 内存泄漏的解决方法:在退出函数之前,将不使用的局部变量全部删除。大部分浏览器,通过赋值为 null,释放内存。在 IE 中 在 return 返回值下面 delete 变量。
闭包可以在父函数外部,改变父函数内部变量的值。 所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,因为都在内存中,所以不要随便改变父函数内部变量的值,否则一处变其它地方也会随之变化。
其实我觉得,根本不需要知道闭包这个概念,一样可以使用闭包!
因为函数也是一个 var,只是它比较特殊,函数里面的 var 不能被外接直接访问,然后在函数里面定义了一个函数来访问。其实跟调用一个对象里面定义的函数然后访问对象里面 key,区别就是对象里面的一个 key 可以被外面显示调用(造成不能隐蔽的问题),闭包解决这个问题。
闭包是 JS 函数作用域的副产品。
由于 JS 的函数内部可以使用函数外部的变量,正好符合了闭包的定义,而不是 JS 故意要使用闭包。
闭包让程序更加安全,封装数据、暂存变量。不让外部直接访问到我们的数据。
函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性就是计算机科学文献中称的 “闭包”。这个术语非常古老,是指函数变量可以被隐藏于作用链之内,因此看起来是和函数将变量“包裹”起来。
在 JavaScript 中,只有函数内部的局部变量不能直接访问到,而函数中的子函数能读取局部变量,因此可以把闭包简单理解成 "定义在一个函数内部的函数"。
《JS 高级程序设计》里面的定义是:一个可以访问另一个函数里局部变量的函数既闭包。
阮一峰的网络日志:在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
闭包是一种形式,它是以函数的方式表现出来。
function A() {
var num = 100;
return function B() {
// 返回值函数B
var num1 = num; // 在函数B中访问函数A的局部变量
console.log(num1);
return num1;
};
}
var fn1 = A(); // 此处调用函数A,执行的结果就是函数B
var fn2 = fn1(); // 此处调用函数fn1(),执行结果就是引用函数B
闭包会使得函数中的变量都被保存在内存中,每个父函数调用完,就会形成新的闭包。父函数中的变量始终会在内存中,相当于缓存。内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,尤其是IE 在我们使用完闭包之后,依然回收不了闭包里面引用的变量。
但是我认为: 使用了闭包那里面的变量明明就是我们需要的变量。不能说是内存泄漏,内存泄露是指: 你用不到(访问不到)的变量,依然占居着内存空间,不能被再次利用起来。
IE 内存泄漏的解决方法:在退出函数之前,将不使用的局部变量全部删除。大部分浏览器,通过赋值为 null,释放内存。在 IE 中 在 return 返回值下面 delete 变量。
闭包可以在父函数外部,改变父函数内部变量的值。 所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,因为都在内存中,所以不要随便改变父函数内部变量的值,否则一处变其它地方也会随之变化。
其实我觉得,根本不需要知道闭包这个概念,一样可以使用闭包!
因为函数也是一个 var,只是它比较特殊,函数里面的 var 不能被外接直接访问,然后在函数里面定义了一个函数来访问。其实跟调用一个对象里面定义的函数然后访问对象里面 key,区别就是对象里面的一个 key 可以被外面显示调用(造成不能隐蔽的问题),闭包解决这个问题。
闭包是 JS 函数作用域的副产品。
由于 JS 的函数内部可以使用函数外部的变量,正好符合了闭包的定义,而不是 JS 故意要使用闭包。
闭包让程序更加安全,封装数据、暂存变量。不让外部直接访问到我们的数据。
用户退出当前页面时,修改的数据未进行保存,需要发送接口请求实现自动保存的功能。简单分析需求可知,退出页面包含 路由切换和关闭浏览器标签页两种情况:
Vue2
开发,离开页面时可以在 beforeDestroy
钩子函数中调用接口实现保存数据,但是这个方法只能在路由切换当前组件销毁前触发,无法监听到浏览器页面关闭的情况。window.onunload
钩子函数中发送请求。/**
* js睡眠函数
* @param numberMillis {number} 毫秒值
* @returns {boolean}
*/
function sleep (numberMillis) {
let now = new Date();
let exitTime = now.getTime() + numberMillis;
while (true) {
now = new Date();
if (now.getTime() > exitTime)
return true;
}
}