性能优化
性能优化,永远是面试的重点,性能优化对于 React 更加重要
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
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 组件如何通讯
父子组件通过 属性 和 props 通讯
通过 context 通讯
通过 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 的自带函数,需要重定向 this
bindClcik1 () { 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)
}
复制代码
表单
<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()
不可变值
可能是异步更新
可能会被合并
// 错误的写法
this.setState({
count: this.state.count + 1
})
// 正确的写法
const count = this.state.count + 1
this.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 + 1
this.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 = 0
this.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 = 0
this.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 初始化
Mounting 挂载
componentWillMount()
和 componentDidMount()
,只会在页面第一次挂载的时候执行,state 变化时,不会重新执行
Updation 组件更新
shouldComponentUpdate()
: 该生命周期要求返回一个bool
类型的结果,如果返回true
组件正常更新,如果返回false
组件将不会更新;
componentWillUpdate()
: 组件被更新之前执行,如果shouldComponentUpdate()
返回false
,将不会被被执行;
componentDidUpdate()
: 组件更新完成之后执行;
componentWillReceiveProps()
: props 独有的生命周期,执行条件如下:
组件要从父组件接收参数;
只要父组件的render()
被执行了,子组件的该生命周期就会执行;
如果这个组件第一次存在于父组件中,不会执行;
如果这个组件之前已经存在于父组件中,才会执行;
Unmounting 组件卸载
生命周期简单使用场景
使用shouldComponentUpdate()
防止页面进行不必要的渲染
# 用生命周期进行性能优化
shouldComponentUpdate () {
if (nextProps.content !== this.props.content) {
return true;
}
return false;
}
复制代码
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 上,有更好的浏览器兼容。
常见使用场景:
父组件 overflow: hidden , 但是子组件又想展示;
父组件的 z-index 太小;
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 单项数据流
dispatch(action)
reducer 产生 newState
subscribe 触发通知
Redux 单项数据流图
React-router
路由模式
后者需要 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>
)}
复制代码
路由跳转
路由配置懒加载
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 模拟 DOM 结构
使用 snabbdom 操作虚拟 dom
Diff 算法
优化前 树 diff 的时间复杂度 (n^3)
遍历 Tree1,遍历 Tree2
排序
假设有 1000 个节点,就要计算 1 亿次,算法不可用
优化后时间复杂度 (n^1)
只比较同一层级,不跨级比较
tag 不相同,则直接删掉重建,不再深度比较
tag 和 key,两者都相同,则认为是相同节点,不再深度比较
React 原理
数据驱动视图(MVVM, 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)
复制代码
事件合成机制
为什么要合成事件机制
setState 和 batchUpdate(批处理)
setState
核心要点
setState 主流程
batchUpdate 机制
transaction(事务)机制
看this.setState()
是否是异步,看 isBatchingUpdates 的状态,为 true 就是异步,为 false 就是同步
哪些能命中 batchUpdate 机制
生命周期(和它调用的函数)
React 中注册的事件(和它调用的函数)
React 可以“管理”的入口
哪些不能命中 batchUpdate 机制
transaction 事务机制
常见基础面试题
1.组件之间如何通讯
2.JSX 本质
JSX => React.createElement() => 虚拟 DOM (JS 对象) => 真实 DOM
3.shouldComponentUpdate 用途
4.redux 单项数据流
Redux 单项数据流图
5.setState 场景题
6.什么是纯函数
7.列表渲染为何要用 key
8.函数组件 和 class 组件区别
纯函数,输入 props,输出 JSX
没有实力,没有生命周期,没有 state
不能扩展其它方法
9.如何使用异步组件
加载大组件
React.lazy
React.Suspense
10.多个组件有公共逻辑,如何抽离
11.react-router 如何配置懒加载
上文中有...
12.PureComponent 有何区别
13.React 事件和 DOM 事件的区别
14.React 性能优化
React 和 Vue 的区别
相同点
都支持组件化
都是数据驱动视图
都是用 vdom 操作 DOM
不同点
JS 基础 - 变量类型和计算
typeof
能判断哪些类型
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.如何准确判断一个变量是不是数组?
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__ // 隐式原型 => People
Student.prototype // 显式原型 => People
me.__proto === Student.prototype => true 全等通过的话,就说明引用的是同一个地址
复制代码
基于原型的执行规则
补充知识 - 类型判断 instanceof
instanceof 工作原理:是顺着__proto__
隐式原型一层层往上找
// 根据上方定义的class
me instanceof Student // true
me instanceof People // true
me instanceof Object // true,可以理解为 Object 是最上层的父类
[] instanceof Array // true
[] instanceof Object // true`
{} instanceof Object // true`
复制代码
原型链
可以理解为,在 extend 继承时,对父类进行了一次实例化,所有拥有隐式原型__proto__
// 根据上方定义的class
Student.prototype.__proto__
People.prototype
console.log(People.prototype === Student.prototype.__proto__) ==> true
复制代码
hasOwnProperty()
就是继承于Object
,hasOwnProperty(functionName) => false
无论继承还是自己的函数,均为false
,hasOwnProperty()
属性名只要是继承或者自己拥有的为true
JS 基础 - 作用域和闭包
1.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.实际开发中闭包的应用场景,举例说明
补充知识 - 作用域和自由变量
作用域
自由变量
补充知识 - 闭包
作用域应用的特殊情况,有两种表现:
左右两张图都将打印 100
补充知识 - this
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')
复制代码
评论