八股文(一)

目录

一、箭头函数和普通函数有什么区别

二、 讲讲promise

1、 promise的3种状态和状态转换

(1)promise状态

(2)先看下三种状态的产生

(3)从pending状态到fulfilled状态的变化过程

(4)从pending状态到rejected状态的变化过程

2、 Promise中回调函数是同步的还是异步的?

3、then的链式调用是同步的还是异步的?

4、catch和then

扫描二维码关注公众号,回复: 16326699 查看本文章

三、 apply、bind、call

四、 typescript泛型

(1)泛型-泛型函数

(2)泛型-类型推断简化函数调用

(3)总结

五、 JavaScript 防抖和节流

1、防抖与节流是什么

定义

(1)防抖:n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时

(2)节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效

2、防抖案例

(1)搜索

​(2)滚动

3、节流案例

(1)搜索点击

4、总结

(1)防抖

(2)节流

一、箭头函数和普通函数有什么区别

(1)箭头函数比普通函数更加简洁

(2)箭头函数没有自己的this

箭头函数不同于传统JavaScript中的函数,箭头函数并没有属于⾃⼰的this,它所谓的this是捕获其所在上下⽂的 this 值,作为⾃⼰的 this 值,并且由于没有属于⾃⼰的this,所以是不会被new调⽤的,这个所谓的this也不会被改变

(3)箭头函数继承来的this指向永远不会改变

var id = 'GLOBAL';
var obj = {
  id: 'OBJ',
  a: function(){
    console.log(this.id);
  },
  b: () => {
    console.log(this.id);
  }
};
obj.a();    // 'OBJ'
obj.b();    // 'GLOBAL'
new obj.a()  // undefined
new obj.b()  // Uncaught TypeError: obj.b is not a constructor

(4)call()、apply()、bind()等方法不能改变箭头函数中this的指向

var id = 'Global';
let fun1 = () => {
    console.log(this.id)
};
fun1();                     // 'Global'
fun1.call({id: 'Obj'});     // 'Global'
fun1.apply({id: 'Obj'});    // 'Global'
fun1.bind({id: 'Obj'})();   // 'Global'

(5)箭头函数不能作为构造函数使用 (在JavaScript中,用new关键字来调用的函数,称为构造函数,构造函数首字母一般大写)

  • 由于箭头函数时没有自己的this,且this指向外层的执行环境,且不能改变指向,所以不能当做构造函数使用。

  • 构造函数是通过new关键字来生成对象实例,生成对象实例的过程也是通过构造函数给实例绑定this的过程,而箭头函数没有自己的this。创建对象过程,new 首先会创建一个空对象,并将这个空对象的proto指向构造函数的prototype,从而继承原型上的方法,但是箭头函数没有prototype。因此不能使用箭头作为构造函数,也就不能通过new操作符来调用箭头函数。

原型和原型链具体可看这篇文章JS的基础知识学习(二)(原型和原型链的基础理论)_七小山的博客-CSDN博客

函数 - prototype - 对象(函数的prototype)

对象 - 对象._proto -对象(函数大的prototype)

(6)箭头函数没有自己的arguments

arguments,它是js中函数内置的一个对象,而执行函数方法的实参中值都存储在arguments中;要想获取到这些实参,就需要像 数组 一样,用下标/索引来定位到每个值上面,但是又不能说它是一个数组,因为它里面还有其他的属性,如callee; 并且不能对它使用shift、push、join等方法。而没有传递值的命名参数将会被自动赋予undefined;

arguments.length----参数个数

arguments.callee()---调用自身

 

箭头函数没有自己的arguments对象。在箭头函数中访问arguments实际上获得的是它外层函数的arguments值。

二、 讲讲promise

1、 promise的3种状态和状态转换

(1)promise状态

pending(等待) fullfilled(已完成) rejected(已拒绝)

(2)先看下三种状态的产生

pending状态的Promise

const promise1 = new Promise((resolve,reject) => {
 
})
console.log(promise1);
fulfilled状态的Promise

const promise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve()
    })
})
console.log(promise1);

rejected状态的Promise

const promise = new Promise((resolve, reject) => {       setTimeout(() => {           reject();       })   });   console.log(promise);​

(3)从pending状态到fulfilled状态的变化过程

resolve()后状态从pending变为了fulfilled

const promise = new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('resolve前Promise的状态:',promise);
            resolve();
            console.log('resolve后Promise的状态:',promise);
        })
    });

(4)从pending状态到rejected状态的变化过程

reject()后状态从pending变为了rejected

const promise = new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log("Promise的状态:reject前:",promise);
            reject();
            console.log("Promise的状态:reject后:",promise);
        })
    });
​

未调用resolve或者reject时候处于pending状态,调用resolve后处于fullfilled状态,调用reject后处于rejected状态。如果在pending状态时候,执行任务抛出错误,则变成reject状态。 状态变化后,会执行通过then注册的回调。执行顺序和调用then方法的顺序相同。 调用then方法时候,如果状态是pending则注册回调,等到状态改变时候执行,如果状态已经改变则执行相应的回调。

状态一旦改变,就无法再次改变状态,这也是它名字 promise-承诺 的由来,一个promise对象只能改变一次

const p = new Promise((resolve, reject) => {
    resolve('test');
});
​
p.then(
    data => console.log(1, 'resolve', data),
  data => console.log(1, 'reject', data)
);
​
p.then(
    data => console.log(2, 'resolve', data),
  data => console.log(2, 'reject', data)
);
​
// 执行结果
1 "resolve" "test"
2 "resolve" "test"
const p = new Promise((resolve, reject) => {
  throw new Error('test-error');
  // 由于抛出错误,promise状态已经改变为rejected,再调用resolve将不会改变promise状态
    resolve('test');
});
​
p.then(
    data => console.log(1, 'resolve', data),
  data => console.log(1, 'reject', data)
);
​
p.then(
    data => console.log(2, 'resolve', data),
  data => console.log(2, 'reject', data)
);
​
// 执行结果
1 "reject" Error: test-error
2 "reject" Error: test-error

2、 Promise中回调函数是同步的还是异步的?

  • 回调函数是 什么

    一个函数A,作为另一个函数B的参数,那么函数A就被称为回调函数

  • promise本身 —同步

let obj=new Promise((res,rej)=>{
     console.log('这是promise对象');
});
console.log('哼哼');

promise的回调函数then --异步

let obj=new Promise((res,rej)=>{
      console.log('这是promise本身');
      res('这是回调函数then的值')
    });
    obj.then((res) => {
      console.log(res);
    });
    console.log('哼哼');

3、then的链式调用是同步的还是异步的?

   // 链式调用
        p.then(value => {
 
        }).then(value => {
 
        });

同步

4、catch和then

catch方法和then方法的reject回调用法相同,如果这时候任务处于rejected状态,则直接执行catch,catch的参数就是reject的reason;如果任务处于pending状态,则注册catch回调,等到状态变成rejected时候再执行。

Promise.prototype.catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数

p.then((val) => console.log('fulfilled:', val))
  .catch((err) => console.log('rejected', err));
​
// 等同于
p.then((val) => console.log('fulfilled:', val))
  .then(null, (err) => console.log("rejected:", err));

三、 apply、bind、call

说下call、apply、bind方法的作用和区别?

具体可以看这篇文章call,apply,bind三者的区别_call和apply及bind三者的区别_七小山的博客-CSDN博客

  • call、apply、bind都是用来改变this指向的,

  • call和apply调用时候立即执行,bind调用不会立即执行而是会返回新的函数。

  • 当需要传递参数时候,call直接写多个参数,apply将多个参数写成数组。

  • bind在绑定时候需要固定参数时候,也是直接写多个参数

  • call和apply第一个参数obj正常都是传入的this,若是没有传,则默认是windows

     let obj = {
                name:'张三',
                age:18,
                func:function(){
                    console.log(this.name + "年龄" + this.age);
                }
            }
            let obj2 = {
                name:'李四',
                age:20
            }
            obj.func.call(obj2) //李四年龄20
            obj.func.apply(obj2); //李四年龄20
            obj.func.bind(obj2)(); //李四年龄20

    这三个结果都一样 bind后面多一个括号是因为 bind返回值是一个函数加上()编程立即执行函数

四、 typescript泛型

泛型,顾名思义,就是可以适用于多个类型,使用类型变量比如T帮助我们捕获传入的类型,之后我们就可以继续使用这个类型。

本质是参数化类型,通俗的将就是所操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和函数的创建中,分别成为泛型类泛型接口泛型函数

(1)泛型-泛型函数

function fn<Type>(value: Type): Type { return value }
// 上面的Type只是一个名字而已,可以改成其他的
 
function fn<T>(value: T): T { return value }

语法:在函数名称的后面写 <>(尖括号),尖括号中添加类型变量,比如此处的 Type。

类型变量 Type,是一种特殊类型的变量,它处理类型而不是值

该类型变量相当于一个类型容器,能够捕获用户提供的类型(具体是什么类型由用户调用该函数时指定)

因为 Type 是类型,因此可以将其作为函数参数和返回值的类型,表示参数和返回值具有相同的类型

类型变量 Type,可以是任意合法的变量名称

调用泛型函数的格式

const num = fn<number>(10)
const str = fn<string>('a')

这样,通过泛型就做到了让 id 函数与多种不同的类型一起工作,实现了复用的同时保证了类型安全

(2)泛型-类型推断简化函数调用

function fn<T>(value: T): T { return value }
// 省略 <number> 调用函数
let num = fn(10)
let str = fn('a')
  • 在调用泛型函数时,可以省略 <类型> 来简化泛型函数的调用

  • 此时,TS 内部会采用一种叫做类型参数推断的机制,来根据传入的实参自动推断出类型变量 Type 的类型

  • 比如,传入实参 10,TS 会自动推断出变量 num 的类型 number,并作为 Type 的类型

  • 推荐:使用这种简化的方式调用泛型函数,使代码更短,更易于阅读

  • 说明:当编译器无法推断类型或者推断的类型不准确时,就需要显式地传入类型参数

(3)总结

定义一个函数或类时,有些情况下无法确定其中要使用的具体类型(返回值、参数、属性的类型不能确定,此时泛型便能够发挥作用。

五、 JavaScript 防抖和节流

1、防抖与节流是什么

本质上是优化高频率执行代码的一种手段

如:浏览器的 resize、scroll、keypress、mousemove 等事件在触发时,会不断地调用绑定在事件上的回调函数,极大地浪费资源,降低前端性能

为了优化体验,需要对这类事件进行调用次数的限制,对此我们就可以采用 防抖(debounce) 和 节流(throttle) 的方式来减少调用频率

  • 防抖:就是一定时间内,只会执行最后一次任务;

  • 节流:就是一定时间内,只执行一次 ;

定义

(1)防抖:n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时

防抖具体指的是某个函数在某段时间内,无论触发了多少次回调,都只执行最后一次。假如我们设置了一个等待时间 3 秒的函数,在这 3 秒内如果遇到函数调用请求就重新计时 3 秒,直至新的 3 秒内没有函数调用请求,此时执行函数,不然就以此类推重新计时

防抖实现原理就是利用定时器,函数第一次执行时设定一个定时器,并且通过闭包缓存起来,之后调用时发现已经设定过定时器就清空之前的定时器,并重新设定一个新的定时器,如果存在没有被清空的定时器,当定时器计时结束后触发函数执行。

(2)节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效

函数节流指的是某个函数在一定时间间隔内(例如 3 秒)只执行一次,在这 3 秒内 无视后来产生的函数调用请求,也不会延长时间间隔。3 秒间隔结束后第一次遇到新的函数调用会触发执行,然后在这新的 3 秒内依旧无视后来产生的函数调用请求,以此类推。函数节流非常适用于函数被频繁调用的场景,例如:window.onresize() 事件、mousemove 事件、上传进度等情况。

实现原理就是通过一个布尔类型变量来判断是否可执行回调,当变量为true时,生成一个定时器,同时将变量取反通过闭包保存起来,当定时器执行完回调后,再将变量变为true,在变量为期false间,调用节流函数不会生成定时器。

2、防抖案例

(1)搜索

<button id="debounce">搜索!</button>
​
<script>
     const dd = document.querySelector("#debounce");
      dd.addEventListener("click", debounce());
​
      function debounce() {
        let timer; // 使用闭包,多次调用都能访问到同一个变量,而不是生成新的变量
        return function () {
        // 每次当用户点击/输入的时候,把前一个定时器清除
          clearTimeout(timer);
          timer = setTimeout(() => {
           // 然后创建一个新的 setTimeout,
        // 这样就能保证点击按钮后的 interval 间隔内
       // 如果用户还点击了的话,就不会执行 定时器里面的内容
            // 需要防抖的操作...
            console.log("防抖成功!");
          }, 500);
        };
      }
</script>

​(2)滚动

 let timer;
      window.onscroll = function () {
        console.log(
          "上一个定时器的序号" +
            timer +
            "滚动" +
            document.documentElement.scrollTop
        );
        clearTimeout(timer);
​
        timer = setTimeout(function () {
          //滚动条位置
          let scrollTop =
            document.body.scrollTop || document.documentElement.scrollTop;
          console.log("滚动条位置:" + scrollTop);
        }, 500);
      };

3、节流案例

(1)搜索点击

<button id="throttle">点我节流!</button>
​
<script>
    $('#throttle').on('click', throttle());
    
    function throttle(fn) {
        let flag = true;
        // 使用闭包,方法多次调用都能访问到同一个变量,而不是生成新的flag变量
        return function () {
            if (!flag) { return; }
            flag = false;
            setTimeout(() => {
                console.log("节流成功!");
                flag = true;
            }, 1000);
        };
    }
</script>
​

4、总结

(1)防抖

单位时间内,频繁触发事件,只执行最后一次

典型场景:搜索框搜索输入

代码思路是利用定时器,每次触发先清掉以前的定时器(从新开始)

(2)节流

单位时间内,频繁触发事件,只执行一次

典型场景:高频事件快速点击、鼠标滑动、resize事件、scroll事件

代码思路也是利用定时器,等定时器执行完毕,才开区定时器(不要打断)


 

猜你喜欢

转载自blog.csdn.net/qq_55928824/article/details/129298610
今日推荐