React学习笔记 --- React初体验

356 阅读5分钟

ES知识点回顾

一、 类的定义

  1. 在ES5中定义的类,一般约定首字母需要大写,这只是一个人为的约定,在ES5中,构造函数也是可以作为普通函数去进行调用的

  2. 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 小案例

列表渲染

需求: dEh0PS.png

想法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'))

结果:

dEfQ6s.png

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'))

结果:

dEh0PS.png

可以看出虽然实现了最终的效果,

但是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

dEbW36.png

计数器

需求: dV1GND.gif

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

上一篇 React学习笔记 --- 邂逅React 下一篇 React学习笔记 --- JSX核心语法(上)