This article brings you knowledge about inheritance and prototype chain in javascript, including issues related to constructors, prototypes and class syntax sugar. I hope it will be helpful to everyone.
Inheritance and prototype chain of JavaScript It is a rare and difficult-to-understand part that I encountered in the process of learning front-end. Here I have recorded everything I know and learned. I hope it can give some tips to brothers who are still struggling in this. Please help me, and you are also welcome to criticize and correct me.
The constructor consists of both instance members and static members, where Instance members are members added inside the function through the this keyword; they can only be accessed through the instantiated object later; while static members are members added to the function itself and can only be accessed through the constructor Come visit.
//创造一个构造函数let Father = function(name,age){ //实例成员 this.name = name; this.age = age; this.method = "我是一个实例成员";} //静态成员Father.like = "mother"; //检验实例对象是否能够被构造函数直接访问console.log(Father.method); //undefinedconsole.log(Father.like); //mother //实例化一个对象let father = new Father("小王",27); //检验静态对象是否能够被实例化对象访问console.log(father.name); //小王console.log(father.age); //27console.log(father.like); //undefined
You can implement an instantiated object through the constructor through the new keyword, so what happens during the specific instantiation process? Woolen cloth? It can be roughly divided into the following steps:
(1) Create an empty object son {}
(2) Prepare a prototype chain connection for son son.__proto__ = Father.prototype
(3) Rebind this so that this of the constructor points to the new object Father.call(this)
(4) As the new object attribute Assignment son.name
(5) Return this return this
. At this time, the new object will have the methods and properties of the constructor
There are two methods of constructing functions. The first is the method directly defined inside the function, and the second is the method added through the prototype;
//函数内部直接定义的方法let Father = function(){ this.read = function(){ console.log("我是内部定义的read方法!"); }}//通过A brief analysis of JavaScript inheritance and prototype chain添加的方法Father.prototype.look = function(){ console.log("我是通过A brief analysis of JavaScript inheritance and prototype chain定义的look方法!");} //实例化对象进行检验let father1 = new Father();let father2 = new Father(); father1.read(); //我是内部定义的read方法!father2.read(); //我是内部定义的read方法!console.log(father1.read === father2.read); //falsefather1.look(); //我是通过A brief analysis of JavaScript inheritance and prototype chain定义的look方法!father2.look(); //我是通过A brief analysis of JavaScript inheritance and prototype chain定义的look方法!console.log(father1.look === father2.look); /true
It can be found that directly inside the function Every time a new object is instantiated for a defined method, a new memory space will be allocated to this method, and methods added through the prototype will share the same space.
There is no problem of memory space. When judging, it depends on whether the values are the same;
let Father = function(name){ this.name = name;}let father1 = new Father("小王"); let father2 = new Father("小红"); console.log(father1.name === father2.name); //falselet father1 = new Father("小王"); let father2 = new Father("小王"); console.log(father1.name === father2.name); //true
So we can summarize the basic rules for defining constructors, that is, public attributes are defined to the constructor Inside, we put the public methods on the prototype object.
Father.prototype is a prototype. It is an object and can also be called a prototype. object.
The role of prototype is to share methods.
We can share methods through Father.prototype.method
, which will not reflect the space storage method.
The point of this in the prototype is the instance.
I feel that the prototype chain is something that is particularly difficult to understand for beginners or some front-end noobs (such as myself). In order to make the following parts easier to understand, it is mandatory to remember the following points here:
__proto__
is an attribute that every object has, and prototype is unique to each function. Method; __proto__
attribute of each object will point to the prototype of its own constructor; Function.__proto__
=== Function.prototype;Object.prototype.__proto__
=== null is the end point of the prototype chain;The process of linking prototypes layer by layer is the prototype chain.
Objects can use the properties and methods of the constructor prototype prototype object, because the object has __proto__
The existence of the prototype is # for each object ##__proto__Existence of prototype
let Father = function(name){ this.name = name;}let father = new Father("老王");console.log(father.__proto__ === Father.prototype); //true //验证上述说法中的第二条
function Star(name) { this.name = name; //(1)首先看obj对象身上是否有dance方法,如果有,则执行对象身上的方法 this.dance = function () { console.log(this.name + '1'); }}//(2)如果没有dance方法,就去构造函数原型对象prototype身上去查找dance这个方法。Star.prototype.dance = function () { console.log(this.name + '2');}; //(3)如果再没有dance方法,就去Object原型对象prototype身上去查找dance这个方法。Object.prototype.dance = function () { console.log(this.name + '3');}; //(4)如果再没有,则会报错。let obj = new Star('小红');obj.dance();
有两种添加方法,第一种为上面的写法,直接通过 构造函数.prototype.方法名 进行添加;第二种为重定义构造函数的prototype,但是此种情况会丢失掉原有的constructor构造器,所以一定要再连接回去,例子如下:
function Star(name) { this.name = name;}Star.prototype = { dance:function(){ console.log("重定义prototype"); }}Star.prototype.constructor = Star;
另外,类似于Array、String这些内置的类是不能这么处理的。
这里就长话短说,首先我们要明确继承需要继承哪些东西,在前文中我们提到了定义构造函数的基本规则,即**公共属性定义到构造函数里面,公共方法我们放到原型对象身上。**我们所需要继承的东西也不外乎就这二者,公共属性的继承可以通过call()或者apply()进行this的指向定义,而公共方法可以通过原型对象的赋值进行处理,因此我们很容易想到如下的方法:
//定义一个父类function Father(name) { this.name = name;}Father.prototype.dance = function () { console.log('I am dancing');};//定义一个子类function Son(name, age) { Father.call(this, name); this.age = age;}//通过赋值的方法连接Son.prototype = Father.prototype;//为子类添加方法Son.prototype.sing = function () { console.log('I am singing');}; let son = new Son('小红', 100); //此时父类也被影响了console.log(Father.prototype) //{dance: ƒ, sing: ƒ, constructor: ƒ}
很显然,当我们只想修改子类里面的方法时,显然上述方法不太合适;因此 我们可以尝试new一个新的父类出来,代码如下:
function Father(name) { this.name = name;}Father.prototype.dance = function () { console.log('I am dancing');};function Son(name, age) { Father.call(this, name); this.age = age;}Son.prototype = new Father();Son.prototype.sing = function () { console.log('I am singing');};let son = new Son('小红', 100);console.log(Father.prototype) //{dance: ƒ, constructor: ƒ}
对于以前了解过面向对象编程的程序员来讲,上述关于继承的写法属实让人有些难以接受,因此在es6里面新增了一个语法糖来更方便更便捷地书写继承,这里就直接上代码了;
class Father { constructor(name) { this.name = name; } dance() { console.log("我是" + this.name + ",我今年" + this.age + "岁," + "我在跳舞"); }}class Son extends Father { constructor(name, age) { super(name); this.age = age; } sing() { console.log("我是" + this.name + ",我今年" + this.age + "岁," + "我在唱歌"); }}let obj = new Son('小红', 19); obj.sing();obj.dance();
分析一下上面代码,首先一个类(构造函数)里面依旧为两部分,即公共属性和公共方法,constructor() 里面存放了该构造函数的公共属性,后面接着的便是公共方法,extends 关键字表示继承的是哪个类,super() 便是将里面父类里面相应的公共属性拿出来,这样看下来便可以将代码规整许多。
相关推荐:javascript学习教程
The above is the detailed content of A brief analysis of JavaScript inheritance and prototype chain. For more information, please follow other related articles on the PHP Chinese website!