Appearance
疑难点集合
TIP
记录疑难点集合
感谢关注三连 😁
再看 this
this
问题之前有发过一篇文章和视频讲解如果判断 this
的指向性问题。
在我以为已经掌握了 this
的指向性问题时,前两天在公司正常开发业务需求时,又遇到一个调试很久的问题,后面经过一个大佬同事的 review 之后,知道了原来是this
执行有问题。
我觉得这个问题太震惊我了,给兄弟们分享下,顺便也考考兄弟们 😋
大致的逻辑代码如下:
js
class Snow {
constructor() {
this.name = 'jimmy'
}
show() {
return this.name
}
}
const getSnow = () => {
return {
hello: 'world',
names: new Snow().show,
}
}
const { hello, names } = getSnow()
console.log('sss', names())
console.log('sss', getSnow().names())
class Snow {
constructor() {
this.name = 'jimmy'
}
show() {
return this.name
}
}
const getSnow = () => {
return {
hello: 'world',
names: new Snow().show,
}
}
const { hello, names } = getSnow()
console.log('sss', names())
console.log('sss', getSnow().names())
以上的代码在node
环境下执行分别会输出什么呢?各位大兄弟们思考下在弹幕上给出答案吧~
公布答案!正确答案是:
第一个会报错,第二个会输出undefined
😱 为啥还能报错啊!!!这里我们需要知道两句口诀,就能搞懂这个问题了!
对于普通函数来说:
函数直接执行时,this 指向全局
由于将
getShow()
解构出来了,直接执行names()
this 执行的就是全局作用域,node 环境中全局的 this 是undefiend
,所以相当于执行undefined.name
就报错啦~谁调用,this 就指向谁!
通过
getSnow().names()
,是getSnow()
调用names()
,所以this
指向的是{ hello: 'world', names: new Snow().show }
,这个对象并没有name
属性,所以返回undefined
对于箭头函数来说:
箭头函数的 this 是声明时绑定的
解决这个问题我们只需要改写一下 Snow 类中的 show 方法,将他改为箭头函数,如下:
jsclass Snow { constructor() { this.name = 'jimmy' } show = () => { return this.name } }
class Snow { constructor() { this.name = 'jimmy' } show = () => { return this.name } }
这样不管咋调用,
this
指向的都是Snow
实例,因为箭头函数的this
是在声明的时候就绑死的。
总结:
长久没看一些这种题目就是会容易忘记一些知识点,这个就是我那个大佬同事给我讲解之后我才恍然大悟,不得不感叹我那个同事太细了,跟个细狗一样[旺柴]
希望这个视频能够帮助小伙伴们理清this
指向问题,感谢各位小伙伴的关注和三连 ❤️。
TDZ
不知道大家是如何理解 TDZ(暂时性死区) 呢?过去我一直不理解到底什么是 TDZ,但是最新看的一本新书里面,我终于能够理解什么是 TDZ 了。现在我就用我认为最简单的方式来给兄弟们解释一下 TDZ 这个概念,最后出几个题目,理解之后相信各位小伙伴也能理解这个概念了。
我们知道 TDZ 这个概念是 ES6 提出来的,也就是之前一直没有,那么什么情况下会触发 TDZ 呢?我们可以看下这个简单例子:
例子 1:
jsfunction foo() { console.log(a) var a = 3 } foo()
function foo() { console.log(a) var a = 3 } foo()
这个例子的结果是什么大家大家可以先思考一下在评论或者弹幕中给出答案。
正确的结果是会输出 undefined。
理解了 undefined 我们就可以来讨论 TDZ 了,为什么在 ES6 之前没有 TDZ 这个概念呢?
因为在此之前,我们创建变量都是使用 var 这个关键字来进行创建变量。而 js 虽然是解释性的语言,读到哪执行到哪,但是在开始读之前整个解析器是会先扫描一遍我们的 js 代码,将所有的变量都进行一次 “提升”
这个也就是为什么上个例子不会报错的原因,相当于 var a 提升到最前面了。这点也就是为什么有 C 语言 或者其他大多数语言的基础的同学在接触 JS 时会觉得 JS 很奇怪的原因,但是也确实是很奇怪!在定义之前就用一些变量居然不会报错!
可能是因为听到了众多开发者的呼声,终于在 ES6 的时候,ESMAScript 提出了新的的创建变量的关键字 const 、 let,我们用这两个变量来执行一下代码:
jsfunction foo() { console.log(a) let a = 3 } foo()
function foo() { console.log(a) let a = 3 } foo()
这里就会报错了,没有错!这里就触发了暂时性死区的问题了!因为 a 在创建之前就使用它了!
我们也就能简单的理解成,在 let a 之前的部分就是 “暂时性死区” 。
不知道我理解的是否有问题,如果有讲错的地方欢迎各位小伙伴们批评指正出来呀~
TDZ 例子
有了上面的基础,我们现在来简单的看几个题目,巩固一下对于 TDZ 的理解!
例题 1:
jsfunction demo() { console.log(a) const a = 'jimmy' } demo()
function demo() { console.log(a) const a = 'jimmy' } demo()
正确答案:会报错,触发了 TDZ
例题 2:
jsfunction foo(a1 = a2, a2) { console.log(a1, a2) } foo('jimmy', 'xuexue')
function foo(a1 = a2, a2) { console.log(a1, a2) } foo('jimmy', 'xuexue')
正确答案:输出 jimmy, xuexue 这个平时正常使用的方法
例题 3:
jsfunction foo(a1 = a2, a2) { console.log(a1, a2) } foo(undefined, 'xuexue')
function foo(a1 = a2, a2) { console.log(a1, a2) } foo(undefined, 'xuexue')
正确答案:会报错,报错原因也是触发了 TDZ,当第一个参数是 undefined 的时候,这时候形参就会执行 a1 = a2, 因为 a2 还没有被定义,所以也是报错,原因本质上也是 TDZ
例题 4:
jsfunction foo(a1 = a2, a2) { console.log(a1, a2) } foo(null, 'xuexue')
function foo(a1 = a2, a2) { console.log(a1, a2) } foo(null, 'xuexue')
正确答案:null xuexue, null 不同于 undefined。
内存泄露与内存溢出
计算机使用内存的流程一般是:申请内存=>使用内存=>释放内存。
内存泄露:被申请使用的内存在使用完毕之后并没有被释放。
大量的内存泄露就会导致内存不足,导致的就是程序崩溃。
可能导致内存泄露的起因:
全局变量
变量往全局放是个很不好的习惯,正因如果,如果在严格模式下,这个是会触发警告的。
闭包
闭包是一个典型的例子,我们可以利用闭包做很多事情,但是如果使用不当,是很容易引发内存泄露的。
内存溢出:申请内存的过程中没有足够的内存情况下,就会触发内存溢出:
假设我需要申请一个 50M 的内存,但是可用的只有 40M 内存了,这时候就会触发内存溢出。
原因:和内存泄露差不多,也是内存未及时清理导致的。
关系:内存泄露会导致内存溢出。
好在 JavaScript 是一门自动做垃圾回收的语言,所以一般情况下很难遇到这种事情,除非是程序死循环之类的,才会比较容易触发。
为什么 vue3 中有了 reactive 来创建响应式变量,还需要有单独的 ref 来创建响应式变量
这问题可能会出现在面试题上
vue3 的响应式系统是基于 proxy
代理对象来实现的,其实从 代理对象 这块我们就能够看出端倪了。必须是 对象类型 才能被代理。
而我们使用 ref
时一般情况下都绑定一些基础类型,如:字符串、数字 类型等。如:
ts
const name = ref('jimmy') // 一般ref 都是绑定这种基础类型
name.value = 'Jack'
const name = ref('jimmy') // 一般ref 都是绑定这种基础类型
name.value = 'Jack'
而对象类型一般都是使用reactive
:
ts
const user = reactive({
name: 'jimmy',
age: 24,
})
const user = reactive({
name: 'jimmy',
age: 24,
})
从 ref
的源码实现上看,其实 ref
就是对于 reactive
的一层封装,简单的思路其实就是如下:
ts
function ref(val: string | number) {
const wrapper = {
value: val,
}
// 标志是一个 ref 对象,不可枚举、不可写
Object.defineProperty(wrapper, '_v_isRef', {
value: true,
})
return reactive(wrapper)
}
function ref(val: string | number) {
const wrapper = {
value: val,
}
// 标志是一个 ref 对象,不可枚举、不可写
Object.defineProperty(wrapper, '_v_isRef', {
value: true,
})
return reactive(wrapper)
}
CSS - 包含块
一个元素的几何信息是受包含块所影响的。一般情况下,我们可能以为一个元素的包含块就是它的父元素。其实不准确。一个元素的包含块跟元素自身有关系。如下:
类型 | 对应包含块 |
---|---|
标准文档流布局 | 最近的祖先元素 |
绝对定位的元素 | 最近开启定位的组件元素 |
fixed 固定定位的元素 | 视口(vw、vh) |
如果有错欢迎补充
关键的是:一些高度为百分比大小的元素,我们不能认为一定是取它父元素的大小!而是要相对的是它包含块的大小。
简单 demo:
html
<html lang="en">
<head>
<title>包含块-1</title>
<style>
.parent {
width: 600px;
height: 100px;
background-color: #2ecc71;
position: relative;
}
.child {
width: 50%;
height: 50%;
background-color: #3498db;
}
.son {
width: 50%; /** 300px */
height: 50%; /** 50px */
background-color: #9b59b6;
position: absolute;
}
</style>
</head>
<body>
<div class="parent">
<div class="child">
<div class="son"></div>
</div>
</div>
</body>
</html>
<html lang="en">
<head>
<title>包含块-1</title>
<style>
.parent {
width: 600px;
height: 100px;
background-color: #2ecc71;
position: relative;
}
.child {
width: 50%;
height: 50%;
background-color: #3498db;
}
.son {
width: 50%; /** 300px */
height: 50%; /** 50px */
background-color: #9b59b6;
position: absolute;
}
</style>
</head>
<body>
<div class="parent">
<div class="child">
<div class="son"></div>
</div>
</div>
</body>
</html>