javascript之面向对象编程
语言发展的历史
面向过程的语言
- 最先开始的古早语言包括汇编语言,c语言
- 特点:难懂,移植性差,只能在特定的地方编译
编程思想
以过程为导向,只关心数学逻辑
面向对象的语言
- 在汇编语言和c语言的基础上,我们又发展了c++,Java,javascript,Python
- 这些语言在不同领域发挥不同的特长
- 特点:引入了面向对象的思想去设计程序
编程思想
1.找出实体
2.分析实体的属性和功能
3.让实体之间相互作用
举个例子
- 有一辆车以100km/h的速度,在1000km的道路上行驶,请问这辆车从起点到终点需要多长时间?
面向过程语言的解决办法
- 因为路程=速度/时间
- 所以1000/100=10h
let hour = 1000/100
console.log(hour)
面向对象的解决办法
//有一辆车(实体)以100km/h(属性)的速度,在1000km(属性)的道路(实体)上行驶(功能),请问这辆车从起点到终点需要多长时间?
let car = {
speed:60,//属性速度60
run:function(road){
return road.length/this.speed}//车子的行驶功能,利用开在的路上的属性,最终返回时间
}
let bridge = {
length:1000}
let hour = car.run(bridge)
构造函数
当我们创造一个对象时,我们可以这么去创建一个对象
let person = {}
person.name="野兽先辈"
person.age="24岁"
person.job="学生"
person.showNmae=function(){
console.log(`我叫${this.name},${this.age},是${this.job}`)}
- 这样,我们就创建了一个叫"人"的对象,24岁,是学生
- 但是,如果我需要造更多的"人",就需要不听的复制刚才写的那些函数然后在修改,就很麻烦.
- 因此,我们可以设置一个"工厂"
- 给工厂一个空白的"原料",工厂内设置给原料"加工"的方法
- 给这个空白的"原料"进行"加工"
- 输出这个"加工"后的"原料",即最终产品
具体思路
1,先创建一个函数,设置他的形参
2.在函数内创建一个空对象
3.给空对象设置属性和方法
4.输出这个对象
function person(name,age){
//添加原料
let obj = {}
//加工 this = new obj
obj.name=name
obj.age=age
obj.showNmae=function(){
console.log(`我叫${this.name},今年${this.age}岁`)}
//输出产品
return obj
}
person(1,2).showNmae()//我叫1,今年2岁
- 因此通过调用
person(name,age)
并给其不同的参数,就能批量的设置不同的对象 - 但是,在设置这个"工厂"时,每次都要设置空对象,非常麻烦,而且
- 因此我们可以使用
new
来创建一个空函数
new
运算符
当我们使用一个new运算符时
- 当前函数中的
this
指向新创建的对象 - 它会自动完成"添加原料"(创建空白对象),“输出最终产品”(
return
操作完成的对象)的操作 - 因此
this
指的就是这个创建的空白对象的名字
因此,之前的"工厂"可以这么写
//先制作一个"工厂"
function person(name,age){
//使用new时,自动创建空对象,并且this指向这个空对象
this.name=name
this.age=age
this.showNmae=function(){
console.log(`我叫${this.name},今年${this.age}岁`)}
}
let p1 = new person(1,2)//启动person工厂的整个流程,添加1,2的加工属性,最后输出值给p1
总结
因此,这种通过new
调用函数,我们把它叫做构造函数
- 注意:构造函数一般首字母都是大写的
ES6的构造函数写法
function person(name,sex,age){
this.name=name
this.sex=sex
this.age=age
person.prototype.showSelf=function(){ console.log(`${this.name}${this.age}${this.sex}`)}
}
//ES6的写法
class person{
//class属性添加
constructor(name,sex,age){
this.name=name
this.sex=sex
this.age=age
}//添加构造函数的构造器
showSelf(){ console.log(`${this.name}${this.age}${this.sex}`)}
}//添加构造函数的原型方法
prototype
原型
- 首先,任何函数(注意是函数),他都有一个
prototype
属性,这个属性拥有一个对象 - 原型是一个对象
- 每一个对象,都有个
__proto__
属性 - 我随便写一个函数,他就有一个
prototype
的对象.
因为函数拥有prototype
属性,所以我们可以给构造函数的原型prototype
添加方法
- 不同的对象可能是由相同的构造函数创建的,但是对象单独创建的函数不能通用
- 因此因为是这些对象是相同的构造函数创建的,所以可以在这一个构造函数的原型中添加函数,然后调用,这个函数就是通用的
- 以此达到通用使用函数的方法
._proto_
- 当用new 函数 创建对象时,对象都拥有
.__proto__
属性,都指向这个构造函数
的.prototype
原理
- 一个对象是由构造函数创建的,那么这个构造函数拥有原型
prototype
,是一个对象 - 一个对象是由构造函数创建的,那么这个对象的
_proto_
指向他的构造函数的prototype
,是一个对象 - 因此,
_proto_
指向的prototype
,就是构造函数的prototype
,他们拥有同一个对象. - 当对象使用属性时,先从自有 的属性找,找不到再从
_proto_
**(即构造函数的原型对象)**找 - 因此,
Dog.prototype.age
=dog1.__proto__.age1=
dog1.age`
- 比如数组是由
new Array()
创造出来的一个对象,数组的_proto_
指向他的构造函数Array()
的原型prototype
.
- 因此
Array()
的prototype
原型中的函数,就是数组[1.2,3]
的_proto_
中的函数
instanceof
- 用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
- 所有的函数最终都是又Function创造的
原型链
自己画个图比较清楚点,以上面创建过的new Dog()
为例
//先制作一个"工厂"
function person(name,age){
//使用this时,自动创建空对象,并且this指向这个空对象
this.name=name
this.age=age
this.showNmae=function(){
console.log(`我叫${this.name},今年${this.age}岁`)}
}
let p1 = new person(1,2)//启动person工厂的整个流程,添加1,2的加工属性,最后输出值给p1
- 构造函数Dog的原型对象由构造函数Object创建
- 构造函数Object的原型对象没人创建,到底了
- 构造函数Object的原型对象上有个函数toString:fn,找到了这个可以直接使用
Object.creat
- 创建一个新对象,用参数作为新对象的原型
__proto__
- 好处是person2.name和person1.name一样,共用一个name属性
let person1 = {name: '小明'}
let person2 = Object.create(person1)//让对象person1最为person2的原型对象
console.log(person2)//{}这个对象是空的,因为对象person1跑人person2的原型对象里去了
person2
的原型对象是person1
person1
原型对象的又是又Object
创建的,全在person2._proto_._proto_
里边