记录一下最近面试题-前端面试记录

react 生命周期有哪些

react 生命周期分为三个状态:

  • 挂载
  • 更新
  • 销毁
    常用生命周期方法:
    componentWillMount 在渲染前调用,在客户端也在服务端。

componentDidMount : 在第一次渲染后调用,只在客户端。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。 如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异步操作阻塞UI)。

componentWillReceiveProps 在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。

shouldComponentUpdate 返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。
可以在你确认不需要更新组件时使用。

componentWillUpdate在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。

componentDidUpdate 在组件完成更新后立即调用。在初始化时不会被调用。

componentWillUnmount在组件从 DOM 中移除之前立刻被调用。

虚拟dom是什么,用来干嘛?

virtual dom(虚拟 DOM) 就是用JS来模拟Dom结构
在使用传统的方式去操作dom时(原生api或jquery),浏览器会从头到尾的执行一遍构建dom树的流程。比如需要一次操作来更新多个DOM节点,我们想要的时一次性构建好DOM树,再执行后续操作,而浏览器收到一个更新dom请求后会直接执行构建流程,执行完成后再进行下一次的dom更新,这样多次更新dom节点就很耗费性能,频繁的操作dom,代价很昂贵,可能操作页面的卡顿。
virtual dom(虚拟 DOM)就是为解决这个问题而设计的,如果一次操作中有多次更新DOM的操作,虚拟DOM不会立即操作DOM,而是将这多次更新的diff内容保存在本地的一个js对象中,最终将这个js对象一次性attach到DOM树上,通知浏览器去执行绘制工作,这样可以避免大量的无谓的计算量。

react diff 原理

diff算法是通过递归对节点进行对比,知道虚拟DOM中真正变化的部分,并针对这一部分进行原生dom操作。
但传统的diff算法因为是递归,算法复杂度高,而react diff 算法降低了这个复杂度。
react三条diff策略:
1 Web UI中DOM节点跨层级的移动操作特别少,可以忽略不计。
2 拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件将会生成不同的树形结构。
3 对于同一层级的一组子节点,它们可以通过唯一 id 进行区分。

在上面三个策略的基础上,React 分别将对应的tree diff、component diff 以及 element diff 进行算法优化,极大地提升了diff效率。
react diff 原理参考

react /vue 多层嵌套组件的父子通信

如果使用了redux,就把需要通信的值放到state中,这样各个地方都可以使用;
如没有使用,可以通过props传递回调方法;
使用事件订阅的方式
vue 修饰符 .sync

redux数据流向

redux 用于管理应用数据状态, 大致流程:视图层通过dispatch触发一个action, action发送到reducer去更新store , store包含最新的state

vue 响应式(双向绑定)原理

vue 通过Object.defineProperty()来实现数据劫持,通过重写对象的get\set方法实现双向绑定;
实现数据的双向绑定,首先要对数据进行劫持监听,所以需要设置一个监听器Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。接着,我们还需要有一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令对应初始化成一个订阅者Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。因此接下去我们执行以下3个步骤,实现数据的双向绑定:

1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。

2.实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。

3.实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器。

vue 计算属性(computed)中能操作localStorage/sessionStorage吗?

面试时,我也很直接的告诉面试官没遇到过,然后面试官回答我不能,原因是初始化vue实例时,还没生成window对象,所以不能操作,而我下来实验后的结果却完全不同:
计算属性中,是能操作localStorage/sessionStorage的

angular 装饰器是什么及使用方法

装饰器是一个函数,它返回的也是一个函数,属于TypeScript的特性,而不是angular的特性,它将元数据添加到类、类成员(属性、方法)和函数参数。
用法:要想应用装饰器,把它放到被装饰对象的上面或左边

angular的设计模式是怎样的

angular属于MVVM,MVVM核心:Model(模型),View(UI),ViewModel(视图模型);

  1. Model:数据展现的对象模型
  2. View:页面UI
  3. ViewModel:实现Model和View的双向绑定

它们的工作模型应该是:Model<=>ViewModel<=>View
MVVM的优点:

  1. 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。

  2. 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。

  3. 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计。

  4. 可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。

TS / es6 接口(interface)的使用方法

interface 是一种描述对象或函数的东西,用来约束对象或函数的类型,使用方法如下:

interface LabelledValue {
  label: string;
}

let Obj: LabelledValue = {label: "str"}; // OK

数据结构转换 树形结构转列表数据(尽量使用es6语法或函数)

   let tree = [{
            name: '1',
            children: [{
                name: '1-1',
                children: [{
                    name: '1-1-1'
                }]
            }]
        },
        {
            name: '2',
            children: [{
                    name: '2-1',
                    children: [{
                        name: '2-1-1'
                    }, ]
                },
                {
                    name: '2-2',
                    children: [{
                        name: '2-2-1'
                    }, ]
                }
            ]
        }
    ]

    const toList = (data, prentName) => {
        let array = []
        data.map(item => {
            array.push({
                ...item,
                prentName: prentName
            });
            if (item.children && item.children.length > 0) {
                array.push(...toList(item.children, item.name))
            }
        })
        return array
    }
    console.log(toList(tree, ''))  // ok

vue/ react 循环的key 是用来干嘛的

key属性相当于组件的身份证,做唯一标识使用,在渲染过程中拿来做新旧的虚拟dom对比。

谈一谈原型链

js中万物皆对象,每个对象都有原型,原型可以理解为对象的默认属性和方法。
每个对象都有一个指向它的原型(prototype)对象的内部链接。这个原型对象又有自己的原型,直到某个对象的原型为 null 为止(也就是不再有原型指向),组成这条链的最后一环。这种一级一级的链结构就称为原型链(prototype chain)

箭头函数与function函数的区别

  1. 箭头函数更加简洁
  2. this指向不同,箭头函数this指向父级作用域this
  3. 箭头函数不能当作构造函数,不能使用new
  4. 箭头函数没有原型属性

js 堆和栈

堆栈是内存的简称。
堆是动态分配内存,内存大小不一,也不会自动释放。栈是自动分配相对固定大小的内存空间,并由系统自动释放.
JS中基本数据类型Undefined、Null、Boolean、String、Number、Symbol都是直接按值直接存在栈中,每种类型的数据占用的内存空间大小都是固定的,并且由系统自动分配自动释放;
而引用数据类型Object,Array,Function这样的数据存在堆内存中,但是数据指针是存放在栈内存中的,当我们访问引用数据时,先从栈内存中获取指针,通过指针在堆内存中找到数据。

深拷贝/浅拷贝

浅拷贝和深拷贝都只针对于引用数据类型,浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存;但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象;
区别:浅拷贝只复制对象的第一层属性、深拷贝可以对对象的属性进行递归复制;

vue route 和router 的区别

1.router是VueRouter的一个对象,通过Vue.use(VueRouter)和VueRouter构造函数得到一个router的实例对象,这个对象中是一个全局的对象,他包含了所有的路由包含了许多关键的对象和属性。

2.route是一个跳转的路由对象,每一个路由都会有一个route对象,是一个局部的对象,可以获取对应的name,path,params,query等

谈一谈js 值(基本)数据类型、引用数据类型

基本数据类型(按值访问):Undefined 、 Null 、 Boolean 、 Number 和 String

引用数据类型(按引用访问):object、Array、function

mvvm 与mvc的区别

MVC是Model-View- Controller的简写。即模型-视图-控制器。M和V指的意思和MVVM中的M和V意思一样。C即Controller指的是页面业务逻辑。使用MVC的目的就是将M和V的代码分离。MVC是单向通信。也就是View跟Model,必须通过Controller来承上启下。MVC和MVVM的区别并不是VM完全取代了C,只是在MVC的基础上增加了一层VM,只不过是弱化了C的概念,ViewModel存在目的在于抽离Controller中展示的业务逻辑,而不是替代Controller,其它视图操作业务等还是应该放在Controller中实现。也就是说MVVM实现的是业务逻辑组件的重用,使开发更高效,结构更清晰,增加代码的复用性。

vue 组件之间的通信

props 、.sync修饰符、$emit触发事件回调、vuex

vue $nextTick()是干什么的

Vue 在修改数据后,视图不会立刻更新,在修改数据之后立即使用这个方法,获取更新后的 DOM。

// 知乎上一个例子
//改变数据
vm.message = 'changed'

//想要立即使用更新后的DOM。这样不行,因为设置message后DOM还没有更新
console.log(vm.$el.textContent) // 并不会得到'changed'

//这样可以,nextTick里面的代码会在DOM更新后执行
Vue.nextTick(function(){
    console.log(vm.$el.textContent) //可以得到'changed'
})

vue 中怎么去掉路由中的#

在配置Router的时候,把mode 设置为 history

export default new Router({
mode: 'history',
...
})

const 是否绝对不可变

定义const 变量为基本数据类型时不可变,但引用数据类型的属性可变

简单说下vuex

vuex 用于状态管理,可以简单地把状态理解成为vue的data里面的变量。当组件之间的data变量关系复杂一点的时候,就把其中的变量抽离出来管理。vue父子组件之间的通信是比较麻烦的,子组件改变父组件数据还要用$emit。如果有一个地方跟仓库一样统一存放着数据,谁要用谁去请求,谁想改就改该多好是吧,vuex就是干这个的,有点全局变量的意思。任何组件需要获取、修改数据,都可以找它。

vue如何写一个双向绑定的组件(这里使用.syns修饰符)

<myComponent  :title.sync="doc.title"></myComponent >

// 当子组件需要更新 title 的值时,它需要显式地触发一个更新事件:
this.$emit('update:title', newValue)

简述js 模块化规范(commonjs、AMD、CMD)

CommonJS是服务器端模块的规范,由Node推广使用,webpack也采用这种规范编写,
CommonJS模块规范主要分为三部分:模块定义、模块标识、模块引用。
模块定义:module对象:在每一个模块中,module对象代表该模块自身。 export属性:module对象的一个属性,它向外提供接口。输出模块变量的最好方法是使用module.exports对象。一个单独的文件就是一个模块。每一个模块都是一个单独的作用域,也就是说,在该模块内部定义的变量,无法被其他模块读取,除非定义为global对象的属性。
模块标识:传递给require方法的参数,必须是符合小驼峰命名的字符串,或者以 . 、… 、开头的相对路径,或者绝对路径。
模块引用:加载模块使用require(同步加载),该方法读取一个文件并执行,返回文件内部的module.exports对象。
优势是在后端,JavaScript的规范远远落后并且有很多缺陷,这使得难以使用JavaScript开发大型应用。比如:没有模块系统、标准库较少、没有标准接口、缺乏包管理系统、列表内容,CommonJS模块规范很好地解决变量污染问题,每个模块具有独立空间,互不干扰,命名空间相比之下就不太好。
CommonJS规范定义模块十分简单,接口十分简洁,支持引入和导出功能,这样可以顺畅地连接各个模块,实现彼此间的依赖关系。
CommonJS规范的提出,主要是为了弥补JavaScript没有标准的缺陷,已达到像Python、Ruby和Java那样具备开发大型应用的基础能力,而不是停留在开发浏览器端小脚本程序的阶段,
缺点没有并行加载机制,由于CommonJS是同步加载模块,这对于服务器端是很不好的,因为所有的模块都放在本地硬盘。等待模块时间就是硬盘读取文件时间,很小。但是,对于浏览器而言,它需要从服务器加载模块,涉及到网速,代理等原因,一旦等待时间过长,浏览器处于”假死”状态。
所以浏览器端不是很适合Common.Js,出现另一种规范AMD

AMD 是运行在浏览器环境的一个异步模块定义规范 ,是RequireJS 在推广过程中对模块定义的规范化产出。AMD推崇依赖前置,在定义模块的时候就要声明其依赖的模块,优点是用户体验好,因为没有延迟,依赖模块提前执行了。

CMD是一个通用模块定义规范;是SeaJs推广过程中对模块定义的规范化产出,CMD推崇依赖就近,只有在用到某个模块的时候才会去require,优点是性能好,因为只有用户需要的时候才执行。

es6 的import 与 require 的区别

require是Commonjs的规范;
import是es6为js模块化提出的新的语法;
require输出的,是一个值的拷贝,而import输出的是值的引用;
require是运行时加载,import是编译时输出接口;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

caperxi

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

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

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

打赏作者

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

抵扣说明:

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

余额充值