day19 原型和继承

面向对象回顾

通过new构造函数的方式

class的constructor (es6)

class Person{
    constructor(name){ //构造器
        this.name = name
    }
}
var person = new Person('tom')
function的方式 (es3)

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

步骤

  • 自动构建对象

  • 手动设置属性

  • 自动返回对象

通过工厂模式

function factory(name){
    var obj = new Object()
    obj.name = name
    return obj
}

步骤

  • 手动构建对象

  • 手动设置属性

  • 手动返回对象

构造函数的缺陷

function Person(name){
    this.name = name
    this.age = 18
    this.sayHello = ()=>{
        console.log('hello');
    }
}
var person = new Person('jack')
var person1 = new Person('tom')
console.log(person == person1);//false 对应的存储地址不一样
console.log(person.age == person1.age);//true
console.log(person.sayHello == person1.sayHello);//false 函数也是引用数据比较的是地址
  • 因为上面的俩个sayHello方法不是一个方法 证明每次去new一个对象的时候对应的sayHello就会重新在对应的堆上开辟一个内存

  • 如果对应的new的操作很多那么对应的sayHello占用的内存就很大 (效率就低了)

  • 按照我们的思考 sayHello方法做的事情是一样 那么我们其实只需要一个sayHello就满足了

  • 构造函数的缺陷 就是里面声明的函数 会在每次new的时候重新开辟内存

  • 解决方式 找一个共享的空间(对象)(只声明一次) 将这个函数放进去)

  • 那么现在这个共享空间应该属于谁 构造函数

  • 这个属于构造函数的空间就被称为原型

原型

prototype

概述:prototype是属于函数的一个空间,它是一个对象。因为构造函数也是函数所以它也具备。而这个prototype属性我们称为显式原型。

函数的prototype

function fn(){
}
console.log(fn.prototype);

对应的这个prototype显示是一个对象,里面具备对应的属性及方法。主要为方法,那么也就是说这个prototype属性上一般存放对应的方法。

构造函数的prototype

function Person(){
}
console.log(Person.prototype);
//获取当前的构造函数
console.log(Person.prototype.constructor);
//将函数存储在原型上
Person.prototype.sayHello = ()=>{
    console.log(this);
}
//新建对象
var person = new Person()
var person1 = new Person()
console.log(person == person1);//false
//person.sayHello ===> Person.prototype.sayHello
//对于prototype上存储的内容 通过实例对象.属性名访问
console.log(person.sayHello == person1.sayHello);//true
///对于prototype上存储的内容 通过实例对象.属性名访问
console.log(person.constructor);
  • 从上可得构造函数的prototype是一个对象,第二个里面有个属性 constructor指向当前的构造函数。

  • 实例对象访问对于的prototype上的内容可以通过实例对象.属性名访问

  • 一般将对应的函数存储在对应prototype上(这个函数只会声明一次)。将函数存储在原型,将属性放在构造函数里面。

  • 在prototype里面声明的函数的this指向当前的调用的实例对象

proto

概述:

proto 称为隐式原型,它是属于对象的一个空间,每个对象都存在这个空间,那么对应的实例对 象也是一个对象,所以它也有这个空间。这个空间指向对应的构造函数的prototype。

__proto__ 的指向问题
__
proto__ 指向对应的构造函数的prototype
构造函数也是一个对象 那么它的 __proto__ 指向谁 指向对应的父类的构造函数的prototype
Object的 __proto__ 指向null
var obj = new Object()
//每个对象都存在的一个空间 它指向对应的构造函数的prototype
console.log(obj.__proto__);
//对象的__proto__指向对应的构造函数的prototype
console.log(obj.__proto__ == Object.prototype);
function Person(){
}
var person = new Person()
console.log(person.__proto__ == Person.prototype);
// Person.prototype.sayHello = ()=>{
// }
person.__proto__.sayHello = ()=>{
    console.log('hello');
}
person.sayHello()
//指向Person.prototype
console.log(person.__proto__);
//指向构造函数的原型的原型 构造函数的原型是啥 是一个对象 Object.prototype
console.log(person.__proto__.__proto__);
//指向构造函数的原型的原型 构造函数的原型是啥 是一个对象 Object.prototype 的__proto__是
null
console.log(person.__proto__.__proto__.__proto__);

原型链

概述:

对象在 proto 上找属性的链式结构被称为原型链。

从上面的指向问题来看 对象在原型上找属性的过程为

  • 先找自己的 proto (对应的构造函数的prototype),

  • 再找对应的自身构造函数的原型的 proto 找到父类构造函数的原型,再找对应的父类的原型的 proto ,直到找到object为止

  • Object的原型的 proto (null)上还找不到返回undefined

Object.prototype.hello = '你好'
class Person{
    constructor(){
        this.name = '张三'
    }
}
Person.prototype.sex = '女'
Person.prototype.username = 'rose'
//对象赋值 有就重新赋值 没有就添加属性赋值
Person.hi = '你好吗'
var person = new Person()
person.age =
    '
109'
class Son extends Person{
    constructor(){
        super()
        this.age = '李四'
    }
}
Son.prototype.sex = '男'
//实例化对象
var son = new Son()
console.log(son.age);//李四
console.log(son.name);//张三
console.log(son.username);//rose
console.log(son.sex);//男
console.log(son.hello);//你好
console.log(son.hi);//undefined

注意事项

  • 原型链不包含对象赋值

  • 对象赋值的操作是找到这个属性了重新设置值

  • 没有找到这个属性进行 添加这个属性进行赋值操作

总结

  • 构造函数的原型prototype

  • 实例对象的原型 proto

  • 实例对象的 proto 指向构造函数的prototype

  • 原型链通过对应的对象的 proto 去找对应的属性 直到找到Object为止

  • 原型一般上面写函数,可以保证函数只声明一次。对应的属性写在构造函数内。

  • 原型上的方法/属性。通过实例对象.属性名直接访问(平常通过对象去点的方法都称为原型法)

  • 在对应的原型上的函数里面的this指向当前调用的实例对象

通过原型来实现数组的高阶函数

forEach实现 map实现

//数组的原型
//在数组的原型上添加一个myForEach的方法
//在对应的原型上的函数里面的this指向当前调用的实例对象
Array.prototype.myForEach = function(fn){
    //遍历
    for(let i=0;i<this.length;i++){
        fn(this[i],i,this)
    }
}
Array.prototype.myMap = function(fn){
    let returnArr = []
    //遍历
    for(let i=0;i<this.length;i++){
        returnArr.push(fn(this[i],i,this))
    }
    return returnArr
}
//回调函数 defaultValue初始值
Array.prototype.myReduce = function(fn,defaultValue){
    //默认请求 前面的值为第一个开始下标为第二个
    let previousValue = this[0]
    let index = 1
    //如果传了初始值 那么对应的初始值为传入的值 开始的下标从0开始
    if(typeof defaultValue != 'undefined'){
        previousValue = defaultValue
        index = 0
    }
    //遍历
    for(let i=index;i<this.length;i++){
        previousValue = fn(previousValue,this[i],i,this)
    }
    return previousValue
}

面向对象的三大特性

封装 (函数的抽取 属性的抽取)

继承 (子类继承父类)

多态 (重写 子类重写父类方法)

继承

概述:

子类继承父类的属性和方法(非私有的属性和方法 非静态的方法)

继承的实现

使用extends关键词实现继承(es6新增 类的继承)

// es6新增类的继承 extends关键词实现
class Person{
    constructor(){
        this.name = 'jack'
    }
}
class Son extends Person{
    constructor(age){
        super()
        this.age = age
    }
}
var son = new Son(18)
console.log(`son`, son);

原型链继承 (覆盖之前原型上的所有方法 显示的时候不会显示继承来的属性 (在原型上重复出现一样的属性))

  • 核心 在子类的原型上创建父类的对象

function Person(){
    this.name = 'jack'
}
function Son(age){
    this.age = age
}
Son.prototype.say = ()=>{
    console.log(`你好`);
}
//原型继承
// 将要继承的类放在继承的子类的原型上
//原型链会覆盖原本原型上的私有的方法及属性
Son.prototype = new Person()
var son = new Son(18)
console.log(`son`, son);
console.log(son.name);
// son.say()

对象冒充 (会显示继承的属性 不能继承原型上的方法)

  • 核心 在子类中调用父类的构造函数 更改this指向

function Person(){
    this.name = 'jack'
}
function Son(age){
    //改this指向 执行父类的构造函数
    Person.call(this)
    this.age = age
}
var son = new Son(18)
console.log(`son`, son);

组合继承 (使用原型链继承和对象冒充结合)

// 组合继承 原型链继承加对象冒充
function Person(){
    this.name = 'jack'
}
Person.prototype.say = ()=>{
    console.log(`hello`);
}
function Son(age){
    //改this指向 执行父类的构造函数
    Person.call(this)
    this.age = age
}
//原型链继承
Son.prototype = new Person()
var son = new Son(18)
console.log(`son`, son);
son.say()

组合寄生继承 (对象冒充 + 原型链继承(创建一个原型对象放在原型链上))

  • 对象冒充

  • 在子类的原型上创建父类的原型对象

//组合寄生继承
function Person(){
    this.name = 'jack'
}
Person.prototype.say = ()=>{
    console.log(`hello`);
}
function Son(age){
    //改this指向 执行父类的构造函数
    Person.call(this)
    this.age = age
}
//寄生
Son.prototype = Object.create(Person.prototype)
var son = new Son(18)
console.log(`son`, son);

轮播图面向对象

<!DOCTYPE html>
    <html lang="en">
        <head>
        <meta charset="UTF-8">
            <meta http-equiv="X-UA-Compatible" content="IE=edge">
                <meta name=
                    "
viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
    *
    {
    margin: 0;
    padding: 0;
}
    .
showBox,
    li {
        width: 400px;
        height: 250px;
    }
li>img {
    width: 100%;
}
.
showBox {
    position: relative;
    margin: 200px auto;
    /* 溢出隐藏 */
    overflow: hidden;
}
ul {
    width: 800%;
    position: relative;
}
ul>li {
    list-style: none;
    float: left;
}
.
cirList {
    position: absolute;
    right: 20px;
    bottom: 10px;
    width: 150px;
}
.
cirList>li {
    width: 10px;
    height: 10px;
    background-color: #fff;
    border-radius: 50%;
    margin: 0 5px;
}
.
cirList .selected {
    background-color: red;
}
.
arrow {
    display: none;
}
.
arrow>a {
    display: block;
    width: 50px;
    height: 50px;
    position: absolute;
    top: 50%;
    margin-top: -25px;
}
.
arrow>.prev {
    background: url(./images/prev.png) no-repeat center;
    background-size: contain;
    left: 0;
}
.
arrow>.next {
    background: url(./images/next.png) no-repeat center;
    background-size: contain;
    right: 0;
}
</style>
</head>
<body>
    <div class="showBox">
        <ul class="nav">
            <li><img src="./images/slidepic1.jpg" alt=""></li>
<li><img src="./images/slidepic2.jpg" alt=""></li>
<li><img src="./images/slidepic3.jpg" alt=""></li>
<li><img src="./images/slidepic4.jpg" alt=""></li>
<li><img src="./images/slidepic5.jpg" alt=""></li>
<li><img src="./images/slidepic6.jpg" alt=""></li>
<li><img src="./images/slidepic7.jpg" alt=""></li>
</ul>
<!-- 焦点 -->
    <ul class="cirList">
        </ul>
<div class="arrow">
    <a href="" class="prev"></a>
<a href="" class="next"></a>
</div>
</div>
</body>
</html>
<script src='https://cdn.bootcdn.net/ajax/libs/move.js/0.5.0/move.js'></script>
<script>
    //需要存储所有的图片的盒子 nav
    // 焦点 左右切换的盒子
    class Carousel {
        constructor(box) {
            this.box = box
            //根据大盒子获取存储图片的盒子
            this.nav = box.querySelector('.nav')
            //根据大盒子获取存储焦点的盒子
            this.cirList = box.querySelector('.cirList')
            //控制的下标
            this.index = 0
            this.init()
            this.handlerClick()
        }
        init() {
            //根据对应的原本的图片个数
            //将this.nav.children 当作数组调用forEach方法
            Array.prototype.forEach.call(this.nav.children, (v, i) => {
                let htmlCode = '<li></li>'
                if(i == 0){
                    htmlCode = "<li class='selected'></li>"
                }
                //给对应的焦点进行添加
                this.cirList.innerHTML += htmlCode
            })
            //生成焦点和在对应的nav后添加第一张图
            var cloneNode = this.nav.children[0].cloneNode(true)
            //加给最后
            this.nav.appendChild(cloneNode)
        }
        move(direction = true) {
            //区间判断
            if (this.index < 1 && !direction) {
                this.index = this.nav.children.length - 1
                //切换位置
                this.nav.style.transform = `translate3d(${this.index *
                    this.box.offsetWidth * -1}px, 0px, 0px)`
            }
            if (this.index > this.nav.children.length - 2 && direction) {
                this.index = 0
                this.nav.style.transform = 'translate3d(-0px, 0px, 0px)'
            }
            //反方向为false
            if (direction) {
                this.index++
            } else {
                this.index--
            }
            //移动
            var x = this.index * this.box.offsetWidth * -1
            this.focusElement(this.index)
            move('.nav').to(x, 0).duration('1s').end()
        }
        autoMove() {
            let
            _
            self = this
            this.timer = setInterval(() => {
                _
                self.move(true)
            }, 2000)
        }
        focusElement(index){
            //取值为0-6
            if(index==7){
                index = 0
            }
            //排他思想
            Array.prototype.forEach.call(this.cirList.children,(v,i)=>{
                v.className = (i == index ? 'selected' : '')
            })
        }
        handlerClick(){
            var
            _
            self = this
            Array.prototype.forEach.call(this.cirList.children,(v,i)=>{
                let index = i
                v.onclick = function(){
                    _
                    self.index = index-1
                    _
                    self.move()
                }
            })
        }
    }
var box = document.querySelector('.showBox')
var carousel = new Carousel(box)
carousel.autoMove()
</script>

ES6的模块化

模块的思想,将对应的功能代码封装为一个模块(js代码 css代码 html代码)。 想要使用别人就导入,想要给别人用就导出。复用。

模块化的常用的模式

  • amd (在对应的加载之前导入)

  • cmd (在用的时候导入)

  • comment.js (基于amd和cmd之上)

es6的模块化的关键词(要想导入想要导出)

  • import 导入

  • export 导出

export的使用

第一种 export default (只能声明一次)

// //默认导出只有一个 如果使用export default 导出的可以使用对应的一个名字来接
export default {
    obj,
    str,
    say
}

接收

import obj from './test.js'
console.log(obj) //{obj,str,say}

第二种导出

//如果直接使用的对应的export 导出那么必须通过{键}来接
export const obj = {
    name:
    '
    jack',
    age:18
}
//如果导出的是值类型 一定要写变量名
export const str =
      "
你好世界"
export const say = ()=>{
    console.log('hello world');
}

接收

import {obj,str,say} from './test.js'

第三种导出

const name = 'tom'
//第三种导出
export {
//只能写变量
name
}

接收

import {name} from './test.js'

import使用

import 名字 from '地址'

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值