面向对象与原型

133 阅读7分钟

面对对象

什么是面向对象

面对对象并不是一种语法,也不是一个新的语言,它是JS完成需求的一种思想

面向对象与面向过程的区别

面向过程:

注重的是过程,每一步写的详细,一步步代码按顺序完成

存在问题:

1.每一个功能相互影响

2.轮播图举例,若有多个轮播图,则js需要书写多遍

面向对象:

注重的是得到一个对象,这个对象就是我们的需求 比如这个对象就是一个轮播图

拿生活中的例子作比较如

我想吃一碗面

面向过程的操作流程

1.准备面粉

2.准备水

3.和面

4.切面

5.烧水

6.煮面

7.吃面

而面向对象的流程是

1.找一个面馆

2.下单

3.吃面  

创造对象的方式

1.字面量方式

let obj = {
name:'qf111'
age:11
}

let obj1 = {
name:'qf222'
age:22
}

这种方式在想创建多个对象时,需要多次创造,所以这种方法不适合批量创建

内置构造函数

let obj = nwe Object({name:'qf111',age:11})

let obj1 = nwe Object({name:'qf222',age:22})

这种方式在想创建多个对象时,需要多次创造,所以这种方法不适合批量创建

工厂函数的方式

就是创建一个函数,但函数内部可以创建一个对象,我们把这种函数叫做工厂函数

         创造一个函数
         function createObj(num){
            1.手动创建一个对象
            const obj = {}
            2.手动给对象添加属性
            obj.name = 'qf001'
            obj.age = num
            3.手动返回对象
            return obj
         }
        let obj1 =  createObj(18) //表示创建了一个age为18的对象
        let obj2 = createObj(24)  //表示创建了一个age为24的对象

利用这个方法可以只书写一个对象,从而创建多个对象

自定义构造函数

构造函数:本质上就是一个普通函数,如果在调用的时候,在前边加上关键字 new , 我们就叫构造函数

function CreateObj1(num ,a){
            //1.自动创造一个对象

            //2.手动给对象添加属性(因为构造函数自动创造的对象通过 this 访问所以构造函数添加属性需要通过 this 添加)
            this.name = a
            this.age = num
            
            //3.自动返回对象
        }

        //构造函数调用时要与 new 连用
        let obj3 = new CreateObj1(11 , 'ab123')
        let obj = new CreateObj1(22,'ab456')

利用这个方法可以只书写一个对象,从而创建多个对象

注意:自定义构造函数书写要求:

1.构造函数的函数名首字母大写(建议,非强制,目的是与普通函数区分开)

2.构造函数内不要书写return

如果return的 是一个基本数据类型,写了也没用

如果return的 是一个引用(复杂)类型,写了会导致构造函数失效

3.构造函数调用时,必须和 new 关键字连用 如果不连用,也可以调用,但构造函数会失效

4.构造函数内部内部的 this

当一个函数和 new 关键字连用的时候,那么我们说这个函数是构造函数然后这个函数内部的 this 指向本次调用被自动创建出来的哪个对象1

5.构造函数不能使用箭头函数

因为箭头函数没有 this 但因为构造函数自动创造的对象通过 this 访问,所以构造函数添加属性需要通过 this 添加

构造函数的缺点:

构造函数内部如果有这个引用数据类型,如函数

在多次调用构造函数时,每一次调用函数时,都会重新创建一个函数,这样必然会造成内存空间的浪费

 function CreateObj1(name , age){
            this.name = name
            this.age = age
            this.fn = function() {
                console.log('我是一个函数')
            }
            
        }
        let obj3 = new CreateObj1(11 , '123466')
        console.log(obj3)

        let obj4 = new CreateObj1(12 , '1225')
        console.log(obj4)

上述代码的运行流程

第一次运行

第一次调用CreateObj1构造函数+自动创建一个对象

1。添加一个属性name,值为形参name

2添加一个属性age,值为形参age

3。添加一个属性a,值为一个囵定的字符串4。添加一个属性fn,值为一个函数,这时的函数是我们在这个函数内部定义的一个函数,假设地址为GD001

自动返回一个对象

第二次运行

第二次调用CreateObj1构造函数+自动创建一个对象

1。添加一个属性name,值为形参name

2添加一个属性age,值为形参age

3。添加一个属性a,值为一个囵定的字符串4。添加一个属性fn,值为一个函数,这时的函数是我们在这个函数内部定义的一个函数,假设地址为GD002

自动返回一个对象

结论

由上述代码可知每次调用CreateObj1构造函数时,都会重新创建一个新的函数,但每次创建的函数内容都是相同的,只不过内部定义的地址不一样,这样就会造成内存空间的浪费.

解决方法:

将内部的引用数据类型(如函数)提取出来,放到构造函数外部,然后在构造函数内部调用这个引用数据类型(如函数)

function fn() {
                console.log('我是一个函数')
            }
        function CreateObj1(name , age){
            this.name = name
            this.age = age
            this.fn = fn
        }
        let obj1 = new CreateObj1('abc123' , 111)

这样在每次调用CreateObj1构造函数时,它内部就会调用函数fn()而不是重新创建函数fn()

原型

什么是原型

1.每一个函数天生拥有一个属性 prototype 它的属性值是一个对象 我们通常把这个对象叫做这个函数的原型(空间|对象)

2.这个对象中有一个属性 叫做 constructor ,这个属性表示的就是当前这个原型是哪个函数的原型

3.每一个对象天生拥有一个属性__proto__(前后都是 两个 下划线) 这个属性指向自己构造函数的原型

向函数原型上添加属性 函数.prototype.属性 = 属性值

原型的作用

把构造函数中公共方法提取出来,放到原型中

为什么这么做

构造函数的原型上的方法或者属性。在每一个实例化对象中都能正常访问

对象访问原则:

访问对象的某一属性时,会先在对象本身去查找,找到就直接用 ,如果没找到 那么会去对象的 __proto__ 中查找如果没有找到 , 那么会去这个对象(原型)__proto__ 查找直到找到js的顶层对象 Object.prototype , 如果没找到就不找了

注意:

在 JS 中, 只要是一个函数, 那么就属于构造函数 Function 的实例化对象

在 JS 中, 只要是一个对象, 那么就属于构造函数 Object 的实例化对象

案例:

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

        }
        Person.prototype.sayHi = function () {
            console.log('你好~~~')
        }
        const p1 = new Person('QF001', 18)

        console.log(fn.prototype.__proto__)

1. p1 的 __proto__ 指向谁

proto 指向自己构造函数的原型 所以 相当于 指向了 Person.prototype

p1.__proto__ === Person.prototype

2. Person 的 __proto__ 指向谁

Person 是一个构造函数, 构造函数本质上就是一个函数而已 在 JS 中, 只要是一个函数, 那么就属于构造函数 Function 的实例化对象 __proto__ 指向自己构造函数的原型

所以相当于 指向了 Function.prototype

Person.__proto__ === Function.prototype

3. Person.prototype 的 __proto__ 指向谁

Person.prototype 是 Person 构造函数的原型, 本质上就是一个对象而已 在 JS 中, 只要是一个对象, 那么就属于构造函数 Object 的实例化对象 __proto__ 指向自己构造函数的原型 所以相当于指向了 Object.prototype

Person.prototype.__proto__ === Object.prototype

4. Function 的 __proto__ 指向谁

Function 是一个构造函数, 构造函数本质上就是一个函数而已 在 JS 中, 只要是一个函数, 那么就属于构造函数 Function 的实例化对象 __proto__ 指向自己构造函数的原型 所以相当于 指向了 Function.prototype

Function.__proto__ === Function.prototype

5. Function.prototype 的 __proto__ 指向谁

Function.prototype 是 Function 构造函数的原型, 本质上就是一个对象而已 在 JS 中, 只要是一个对象, 那么就属于构造函数 Object 的实例化对象 __proto__ 指向自己构造函数的原型 所以相当于指向了 Object.prototype

Function.prototype.__proto__ === Object.prototype

6. Object 的 __proto__ 指向谁

Object 是一个构造函数, 构造函数本质上就是一个函数而已 在 JS 中, 只要是一个函数, 那么就属于构造函数 Function 的实例化对象 __proto__ 指向自己构造函数的原型 所以相当于 指向了 Function.prototype

Object.__proto__ === Function.prototype

7. Object.prototype 的 __proto__ 指向谁

因为 Object.prototype 是 JS 的顶层对象, 再往上就没有了, 所以这个值为 null