day19 原型和继承

面向对象的回顾

对象的声明方式

使用new关键词

使用class

class Person{
    constructor(name){
        this.name = name
    }
}
let person =  new Person('tom')

使用构造函数

function Person(name){
    this.name = name
}
let person = new Person('jack')

使用工厂函数

function factory(name){
    let obj = new Object()
    obj.name = name
    return obj
}
let obj = factory('rose')

构造函数的缺陷

function Person(){
    this.name = 'jack'
    this.sayHello = function(){
        console.log('hello')
    }
}
let person1 = new Person()
let person2 = new Person()
console.log(person1.name == person2.name)
person1.sayHello()//hello
person2.sayHello()//hello
console.log(person1.sayHello == person2.sayHello)//false person1的调用的方法和person2调用的不//一致
  • person1调用的方法和person2调用的不一致
  • 不一致就说明person1里面有个sayHello的内存空间 person2里面也有一个sayHello内存空间
  • 那么就是new多少次就会创建多少次的sayHello的内存空间(内存浪费)
  • 也就是说我们要将所有的sayHello只有一个空间(公共空间)
  • 综上所述 对应的这个公共空间必须跟对应的构造函数产生联系(必须所有实例都能调用)

 原型

函数的原型(prototype)

概述

prototype是函数内的一个对象空间,每个函数都有一个,他被称为显式原型.

应用

  • 对应的构造函数也属于函数 那么他同样也拥有一个prototype,且对应的prototype只有一个也就是说他会在预编译的时候声明一次,也就是prototype是对应的一个构造函数的公共空间,且他只声明一次 那么也就是说他就可以解决对应的构造函数的缺陷了
    function Person(){
        this.name = 'jack'
    }
    //构造函数的prototype空间
    console.log(Person.prototype)
  • 从上可得对应的函数的prototype这个空间其实是一个对象,那么我们就可以在这个对象里存入对应的函数
    function Person(){
        this.name = 'jack'
    }
    Person.prototype.sayHello = function(){
        console.log('hello')
    }
    let person1 = new Person()
    person1.sayHello()
    let person2 = new Person()
    person2.sayHello()
    console.log(person1.sayHello === person2.sayHello)//true
  • 以上代码发现 对应的函数的prototype中存入的属性 可以直接通过对应的实例.属性来访问

总结

  • 每个函数内都存在一个prototype的对象空间 构造函数也是函数所以它也存在
  • prototype这个空间会在预编译的时候进行开辟(只开辟一次)
  • 利用prototype可以解决构造函数内存储的函数开辟多个内存空间的问题
  • prototype里面的方法 可以直接通过对应的实例对象.方法名来访问
  • 建议将对应的属性存入对应的构造函数 将对应的方法存入prototype

class的机制

将constructor外部声明的函数自动加入到原型中

class Animal{
    constructor(){
    }
    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__)
//对象的__proto__指向对应构造函数prototype
console.log(new Object().__proto__.__proto__ == Object.prototype)
console.log({}.__proto__ == Object.prototype)

__proto__指向对应的构造函数的prototype那么也就意味着如果往对象的__proto__里面添加内容,其实就是往构造函数的prototype中添加.

//往对象的隐式原型中添加对应的内容 其实就是往构造函数的prototypr中添加内容
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__又指向谁?
  • 对于构造函数来说他有一个prototype属性他也有一个对象空间,那么这个对象空间的__proto__又指向谁?

原型链

概述

在__proto__中寻找属性的过程形成的链子 被称为原型链

示例

function Person(){
    console.log(111)
}
let person = new Person()
comsole.log(Person.__proto__.__proto__.__proto__)
//函数的__proto__ f(){[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.prototypede__proto__==>null
//找到对应null结束 Person -- Object -- null

原型链查找过程

  •  对应的函数的__proto__指向对应的Function的构造函数的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
}
function Person(){

}
console.log(myInstanceOf(dog,Dog))
console.log(myInstanceOf(dog,Animal))
console.log(myInstanceOf(dog,Object))
console.log(myInstanceOf(dog,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

Array.prototype.myForEach = function(callback){
            if(typeof callback!='function'){
                throw new Error()
            }
            for(let i =0;i<this.length;i++){
                callback(this[i],i,this)
            }
        }
        new Array(1,3,4,7).myForEach((v,i,arr)=>{
            console.log(v,i,arr)
        })

reduce

Array.prototype.myReduce = function(callback,previous){
            if(typeof callback!='function'){
                throw new Error()
            }
            let index = 0
            if(!previous){
                previous = this[0]
                index = 1
            }
            for(;index<this.length;index++){
                previous = callback(previous,this[index],index,this)
            }
            return previous 
        }
        let result = new Array(1,4,5,6,7).myReduce((prev,v,i,arr)=>{
            return prev+v
        })
        console.log(result)

继承

面向对象三大特性

  • 封装
  • 继承
  • 多态

继承的实现

class继承实现extends(es6)

class Animal{
    constructor(){
        this.name = 'jack'
    }
    say(){
        console.log('hello')
    }
    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.sayHello = function(){
    console.log('hello')
}
Person.run = function(){
    console.log('running!!!!!!!!!')
}
function Student(classNumber,name,age){
    this.class = classNumber
}
//原型链继承
//将父类对象放在子类的原型链上 默认继承的元素的属性在原型上显示为undefined
//缺点 不能进行初始化赋值操作(undefined)
//覆盖子类原型 子类的原型只能在原型继承之后
Student.prototype = new Person()

缺点

  • 不能进行初始化赋值操作(undeveloped)
  • 覆盖子类原型(子类的原型方法只能在原型继承之后声明)

对象冒充

将对应的父类构造函数 当做普通函数执行 传入对应的子类构造中的this

//对象冒充继承
//将对应的父类构造函数 当做普通的函数执行 传入对应的子类构造中的this
//子类构造
function Student(classNumber,name,age){
    //对象冒充
    Person.call(this,name,age)
    this.class = classNumber
}

缺点

  • 获取不了原型上的内容

组合继承(原型链继承+对象冒充)

//组合继承
//对象冒充继承
//将对应的父类构造函数 当做普通函数执行 传入对应的子类构造中的this
//子类构造
function Student(classNumber,name,age){
    //对象冒充
    Person.call(this,name,age)
    this.class = classNumber
}
//原型链继承
Student.prototype = new Person()

 缺点

  • 原型上有重复的属性

寄生组合继承

主要寄生原型(将父类原型对象加入到子类的原型上)+对象冒充

//寄生继承
//利用寄生原型+对象冒充
function Student(classNumber,name,age){
    //对象冒充
    Person.call(this,name,age)
    this.class = classNumber
}
//寄生继承
Student.prototype = Object.creat(Person.prototype)

面向对象的轮播图

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()
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值