首页 > 其他分享 >day19 原型和继承

day19 原型和继承

时间:2022-10-25 22:27:01浏览次数:49  
标签:console log 继承 Person 原型 prototype day19 构造函数

面对对象回顾

通过new构造函数的方法
  • class的constructor ( es6 )

class Person{
    constructor(name,age){ //构造器
        this.name=name
    }
}
var person = new Person('小池')
  • function的方式 ( es3 )
function Person(name){
    this.name=name
}
var person = new Person('jack')
步骤
  • 自动构建对象

  • 手动设置属性

  • 自动返回对象

通过工厂模式
function factory(name){
    var obj=new Objtect()
    obj.name=name
    return obj
}
步骤
  • 手动构建对象

  • 手动设置属性

  • 手动返回对象

 

构造函数的缺陷
  • 因为上面的两个sayhi方法不是同一个方法 , 所有每次new 都会重新在对应的堆上开辟内存

  • 如果new操作很多 , 那sayhi方法占用的内存就越来越多 ( 效率就低了 )

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

function Person(name) {
    this.name = name
    this.age = 18
    this.sayhi = () => {
        console.log('hello')
    }
}
var person = new Person('jack')
var person1 = new Person('tom')
console.log(person == person1) //false 对应的存储地址不一样
console.log(preson.age == person1.age) //true
console.log(person.sayhi == person1.sayhi) //false 函数也是引用类型,它比较的是存储地址
解决方案
  • 找一个共享的空间 ( 对象 ) 只声明一次 , 将这个函数放进去 便可实现复用

  • 它的这个共享空间 是属于 构造函数的

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

 

原型

prototype

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

函数的prototype
function fn(){
    
}
console.log(fn.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)
// prototype上存储的内容,可通过实例对象.属性名访问
console.log(person.sayHello == person1.sayHello)
  • 从上可得构造函数的prototype是一个对象 , 里面有个属性 constructor 指向当前的构造函数 . 一般将对应的函数存储在prototype内

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

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

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

 

__proto__

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

var obj = new Object()
console.log(obj.__proto__)
// 对象的__proto__指向对应的构造函数的prototype
console.log(obj.__proto__ == Object.prototype) //true
function Person() {
}
var person = new Person()
console.log(person.__proto__ == Person.prototype) //true
person.__proto__.sayhi = () => {
    console.log('hello world')
}
person.sayhi() //hello world

__proto__的指向问题

__proto__指向的是对应构造函数的原型 ( prototype )

构造函数也是一个对象 , 那么它的__proto__也是指向父类的构造函数的原型

Object的__proto__指向null , 返回的是undefined

//指向父类构造函数的原型
console.log(person.__proto__)//Person.prototype
//指向父类构造函数的原型再向上 指向父类的父类的构造函数原型 ,是Object.prototype
console.log(person.__proto__.__proto__)//Object.prototype
//Object.prototype的__proto__指向null
console.log(person.__proto__.__proto__.__proto__)//null

 

原型链

概述 : 对象在__proto__上找属性的链式结构被称为原型链

从上指向问题来看 , 对象在原型上找属性 的过程为 :
  • 先找自己的__proto__, ( 对应构造函数的prototype )

  • 再找父类构造函数的原型 , 再找对应的父类的原型 , 直到找到objtect为止

  • object上还找不到返回null ( null的值是undefined )

Object.prototype.sex = '男'
class Person {
    constructor() {
        this.name = 'jack'
        this.age = 24
    }
}
Person.prototype.aloha = '111'
class Son extends Person {
    constructor() {
        super()
        this.name = 'rose'
    }
}
// 对象赋值,有这个属性就重新赋值,没有就添加这个属性
Son.hi = 23
Son.prototype.sayHi = '侬好哇'
// 实例化对象
var son = new Son()
console.log(son.name) //rose
console.log(son.age) //24
console.log(son.sayHi) //侬好哇
console.log(son.aloha) //111
console.log(son.sex) //男
console.log(son.hi) //undefined

 

注意事项

  • 原型链不包含对象赋值

  • 对象赋值的操作是 有这个属性就重新设置值 ,

  • 没有 就添加这个属性进行赋值

  • 对象赋值跟原型链没有关系

总结

  • 构造函数的原型叫prototype ,

  • 实例对象的原型是__proto__

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

  • 原型链是通过对象的__proto__去找对应的属性 , 直到找到Objtect为止

  • 原型上一般写函数 , 是可以保证函数只声明了一次 , 属性一般写在构造函数内

  • 原型上的方法 / 属性 可以通过实例对象.属性名直接访问 ( 平常通过对象.的方法都是原型的方法 )

 

 

 

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

  • forEach

// 数组的构造函数内的原型添加一个myForEach方法
Array.prototype.myForEach = function (fn) {
    for (let i = 0; i < this.length; i++) {
        fn(this[i], i, this)
    }
}
var arr = [1, 2, 3, 4, 5]
arr.myForEach((item, index, arr) => {
    console.log(item)
})
  • map
// 数组的构造函数内的原型添加一个myForEach方法
Array.prototype.myMap = function (fn) {
    let returnArr = []
    for (let i = 0; i < this.length; i++) {
        returnArr.push(fn(this[i], i, this))
    }
    return returnArr
}
var mapArr = arr.myMap((item, index, arr) => {
    return index
})
console.log(mapArr)
  • reduce
Array.prototype.myReduce = function (callback, init) {
    let prev = this[0]
    let currentIndex = 1
    if (typeof init != 'undefined') {
        prev = init
        currentIndex = 0
    }
    for (let i = 0; i < this.length; i++) {
        prev = callback(prev, this[i], i, this)
    }
    return prev
}
let reduceArr = arr.myReduce((prev, current) => {
    return prev + current
})
console.log(reduceArr)

 

面对对象的三大特性

  • 封装 ( 把相同的属性和方法提取出来 )

  • 继承

  • 多态

继承

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

继承的实现

  • 使用extends 关键词实现继承 ( es6类的继承 ) 常用 , 有兼容问题

// es6类的继承 使用extends
class Person {
    constructor() {
        this.name = 'jack'
    }
}
class Son extends Person {
    constructor() {
        super()
        this.age = 22
    }
}
var son = new Son()
console.log(son) //name:jack,age:22
  • 原型链继承 ( 会覆盖之前原型上的所有方法 ( 放继承后面就不会覆盖 ) , 不会显示继承来的属性 , 只能再原型上显示 )

    核心 : 在子类的原型上创建父类的原型对象
//原型链继承
function Person() {
    this.name = 'jack'
}
function Son(age) {
    this.age = age
}
Son.prototype.say = () => {
    console.log('侬好侬好')
}
// 原型链继承
Son.prototype = new Person()
var son = new Son(22)
console.log(son)
son.say() //会报错 , 本身设置的原型方法会被继承来的全部覆盖

 

  • 对象冒充 ( 会显示继承来的属性 , 不能继承原型上的方法 )
  核心 : 通过改变父类构造函数的this 实现
//对象冒充 通过改变父类构造函数的this实现
//不能继承原型上的方法
function Person() {
    this.name = 'rose'
}
Person.prototype.hi = () => {
    console.log('aloha');
}
function Son(age) {
    // 对象冒充 : 通过改变父类构造函数的this来实现
    Person.call(this)
    this.age = age
}
var son = new Son(24)
console.log(son)
son.hi() //会报错 , 不能继承原型上的方法
  • 组合继承 ( 使用原型链继承和对象冒充结合 , 会显示多余的内容 , 多余的内容是父类的属性 )
// 组合继承 (对象冒充+原型链继承)
// 可以继承本身的原型方法,但是会有多的内容
function Person() {
    this.name = 'rose'
}
// Person上的原型say方法
Person.prototype.say = () => {
    console.log('aloha')
}
function Son() {
    // 对象冒充,改变父类构造函数的this实现
    Person.call(this)
    this.age = 25
}
// 原型链继承
Son.prototype = new Person()
var son = new Son()
console.log(son)
son.say() //aloha
  • 组合寄生继承 ( 对象冒充 + 原型链 ( 通过创建一个原型对象放在原型链上 ) 可以拿到本身和继承的属性和方法 , 也不会显示多余的内容 )
//组合寄生继承 (冒充继承+原型链继承(通过创建一个原型对象))
//可以拿到本身和继承的属性和方法,也不会显示多余的内容
function Person() {
    this.name = 'Alex'
}
Person.prototype.sayHi = () => {
    console.log('long time no see')
}
function Son() {
    //冒充继承
    Person.apply(this)
    this.age = 25
}
// 创建一个原型对象
Son.prototype = Object.create(Person.prototype)
var son = new Son()
console.log(son)
son.sayHi()

 

轮播图面对对象

<!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>
        <script src='https://cdn.bootcdn.net/ajax/libs/move.js/0.5.0/move.js'></script>
    </body>

</html>
<script>
    class Carousel {
        constructor(box) {
            this.box = box
            // 根据大盒子获取存储图片的盒子(nav)
            this.nav = box.querySelector('.nav')
            // 根据大盒子获取存储焦点的盒子
            this.cirList = box.querySelector('.cirList')
            // 控制的下标默认设为0
            this.index = 0
            this.init() //添加焦点
            this.handlerClick()
        }
        init() {
            // 根据图片个数来添加焦点(先克隆第一张图片到最后),因为克隆的图片不需要添加,所有放在克隆前
            // 把this.nav.children当做数组来调用forEach方法
            Array.prototype.forEach.call(this.nav.children, (item, index) => {
                let htmlCode = '<li></li>'
                // 给第一个li添加焦点和class样式
                if (index == 0) {
                    htmlCode = '<li class="selected"></li>'
                }
                // 其余的添加焦点
                this.cirList.innerHTML += htmlCode
            })
            // 克隆第一张图片放到最后一张后面 
            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)`
            }
            // 如果是最后一张(因为克隆了一张所以需要-2)并且是正方向,就把它切换到第一张,在设置位置
            if (this.index > this.nav.children.length - 2 && direction) {
                this.index = 0
                this.nav.style.transform = 'translate3d(0px,0px,0px)'
            }

            // 正方向下标就加加
            if (direction) {
                this.index++
            } else {
                // 反方向就减减
                this.index--
            }
            // 调用第三方插件 x是需要设置宽度(左右切换)
            var x = this.index * this.box.offsetWidth * -1
            move('.nav').to(x, 0).duration('1s').end()
            this.foucsEvent(this.index)
        }
        // 自动轮播,用定时器实现
        autoMove() {
            let self = this
            this.timer = setInterval(() => {
                // 调用第三方move方法
                self.move()
            }, 2000)
        }
        // 焦点切换
        foucsEvent(index) {
            // 遍历并设置样式
            Array.prototype.forEach.call(this.cirList.children, (item, i) => {
                item.className = (i == index ? 'selected' : '')

            })
        }
        // 焦点点击
        handlerClick() {
            let self = this
            // 遍历并给每个成员设置点击事件
            Array.prototype.forEach.call(this.cirList.children, (item, i) => {
                let index = i
                item.onclick = () => {
                    // 点击的下标应该是当前下标-1,因为点击的下标是0-6,图片切换的下标是1-7
                    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 导入

inport 名字(obj/{obj,str,say}/{name}) from '地址'

export 导出

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

const obj = {
    name: "jack",
    age: 24,
    sex: '男'
}
const str = 'hello world'
const say = function () {
    console.log('aloha')
}
// 默认只能导出一个,导出时可以使用对应的名字来接(这里写的是obj)
export default {
    obj,
    str,
    say
}
接收
<script type="module">
    import obj from './test.js'
    console.log(obj)
    console.log(obj.str)
    console.log(obj.say)
    console.log(obj.obj)
</script>

第二种 : export 加在声明的变量前

export const obj = {
    name: "jack",
    age: 24,
    sex: '男'
}
export const str = 'hello world'
export const say = function () {
    console.log('aloha')
}
接收
<script type="module">
    import {obj,str,say} from './test.js'
    console.log(obj)
    console.log(str)
    console.log(say)
</script>

第三种 : 直接写对象

const obj = {
    name: "jack",
    age: 24,
    sex: '男'
}
export {obj}
const str = 'hello world'
export {str}
const say = function () {
    console.log('aloha')
}
export {say}
接收
<script type="module">
    import { str,obj,say } from './test.js'
    console.log(obj)
    console.log(str)
    console.log(say)
</script>

标签:console,log,继承,Person,原型,prototype,day19,构造函数
From: https://www.cnblogs.com/itlulu/p/16826560.html

相关文章

  • B端产品设计之原型设计
    前几篇文章从B端产品设计的业务、流程设计角度出发,给大家阐述了自己的想法,这篇文章将从原型设计角度出发,主要是原型设计工具,如何设计原型,设计边界,设计规范以及PRD结合这几个......
  • unity 解决无法添加脚本,该脚本不会继承可以管理脚本的本级类的错误
    错误提示报错的原因unity中c#代码的名称与编辑器中打开的代码class名称不同将两者的名称修改一致即可解决问题......
  • 继承
    定义:父对象中的成员,子对象无需创建,就直接可以使用实现:1.js中的继承都是通过继承原型对象来实现的2.原型对象:专门保存一个类型的所有子对象共有的成员的父对象......
  • 设计模式之原型模式
    概述在使用原型模式时,需要首先创建一个原型对象,再通过复制这个原型对象来创建更多同类型的对象。其定义如下:使用原型实例指定创建对象的种类,并且通过克隆这些原型创建新的......
  • 面向对象编程 封装继承
    访问修饰符访问修饰符访问级别public公有地,外部可以访问protected受保护的,只有本类和派生类才能够访问private私有的,只有本类中可以访问封装字段......
  • 瞎写的原型理解
    原型与原型链的基础定义理解关于原型与原型链下面这些基础JavaScript知识一定要“死记硬背”的。只有“死记“,才能“用活”。对象是某个特定引用类型的实例,可以理......
  • 【maven】什么是坐标(依赖)继承与模块、web项目启动&访问
    目录​​2.Maven基础​​​​2.1坐标​​​​2.1.0什么是坐标(依赖)​​​​2.1.1获得坐标​​​​2.1.2使用坐标​​​​2.1.3依赖范围​​​​2.1.4依赖传递​​​......
  • 【C语言】函数的概述、函数的好处、库函数、语法原型。
    ......
  • java第四讲-继承与多态-InheritsAndPolymorphismSourceCode
    1.继承条件下类的访问权限public:外界可自由访问;private:外界不可访问;protected:同一包中的子类都可以访问,另一包中的子类(派生于同一个父类)也可以访问;default:如果......
  • Java不能继承多个类?内部类帮你解决这个问题
    Java不能继承多个类?内部类帮你解决这个问题内部类在Java中也是一个很重要的概念,很多类中都存在内部类。内部类与内部类的对应的是外围类,内部类可以操作外围类的所有成员,p......