Learning JavaScript, how can you not prototypal inheritance?

prototypal inheritance


Before ES6, JavaScript did not have the concept of class, so it must use other methods to achieve inheritance—that is, prototypal inheritance. (Friendly reminder, if you don’t know what is a prototype object, prototype, etc., it is recommended to figure it out first (you can read my other blog to thoroughly JavaScript prototype objects and prototype chains , otherwise the following content is very unfriendly)

We all know that every object in JavaScript has a [[Prototype]] attribute (called __proto__ in browsers), pointing to its prototype object, so the easiest way to implement inheritance is to directly assign an object's [[Prototype]] point to another object

//方法1:直接指定对象的 __proto__ 属性(强烈不推荐)

//创建 Person 对象,具有 eat 和 run 的方法
var Person = {
    
    
    eat: function () {
    
    
        console.log(this.name + " 在吃饭");
    },

    run: function () {
    
    
        console.log(this.name + " 在跑步");
    }
}

//创建 Mike 对象,设置基本属性,具有 doHomework 的方法
var Mike = {
    
    
    name: "Mike",
    age: 15,
    grade: "8 年级",

    doHomework: function () {
    
    
        console.log(this.name + " 正在写 " + this.grade + "的作业");
    }
}

//我们让 Person 变为 Mike 的原型对象,注意只有支持 ES6 的浏览器才能这么写
Mike.__proto__ = Person;
Mike.eat();
Mike.run();
Mike.doHomework();

Console operation results:

41

It is indeed possible to achieve "inheritance", but I do not recommend it, not only because of browser restrictions, but also directly using __proto__ to modify the prototype object of an object may cause unexpected trouble, so the above method just have fun


If you have read the blog I wrote about prototype objects and prototype chains, you must know that functions can also be used to create objects in JavaScript. Let’s look at the second way of writing:

//Person 函数(作为构造函数),注意首字母大写
function Person(obj) {
    
    
}

//对 Person 函数的原型对象添加 eat 和 run 方法(我就不说明为什么这么写了)
Person.prototype.eat = function () {
    
    
    console.log(this.name + " 在吃饭");
}

Person.prototype.run = function () {
    
    
    console.log(this.name + " 在跑步");
}

//Teenager 函数(作为构造函数),设置 name、age、grade 属性
function Teenager(obj) {
    
    
    this.name = obj.name;
    this.age = obj.age;
    this.grade = obj.grade;
}

/* 重点来了,我们将 Teenager 函数的原型对象指向 Person 函数产生的实例对象,
   这个实例对象并没有任何的属性和方法,但是它的原型对象,也就是 Object,拥有 eat 和 run 方法
   毕竟我们一开始是使用 Person.prototype.方法名 的方式!*/
Teenager.prototype = new Person();

//然后再在 Teenager 的新原型对象上添加 doHomeWork 方法(所以实际上 Teenager 的原型对象只有这一个方法,其他的都在 Object 上)
Teenager.prototype.doHomeWork = function () {
    
    
    console.log(this.name + " 正在写 " + this.grade + "的作业");
}

//创建实例对象
var Mike = new Teenager({
    
    name: "Mike", age: 15, grade: "8 年级"});
Mike.eat();
Mike.run();
Mike.doHomeWork();

Console operation results:

42


"Inheritance" is also realized. We use Teenager.prototype = new Person() to make the instance object generated by the Person function become the prototype object of Teenager, but note that this prototype object does not have a constructor. After all, it is a instance . So this actually does not meet the definition of the prototype chain


So is there a way to solve this problem? Yes, let's continue to upgrade the code (here I also refer to the JavaScript tutorial of Mr. Liao Xuefeng):

//这回我们把 name 和 age 抽象到 Person 函数中
function Person(obj) {
    
    
    this.name = obj.name;
    this.age = obj.age;
}

//一样的步骤,不重复说明了
Person.prototype.eat = function () {
    
    
    console.log(this.name + " 在吃饭");
}

Person.prototype.run = function () {
    
    
    console.log(this.name + " 在跑步");
}

//请注意!新的点来了
function Teenager(obj) {
    
    
    //这句话的意思是调用 Person 函数,并且改变后续 this 的绑定,让 this 和实例对象(就是 = 左边的对象)绑定在一起
    Person.call(this, obj);
    this.grade = obj.grade;
}

//下面四句话才是重中之重!我们的思路是想要找到一个中介,作为 Teenager 和 Person 的桥梁,即达到继承的目的,又不破坏它们的原型对象和构造函数
//1、首先定义一个空函数
function F(){
    
    }

/* 2、然后将这个函数的原型对象指向 Person 函数的原型对象。这句话我们好好理解一下,Person 的原型对象除了有 eat 和 run 方法,
别忘了还有构造函数,那么当我们下次使用 new F() 的时候,本质上就是使用 Person.prototype.constructor(就是 Person()) */
F.prototype = Person.prototype;

//3、将 Teenager 的原型指向 F,而 F 又指向 Person,桥梁已经搭好了
Teenager.prototype = new F();

/* 4、最后将 Teenager 的原型对象的构造函数重新修改为 Teenager,
否则 Teenager.prototype.constructor === Person.prototype.constructor */
Teenager.prototype.constructor = Teenager;

//5、最后在 Teenager 的原型对象上定义方法
Teenager.prototype.doHomework = function () {
    
    
    console.log(this.name + " 正在写 " + this.grade + "的作业");
}

let Mike = new Teenager({
    
    name: "Mike", age: 15, grade: "8 年级"});
Mike.eat();
Mike.run();
Mike.doHomework();

The console output results (all requirements are met, this is the real prototype inheritance):

43



That’s about it for JavaScript prototypal inheritance. It’s really confusing. When you read it, you must understand the relationship between functions, objects, prototype objects, and constructors.

Guess you like

Origin blog.csdn.net/qq_52174675/article/details/122663494