变量提升
- 变量提升是指在 JavaScript 代码执行过程中,JavaScript 引擎把变量声明和函数声明提升到当前作用域最前面的行为。
- 函数声明提升优先级高于变量声明,且不会被同名变量声明覆盖,但是会被变量赋值后覆盖。
- 下面总结变量提升的过程:
-
在当前作用域查找所有的变量声明和函数声明,把这些声明都提到最前面,函数声明在变量声明之前。
-
如果函数声明或者变量声明存在同名的,则同名的函数声明或者同名变量声明都是后者覆盖前者,但是变量声明和函数声明同名,变量声明不能覆盖函数声明。
作者:码海浮萍
链接:juejin.cn/post/721891…
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
作用域与作用域链
作用域定义了变量的可见性或可访问性。大白话来说,就是一个变量能不能被访问或引用,是由它的作用域决定的。
在 JavaScript 中有三种作用域。
- 全局作用域
- 函数作用域(局部作用域)
- 块作用域
当一个变量在当前作用域无法找到时,便会尝试寻找其外层的作用域,如果还找不到,再继续往外寻找(只会往外寻找,不会寻找兄弟作用域,更不会往内寻找)。这种如同链条一样的寻找规则便被称为作用域链。
词法作用域
能访问外部变量的函数就叫闭包,所有的函数好像都有这个特性,所以一般能利用函数的这个特性将某个独立作用域的变量取出来,同时这个变量就会被存入堆内存不会被销毁。那么当多个函数嵌套起来,词法作用域链由此诞生:内部的函数可以一层一层的访问外部函数的变量。
this
-
普通函数内部的this指向被调用时的对象 // Object.fn()
如果没有被对象调用,那就指向全局作用域
-
箭头函数的this永远指向定义时的父作用域
var a = 1;
function printA(){
console.log(this.a);
}
var obj={
a:2,
foo:printA,
bar:function(){
printA();// 和自调用函数执行一样,this都是指向window
}
}
obj.foo(); // 2
obj.bar(); // 1
var foo = obj.foo;
foo(); // 1
***************************
var x = 3;
var y = 4;
var obj = {
x: 1,
y: 6,
getX: function() {
var x = 5;
return function() {
return this.x;
}(); // 自执行函数this指向window
},
getY: function() {
var y = 7;
return this.y;
}
}
console.log(obj.getX()) // 3
console.log(obj.getY()) // 6
new
1.新建一个空对象
2.将构造函数的原型和这个空对象进行绑定
3.执行构造函数,将this指向空对象
4.返回这个对象
function _new (Constructor, ...params) {
const obj = Object.create(Constructor.prototype);
const resultObj = Constructor.apply(obj, params);
return typeof resultObj === 'Objiect' ? reslutObj : obj;
}
asyc函数
没有继发关系的异步可以按如下写法,解决asyc函数内await的阻塞问题
// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
if else 判断过多 策略模式
1,用对象存储一系列方法逻辑 2,用一个方法,对象键值映射对应方法逻辑
防抖
function debounce(func, wait=0) {
if (typeof func !== 'function') {
throw new TypeError('need a function arguments')
}
let timeid = null;
let result;
return function() {
let context = this;
let args = arguments;
if (timeid) {
clearTimeout(timeid);
}
timeid = setTimeout(function() {
result = func.apply(context, args);
}, wait);
return result;
}
}
// 处理函数
function handle() {
console.log(Math.random());
}
// 滚动事件
window.addEventListener('scroll', debounce(handle, 1000));
节流
// 节流throttle代码(定时器):
当触发事件的时候,我们设置一个定时器,再次触发事件的时候,如果定时器存在,就不执行,直到delay时间后,定时器执行执行函数,并且清空定时器,这样就可以设置下个定时器。当第一次触发事件时,不会立即执行函数,而是在delay秒后才执行。而后再怎么频繁触发事件,也都是每delay时间才执行一次。当最后一次停止触发后,由于定时器的delay延迟,可能还会执行一次函数。
var throttle = function(func, delay) {
var timer = null;
return function() {
var context = this;
var args = arguments;
if (!timer) {
timer = setTimeout(function() {
func.apply(context, args);
timer = null;
}, delay);
}
}
}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000));
区别:
函数防抖:将几次操作合并为一此操作进行。原理是维护一个计时器,规定在delay时间后触发函数,但是在delay时间内再次触发的话,就会取消之前的计时器而重新设置。这样一来,只有最后一次操作能被触发。
函数节流:使得一定时间内只触发一次函数。原理是通过判断是否到达一定时间来触发函数。
区别: 函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数。 比如在页面的无限加载场景下,我们需要用户在滚动页面时,每隔一段时间发一次 Ajax 请求,而不是在用户停下滚动页面操作时才去请求数据。这样的场景,就适合用节流技术来实现
缓存
www.jianshu.com/p/7bc3b8983… 比如:浏览器第一次请求a.jpg 时,服务器会发送完整的文件并附带额外信息
Cache-Control: max-age=300;
浏览器把文件和附带信息保存起来。当再次需要a.jpg 时,如果是在300秒以内发起的请求则直接使用缓存(200, from xx cache),否则重新发起网络请求(200)。下面是Cache-Control常见的几个值:
- Public表示响应可被任何中间节点缓存,如 Browser <-- proxy1 <-- proxy2 <-- Server,中间的proxy可以缓存资源,比如下次再请求同一资源proxy1直接把自己缓存的东西给 Browser 而不再向proxy2要。
- Private表示中间节点不允许缓存,对于Browser <-- proxy1 <-- proxy2 <-- Server,proxy 会老老实实把Server 返回的数据发送给proxy1,自己不缓存任何数据。当下次Browser再次请求时proxy会做好请求转发而不是自作主张给自己缓存的数据。
- no-cache表示不使用 Cache-Control的缓存控制方式做前置验证,而是使用 Etag 或者Last-Modified字段来控制缓存
- no-store ,真正的不缓存任何东西。浏览器会直接向服务器请求原始文件,并且请求中不附带 Etag 参数(服务器认为是新请求)。
- max-age,表示当前资源的有效时间,单位为秒。
优点:缓存控制功能更强大
缺点:假如浏览器再次请求资源a.jpg的时间间隔超过了max-age,这时候向服务器发送请求服务器应该会重新返回a.jpg的完整文件。但如果 a.jpg 在服务器上未做任何修改,发送a.jpg的完整文件就太浪费带宽了,其实只要发送一个「a.jpg未被更改」的短消息标示就好了。
缓存+更新机制终极版
比如:浏览器第一次请求a.jpg 时,服务器会发送完整的文件并附带额外信息,其中Etag 是 对a.jpg文件的编码,如果a.jpg在服务端未被修改,这个值就不会变
Cache-Control: max-age=300;
ETag:W/"e-cbxLFQW5zapn79tQwb/g6Q"
git
警惕:错误的Git版本回退姿势 - 许怀远的文章 - 知乎 zhuanlan.zhihu.com/p/34844206
proxy defineProperty
-
Proxy 是对整个对象的代理,而 Object.defineProperty 只能代理某个属性。所以我们在编写响应式函数的时候,defineProperty 需要用for in 去给每个属性添加监听
-
对象上新增属性,Proxy 可以监听到,Object.defineProperty 不能。
-
数组新增修改,Proxy 可以监听到,Object.defineProperty 不能。
-
vue中对数组没有进行defineProperty,而是重写了数组的7个方法。 分别是:
-
push
-
shift
-
pop
-
splice
-
unshift
-
sort
-
reverse
因为这些方法都会改变数组本身。
数组里的索引和长度是无法被监控的。
-
若对象内部属性要全部递归代理,Proxy 可以只在调用的时候递归,而 Object.definePropery 需要一次完成所有递归,性能比 Proxy 差。 这个我们可以对比两个递归,definePropery 是在一开始,将传入的对象,所有属性,包括内部属性全部进行递归。之后才取处理set get。 但是Proxy的递归是在set中,这样,我们就可以根据需求,来调整递归原则,也就是说,在一些条件下,让其不进行递归。 举个很简单的例子。 我们页面上需要渲染一个对象,这个对象总是 会被整体重新赋值。不会单独的去修改其中的属性。那么我们就可以通过Proxy控制不让其递归这个对象,从而提高性能
-
Proxy 不兼容 IE,Object.defineProperty 不兼容 IE8 及以下
-
Proxy 使用上比 Object.defineProperty 方便多。
作者:云积分大前端团队
链接:juejin.cn/post/699433…
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
-
CSS columns分栏布局和break-inside属性
-
最外层列表盒子加上属性: column-count:2; 将页面分为两列
-
列表中的每一个单独的小盒子添加属性:break-inside:avoid; 控制文本块分解成单独的列,以免项目列表的内容跨列,破坏整体的布局**
-
图片的宽度设置:width:100%
flex
symbol
Symbol 生成一个全局唯一的值。
var race = {
protoss: Symbol(),
terran: Symbol(),
zerg: Symbol()
}
race.protoss !== race.terran // true
race.protoss !== race.zerg // true