面向对象简单易懂小知识

32 阅读11分钟

* 认识面向对象

         * 
         *      不是一个新语法, 只是一个编程方式/思想
         * 
         *      面向过程:   脸 朝着 过程; 重心在过程
         *      面向对象:   脸 朝着 对象; 重心在对象
         * 
         * 
         *      比如我们现在要吃面:
         *          面向过程:
         *              1. 准备面粉
         *              2. 准备水
         *              3. 和面
         *              4. 切面
         *              5. 煮面
         *              6. 吃面
         *          面向对象:
         *              1. 找一个面馆
         *              2. 激情下单
         *              3. 吃面
         * 
         *          面向对象的重点是有一个 '面馆', 所以我们需要寻找一个面馆, 如果没有 自己创造一个
         * 
         * 
         *      如果我们现在需要书写一个轮播图
         *          面向过程:
         *              第一个轮播图
         *                  1. const banner = XXX
         *                  2. const imgBox = XXX
         *                  3. let index = XXX
         *                  4. let timeId = XXX
         *                  5. const foucs = XXX
         *                  6. function copyEl () { 操作 imgBox 相关的内容 }
         *                  7. function setFoucs () { 操作 foucs 相关的内容 }
         *              第二个轮播图
         *                  1. const banner2 = XXX
         *                  2. const imgBox2 = XXX
         *                  3. let index2 = XXX
         *                  4. let timeId2 = XXX
         *                  5. const fouc2s = XXX
         *                  6. function copyEl2 () { 操作 imgBox2 相关的内容 }
         *                  7. function setFoucs2 () { 操作 foucs2 相关的内容 }
         * 
         *          面向对象:
         *              第一个轮播图:
         *                  const obj = {
         *                      banner: XXX,
         *                      imgBox: XXX,
         *                      index: xxx,
         *                      copyEl () { 操作 this 的 imgBox 相关的内容 }
         *                  }
         * 
         *              第二个轮播图:
         *                  const obj_2 = {
         *                      banner: XXX,
         *                      imgBox: XXX,
         *                      index: xxx,
         *                      copyEl () { 操作 this 的 imgBox 相关的内容 }
         *                  }
         * 
         * 
         *          面向对象的最终核心其实是我们寻找到一个可以批量生产对象的机器, 帮助我们生产一些对象
         *          如果 JS 提供了 这个机器, 我们直接使用, 如果没有提供这个机器, 那么我们先创建一个机器, 由 机器帮助我们批量生产对象
         *
         *
         *
      
   

* 创建对象的方式

     *
     *      1. 字面量的方式
     *      2. 内置构造函数的方式
     *
     *      上述的两种方式都能够帮助我们创建对象, 但是不能帮助我们批量生产对象
     * 
     * 
     *      3. 工厂函数模式
     *          本质上就是一个函数, 函数内部帮助我们创建一个对象, 然后返回出来
     *          将来需要对象的时候, 我们直接调用函数即可
     * 
     *      4. 自定义构造函数
     *          本质上也是一个函数, 函数内部会帮助我们创建一个对象, 然后返回出来
     *          将来需要对象的时候, 我们直接调用函数即可
    */


            // 4. 自定义构造函数
            function createObj2 () {
                // 4.1 函数内部自动创建一个对象

                // 4.2 我们手动向刚才创建出来的那个对象内添加一些属性

                // 4.3 函数内部自动返回刚才创建的对象
            }









            // 3. 工厂函数
            function createObj (name, age) {
                // 3.1 手动创建一个对象
                const obj = {}

                // 3.2 手动向对象内部添加一些属性
                obj.name = name
                obj.age = age

                // 3.3  手动返回一个对象
                return obj
            }

            const res1 = createObj('QF001', 18)
            console.log(res1)

            const res2 = createObj('QF002', 28)
            console.log(res2)
            
            
            
            
            
            
            
            
         

* 自定义构造函数的书写

     * 
     *      1. 构造函数的函数名 建议首字母大写 (为了和普通函数做一个区分)
     *      2. 构造函数将来在调用的时候, 必须和 new 关键字 一起使用 (必须这样写)
     *      3. 构造函数内部不需要书写 return
     *              如果 手动返回了 基本数据类型, 那么写了和没写一样
     *              如果 手动返回了 引用数据类型, 那么写了之后构造函数失效
     *      4. 构造函数在使用的时候 内部 的 this 规则和之前的函数不太一样       (重点!!!)
     *              因为一个函数要想当成构造函数去使用, 那么必须要和 new 关键字一起使用
     *              如果一个函数和 new 关键字一起使用, 那么内部的 this 就指向函数中被创建出来的对象
     * 
     *              构造函数内部的 this 指向于 函数内部自动创建出来的那个对象
     * 
     *      5. 构造函数不能使用箭头函数, 因为箭头函数内部没有 this
    


                function Person () {
                    this.name = 'QF001'
                    this.abc = 10086

                    // return '我是构造函数内部的返回值'        // 当前返回的是 基本数据类型, 那么是没有意义的
                    // return [1, 2, 3]                     // 当前返回的是 引用数据类型, 会导致我们的构造函数失效, 不能帮我们创建对象了
                }

                const res = new Person()
                console.log(res)
                
                
                
                
                
                
                
                
                
                
                

构造涵数的缺点

   function Person(name, age) {
        this.name = name
        this.age = age
        this.sayHi = function () {
            console.log('你好')
        }
    }

    /**
     *  我们如果要调用 Person 并当成一个 构造函数使用的话, 必须和 new 关键字结合
     * 
     *      我们将 new 关键字于 Person 结合的时候 叫做 实例化
     * 
     *      我们调用完毕后 会得到一个对象, 然后这个对象也有一个名字, 叫做 : 实例化对象
    */

    const p1 = new Person('QF001', 18)
    /**
     *  当前会调用 Person 构造函数, 内部会自动创建一个对象
     *  对象内部有 name 值是形参, age 也是一样
     *  还有一个属性 是 sayHi 他的值是一个函数, 所以我们需要开启一个内存空间, 存储这个函数
     *      假设这个函数的地址是 XF001
    */
    // console.log(p1)

    const p2 = new Person('QF002', 28)
    /**
     *  当前会调用 Person 构造函数, 内部会自动创建一个对象
     *  对象内部有 name 值是形参, age 也是一样
     *  有一个属性 是 sayHi 他的值是一个函数, 以我们需要开启一个内存空间, 存储这个函数
     *      假设这个函数的地址是 XF002
    */
    // console.log(p2)


    // p1.sayHi()
    // p2.sayHi()


    console.log(p1.sayHi)
    console.log(p2.sayHi)

    console.log(p1.sayHi === p2.sayHi) // false
    
    
    
    
    
    
    
    
    
     
    

* 函数访问原型对象

     *      首先要明确一点, 原型是为了解决 构造函数的缺点而存在的, 就是构造函数内声明的函数提取到一个公共的地方
     * 
     *      我们JS中, 所有的函数一定有一个属性 prototype, 这个属性指向了一个对象, 我们将这个对象称之为 原型对象/原型/原型空间
     * 
     *      每一个原型对象中一定会有一个属性 constructor, 当前属性的属性值就是说当前原型对象是那个函数的
     * 
     * 
     *      注意点:
     *              我们可以通过 函数的 prototype 属性获取到这个函数的 原型对象, 获取到这个原型对象后, 我们可以向内部添加一些属性
     *              属性的值 没有任何限制, 就和我们向一个普通对象中添加内容一样, 但是我们只推荐向内部书写 函数
     * 
     *              同时我们也要明确一个点, 就是我们添加的这个数据并不是给构造函数使用, 而是给当前的构造函数的所有实例化对象去使用的
     * 
     * 
     * 
     * 
     *  对象访问自己构造函数的原型对象
     *      每一个对象中 都有一个属性 __proto__ (注意: 前后都是两个下划线, 一共是 四个)
     *      这个属性会指向自己当前构造函数的原型对象
     * 
     *      注意: 我们实例化对象可以通过 __proto__ 获取到原型对象的内容, 但是我们一般不会书写 __proto__
     *              因为这个属性可以省略, 我们可以直接拿 实例化对象去调用当前构造函数的原型对象中的内容
    */


    function Person(name, age) {
        this.name = name
        this.age = age
    }

    // console.log(Person)      // ƒ Person(name, age) { this.name = name; this.age = age }
    // console.log(Person.prototype)   // {constructor: ƒ}
    // console.log(Person.prototype.constructor)   // ƒ Person(name, age) { this.name = name; this.age = age }

    Person.prototype.qwe = 'QF001'
    Person.prototype['asd'] = 'QF002'
    Person.prototype.a = 10086
    Person.prototype.b = true
    Person.prototype.c = [1, 2, 3]
    Person.prototype.sayHi = function () {
        console.log('你好')
    }

    // console.log(Person.prototype)   // { constructor: ƒ, qwe: 'QF001', asd: 'QF002' }


    const p1 = new Person('张三', 18)
    // console.log(p1) // { name: '张三', age: 18 }

    console.log(p1.qwe)  // 'QF001'

    console.log(p1.__proto__)   // {qwe: 'QF001', asd: 'QF002', a: 10086, b: true, c: Array(3), …}
    console.log(p1.__proto__.qwe)   // 'QF001'



* 在 ES6 之前, 我们在开发的时候, 都是书写 构造函数, 但是 ES6 之后, 都改为 class 类

    // class '当前的类名' {}

    class Person {
        // 构造器, 功能和 构造函数的函数体 完全一样
        constructor (name) {
            this.name = name
            this.age = 18
        }

        // 当前位置开始, 以下全都是类似于 构造函数原型的位置


        // sayHi: function () {}    // 这样书写 有报错
              sayHi () {
                        console.log(123)
                    }
                    a () {
                        console.log('QF001')
                    }
                }

                const p1 = new Person('张三')

                console.log(p1)

                p1.a()
                p1.sayHi()

利用构造函数数组增添方法

     *  原型的作用就是为了将公共的方法提取出来只创建一次, 然后所有的实例化对象都可以使用到
     * 
     *  我们 JS 中也有很多构造函数, 比如: Object, Array, Date...
     * 
     *  需求:
     *      给数组添加一个求最大值的方法, 让我们的所有数组都可以使用这个方法
     * 
     * 
     *  所以我们根据上述的描述可以得知, 我们数组都是属于 Array 这个构造函数创建出来的
     *                                          也就是说, 我们的每一个数组, 都可以用到 Array 这个构造函数的原型对象上的方法
     * 
     *  我们JS中每一个函数都是属于 Function 这个构造函数创建出来的, 那么我们的每一个函数, 都可以使用 Function 这个构造函数上的方法
 


    // console.log(Array.prototype)
    // console.dir(Function.prototype)


                const arr = [100, 200, 300, 400, 500]
                const arr1 = [1000, 200, 300, 400, 500, 600]

                // 书写你的代码...

                Array.prototype.getMax = function () {
                    /**
                     *  JS 中 所有的 引用数据类型都可以当作一个 对象去使用
                     *  所以我们现在可已经 arr 或者 arr1 当作一个对象
                     *  调用函数的时候语法是:   arr.getMax()    arr1.getMax()
                     *  根据我们以前学过的 函数内this的指向, 现在是指向调用者
                     *  也就是指向了 arr    arr1
                    */
                    // console.log(this)
                    let max = this[0]
                    for (let i = 1; i < this.length; i++) {
                        if (this[i] > max) {
                            max = this[i]
                        }
                    }
                    // 这是原型,不是构造涵数
                    return max
                }

                const res = arr.getMax()
                console.log(res) // 500
                const res1 = arr1.getMax()
                console.log(res1) // 1000
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                

原型链

     *      万物皆对象
     *          对象的一个定义:
     *                  1. 一种数据类型: { key: value, key2: value2 }
     *                  2. 一类内容中的一个真实个体
     *          // 把这三个比作对象
     *          在 JS 中 只要是 数组[]  那么就属于 Array 这一类内容中的一个真实个体   Array 是数组的构造涵数
     *          在 JS 中 只要是 函数function  那么就属于 Function 这一类内容中的一个真实个体  Function 是涵数的构造涵数
     *          在 JS 中 只要是 对象{}  那么就属于 Object 这一类内容中的一个真实个体   Object 是对象的构造涵数  
     * 
     *      原型链
     *          是关于对象内部属性的查找规则
     * 
     *          当我们在一个对象内部查找一个属性的时候, 会现在当前对象的自身查找
     *              如果找到直接使用并停止查找, 如果没有找到, 那么会去到当前对象的 __proto__ 中继续查找
     *              如果找到直接使用并停止查找, 如果没有找到, 那么会继续去当前对象的 __proto__ 中继续查找
     *              .......
     *              一直到最顶层的对象 Object.prototype, 还是没有找到, 那么返回一个 undefined
    */


                        function Person () {
                            this.name = 100
                        }
                        Person.prototype.age = 18


                        const p1 = new Person()

                        console.log(p1.age) // 18
                        console.log(p1.age_100) // undefined
                        
                        
                        
                        

理解构造构造涵数,原型

    console.log('1. ', p.__proto__ === Person.prototype)
    console.log('2. ', Person.__proto__ === Function.prototype)
    console.log('3. ', Person.prototype.__proto__ === Object.prototype)
    console.log('4. ', Function.__proto__ === Function.prototype)
    console.log('5. ', Function.prototype.__proto__ === Object.prototype)
    console.log('6. ', Object.__proto__ === Function.prototype)
    console.log('7. ', Object.prototype.__proto__, Object.prototype.__proto__ === null)


    /**
     *  1. p.__proto__                      === ???
     *      p 其实就是一个 实例化对象, 构造函数是 Person
     *          p.__proto__ === Person.prototype
     * 
     *  2. Person.__proto__                 === ???
     *      Person 是构造函数, 本质上就是一个函数
     *      在 JS 中, 只要是一个函数, 那么就是 Function 这个构造函数的 实例化对象
     *          Person.__proto__ === Function.prototype
     * 
     *  3. Person.prototype.__proto__       === ???
     *      Person 是一个构造函数, 本质上就是一个函数
     *      Person.prototype        其实就是一个函数的原型对象      本质上就是一个对象
     *      所以 Person.prototype.__proto__ 其实就是指向了一个对象的构造函数的原型对象
     *      因为JS 中 对象的构造函数就是 Object
     *      所以我们可以得到一个等式 Person.prototype.__proto__ === Object.prototype
     * 
     *  4. Function.__proto__               === ???
     *      Function 是 JS 中内置的一个构造函数, 本质上就是一个函数
     * 
     *          所以我们现在其实就是在找一个函数的构造函数的原型对象,   因为函数的原型对象是 Function
     *              Function.__proto__ === Function.prototype
     * 
     *  5. Function.prototype.__proto__     === ???
     *      Function    内置构造函数, 其实就是一个函数
     *      Function.prototype  指向了当前构造函数的原型对象, 本质上就是一个对象
     * 
     *          所以我们现在本质上就是在找一个对象的 构造函数的原型对象是谁
     * 
     *              Function.prototype.__proto__ === Object.prototype
     * 
     *  6. Object.__proto__                 === ???
     *      Object  是 JS 中 一个内置构造函数, 其实就是一个函数
     *          现在就相当于在找一个函数的 构造函数的原型对象
     * 
     *              Object.__proto__ === Function.prototype
     * 
     *  7. Object.prototype.__proto__       === ???
     *      在 JS 中最顶层的对象就是    Object.prototype, 所以如果再向上找的话, 就找不到任何东西了
     *      然后 在 JS 中, 给我们返回了一个数据 null
     * 
     *          Object.prototype.__proto__ === null

                        
                        
                        

* 判断数据类型

     * 
     *      1. typeof
     *          缺点: 引用数据类型判断的时候不太准确
     * 
     *      2. constructor
     *          语法: 数据.constructor === 构造函数
     *              我们可以通过 对比他等于那个构造函数, 然后确认他是那个数据类型
     * 
     *          缺点: undefinednull 不能使用
     *                  constructor 其实就是一个对象内部的一个属性, 是有可能被修改的
     * 
     *      3. instanceof
     *          语法: 数据 instanceof 构造函数
     *          
     *          缺点:   undefinednull 不能使用
     *                  引用数据类型, 有可能会被识别对 对象类型
     * 
     * 
     *      4. Object.prototype.toString.call(要判断数据类型的数据)
     *          能够判断 JS 中 所有的 数据类型
    */

    console.log(Object)  // 一个内置的构造函数
    console.log(Object.prototype)    // 构造函数的原型对象
    console.log(Object.prototype.toString)   // 原型对象中的一个属性, 值是一个函数
    console.log(Object.prototype.toString.call([]))  // 调用了 函数的 call 这个方法, 修改了 前边函数内部的 this


    console.log(Object.prototype.toString.call([]))
    console.log(Object.prototype.toString.call({}))
    console.log(Object.prototype.toString.call(100))
    console.log(Object.prototype.toString.call('100'))
    console.log(Object.prototype.toString.call(undefined))
    console.log(Object.prototype.toString.call(null))
    console.log(Object.prototype.toString.call(function () { }))
    console.log(Object.prototype.toString.call(true))
    console.log(Object.prototype.toString.call(/\d/))


























    // 3. instanceof
    // const arr = []
    // console.log(arr instanceof Array)
    // console.log(arr instanceof Function)
    // console.log(arr instanceof Object)
    // const fn = function () {}
    // console.log(fn instanceof Function)
    // console.log(fn instanceof Array)
    // console.log(fn instanceof Object)
    // const un = undefined
    // const nu = null
    // console.log(un instanceof Undefined)
    // console.log(un instanceof Null)





































    // 2. constructor
    // console.log(Array)
    // console.log(Array.prototype)
    // console.log(Array.prototype.constructor)

    // Array.prototype.constructor = '一个错误的修改'

    // console.log(Array)
    // console.log(Array.prototype)
    // console.log(Array.prototype.constructor)


    // const arr = []
    // console.log(Array.prototype === arr.__proto__)
    // console.log(Array.prototype.constructor)
    // console.log(arr.__proto__.constructor === Array)
    // console.log(arr.constructor === Array)
    // console.log(arr.constructor === Function)
    // console.log(arr.constructor === Object)
    // const obj = {}
    // console.log(obj.constructor === Object)
    // console.log(obj.constructor === Array)

    // const un = undefined
    // const nu = null
    // const num = 100
    // const str = 'QF001'

    // console.log(un.constructor) // undefined 不能使用
    // console.log(nu.constructor) // null 不能使用
    // console.log(num.constructor) // 能够正常使用
    // console.log(str.constructor) // 能够正常使用



















    // 1. typeof
    // console.log(typeof('100'))
    // console.log(typeof(100))
    // console.log(typeof(null))
    // console.log(typeof({}))
    // console.log(typeof([]))
    // console.log(typeof(function () {}))