js常见的问题(闭包、原型、防抖、拷贝)

1、闭包

定义:函数 A 返回了一个函数 B,并且函数 B 中使用了函数 A 的变量,函数 B 就被称为闭包。

 

闭包的特性:

1)函数嵌套

2)内部函数引用外部函数变量

3)变量和参数不会被垃圾回收机制回收

 

闭包的缺点:

常驻内存,会增大内存使用量,使用不当很容易造成内存泄露

 

闭包的优点:

1)避免全局变量的污染

2)希望常驻内存(常用场景:函数中使用定时器)

 

2、原型和原型链

原型对象:

在 JavaScript 中,每当定义一个对象(函数也是对象)时候,对象中都会包含一些预定义的属性;

其中每个函数对象都有一个prototype 属性,这个属性指向函数的原型对象。

 

原型:

在JavaScript中,每当定义一个函数数据类型(普通函数、类)时候,都会天生自带一个

prototype属性,这个属性指向函数的原型对象,并且这个属性是一个对象数据类型的值。

 

原型链:

原型链是由原型对象组成的;

每个对象都有 __proto__ 属性,指向了创建该对象的构造函数的原型,__proto__ 将对象连接起来组成了原型链。

是一个用来实现继承和共享属性的有限的对象链。

 

注意:

  prototype是函数的一个属性(每个函数都有一个prototype属性),这个属性是一个指针,指向一个对象。它是显示修改对象的原型的属性。

  __proto__是一个对象拥有的内置属性(请注意:prototype是函数的内置属性,__proto__是对象的内置属性),是JS内部使用寻找原型链的属性。

 

3、防抖和节流

防抖和节流的作用都是防止函数多次调用。

区别在于,假设一个用户一直触发这个函数,且每次触发函数的间隔小于wait,防抖的情况下只会调用一次,而节流的 情况会每隔一定时间(参数wait)调用函数。

 

在日常开发中表现为:

节流:在滚动事件中需要做个复杂计算

防抖:按钮的防二次点击操作

/**
 * @desc 函数防抖
 * @param func 目标函数
 * @param wait 延迟执行毫秒数
 * @param immediate true - 立即执行, false - 延迟执行
 * @param 三个参数,第二,三个参数可以省略,默认为1000,false
 */
function debounce(func, wait, immediate) {
  let timer;
  wait = wait || 1000;
  return function () {
    let context = this,
      args = arguments;
    if (timer) clearTimeout(timer);
    if (immediate) {
      let callNow = !timer;
      timer = setTimeout(() => {
        timer = null;
      }, wait);
      if (callNow) func.apply(context, args);
    } else {
      timer = setTimeout(() => {
        func.apply(context, args);
      }, wait);
    }
  };
}
/**
 * @function throttle 函数节流
 * @param {Function} fn 需要节流的函数
 * @param {Number} interval 间隔时间
 * @return {Function} 经过节流处理的函数
 * */
function throttle(fn, interval) {
  let timer = null; // 定时器
  let firstTime = true; // 判断是否是第一次执行
  // 利用闭包
  return function () {
    // 拿到函数的参数数组
    let args = Array.prototype.slice.call(arguments, 0);
    // 拿到当前的函数作用域
    let _this = this;
    // 如果是第一次执行的话,需要立即执行该函数
    if (firstTime) {
      // 通过apply,绑定当前函数的作用域以及传递参数
      fn.apply(_this, args);
      // 修改标识为null,释放内存
      firstTime = null;
    }
    // 如果当前有正在等待执行的函数则直接返回
    if (timer) return;
    // 开启一个倒计时定时器
    timer = setTimeout(function () {
      // 通过apply,绑定当前函数的作用域以及传递参数
      fn.apply(_this, args);
      // 清除之前的定时器
      timer = null;
      // 默认300ms执行一次
    }, interval || 300);
  };
}

 

4、深拷贝和浅拷贝

深拷贝和浅拷贝是针对复杂数据类型来说的,浅拷贝只拷贝一层,而深拷贝是层层拷贝。

1)深拷贝

深拷贝复制变量值,对于非基本类型的变量,则递归至基本类型变量后,再复制。 深拷贝后的对象与原来的对象是完全隔离的,互不影响,对一个对象的修改并不会影响另一个对象。

2)浅拷贝

浅拷贝是会将对象的每个属性进行依次复制,但是当对象的属性值是引用类型时,实质复制的是其引用,当引用指向的值改变时也会跟着变化

 

实现对象的深拷贝:

1)递归实现

2)JSON.parse(JSON.stringify(obj))

 

5、异步

js的异步和其它语言的异步是不相同的,本质上还是同步;因为浏览器会有一个Event Queue存放异步通知,js执行代码时会产生一个执行栈,同步的代码在执行栈中,异步的在Event Queue中;

只有当执行栈为空时,就是才会去Event Queue中查看是否有需要处理的通知,有的话拿到执行栈中去执行。

 

 

 

posted on 2021-03-10 11:38  活在当下zql  阅读(160)  评论(0)    收藏  举报