date1114面向对象(完整详细版)

79 阅读11分钟

面向对象

一、认识面向对象

概念:首先要确定 这个面向对象不是让你看着你对象 也不是一个语法 他其实是一种编程思想。

功能:去吃一碗面

面向过程的思想(自己做饭):
    1. 和面
    2. 切面
    3. 烧水
    4. 煮面
    5. 炒个菜
    6. 吃面
    
面向对象的思想(外面吃或者点外卖):
    1.找个面馆
    2.下单
    3.吃面

eg:之前所学的轮播图、分页、瀑布流都属于面向过程 里面的代码之间都具有一定的关联性

面向对象:找一个"面馆" 他能生产一碗面,然后我们通过 这个"面馆"得到一碗面,如果有这个面馆 那我们直接使用 如果没有 我就自己开一家面馆 下次要吃面的话 就直接下单就好了。

找一个'机器'这个机器能生产产品 这个产品就是轮播图。

如果有这个机器 我们直接通过这个机器去得到轮播图 如果没有这个机器 我们需要自己创建一个机器

面向过程 => 脸朝着过程;编程时注重过程

一切功能都是一步一步来的

面向对象 => 脸朝着

轮播图: 面向过程的思想

创建第一个轮播图:

  1. const banner = XXX
  2. const imgBox =xXX
  3. const focus =XXX
  4. const index = xxx
  5. function copyELe {操作imgBox}
  6. function setFocus o{操作focus)
  7. function autoPLay o){操作imgBox和index}

创建第二个轮播图:

  1. const banner2 = XXX
  2. const imgBox2 =XXX
  3. const focus2 =XXX
  4. const index2 = XXX
  5. function copyELe2o{操作imgBox2}
  6. function setFocus2){操作focus2}
  7. function autoPLay2){操作imgBox2和index2]

面向对象的思想

b1 = { banner: xxx,

imgBox = xxx,

focus = xxx,

index = xxx,

copyEle () {操作 this 的 imgBox},

setFocus () {操作 this 的 focus},

autoPlay () {操作 this 的 imgBox 和 this 的 index}

}

b2 = { banner: yyy,

imgBox = yyy,

focus = yyy,

index = yyy,

copyEle () {操作 this 的 imgBox},

setFocus () {操作 this 的 focus},

autoPlay () {操作 this 的 imgBox 和 this 的 index}

}

对象的 属性值不相同, 但是 方法 相似但不同

机器的核心 能够批量生产

今天学的目标,

  •      找到一个能批量生产对象的机器
    
  •      并且是合理生产
    
const obj = {
            a: 1,
            b: 2,
            fn() {
                console.log(this.a)
            }
        }
        obj.fn()

二、工厂模式

  1. 字面量方式

    • 概念:不可以批量创建,所以不行。
  2. 内置构造函数

    • 不能批量创建,所以不行。
  3. 工厂函数方式创建对象

    • 工厂函数,其实就是一个函数。
    • 步骤:第一步,手动给对象添加对象;第二步:手动返回一个对象;第三步:手动返回这个对象。
    • 这个方法能能够创建对象,并且批量生产也可以。
  4. 自定义构造函数

    • 自定义构造函数, 本质是就是一个函数
    • 当他和 new 关键字连用的时候, 就叫做构造函数
    • 步骤:第一步:自动创建一个对象;第二步:手动向创建的对象中添加属性;第三步:自动返回一个对象。

1. 字面量方式

        // 同样的属性属性值 多次调用 多次赋值 多次创建对象
        const obj11 = {
            name:'Kack',
            sex:'男',
            age:18,
        }
        console.log(obj11);

        const obj12 = {
            name:'Kack',
            sex:'男',
            age:18,
        }
        console.log(obj12);

        const obj13 = {
            name:'Kack',
            sex:'男',
            age:18,
        }
        console.log(obj13);

01.png 2. 内置构造函数

        // 同样的属性属性值 多次调用 多次赋值 多次创建对象
        const obj21 = new Object();
        obj21.name = 'Kack';
        obj21.sex = '男';
        obj21.age = 18;
        console.log(obj21);

        const obj22 = new Object();
        obj22.name = 'Kack';
        obj22.sex = '男';
        obj22.age = 18;
        console.log(obj22);

        const obj23 = new Object();
        obj23.name = 'Kack';
        obj23.sex = '男';
        obj23.age = 18;
        console.log(obj23);

02.png 3. 工厂函数方式创建对象

        // 工厂模式创建对象
        function createObj(name , sex , age){
            // 1.手动创建一个一个空对象
            const obj3 = {};

            // 2.向空对象添加属性
            // 由于对象里面的属性此时是固定的 我们可以将键名 键值 转化为变量的形式存储在形参中
            obj3.name = name;
            obj3.sex = sex;
            obj3.age = age;

            // 3.返回对象
            return obj3;
        }
        // 4.调用函数  多次赋值 多次调用
        console.log(createObj('name' , 'Kack' , 18));
        console.log(createObj('name' , 'Jack' , 28));
        console.log(createObj('name' , 'Sack' , 38));

02.png 4. 自定义构造函数

三、自定义构造函数

自定义构造函数的书写:

  1. 一定是和 new 关键字 连用,如果没有和 new 连用, 那么他就是一个普通函数;
  2. 当一个函数和 new 关键字连用的时候, 这个函数就被称为自定义构造函数, 这个函数内的 this 指向, 指向返回出来对象;
  3. 构造函数不能使用给箭头函数,因为箭头函数内部没有 this;
  4. 构造函数内部不需要 return;
    • return 了基本数据类型, 写了和没写一样
    • return 了引用数据类型, 写了构造函数就没用了

5.书写构造函数时, 首字母建议大写,目的: 仅仅是为了和普通函数区分;

  1. 我们构造函数 通过 new 关键字 创建出来的 对象, 叫做 实例化对象, 本质上还是一个对象, 只不过名字上叫做实例化对象(实例对象),我们把 构造函数通过 new 关键字 创建对象 的过程 叫做 实例化。
            function CreateObj(name , sex , age){
            // this指向的是CreateObj
            // this.name = 'Jack';
            // this.sex = '男';
            // this.age = '女';

            this.name = name;
            this.sex = sex;
            this.age = age;
        }
        // 通过new关键字创建一个实例化对象 
        // 实例化对象中的this指向构造函数
        const oO1 = new CreateObj('Jack' , '男' , 18);
        const oO2 = new CreateObj('Wack' , '男' , 22);
        console.log(oO1);
        console.log(oO2);

03.png

四、认识构造函数不合理的地方

        /*
            认识构造函数不合理的地方
        */ 
        function fn(){
                console.log(11111);
            }

        function CreateObj(name , age , sex){
            this.name = name; 
            this.age = age;
            this.sex = sex;

             /*
               这样写 实际功能也能完成, 并且在多次创建的时候
               每次给 this.fn 赋值时, 都会去找到 fn 函数           
               然后多个对象的 this.fn 的引用地址 都是一个
            */
            // this.fn = fn;


            /*
                这样写 实际功能也能完成, 但是多次创建对象时
                会多次创建 功能代码完全相同一个函数
                这对内存空间是一种浪费
            */
            this.fn = function(){
                console.log(999);
            }
        }
        const newObj = new CreateObj('Jack' , 18 , '男');
        console.log(newObj);

五、原型

  1. 每一个函数都有一个 原型(是一个空间,或者是一个对象, 内部能存储一些东西);
    • 构造函数, 本质上也是一个函数, 所以他也有这个原型; *
    • 原型内部都有一个 constructor ,这个属性表明当前这个原型, 是那个函数的 *
    • 函数访问原型; 函数.prototype *
  2. 每一个对象都有一个 proto(两个下划线), 可以去访问到自己构造函数的原型;
    • 实例化对象, 本质上也是一个函数, 所以他可以访问到自己构造函数的原型
    • 对象访问原型: 对象.proto
    • 对象的访问规则, 现在当前作用域(对象内部)查找, 找到就使用
    • 如果没找到, 则会顺着 proto 向上查找

构造函数函数体内, 通常写属性

造函数的原型内, 通常写方法(函数)

构造函数的原型内部添加方法, 并不是为了给构造函数使用

通常是为了给实例化对象使用

        function Person(name, age) {
            this.name = name
            this.age = age
        }
        Person.prototype.a = '我是后续通过 JS 代码添加到函数的原型内部的属性'
        Person.prototype.sayHi = () => {
            console.log('你好')
        }
 
 
        // console.log(Person.prototype)   // 打印函数 Person 的原型
 
 
        const p1 = new Person('QF001', 18)
        const p2 = new Person('QF002', 28)
         
        p1.sayHi()
        console.log(p1.__proto__)
        console.log(p1.a)
 
        console.log(p1.__proto__ === Person.prototype)   // 打印对象的构造函数的原型
         
        console.log(p1.sayHi)
        console.log(p2.sayHi)
        console.log(p1.sayHi == p2.sayHi)

每一个函数 都有一个属于自己的原型

作用:
- 我们会在原型内部放一些公共的方法, 目的不是为了让构造函数去使用, 而是为了让实例化对象去使用
        // function Person(name) {
        //     this.name = name
        // }
        // Person.prototype.sayHi = () => {
        //     console.log(111)
        // }
        // const p = new Person('QF666')
        // p.sayHi()

        // const arr = new Array()
        // const obj = new Object()
        // const reg = new RegExp()
        // const date = new Date()

        // console.log(arr)
        // console.log(obj)
        // console.log(reg)
        // console.log(date)

        // 找到数组构造函数的原型
        // const arr = new Array()
        // console.log(arr.__proto__)
        // console.log(Array.prototype)

        // // 找到对象构造函数的原型
        // console.log(Object.prototype)


        /**
         *  需求: 给数组扩展一个方法 abc 让他能够打印 123
         * 
         *      任何一个数组的构造函数 都是 Array
         *      任何一个对象的构造函数 都是 Object
         *      任何一个函数的构造函数 都是 Function
        */
        // Array.prototype.abc = () => {
        //     console.log(123)
        // }
        // const arr = new Array()
        // const arr1 = new Array()
        // arr.abc()
        // arr1.abc()

        /**
         *  需求: 给数组扩展一个 求最大值的方法
        */
        Array.prototype.getMax = function () {
            let max = this[0]
            for (let i = 1; i < this.length; i++) {
                if (max < this[i]) {
                    max = this[i]
                }
            }
            return max
        }
        const arr1 = new Array(1, 2, 100, 5, 3, 4)
        const max = arr1.getMax()
        console.log(max)


        const arr2 = new Array(1, 2, 100, 5, 3, 4, 999)
        const max2 = arr2.getMax()
        console.log(max2)

六、万物皆对象

/**
         *  JS 中, 万物都可以都可以称为对象
         *      对象: 
         *          含义1: 一种数据格式 {key: value, key2: value2}
         *          含义2: 某一类事务的实例 (某一类内容中的 真实个体)
         * 
         * 
         *      const arr = [1, 2, 3]
         *      const arr1 = [1, 2, 3, 4]
         *      const arr2 = [1, 2]
         *          这个 arr1 就是 Array 这一类内容中的 某一个 真实个体, 数组也可以算作一个对象(Array 这一类事务中的一个个体)
         * 
         *      const fn = () => {console.log(1)}
         *      const fn2 = () => {console.log(2)}
         *      const fn3 = () => {console.log(3)}
         *          函数也是一个对象(属于 Function 这一类事务中的一个个体)
         * 
         *  如果一个数据 [] 那么他就是Array 这个对象中的某一个个体
         *  如果一个数据 {} 那么他就是Object 这个对象中的某一个个体
         *  如果一个数据 function(){}   那么他就是 Function 这个对象中的某一个个体
        */

        const arr1 = [1, 2, 3]
        const arr2 = new Array(4, 5, 6)

七、原型链

        原型链
         * 
         *      查找对象的某一个属性:
         *          先在对象内部开始查找, 找到直接使用, 然后停止查找
         *          如果没有找到, 会找对象的 obj.__proto__, 如果找到直接使用, 然后停止查找
         *          如果这里没找到, 会继续去对象的 __proto__ 查找, 找到直接使用, 然后停止查找
         *          如果还是没找到, 会继续向上查找
         *          ...
         *          直到找到顶层作用对象    Object.prototype, 找到就是用, 找不到 undefined
        */
        function Person (name) {
            this.name = name
        }
        Person.prototype.sayHi = function () {
            console.log(100)
        }
        const p = new Person('QF001')
        // console.log(p)
        
        /**
         *  问题1: p 的 __proto__ 指向谁?
         *          + p 是 Person 的实例化对象
         *          + __proto__ 指向自身构造函数的原型
         *          + p.__proto === Person.prototype
         * 
         *  问题2: Person 的 __proto__ 指向谁?
         *      + Person 是构造函数, 本质上函数
         *      + 只要是一个函数, 他就是 Function 的实例
         *      + Person.__proto__ 指向了 他的构造函数的原型, 构造函数是 Function, 那么构造函数的 原型 Function.prototype
         *      + Person.__proto__ === Function.prototype
         * 
         *  问题3: Person.prototype 的 __proto__ 指向谁?
         *      + Person.prototype 其实就是  构造函数 Person 的原型对象, 本质上就是对象
         *      + 只要是一个 对象, 他就是 Object 的实例
         *      + Person.prototype.__proto__ 指向了 他的构造函数的原型, 构造函数 Object, 那么构造函数的原型 Object.prototype
         * 
         *  问题4: Function 的 __proto__ 指向谁?
         *      + Function 是构造函数, 本质上就是一个函数
         *      + 只要是一个函数, 他就是 Function 的实例
         *      + Function.__proto__ 指向了 他的构造函数的原型, 构造函数 Function, 那么 构造函数的原型 Function.prototype
         *      + Function.__proto__ === Function.prototype
         * 
         *  问题5: Function.prototype 的 __proto__ 指向了谁?
         *      + Function.prototype 其实就是 构造函数 Function 的原型对象, 本质上是对象
         *      + 只要是一个对象, 他就是 Object 的实例
         *      + Function.prototype.__proto__ 指向了 他的构造函数的原型, 构造函数 Object, 那么构造函数的原型 Object.prototype
         *      + Function.prototype.__proto__ === Object.prototype
         * 
         *  问题6: Object 的 __proto__ 指向了谁?
         *      + Object 是一个构造函数, 本质上还是一个函数
         *      + 只要是一个函数, 那么他的构造函数 就是 Function
         *      + Object.__proto__ 指向了他的构造函数的原型, 他的构造函数是 Function, 那么构造函数的原型 Function.prototype
         *      + Object.__proto__ === Function.prototype
         * 
         *  问题7: Object.prototype 的 __proto__ 指向了谁?
         *      + Object.prototype 是构造函数 Object 的原型对象, 本质上就是一个对象
         *      + 但是重点: Object.prototype 是 JS 顶层的对象
         *      + Object.prototype.__proto__ === null
        */

        console.log(p.__proto__ === Person.prototype)
        console.log(Person.__proto__ === Function.prototype)
        console.log(Person.prototype.__proto__ === Object.prototype)
        console.log(Function.__proto__ === Function.prototype)
        console.log(Function.prototype.__proto__ === Object.prototype)
        console.log(Object.__proto__ === Function.prototype)
        console.log(Object.prototype.__proto__)

八、判断数据类型

        判断数据类型
         *      1. typeof   判断基本数据类型
         * 
         *      2. constructor 可以判断 当前数据的 构造函数 是谁
         *              问题1: 能够判断引用数据类型
         *                      但是! 这个属性其实就是对象内的一个属性
         *                      我们可以拿到这个对象, 然后修改他的属性值
         *              问题2: 不能判断 undefinednull
         * 
         *      3. instanceof 可以判断 左边的构造函数是否等于右边的构造函数
         *          语法: 检测的数据 instanceof 构造函数
         *          问题: 不能判断 undefinednull
         * 
         *      4. Object.prototype.toString.call(要判断的数据结构)
         *              Object 这个构造函数的 原型内部 有一个 toString 的方法
         *              这个方法能够帮我们将数据结构转为字符串的形式    '[object 数据结构]'
         * 
         *          我们在使用的时候 如果需要判断其他数据类型, 需要使用 .call这个方法改变内部 this 指向
         * 
         *          这个方法任何数据类型都能准确判断(推荐使用)
        */

        // 1. typeof
        // console.log(typeof('123'))
        // console.log(typeof(123))
        // console.log(typeof(true))
        // console.log(typeof(undefined))
        // console.log(typeof([1, 2, 3]))
        // console.log(typeof({a:1}))

        // 2. constructor
        // const arr = [1, 2, 3]
        // console.log(arr.__proto__.constructor)  // constructor 指向当前这个原型是那个构造函数的原型
        // console.log(arr.constructor)  // constructor 指向当前这个原型是那个构造函数的原型
        // console.log(arr.constructor === Array)  // true
        // console.log(arr.constructor === Function)  // false

        // console.log(arr)    // 数组
        // console.log(arr.__proto__)    // 自己构造函数的原型对象
        // console.log(Array.prototype)    // 构造函数 Array 的原型对象
        // console.log(Array.prototype.constructor)    // 构造函数 Array 的原型对象


        // Array.prototype.constructor = { a: 1 }
        // console.log(Array.prototype.constructor)
        // console.log(arr.__proto__.constructor === Array)

        // console.log(undefined.constructor)  // undefined 内部没有 constructor 所以我么不能这样去判断
        // console.log(null.constructor)                   // null 内部 没有 constructor 所以我们不用这样去判断

        // const obj = {
        //     a: 1
        // }
        // obj.a = 100
        // console.log(obj)

        // const arr = [1, 2, 3]
        // const obj = {
        //     a: 1,
        //     b: 2
        // }
        /**
         *  instanceof
         *      判断左边的构造函数是否等于右边的
        */
        // console.log(arr instanceof Array)
        // console.log(arr instanceof Function)
        // console.log(obj instanceof Array)
        // console.log(obj instanceof Object)

        // Array.prototype.constructor = { a: 1 }
        // console.log(arr instanceof Array)

        // console.log(undefined instanceof Array)
        // console.log(undefined instanceof Object)
        // console.log(undefined instanceof Function)



        // 4. 
        const arr = [1, 2, 3]
        const obj = {
            a: 1,
            b: 2
        }
        /**
         *  Object 这个构造函数的 原型内部 有一个 toString 的方法
         *      这个方法能够帮我们将数据结构转为字符串的形式    '[object 数据结构]'
         * 
         *      我们在使用的时候 如果需要判断其他数据类型, 需要使用 .call这个方法改变内部 this 指向
         * 
         *  这个方法任何数据类型都能准确判断(推荐使用)
        */
        console.log(Object.prototype.toString.call([]))
        console.log(Object.prototype.toString.call(function () { }))
        console.log(Object.prototype.toString.call(undefined))
        console.log(Object.prototype.toString.call(null))