从js中对象的创建方式和继承,谈谈对原型链的理解

在理解每一个概念的时候,我习惯性的会从以下角度去思考:

  • 这个概念为什么会被提出,它的提出背景是什么?
  • 这个概念是什么,它解决了什么问题?
  • 这个概念如何解决相关问题?

同样在理解原型链的过程中,我也进行过相关思考,现对自己的理解做出总结。

理解原型链之前,最好先理解清楚以下两个概念:js中对象的创建方式继承

一、对象的创建方式

对象字面量

var stu = {
    name:'andy', 
    gender:'female',
    showInfo:function(){
        console.log('common function');
    }
};

使用字面量方式直接创建对象,虽然直观明了,但是当需要创建10个、100个、甚至1000个同一类型的对象的时候,这种方式的弊端就显而易见。我们不应为了实现需求而真的复制粘贴1000遍相同结构的代码,那么我们应该怎么做呢?这个时候,大家比较容易想到的就是函数,下面就介绍使用函数创建对象的方式–工厂方式。

工厂方式

function createStudent(name,gender){
    return {
        name:name,
        gender:gender,
        showInfo:function(){
            console.log('common function');
        }
    }
}
var stu = createStudent('andy','female');

使用工厂函数这种方式,如果要创建10000个对象,只需要调用1000次函数即可。但是,每个对象都拥有公共方法showInfo(),这显然是不合理的。如何让每个对象都保有自己独特属性值的同时,共享同一个公共方法呢?

Object.create()方式

Object.create()以一个对象为原型,创建新对象的方式。

var A = {
    name:'andy',
    gender:'female',
    showInfo:function(){
        console.log('common function');
    }
};
var B = Object.create(A);//B={}
var C = Object.create(A);//C={}

B和C都是以A对象为原型创建的新对象,并且刚创建出来都是空对象。这其中的原理又是什么呢?A可以看作是共享池,这个池子中放着很多共享的属性和方法,B和C通过某种“指针”__proto__指向A,这样B和C就“拥有”共享池中的方法和属性了。但是,这种方式也存在一定的弊端:

  • 因为A是共享的,那么B和C就可以通过__proto__“指针”修改A中的属性值和方法;
  • B和C如果想要在不改变A属性的情况下,拥有特有的属性值,必须要进行以下操作:
    B.name = 'bob';
    B.gender = 'male';
    C.name = 'cindy';
    C.gender = 'female';

使用Object.Create()方法创建1000个不同属性值的对象,虽然实现了方法的共享,但是也存在一些隐患。那么,有没有其他方式呢?

构造函数

function Student(name,gender){
    this.name = name;
    this.gender = gender;
}
Student.prototype.showInfo = function (){
    console.log('common function');
}
var andy = new Student('andy','female');
andy.showInfo();//common function
var bob = new Student('bob','male');
bob.showInfo();//common function

使用构造函数的方式,如果要创建1000个对象,只需要 new 构造函数(参数) 1000次即可。它和工厂方式的区别是,同样创建1000个对象,使用构造函数方式时showInfo()在内存中占有的容量仅是工厂方式的1/1000。而这里new的本质是什么呢?这里简要总结一下:

1. 创建一个空对象{}//var obj = {};
2. 将这个空对象的原型,指向其构造函数的prototype对象;//obj.__proto__ = Object.create(构造函数.prototype)
3. 绑定this关键字到当前空对象;
4. 执行构造函数内部的代码,执行完毕返回对象实例。

但是有的构造函数和构造函数之间存在某种关系(比如学生包括小学生、中学生、大学生),而这个关系可以用“继承”来描述。

二、JS中的继承

假如,我们需要创建这样一种对象:

function MidStudent(name,gender,school){
    this.name = name;
    this.gender = gender;
    this.school = school;
}
MidStudent.prototype.showInfo = function (){
    console.log('common function');
}

var cindy = new MidStudent('cindy','female','一中');
cindy.showInfo();//common function
var elle = new MidStudent('elle','female','十四中');
elle.showInfo();//common function

我们可以发现MidStudent和Student比起来只是多了一个属性school,如果重新定义MidStudent,实际上也是浪费空间的。那么有什么方式可以更节省空间呢?可以想到的方式是继承。那么,js又是如何实现继承的呢?

function MidStudent(name,gender,school){
    Student.call(this,name,gender);//改变this指向
    this.school = school;
}
MidStudent.prototype = Object.create(Student.prototype);//以Student.prototype对象为原型,创建MidStudent.prototype对象
MidStudent.prototype.constructor = MidStudent;//让MidStudent的constructor指向自身

var cindy = new MidStudent('cindy','female','一中');
cindy.showInfo();//common function
var elle = new MidStudent('elle','female','十四中');
elle.showInfo();//common function

关于使用call函数改变this指向,这里的原理不再展开介绍。

三、原型链

基于以上对对象的创建方式继承的讨论,下面开始对原型链的探讨。在使用构造函数Student创建andy和bob对象的时候,这两个对象中并没有定义showInfo方法,那么它们是如何实现对showInfo方法的调用的呢?
从js中对象的创建方式和继承,谈谈对原型链的理解_第1张图片
在js中,所有对象都有__proto__属性,而函数除此之外还有prototype属性。回到刚刚的问题,实例对象andy和bob是如何实现对showInfo方法方法的调用的呢?

从js中对象的创建方式和继承,谈谈对原型链的理解_第2张图片
结合上图,andy.showInfo() 方法的调用过程如下:

  1. 首先查找自己内部是否有此方法,如果有直接调用,如果没有,通过andy.__proto__向上查找至Student.prototype;
  2. 查找Student.prototype内部是否有此方法,如果有直接调用,如果没有,通过Student.prototype.__proto__向上查找至Object.prototype;
  3. 查找Object.prototype内部是否有此方法,如果有直接调用,如果没有,通过Object.prototype.__proto__向上查找至null,结束。

整个查找过程就是如下的一个原型链的链式查找过程:
链式

总结下来,原型链描述的是实例对象和构造函数之间的一种关系,这种关系就是通过对象的__proto__属性和函数的prototype属性进行链接。
回到最开始讨论的问题,

  • 这个概念为什么会被提出,它的提出背景是什么?

      原型链主要就是为了在创建对象时更节省内存空间,对象可以通过链式查找的过程去共享属性和方法的定义。
    
  • 这个概念是什么,它解决了什么问题?

      原型链描述的是实例对象和构造函数之间的一种关系,可以解决对象间不用重复定义相同方法和属性的问题。
    
  • 这个概念如何解决相关问题?

      通过对象的__proto__属性和函数的prototype属性进行链接,不断向上查找的过程。
    
关于本文的不合理之处,请各位不吝赐教,悉数指出。

你可能感兴趣的:(JavaScript)