面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次 调用就可以了。
简单来说:面向过程,就是按照我们分析好了的步骤,按照步骤解决问题。
面向对象编程(oop)是把事务分解成为一个个对象,然后由对象之间分工与合作。
简单来说:面向对象是以对象功能来划分问题,而不是步骤。
面向对象的特性: 封装性,继承性,多态性
好处:
在面向对象程序开发思想中,每一个对象都是功能中心,具有明确分工。
面向对象编程具有灵活、代码可复用、容易维护和开发的优点,更适合多人合作的大型软件项目。
面向过程编程
优点:
性能比面向对象高,适合跟硬件联系很紧密 的东西,例如单片机就采用的面向过程编程。
缺点:
没有面向对象易维护、易复用、易扩
面向对象编程
优点:
易维护、易复用、易扩展,由于面向对象有封装 、继承、多态性的特性,可以设计出低耦合的系统,使 系统更加灵活、更加易于维护
缺点:
性能比面向过程低
注:
前端面向过程更多
作用:
构造函数体现了面向对象的封装特性。
构造函数实例创建的对象彼此独立、互不影响
缺点:
存在浪费内存的问题
需要借助原型对象来使所有的对象使用同一个函数以此来节省内存
function Star (uname,age){ this.uname = uname this.age = age this.sing = function (){ console.log('唱歌') } } const ldh = new Star('刘德华',55) const zxy = new Star('张学友',58) 因为新建一个对象会在栈里面新建一个地址,通过地址指向堆里面新建的sing function, 但是sing function,会多占用内存 console.log(ldh.sing() === zxy.sing()) // false
作用:利用原型对象实现方法共享
原理:
构造函数通过原型分配的函数是所有对象所共享的。
JavaScript 规定,每一个构造函数都有一个prototype 属性,指向另一个对象,所以我们也称为原型对象
这个对象可以挂载函数,对象实例化不会多次创建原型上函数,节约内存
我们可以把那些不变的方法,直接定义在prototype 对象上,这样所有对象的实例就可以共享这些方法。
构造函数和原型对象中的this 都指向实例化的对象
// 原型是一个对象,我们也称为 prototype为原型对象 function Star (uname,age){ this.uname = uname this.age = age } // 公共的方法写到原型对象上 // 这样不管创建几次对象,都是指向的构造函数Star的sing方法 Star.prototype.sing = function (){ console.log('唱歌') } const ldh = new Star('刘德华',55) const zxy = new Star('张学友',58) console.log(ldh.sing() === zxy.sing()) // true
每个原型对象(prototype)里面都有个constructor属性(constructor 构造函数)
作用:
constructor 指向该原型对象的构造函数
function Star (uname,age){ this.uname = uname this.age = age this.sing = function (){ console.log('唱歌') } } // 原型对象的constructor 属性 每个原型对象里面抖音,该属性指向对象的构造函数 // 作用 指向原型对象的构造函数 console.log(Star.prototype.constructor);// Star console.log(Star.prototype.constructor === Star)// true
使用场景:
如果有多个对象的方法,我们可以给原型对象采取对象形式赋值. 但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象constructor 就不再指向当前构造函数了 此时,我们可以在修改后的原型对象中,添加一个constructor 指向原来的构造函数。
function Person(){ this.eyes = 2 this.head = 1 } function Teacher(){ } // Teacher 通过原型来继承Person Teacher.prototype = new Person() // 不同的对象 // 因为进行了赋值覆盖 还需要指回来原来的构造函数 Teacher.prototype.constructor = Teacher
对象都会有一个属性__proto__ 指向构造函数的prototype 原型对象。
__proto__对象原型里面也有一个constructor属性,指向创建该实例对象的构造函数
注:
__proto__ 是JS非标准属性
[[prototype]]和__proto__意义相同
用来表明当前实例对象指向哪个原型对象prototype
// 实例对象里面有对象原型: __proto__ 指向构造函数的prototype原型对象 // __proto__ 是对象的一个属性 //是JS非标准属性 [[prototype]] 和__proto__意义相同 // __proto__对象原型里面也有一个constructor属性,指向创建该实例对象的构造函数 // 是只读属性 function Star() {} const ldh = new Star() console.log(ldh.__proto__) console.log(ldh.__proto__ === Star.prototype)//true console.log('------------------------') console.log(ldh.__proto__.constructor) console.log(ldh.__proto__.constructor === Star)//true
JavaScript 中大多是借助原型对象实现继承的特性。
1. 封装-抽取公共部分
2. 继承-让老师和学生都能继承人的一些属性和方法、
3.老师和学生不要使用同一个对象,但是不同对象里面包含相同的属性和方法(构造函数)
注:
老师和学生都同时使用了同一个对象,根据引用类型的特点,他们指向同一个对象,修改一个就会都影响
总结:
人共有的属性和方法有那些,然后做个构造函数,进行封装,一般公共属性写到构造函数内部,公共方法,挂载 到构造函数原型身上。
老师做成构造函数继承人的属性和方法,之后创建自己独有的属性和方法
学生同理
// 父构造函数(父类)// 公共属性和方法 子构造函数(子类)继承父类并具有独特的属性和方法 // 子类的原型 = new 父类 // 子类的原型的constructor = 子类 //(可以选择给子类的(原型对象)prototype添加独特的方法) //...................... // 对象在new来进行实例化子类 // 封装成构造函数的形式,new出来的对象 结构一样,但是对象不一样 function Person(){ this.eyes = 2 this.head = 1 } // 老师构造函数 继承 Person function Teacher(){ } // Teacher 通过原型来继承Person Teacher.prototype = new Person() // 不同的对象 // 因为进行了赋值覆盖 还需要指回来原来的构造函数 Teacher.prototype.constructor = Teacher // 给老师添加一个方法 讲课 Teacher.prototype.teaching = function (){ console.log('老师讲课') } // 会出现一个问题 因为老师和学生的prototype 都是指向的Person // 给老师的prototype添加方法就是修改Person, // 这样会导致同样指向Person的Student的prototype也会被修改 // 解决办法,给他们的prototype 指向的对象不一样但是要指向的内容一样 // 就把之前抽取的对象,改成抽取成为构造函数就是,这样 new一个就是一个新的对象 const teacher = new Teacher() console.log(teacher.__proto__) // 学生构造函数 继承Person function Student(){ } // Student 通过原型来继承Person Student.prototype = new Person() // 不同的对象 // 因为进行了赋值覆盖 还需要指回来原来的构造函数 Student.prototype.constructor = Student const student = new Student() console.log(student)
原型链:
总结:(为了一眼明目)
只要是构造函数就有属性 原型对象(prototype)
通过new来创建构造函数对应的实例对象
只要是对象(实例对象,原型对象(prototype)等)就有属性对象原型(__proto__)指向原型链上一级的原型对象(prototype)
原型对象(prototype)和对象原型(__proto__)都有属性constructor指向对应的构造函数
万物皆对象,Object的原型对象(prototype)的对象原型(__proto__)指向null
定义:
基于原型对象的继承使得不同构造函数的原型对象关联在一起,并且这种关联的关系是一种链状的结构,我们将原型对 象的链状结构关系称为原型链
原型链查找规则:
1. 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。
2. 如果没有就查找它的原型(也就是__proto__指向的prototype 原型对象)
3. 如果还没有就查找原型对象的原型(Object的原型对象)
4. 依此类推一直找到Object 为止(null)
5. __proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线
注:
可以使用 instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上
// 检查是否出现在其原型链上 instanceof 子 instanceof 父 function Star (uname,age){ this.uname = uname this.age = age } const ldh = new Star('刘德华',55) console.log(ldh instanceof Star) // true console.log(ldh instanceof Object)// true //console.log(Star instanceof ldh)// 报错 console.log(ldh instanceof Array)// false console.log([1,2,3] instanceof Array)// true // 最终的一切的一切,万物皆对象 console.log(Array instanceof Object)// true