前端面试题(1)- 20.08.27总结

一、vue中render函数与template对比。

介绍:

render --- js的方式做渲染。

tempalte --- html方式做渲染。

render里有一个函数h,这个h的作用是将单文件组件进行虚拟DOM的创建,然后再通过render进行解析。h就是createElement()方法:createElement( 标签名称, 属性配置, children )。

template是一种编译方式,但是template最终还是要通过render的方式再次进行编译。

区别:

1、render渲染方式可以让我们将js发挥到极致,因为render的方式其实是通过createElement()进行虚拟DOM的创建。逻辑性比较强,适合复杂的组件封装;

2、template是类似于html一样的模板来进行组件的封装,适合简单的组件封装;

3、render的性能比template的性能好很多;

4、render函数优先级大于template。

 

二、redux中createStore方法内部大概做了些什么?(也就是说store是怎么生成的?)

首先,执行createStore方法时,传入reducer,createStore方法返回一个对象,包含三个方法:getState,dispatch,subscribe。

subscribe方法相当于一个监听器,用来接收监听函数,并将监听函数push到一个数组中,重复的函数将不再被添加,当执行dispatch方法时,在dispatch方法内部,调用reducer,返回新的state,并执行数组中的所有监听函数,getState方法就是返回最新的state。

 

三、'true'==true的返回结果是什么?为什么?(相等运算符在计算的时候遵循的规则是什么?)

答案:

返回结果是false。

分析:

首先,相等运算符在比较不同的数据类型之前会强制转换。

在转换不同的数据类型时,相等运算符遵循下列基本规则:

        1、如果有一边是布尔,会在比较之前将其转换为数值,false --> 0,true --> 1;

        2、如果有一边是字符串,另一边是数值,在比较之前先将字符串转换为数值;

        3、如果有一边是对象,另一边不是,则调用对象的valueOf()方法,用得到的基本类型按照前两条规则进行比较。

那么,'true'==true,根据规则1,就转换为'true'==1,再根据规则2,将字符串'true'转换为数值,转换为数值会调用Number()函数,Number()函数转换规则如下:

        1、如果是Boolean值,true和false将分别被转换为1和0;

        2、如果是数字值,只是简单的传入和返回;

        3、如果是null值,返回0;

        4、如果是undefined,返回NaN;

        5、如果是字符串,遵循下列规则:

            (1)如果字符串只包含数字(包括前面带加号或负号的情况),则将其转换为十进制数值,即'1'会变成1,'111'会变成111,'01234'会变成1234,这里的前面的0会被省略;

            (2)如果字符串中包含有效的浮点格式,如"1.1",则将其转换为对应的浮点数值(同样,如果前面有0,如02.1,也会忽略前面的0);

            (3)如果字符串中包含有效的十六进制格式,例如"0xf",则将其转换为相同大小的十进制整数值;

            (4)如果字符串是空的(不包含任何字符),则将其转换为0;

            (5)如果字符串中包含除上述格式之外的字符,则将其转换为NaN。

那么,按照规则5(5),'true'会被转换为NaN,最后就变为对NaN==1进行判断求值,又有NaN与任何值都不相等,包括NaN本身,所以最终的结果为false。

整个判断流程为:'true' == true --> 'true' == 1 --> NaN == 1 --> false。

 

四、Promise为什么可以实现链式调用,以及链式调用的执行顺序?

Promise为什么可以实现链式调用?

promise 的 then/catch 方法执行后会也返回一个 promise。

链式调用的执行顺序?

关于执行顺序,这里有两个结论:

        1、当执行 then 方法时,如果前面的 promise 已经是 resolved 状态,则直接将回调放入微任务队列中;

        2、当一个 promise 被 resolve 时,会遍历之前通过 then 给这个 promise 注册的所有回调,将它们依次放入微任务队列中。

关于结论1:

执行then方法是同步的,而then中的回调是异步的,同步代码执行时,会先将then中的回调先放入微任务队列,等同步任务执行完毕后,再依次取出执行,同时,在同步执行then方法时,会进行判断:

        1、如果前面的 promise 已经是 resolved 状态,则会立即将回调推入微任务队列(但是执行回调还是要等到所有同步任务都结束后);

        2、如果前面的 promise 是 pending 状态则会将回调存储在 promise 的内部(promse内部的一个数组中),一直等到 promise 被 resolve 才将回调推入微任务队列。

关于结论2:

对于会遍历之前通过 then 给这个 promise 注册的所有回调这句话的理解,来考虑一种情景,假设实例化一个promise,该promise被延迟resolve(比如将resolve的调用放入setTimeout函数中,设置延迟时间为3秒),但因为then方法是同步的,那么,在这个promise被resolve之前,可能已经通过then方法注册了几个回调函数,那么这几个回调函数不会被执行,也不会被放入到微任务队列中,它们会被这个promise内部储存起来,等到这个promise被resolve后,才会对之前储存的这几个回调函数进行遍历依次推入微任务队列(这里先存入的回调函数就先推入微任务队列),此时如果没有同步任务就会逐个取出再执行。

这里需要注意的点:

        1、对于普通的 promise 来说,当执行完 resolve 函数时,promise 状态就为 resolved;

        2、对于 then 方法返回的 promise 它是没有 resolve 函数的,取而代之只要 then 中回调函数代码执行完毕,这个 then 返回的 promise 就算被 resolve。

(原文参考链接:https://mp.weixin.qq.com/s/VfP9_8kUx6hyrII7LVae6g

 

五、简单说一下es6装饰器,在react中,装饰器可以用来装饰函数组件吗?

介绍:

装饰器(Decorator)是一种与类(class)相关的语法,用来注释或修改类和类方法。装饰器是一种函数,写成@ + 函数名,它可以放在类和类方法的定义前面。装饰器函数的参数就是被修饰的类和类方法本身。如果觉得一个参数不够用,可以在装饰器外面在封装一层函数,该函数再返回一个函数,用来接收类和类方法。

在react中,装饰器可以用来装饰函数组件吗?

答案:

不能。

分析:

装饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,装饰器能在编译阶段运行代码。也就是说,装饰器本质就是编译时执行的函数。

react函数组件本身是个函数,函数是存在函数提升的。在代码编译时,函数已经被定义,但是没有被赋值,在代码编译时,装饰器也已经执行完毕,等到代码运行的时候,装饰器已不再起作用了。

react函数组件本身是个函数,函数是存在函数提升的。在代码编译阶段,函数被定义,装饰器开始执行,但由于函数提升,函数体还没有任何内容,所以装饰器也就起不到他应有的装饰作用(也就是增强或者扩展函数功能)。(感觉这段之前的描述不太准确,故作修改。--- 20.09.02改)

 

六、简单说一下,如何在react中实现瀑布流加载?(左右两列的一个商品长列表)

分析:

首先,实现瀑布流加载,需要把长列表左右两列分开进行渲染,如果不这样做,按照正常思路合并为一个列表进行渲染的话,尽管每一个商品的高度不一样,但是在渲染的时候,每渲染完一行,左右两列中,短的一列会自动补白,用来填补高度,保持和长的一列一样高,这样最终的渲染结果就是每一行的两个商品是一样高的,最终左右两列也是一样高的,所以必须得左右两列分开渲染。

思路:

1、在componentDidMount中,可以直接对window进行滚动事件的监听,但是会出现因为被滚动父级盒子不是window,导致滚动事件不被触发的情况,这里只需要把addEventListener第三个参数设为true,即在捕获阶段就执行window滚动事件的回调函数;(这一步多写了,属于触底后加载数据的部分,故删除,那么在TheWaterfallFlow.jsx的componentDidMount中的给window添加滚动事件的监听这一步也作删除。--- 20.08.28改)

2、拿到总数据后,存入this.arr中,接着创建两个ref,假设分别为this.leftDiv和this.rightDiv,分别绑定到左右列表的单个商品的最外层容器盒子上(即进行循环的部分的最外层div),再设置两个响应属性,这里假设为leftData和rightData,定义为两个空数组,分别用来存储左右两列的数据;

3、在componentDidmount中,调用this.setState方法,先往两个空数组中分别存入一个数据(用的是数组的shift()方法),等到dom渲染完毕,在this.setState方法的第二个参数回调函数中,就可以通过this.leftDiv.current和this.rightDiv.current拿到两个dom元素,通过this.leftDiv.current.offsetHeight和this.rightDiv.current.offsetHeight可以获取到这两个dom元素的高度,然后再设置this.leftContainerHeight和this.rightContainerHeight把这两个高度分别存起来。这时候调用另外定义的一个函数,这里假设这个函数为this.handleLoadNextElement,这个函数内部通过判断this.leftContainerHeight和this.rightContainerHeight的大小关系,调用this.setState方法,往高度小的一边存入一个新的数据,和旧数据进行合并(展开运算符,数组的shift()方法),大的一边本次不存入新数据,如果相等,就往leftData里存入一个新数据,和旧数据进行合并,接下来的步骤就和上面所说的步骤差不多了,等到dom渲染完毕,在this.setState方法的第二个参数回调函数中,就可以通过this.leftDiv.current和this.rightDiv.current分别拿到两个列表的最后一个dom元素,通过this.leftDiv.current.offsetHeight和this.rightDiv.current.offsetHeight可以获取到这两个dom元素的高度,再分别将这两个高度累加到this.leftContainerHeight和this.rightContainerHeight中;

4、累加完高度,紧接着就是递归调用this.handleLoadNextElement了,进行高度的比较,接着存数据,再进行渲染,每次渲染完,在this.setState的第二个参数回调函数中,拿到左右两列列表的最后一个dom元素的高度,再进行高度的累加,如此反复,直到数据渲染完毕。

大概代码实现如下:

// TheWaterfallFlow.jsx
import React, { Component, createRef } from 'react'
import TheWaterfallFlowUI from './TheWaterfallFlowUI'
import axios from 'axios'

const get = ({url, params}) => {
    return axios({
        url,
        method: 'get',
        params
    })
    .then((result) => {
        return result.data;
    })
}

const post = ({url, data}) => {
    return axios({
        url,
        method: 'post',
        data
    })
    .then((result) => {
        return result.data;
    })
}

export default class TheWaterfallFlow extends Component {
    constructor() {
        super();
        this.leftDiv = createRef();
        this.rightDiv = createRef();
    }

    state = {
        leftData: [],
        rightData: []
    }

    handleLoadNextElement() {
        if (!this.arr.length) return
        if (this.leftContainerHeight <= this.rightContainerHeight) {
            this.setState(
                (preState) => {
                    return {
                        leftData: [
                            ...preState.leftData,
                            this.arr.shift()
                        ],
                        rightData: preState.rightData
                    }
                },
                () => {
                    if (this.leftDiv.current) {
                        this.leftContainerHeight += this.leftDiv.current.offsetHeight;
                        this.handleLoadNextElement();
                    }
                }
            );
        } else {
            this.setState(
                (preState) => {
                    return {
                        leftData: preState.leftData,
                        rightData: [
                            ...preState.rightData,
                            this.arr.shift()
                        ]
                    }
                },
                () => {
                    if (this.rightDiv.current) {
                        this.rightContainerHeight += this.rightDiv.current.offsetHeight;
                        this.handleLoadNextElement();
                    }
                }
            );
        }
    }

    async componentDidMount() {
        // window.addEventListener('scroll', (e) => this.handleLazyLoading(e), true);
        this.arr = [];
        this.leftContainerHeight = 0;
        this.rightContainerHeight = 0;
        let result = await get({
            url: 'url'
        })
        this.arr = result;
        this.setState(
            {
                leftData: [this.arr.shift()],
                rightData: [this.arr.shift()]
            },
            () => {
                this.leftContainerHeight += this.leftDiv.current.offsetHeight;
                this.rightContainerHeight += this.rightDiv.current.offsetHeight;
                this.handleLoadNextElement();
            }
        );
    }

    render() {
        return (
            <TheWaterfallFlowUI
                leftData={this.state.leftData}
                rightData={this.state.rightData}
                leftDiv={this.leftDiv}
                rightDiv={this.rightDiv}
            />
        )
    }
}
// TheWaterfallFlowUI.jsx
import React from 'react'

export default function TheWaterfallFlowUI(props) {
    return (
        props.leftData.length > 0 && props.rightData.length > 0 &&
        <div className="content">
            <div>
                {
                    props.leftData.map((item) =>
                        (
                            <div
                                key={item.id}
                                ref={props.leftDiv}
                            >
                                ......
                            </div>
                        )
                    )
                }
            </div>

            <div>
                {
                    props.rightData.map((item) =>
                        (
                            <div 
                                key={item.id} 
                                ref={props.rightDiv}
                            >
                                ......
                            </div>
                        )
                    )
                }
            </div>
        </div>
    )
}

 

如果发现有什么不足之处或者不正确需要修改补充的地方,欢迎指正,共同进步~~~ 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值