【个人笔记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
}

但通常的应用场景下,你会需要生成多种动物。这则需要把以上的逻辑抽象成一个函数。可见以下代码,生成了 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)

但是按照上面代码,每次新创建一个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;
}

以上代码中,比如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.

以上的用法就是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.

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

如何创建一个构造函数(构造函数就是构造一个对象)
如何将方法添加到构造函数的原型上(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.

以上这些操作,就是基本上创建了一个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.

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值