1 React定义组件
1.1 函数式组件
- 函数式组件适用于简单组件的定义,其首字母需大写,且虚拟DOM元素必须有结束标签
- 函数组件中的this指向为window,但babel编译后默认开启了严格模式,因此结果为undefined
- 返回的虚拟DOM元素只能有一个根元素
- 渲染函数式组件的过程:
- React解析组件标签,找到了Demo组件
- 发现组件式使用函数定义的,随后调用该函数,将返回的虚拟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 属性来标识自己
需求: 自定义组件
-
点击按钮, 提示第一个输入框中的值
-
当第 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" />
<button onClick={this.showData}>点击显示左侧内容</button>
<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" />
<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" />
<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" />
<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" />
<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 原生事件绑定
- addEventListener(事件监听器)
- onclick(js动态绑定)
- 直接在标签上绑定(行内绑定)
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
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));
|