• 【个人笔记js的原型理解】


    在 JavaScript 中,最常见的新建一个对象的方式就是使用花括号的方式。然后使用’ . '的方式往里面添加属性和方法。可见以下代码:

    let animal = {};
    animal.name = 'Leo';
    animal.energe = 10;
    
    animal.eat = function (amount) {
        console.log(`${this.name} is eating.`)
        this.energe += amount
    }
    
    animal.sleep = function (length) {
        console.log(`${this.name} is sleeping.`)
        this.energe += length
    }
    
    animal.play = function (length) {
        console.log(`${this.name} is playing.`)
        this.energe -= length
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    但通常的应用场景下,你会需要生成多种动物。这则需要把以上的逻辑抽象成一个函数。可见以下代码,生成了 leo 和 snoop 两个动物。

    function Animal(name, energy){
        let animal = {};
        animal.name = name;
        animal.energy = energy;
    
        animal.eat = function (amount) {
            console.log(`${this.name} is eating.`)
            this.energy += amount
        }
    
        animal.sleep = function (length) {
            console.log(`${this.name} is sleeping.`)
            this.energy += length
        }
    
        animal.play = function (length) {
            console.log(`${this.name} is playing.`)
            this.energy -= length
        }
        return animal;
    }
    
    const leo = Animal('Leo', 7);
    const snoop = Animal('Snoop', 10)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    但是按照上面代码,每次新创建一个animal,就需要把里面的eat、sleep、play方法都会被重新创建一遍。

    因为eat、sleep、play方法都很相似,所以我们可以把他们放在一个animalMethods里面,这样只需要在内存里面创建一次。然后每次我们需要创建一个新动物,我们只是指向了animalMethods里面的方法,而不是重新创造这些方法。

    const animalMethods = {
        eat(amount) {
            console.log(`${this.name} is eating.`)
            this.energy += amount
        },
        sleep(length) {
            console.log(`${this.name} is sleeping.`)
            this.energy += length
        },
        play(length) {
            console.log(`${this.name} is playing.`)
            this.energy -= length
        }
    }
    function Animal(name, energy){
        let animal = {};
        animal.name = name;
        animal.energy = energy;
        animal.eat = animalMethods.eat;
        animal.sleep = animalMethods.sleep;
        animal.play = animalMethods.play;
        
        return animal;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    以上代码中,比如animal增加一个方法poop,就需要到Animal函数 里面增加 animal.poop = animalMethods.poop 。 如果想要Animal总能指向 animalMethods里面的任何一个方法,可以使用Object.create()传入animalMethods,使得在Animal里面找不到对应属性或方法时,就会去animalMethods查找,并调用对应方法。

    const animalMethods = {
        eat(amount) {
            console.log(`${this.name} is eating.`)
            this.energy += amount
        },
        sleep(length) {
            console.log(`${this.name} is sleeping.`)
            this.energy += length
        },
        play(length) {
            console.log(`${this.name} is playing.`)
            this.energy -= length
        }
    }
    function Animal(name, energy) {
        let animal = Object.create(animalMethods)
        animal.name = name;
        animal.energy = energy;
    
        return animal;
    }
    const leo = Animal('Leo', 7);
    console.log(leo.play(7)) //Leo is playing.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    以上的用法就是JavaScript 原型(prototype)的由来。

    那么什么原型?它就是函数上的一个属性,指向一个对象。

    既然原型是每个函数都有的属性,那么与其单独管理 animalMethods ,为什么我们不把 animalMethods 放到函数的原型上呢?

    function Animal(name, energy) {
        let animal = Object.create(Animal.prototype)
        animal.name = name;
        animal.energy = energy;
    
        return animal;
    }
    
    Animal.prototype.eat = function (amount) {
        console.log(`${this.name} is eating.`)
        this.energy += amount
    }
    Animal.prototype.sleep = function (length) {
        console.log(`${this.name} is sleeping.`)
        this.energy += length
    }
    Animal.prototype.play = function (length) {
        console.log(`${this.name} is playing.`)
        this.energy -= length
    }
    
    const leo = Animal('Leo', 7);
    console.log(leo.eat(5)) //Leo is eating.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    以上代码告诉了我们三点:

    如何创建一个构造函数(构造函数就是构造一个对象)
    如何将方法添加到构造函数的原型上(eg:Animal.prototype.eat()={…})
    如何使用Object.create()指向函数原型。
    这三点的目的就是为了构造函数的所有实例都能共享实例上的方法。

    接下来会引入关键词 new 来对上面代码再进行进一步优化。使用new关键词,js会自动做Object.create()和return的动作,并且需要使用this对象来替换原本的animal对象。

    function Animal(name, energy) {
        // let this = Object.create(Animal.prototype) //这一步的作用:1、创建对象 2、指向Animal.prototype
        this.name = name;
        this.energy = energy;
    
        // return this; //输出创建的对象
    }
    
    Animal.prototype.eat = function (amount) {
        console.log(`${this.name} is eating.`)
        this.energy += amount
    }
    Animal.prototype.sleep = function (length) {
        console.log(`${this.name} is sleeping.`)
        this.energy += length
    }
    Animal.prototype.play = function (length) {
        console.log(`${this.name} is playing.`)
        this.energy -= length
    }
    
    const leo = new Animal('Leo', 7);
    const snoop = new Animal('Snoop', 10)
    console.log(leo.sleep(15)) //Leo is sleeping.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    以上这些操作,就是基本上创建了一个class。在es6,JavaScript有了class关键词。接下来就用class来重构以上的代码。

    class Animal {
        constructor(name, energy) {
            this.name = name
            this.energy = energy
        }
        eat(amount) {
            console.log(`${this.name} is eating.`)
            this.energy += amount
        }
        sleep(length) {
            console.log(`${this.name} is sleeping.`)
            this.energy += length
        }
        play(length) {
            console.log(`${this.name} is playing.`)
            this.energy -= length
        }
    }
    
    const leo = new Animal('Leo', 7);
    const snoop = new Animal('Snoop', 10)
    console.log(leo) //Animal { name: 'Leo', energy: 7 }
    console.log(leo.sleep(1)) //Leo is sleeping.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    既然可以用js建造class,那么为什么还需要花这么多时间了解上面的prototype,this,new?原因是class是function的语法糖,提供了更便捷的方式创建对象。class最终会被编译为function,其中的方法会成为prototype上面的共享方法。

  • 相关阅读:
    [附源码]Python计算机毕业设计SSM绝味鸭脖连锁店信息系统(程序+LW)
    Day3-渐渐明白[链表]的边界
    Java 实现多风扇
    SQLite利用事务实现批量插入(提升效率)
    `pip` 下载速度慢
    c++ Makefile clion ide remote构建
    LeetCode50天刷题计划(Day 20—— 有效的数独(12.10-13.10)
    基于信息融合的风电机组关键部件状态识别
    初识java
    条款31、将文件间的编译依赖依存关系降至最低
  • 原文地址:https://blog.csdn.net/weiyi47/article/details/132644058