ES知识点回顾
一、 类的定义
-
在ES5中定义的类,一般约定首字母需要大写,这只是一个人为的约定,在ES5中,构造函数也是可以作为普通函数去进行调用的
-
const p = new Person此时直接打印p会输出Person { ... }也就是说明,使用构造函数定义的对象,其类型可以认为就是Person类型(Person也是Object,这样的目的是为了细分类的类型,使类的类型不全是Object)
ES5中类的定义方式
function Person (name, age) {
this.name = name
this.age = age
}
Person.prototype.running = function() {
console.log(this.name, this.age, 'running')
}
const p = new Person('Klaus', 23)
p.running() // Klaus 23 running
ES6中类的定义
class Person {
constructor(name, age) {
// 变量的初始化和赋值操作
this.name = name
this.age = age
}
// 在这里定义的方法最后会被挂载到实例对象的原型上去
running() {
console.log(this.name, this.age, 'running')
}
}
const p = new Person('Klaus', 23)
p.running() // Klaus 23 running
二, this的指向
ES5中this的指向
function Person (name, age) {
this.name = name
this.age = age
}
Person.prototype.running = function() {
console.log(this)
console.log(this.name, this.age, 'running')
}
const p = new Person('Klaus', 23)
const fun = p.running
fun() // 此时this指向的是window对象
ES6中this的指向
class Person {
// constructor方法会在使用new操作符实例化一个对象的时候被调用
// 其的职责主要是进行变量(属性)的初始化
// 是不可以人为手动去调用constructor方法
constructor(name, age) {
this.name = name
this.age = age
}
running() {
console.log(this)
console.log(this.name, this.age, 'running')
}
}
// 使用Person来创建(声明)一个类
const p = new Person('Klaus', 23)
let fun = p.running
// 因为在es6中定义类的时候,内部默认使用的是严格模式
// 在严格模式中 this是不可以指向window对象的
// 所以此时直接调用fun的时候,this的值就是undefined
fun()
// 显示绑定this的值
// apply/call
// apply 和 call的区别在于
// apply在传递参数的时候 使用的是数组
// call在传递参数的时候,使用的是可变参数
fun.call(p)
// bind
fun = fun.bind(p)
fun()
三、继承
// 定义父类(超类)
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
// 这个方法会被定义在Person的实例的原型上,所以也就可以被Person的子类所访问到
// 可以将子类中共有的属性和方法定义到父类中
running() {
console.log('running')
}
}
class Student extends Person{
constructor(name, age, sno) {
// super方法调用父类的constructor
// 以便于对父类进行初始化操作
// 注意: 如果没有自定义子类的构造函数的时候,其是有一个默认的构造函数
// 这个默认的构造函数中是会调用super方法,以便于将子类的原型修改为父类的实例对象
// 但是自定义了constructor后,其就不会去调用默认的constructor了
// 所以在自定义的构造器中必须显示的调用super方法,如果可以,可以往其中进行赋值以传递给父类
// super函数在调用的时候,会将子类的this所指向的对象的原型修改为父类的实例对象
// 所以必须在子类构造器中使用this之前的位置,调用super进行初始化操作
// 否则是会报错的
super(name, age)
// 在这里可以定义子类特有的属性和方法
this.sno = sno
}
}
const stu = new Student('Klaus', 23, 1810166)
console.log(stu.name, stu.age, stu.sno) // 'Klaus', 23, 1810166
stu.running() // running
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
}
class Student extends Person{
constructor(name, age, sno) {
super(name, age)
this.sno = sno
}
}
const stu = new Student('Klaus', 23, 1810166)
// 在子类的实例对象中,也是可以访问到直接在父类中定义的变量的
console.log(stu.demo) // demo
React 小案例
列表渲染
需求:
想法1 --- 直接输出
class App extends React.Component {
constructor() {
super()
this.state = {
persons: ['Kobe', 'Steven', 'Alice', 'Jhon']
}
}
// 每一个组件必须有一个render函数
// 以获取该对象导出的JSX
render() {
return (
<div>
{ this.state.persons }
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
结果:

在
jsx中使用数组,其自动对数组进行了遍历,所以可以将数组中的每一项都给输出出来但是因为没有
li标签的包裹,所有的默认外部都包裹了span标签,所以显示的时候,全部都显示在了一行
想法2 --- 构造li标签包裹
class App extends React.Component {
constructor() {
super()
this.state = {
persons: ['Kobe', 'Steven', 'Alice', 'Jhon']
}
}
render() {
// 可以在render函数return之前进行逻辑运算后,再返回组件界面结构
const arr = []
// 对数组中的元素进行封装,使数组中的每一个元素的外部都包裹上li标签
for(let person of this.state.persons) {
// arr.push方法会修改原数组中的值
arr.push(<li>{ person }</li>)
}
// 返回最终的界面布局
return (
<div>
<h2>人物列表</h2>
{/*
在arr中已经为数组中的每一个元素外部包裹一层li标签了
*/}
{ arr }
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
结果:

可以看出虽然实现了最终的效果,
但是render函数在return之前,还需要进行逻辑操作
这导致界面和逻辑分离,可以进行进一步的改进
方式3 --- 使用map函数
class App extends React.Component {
constructor() {
super()
this.state = {
persons: ['Kobe', 'Steven', 'Alice', 'Jhon']
}
}
render() {
return (
<div>
<h2>人物列表</h2>
{
{/*
map会将数组中的每一个元素都执行对应的callback
其每一个元素执行callback后返回的元素,
会组合成一个新的数组后返回(也就是进行一个映射操作)
*/}
this.state.persons.map(person => {
return <li>{ person }</li>
})
}
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
注意点:
this.state.persons.map(person => { {/* 这里需要返回的是jsx 不可以使用 return `<li>${ person }</li>` 这里的话返回的就是字符串,babel会将其当做字符串来进行处理的 不会将其解析为VDOM对象,最终li会被输出在界面上,结果见图1 */} return <li>{ person }</li> })图1
计数器
需求: 
class App extends React.Component {
constructor() {
super()
this.state = {
count: 0
}
}
increment() {
this.setState({
count: this.state.count + 1
})
}
decrement() {
// setState方法是从父类中继承下来的方法
this.setState({
count: this.state.count - 1
})
}
render() {
return (
<div>
<h3>{ this.state.count }</h3>
{/*
在react中,方法中默认this是undefined,
所以需要使用bind来显示修改方法调用时候的this指向
*/}
<button onClick={ this.increment.bind(this) }>+</button>
<button onClick={ this.decrement.bind(this) }>-</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
在
React中组件的方法中的this默认是undefined,原因是
onClick={ this.increment }中,将this.increment的地址传递给了onClick方法而
React内部在调用increment方法的时候,其执行方式是increment.call(undefined)所以在
React中的方法的this默认指向是undefined所以在调用方法的时候可以使用
bind来显示绑定this
