从面向过程开发到面向对象开发,是个思维的重大转变,当真正理解了面向对象编程后,对于日常的开发就会有一个质的飞跃。
一. 面向过程与面向对象的区别
1.概念
1.面向过程
是一种以过程为中心的编程思想,面对问题,罗列出解决问题的步骤,然后按步骤一步步去实现
比如:蛋炒饭的制作,蛋和饭都混在一起,省事省力就能做出一盘美味可口的炒饭
2.面向对象
是以对象为核心,不需要关心程序内部的实现。解决问题时候,把问题抽象成对象,分析解决问题需要哪些对象,然后给对象里赋值一些方法和属性,让对象执行自己的方法,解决问题。
比如:盖浇饭的制作,把一份完整的饭拆分成米饭和菜,通过组合不同的米粉和菜能满足不同客户定制化的需求
2.区别
1.面向过程:
优点:性能较高
缺点:耦合性强,不易维护,扩展和复用
2.面向对象
优点:程序间低耦合,易于维护,扩展和复用,灵活配置
缺点:性能低于对象过程,因为面对对象需要实力化,比较消耗性能
二. 面向对象的特性
1.三大原则:封装,继承,多态
1.封装
1.概念:也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
封装可以给用户提供一个函数,函数里的方法属性不对外暴漏,而能实现用户想要实现的功能
好处:将变化隔离,程序便于使用,易于复用,安全性较高,
原则:隐藏不需要对外暴漏的属性,仅提供公共访问方式
2.继承
1.概念:让一个类拥有另一个类的属性和方法
2.创建对象的方式
1.对象字面量
const obj = {}
2.构造函数
const = new Object()
3.Object.create()
用这种方式创建一个空对象 Object.create(Object.prototype),类似 {},注意,Object.create(null)可以创建对象,但是这个对象没有原型,不会继承任何东西,连toString()方法都没有哦。
const obj1 = {name:'111'}
const obj2 = Object.create(obj1)
console.log(obj2.name) // 111
2.继承的方式
1.原型链
1.1 基本思想:利用原型,让一个引用类型继承另一个引用类型的属性和方法
// 父函数
function Parent(){
this.value1 = true
}
Parent.prototype.getParentValue = function(){
return this.value1
}
// 子类函数
function Child(){
this.value2 = false
}
// 这句话是最关键的。child 继承了parent的原型
Child.prototype = new Parent()
Child.prototype.getChildvalue = function(){
return this.value2
}
const cc = new Child()
console.log(cc.getParentValue()) // true 这个是继承的父类的方法
console.log(cc.getChildvalue()) // false 自己的方法
1.2 缺点:
缺点1.实例共享引用类型,
原型链继承主要的问题是包含引用类型值的原型, 因为包含引用类型值的原型属性会被所有的实例共享, 而在通过原型来实现继承的时候, 原型实际变成了另外一个函数的实例(这里边就有可能存在引用类型)
缺点2.在创建 Child 的子类的时候,无法像继承元素传递参数
所以综上,这种继承方式少用
2.构造函数
3.组合继承
4.原型式继承
5.寄生式模式
6.寄生组合试继承
3.多态
三. 对象的特性
1.对象的方法
1.1 属性的简洁方
const name='zy'
const obj = {name:name}
// 简写
const obj1 ={name}
console.log('obj1',obj1) // obj1: {name:'zy'}
1.2 属性名表达式
const obj = {name:'zy'}
obj.name // 'zy'
obj['name'] // zy
1.3 属性的可枚举和遍历 ***
1.可枚举性
1. Object.getOwnPropertyDescriptor
每个属性都有一个Descriptor,方法是 Object.getOwnPropertyDescriptor(对象,属性名)
const obj = {name:'zy'}
console.log(Object.getOwnPropertyDescriptor(obj,'name'))
// {
// configurable: true, // 是否可以删除
// enumerable: true, // 是否可以枚举
// value: zy,
// writable: true, // 是否可以编辑
// }
2.Object.getOwnPropertyDescriptors
若要看整个对象的描述,可以用Object.getOwnPropertyDescriptors(obj)
const obj = {name:'zy',age:'1'}
Object.getOwnPropertyDescriptors(obj)
// {
// age: {
// configurable: true,
// enumerable: true,
// value: "1",
// writable: true,
//}
//name: {
// configurable: true,
// enumerable: true,
// value: "zy",
// writable: true,
//}
//}
3.Object.defineProperty
这里可以看到通过对象字面量直接创建的属性默认是可以遍历,可以更改和可以删除的,下面我们通过另一种方法创建对象的属性,Object.defineProperty
const obj = {}
Object.defineProperty(obj,'name',{
value:'zy'
})
console.log(obj) // {name:'zy}
console.log(Object.getOwnPropertyDescriptor(obj,'name'))
// {
// configurable: false
// enumerable: false
// value: 12
// writable: false,
// }
通过对比可以发现,直接字面量创建的对象属性默认都是可以编辑遍历和删除的,而通过Object.definePorperty创建的对象属性则默认是不可编辑遍历和删除的。
扩展下 defineProperty的相关知识
configurable 是配置属性是否可以删除,默认true
enumerable 是配置属性是否可以枚举,默认true
writable 是配置属性是否可以编辑,默认true
4.Object.preventExtensions
Object.preventExtensions()方法让一个对象变的不可扩展,也就是永远不能再添加新的属性。
1.通过对象字面量新增属性,静默的失败
const obj = {name:'zy'}
Object.preventExtensions(obj)
obj.age = '1' //通过对象这种方式添加属性已经添加不上去了
console.log(obj) // {name:'zy'}
2.通过defineProperty给对象添加属性则直接报错
const obj = {name:'zy'}
Object.preventExtensions(obj)
Object.defineProperty(obj,'age',{value:'1'})
console.log(obj)
// Uncaught TypeError: Cannot define property age, object is not extensible
5.Object.isExtensible()
Object.isExtensible() 方法判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。
const obj = {name:'zy'}
Object.isExtensible(obj) // true
Object.preventExtensions(obj)
Object.isExtensible(obj) //false
6.Object.seal()
Object.seal()方法封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置。当前属性的值只要原来是可写的就可以改变。
7.Object.freeze()
方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象。
2.遍历
对象的遍历总共有5种方式
1.for...in
for in可以遍历对象自身和继承的属性,不含不可枚举和Symbol
补充个知识点:in 操作符 和 hasOwnProperty
in和hasOwnProperty的区别
in 可以检查出自身和原型上的所有属性
hasOwnProperty 判断的自身的属性
通过这两个属性可以区分是哪些属性是自身的,哪些属性是原型上的
const obj = {
name:'zy',
age:'1',
}
// in 操作符
console.log('name' in obj) // true 自身的属性
console.log('age' in obj) // true 自身的属性
console.log('wdith' in obj) // false 不存在这个属性
console.log('toString' in obj) // true 原型上的方法
// hasOwnProperty
console.log(Object.hasOwnProperty('name')) // true 自身的属性
console.log(Object.hasOwnProperty('toString')) // false 不是自身的属性和方法
// 通过in 和 hasOwnProperty组合区分出原型的方法属性
in 返回true hasOwnProperty 返回false的就是原型上的方法
function isPropertyItem(item,obj){
return item in obj && !Object.hasOwnProperty(item)
}
2.Object.keys(obj)
Object.keys,返回一个数组,可遍历对象自身属性,不含不可枚举和Symbol
3.Object.getOwnpropertyNames(obj)
getOwnpropertyNames ,返回数组,获取自身所有属性,包含不可枚举,不含Symbol
4.Object.getOwnPropertySymbols(obj)
Object.getOwnPropertySymbols 返回数组,获取自身所有Symbol属性
5.Reflect.ownKeys(obj)
Reflect.ownKeys返回一个数组,获取自身所有属性,包含 不可枚举和Symbol