前端|想到什么写什么

记录当初伤害过我的一些概念

一、闭包

闭包是指有权访问另一个函数作用域中变量的函数。

var i = 0;
function a() {
  console.log(i);
}
a();
// 这就是一个简单的闭包,该函数使用了外部的数据

我们先说一下闭包的特点
(1)通过闭包可以让外部环境访问到函数内部的局部变量。
(2)通过闭包可以让局部变量持续保存下来,不随着它的上下文环境一起销毁。

理解闭包以及它的特点我们需要先了解两个概念:
(1)作用域链:比如你在一个函数中用到了一个变量,但是在当前作用域中没有找到它的值,就会向上级作用域去查,直到查到全局作用域,这个过程就形成了一条作用域链。
(2)垃圾回收:垃圾收集器会周期性地找出那些不再使用的变量,然后释放其内存。如果该变量还在使用,那么就不会被回收。

然后我们来看个栗子:

function a() {
  var i = 0;
}
a();
console.log(i); // 报错i is not defined

输出会报错,为什么?因为执行完a函数后,i 作为局部变量不再使用会被销毁(垃圾回收),所以在输出就会报错not defined。

function a() {
  var i = 0;
  return function b() {
    console.log(i); // 输出0
  }
}
var c = a();
c(); 

显而易见,b函数中用到了a函数中的变量,于是形成了闭包。为什么执行完a函数后i 没被销毁呢?因为执行完a函数后,函数b被赋值给了c,c是全局变量不会被销毁,也就是函数b中的i 还会继续被使用,所以不会被回收。可以看出外部环境也可以访问到函数内部的局部变量。

理解完这个再回看它的概念以及特点就比较好理解了,同时也会发现由于变量不会被销毁,所以过度的使用闭包就可能会产生内存泄漏的问题。

完事儿来做两个题吧!!:
(1)请补全JavaScript代码,要求每次调用函数"closure"时会返回一个新计数器。每当调用某个计数器时会返回一个数字且该数字会累加1。
注意:1. 初次调用返回值为1;2. 每个计数器所统计的数字是独立的。

//来自牛客网上的一道js题
<!DOCTYPE html>
<html>
    <head>
        <meta charset=utf-8>
    </head>
    <body>
        <script type="text/javascript">
            const closure = () => {
                // 补全代码
            }
        </script>
    </body>
</html>

答案(没有标准答案哦,实现就行):

const closure = () => {
    var i = 0;
    return function () {
      return ++i;
    }
 }
let a = closure();
a();

(2)执行以下代码,会输出什么结果?

for (var i = 1; i <= 3; i++) {
    setTimeout(function () {
        console.log(i);
    }, 1000);
}

答案: 4 4 4
解析:在匿名函数中使用了外部变量i ,形成了闭包,匿名函数在1s后执行,因为是闭包所以在循环结束后依然可以访问到i ,此时i 已经变成4(i =4后,不符合条件,才停止循环),所以就会输出三个4。
扩展:如何可以输出1 2 3呢?最简单的办法把var写成let即可。


二、深拷贝、浅拷贝、直接赋值

(1)直接赋值: 把一个对象a赋值给一个对象b相当于把对象b的地址指向对象a的地址,所以他们实际上是同一个对象,新旧对象还是共享同一块内存。因此,修改其中一个对象的对象或非对象属性,另一个也会受到影响。

(2)浅拷贝: 浅拷贝只拷贝对象的非对象属性,赋值后的对象与原对象不会指向同一个地址。修改赋值后的对象b的非对象属性不会影响原对象a的非对象属性;修改赋值后的对象b的对象属性,却会影响原对象a的对象属性。
扩展
1、es6中的 Object.assign,如果对象的属性值为基础类型,通过Object.assign()拷贝的那个属性而言是深拷贝。如果对象的属性值为引用类型,通过Object.assign()拷贝的那个属性而言是浅拷贝。
2、es6中还有一个扩展运算符"…"也是浅拷贝。

(3)深拷贝: 拷贝父级对象和父级对象中的所有引用数据类型,会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。


三、slice、splice、split、filter、concat、sort

(1)slice():可操作字符串和数组,通过索引位置获取新的数组,该方法不会改变原数组,只是返回一个新的子数组(左闭右开)。
(2)splice(index,howmany,item1,…,itemX): 只可操作数组,删除、插入和替换,这种方法会改变原数组
参数如下:
1、index:必需。规定从何处添加/删除元素;
2、howmany:可选。规定应该删除多少元素。必须是数字,但可以是 “0”,如果未规定此参数,则删除从 index 开始到原数组结尾的所有元素;
3、item:可选。要添加到数组的新元素;
(3)split():把一个字符串分割成字符串数组。
(4)filter():对数组进行过滤,Array.filter(function(currentValue, index, arr), thisValue),不改变原数组
(5)concat():用于连接两个或多个数组,该方法不会改变原数组
(6)sort():用于对数组的元素进行排序,默认排序顺序为按字母升序,使用数字排序,你必须通过一个函数作为参数来调用,会改变原数组


四、for in和for of、 map和foreach

(1)for…in 循环:只能获得对象的键名,不能获得键值,不能return。
(2)for…of 循环:允许遍历获得键值,遍历数组,不可遍历对象,可以return。
(3)map:循环遍历数组中的每一项,只能遍历数组,返回新的数组,有返回值。
(4)foreach:循环遍历数组中的每一项,只能遍历数组,会修改原来的数组,没有返回值。


五、原型和原型链

六、vuex和pinia

七、跨域

八、await、promise

九、computed和watch

十、


等我明天补充!

你可能感兴趣的:(前端)