1.概念
-
面向对象是什么?
- 他并不是一个语法,也并不是一个新的语言,是用 JS 完成需求 的一种思想
-
面向过程:
- 注重的是过程,每一步事无巨细,全都由我们的代码从上往下,依次完成
- 问题:
- 每一个功能之间会互相影响
- 拿轮播图举例,如果有多个,那么这个代码需要重复写多次
-
面向对象:
- 注重的是得到一个对象,这个对象就是我们的需求,比如说这个对象可以是一个轮播图,这个对象也可以是一个分页
-
注意点: 面向对象我们关注的就是 对象,因为需要批量创建,所以我们创建对象的方式就要和以前不同
创建对象的方式
1.字面量的方式
- 这个方式不利于批量创建
let obj = {
name: 'QF001',
age: 18
}
2.内置构造函数
- 这个方式不利于批量创建
let obj = new Object()
let obj2 = new Object()
3.工厂函数的方式
- 其实就是创建一个函数,但是函数内部可以创建一个对象,我们把这种函数叫做工厂函数
function createObj (num){
//3.1 手动创建一个对象
const obj = {}
//3.2 手动给对象上添加一些属性
obj.name = '001' // 添加一个 固定的字符串,每次创建对象它的属性 都是 '001'
obj.age = num // 利用形参给对象的属性赋值一个 '变量', 这样每次创建对象时都可以修改这个属性的值
//3.3 手动给对象返回
return obj
}
let obj1 = createObj(18)
let obj2 = createObj(66)
console.log(obj1)
console.log(obj2)
4.自定义构造函数的方式
- 什么是构造函数?
- 本质上就是一个普通函数,如果在调用的时候,前边加上一个关键字 new,那么我们把这种函数叫做 构造函数
- 构造函数的书写
- 构造函数的函数名首字母 大写(建议,非强制),目的就是为了和普通函数做一个区分
- 构造函数内不要写 return
- 如果 return 的是一个基本数据类型,写了没用
- 如果 return 的是一个引用数据类型,写了就会导致构造函数没用
- 构造函数调用时,必须和 new 关键字连用
- 如果不连用,也能调用,但是构造函数就没用了
- 构造函数内部的 this 指向
- 当一个函数和 new 关键字连用的时候,那么我们就说这个函数是 构造函数
- 然后这个函数内部的 this 指向本次调用被自动创建出来的那个 对象1
- 构造函数不能使用 箭头函数
- 因为箭头函数内部没有 this
//构造函数
function Person(name,age){
//1.自动创建一个对象
//2.手动给对象添加一些属性
//因为构造函数自动创建出来的对象可以通过 this 来访问,所以我们需要向对象上添加属性的时候,可以通过 this 来添加
this.name = name
this.age = age
//3.自动返回对象
}
const p1 = new Person('gg',18)
构造函数的缺点
- 构造函数内部如果有引用数据类型,比如函数;在多次调用构造函数时,每一次都会重新创建一个函数,必然会造成这个内存空间的浪费
function Person() {
this.name = 'Jack'
this.age = 18
this.sayHi = function () {
console.log('hello word')
}
}
var o1 = new Person()
var o2 = new Person()
- 第一次 new 的时候, Person 这个函数要执行一遍, 执行一遍就会创造一个新的函数, 并且把函数地址赋值给 this.sayHi
- 第二次 new 的时候, Person 这个函数要执行一遍, 执行一遍就会创造一个新的函数, 并且把函数地址赋值给 this.sayHi
- 执行一遍就会创造一个新的函数, 并且把函数地址赋值给 this.sayHi
- 这样写的话, 我们两个对象内的
sayHi
函数就是一个代码一模一样, 功能一模一样 - 但却占用了两个内存空间
- 也就是说
o1.sayHi
是一个地址,o2.sayHi
是一个地址 - 所以我们执行
console.log(o1.sayHi === o2.sayHi)
的到的结果是false
- 缺点: 一模一样的函数出现了两次, 占用了两个空间地址
- 要想解决的话, 就需要一个东西, 叫做原型
原型
-
什么是原型?
1.每一个函数天生拥有一个属性,叫 prototype,它的属性值是一个对象
2.我们通常把这个对象叫做 这个函数的 原型
3.原型这个对象中有一个属性叫 constructor,这个属性表示的是当前这个 原型 是哪个函数的原型
-
如何使用原型中的属性?
每一个对象 天生拥有一个属性__proto__(前后都是两个下划线)
这个属性指向自己构造函数的原型
function Person () {}
//向 Person 函数的原型(对象)上 添加一些属性
Person.prototype.abc = '我是添加到 Person 函数的原型上的一个字符串,你可以通过这个 Person 创建出来的对象 身上的 __proto__查看到'
Person.prototype.age = 18
Person.prototype.name = 'gg'
Person.prototype.fn = function () {
console.log('我是添加在 Person 函数的原型对象上的一个函数')
}
let p1 = new Person()
/**
* 使用 new 结合 Person() 这个过程叫做 构造函数的实例化,最终会得到一个对象
* 在我们当前案例中,是将这个对象放在 变量p1 里面
* 所以有些开发管这个 p1 叫做 实例化对象,实例对象,本质上依然是一个对象
*/
console.log(p1.__proto__ === Person.prototype)
//__proto__属性指向自己构造函数的原型
//因为 p1 这个对象的构造函数是 Person,所以p1.proto__实际指向的是 Person这个函数的原型
原型的作用
- 把构造函数中 公共方法 提取出来,放在原型中
- 为什么这样做?
- 构造函数的原型上的方法或者属性,在每一个实例化对象中都能正常访问
对象的访问规则
- 访问对象的某一个属性时,会先在对象本身去查找,找到就直接使用,
- 如果没有找到,那么会去对象的__proto__中查找,找到就使用,
- 如果没有找到,那么会去这个对象(原型)的__proto__查找
- 直到查找到 JS 的顶层对象 Object.prototype,如果还没找到,就不再向上查找
案例
- 需求: 在 数组 上扩展一个方法 getMax,作用是求出数组中的最大值,并且要求所有数组都能使用
let arr = [1,2,3,4,5]
let arr2 = [6,7,8,9,10]
Array.prototype.getMax = function () {
//函数的 this 指向的是他的调用者,所以我们可以通过 this 拿到数组中的值
let max = this[0]
for(let i = 1; i <this.length; i++){
if(this[i] > max){
max = this[i]
}
}
console.log(max)
}
arr.getMax()
__proto__指向
function Person(name,age){
this.name = name
this.age = age
}
Person.prototype.sayHi = function(){
console.log('你好~~~')}
const p1 = new Person('gg',18)
console.log(p1.__proto__ === Person.prototype) // true
console.log(Person.__proto__ === Function.prototype) // true
console.log(Person.prototype.__proto__ === Object.prototype) // true
console.log(Function.__proto__ === Function.prototype) // true
console.log(Function.prototype.__proto__ === Object.prototype) // true
console.log(Object.__proto__ === Function.prototype) // true
console.log(Object.prototype.__proto__) // null
- p1 的 proto 指向谁
- proto 指向自己构造函数的原型
- 所以 相当于 指向了 Person.prototype
- p1.proto === Person.prototype
- Person 的 proto 指向谁
- Person 是一个构造函数, 构造函数本质上就是一个函数而已
- 在 JS 中,只要是一个函数,那么就属于构造函数 Function 的实例化对象
- proto 指向自己构造函数的原型
- 所以相当于 指向了 Function.prototype
- Person.proto === Function.prototype
- Person.prototype 的 proto 指向谁
- Person.prototype 是 Person 构造函数的原型, 本质上就是一个对象而已
- 在 JS 中, 只要是一个对象, 那么就属于构造函数 Object 的实例化对象
- proto 指向自己构造函数的原型
- 所以相当于指向了 Object.prototype
- Person.prototype.proto === Object.prototype
- Function 的 proto 指向谁
- Function 是一个构造函数, 构造函数本质上就是一个函数而已
- 在 JS 中, 只要是一个函数, 那么就属于构造函数 Function 的实例化对象
- proto 指向自己构造函数的原型
- 所以相当于 指向了 Function.prototype
- Function.proto === Function.prototype
- Function.prototype 的 proto 指向谁
- Function.prototype 是 Function 构造函数的原型, 本质上就是一个对象而已
- 在 JS 中, 只要是一个对象, 那么就属于构造函数 Object 的实例化对象
- proto 指向自己构造函数的原型
- 所以相当于指向了 Object.prototype
- Function.prototype.proto === Object.prototype
- Object 的 proto 指向谁
- Object 是一个构造函数, 构造函数本质上就是一个函数而已
- 在 JS 中, 只要是一个函数, 那么就属于构造函数 Function 的实例化对象
- proto 指向自己构造函数的原型
- 所以相当于 指向了 Function.prototype
- Object.proto === Function.prototype
- Object.prototype 的 proto 指向谁
- 因为 Object.prototype 是 JS 的顶层对象, 在往上就没有了, 所以这个值为 null
- console.log(Object.prototype.proto) null