一.面向对象是什么 ?
- => 并不是一个语法, 也并不是一个新的语言, 他是用js完成 需求 的一种思想
- => 我们一开始学习的解决问题的方式 统称为 面向过程
面向过程 :
- 注重的是过程, 每一步 事无巨细, 全都有我们代码从上往下
- 问题 : 每一个功能之间会 互相影响
- 拿轮播图举例, 如果有多个, 那么这个代码 需要重复的写多次
面向对象 :
- 注重的是得到一个对象, 这个对象就是我们的需要, 比如说这个对象可以是一个轮播图,也可以是一个分页
二.创建对象的方式 ?
- 字面量方式 : 不利于批量创建
// 1.字面量方式, 不利于批量创建
let obj = {
name : 'qwe',
age :18
}
let obj1 = {
name : 'qwe',
age :18
}
let obj2 = {
name : 'qwe',
age :18
}
- 内置构造函数 : 不利于批量创建
// 2. 内置构造函数
let obj3 = new Object()
let obj4 = new Object()
let obj5 = new Object()
- 工厂函数的方式(开发者思考出来的一种方式) - 其实就是创建一个函数, 但是函数内部可以创建一个对象, 我们把这种函数叫做工厂函数
/ 3. 工厂函数的方式
function creatObj(num){
// 3.1 手动创建一个方式
const obj = {}
// 3.2 手动给对象上添加 一些属性
obj.name = 'qwe' // 添加一个固定的字符串, 每次创建对象的属性值都是 'qwe'
obj.age = 20 // 写法一 (写死值)
obj.age = num // 写法二 (可以通过参数), 利用形参多给对象的属性赋值一个'变量',
// 3.3 手动给对象返回
return obj
}
let objA = creatObj(22)
console.log(objA);
let objB = creatObj(23)
console.log(objB);
三.构造函数的书写 :
- 自定义构造函数 ( 构造函数 )
什么是构造函数 ?
- 本质上就是一个 普通函数
- => 通过在调用的时候, 前边加一个关键字 new, 那么我们把这种函数叫做构造函数
构造函数的书写 :
- 构造函数的函数名 首字母是大写(建议,非强制, 目的就是为了和普通函数做一个区别)
- 构造函数内 不要写 return
- 如果 return 是一个基本数据类型, 写了也没用(写不写都一样, 都会返回实例对象)
- 如果 return 是一个引用数据类型, 写了就会导致构造函数没用(写了会返回return后写的内容)
- 构造函数调用时, 必须和 new 关键字连用
- 如果不连用, 也能调用, 但是构造函数就没用了
- 构造函数内部的 this 指向
- 当一个函数 和 new 关键字连用的时候, 那么我们说这个函数是 构造函数, 然后这个内部 this 指向本次调用被自动创建出来的那个对象
- 构造函数不能使用 箭头函数
- 因为箭头函数 内部没有this
面试题 : 构造函数有什么注意点 ?
- 函数名 首字母大写
- 不用return
- 调用时, 和 new 关键字 (new 函数名)
- 通过this 指向访问该对象属性
- 工厂函数方式 ( 手动 )
function creatObj(num){
// 3.1 手动创建一个方式
const obj = {}
// 3.2 手动给对象上添加 一些属性
obj.name = 'qwe'
obj.age = num
// 3.3 手动给对象返回
return obj
}
- 构造函数方式
function creatObj1(num){
// 1. 自动创建一个对象 (通过this访问)
// 2. 手动给对象添加一些属性
this.name = 'qwe'
this.age = num
// 3. 自动返回对象
}
- 构造函数书写
// 构造函数的书写
function Person(name,age){
// 因为构造函数自动创建出来的对象可以通过 this 来访问, 所以我们需要想这个对象添加属性的时候, 可以通过 this 来添加
this.name = name
this.age = age
this.a = '我是随便添加的一个属性a, 我是固定的内容'
}
const p1 = new Person('zxc',18)
console.log(p1);
const p2 = new Person('qaz',26)
console.log(p2);
四.构造函数的缺点 ?
- 构造函数内部 如果有引用数据类型
- 在很多次调用构造函数时, 每一次都会重新创建一个函数, 会造成内存空间浪费
function Person(name,age){
// 因为构造函数自动创建出来的对象可以通过 this 来访问, 所以我们需要想这个对象添加属性的时候, 可以通过 this 来添加
this.name = name
this.age = age
this.a = '我是随便添加的一个属性a, 我是固定的内容'
this.fn = function(){
console.log('我是 fn 函数');
}
}
const p1 = new Person('asd',22)
const p2 = new Person('qwe',24)
console.log('两个对象内部的函数 是否为同一个地址' ,p1.fn === p2.fn); // flase 不是同一个地址
- 如何处理 ?
-
- 在构造函数外, 封装一个函数, 在构造函数内调用
-
function winfn(){
console.log('我是 fn 函数');
}
function Person(name,age){
// 因为构造函数自动创建出来的对象可以通过 this 来访问, 所以我们需要想这个对象添加属性的时候, 可以通过 this 来添加
this.name = name
this.age = age
this.a = '我是随便添加的一个属性a, 我是固定的内容'
this.fn = winfn
}
const p1 = new Person('asd',22)
const p2 = new Person('qwe',24)
console.log('两个对象内部的函数 是否为同一个地址' ,p1.fn === p2.fn); // true 共用同一个地址
五. 原型 ( 原型空间 / 原型对象)
什么是原型 ?
- 每一个函数天生拥有一个 属性 prototype(原型), 他的属性是一个对象
- 我们通常把这个对象叫做 这个函数的原型(空间|对象)
- prototype 这个对象中有一个属性 constructor, 表示 当前这个原型是哪个 函数的原型
如何使用原型中的属性 ?
- 每一个对象 天生拥有 一个原型 proto(前后两个下划线)
- 这个属性指向自己构造函数的原型
function Person() { }
// 向 Person 函数的原型(对象)上 添加一个属性 age, 值为 18
Person.prototype.age = 18
Person.prototype.name = '千锋'
Person.prototype.fn = function () {
console.log('我是添加在 Person 函数的原型对象上 的一个函数')
}
Person.prototype.abc = '我是添加到 Person 函数的原型上的一个字符串, 你可以通过 Person 创建出来的对象上的 __proto__ 查看'
let p1 = new Person()
/*
使用 new 结合 Person() 这个过程叫做 构造函数的实例化, 最终会得到一个对象
在我们当前案例中, 是将这个对象放在 变量 p1 里边
所以有些开发管这个 p1 叫做 实例化对象, 实例对象, 本质上依然是一个对象
*/
// console.log(p1.__proto__)
console.log(p1.__proto__ === Person.prototype)
/*
__proto__ 属性指向自己构造函数的原型
因为 p1 这个对象的构造函数 是 Person
那么也就是说, p1.__proto__ 实际指向的就是 Person 这个函数的原型
*/
原型作用 ?
- 就是把构造函数中 公共方法 提取出来, 放在原型中
- 为什么这样做 ?
- 构造函数的原型上的方法或属性, 在每一个实例化对象中 都能正常访问
对象访问规则 ( 面试题 ) ?
访问对象的某一个属性, 会在对象本身去查找, 找到直接使用
- 如果没有找到, 那么就回去 对象的 proto 中查找, 找到直接使用
- 如果还没有找到, 那么就会去这个对象(原型) 的 proto 查找
- 一直查找到 js 顶级对象 Object.proytype, 如果还没找到, 不在向上查找, 就返回undefined
六. 扩展内置构造函数
- new Object()
- new Array()
- new RegExp()
- new String()
js 内置数据结构
- 在 js 中, 任何一个数组, 他的构造函数 都是 Array
- 在 js 中, 任何一个对象, 他的构造函数 都是 Object
- Array.prototype
-
需求 : 在数组上扩展一个方法, 需求, 所有数组都能调用
Array.prototype.abc = function () {
console.log('我是扩展在 Array 构造函数的原型上的一个函数')
}
let arr = [1, 2, 3, 4, 5]
// console.log(arr.__proto__)
arr.abc()
-
需求 : 在数组上 扩展一个方法 getMax, 作用是求出数组中的最大值, 并且要求所有数组
let arr = [1, 2, 3, 4, 5]
let arr2 = [1, 2, 3, 4, 5, 6, 7, 8]
Array.prototype.getMax = function () {
// console.log(this);
// 函数的 this 指向他的调用者, 所以我们可以通过 this 拿到数组中能够的值
let max = this[0]
for (let i = 1; i < this.length; i++) { // i = 0 就是第一个自己, 没必要和自己比
if (this[i] > max) {
max = this[i]
}
}
console.log(max);
}
arr.getMax()
arr2.getMax()
七. 课堂练习
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.sayHi = function () {
console.log('你好~~~')
}
const p1 = new Person('QF001', 18)
- p1 的 proto 指向谁
- p1.proto === Person.prototype
- ptoto 指向自己构造函数的原型
- p1.proto === Person.prototype
-
- Person 的 proto 指向谁
- Person.proto === Function.prototype
- Person 是一个构造函数, 构造函数本质就是一个 函数而已
- 在 js 中, 只要是一个函数, 那么就属于构造函数 Function 的实例化对象
- proto 指向了 自己构造函数的原型, 所以相当于 指向了 Function.prototype
-
- Person.prototype 的 proto 指向谁
- Preson.prototppe.proto === Object.prototype
- Person.prototype 是 Person 的构造函数原型, 本质上就是一个 对象而已
- 在 js 中, 只要是一个对象, 那么就属于构造函数 Object 的实例对象
- proto 指向自己构造函数的原型, 所以相当于 指向了 Object.prototype
-
- Function 的 proto 指向谁
- Function.proto === Function.protype
- function 是一个构造函数, 构造函数本质上是一个 函数而已
- 在 js 中, 只要是一个函数, 那么就属于构造函数 Function 的实例化对象
- proto 指向自己构造函数的原型, 就相当于 指向了 Function.prototype
-
- Function.prototype 的 proto 指向谁
- Function.prototype.protype === Object.prototype
- Function.prototype 是 Function 构造函数的原型, 本质上就是一个对象而已
- 在 js 中, 只要是一个对象, 那么就属于构造函数 Object 的实例化对象
- proto 指向自己构造函数的原型, 所以相当于 指向了 Object.prototype
-
- Object 的 proto 指向谁
- Object.proto === Function.prototype
- Object 是一个构造函数, 构造函数本质上就是一个 函数而已
- 在 js 中, 只要是一个函数, 那么就属于构造函数 Function 的实例化对象
- proto 指向自己构造函数原型, 所以相当于 指向了 Function.prototype
-
- Object.prototype 的 proto 指向谁
- Object.proto.proto === null
- 因为 Object.prototype 是 js 的顶级对象, 再往上就没有了, 所以这个值为 null
八. 选项卡案例 - 面向对象版
分析 :
- 面向对象 就是寻找一个机器,这个机器能够帮助我们批量生产 选项卡
- 目前来看没有这个机器, 那么我们就需要 自己创建一个 机器
- 思考 :
- 这个机器需要什么 ?
- 需要获取元素
- 给我们获取的元素添加 对应的事件