认识面向对象
面向对象并不是一个语法, 也并不是一个新的语言, 他是用 JS 完成需求的一种 思想
我们一开始学习的解决问题的方式,统称为面向过程
面向过程:
注重的是过程,每一步事无巨细,全都有我们的代码从上往下,依次完成
问题:
1.每个功能之间相互影响
2.拿轮播图举例: 如果有多个, 那么这个代码 需要重复书写多次
面向对象:
注重的是得到一个对象, 这个对象就是我们的需求,比如说这个对象可以是一个轮播图,这个对象也可以是一个 分页
举例:
拿一个功能: 我想吃碗面
面向过程:
1.准备面粉
2.准备水
3.和面
4.切面
5.烧水
6.煮面
7.吃面
面向对象:
1.找一个面馆
2.下单
3.吃面
假如 我们没有所谓的 "面馆", 那么我们直接开一个面馆
创建完成之后的"面馆", 除了你能使用, 其他人也能使用, 起到一个 多次复用的效果
创建对象
创建对象的方式
1.字面量的方式(此方式不合适,不利于批量创建)
例: let obj = {
name: 'qwer',
age: 23
}
2.内置构造函数(此方式不合适,不利于批量创建)
例: let obj = new Object()
3.工厂函数的方式
其实就是 创建一个函数, 但是函数内部可以创建一个对象, 我们把这种函数叫做工厂函数
例:
function createObj(num) {
const obj = {}
obj.name = 'qwer'
obj.age = num
return obj
}
let obj1 = createObj(23)
自定义构造函数
什么是构造函数
本质上就是一个普通函数,如果在调用的时候, 前面加上一个关键字 new , 那么我们把这种函数叫做 构造函数
工厂函数:
function createObj() {
}
构造函数:
function CreateObj1() {
}
构造函数的书写!!!
1.构造函数的函数名首字母 大写(非强制,建议大写),目的是为了和普通函数做一个区分
2.构造函数内,不要写 return
如果 return 的是一个 基本数据类型, 写了也没用
如果 return 的是一个 引用数据类型, 写了就会导致构造函数失败
3.构造函数调用时, 必须和 new 关键字使用,如果不连用,也能调用,但是失去了构造函数的功能
!!! 4.构造函数内部的 this !!!
当一个函数 和 new 关键字连用的时候, 那么我们说这个函数 是构造函数, 然后这个函数内部的 this 指向本次调用被自动创建出来的那个对象
5.构造函数不能使用 箭头函数
因为箭头函数内部没有 this
例:
function Fn () {
this.name = name
this.age = age
this.a = '我是随便添加的一个属性 a, 我是固定的内容'
}
const p1 = new Person('eddie', 18)
console.log(p1)
构造函数的缺点
构造函数内部 如果有这个引用数据类型, 比如函数
在多次调用构造函数时,每一次都会重新创建一个函数, 必然会造成内存空间浪费
原版构造函数(会出现浪费内存的情况)
function Fn1 (name, age) {
this.name = name
this.age = age
this.a = '我是随便添加的一个属性 a, 我是固定的内容'
this.fn = function () {
console.log('我是 fn 函数')
}
}
const p1 = new Fn1('彭于晏', 41)
p1.Fn1
const p2 = new Fn1('秦霄贤',24)
p2.Fn1
console.log('验证两个对象内部的 fn 函数是否为同一个内存地址: ', p1.Fn1 === p2.Fn1, p1.Fn1 === p3.Fn1) //false
优化版 构造函数
function WinFn () {
console.log('我是 fn 函数')
}
function Fn1 () {
this.name = name
this.age = age
this.a = '我是随便添加的一个属性 a, 我是固定的内容'
this.fn = WinFn
}
const p1 = new Fn1('彭于晏', 41)
p1.Fn1
const p2 = new Fn1('秦霄贤',24)
p2.Fn1
console.log('验证两个对象内部的 fn 函数是否为同一个内存地址: ', p1.Fn1 === p2.Fn1, p1.Fn1 === p3.Fn1) //true
原型
别名: 原型空间 / 原型对象
什么是原型
=> 每一个函数 天生拥有一个属性 prototype,他的属性值是一个 对象
=> 我们通常把这个对象叫做 这个函数的原型(空间|对象)
=> 原型这个对象中 有一个属性 叫做 constructor,这个属性表示的是 当前这个 原型对象 是那个 函数的原型
如何使用原型中的属性
=> 每一个对象 天生拥有一个属性 __proto__(前后都是两个下划线)
=> 这个属性指向 自己的构造函数 的原型
总结: !!!!!!
函数 的prototype属性 指向 自己的原型对象
实例对象 的__proto__属性 指向 自己构造函数的原型对象
原型对象 的constructor属性 指向 自己的构造函数
实例:
function Fn () { }
Fn.prototype.age = 23
Fn.prototype.name = '秦霄贤'
Fn.prototype.Fn1 = function () {
console.log('我是添加在 Fn1 函数的原型对象上的 一个函数')
}
console.log(Fn.prototype)
let p1 = new Fn1
使用 new 结合 Fn1() 这个过程叫做 构造函数的实例化, 最终回得到一个对象
在我们当前案例中, 是将这个对象放在 变量 p1 里面
所以有些开发管这个 p1 叫做 实例化对象/实例对象,本质上依然是一个对象
console.log(p1.__proto__)
console.log(p1.__proto__ === Fn1.prototype)
__proto__属性指向自己构造函数的原型
因为 p1 这个对象的构造函数 是 Fn1, 那么也就说,p1__proto__实际指向的就是 Fn1 这个函数的原型
原型链
把构造函数中 公共方法 提取出来, 放在原型中
这么做的原因:
构造函数的原型上的方法或者属性, 在每一个实例化对象中都能正常访问
对象的访问规则
访问对象的某一个属性时,会先在对象本身去查找, 找到就直接使用, 如果没有找到
那么会去对象的 __proto__ 中查找,找到就使用, 如果没有找到
那么会去这个对象(原型) 的 __proto__ 查找
直到查找到 JS 的顶层对象 Object.prototype, 如果还没找到,就不再向上查找
扩展内置构造函数
内置构造函数:
new Object()
new Array()
new RegExp()
new String()
JS 内的数据结构类型
=> 在 JS 中, 任何一个数组, 他的构造函数都是 Array
=> 在 JS 中, 任何一个对象, 他的构造函数都是 Object
例:
用 数组 举例 ---Array.prototype
需求:在数组上扩展一个方法 getMax ,作用是求出数组中最大值, 并且要求所有的数组都能使用
let arr1 = [1, 2, 5, 8, 9, 12]
ler arr2 = [12, 58, 78, 98, 21]
Array.prototype.getMax = function () {
let max = this[0]
for (let i = 0;i < this.length; i++) {
if(this[i] > max){
max = this[i]
}
}
return max
}
console.log(arr1.getMax())
console.log(arr2.getMax())
面向对象的逻辑问题
function Fn1(name, age) {
this.name = name
this.age = age
}
Fn1.prototype.sayHi = function(){
console.log('Holle')
}
const p1 = new Fn1('abcd',23)
1.p1 的 __proto__指向谁
__proto__指向自己的构造函数的原型
所以 相当于 指向了 Fn1.prototype
p1.__proto__=== Fn1.prototype
2.Fn1 的 __proto__指向谁
Fn1 是一个构造函数,构造函数本质上就是一个函数而已
在 JS 中, 只要是一个函数, 那么就属于构造函数 Function 的实例化对象
__proto__指向自己构造函数的原型
所以就相当于 指向了 Function.prototype
Fn1.__proto__ === Function.prototype
3.Fn1.prototype 的 __proto__指向谁
Fn1.prototype 是 Fn1 构造函数的原型, 本质上就是一个对象而已
在 JS 中,只要是一个对象,那么就属于构造函数 Object 的实例化对象
__proto__指向自己的构造函数的原型, 所以就相当于指向了Object.prototype
Fn1.prototype.__proto__ === Object.prototype
4.Function 的 __proto__ 指向谁
Fnction 是一个构造函数, 构造函数本质上就是一个函数而已
在 JS 中,只要是一个函数,那么就属于构造函数 Function的实例化对象
__proto__指向自己构造函数的原型
Function.__proto__ === Function.prototype
5.Funtion.prototype.__proto__ 指向谁
Function.prototype是Function的原型对象,本质上就是一个对象而已
在 JS 中,只要是一个对象,那么就属于构造函数 Object的实例化对象
__proto__是指自己构造函数的原型对象
Funtion.prototype.__proto__ === Object.prototype
6.Object 的 __proto__指向谁
在 JS 中, 只要是一个函数, 那么就属于构造函数 Function 的实例化对象
__proto__ 指向自己构造函数的原型
所以相当于 指向了 Function.prototype
Object.__proto__ === Function.prototype
7.Object.prototype 的 __proto__ 指向谁
因为 Object.prototype 是 JS 的顶层对象, 再往上就没有了, 所以这个值为 null
console.log(Object.prototype.__proto__) null