面向对象回顾
对象的声明方式
使用new关键词
使用class
class Person{
constructor(name){
this.name = name
}
}
let person = new Person('joe')
使用构造函数
function Person{
this.name = name
}
let person = new Person('joe')
使用工厂函数
function factory(name){
this.name = 'joe'
this.say = function(){
console.log('hi')
}
}
let obj = factory('joe')
构造函数的缺陷
function Person{
this.name = 'joe'
this.say = function(){
console.log('hi')
}
}
let person1 = new Person()
let person2 = new Person()
console.log(person1 == person2)//false 两者调用方法不一致
console.log(person1.name == person2.name)
person1.say()//hi
person2.say()//hi
- person1和person2 的调用方法不一致
- 不一致说明person1里面有一个say的内存空间person2里面也有一个say的内存空间
- new多少次就有多少个内存空间(导致内存浪费)
- 如果要解决这个问题 那么在对应的构造函数的时候就不能创建对应的一个say的空间(对应的对象的函数定义不要放在构造函数中)
- 将所有的say只有一个空间(公共空间)
- 并且这个公共空间必须跟对应的构造函数产生联系(必须所有的实例都能调用)
原型
函数的原型(prototype)
概述
prototype是函数内的一个对象空间 每个函数都有一个 被称为显示原型
应用
对应的构造函数也属于函数 同样拥有一个prototype 有且只有一个 说明他在预编译的时候只声明一次 也就是prototype是对应构造函数的一个公共空间 只声明一次就可以解决构造函数的缺陷
function Person(){
this.name = 'joe'
}
// 构造函数的prototype空间
console.log(Person.prototype)
从上面可得对应的函数的prototype其实就是一个对象,且是个可以存储函数的对象
function Person{
this.name = 'joe'
}
//在构造函数的prototype中存入对应的函数
Person.prototype.say= function(){
console.log('hi')
}
let person1 = new Person()
let person2 = new Person()
console.log(person1.say === person2.say)//true
person1.say()//hi
person2.say()//hi
从上可知 对应的函数的prototype中存入的属性 可以直接通过对象实例.属性名来访问
总结
- 每个函数中都存在一个prototype的对象空间 构造函数也是函数也存在
- prototype在预编译时会进行一次空间开辟(只开辟一次)
- 利用prototype可以解决构造函数内存储的的函数开辟多个内存空间的问题
- prototype里面的方法 可以直接通过 实例对象.方法名 来访问
- 建议将对应的属性存入对应的构造函数 将对应的方法存入prototype
class的机制
将constructor外部声明的函数自动加入到原型中
// class的机制
// 将constructor外部声明的函数自动加入到原型中
class Animal {
constructor() {
}
//声明一个函数 在prototype里面
say() {
console.log('hello')
}
}
new Animal().say()
new Animal().say()
console.log(new Animal().say == new Animal().say)//true
对象的原型(__proto__)
__proto__是对象的一个对象空间(实例对象也属于对象),其指向对应的构造函数的prototype,被称为隐式原型
console.log({}.__proto__) //true
//对象的__proto__ 指向对应构造函数prototype
console.log(new Object().__proto__ == Object.prototype)//true
console.log({}.__proto__ == Object.prototype)//true
__proto__指向对应的构造函数的prototype 那么意味着如果在对象里的__proto__添加内容就是添加进prototype
//往对象的隐式原型中添加对应的内容 其实就是往构造函数的prototype中添加内容
new Person().__proto__.sayHello = () => console.log('hello')
new Person().sayHello()
//同一构造函数产生的实例对象的 __proto__的对象是一个
console.log(new Person().__proto__ == new Person().__proto__)//true
console.log(new Person().__proto__ == Person.prototype)
总结
__proto__是所有对象都拥有的一个对象空间,它指向对应的构造函数的prototype、
问题
- 对于引用数据类型来说所有的引用数据类型都是对象类型,那么对应的function是不是也是一个对象 ,构造函数他也是对象同样他是不是也拥有 __proto__ ,那么它的 __proto__ 又指向谁呢?
对应的函数的 __proto__ 指向对应的Funtion的构造函数的prototype
- 对于构造函数来说它有一个prototype属性它也一个对象空间,那么这个对象空间的 __proto__ 又指向谁呢?
Object的构造函数的prototype的 __proto__ 指向null (查找属性找到null 还没有返回
undefined)
原型链
概述
在 __proto__中寻找属性的过程中形成的链子 称为原型链
function Person() {
console.log(111)
}
let person = new Person()
console.log(Person.__proto__.__proto__.__proto__) //
//函数的 __proto__ ƒ () { [native code] } ==> Object ==> null
console.log(person.__proto__) //Person的prototype 对象
console.log(person.__proto__.__proto__) //对象的__proto__ (Object.prototype)
console.log(person.__proto__.__proto__.__proto__) //Object.prototype的__proto__==> null
// 找到对应null结束 Person -- Object -- null
原型链查找过程
- 对应的函数__proto__指向对应的funcction的构造函数prototype
- 对应的指向关系(原型链的查找)
先指向自身的构造函数prototype
再指向对应的父类的构造函数的prototype
再指向上级父类 直到找到Object的构造函数prototype
Object的构造函数的prototype的__proto__指向null (查找属性找到null 还没有 就返回undefined)
- 对象赋值不遵从原型链(如果存在属性那么对应的就是修改 如果没有就是添加)
模拟实现instanceOf
// 模拟实现instanceOf关键词
function MyInstanceOf(obj, con) {
while (obj.__proto__) {
obj = obj.__proto__
if (obj.constructor == con) {
return true
}
}
return false
}
console.log(MyInstanceOf(dog, Dog))
console.log(MyInstanceOf(dog, Animal))
console.log(MyInstanceOf(dog, Object))
console.log(MyInstanceOf(dog, Person))
function Person() {
this.name = 'aha'
}
console.log(new Person())
模拟实现new关键词
// 模拟实现new
function myNew(fn) {
// 自动创建对象
let obj = {}
// 将对应的obj 的原型指向对应的构造函数的原型
obj.__proto__ = fn.prototype
// 手动属性赋值
fn.call(obj)
// 自动返回
return obj
}
console.log(myNew(Person))
在于对应的一些内置对象里面的方法大部分放在原型上面
function fn() {
//遍历对应的arguments
//先将arguments转为数组再调用对应的方法
// return Array.from(arguments).reduce((prev,current)=>{
// return prev + current
// })
//reduce其实数组的原型方法
return Array.prototype.reduce.call(arguments, (prev, current) => {
return prev + current
})
}
console.log(fn(1, 2, 3, 4, 5))
模拟数组的高阶函数
forEach
//模拟forEach方法
Array.prototype.myForEach = function (callback) {
if (typeof callback != 'function') {
throw new Error('参数错误')
}
//遍历对应的数组 this当前调用的数组
for (let i = 0; i < this.length; i++) {
callback(this[i], i, this)
}
}
new Array(1, 2, 3).myForEach((v, i, arr) => {
console.log(v, i, arr)
})
map
//模拟map的方法
Array.prototype.myMap = function (callback) {
if (typeof callback != 'function') {
throw new Error('参数错误')
}
var arr = []
//遍历对应的数组 this当前调用的数组
for (let i = 0; i < this.length; i++) {
arr.push(callback(this[i], i, this))
}
return arr
}
let result = new Array(1, 2, 3).myMap((v, i, arr) => {
return v + 'haha'
})
console.log(result)
every
//模拟every的方法
Array.prototype.myEvery = function(callback){
if(typeof callback != 'function'){
throw new Error('参数错误')
}
//遍历对应的数组 this当前调用的数组
for(let i=0;i<this.length;i++){
if(!callback(this[i],i),this){
return false
}
}
return true
}
let result = new Array(1,2,3).myEvery((v,i,arr)=>{
return v>2
})
console.log(result)
some
//模拟some的方法
Array.prototype.mySome = function(callback){
if(typeof callback != 'function'){
throw new Error('参数错误')
}
//遍历对应的数组 this当前调用的数组
for(let i=0;i<this.length;i++){
if(callback(this[i],i),this){
return true
}
}
return false
}
let result = new Array(1,2,3).mySome((v,i,arr)=>{
return v>2
})
console.log(result)
filter
//调用filter的方法
Array.prototype.myFilter = function(callback){
if(typeof callback != 'function'){
throw new Error('参数错误')
}
var arr = []
for(let i =0;i<this.length;i++){
if(callback(this[i],i,this)){
arr.push(this[i])
}
}
return arr
}
let result = new Array(1,2,3).myFilter((v,i,arr)=>{
return v>2
})
console.log(result)
reduce
//模拟reduce的方法
Array.prototype.myReduce = function(callback,prev){
if(typeof callback != 'function'){
throw new Error('参数错误')
}
var index = 0
if(!prev){
if(this.length == 0){
throw new Error
}
index = 1
prev = this[0]
}
//遍历对应的数组 this当前调用的数组
for(;index<this.length;index++){
prev = callback(prev,this[index],index,this)
}
return prev
}
let result = new Array(1,2,3).myReduce((prev,v,i,arr)=>{
return prev+v
})
console.log(result)
继承
面向对象的三大特性
- 封装
- 继承
- 多态
继承的实现
class继承实现 extend是(es6)
class Animal{
constructor(){
this.name = 'joe'
}
say(){
console.log('hi')
}
static run(){
console.log('running')
}
}
class Cat extends Animal{
constructor(){
super() //父类构造
this.color = '黑色'
}
}
// 获取父类的属性和方法和属性
let cat = new Cat()
console.log(cat.name)
cat.say()
// 静态方法 也可继承
Cat.run()
构造函数的继承(继承不了静态的方法和属性)
原型链继承
将对应的父类对象放入到对应的子类构造的原型上
//构造函数继承无法继承对应的静态方法
// 父类构造
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.sayHi = function () {
console.log('hi')
}
Person.run = function () {
console.log('running')
}
// 子类构造
function Student(classNumber,name,age){
this.class = classNumber
}
// 原型链继承
// 将父类对象放在子类的原型链上 默认继承的元素属性在原型上显示undefined
// 缺点 不能进行初始化赋值操作(undefined)
// 覆盖子类原型 子类的原型只能在原型继承之后
Student.prototype = new Person()
缺点
- 不能进行初始化赋值操作(undefined)
- 覆盖子类原型(子类的原型方法只能在原型继承之后声明)
对象冒充
将对应的父类构造函数 当做普通函数执行 传入对应的子类构造中的this
// 对象冒充继承
// 将对应的父类构造函数 当做普通函数执行 传入对应的子类构造中的this
// 子类构造
function Student(classNumber, name, age) {
// 对象冒充
Person.call(this, name, age)
this.class = classNumber
}
缺点
- 获取不了原型上的内容
组合继承(原型链继承+对象冒充)
// 组合继承(原型链继承+对象冒充)
// 缺点 原型prototype上有重复的属性
// 对象冒充继承
// 将对应的父类构造函数 当做普通函数执行 传入对应的子类构造中的this
// 子类构造
function Student(classNumber, name, age) {
// 对象冒充
Person.call(this, name, age)
this.class = classNumber
}
// //原型链继承
Student.prototype = new Person()
let student = new Student('一班', 'joe', 18)//能打印对应的值
console.log(student)
student.sayHi() //'hi'
缺点
- 原型上有重复的属性
寄生组合继承
主要寄生原型(将父类原型对象加入到子类的原型上)+ 对象冒充
// 寄生组合继承
// 利用寄生原型+对象冒充
function Student(classNumber, name, age) {
Person.call(this,name,age)
this.class = classNumber
}
// 寄生原型
Student.prototype = Object.create(Person.prototype)
let student = new Student('一班', 'joe', 18)//能打印对应的值
console.log(student)
console.log(student instanceof Student)//true
student.sayHi() //'hi'
面向对象的轮播图
class Carousel {
//轮播图的属性
constructor(element) {
//切换的ul
this.toggleUl = element.querySelector('.toggleUl')
//焦点 容器
this.focusElement = element.querySelector('.focus')
//箭头
this.prevArrow = element.querySelector('.prev')
this.nextArrow = element.querySelector('.next')
//定时器
this.timer = null
//下标
this.index = 0
//原始个数
this.size = this.toggleUl.children.length
//记录li的宽度和高度
this.elementWidth = this.toggleUl.children[0].clientWidth
this.elementHeight = this.toggleUl.children[0].clientHeight
//方向 上下 (top)2 3 左右 (left)0 1
this.direction = 1
//初始化
this.init()
}
//移动方法
move(isAsc) {
let key = this.direction / 2 >= 1 ? 'top' : 'left'
this.distance = this.direction / 2 >= 1 ? this.elementHeight :
this.elementWidth
//判断index的值和方向 正向 ++ () 逆向 --
//正向
if (this.index >= this.size && isAsc) {
this.index = 0
//变成0的位置
this.toggleUl.style[key] = this.index * -1 * this.distance + 'px'
}
//逆向
if (this.index <= 0 && !isAsc) {
this.index = this.size
//变成最后的位置
this.toggleUl.style[key] = this.index * -1 * this.distance + 'px'
}
//控制对应的index进行变化
isAsc ? this.index++ : this.index--
//移动操作
let targetObj = {
top: {
top: this.index * -1 * this.distance
},
left: {
left: this.index * -1 * this.distance
}
}
bufferAnimation(this.toggleUl, targetObj[key])
//焦点变化
this.toggleFocus()
}
//轮播方法
autoMove() {
this.timer = setInterval(() => {
this.move(true)
}, 2000)
}
//焦点切换的方法
toggleFocus() {
//主要焦点的选择
//排他
Array.prototype.forEach.call(this.focusElement.children, (v) => {
v.className = ''
})
this.focusElement.children[this.index % this.size].className = 'selected'
}
//处理点击事件的方法
//处理焦点点击
handlerFocusClick() {
Array.prototype.forEach.call(this.focusElement.children, (v, i) => {
v.onclick = () => {
this.index = i - 1
//移动过去
this.move(true)
}
})
}
//处理箭头点击
handlerArrowClick() {
this.prevArrow.onclick = () => {
this.move(false)
}
this.nextArrow.onclick = () => {
this.move(true)
}
}
//初始化方法
init() {
let
_this = this
//初始化焦点
//声明index记录对应的图片位置
//根据图片的个数去生成对应的焦点
Array.from(this.toggleUl.children).forEach((v, i) => {
let li = document.createElement('li')
li.innerText = i + 1
//给第一个添加对应的selected
if (i == 0) {
li.className = 'selected'
}
//再将声明的li添加给ul
_this.focusElement.appendChild(li)
})
//在最后一张后面添加一个第一张
this.toggleUl.appendChild(this.toggleUl.firstElementChild.cloneNode(true))
//加入点击事件
this.handlerFocusClick()
this.handlerArrowClick()
//调用自动轮播
this.autoMove()
}
}