面试题

2025-07-16  本文已影响0人  布卡卡的晴空

1.闭包是什么?利弊?如何解决弊端?

闭包是函数的嵌套,外层函数里面包裹了内存函数,内存函数可以访问外层函数的变量,而外层函数无法操控内存函数的变量。

闭包的好处

1.隔离作用域,保护私有变量;有了闭包才有局部变量,要不然都是全局变量了
2.闭包不会被内存机制回收,可以延展函数作用域
3.让我们可以使用回调,操作其他函数内部

闭包的弊端

1.因闭包不会被内存机制回收,占用内存资源,过多时,易引起内存泄露,

解决办法:

无法自动销户,就及时手动回收,使用后将函数的引用赋null

2.深拷贝与浅拷贝的区别?

拷贝层级不同,深拷贝新的对象变动不会影响老对象,浅拷贝只有第一层属性变动互不影响,深层改变还是会改变原来的对象。

实现拷贝方法

浅拷贝:数组可以用拓展运算符[...arr],或者slice().浅拷贝对象可以用Object.assign({},obj)
深拷贝:JSON.parse(JSON.stringify(obj)),或封装递归方法

JSON.parse(JSON.stringify(obj))处理的缺点

1.如果对象中有属性是function或者undefined,处理后会被过滤掉;
2.如果属性值是对象,且由构造函数生成的实例对象,会丢弃对象的constructor;

3.如何判断空对象?

1.用JSON的stringify转成字符串后,跟'{}'对比
2.用ES6,判断Object.keys(targetObject)返回值数组的长度是否为0;
3.用ES5,判断Object.getOwnPropertyNames(targetObject)返回的数组长度是否为0;

4.如何区分数据类型?

let a = [1,2]
Object.prototype.toString.call(a)

5.如何改变this指向?区别?

call 和 apply 都是可以改变this 指向的问题, call 方法中传递参数要求一个 一个传递参数。 但是apply 方法要求传递参数是一个数组形式。bind也可以改变this 指向,但是bind会返回一个新的函数

6.沙箱隔离怎么做?

使用iframe可以实现变量隔离

7.浏览器存储,他们的区别?

1.localStorage:永久保存,以键值对保存,存储空间5M
2.sessionStorage:关闭页签/浏览器时清空
3.cookie:随着请求发送,通过设置过期时间删除
4.session:保存在服务端
localStorage/sessionStorage是window的属性,cookie是document的方法

8.常用的数组方法有哪些?

改变原数组:push、pop、shift、unshift、sort、splice、reverse
不改变原属组:concat、join、map、forEach、filter、slice

9.Dom事件流的顺序?什么是事件委托?

当页面上的一个元素被点击时,先从document向下 一层层捕获到该元素。然后再向上冒泡,一层层触发。
事件委托是将本应该注册给子元素的事件, 注册给父元素,原理是事件冒泡,子元素的事件会向外冒泡,触发父元素的相同事件, 根据事件对象可以找到真正触发事件的事件源。

10.对原型链的认识

原型链就是沿着原型一层一层去向上找,形成的这个链条其实就是原型链;也就是说当你调用某个对象的属性或方法,它自身没有,就去找他继承的prototype若还没有就在找它prototype所继承的prototype层层向上。原型链的作用是实现属性和方法的共享,通过原型链,多个实例可以访问同一个原型对象上的属性和方法,从而减少内存占用并提高代码复用性。

11.防抖/节流的区别?

防抖:只会在最后一次事件后执行触发函数,主要是为了防止用户点击过快。(输入框的实时搜索,短信倒计时,登陆提交,按电梯)
节流:不管事件多么的频繁,都会保证在规定时间段内触发事件函数(滚动事件、鼠标移动事件,)

12.继承的方法有哪些?

1.原型链继承,通过将一个对象的原型设置为另一个对象来实现继承。

function Parent(){
   this.name = 'web前端';
   this.type = ['JS','HTML','CSS'];
}

Parent.prototype.Say = function(){
     console.log(this.name);
}

function Son(){};
Son.prototype = new Parent();
son1 = new Son();
son1.Say();

优点:可以实现继承。
缺点:①因为Son.prototype(即原型对象)继承了Parent实例化对象,这就导致了所有Son实例化对都一样,都共享有原型对象的属性及方法。② Son构造函数实例化对象无法进行参数的传递。

2.构造函数继承:通过构造函数call 方法进行继承,或者apply()调用父类型构造函数

function Parent(){
    this.name = '前端';
    this.type = ['JS','HTML','CSS'];
}

function Son(){
    Parent.call(this);
}
son1 = new Son();
son1.type.push('VUE');
console.log(son1.type);//['JS','HTML','CSS','VUE']
son2 = new Son();
console.log(son2.type);//['JS','HTML','CSS']

优点: ① 实现实例化对象的的独立性;② 还可以给实例化对象添加参数。
缺点: ① 方法都在构造函数中定义,每次实例化对象都创建一遍方法,基本无法实现函数复用。②call方法仅仅调用了父级构造函数的属性及方法,没有办法调用父级构造函数原型对象的方法。

3.组合继承:原型链+借用构造函数:组合继承结合了原型继承和构造函数继承的优点,通过在子类构造函数中调用父类构造函数并设置子类原型为父类实例来实现继承。

function Parent(name){
    this.name = name;
    this.colors = ['red', 'blue', 'yellow'];
}

Parent.prototype.getName = function(){
    console.log(this.name);
}

function Child(name, age){
   // 核心代码 1
   Parent.call(this, name);

   this.age = age;
}

// 核心代码 2 子类型的原型为父类型的一个实例对象
Child.prototype = new Parent();
Child.prototype.constructor = Child;

// 可以通过子类给父类的构造函数传参
let child1 = new Child('litterStar');
let child2 = new Child('luckyStar');
child1.getName(); // litterStar
child2.getName(); // luckyStar

child1.colors.push('pink');
// 修改 child1.colors 不会影响 child2.colors
console.log(child1.colors); // [ 'red', 'blue', 'yellow', 'pink' ]
console.log(child2.colors); // [ 'red', 'blue', 'yellow' ]

4.ES6 中class的继承:ES6中引入了class关键字,可以通过extends关键字实现继承。

class Parent {}
class Child extends Parent {
    constructor(name, age, color) {
        // 调用父类的constructor(name, age)
        super(name, age);
        this.color = color;
    }
    toString() {
        return this.color + ' ' + super.toString(); // 调用父类的toString()
    }
}

5.寄生组合式继承

function Parent(name) {
    this.name = name;
    this.colors = ['red', 'blue', 'yellow'];
}
Parent.prototype.getName = function() {
    console.log(this.name)
}

function Child(name, age) {
    // 核心代码①
    Parent.call(this, name);

    this.age = age;
}
// 核心代码②
Child.prototype = Object.create(Parent.prototype);//Object.create MDN上的解释:它会创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
Child.prototype.constructor = Child;

13.作用域

简单来说,作用域就是变量可以生效的地方。分为全局作用域和局部作用域,es6的块作用域(let声明的变量)

14.常用操作字符串方法有哪些?

1.charAt (索引)
2.toUpperCase() 给字符串转成大写字母
3.toLowerCase() 给字符串转成小写字母
4.substr(从哪个索引开始,截取多少个) 也是用来截取字符串的
5.substring (从哪个索引开始,到哪个索引截止) 是用来截取字符串使用的,包含开始索引,不包含结束索引
6.slice (开始索引, 结束索引) ,和上面的区别是可以写负数,不包含结束索引
7.split('-') 按照分隔符拆分字符串,返回值是数组
8.concat() 字符串拼接,不存在返回-1
9.indexOf() 就是按照字符找到对应的索引 从开始索引查找你要找的字符,找到了返回值就是该字符对应的索引,找不到就是-1
10.lastIndexOf( ) 同上,从右往左找
11.includes()字符串中是否包含该字符串片段,返回值:布尔值
12.startwith( ) 判断是否以该片段开头,返回布尔值
13.endswith( ) 判断是否以该片段开结尾,返回布尔值
14.trim()去除字符串首尾空白,返回去除首尾后的新字符串
15.trimStart()或者trimLeft()去除左边空白
16.trimEnd()或者trimRight()去除右边空白
17.replace()替换字符串

15.break/continue/return的使用场景?

break:用于结束整个循环(for、while、do-while)
continue:用于结束循环中的当前层循环,进入下一层循环
return:用户结束整个函数

16.重绘和重排(回流/重构/重载)是什么?如何优化?

样式的调整会引起重绘,比如字体颜色、背景色调整等
Dom的变动会引起重排,比如定位改动、元素宽高调整
所以重排一定会引起页面的重绘, 重绘不一定会引起重排.

避免循环插入dom,比如table的行。可以js循环生成多个dom后,一次性插入。

17.html5有哪些新特性

本地存储,比如localStorage、sessionStorage
语义化标签,如header、footer、nav等,使代码结构清晰,利于seo
canvas svg
web worker,在主线程外再创建一个线程,可与主线程交互
拖放功能

18.伪类和伪元素区别?

伪元素本质上是创建了一个有内容的虚拟元素,如::before ::after。因为相当于多了一个元素/节点,所以叫为元素
伪类本质上用于弥补常规css选择器的不足,因为如果没有我们可能需要多写一个class,所以叫伪类

19.单页面应用是什么?优缺点?如何弥补缺点

单页面对一个入口DOM通过路由去更改内容,整个应用只有一个html页面
SPA优点:用户体验好,没有页面切换就没有白屏情况;
SPA缺点:首屏加载慢,不利于SEO
SPA弥补:通过压缩、路由懒加载缓解首屏慢;通过SSR 服务器端渲染解决SEO问题;

20.组件通信方式有哪些?

1.父向子传值
父组件:

<template>
    <div id="father">
        <son :msg="msg" :fn="myFunc"></son>
    </div>
</template>
 
<script>
import son from "./son.vue";
export default {
    name: "father",
    components: {
        son
    },
    data() {
        msg: "我是父组件";
    },
    methods: {
        myFunc() {
            console.log("我是父组件的方法");
        }
    }
};
</script>

子组件:

<template>
    <div id="son">
        <p>{{msg}}</p>
        <button @click="fn">按钮</button>
    </div>
</template>
<script>
export default {
    name: "son",
    props: ["msg", "fn"]
};
</script>

2.子向父传值
父组件:

<template>
  <div id="father">
    <son :arrList="arrList" @changeIndex="changeIndex"></son>
    <p>{{currentIndex}}</p>
  </div>
</template>
 
<script>
import son from './son.vue'
export default {
  name: 'father',
  components: { son},
  data() {
    return {
      currentIndex: -1,
      arrList: ['龙族', '绘梨衣', '前端','后端']
    }
  },
  methods: {
    changeIndex(index) {
      this.currentIndex = index
    }
  }
}
</script>

子组件:

<template>
  <div>
    <div v-for="(item, index) in arrList" :key="index" @click="emitIndex(index)">{{item}}</div>
  </div>
</template>
 
<script>
export default {
  props: ['arrList'],
  methods: {
    emitIndex(index) {
      this.$emit('changeIndex', index) // 触发父组件的方法,并传递参数index
    }
  }
}
</script>

3.ref / $refs:这个属性用在子组件上,它的引用就指向了该子组件的实例,可以通过实例来访问组件的数据和方法;如果在普通的 DOM 元素上使用,引用指向的就是 DOM元素。
父组件:

<template>
  <child ref="child"></component-a>
</template>
<script>
  import child from './child.vue'
  export default {
    components: { child },
    mounted () {
      console.log(this.$refs.child.name);  // mySon
      this.$refs.child.sayHello();  // Hello father!
    }
  }
</script>

子组件:
``
<template>
<div id="app"></div>
</template>
<script>
export default {
name:'child',
data () {
return {
name: 'mySon'
}
},
methods: {
sayHello () {
console.log('Hello father!')
}
}
}
</script>`




上一篇 下一篇

猜你喜欢

热点阅读