1 React定义组件

1.1 函数式组件

  • 函数式组件适用于简单组件的定义,其首字母需大写,且虚拟DOM元素必须有结束标签
  • 函数组件中的this指向为window,但babel编译后默认开启了严格模式,因此结果为undefined
  • 返回的虚拟DOM元素只能有一个根元素
  • 渲染函数式组件的过程:
    1. React解析组件标签,找到了Demo组件
    2. 发现组件式使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM。随后呈现在页面中
1
2
3
4
5
function Demo(){
	console.log(this);//undefined
	return <h1>函数式组件适用于简单组件的定义</h1>
}
ReactDOM.render(<Demo/>,document.getElementById('test'))

1.2 类式组件

  • 继承React.Component类,组件名首字母必须大写,且虚拟DOM元素必须有结束标签

  • 必须有render()方法,且render方法必须有返回值,返回的虚拟DOM元素只能有一个根元素

  • 渲染类式组件的过程

    1.React 内部会创建组件实例对象

    2.调用 render()得到虚拟 DOM, 并解析为真实 DOM

    3.插入到指定的页面元素内部

1
2
3
4
5
6
7
8
9
class MyComponent extends React.Component{
	render(){
	//render放在MyComponent的原型对象(React.Component)上,供实例使用
	//render中的this指向MyComponent实例对象(组件实例对象)
	console.log('render方法中的this指向:',this);
	return <h1>类式组件适用于复杂组件的定义</h1>
	}
}
ReactDOM.render(<MyComponent/>,document.getElementById('test'))

2 组件实例三大属性

2.1 state

2.1.1 state基本使用

state 是组件对象最重要的属性,值是对象(可以包含多个 key-value 的组合)

组件被称为"状态机",通过更新组件的 state 来更新对应的页面显示(重新渲染组件)

需求:定义一个展示天气信息的组件

  • 默认展示天气炎热 或 凉爽
  • 点击文字切换天气

代码如下:(无效)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Weather extends React.Component {
	constructor(props) {
		super(props)
		this.state = { isHot: false }//this指向当前类的实例对象
        //解决方案
        //利用bind将原型对象上的changeWeather方法的this指向强行绑定为实例对象
        //从而形成一个新函数,再将其赋值给新的变量changeWeather
        this.changeWeather = this.changeWeather.bind(this)
    }
	changeWeather() {
        //changWeather方法放在Weather的原型对象上,供实例对象使用
        //由于changeWeather是作为onClick的回调,所以是直接调用的,而不是通过实例调用的,此时this应该指向window对象
        //又因为类中的方法默认开启了局部严格模式,所以结果为undefined
        console.log(this);//undefined
    }
	render() {
		const { isHot } = this.state//this是组件实例对象
		console.log(this);
		//绑定默认事件时,要注意C大写,如onBlur;
		//事件的回调函数不需要加括号,否则react会将方法执行后的返回值(这里是undefined)赋给onClick,使得单击事件失效
		return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '寒冷'}</h1>
	}
}
ReactDOM.render(<Weather />, document.getElementById('test'))

2.1.2 注意

  • 组件中 render 方法中的 this 为组件实例对象

  • 组件自定义的方法中 this 为 undefined,如何解决?

    1.强制绑定 this: 通过函数对象的 bind()

    2.箭头函数

  • 状态数据,不能直接修改或更新,需要在方法中使用**this.setState({})**来修改,修改后代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Weather extends React.Component {

    //构造器调用几次?————1次
    constructor(props) {
        super(props)
        //初始化状态
        this.state = { isHot: false, wind: '微风' }//this指向当前类的实例对象
        //解决this指向问题
        this.changeWeather = this.changeWeather.bind(this)
    }

    //render调用几次?————1+n次 1是指初始化的那次 n是状态更新的次数
    render() {
        //读取状态,ES6的解构赋值
        const { isHot, wind } = this.state//this是组件实例对象
        return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '寒冷'}{wind}</h1>
    }

    //changeWeather调用几次?————点几次调用几次
    changeWeather() {
        console.log(this);//undefined
        //获取原来的isHot值
        const isHot = this.state.isHot
        //严重注意,state状态值不能直接更改,
        //this.state.isHot = !isHot是错误的,react是严格的单向绑定,值在这变化虽然有效,但react不认
        //状态值必须通过setState方法进行更新,且更新是一种合并状态,而不是替换,否则微风也就不会显示了
        this.setState({ isHot: !isHot })
    }
}
ReactDOM.render(<Weather />, document.getElementById('test'))

2.1.3 state的简写方式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class Weather extends React.Component {
    //初始化状态
    //利用类的特性,可以直接写赋值语句,表示在该类的每一个实例对象中添加state属性
    state = { isHot: false, wind: '微风' }
    render() {
        const { isHot, wind } = this.state
        return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '寒冷'}{wind}</h1>
    }
    //自定义方法(只能作为事件的回调,因为react只会帮我们new实例之后执行render方法):赋值语句+箭头函数
    //由于箭头函数没有自己的this,回去找外层的this,正是实例对象
    changeWeather = () => {
        const isHot = this.state.isHot
        this.setState({ isHot: !isHot })
        console.log(this);
    }
}
ReactDOM.render(<Weather/>, document.getElementById('test'))

2.2 props

2.2.1 props基本使用

每个组件对象都会有 props(properties 的简写)属性,组件标签的所有属性都保存在 props 中,可以通过标签属性从组件外向组件内传递变化的数据

注意: 组件内部不要修改 props 数据

需求:自定义用来显示一个人员信息的组件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Person extends React.Component {
    render() {
        //console.log(this);//均指向实例对象
        //this.props.name = "haha"会报错,因为props是只读的
        const { name, age, gender } = this.props
        return (
            <ul>
                <li>name:{name}</li>
                <li>age:{age + 1}</li>
                <li>gender:{gender}</li>
            </ul>
        )
    }
}
//注意传参时的age要用{}包裹起来
ReactDOM.render(<Person name='tom' age={18} gender='man' />, document.getElementById('test'))
ReactDOM.render(<Person name='gsq' age={20} gender='man' />, document.getElementById('test2'))

const personAjax = { name: 'ajax', age: 10, gender: 'man' }
//可以使用展开运算符批量传递props
ReactDOM.render(<Person {...personAjax} />, document.getElementById('test3'))
//注意,此处的{...personajax}并不是展开运算符的克隆对象用法,
//而是babel和react允许展开运算符展开对象并赋值
console.log(...personAjax);//为空,虽然允许展开,但不能让你随意操作

2.2.2 对props进行限制

需求:

  • 姓名必须指定,且为字符串类型
  • 性别为字符串类型,如果性别没有指定,默认为男
  • 年龄为字符串类型,且为数字类型,默认值为 18
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<!-- 引入propstype用来对props进行限制其实就是弹出一些警告 -->
<script src="../js/prop-types.js"></script>
...
	//对标签属性进行类型、必要性等的限制
    Person.propTypes = {//此处的p小写
        name:PropTypes.string.isRequired,//别忘逗号和P和T都大写
        gender:PropTypes.string,
        age:PropTypes.number,
        eat:PropTypes.func//如果要限制传递方法,注意方法的名字是func
    }
    //指定默认标签属性值
    Person.defaultProps={
        gender:'不男不女',
        age:0
    }

2.2.3 props的简写方式

就是将定义在类之外的限制props的语句作为静态方法,写到类中

若要给类的原型对象自身加属性,用static关键字

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Person extends React.Component {
    //对标签属性进行类型、必要性等的限制
    static propTypes = {//此处的p小写
        name: PropTypes.string.isRequired,//别忘逗号和P和T都大写
        gender: PropTypes.string,
        age: PropTypes.number,
        eat: PropTypes.func//如果要限制传递方法,注意方法的名字是func
    }
    //指定默认标签属性值
    static defaultProps = {
        gender: '不男不女',
        age: 0
    }
    render() {
        const { name, age, gender } = this.props
        return (
            <ul>
            	<li>name:{name}</li>
				<li>age:{age + 1}</li>
				<li>gender:{gender}</li>
			</ul>
		)
	}
}

2.2.4 类式组件中的构造器与props

类中的构造器可以省略

如果写了构造器,同时接受props并传递给super,那么就可以通过this.props访问实例对象的props了(几乎用不到)

1
2
3
4
5
6
7
constructor(props) {
    //构造器是否接受props,是否传递给super,取决于:
    //是否希望在构造器中通过this访问props(即访问每个实例对象的props)
    //一般能不写就不写
    super(props)
    console.log('constractor',props);
}

2.2.5 函数组件使用props

函数组件因为自身能够传参,因此可以使用props属性

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//函数组件因为自身能够传参,因此可以使用props属性
function Person(props) {
    const {name,gender,age} = props
    return (
        <ul>
            <li>姓名{name}</li>
            <li>性别{gender}</li>
            <li>年龄{age}</li>
        </ul>
    )
}
//对props进行限制,只能放到函数外面了
Person.propTypes = {//此处的p小写
    name: PropTypes.string.isRequired,//别忘逗号和P和T都大写
    gender: PropTypes.string,
    age: PropTypes.number,
    eat: PropTypes.func//如果要限制传递方法,注意方法的名字是func
}
Person.defaultProps = {
    gender: '不男不女',
    age: 0
}
ReactDOM.render(<Person age={18} gender='man' />, document.getElementById('test'))

2.3 refs

组件内的标签可以定义 ref 属性来标识自己

需求: 自定义组件

  1. 点击按钮, 提示第一个输入框中的值

  2. 当第 2 个输入框失去焦点时, 提示这个输入框中的值

2.3.1 字符串形式的ref

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class Demo extends React.Component {
    render() {
        console.log(this);
        return (
            <div>
                <input ref="inp1" type="text" />&nbsp;
                <button onClick={this.showData}>点击显示左侧内容</button>&nbsp;
                <input ref="inp2" onBlur={this.showData2} placeholder="失去焦点弹出输入内容" type="text" />
            </div>
        )
    }
    showData = () => {
        const {inp1} = this.refs
        alert(inp1.value)
    }
    showData2 = () => {
        const {inp2} = this.refs
        alert(inp2.value)
    }
}
ReactDOM.render(<Demo />, document.getElementById('test'))

2.3.2 回调函数形式的ref

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class Demo extends React.Component {
    render() {
        console.log(this);
        return (
            <div>
                {/* c表示currentNode,this表示组件实例对象,
                把ref当前所处节点(c)挂在了实例自身上,并命名为inp1 */}
                <input ref={c => this.inp1 = c} type="text" />&nbsp;
                <button onClick={this.showData}>点击显示左侧内容</button>
            </div>
        )
    }
    showData = () => {
        //const { inp1 } = this.refs不用从refs中取了,从实例自身取即可
        const { inp1 } = this
        alert(inp1.value)
    }
}
ReactDOM.render(<Demo />, document.getElementById('test'))

2.3.3 回调形式ref调用次数的问题

  • 初始化时,ref的回调会执行一次。但是当更新组件时,ref的回调会执行两次,分别返回null和currentNode,因为每次渲染时都会创建一个新的函数实例,所以React清空旧的ref并设置新的。
  • 可以定义成class的绑定函数的方法来解决,但一般就用内联就可以了,多此一举。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class Demo extends React.Component {
    state = { isHot: true }
    //不用管它 只是为了更新组件方便些
    changeWeather = () => {
        const { isHot } = this.state
        this.setState({ isHot: !isHot })
    }
    showData = () => {
        //const { inp1 } = this.refs不用从refs中取了,从实例自身取即可
        const { inp1 } = this
        alert(inp1.value)
    }
    //只是将将内联形式提取出来而已,可以解决此问题
    saveRef = (c) => {
        this.inp2 = c//将传入的c节点挂载到实例对象上,命名为inp2
        console.log("@@", c);
    }
    showData2 = () => {
        const { inp2 } = this
        alert(inp2.value)
    }
    render() {
        const { isHot } = this.state
        return (
            <div>
            <h2>今天天气很{isHot ? '炎热' : '寒冷'}</h2>
            <button onClick={this.changeWeather}>单击切换天气</button><br />
            {/*初始化时,ref的回调会执行一次
            但是当更新组件时,ref的回调会执行两次,分别返回null和currentNode,
            因为每次渲染时都会创建一个新的函数实例,所以React清空旧的ref并设置新的*/}

            {/*传统内联回调形式*/}
            <input ref={c => { this.inp1 = c, console.log("@", c); }} type="text" />&nbsp;
            <button onClick={this.showData}>点击显示左侧内容</button>

            {/*可以定义成class的绑定函数的方法来解决 但一般就用内联就可以了 多此一举*/}
            <input type="text" ref={this.saveRef} />
            <button onClick={this.showData2}>点击显示左侧内容</button>
            </div>
    	)	
    }
}
ReactDOM.render(<Demo />, document.getElementById('test'))

2.3.4 createRef

  • React.createRef()调用后,返回一个容器,该容器可以存储被ref所标识的节点
  • 一个容器只能存一个节点
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Demo extends React.Component {
    /*
    React.createRef()调用后,返回一个容器
    该容器可以存储被ref所标识的节点
    一个容器只能存一个节点
    */
    myRef1 = React.createRef()
    myRef2 = React.createRef()
    render() {
        console.log(this);
        return (
            <div>
                <input ref={this.myRef1} type="text" />&nbsp;
                <button onClick={this.showData}>点击显示左侧内容</button>
                <input ref={this.myRef2} onBlur={this.showData2} type="text"/>
            </div>
        )
    }
    showData = () => {
    	alert(this.myRef1.current.value)
    }
    showData2 = ()=>{
    	alert(this.myRef2.current.value)
    }
}
ReactDOM.render(<Demo />, document.getElementById('test'))

2.3.5 事件处理

1.通过onXxx属性指定事件处理函数(注意大小写)

  • React使用的是自定义事件,而不是原生DOM事件——为了更好的兼容性
  • React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)——为了高效率
  • 事件委托的原理是事件冒泡

2.通过event.target(发生事件的事件源)得到发生事件的DOM元素对象——不要过度使用ref

  • 当发生事件的元素正好是我们要操作的元素,就可以省略ref,例如下面的失去焦点事件
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Demo extends React.Component {
    //创建ref容器
    myRef1 = React.createRef()
    render() {
        console.log(this);
        return (
            <div>
            <input ref={this.myRef1} type="text" />&nbsp;
            <button onClick={this.showData}>点击显示左侧内容</button>
            <input onBlur={this.showData2} type="text" />
        	</div>
    	)
    }
    //展示左侧输入框的数据
    showData = () => {
        alert(this.myRef1.current.value)
    }
    //展示右侧输入框的数据
    //react在调用这个回调的同时会传入event事件对象
    showData2 = (e) => {
        alert(event.target.value)
    }
}
ReactDOM.render(<Demo />, document.getElementById('test'))

3 React收集表单数据

需求: 定义一个包含表单的组件

输入用户名密码后,点击登录提示输入信息

3.1 非受控组件

输入类型的DOM(表单),对其值现用现取(username.value)的组件,称其为非受控组件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class Login extends React.Component {
    handleSubmit = () => {
        event.preventDefault()//阻止表单的默认提交动作
        const{username,password} = this
        // alert(username)是错误的,因为挂载到实例自身的是username节点,表示的是用户名输入框这个节点
        // 所以要通过.value显示其值
        alert(`username:${username.value},password:${password.value}`)
    }
    render() {
        return (
            <form action="http://www.atguigu.com" onSubmit={this.handleSubmit}>
                用户名<input ref={c => this.username = c} type="text" name="username" />
                密码<input ref={c => this.password = c} type="password" name="password" />
                <button>登录</button>
            </form>
        )
    }
}
ReactDOM.render(<Login />, document.getElementById('test'))

3.2 受控组件

页面中所有输入类的dom,随着我们的输入,就能把数据维护到状态中,需要用时直接在状态中取出即可

即实现双向数据绑定的组件称之为受控组件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Login extends React.Component {
	//初始化状态
    state = {
        username:"",
        password:""
    }
    //保存用户名到状态中
    saveUsername = (event) => {
    	this.setState({ username: event.target.value })
    }
    //保存密码到状态中
    savePassword = (event) => {
    	this.setState({ password: event.target.value })
    }
    //表单提交的回调
    handleSubmit = () => {
    	event.preventDefault()
    	const { username, password } = this.state
    	alert(`username=${username},password=${password}`)
    }
    //页面中所有输入类的dom,随着我们的输入,就能把数据维护到状态中,需要用时直接在状态中取出即可
    //即双向数据绑定
    render() {
        return (
            <form action="http://www.atguigu.com" onSubmit={this.handleSubmit}>
                用户名:<input onChange={this.saveUsername} type="text" name="username" />
                密码:<input onChange={this.savePassword} type="password" name="password" />
                <button>登录</button>
            </form>
        )
    }
}
ReactDOM.render(<Login />, document.getElementById('test'))

4 高阶函数以及函数柯里化

上文中的方法过于冗杂,不适于输入项较多的表单

可以只写一个函数,通过表单项回调函数的参数来界定保存的数据

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Login extends React.Component {
    //初始化状态
    state = {
        username: "",
        password: ""
    }
    //保存表单数据到状态中
    saveFormData = (dataType) => {
        //当我们给onchange事件的回调函数加上括号,实际表示将其返回值作为onchange事件的回调了
        //因此我们索性将此回调函数的返回值写作一个新回调函数,作为onchange事件真正的回调函数
        return () => {
            //dataType需要加中括号,因为{}对象中左侧的默认是字符串类型,加不加引号都属于字符串
            this.setState({ [dataType]: event.target.value })
        }
    }
    //表单提交的回调
    handleSubmit = () => {
        event.preventDefault()
        const { username, password } = this.state
        alert(`username=${username},password=${password}`)
    }
    render() {
        return (
            <form action="http://www.atguigu.com" onSubmit={this.handleSubmit}>
                用户名<input onChange={this.saveFormData('username')} type="text" name="username" />
                密码<input onChange={this.saveFormData('password')} type="password" name="password" />
                <button>登录</button>
            </form>
        )
    }
}
ReactDOM.render(<Login />, document.getElementById('test'))

4.1 高阶函数与函数柯里化的定义

1.高阶函数:如果一个函数符合下面规范中的任何一个,那该函数就是高阶函数

  • 若A函数,接受的参数是一个函数,那么A就称为高阶函数
  • 若A函数,调用的返回值依然是一个函数,那么A就称为高阶函数
  • 常见的高阶函数有:Promise、setTimeout、arr.map

2.函数的柯里化:通过函数调用继续返回函数的方式,实现多次接受参数最后统一处理的函数编码形式

4.2 不使用柯里化的写法

直接将返回值中的回调函数写成内联的形式,并传出event对象.

柯里化的核心点在于,事件的回调必须是一个函数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Login extends React.Component {
    //初始化状态
    state = {
        username: "",
        password: ""
    }
    //保存表单数据到状态中
    saveFormData = (dataType) => {
        //当我们给onchange事件的回调函数加上括号,实际表示将其返回值作为onchange事件的回调了
        //因此我们索性将此回调函数的返回值写作一个新回调函数,作为onchange事件真正的回调函数
        return () => {
            //dataType需要加中括号,因为{}对象中左侧的默认是字符串类型,加不加引号都属于字符串
            this.setState({ [dataType]: event.target.value })
        }
    }
    //表单提交的回调
    handleSubmit = () => {
        event.preventDefault()
        const { username, password } = this.state
        alert(`username=${username},password=${password}`)
    }
    render() {
        return (
            <form action="http://www.atguigu.com" onSubmit={this.handleSubmit}>
                用户名<input onChange={this.saveFormData('username')} type="text" name="username" />
                密码<input onChange={this.saveFormData('password')} type="password" name="password" />
                <button>登录</button>
            </form>
        )
    }
}
ReactDOM.render(<Login />, document.getElementById('test'))

补充:

1 类的基本知识

  • 类中的构造器不是必须写的,如果要对实例进行一些初始化的操作,如添加指定属性时再写
  • 若A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须调用的
  • 类中所定义的方法,都是放在类的原型对象上,供实例去使用
  • 类中允许直接写赋值语句
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//创建一个Person类
class Person {
    //构造器方法
    constructor(name, age) {
        //构造器中的this指向实例对象,因为在new的时候实际上是实例对象调用的构造器方法
        this.name = name
        this.age = age
    }
    //一般方法
    speak() {
        console.log(`我叫${this.name},我今年${this.age}岁了`);
        //speak方法写在类的的原型对象上,供实例使用
        //实例在调用自身不存在的方法时,会自动去原型链上找
        //通过Person的实例调用speak方法时,speak中的this就是person实例
        //具体情况需要看是谁调用的,call bind apply都可以改变this的指向
    }
}
//创建一个Person的实例对象
const p1 = new Person('gsq', 25)
const p2 = new Person('zs', 26)
console.log(p1);
console.log(p2);
p1.speak()
p2.speak()

//创建一个Student类,继承于Person类
class Student extends Person {
	constructor(name, age, grade) {
		super(name, age)//必须调用super,且必须写在第一句
		this.grade = grade
	}
	//方法的重写
	speak() {
		console.log(`我叫${this.name},我今年${this.age}岁,今年上${this.grade}`);
	}
	study() {
		console.log("正在学习");
	}
}
const s1 = new Student('abc', 26, "研三")
s1.speak()
s1.study()
/*
总结:
1.类中的构造器不是必须写的,如果要对实例进行一些初始化的操作,如添加指定属性时再写
2.若A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须调用的
3.类中所定义的方法,都是放在类的原型对象上,供实例去使用
*/

//另,类中允许直接写赋值语句,应用如下
class Car {
    constructor(name, price) {
    this.name = name
    this.price = price
	}
	a = 1
	wheels = 4//类中允许直接写赋值语句,此属性会给每个实例对象
	//若要给类的原型对象自身加属性,用static关键字
	static b = 3
	fn1() {
	//这是放在类的原型对象上的方法
	}
	fn2 = () => {
	//这是放在类的实例对象上的方法
	}
}
const c1 = new Car('benz', 10000)
console.log(c1);//可以利用此特性,简化react中state的编写
console.log(Car.b);

2 原生事件绑定

  1. addEventListener(事件监听器)
  2. onclick(js动态绑定)
  3. 直接在标签上绑定(行内绑定)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<button id="btn1">按钮1</button>
<button id="btn2">按钮2</button>
<button onclick="demo()">按钮3</button>
<script>
    //方式1,addEventListener(事件监听器)
    const btn1 = document.getElementById('btn1')
	btn1.addEventListener('click',()=>{
    	alert('按钮1被点击了')
	})
	//方式2,onclick(js动态绑定)
	const btn2 = document.getElementById('btn2')
	btn2.onclick = ()=>{
    	alert('按钮2被点击了')
	}
	//方式3,直接在标签上绑定(行内绑定)
	function demo(){
    	alert('按钮3被点击了')
	}
	//react推荐使用方法3
</script>

3 类中方法的this指向

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Student{
    constructor(name,age){
        this.name = name
        this.age = age
	}
    study(){//study方法在Student类的原型对象上,供实例对象使用
    	console.log(this);//这里的this指向Student实例对象
    }
}

const s1 = new Student('gsq',18)
s1.study()//通过实例调用原型对象上的study方法

const x = s1.study//将study方法赋值给了x
x()//undefined,因为这属于直接调用study方法,由于类中默认开启严格模式,所以是undefined(不然应该是window对象)

//关于局部开启严格模式
function demo1(){
	console.log(this);//this指向window对象
}
demo1()

function demo2(){
	'use strict'//局部开启严格模式
	console.log(this);//此时是undefined,因为严格模型不让this指向window
}
demo2()

4 展开运算符的用法

  1. 展开一个数组
  2. 数组的拼接
  3. 函数传参(批量传入参数)
  4. 复制对象(深复制)
  5. 复制一个对象的同时修改器属性
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
let arr1 = [1,3,5,7,9]    
let arr2 = [2,4,6,8,10]    

//1.展开一个数组
console.log(arr1)//[1,3,5,7,9]
console.log(...arr1)//1 3 5 7 9

//2.数组的拼接
console.log(...[...arr1,...arr2])//展开拼接后的数组1 3 5 7 9 2 4 6 8 10

//3.函数传参,批量传入参数
//需求:数组求和
function sum(...numbers) {
	return numbers.reduce((preValue,currentValue) => {//这是数组中非常重要的一个方法reduce
		return preValue + currentValue
	})
}
console.log(sum(1,2,3,4));//10

//4.复制对象
let p1 = {name:'gsq',age:10}
//console.log(...p1);//报错,因为在原生js中,展开运算符不可以展开对象
//但如果在外面包上一层展开运算符,就可以克隆一个对象(深复制),
let p2 = {...p1}
p1.name = 'zs'//更改p1的值,p2并不会受影响
console.log(p2,p1);

//5.复制一个对象的同时修改其属性
//实质上是两个对象的合并
let p3 = {...p1,name:'gsqzs'}
console.log(p3);

5 对象相关知识

1
2
3
4
5
let a = 'name'
const obj = {} //需求是将其变成{name:'tom'}
// 不可直接obj.a = "tom"
obj[a] = "tom"
console.log(obj)

6 显示函数柯里化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
//正常写法
function sum(a, b, c) {
	return a + b + c
}
console.log(sum(1, 2, 3));

//函数柯里化写法
function sum2(a) {
    return (b) => {
        return (c) => {
        	return a + b + c
        }
    }
}
console.log(sum2(1)(2)(3));