性能优化

性能优化,永远是面试的重点,性能优化对于 React 更加重要

  • 在页面中应用了setTimout()addEventListener()等,要及时在componentWillUnmount()中销毁
  • 应用异步组件
  • 应用 React-loadable 动静加载组件
  • shouldComponentUpdate(简称SCU )、React.PureComponentReact.memo
  • 不可变值 ImmutableJS
shouldComponentUpdate (nextProps, nextState) {    return true // 能够渲染,执行 render(),默认返回 true    return false // 不能渲染,不执行 render()}

什么状况下须要应用 shouldComponentUpdate

在React中,默认状况下,如果父组件数据产生了更新,那么所有子组件都会无条件更新 !!!!!!
通过shouldComponentUpdate()retrun fasle 来判断阻止 Header 组件做无意义的更新
shouldComponentUpdate()并不是每次都须要应用,而是须要的时候才会优化
class App extends React.Component {    constructor () {        this.state = { list: [] }    }    render () {        return (            <div>                {/* 当list数据发生变化时,Header组件也会更新,调用 render() */}                <Header />                <List data={this.state.list}            </div>        )    }}

shouldComponentUpdate()判断中,有一个有意思的问题,解释为什么 React setState() 要用不可变值

// 父组件中changeList () {    this.state.list.push({id: 2})    this.setState({        list: this.state.list    })}// 子组件中import _ from 'lodash'shouldComponentUpdate(nextProps, nextState) {    // 数组深度比拟(一次性递归到底,消耗性能,工作中慎用)    if (_.isEqual(nextProps.list, this.props.list)) {        return false // 相等,不渲染    }    return true // 不相等,渲染}
子组件将始终不会渲染,因为在shouldComponentUpdate()中,this.state.list.push()曾经批改了this.props.list,而this.setState()批改了nextProps.list所以两个值深度比拟,将始终雷同。

PureComponent 和 memo

  • class类组件中用PureComponent,无状态组件(无状态)中用memo
  • PureComponent, SCU中实现了浅比拟
  • 浅比拟已应用大部分状况(尽量不要做深度比拟)
PureComponent 与一般 Component 不同的中央在于,PureComponent自带了一个shouldComponentUpdate(),并且进行了浅比拟
// memo用法function MyComponent (props) {    /* 应用 props 渲染 */}// areEqual 也可不传function areEqual(prevProps, nextProps) {    if (prevProps.seconds===nextProps.seconds) {        return true    } else {        return false    }}export default React.memo(MyComponent, areEqual)

immutable.js

  • 彻底拥抱“不可变值”
  • 根底共享数据(不是深拷贝),速度快
  • 有肯定学习和迁徙老本

常见根底面试考题

React 组件如何通信

  1. 父子组件通过 属性 和 props 通信
  2. 通过 context 通信
  3. 通过 Redux 通信

this.setState()相干

import React from 'react'class App extends React.Component {    constructor (props) {        super(props)        this.state = { count: 0 }    }    componentDidMount () {        this.setState({ count: this.state.count + 1 })        console.log(this.state.count) // 0        this.setState({ count: this.state.count + 1 })        console.log(this.state.count) // 0        setTimeout(() => {            this.setState({count: this.state.count + 1 })            console.log(this.state.count) // 2        }, 0)        setTimeout(() => {            this.setState({count: this.state.count + 1 })            console.log(this.state.count) // 3        }, 0)        // setTimeout(function () {        //     this.setState({count: this.state.count + 1 })        //     console.log(this.state.count) // 报错,this 指向问题        // }, 0)    }    render () {        return <h1>{this.state.count}</h1>    }}export default App // 返回高阶函数

JSX实质是什么.....

前端富文本 dangerouslySetInnerHTML

const rawHtml = '<div><p>Title</p></div>'const rawHtmlData = {    __html: rawHtml // 这里有个下划线}return <div dangerouslySetInnerHTML={rawHtmlData}></div>

两种绑定事件

<button onClcik={bindClcik1.bind(this)}> 应用 .bind(this) </button><button onClcik={bindClcik2}> 箭头函数 </button>// 应用 class 的自带函数,须要重定向 thisbindClcik1 () { alert('bindClcik1') }// 应用静态方法,应用箭头函数不须要应用 bind(this)bindClick2 = () => { alert('bindClcik2') }

Event、默认事件、事件冒泡

这里打印进去的Event对象是 React 封装过的SyntheticEvent,能够看__proto__.constructor。React 标准化了事件对象,因而在不同的浏览器中都会有雷同的属性。

React 中事件绑定跟 Vue 中齐全不同,Vue中事件绑定和触发的对象为同一元素,React中事件触发的对象为document,绑定元素为以后元素。React的所有事件都会被挂载到document上和DOM事件不同。

Vue 的Event是原生,事件被挂载到以后元素和DOM事件一

<a href="www.baidu.com" onClick={this.getEvent} target="blank">Get Event</a>getEvent = (event) => {    event.preventDefault() // 阻止默认事件    event.stopPropagation() // 阻止事件冒泡    console.log(event) // 非原生的 Event    console.log(event.nativeEvent) // 获取原生的 Event    console.log(event.nativeEvent.target) // 绑定事件的对象,这里为 <a></a>    console.log(event.nativeEvent.currentTarget) // 触发事件的对象,这里为 document}

事件传参

通过.bind()传参

<div onClick={this.getParams1.bind(this, 'id1', 'title1')}>get params 1</div>getParams1 (id, title, event) {    console.log('id', id)    console.log('title', title)    console.log('event', event)  // 最初一个参数为Event对象}

通过箭头函数传参

<div onClick={(event) => { this.getParams2('id2', 'title2', event) }}>get params 2</div>getParams2 (id, title, event) {    console.log('id', id)    console.log('title', title)    console.log('event', event) }

参考 前端react面试题具体解答

表单

<div>    <label htmlFor="userName"></label>    <input value={this.state.userName} onChange={this.handleInputChange.bind(this)} /></div>// 实现相似双向数据绑定handleInputChange (even t) {    const userName = event.target.value    this.setState(() => ({        userName    }))    // 上面这种写法会报错,因为 this.setState 传递一个函数时,为异步办法,等异步执行时曾经没有 event    this.setState(() => ({        userName = event.target.value    }))}

组件传参

一般参数/函数

// 父组件<div>    <Child text={this.state.text} /></div>// 子组件<div>    <p>{this.props.text}</p></div>

属性类型查看

import PropTypes from 'prop-types'// 对传递的参数强校验TodoItem.propTypes = {  content: PropTypes.string.isRequired, // 限度为字符串且必传}

setState()

  1. 不可变值
  2. 可能是异步更新
  3. 可能会被合并
// 谬误的写法this.setState({    count: this.state.count + 1})// 正确的写法const count = this.state.count + 1this.setState({ count })

正确批改数组值

// 不能应用 push pop splice 等,这样违反了不可变值,会影响 shouldCompententUpdate 判断this.setState(() => ({    list1: this.state.list1.concat(100), // 追加    list2: [...this.state.list2, 100], // 追加    list3: this.state.list3.slice(0, 3) // 截取    list4: this.state.list4.filter(item => item > 100) // 筛选}))

正确批改对象值

this.setState(() => ({    obj1: Object.assign({}, this.state.obj1, {a: 100}),    obj2: {...this.state.obj2, a: 100}}))

通常状况下,setState()为异步更新数据

const count = this.state.count + 1this.setState({    count: count}, () => {    // 这个函数没有默认参数    // 类比 Vue 的 $nextTick    console.log(this.state.count) // 打印更新后的值})console.log(this.state.count) // 打印更新前的值

setState()同步更新数据,在setTimeout()setState()是同步的

setTimeout(() => {    const count = this.state.count + 1    this.setState({ count })    console.log(this.state.count)})

本人定义的 DOM 事件,setState() 是同步的

componentDidMount () {    document.body.addEventListener('click', () => {        const count = this.state.count + 1        this.setState({ count })        console.log(this.state.count)    })}

【重点】 传入对象,会被合并,后果只执行一次,相似于Object.assgin()

初始值 this.state.count = 0this.setState({    count: this.state.count + 1})this.setState({    count: this.state.count + 1})this.setState({    count: this.state.count + 1})输入值 this.state.count = 1

【重点】 传入函数,不会被合并,因为函数无奈合并

初始值 this.state.count = 0this.setState((prevState, props) => {   return {       count: prevState.count + 1   } })this.setState((prevState, props) => {   return {       count: prevState.count + 1   } })this.setState((prevState, props) => {   return {       count: prevState.count + 1   } })输入值 this.state.count = 3

组件生命周期

Initialization 初始化

  • constructor() : class 的构造函数,并非React独有

Mounting 挂载

  • componentWillMount() : 在组件行将被挂载到页面的时刻主动执行;
  • render() : 页面挂载;
  • componentDidMount() : 组件被挂载到页面之后主动执行;

componentWillMount()componentDidMount(),只会在页面第一次挂载的时候执行,state变动时,不会从新执行

Updation 组件更新

  • shouldComponentUpdate() : 该生命周期要求返回一个bool类型的后果,如果返回true组件失常更新,如果返回false组件将不会更新;
  • componentWillUpdate() : 组件被更新之前执行,如果shouldComponentUpdate()返回false,将不会被被执行;
  • componentDidUpdate() : 组件更新实现之后执行;

componentWillReceiveProps() : props独有的生命周期,执行条件如下:

  1. 组件要从父组件接管参数;
  2. 只有父组件的render()被执行了,子组件的该生命周期就会执行;
  3. 如果这个组件第一次存在于父组件中,不会执行;
  4. 如果这个组件之前曾经存在于父组件中,才会执行;

Unmounting 组件卸载

  • componentWillUnmount() : 当组件行将被从页面中剔除的时候,会被执行;

生命周期简略应用场景

  1. 应用shouldComponentUpdate()避免页面进行不必要的渲染
# 用生命周期进行性能优化shouldComponentUpdate () {    if (nextProps.content !== this.props.content) {      return true;    }    return false;}
  1. Ajax 申请页面初始数据componentDidMount()

不能写在render()之中,因为会反复调用,也不能写在componentWillMount()之中,会与RN等其它框架抵触,不然也能够写在这外面,同样是只执行一次。

同样也能够写在构造函数constructor()之中,然而不倡议这样做。

import axios from 'axios'componentDidMount () {    axios.get('/api/todolist').then((res) => {      console.log(res.data);      this.setState(() => ({        list: [...res.data]      }));    }).catch((err) => {      console.log(err);    });}

无状态组件(函数组件)

当一个组件只有一个render()函数时,咱们就可将这个组件定义为无状态组件,无状态组件只有一个函数。
无状态组件的性能比拟高,因为它仅是一个函数,而一般组件是一个class

  • 纯函数
  • 输出props,输入JSX
  • 没有实例
  • 没有生命周期
  • 没有state
  • 不能扩大其它办法
function List (props) {    const { text } = this.props    return (        <div>{text}</div>    )}

非受控组件

class App extends React.Component {    constructor (props) {        super(props)        this.state = {            name: '',            flag: true        }        this.nameInputRef = React.createRef() // 创立 ref        this.fileInputRef = React.createRef() // 创立 ref    }    render () {        return (            <div>                {/* 这里应用 defaultValue 而不是value,应用 ref */}                <input defaultValue={this.state.name} ref={this.nameInputRef} />                <button onClick={this.alertName.bind(this)}>alert value</button>                {/* file 类型的必须用 ref 获取 dom 来获取数据 */}                <input type="file" ref={this.fileInputRef} />            </div>        )    }    alertName () {        const ele = this.nameInputRef.current // 通过 ref 获取 dom 节点        alert(ele.value)    }}

portals 传送门

应用 Portals 渲染到 body 上,fixed 元素要放在 body 上,有更好的浏览器兼容。

常见应用场景:

  1. 父组件 overflow: hidden , 然而子组件又想展现;
  2. 父组件的 z-index 太小;
  3. fixed 须要放在 body 第一层;
import ReactDOM from 'react-dom'render () {    return ReactDOM.creatPortal(        <div>{this.props.children}</div>,        document.body    )}

context 上下文

应用场景:公共信息(语言、主题)传递给每个组件,如果组件层级过多,用props传递就会繁琐,用 redux 小题大做。

import React from 'react'// 创立 Context 填入默认值(任何一个 js 变量)export const {Provider,Consumer} = React.createContext("默认名称")// 在最下级组件中constructor (props) {    super(props)    this.state = { theme: 'light' }}render () {    // 这里应用 this.state.theme 是为了能够批改,初始化的值为默认值,不能批改    // value 中放共享的数据    return (        <Provider value={this.state.theme}>            ....            <button onClick={this.changeTheme}></button>        </Provider>    )}// 子组件中调用import { Consumer } from "./index";//引入父组件的Consumer容器render () {    return (        // Consumer 容器,能够拿到上文传递下来的 theme 属性,并能够展现对应的值        <Consumer>            { theme => <div>子组件。获取父组件的值: {theme} </div> }        </Consumer>    )}

异步组件

// 引入须要异步加载的组件const LazyComponent = React.lazy(() => import('./lazyDemo') )// 应用异步组件,异步组件加载中时,显示fallback中的内容<React.Suspense fallback={<div>异步组件加载中</div>}>    <LazyComponent /></React.Suspense>

组件公共逻辑的抽离

  • Vue 中的 mixin,已被 React弃用
  • 高阶组件 HOC
  • Render Props

高阶组件

高阶组件不是一种性能,而是一种模式

// 高阶组件 根本用法const HOCFactory = (Component) => {    class HOC extends React.Component {        // 在此定义多个组件的公共逻辑        render () {            return <Component {...thi.props} /> // 返回拼装的后果        }    }    return HOC}const MyComponent1 = HOCFactory(WrappedComponent1)const MyComponent2 = HOCFactory(WrappedComponent2)

理论案例

import React from 'react'// 高阶组件const withMouse = (Component) => {    class withMouseComponent extends React.Component {        constructor(props) {            super(props)            this.state = { x: 0, y: 0 }        }        handleMouseMove = (event) => {            this.setState({                x: event.clientX,                y: event.clientY            })        }        render() {            return (                <div style={{ height: '500px' }} onMouseMove={this.handleMouseMove}>                    {/* 1. 透传所有 props 2. 减少 mouse 属性 */}                    <Component {...this.props} mouse={this.state}/>                </div>            )        }    }    return withMouseComponent}const App = (props) => {    const a = props.a    const { x, y } = props.mouse // 接管 mouse 属性    return (        <div style={{ height: '500px' }}>            <h1>The mouse position is ({x}, {y})</h1>            <p>{a}</p>        </div>    )}export default withMouse(App) // 返回高阶函数

Render Props

Render Props 核心思想:通过一个函数将 class 组件的 state 作为 props 传递给纯函数组件

class Factory extends React.Component {    constructor () {        this.state = {            /* 这里 state 即多个组件的公共逻辑的数据 */        }    }    /* 批改 state */    render () {        return <div>{this.props.render(this.state)}</div>    }}const App = () => {    /* render 是一个函数组件 */    <Factory render={        (props) => <p>{props.a} {props.b}...</p>    } />}

Redux 单项数据流

  1. dispatch(action)
  2. reducer 产生 newState
  3. subscribe 触发告诉

Redux 单项数据流图

React-router

路由模式

  • hash模式(默认),如:baidu.com/#/user/10
  • H5 history 模式,如:baidu.com/user/20

后者须要 server 端反对,因而无非凡需要可抉择前者

罕用组件

import {    HashRouter,    BrowserRouter,    Switch,    Route} from 'react-router-dom'function RouterComponent () {    return (        <BrowserRouter>            <Switch>                <Route path='/' exact component={Home}>                {/* 动静路由 */}                <Route path='/detail/:id' exact component={Detail}></Route>                {/* 匹配404等页面 */}                <Route path='*' exact component={NotFound}></Route>            </Switch>        </BrowserRouter>    )}

路由跳转

  • 标签跳转,通过 <Link to="/"> 这个隐性 a 标签跳转
  • JS跳转,import { useHistory } from 'react-router-dom' && history.push('/')

路由配置懒加载

import React from 'react'import { BrowserRouter, Route, Switch } from 'react-router-dom'const Home = React.lazy(() => import('./pages/Home'))const Detail = React.lazy(() => import('./pages/Detail'))const App = () => (    <BrowserRouter>        <React.Suspense fallback={<div>Loading...</div>}>            <Switch>                <Route exact path="/" component={Home} />                <Route exact path="/detail" component={Detail} />            </Switch>        </React.Suspense>    </BrowserRouter>)

Vue 原理

数据驱动视图(MVVM, setState),Vue MVVM ( Model + View + ViewModel )

Vue响应式

组件 data 的数据一旦变动,立即触发视图的更新,实现数据驱动视图的第一步

外围API:Object.defineProperty,Object.defineProperty 有一些毛病,Vue3.0 开始启用 Proxy, Proxy有兼容性问题,且无奈 polyfill(兼容性问题解决方案)
// Object.defineProperty 根底应用const data = {}const name = 'Actoress'Object.defineProperty(data, "name", {    get: function () {       console.log('get')       return name    },    set: function (newValue) {        console.log('set')        name = newValue    }})// 调用console.log(data.name) // get() 执行 => 'Actoress'data.name = 'Wu' // set() 执行

深度监听

  • 深度监听,须要递归到底,一次性计算量大
  • 无奈监听新增属性/删除属性
  • 数组须要从新定义数组原型
// 触发更新视图function updateView() {   console.log('视图更新')}// 从新定义数组原型const oldArrayProperty = Array.prototype// 创立新对象,原型指向 oldArrayProperty ,再扩大新的办法不会影响原型const arrProto = Object.create(oldArrayProperty);['push', 'pop', 'shift', 'unshift', 'splice'].forEach(methodName => {   arrProto[methodName] = function () {       updateView() // 触发视图更新       oldArrayProperty[methodName].call(this, ...arguments)       // Array.prototype.push.call(this, ...arguments)   }})// 从新定义属性,监听起来function defineReactive(target, key, value) {   // 深度监听   observer(value)   // 外围 API   Object.defineProperty(target, key, {       get() {           return value       },       set(newValue) {           if (newValue !== value) {               // 深度监听               observer(newValue)               // 设置新值               // 留神,value 始终在闭包中,此处设置完之后,再 get 时也是会获取最新的值               value = newValue               // 触发更新视图               updateView()           }       }   })}// 监听对象属性function observer(target) {   if (typeof target !== 'object' || target === null) {       // 不是对象或数组       return target   }   // 不能写在这里,这样会净化全局的 Array 原型   // Array.prototype.push = function () {   //     updateView()   //     ...   // }   if (Array.isArray(target)) {       target.__proto__ = arrProto   }   // 从新定义各个属性(for in 也能够遍历数组)   for (let key in target) {       defineReactive(target, key, target[key])   }}// 筹备数据const data = {   name: 'Actoress',   age: 20,   info: {       address: '北京' // 须要深度监听   },   nums: [10, 20, 30]}// 监听数据observer(data)// 测试// data.name = 'Wu'// data.age = 21// // console.log('age', data.age)// data.x = '100' // 新增属性,监听不到 —— 所以有 Vue.set// delete data.name // 删除属性,监听不到 —— 所有已 Vue.delete// data.info.address = '上海' // 深度监听

虚构DOM(Virtual DOM)

vdom 是实现 Vue 和 React 的重要基石
为什么会有 vdom

  • 有了肯定复杂度,想缩小计算次数比拟难
  • 能不能把计算,更多的转移到JS计算?因为JS执行速度很快
  • vdom 用 JS 模仿 DOM 构造,计算出最小的变更,操作DOM

用JS模仿DOM构造

应用 snabbdom 操作虚构 dom

Diff 算法

  • diff 算法是 vdom 中最外围、最要害的局部
  • diff 算法能在日常应用 Vue React 中体现进去(循环的 key)

优化前 树 diff 的工夫复杂度 (n^3)

  1. 遍历Tree1,遍历Tree2
  2. 排序
  3. 假如有1000个节点,就要计算1亿次,算法不可用

优化后工夫复杂度 (n^1)

  1. 只比拟同一层级,不跨级比拟
  2. tag 不雷同,则间接删掉重建,不再深度比拟
  3. tag 和 key,两者都雷同,则认为是雷同节点,不再深度比拟

React 原理

数据驱动视图(MVVM, setState)

  • 数据驱动视图 - React this.setState()
  • 函数式编程:函数式式编程是一种编程范式,两个最重要的概念是 纯函数不可变值

JSX 实质

  • JSX 等同于 Vue 模板
  • Vue 模板不是 html
  • JSX 也不是 JS

讲JSX语法,通过 React.createElement()编译成Dom,BABEL 能够编译JSX
流程:JSX => React.createElement() => 虚构DOM (JS对象) => 实在DOM
React 底层会通过 React.createElement() 这个办法,将 JSX 语法转成JS对象,React.createElement() 能够接管三个参数,第一个为标签名称,第二参数为属性,第三个参数为内容

createElement() 依据首字母大小写来辨别是组件还是HTML标签,React规定组件首字母必须大写,HTML规定标签首字母必须小写
// 第一个参数为 标签(tag) 可为 'div'标签名 或 List组件// 第二个参数为:属性(props)// 第三个参数之后都为子节点(child),能够在第三个参数传一个数组,也能够在第三、四、五....参数中传入React.createElement('tag', null, [child1, chlild2, child3])或者React.createElement('tag', { className: 'class1' }, child1, chlild2, child3)

事件合成机制

  • 所有事件挂载到 document 上
  • event 不是原生的,是SyntheticEvent合成事件对象
  • 与 Vue 事件不同,和 DOM 事件也不同

为什么要合成事件机制

  • 更好的兼容性和跨平台,解脱传统DOM事件
  • 挂载到document,缩小内存耗费,防止频繁解绑
  • 不便事件的对立治理,如:事务机制

setState 和 batchUpdate(批处理)

setState

  • 有时异步(一般应用),有时同步(setTimeout, DOM事件)
  • 有时合并(对象模式),有时不合并(函数模式),比拟好了解(相似 Object.assign),函数无奈合并

外围要点

  • setState 主流程
  • batchUpdate 机制
  • transaction(事务)机制

this.setState()是否是异步,看 isBatchingUpdates 的状态,为 true 就是异步,为 false 就是同步

哪些能命中 batchUpdate 机制

  • 生命周期(和它调用的函数)
  • React 中注册的事件(和它调用的函数)
  • React 能够“治理”的入口

哪些不能命中 batchUpdate 机制

  • setTimeout setInterval等(和它调用的函数)
  • 自定义的DOM工夫(和它调用的函数)
  • React“管不到”的入口

transaction 事务机制

常见根底面试题

1.组件之间如何通信

  • 父子组件 props
  • 自定义事件
  • Redux 和 Context,简略数据用 Context

2.JSX 实质

JSX => React.createElement() => 虚构DOM (JS对象) => 实在DOM

3.shouldComponentUpdate 用处

  • 性能优化
  • 配合“不可变值”一起应用,否则会出错

4.redux单项数据流

Redux 单项数据流图

5.setState场景题

6.什么是纯函数

  • 返回一个新值,没有副作用(不会批改其它值)

7.列表渲染为何要用key

  • 必须用 key,且不能是 index 和 random
  • diff 算法中通过 tag 和 key 判断,是否是同一个节点
  • 缩小渲染次数,晋升渲染性能

8.函数组件 和 class 组件区别

  • 纯函数,输出 props,输入JSX
  • 没有实力,没有生命周期,没有state
  • 不能扩大其它办法

9.如何应用异步组件

  • 加载大组件
  • React.lazy
  • React.Suspense

10.多个组件有公共逻辑,如何抽离

  • 高阶组件 HOC
  • Render Props

11.react-router 如何配置懒加载

上文中有...

12.PureComponent 有何区别

  • 实现了浅比拟的 shouldComponentUpdate
  • 优化性能
  • 但要联合不可变值应用

13.React事件和DOM事件的区别

  • 所有事件挂载到 document 上
  • event 不是原生的,是 SyntheticEvent 合成事件对象

14.React性能优化

  • 渲染列表时加Key
  • 自定义事件、DOM事件及时销毁
  • 正当应用异步组件
  • 缩小函数 bind this 的次数
  • 正当应用 shouldComponentUpdate、PureComponent 和 memo
  • 正当应用 ImmutableJS
  • webpack层面优化
  • 前端通用是能优化,如图片懒加载
  • 应用SSR

React 和 Vue 的区别

相同点

  • 都反对组件化
  • 都是数据驱动视图
  • 都是用 vdom 操作 DOM

不同点

  • React 应用 JSX 拥抱JS,Vue应用模板拥抱 html
  • React 函数式编程,Vue申明式编程
  • React 更多须要自力更生,Vue把想要的都给你

JS 根底 - 变量类型和计算

typeof能判断哪些类型

  • 辨认所有类型
  • 辨认函数
  • 判断是否是援用类型,返回都为 object,不能再细分

2. 何时应用===何时应用==

3. 值类型和援用类型的区别

援用类型的实质是雷同的内存地址,出于性能问题思考,所以JS对象应用援用类型,为了防止这种状况所以须要深拷贝

常见值类型:undefined、String、Bool、Symbol('s')
常见援用类型:Object、Array、null(指向空地址)
非凡援用类型:function

4.变量计算

字符串拼接

5. 手写深拷贝

funciton deepClone ( obj = {}) {    // 判断是否须要深拷贝,不是对象和数组    if (typeof obj !== 'object' || obj == null) {        return obj    }    let result    // 判断是否为一个数组    if (obj instanceof Array) {        result = []    } else {        result = {}    }    // 遍历对象    for (let key in obj) {        // 保障 key 不是原型的属性        if (obj.hasOwnProperty(key)) {            // 递归【重点】            result[key] = deepClone(obj[key])        }    }    return result}

JS 根底 - 原型和原型链

JS自身是一个基于原型继承的语言,PS:class 的 extends 实质也是原型链继承

1.如何精确判断一个变量是不是数组?

a instanceof Array

2.手写一个繁难的jQuery,思考插件和扩展性

class jQuery {    constructor (selector) {        const result = document.querySelectorAll(selector)        const length = result.length        for (let i = 0; i < length; i++) {            this.[i] = result[i]        }        this.length = length    }    get (index) {        return this[index]    }    each (fn) {        for (let i = 0; i < this.length; i++) {            const elem = this[i]            fn(elem)        }    }    on (type, fn) {        return this.each(elem => {            elem.addEventListener(type, fn, false)        })    }}// 插件jQuery.prototype.dialog = function (info) {    alert(info)}// 复写,造轮子class MyJQuery extends jQuery {    constructor (selector) {        super(selector)    }    ......}

3.class 的原型实质,怎么了解?

  • 原型和原型链的图示
  • 属性和办法的执行规定

补充常识 - 定义class

// 父类class People {    constructor (old) {        this.old = old    }    eat () {        consoloe.log('eating')    }}// 继承class Student extends People {    constructor(name, number,old) {        super(old) // 变量传递给父类执行        this.name = name        this.number = number    }    sayHi () {        console.log(this.name, this.number)    }}const me = new Student('小明', 10, 20)  // 新建对象console.log(me.name)   // => 小明me.sayHi()             // => 小明 10// class 实际上是函数,可见是语法糖typeof People => 'function'typeof Student => 'function'// 隐式原型和显式原型me.__proto__ // 隐式原型           => PeopleStudent.prototype // 显式原型      => Peopleme.__proto === Student.prototype   => true 全等通过的话,就阐明援用的是同一个地址
  • 每个实例都有隐式原型__proto__
  • 每个 class 都有显式原型 prototype
  • 实例的隐式原型指向对应class的显式原型

基于原型的执行规定

  • 优先在本身属性和本身办法中查找
  • 如果找不到则主动去 __proto__ 隐式原型中查找

补充常识 - 类型判断 instanceof

instanceof 工作原理:是顺着__proto__隐式原型一层层往上找
// 依据上方定义的classme instanceof Student // trueme instanceof People  // trueme instanceof Object  // true,能够了解为 Object 是最上层的父类[] instanceof Array   // true[] instanceof Object  // true`{} instanceof Object  // true`

原型链

能够了解为,在 extend 继承时,对父类进行了一次实例化,所有领有隐式原型__proto__

// 依据上方定义的classStudent.prototype.__proto__People.prototypeconsole.log(People.prototype === Student.prototype.__proto__) ==> true

hasOwnProperty()就是继承于ObjecthasOwnProperty(functionName) => false无论继承还是本人的函数,均为falsehasOwnProperty()属性名只有是继承或者本人领有的为true

JS 根底 - 作用域和闭包

1.this 的不同利用场景,如何取值?

  • 作为一般函数
  • 应用 call apply bind 扭转 this 指向
  • 作为对象办法被调用
  • 在 class 办法中调用
  • 箭头函数,永远是取下级作用域的 this

2.手写 bind 函数

Function.prototype.bind1 = function () {    // 将参数拆解为数组    const args = Array.prototype.slice.call(arguments)    // 获取 this (数组的第一项)    const that = args.shift() // 删除并返回数组第一项    // 获取 fn1.bind(...) 中的 fn1    const self = this    // 返回一个函数    return function () {        return self.apply(that, args)    }}

3.理论开发中闭包的利用场景,举例说明

  • 暗藏数据,只提供API,如做一个简略的 cache 工具

补充常识 - 作用域和自在变量

作用域

  • 全局作用域
  • 函数作用域
  • 块级作用域(ES6新增)

自在变量

  • 一个变量在以后作用域没有定义,但被应用
  • 向下级作用域,一层一层顺次寻找,直至找到为止
  • 如果到全局作用域没找到,就会报错 xx is not defined

补充常识 - 闭包

作用域利用的非凡状况,有两种体现:

  • 函数作为参数被传递
  • 函数作为返回值
  • 函数自在变量的查找,是在函数定义的中央,向下级作用域查找,不是在执行的中央

左右两张图都将打印 100

补充常识 - this

this 在各个场景中取什么值,是在函数执行的时候确定的,不是在定义函数定义的时候决定的
  • 作为一般函数
  • 应用 call apply bind 扭转 this 指向
  • 作为对象办法被调用
  • 在 class 办法中调用
  • 箭头函数,永远是取下级作用域的 this
call 是间接执行,bind是返回一个新的函数去执行

JS 根底 - 事件

手写一个通用绑定事件

function bindEvent (elem, type, fn) {    elem.addEventListener(type, fn)}

Promise 图片懒加载

function loadImg (src) {    var promise = new Promise(function (resolve, reject) {        var img = document.createElement('img')        img.onload = function () {            resolve(img)        }        img.onerror = function () {            reject('图片加载失败')        }        img.scr = src    })    retrun promise}var result = loadImg('www.baidu.com')