前端的路程二之面向过程&面向对象&原型&原型链&继承

面向过程

面向过程就是我们再开发或者说在生活中遇到一些事情的时候,按照需求去做,遇到了就解决,列举去问题一步一步的去实现
举例:现在有一台挖土机,它需要挖土

// 挖土机要挖土
function excavatorMove(){//...挖土机移动的逻辑}
function excavation(){//...挖土机挖土的逻辑}
function complete(){//...挖土机挖土完成的逻辑}
function render(){// 重新加载}
//第一步移动
excavatorMove()
render()
//第二步挖土
excavation()
render()
//第三步完成收工
complete()
render()

像这样一步一步的去完成我们需要的功能,就叫做面向过程,其实和面向函数是差不多的意思,面向函数就是将各种动作进一步封装成工具,就像项目中的utils文件一样,都已一个一个的功能函数。

对比于面向对象的优缺点:

优点缺点
每一步都要重新执行对应的逻辑代码,不利于维护、复用和扩展步骤紧密,每一步都独立执行,性能上会更好

面向对象(面试重点呀)

先来概括一下面向对象编程:全称(Object Oriented Programming 面向对象的程序设计),意思就是将想法拟成一个实在的对象的变成范式,就是以问题来划分功能,将功能具象化到对象上,或者说使得对象具备某种能力。
举例:
一台挖土机,它还需要挖土。
那么在挖土机干活的时候会有几个步骤;
1.挖土机移动
2.挖土
3.再回来

// 挖土机挖土
// 这里不想太啰嗦,就假设Excavator封装了各种挖土机的执行操作 renderScene负责渲染
const excavator = new Excavator('挖土机一号')
const renderScene= new RenderScene()
// 第一步 挖土机移动
excavator.move()
renderScene.render()
// 第二步 挖土
excavator.excavation()
renderScene.render()
// 第三步 完成
excavator.complete()
renderScene.render()

这里会感觉这跟面向过程差不多的呀
那多追加一步操作,挖土一铲子倒回去半铲子

// 面向过程每次的excavation都要追加倒回
excavatorMove()
refund()
render()
// ...一系列回倒操作

// 面向对象
excavator.refund()
renderScene.refund()
renderScene.render()

这样来讲面向对象就是将能力赋予一个一个的对象,然后分工合作来完成所需,并且这些能力是可扩展的
对比于面向对象的优缺点:

优点缺点
易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统 更加灵活、更加易于维护性能比面向过程低
为什么说面向对象性能低,这样理解,因为比面向过程多了一层继承包裹,多一步访问操作肯定比直接就是干委婉一点,我是这样理解的

js中的面向对象
创建方法

var car = {}
var car = Object.create({})
var car = new Car()

var car ={} 和 var car = Object.create({})什么区别呢
在这里插入图片描述
对比起来 Object.create({})多了一层Prototype

// An highlighted block
var car = {}
var carz = Object.create({})
console.log(car.__proto__.constructor == carz.__proto__.__proto__.constructor)
// 打印结果 true
// 对比起来其实就是多了一层构造

至于new关键字,就是创建出对应的实例

重点来了,面试必备 手动实现new
先看new干了什么事情

  1. 创建一个对象
  2. 对象的原型指向构造函数的prototype
  3. 该对象实现了上层构造函数的方法
  4. 根据对应的情况返回
    如果构造函数没有返回,或者返回了非对象,那么new之后就返回创建的对象;
    如果构造函数明确返回了一个对象,那么new之后就返回那个对象;
function myNew(foo,...rest){
   if(typeof foo !== 'function'){
       throw new Error('目标不是一个函数,目标需要一个构造函数入参')
   }
   // 创建一个对象对象的原型指向构造函数的prototype
   const obj = Object(foo.prototype)
   // 或者 是const obj = {} obj.__proto__ = foo.prototype赋值新的原型
   // 在对象上实现上层构造函数的方法(通过指向改变,将构造函数中的逻辑执行一遍)
   const result = foo.call(obj,rest)
   // 根据情况返回对应的值
   return result&&typeof result == 'Object'?result:obj
}

原型

什么是原型,prototype,这个就是原型,他的概念就是被实例化的对象在访问属性的时候,如果本身没有挂载对应的属性那么就会去原型上找,原型的本质也是一个对象,可以理解为一个指针指向了某一个对象,每个对象都会有这样的指针,从而产生可扩展可继承性。

// 简单示例
var obj = {}
console.log(obj.__proto__)
// 输出 {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
// obj.__proto__就指向它的原型对象,对象的方法都在原型上可以找到。
// 数组对象、字符串对象、数字对象都一样。

(比喻一下)可以说原型就是对象的父级,对象被构造函数创建出之后会携带原型上的属性,构造函数和原型合作支撑了对象的产生和使用。

原型链

对象的基础是构造函数创建,实例对象通过__proto__关联构造函数的原型,构造函数通过prototype关联原型,原型通过constructor关联构造函数,相互联系。
原型链是对象通过__proto__指向原型对象,原型对象又可以通过__proto__指向自己的原型一直往上查询到null,这就搭建了原型链。

为什么这么设计?

function Per(name){
	this.defined = '人'
	this.name = name
}
Per.prototype.method1 = function (){console.log('我会coding')}
var p = new Per('大老王')
var p2 = new Per('小王')
p2.method1 = function (){console.log('我会很秀的coding')}
p2.sex = '外星人'
  1. p和p2拥有Per上的能力,并且继承了prototype的coding能力,不用再去反复的创建;
  2. p2新增一些Per没有的能力,有良好的扩展性;

通常记下就ok,灵活运用起来的特点就是扩展性,复用性,可维护性

重中之中-继承(面试重点)

前端的继承方式

  1. 构造函数继承;
  2. prototype原型继承;
  3. class继承;
  4. call、apply、bind寄生式继承;

基础是这些,也可以混合使用

原型继承

function Per(sex){
	this.sex = sex
	this.list = [1,2]
}
Per.prototype.say = function (){console.log('你是谁')}
function p(){}
// 修改原型指向
p.prototype = new Per('男')
// 指回构造器
p.prototype.constructor = p
var son = new p()
var son2 = new p()
son.list.pop()
console.log(son2.list) //输出[1]

为什么要修改constructor?
不修正的时候constructor指向的是Per,那么在使用构造器的时候正常应该是访问到p,但是访问到的却不是p而是Per
实例化的时候无法传参数到被继承方,被继承方有引用类型的属性,修改时候会影响所有

构造函数继承

function Per(sex){
	this.sex = sex
	this.list = [1,2]
	this.say2= function(){console.log('你是那个')}
}
Per.prototype.say = function (){console.log('你是谁')}
function p(sex){
	Per.call(this,sex)
}
var son = new p('女')
var son2 = new p('男')

属性独立,无法继承原型上的属性,方法都在构造函数中定义,每次创建实例都会创建一遍方法

组合继承

function Per(sex){
	this.sex = sex
	this.list = [1,2]
}
Per.prototype.say = function (){console.log('你是谁')}
function p(sex){
	Per.call(this,sex)
}
p.prototype = new Per()
p.prototype.constructor = p
const son = new p()

可以继承原型属性也可以继承静态属性,但会调用两次父级构造函数

组合寄生继承

function Per(sex){
	this.sex = sex
	this.list = [1,2]
}
Per.prototype.say = function (){console.log('你是谁')}
function p(sex){
	Per.call(this,sex)
}
p.prototype = Object.create(Per.prototype)
p.prototype.constructor = p

可继承静态和原型属性,避免属性共享

class继承 直接用

class XXX extends xxx {}

class继承和组合寄生的区别

  1. class继承继承静态属性;
  2. class子类中的constructor中必须调用super,因为子类的this要通过父类的构造函数去构建;
  • 21
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值