前端高频面试题—JavaScript篇(二)

💻前端高频面试题—JavaScript篇(二) 🏠专栏:前端面试题
👀个人主页:繁星学编程🍁
🧑个人简介:一个不断提高自我的平凡人🚀
🔊分享方向:目前主攻前端,其他知识也会阶段性分享🍀
👊格言:☀️没有走不通的路,只有不敢走的人!☀️
👉让我们一起进步,一起成为更好的自己!!!🎁

前端高频面试题—JavaScript篇(二)

本文主要讲述的前端高频面试题知识有:

  1. let const var的区别
  2. 讲讲this
  3. 讲讲Map和Set数据结构
  4. new的过程发生了什么
  5. localStorage sessionStorage cookies 有什么区别

一. let const var的区别

ES6新增了两种定义变量和常量的关键字let和const

目的:解决原有语法上的一些不足

let 定义变量
const 定义常量

(1) let/const 和 var的区别

1. 预解析

var 会进行预解析

let/const 没有预解析,必须先声明后使用

// eg: 使用var声明
console.log(username); // undefined
// var 会进行预解析
var username = "tom";
console.log(username); // tom

fn(); // 1
function fn() {
    console.log(1);
}
fn(); // 1
// eg: 使用let声明
console.log(num);
let num = 100;
console.log(num);
// 报错:Uncaught ReferenceError: Cannot access 'num' before initialization
2. 重复变量名

var 定义的变量可以重名

let/const 不允许定义重复的变量

// eg:使用var重复声明变量(后面的会覆盖前面的)
var num = 100;
var num = 200;
console.log(num); // 200
// eg:使用let/const重复声明变量报错:(Uncaught SyntaxError: Identifier 'num' has already been declared)
let num = 100;
let num = 200;
const num = 100;
const num = 200;
3. 块级作用域

var 没有块级作用域,只有函数能限制变量的使用范围

let/const 有块级作用域,任何一个可以执行代码的 {} 都会限制变量的使用范围

if (10 > 1) {
    let num = 100;
    console.log(num); // 100
    const a = 200; 
    console.log(a); // 200
}
console.log(a); // 报错:Uncaught ReferenceError: a is not defined
console.log(num);

(2) let 和 const区别

1. 变量值可以变

let 定义的变量可以修改值

const 对于普遍类型声明之后值不可改变,复杂类型声明后值可以改变,但是类型不可改变(因为赋值复杂数据类型的时候传的不是值,是地址)

let num = 100;
num = 200;
console.log(num); // 200
const num = 100;
num = 200;
console.log(num); // 报错:Uncaught TypeError: Assignment to constant variable

:const存的值是引用数据类型的时候,只要地址没变就可以

const obj = {
username: "tom",
};
obj.username = "jerry";
console.log(obj); // {"username": "jerry"}

把const声明的常量的地址改变,就会报错

obj = { age: 18 };
console.log(obj);
// 报错:Uncaught TypeError: Assignment to constant variable
2. 初始化赋值

let 定义的时候可以不赋值
const 定义的时候必须赋值,而且一旦赋值不允许修改

let num;
num = 100;
console.log(num); // 100
const num;
num = 100;
console.log(num);
// 报错:Uncaught SyntaxError: Missing initializer in const declaration

(3) 总结

区别varletconst
变量提升可以不可以不可以
预解析可以不可以不可以
定义的是变量变量常量
重复声明可以不可以不可以
块级作用域没有
修改声明的变量不能
先声明在赋值可以可以不可以(一旦声明必须初始化)

const实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动

二. 讲讲this

1.this指向调用者这个关系一定要清楚
2.要知道改变this指向的几种方式(call, bind, apply)
3.箭头函数中this的特殊性要能讲清楚
4.call apply bind原理理解

什么是this?

它是一个js内的关键字,是一个使用在作用域内的关键字。

(1) this的指向

  1. 全局中的this ==> window

  2. 函数中的this:

    不管函数怎么定义,不管函数在哪定义,只看函数调用方式

    • 普通调用 ==> window
    • 对象中的this ==> .前面是谁就是谁
    • 定时器中的this ==> window
    • 事件处理函数 ==> 事件源
    • 箭头函数中的this ==> 该作用域外部作用域的this
    • 构造函数中的this ==> 本次调用 被自动创建的对象
//在全局使用this指向window
console.log(this); // window
console.log(window); // window
console.log(window === this); // true
// 函数中this指向
// 1. 普通调用  this===> window
function fn() {
    console.log(this); // window
}
fn();
// 2. 对象调用
function fn() {
    console.log(this); // {name: '小花', f: ƒ}
    console.log(this.name); // 小花
}
// 把fn这个函数当做一个值存在对象中键名为f的位置
var obj = {
    name: "小花",
    f: fn,
};
obj.f();
// 3. 定时器调用 this 指向window
function fn() {
    console.log(this); // window
}
setTimeout(fn, 2000);
var username = "tom";
function fn() {
    console.log(this); // window
    console.log(this.username); // tom
}
var obj = {
    username: "小花",
    f: fn,
};
setTimeout(obj.f, 2000);
// 4. 事件处理函数 this指向事件源
function fn() {
    console.log(this); // #document
}
document.onclick = fn;

(2) 如何改变this的指向方法

1. call()方法

语法:函数名.call(参数)

参数:

  • 参数1:该函数内的this指向(新指向)
  • 参数2:依次给函数进行形参赋值

特点:立即调用函数

2. apply()方法

语法:函数名.apply(参数1, 参数2)

参数:

  • 参数1:该函数内的this指向(新指向)
  • 参数2:是一个数组,内部的每一项都是给函数形参赋值

特点:立即调用函数

3. bind()方法

语法:函数名.bind(参数)

参数:

  • 参数1:该函数内的this指向(新指向)
  • 从参数2开始,依次给函数的形参赋值

特点:

  • 不会立即调用函数,而是返回一个新函数
  • 有一个返回值,是一个和原始函数一模一样的新函数,只不过this被锁死了
var name = 'zjk';
  function fun() {
  console.log (this.name);
}
var obj= {
  name: 'jake'
};
fun(); // zjk
fun.call(obj); //Jake

(3).改变this指向的方法有什么区别

  1. 三者都可以改变函数this对象的指向
  2. 三者第一个参数都是this要指向的对象,如果没有这个参数或参数为undefined或null,则默认指向全局window
  3. 三者都可以传参,但是apply是数组,而call是参数列表,且apply和call是一次性传入参数,而bind可以分为多次传入
  4. bind是返回绑定this之后的函数,apply、call 则是立即执行

(4) call apply bind原理理解

  1. call

    • 第一个参数为null或者undefined时,this指向全局对象window,值为原始值的指向该原始值的自动包装对象,如 String、Number、Boolean
    • 为了避免函数名与上下文(context)的属性发生冲突,使用Symbol类型作为唯一值
    • 将函数作为传入的上下文(context)属性执行
    • 函数执行完成后删除该属性
    • 返回执行结果
    Function.prototype.myCall = function(context,...args){
        let cxt = context || window;
        //将当前被调用的方法定义在cxt.func上.(为了能以对象调用形式绑定this)
        //新建一个唯一的Symbol变量避免重复
        let func = Symbol() 
        cxt[func] = this;
        args = args ? args : []
        //以对象调用形式调用func,此时this指向cxt 也就是传入的需要绑定的this指向
        const res = args.length > 0 ? cxt[func](...args) : cxt[func]();
        //删除该方法,不然会对传入对象造成污染(添加该方法)
        delete cxt[func];
        return res;
    }
    
  2. apply

    前部分与call一样

    第二个参数可以不传,但类型必须为数组或者类数组

    Function.prototype.myApply = function(context,args = []){
        let cxt = context || window;
        //将当前被调用的方法定义在cxt.func上.(为了能以对象调用形式绑定this)
        //新建一个唯一的Symbol变量避免重复
        let func = Symbol()
        cxt[func] = this;
        //以对象调用形式调用func,此时this指向cxt 也就是传入的需要绑定的this指向
        const res = args.length > 0 ? cxt[func](...args) : cxt[func]();
        delete cxt[func];
        return res;
    }
    
  3. bind

    需要考虑

    • bind() 除了 this 外,还可传入多个参数;
    • bind 创建的新函数可能传入多个参数;
    • 新函数可能被当做构造函数调用;
    • 函数可能有返回值;

    实现方法

    • bind 方法不会立即执行,需要返回一个待执行的函数;(闭包)
    • 实现作用域绑定(apply)
    • 参数传递(apply 的数组传参)
    • 当作为构造函数的时候,进行原型继承
    Function.prototype.myBind = function (context, ...args) {
        //新建一个变量赋值为this,表示当前函数
        const fn = this
        //判断有没有传参进来,若为空则赋值[]
        args = args ? args : []
        //返回一个newFn函数,在里面调用fn
        return function newFn(...newFnArgs) {
            if (this instanceof newFn) {
                return new fn(...args, ...newFnArgs)
            }
            return fn.apply(context, [...args,...newFnArgs])
        }
    }
    

(5) 箭头函数没有this

箭头函数内的this 就是该作用域外部作用域的this
箭头函数外面的函数this是啥,箭头函数this就是啥

注意: 事件处理函数不要用箭头函数,会改变this指向

三. 讲讲Map和Set?

1.Map的key相比较普通对象来说更为灵活,普通对象的key只能以基础数据类型作为key值,并且所有传入的key值都会被转化成string类型,而Map的key可以是各种数据类型格式。

2.Set可以讲讲它去重的特性。

数据结构 Set

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

Set本身是一个构造函数,用来生成 Set 数据结构。

由于Set是伪数组,并且伪数组里面的值都是唯一的,所以可以用在数组去重中

const s = new Set()
const arr = [7, 2, 3, 4, 2, 5, 5];
arr.forEach(item => { s.add(item) });
console.log(s); // Set(5) {7, 2, 3, 4, 5}
console.log([...s]); // (5) [7, 2, 3, 4, 5]
// 简化为
const set = new Set([7, 2, 3, 4, 2, 5, 5]);
[...set]// [7, 2, 3, 4, 5]
(1) 创建 Set 数据结构

语法

var s = new Set([数据1, 数据2, 数据3....])
(2) Set 数据结构的属性和方法

属性:size

方法:add()、has()、delete()、clear()、forEach()

  1. size 属性

    该数据结构中有多少个数据

    语法:数据结构.size

    // eg:
    const s = new Set([100, 200, 300, 200]);
    console.log(s.size); // 3
    
  2. add() 方法

    向该数据结构添加内容

    语法:数据结构.add(数据)

    // eg:
    const s = new Set([100, 200, 300]);
    s.add(500)
    s.add(100)
    console.log(s); // Set(4) {100, 200, 300, 500}
    
  3. has() 方法

    查找Set中是否有该数据

    语法:数据结构.has(数据)

    true 该数据结构内有该数据
    false 该数据结构内没有该数据

    // eg:
    const s = new Set([100, 200, 300]);
    console.log(s.has(100)); // true
    console.log(s.has(500)); // false
    
  4. delete()方法

    删除数据

    语法:数据结构.delete(数据)

    // eg:
    const s = new Set([100, 200, 300]);
    s.delete(100);
    console.log(s); // Set(2) {200, 300}
    
  5. clear() 方法

    清除该数据结构内所有内容

    语法:数据结构.clear()

    // eg:
    const s = new Set([100, 200, 300]);
    s.clear();
    console.log(s); // Set(0) {size: 0}
    
  6. forEach() 方法

    语法 :数据结构.forEach(function(value, key, origin){})

    // eg:
    const s = new Set([100, 200, 300]);   
    s.forEach(function(value, key, origin){
        console.log(value, key, origin); // 100 100 Set(3) {100, 200, 300}
    });
    
(3) Set中判断两个值是否不同

向 Set 加入值的时候,不会发生类型转换,所以5"5"是两个不同的值。Set 内部判断两个值是否不同,使用的算法叫做“Same-value-zero equality”,它类似于精确相等运算符(===),主要的区别是向 Set 加入值时认为NaN等于自身,而精确相等运算符认为NaN不等于自身。

let set = new Set();
let a = NaN;
let b = NaN;
set.add(a);
set.add(b);
set // Set {NaN}

上面代码向 Set 实例添加了两次NaN,但是只会加入一个。这表明,在 Set 内部,两个NaN是相等的。

另外,两个对象总是不相等的。

let set = new Set();

set.add({});
set.size // 1

set.add({});
set.size // 2

上面代码表示,由于两个空对象不相等,所以它们被视为两个值。

数据结构 Map

Map 数据结构:类似于对象的数据结构,它的key可以是任何数据类型,可以被看做为一个 值 = 值 的数据结构

(1) Map 基本概念

ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。

(2) Map 特征
  • Map 对象保存键值对,并且能够记住键的原始插入顺序。
  • 任何值(对象或者原始值) 都可以作为一个键或一个值。
  • Map 是 ES6 中引入的一种新的数据结构
(3) Maps 和 Objects 的区别
  • 一个 Object 的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值。
  • Map 中的键值是有序的(FIFO 原则),而添加到对象中的键则不是。
  • Map 的键值对个数可以从 size 属性获取,而 Object 的键值对个数只能手动计算。
  • Object 都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。
(4) 创建一个 Map数据结构

语法

var m = new Map( [ [key, value], [key, value] ] )
(5) Map 数据结构的属性和方法

属性:size

方法:set()、get()、has()、delete()、clear()、forEach()

  1. size 属性

    该数据结构内有多少个数据

    语法:数据结构.size

    // eg:
    const m = new Map([['name', 'zs'], ['age', 18]]);
    console.log(m.size); // 2
    
  2. set() 方法

    向该数据结构添加内容

    语法:数据结构.set(key, value)

    // eg:
    const m = new Map([['name', 'zs'], ['age', 18]]);
    m.set('time', '两年半');
    console.log(m); // Map(3) {'name' => 'zs', 'age' => 18, 'time' => '两年半'}
    
  3. get() 方法

    语法:数据结构.get(key)

    返回值:该数据结构内key对应的value

    // eg:
    const m = new Map([['name', 'zs'], ['age', 18]]);
    console.log(m.get('name')); // zs
    
  4. has() 方法

    该数据结构中是否有该数据

    语法:数据结构.has(key)

    // eg:
    const m = new Map([['name', 'zs'], ['age', 18]]);
    console.log(m.has('name')); // true
    
  5. delete() 方法

    删除该数据结构内某一个数据

    语法:数据结构.delete(key)

    // eg:
    const m = new Map([['name', 'zs'], ['age', 18]]);
    m.delete('name');
    console.log(m); // Map(1) {'age' => 18}
    
  6. clear() 方法

    清除该数据结构里面的所有内容

    语法:数据结构.clear()

    // eg:
    const m = new Map([['name', 'zs'], ['age', 18]]);
    m.clear();
    console.log(m); // Map(0) {size: 0}
    
  7. forEach()

    语法:

    数据结构.forEach(function(value, key, origin){})
    
    // eg:
    const m = new Map([['name', 'zs'], ['age', 18]]);
    m.forEach(function (value, key, origin) {
        console.log(value, key, origin); // zs name Map(2) {'name' => 'zs', 'age' => 18}
        							  // 18 'age' Map(2) {'name' => 'zs', 'age' => 18}
    })
    

四. new的过程发生了什么

  1. 首先创建了一个新对象
  2. 设置原型,将对象的原型设置为函数的prototype对象
  3. 让函数的this指向这个对象,执行构造函数的代码(为这个新对象添加属性)
  4. 判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象

五. localStorage sessionStorage cookies 有什么区别

(1) 什么是会话级存储

window.sessionStorage

特点

  1. 生命周期结束为关闭浏览器窗口
  2. 在同一窗口(页面)下数据可以共享(不能跨页面共享)
  3. 以键值对的形式存储使用

(2) 什么是持久性存储

window.localStorage

特点

  1. 生命周期永久生效,除非手动删除否则关闭页面也会存在
  2. 可以多窗口(页面)共享(同一浏览器可以共享)
  3. 以键值对的形式存储使用

(3) 什么是cookie

特点

  1. 只能存储字符串,并且有自己的固定格式

    key=value;key2=value2;key3=value3
    
  2. cookie 存储有大小限制

    存储空间:4kb左右

  3. 存储的时效性

    默认是会话级别,可以手动设置过期时间

  4. 操作依赖服务器

    本地打开的页面不能操作cookie

    必须用服务器打开

  5. 跟随前后端请求自动携带

  6. 前后端都可以操作

  7. 存储的时候依赖域名

    使用哪个域名存储,就用哪个域名

    不能跨域名通讯

(4) cookie和storage的区别

  1. 出现时间

    cookie 在JavaScript刚出现的时候就有了

    storage 是H5新增的新特性

  2. 存储大小

    cookie 4kb

    storage 20M

  3. 前后端交互

    storage 存储的数据不会跟随页面携带

    cookie 存储的数据会跟随页面请求自动携带

  4. 前后端操作

    storage 只能利用JavaScript操作

    cookie 前后端都可以操作

  5. 过期时间

    cookie 默认是会话级,可以手动设置过期时间

    storage 不能手动设置过期时间

(5) localStorage和sessionStorage的区别

  1. 过期时间

    localStorage 永久存储

    sessionStorage 临时存储

  2. 跨页面通讯

    localStorage 直接跨页面共享数据

    sessionStorage 只能是从当前窗口跳转的

  3. 共同点

    只能存字符串,不能存复杂数据类型

结束语

希望对您有一点点帮助,如有错误欢迎小伙伴指正。
👍点赞:您的赞赏是我前进的动力!
⭐收藏:您的支持我是创作的源泉!
✍评论:您的建议是我改进的良药!
一起加油!!!💪💪💪

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

繁星学编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值