面向对象
一、认识面向对象
概念:首先要确定 这个面向对象不是让你看着你对象 也不是一个语法 他其实是一种编程思想。
功能:去吃一碗面
面向过程的思想(自己做饭):
1. 和面
2. 切面
3. 烧水
4. 煮面
5. 炒个菜
6. 吃面
面向对象的思想(外面吃或者点外卖):
1.找个面馆
2.下单
3.吃面
eg:之前所学的轮播图、分页、瀑布流都属于面向过程 里面的代码之间都具有一定的关联性
面向对象:找一个"面馆" 他能生产一碗面,然后我们通过 这个"面馆"得到一碗面,如果有这个面馆 那我们直接使用 如果没有 我就自己开一家面馆 下次要吃面的话 就直接下单就好了。
找一个'机器'这个机器能生产产品 这个产品就是轮播图。
如果有这个机器 我们直接通过这个机器去得到轮播图 如果没有这个机器 我们需要自己创建一个机器
面向过程 => 脸朝着过程;编程时注重过程
一切功能都是一步一步来的
面向对象 => 脸朝着
轮播图: 面向过程的思想
创建第一个轮播图:
- const banner = XXX
- const imgBox =xXX
- const focus =XXX
- const index = xxx
- function copyELe {操作imgBox}
- function setFocus o{操作focus)
- function autoPLay o){操作imgBox和index}
创建第二个轮播图:
- const banner2 = XXX
- const imgBox2 =XXX
- const focus2 =XXX
- const index2 = XXX
- function copyELe2o{操作imgBox2}
- function setFocus2){操作focus2}
- function autoPLay2){操作imgBox2和index2]
面向对象的思想
b1 = { banner: xxx,
imgBox = xxx,
focus = xxx,
index = xxx,
copyEle () {操作 this 的 imgBox},
setFocus () {操作 this 的 focus},
autoPlay () {操作 this 的 imgBox 和 this 的 index}
}
b2 = { banner: yyy,
imgBox = yyy,
focus = yyy,
index = yyy,
copyEle () {操作 this 的 imgBox},
setFocus () {操作 this 的 focus},
autoPlay () {操作 this 的 imgBox 和 this 的 index}
}
对象的 属性值不相同, 但是 方法 相似但不同
机器的核心 能够批量生产
今天学的目标,
-
找到一个能批量生产对象的机器 -
并且是合理生产
const obj = {
a: 1,
b: 2,
fn() {
console.log(this.a)
}
}
obj.fn()
二、工厂模式
-
字面量方式
- 概念:不可以批量创建,所以不行。
-
内置构造函数
- 不能批量创建,所以不行。
-
工厂函数方式创建对象
- 工厂函数,其实就是一个函数。
- 步骤:第一步,手动给对象添加对象;第二步:手动返回一个对象;第三步:手动返回这个对象。
- 这个方法能能够创建对象,并且批量生产也可以。
-
自定义构造函数
- 自定义构造函数, 本质是就是一个函数
- 当他和 new 关键字连用的时候, 就叫做构造函数
- 步骤:第一步:自动创建一个对象;第二步:手动向创建的对象中添加属性;第三步:自动返回一个对象。
1. 字面量方式
// 同样的属性属性值 多次调用 多次赋值 多次创建对象
const obj11 = {
name:'Kack',
sex:'男',
age:18,
}
console.log(obj11);
const obj12 = {
name:'Kack',
sex:'男',
age:18,
}
console.log(obj12);
const obj13 = {
name:'Kack',
sex:'男',
age:18,
}
console.log(obj13);
2. 内置构造函数
// 同样的属性属性值 多次调用 多次赋值 多次创建对象
const obj21 = new Object();
obj21.name = 'Kack';
obj21.sex = '男';
obj21.age = 18;
console.log(obj21);
const obj22 = new Object();
obj22.name = 'Kack';
obj22.sex = '男';
obj22.age = 18;
console.log(obj22);
const obj23 = new Object();
obj23.name = 'Kack';
obj23.sex = '男';
obj23.age = 18;
console.log(obj23);
3. 工厂函数方式创建对象
// 工厂模式创建对象
function createObj(name , sex , age){
// 1.手动创建一个一个空对象
const obj3 = {};
// 2.向空对象添加属性
// 由于对象里面的属性此时是固定的 我们可以将键名 键值 转化为变量的形式存储在形参中
obj3.name = name;
obj3.sex = sex;
obj3.age = age;
// 3.返回对象
return obj3;
}
// 4.调用函数 多次赋值 多次调用
console.log(createObj('name' , 'Kack' , 18));
console.log(createObj('name' , 'Jack' , 28));
console.log(createObj('name' , 'Sack' , 38));
4. 自定义构造函数
三、自定义构造函数
自定义构造函数的书写:
- 一定是和 new 关键字 连用,如果没有和 new 连用, 那么他就是一个普通函数;
- 当一个函数和 new 关键字连用的时候, 这个函数就被称为自定义构造函数, 这个函数内的 this 指向, 指向返回出来对象;
- 构造函数不能使用给箭头函数,因为箭头函数内部没有 this;
- 构造函数内部不需要 return;
- return 了基本数据类型, 写了和没写一样
- return 了引用数据类型, 写了构造函数就没用了
5.书写构造函数时, 首字母建议大写,目的: 仅仅是为了和普通函数区分;
- 我们构造函数 通过 new 关键字 创建出来的 对象, 叫做 实例化对象, 本质上还是一个对象, 只不过名字上叫做实例化对象(实例对象),我们把 构造函数通过 new 关键字 创建对象 的过程 叫做 实例化。
function CreateObj(name , sex , age){
// this指向的是CreateObj
// this.name = 'Jack';
// this.sex = '男';
// this.age = '女';
this.name = name;
this.sex = sex;
this.age = age;
}
// 通过new关键字创建一个实例化对象
// 实例化对象中的this指向构造函数
const oO1 = new CreateObj('Jack' , '男' , 18);
const oO2 = new CreateObj('Wack' , '男' , 22);
console.log(oO1);
console.log(oO2);
四、认识构造函数不合理的地方
/*
认识构造函数不合理的地方
*/
function fn(){
console.log(11111);
}
function CreateObj(name , age , sex){
this.name = name;
this.age = age;
this.sex = sex;
/*
这样写 实际功能也能完成, 并且在多次创建的时候
每次给 this.fn 赋值时, 都会去找到 fn 函数
然后多个对象的 this.fn 的引用地址 都是一个
*/
// this.fn = fn;
/*
这样写 实际功能也能完成, 但是多次创建对象时
会多次创建 功能代码完全相同一个函数
这对内存空间是一种浪费
*/
this.fn = function(){
console.log(999);
}
}
const newObj = new CreateObj('Jack' , 18 , '男');
console.log(newObj);
五、原型
- 每一个函数都有一个 原型(是一个空间,或者是一个对象, 内部能存储一些东西);
- 构造函数, 本质上也是一个函数, 所以他也有这个原型; *
- 原型内部都有一个 constructor ,这个属性表明当前这个原型, 是那个函数的 *
- 函数访问原型; 函数.prototype *
- 每一个对象都有一个 proto(两个下划线), 可以去访问到自己构造函数的原型;
- 实例化对象, 本质上也是一个函数, 所以他可以访问到自己构造函数的原型
- 对象访问原型: 对象.proto
- 对象的访问规则, 现在当前作用域(对象内部)查找, 找到就使用
- 如果没找到, 则会顺着 proto 向上查找
构造函数函数体内, 通常写属性
造函数的原型内, 通常写方法(函数)
构造函数的原型内部添加方法, 并不是为了给构造函数使用
通常是为了给实例化对象使用
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.a = '我是后续通过 JS 代码添加到函数的原型内部的属性'
Person.prototype.sayHi = () => {
console.log('你好')
}
// console.log(Person.prototype) // 打印函数 Person 的原型
const p1 = new Person('QF001', 18)
const p2 = new Person('QF002', 28)
p1.sayHi()
console.log(p1.__proto__)
console.log(p1.a)
console.log(p1.__proto__ === Person.prototype) // 打印对象的构造函数的原型
console.log(p1.sayHi)
console.log(p2.sayHi)
console.log(p1.sayHi == p2.sayHi)
每一个函数 都有一个属于自己的原型
作用:
- 我们会在原型内部放一些公共的方法, 目的不是为了让构造函数去使用, 而是为了让实例化对象去使用
// function Person(name) {
// this.name = name
// }
// Person.prototype.sayHi = () => {
// console.log(111)
// }
// const p = new Person('QF666')
// p.sayHi()
// const arr = new Array()
// const obj = new Object()
// const reg = new RegExp()
// const date = new Date()
// console.log(arr)
// console.log(obj)
// console.log(reg)
// console.log(date)
// 找到数组构造函数的原型
// const arr = new Array()
// console.log(arr.__proto__)
// console.log(Array.prototype)
// // 找到对象构造函数的原型
// console.log(Object.prototype)
/**
* 需求: 给数组扩展一个方法 abc 让他能够打印 123
*
* 任何一个数组的构造函数 都是 Array
* 任何一个对象的构造函数 都是 Object
* 任何一个函数的构造函数 都是 Function
*/
// Array.prototype.abc = () => {
// console.log(123)
// }
// const arr = new Array()
// const arr1 = new Array()
// arr.abc()
// arr1.abc()
/**
* 需求: 给数组扩展一个 求最大值的方法
*/
Array.prototype.getMax = function () {
let max = this[0]
for (let i = 1; i < this.length; i++) {
if (max < this[i]) {
max = this[i]
}
}
return max
}
const arr1 = new Array(1, 2, 100, 5, 3, 4)
const max = arr1.getMax()
console.log(max)
const arr2 = new Array(1, 2, 100, 5, 3, 4, 999)
const max2 = arr2.getMax()
console.log(max2)
六、万物皆对象
/**
* JS 中, 万物都可以都可以称为对象
* 对象:
* 含义1: 一种数据格式 {key: value, key2: value2}
* 含义2: 某一类事务的实例 (某一类内容中的 真实个体)
*
*
* const arr = [1, 2, 3]
* const arr1 = [1, 2, 3, 4]
* const arr2 = [1, 2]
* 这个 arr1 就是 Array 这一类内容中的 某一个 真实个体, 数组也可以算作一个对象(Array 这一类事务中的一个个体)
*
* const fn = () => {console.log(1)}
* const fn2 = () => {console.log(2)}
* const fn3 = () => {console.log(3)}
* 函数也是一个对象(属于 Function 这一类事务中的一个个体)
*
* 如果一个数据 [] 那么他就是Array 这个对象中的某一个个体
* 如果一个数据 {} 那么他就是Object 这个对象中的某一个个体
* 如果一个数据 function(){} 那么他就是 Function 这个对象中的某一个个体
*/
const arr1 = [1, 2, 3]
const arr2 = new Array(4, 5, 6)
七、原型链
原型链
*
* 查找对象的某一个属性:
* 先在对象内部开始查找, 找到直接使用, 然后停止查找
* 如果没有找到, 会找对象的 obj.__proto__, 如果找到直接使用, 然后停止查找
* 如果这里没找到, 会继续去对象的 __proto__ 查找, 找到直接使用, 然后停止查找
* 如果还是没找到, 会继续向上查找
* ...
* 直到找到顶层作用对象 Object.prototype, 找到就是用, 找不到 undefined
*/
function Person (name) {
this.name = name
}
Person.prototype.sayHi = function () {
console.log(100)
}
const p = new Person('QF001')
// console.log(p)
/**
* 问题1: p 的 __proto__ 指向谁?
* + p 是 Person 的实例化对象
* + __proto__ 指向自身构造函数的原型
* + p.__proto === Person.prototype
*
* 问题2: Person 的 __proto__ 指向谁?
* + Person 是构造函数, 本质上函数
* + 只要是一个函数, 他就是 Function 的实例
* + Person.__proto__ 指向了 他的构造函数的原型, 构造函数是 Function, 那么构造函数的 原型 Function.prototype
* + Person.__proto__ === Function.prototype
*
* 问题3: Person.prototype 的 __proto__ 指向谁?
* + Person.prototype 其实就是 构造函数 Person 的原型对象, 本质上就是对象
* + 只要是一个 对象, 他就是 Object 的实例
* + Person.prototype.__proto__ 指向了 他的构造函数的原型, 构造函数 Object, 那么构造函数的原型 Object.prototype
*
* 问题4: Function 的 __proto__ 指向谁?
* + Function 是构造函数, 本质上就是一个函数
* + 只要是一个函数, 他就是 Function 的实例
* + Function.__proto__ 指向了 他的构造函数的原型, 构造函数 Function, 那么 构造函数的原型 Function.prototype
* + Function.__proto__ === Function.prototype
*
* 问题5: Function.prototype 的 __proto__ 指向了谁?
* + Function.prototype 其实就是 构造函数 Function 的原型对象, 本质上是对象
* + 只要是一个对象, 他就是 Object 的实例
* + Function.prototype.__proto__ 指向了 他的构造函数的原型, 构造函数 Object, 那么构造函数的原型 Object.prototype
* + Function.prototype.__proto__ === Object.prototype
*
* 问题6: Object 的 __proto__ 指向了谁?
* + Object 是一个构造函数, 本质上还是一个函数
* + 只要是一个函数, 那么他的构造函数 就是 Function
* + Object.__proto__ 指向了他的构造函数的原型, 他的构造函数是 Function, 那么构造函数的原型 Function.prototype
* + Object.__proto__ === Function.prototype
*
* 问题7: Object.prototype 的 __proto__ 指向了谁?
* + Object.prototype 是构造函数 Object 的原型对象, 本质上就是一个对象
* + 但是重点: Object.prototype 是 JS 顶层的对象
* + Object.prototype.__proto__ === null
*/
console.log(p.__proto__ === Person.prototype)
console.log(Person.__proto__ === Function.prototype)
console.log(Person.prototype.__proto__ === Object.prototype)
console.log(Function.__proto__ === Function.prototype)
console.log(Function.prototype.__proto__ === Object.prototype)
console.log(Object.__proto__ === Function.prototype)
console.log(Object.prototype.__proto__)
八、判断数据类型
判断数据类型
* 1. typeof 判断基本数据类型
*
* 2. constructor 可以判断 当前数据的 构造函数 是谁
* 问题1: 能够判断引用数据类型
* 但是! 这个属性其实就是对象内的一个属性
* 我们可以拿到这个对象, 然后修改他的属性值
* 问题2: 不能判断 undefined 和 null
*
* 3. instanceof 可以判断 左边的构造函数是否等于右边的构造函数
* 语法: 检测的数据 instanceof 构造函数
* 问题: 不能判断 undefined 和 null
*
* 4. Object.prototype.toString.call(要判断的数据结构)
* Object 这个构造函数的 原型内部 有一个 toString 的方法
* 这个方法能够帮我们将数据结构转为字符串的形式 '[object 数据结构]'
*
* 我们在使用的时候 如果需要判断其他数据类型, 需要使用 .call这个方法改变内部 this 指向
*
* 这个方法任何数据类型都能准确判断(推荐使用)
*/
// 1. typeof
// console.log(typeof('123'))
// console.log(typeof(123))
// console.log(typeof(true))
// console.log(typeof(undefined))
// console.log(typeof([1, 2, 3]))
// console.log(typeof({a:1}))
// 2. constructor
// const arr = [1, 2, 3]
// console.log(arr.__proto__.constructor) // constructor 指向当前这个原型是那个构造函数的原型
// console.log(arr.constructor) // constructor 指向当前这个原型是那个构造函数的原型
// console.log(arr.constructor === Array) // true
// console.log(arr.constructor === Function) // false
// console.log(arr) // 数组
// console.log(arr.__proto__) // 自己构造函数的原型对象
// console.log(Array.prototype) // 构造函数 Array 的原型对象
// console.log(Array.prototype.constructor) // 构造函数 Array 的原型对象
// Array.prototype.constructor = { a: 1 }
// console.log(Array.prototype.constructor)
// console.log(arr.__proto__.constructor === Array)
// console.log(undefined.constructor) // undefined 内部没有 constructor 所以我么不能这样去判断
// console.log(null.constructor) // null 内部 没有 constructor 所以我们不用这样去判断
// const obj = {
// a: 1
// }
// obj.a = 100
// console.log(obj)
// const arr = [1, 2, 3]
// const obj = {
// a: 1,
// b: 2
// }
/**
* instanceof
* 判断左边的构造函数是否等于右边的
*/
// console.log(arr instanceof Array)
// console.log(arr instanceof Function)
// console.log(obj instanceof Array)
// console.log(obj instanceof Object)
// Array.prototype.constructor = { a: 1 }
// console.log(arr instanceof Array)
// console.log(undefined instanceof Array)
// console.log(undefined instanceof Object)
// console.log(undefined instanceof Function)
// 4.
const arr = [1, 2, 3]
const obj = {
a: 1,
b: 2
}
/**
* Object 这个构造函数的 原型内部 有一个 toString 的方法
* 这个方法能够帮我们将数据结构转为字符串的形式 '[object 数据结构]'
*
* 我们在使用的时候 如果需要判断其他数据类型, 需要使用 .call这个方法改变内部 this 指向
*
* 这个方法任何数据类型都能准确判断(推荐使用)
*/
console.log(Object.prototype.toString.call([]))
console.log(Object.prototype.toString.call(function () { }))
console.log(Object.prototype.toString.call(undefined))
console.log(Object.prototype.toString.call(null))