前端一揽子精选文章
created At : 2022/01/11
updated At : 2022/01/11
JS作用域
https://juejin.cn/post/6844903797135769614
深入理解JavaScript作用域和作用域链
简单概述:电面提的问题没回答上来的一个知识点。
作用域是在运行时代码中的某些特定部分中变量,函数和对象的可访问性
三大作用域
1.块级作用域(ES6 新增let/const
实现)
块语句(大括号“{}”中间的语句)
自从有了 let
以后块里面的变量就只能真的在块里了
{
let a='nihao2022';
console.log(a); //output:nihao2022
}
console.log(a) //a是没有被定义的 找不到a
而var
这个老的变量定义方式 还是可以冲破这个限制的,块限制不住它,作用域大于块
if (true) {
//'if' 条件语句块不会创建一个新的作用域
var name = 'Hammad'; // name 依然在全局作用域中
}
console.log(name); // logs 'Hammad'
2.全局作用域 :主要还是暴露在最外层的所有定义 ,都是全局的 ,然后几乎哪都能get到这些函数/变量,都有权限访问到这些被全局声明的
3.局部作用域
也称之为函数作用域 在一个函数里定义的 外边是访问不到函数里面定义的变量的
PS
牵扯到面试另一个没回答的问题,IIFE立即执行(()=>{})()
这个可以很好地封闭函数里的变量不被污染 外边是访问不到内部的变量的,现代工具库喜欢弄这个,jq就是这样的。
for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
// abc
// abc
// abc
1. js引擎子作用域优先 输出abc说明循环体内部是子作用域
2. i 可以被 let
两次说明分属两个作用域
( let 不能在同一作用域内重复声明一个已有标识符:会报错)
作用域链
就是使用一个变量但本作用域无此变量(即自由变量) 就像链上寻找(父级)之类的
特殊:在函数里找父级链上变量是基于定义时的作用域(称之为静态作用域)
var x=100;
const f1=()=>{
console.log(x);
}
const f2=()=>{
let x=999;
f1();
}
f2(); //输出100
与调用时作用域无关/与定义时作用域相关
原理:js的运行机制时解释+执行 解释时就已经确定了作用域!!!!!
执行时自然按照解释后的作用域来执行(this的指向是执行时确定的,而作用域访问的变量是编写代码的结构确定的)
Proxy与this
proxy与this有这奇妙关系 为啥这么说呢 因为proxy代理后的对象运行this与源对象运行时的this并不相同
这会造成一些问题 比如原生隐藏的内部属性需要用到this 而 通过代理这层访问不到真this也就是源对象 代理后this也成为了proxy
有些原生对象的内部属性,只有通过正确的this
才能拿到,所以 Proxy 也无法代理这些原生对象的属性。
代理前:obj的this =>obj
代理后:proxy的this =>proxy
一个原生Date对象的代理
const d1=new Date()
const p1=new Proxy(d1,{})
p1.getDate(); // TypeError: this is not a Date object.
const target = new Date();
const handler = {
get(target, prop) {
if (prop === 'getDate') {
return target.getDate.bind(target);
}
return Reflect.get(target, prop);
}
};
const proxy = new Proxy(target, handler);
proxy.getDate() // get到的
我们需要自己手动绑定真对象才能拿到内部函数属性
Reflect实际作用是一个基础的默认操作,通过基础操作Reflect叠加我们可以在默认操作做一点副作用 也就是在trap函数加点东西+一个返回Reflect的默认操作就是我们完整所要写的代理逻辑了,不过不用默认操作也是可以。
Vue响应式
Vue3.0的响应式我认为更像是一个连锁反应 ,一个带动另一个 ,一个值的变化带动依赖该值的另一个值变化,从而形成响应式。原理基于ES6推出的Proxy代理,就是给默认行为添加副作用,这些副作用是实现连锁反应的逻辑操作。可能是以前学过的发布订阅模型实现~ 现在印象不是很深。
来自VUE文档
1.当一个值被读取时进行追踪:proxy 的
get
处理函数中track
函数记录了该 property 和当前副作用。2.当某个值改变时进行检测:在 proxy 上调用
set
处理函数。3.重新运行代码来读取原始值:
trigger
函数查找哪些副作用依赖于该 property 并执行它们。
const dinner = {
meal: 'tacos'
}
const handler = {
get(target, property, receiver) {
track(target, property)
return Reflect.get(...arguments)
},
set(target, property, value, receiver) {
trigger(target, property)
return Reflect.set(...arguments)
}
}
const proxy = new Proxy(dinner, handler)
console.log(proxy.meal)
// tacos
用自己的话翻译一下:
其中有两个trap函数
1.当有地方读取这个响应式数据时,代理的hadler的get函数的副作用用track
函数记录下这个值在哪里被用到,告诉声明Vue我这里用了这个响应式数据,到时候更新带我一个的意思。|(记录位置)
2.当该响应式数据被改变时,trigger函数被触发,随即会寻找哪些需要使用这个响应式数据的地方。去找那些喊着更新带我一个的地方。|(找位置)
3.OK现在也找到它们的位置了,直接进行新值的更新。|(换新值)
总结:track实际上就是订阅的动作,响应式的更新trigger就是发布的意思,响应式的过程就是向所有订阅者发布新值的过程。这个过程在Vue3是proxy
实现,而2是Object.defineProperties
实现的
CSS对齐
https://juejin.cn/post/6844904084885995528
-
基线:书写英语字母时,字母 X 底部所在的位置,可以了解下 字母’x’在CSS世界中的角色和故事
-
行高( line-height ):两行文字基线之间的距离
一个基线问题案例
<ul class="box">
<li>文本</li>
<li></li>
</ul>
<style>
.li {
font-size: 20px;
width: 160px;
height: 160px;
display: inline-block;
border: 1px solid #ccc;
}
</style>
原因:第一个元素基线是子元素”文本“的基线,也就是基于we的baseline基线,而第二个是盒子的底边缘作为baseline,默认基线对齐,两个元素基线位置不一致,所有会产生上图现象