前端学习笔记(二十三)--css,this 指向,闭包 等

327 阅读7分钟

继续仿掘金,虽说也不是全仿了。

0. webpack 打包自带严格模式

我说怎么 this 都是 undefined,我还想用 window 来着。

1. flex 设置垂直居中,同时不改变元素尺寸。

flex 的 align-items 可以设置垂直居中这很好。但是 flex 会改变元素某一设置方向的尺寸,想要不受 flex 的影响,可以给不想受影响的元素设置 flex: none

2. 元素重叠

元素重叠是定位元素才会发生的问题。这也是为什么 z-index 只能对定位元素生效。 对 position:static 是没有用的。
所以一旦发现重叠,应该意识到这些是定位元素。
(虽然这么说,但是 :hover 无需定位也可以使用 z-index,比如说设置了一个 :hover 时候的盒阴影,被别的元素盖住了,可以直接用 z-index)

3. 文本超出转成省略号

text-overflow: ellipise 的使用必须配合 overflow: hidden;
而为了产生单行溢出的情况,可以设定宽度 width: XXpx 并设置 white-space: nowrap

4. 回到顶部事件可以节流

回到顶部的按钮,并不需要多精确在多少像素的时候一定要出现。完全可以设置一个较大的延迟。

5. 函数

因为用到了节流,而节流用到了闭包,发现其中 this 的指向和想象中有点不一样。于是去看了看红宝书的函数章节。

5.1 arguments

arguments 是函数的传参的数组。
函数本身对参数没有任何验证。即使定义的时候有 10 个形参,只要实参只有 1 个,那么 arguments 也就只有一个元素。
arguments 存在的一个好处就是弥补了 js 没有函数重载的缺点。

  • 为什么 js 没有重载?因为函数本身也是个对象。定义两个函数只会让后面的函数覆盖前面的函数。

5.2 默认参数(ES6)

  1. 首先这是 es6 的语法。
  2. 其次,如果想要在定义了默认参数的情况下,只传入第二个参数。可以给第一个参数传 undefined:
function add(a=1, b=2) {
  return (a+b)
}
add(undefined, 3) // 第一个实参为 undefined,就会使用默认值: return(1+3),而不是: return(undefined+3)
  1. 默认参数不会被 arguments 使用,arguments 真的就是传入什么就是什么。
  2. 后面的默认参数可以使用前面的默认参数求值:
function add(num1, num2=num1+100) {
  return(num1 + num2)
}
add(100) // 输出 300

5.3 函数内部对象

箭头函数没有这些对象

5.3.1 arguments

虽然前面说了 arguments 是一个类数组对象,但是 arguments 本身还有一个属性叫做 callee。指向的正是 arguments 所在的函数本身。可以用于防止递归函数的紧密耦合。(不过不推荐,影响性能,在严格模式下也不能使用)

5.3.2 this 指向(重要)

this 在不同情况下指向不一样。
在 function 定义的函数中,this 指向的是把函数作为方法的上下文对象。(也就是说即使是同一个函数,在不同地方调用也不一样
箭头函数的 this 则为定义该箭头函数时候的 this 指向。

window.name = "餅月ひまり"
let aqua = {
  name: "湊あくあ"
}
function myName() {
  console.log(this.name); // 在不同调用中不一样
}

myName() // 相当于 window.myName()。因此 this 为 window,因此输出 "餅月ひまり"

aqua.myName = myName; // 先把函数给它
aqua.myName() // 此时被 aqua 对象调用,因此 this 为 aqua,因此输出 "湊あくあ"

5.3.3 this 的几种情况(重要)

1. 给 window 加上滚动监听函数。

此时函数里的 this 就是 window。因为 此时 监听函数是被 window 调用的。

2. setTimeout 里不用箭头函数的 this 为什么是 window。(重要)

  • 这句话其实是不准确的,准确点应该说 setTimeout 的非箭头回调函数被调用的时候 this 指向 window。

就是因为 setTimeout 的回调函数是被 window 定时调用的。
那么为什么箭头函数就可以避免这种情况?因为在设置定时器的时候此时的 this 还是此时函数上下文的 this,见下图

setTimeout(function () {
  console.log(this); // 无论setTimeout在哪里定义,都会输出 window。
                     // 因为 function(){} 和执行对象有关
}, 1000);



setTimeout(() =>{
  console.log(this); // timer 所在的上下文的 this 是什么,这里就是什么
                     // 但这并不表示此时函数不是被 window 调用了。此时仍然是被 window 调用,这点是不会变的
                     // ****这完全因为箭头函数根本不 care 是谁调用了我****
                     // 当我们定义这个 setTimeout 计时器的时候,this 已经被绑定好了
                     // 此后无论函数在哪里调用,this 永远都是指向刚定义的时候的上下文
}, 1000)

可以理解为 function 的 this 只关心函数在哪里调用。而箭头函数的 this 只关心函数在哪里定义

5.3.4 caller

查看调用该函数的函数。

function a() {
  b();
}

function b() {
  console.log(b.caller)
}

a() // 输出 a 的源代码

5.3.5 new.target

由于 js 中的所有函数都可以被当做构造函数实例化。
因此 new.target 可以查看该函数是否被当作普通函数调用,还是被当做构造函数实例化。

function a() {
  if (new.target === undefined) {
    console.log("被当做普通函数调用");
  }
  if (new.target === a) {
    console.log("被当做构造函数调用");
  }
}
a(); // 被当做普通函数调用
new a(); // 被当做构造函数调用

可以用这个属性来强制必须使用 new 实例化。或者相反,强迫不能实例化只能使用静态方法(抽象基类)。

5.3.6 apply 和 call

这两个方法强大之处在于可以随意替换函数调用时的 this 指向。

window.name = "himari"
let aqua = {
  name: "minato"
}

function myName() {
  console.log(this.name);
}

window.myName(); // 输出 "himari"
aqua.myName = myName;
aqua.myName(); // 输出 "minato"
myName.call(aqua); // 输出 "minato"
window.myName.call(aqua); // 输出 "minato"
window.myName.call(window); // 输出 "himari"

5.3.7 bind

bind 返回一个给函数绑定好 this 的指向的新函数。
当需要指定函数的 this 的时候,就用这个。如果只是想在某次调用函数的时候绑定this,就用上面的 apply/call。

5.4 尾调用优化

尾调用是嵌套函数中的概念,嵌套函数有一个外部函数和一个内部函数。
尾调用的意思是:外部函数的返回值恰好是一个内部函数的返回值,这个内部函数叫做尾调用函数。
只要在一段函数代码满足某些条件情况下,es6 就会自动进行尾调用优化,提前销毁外部函数的栈。
满足的条件如下:

  1. 严格模式。
  2. 外部函数的返回值是对尾调用函数的调用(必须在return 语句中被调用,不能先调用再返回)。
  3. 尾调用函数返回之后不需要额外逻辑。(返回之前可以,比如说 return inner(a+b),但是 return inner(a) + b 不行。还有 return condition?inner():anotherInner()可以 的)
  4. 不是闭包。

尾调用优化对递归函数的优化很有帮助,如何把一个递归函数转为尾调用函数可以看这里

5.5 闭包(重要)

闭包是引用了另一个函数作用域中变量的函数。
当嵌套函数中的内部函数被返回之后,这个内部函数在其他地方被调用的时候,依然可以使用外部函数作用域的变量。
这个其实是作用域链的结果,返回的函数的作用域后面接着外部函数的作用域,因此即使在别的地方调用,依然可以改变外部函数的变量。

5.5.1 为什么不被垃圾回收销毁

总有一个问题,外部函数执行完后,里面的变量为什么没有被销毁。
因为内部函数的作用域恋中有对外部函数变量的引用,这些变量不会被销毁(意思就是其他变量会被销毁)。
只有当返回的内部函数被销毁的时候,这些变量才会随着内部函数一起被销毁。(比如说节流后的函数被赋值为 null,下一次垃圾回收的时候就会发现内部函数无法被引用了,那么包括内部函数引用的这些变量也会被一起销毁)

5.5.2 闭包中的 this 和 argument 都需要注意

如果不用箭头函数,this 就和调用时候的上下文有关。
内部函数的 arguments 和外部也是不一样的需要注意。