浅谈我对原型链的理解。。。

说到原型链,首先我们要想到三个东西构造函数-实例对象-原型对象。

我们先说说构造函数,之前我们通常创建对象就是var一个对象出来,例如:

var person = {
  name: 'zhangsan',
  age: 18,
  sayName: function () {
    console.log(this.name)
  }
}

这样创建对象是最简单粗暴的,但是,如果我想创建多个对象呢?有的人会说我多写几个对象不就行了,可是同样的代码重复多次,是我们所有程序员的忌讳,这时候我们可以采用函数的办法来创建我们需要的对象:

//例如我们可以创建一个函数,它是用来创建一个对象的
function createPerson (name, age) {
  return { 
    name: name,
    age: age,
    sayName: function () {
      console.log(this.name)
    }
  }
}

这个时候你想创建多个对象,你就可以直接使用函数来执行:

var person_1 = creatPerson('zhangsan', 18);
var person_2 = creatPerson('lisi', 18);
var person_3 ......

这样是不是就一目了然了,避免代码冗余的同时,也达到了我们想要的效果。

但是,我们还可以使用一种更为简单粗暴的方法:

function Person (name, age) {
  this.name = name
  this.age = age
  this.sayName = function () {
    console.log(this.name)
  }
}

var p1 = new Person('zhangsan', 18)
p1.sayName() // => zhangsan

var p2 = new Person('lisi', 18)
p2.sayName() // => lisi

细心的人可能会发现Person()中的代码与creatPerson有以下几点不同之处:

  • 没有显示的创建对象

  • 直接将属性和方法赋给了 this 对象

  • 没有return语句

  • 函数名使用的是大写的Person

而我们在使用Person函数时,需要new一个新的实例化对象出来,在new一个实例化对象的时候,执行了创建一个新对象 -- > 将构造函数的作用域赋给新的对象 --> 执行构造函数中的代码 --> 返回新的对象。简单的用代码实现上述过程,如下:

function Person (name, age) {
  // 当使用 new 操作符调用 Person() 的时候,实际上这里会先创建一个对象
  // var instance = {}
  // 然后让内部的 this 指向 instance 对象
  // this = instance
  // 接下来所有针对 this 的操作实际上操作的就是 instance

  this.name = name
  this.age = age
  this.sayName = function () {
    console.log(this.name)
  }

  // 在函数的结尾处会将 this 返回,也就是 instance
  // return this
}

通过上述简单的描述,我们可以得出构造函数和实例对象的关系:在每一个实例对象中同时有一个constructor属性,该属性指向创建该实例的构造函数,我们可以用代码检测:

//判断P1的constructor属性指向
console.log(p1.constructor === Person) // => true

//判断P2的constructor属性指向
console.log(p2.constructor === Person) // => true


console.log(p1.constructor === p2.constructor) // => true

综上所述,构造函数具有一下特点:

  • 构造函数是根据具体的事物抽象出来的抽象模板

  • 实例对象是根据抽象的构造函数模板得到的具体实例对象

  • 每一个实例对象都具有一个 constructor 属性,指向创建该实例的构造函数

当然了,上述所写的构造函数还有些缺陷,比如造成内存浪费等,在这里我就不过多赘述了,下面我们来说说原型。

 

在JS中,每一个构造函数都有一个prototype属性,指向另外一个对象。这也就说明这个对象所有的属性和方法都会被构造函数所拥有。

我们可以再写一段代码,还是创建一个人的函数,这次用prototype来做:

function Person (name, age) {
  this.name = name
  this.age = age
}

console.log(Person.prototype)

Person.prototype.type = 'human'

Person.prototype.sayName = function () {
  console.log(this.name)
}

var p1 = new Person('zhansan', 18);
var p2 = new Person('lisi', 18);

console.log(p1.sayName === p2.sayName) // => true

这时候,我们就可以列出构造函数/实例和原型之间的关系:

图示中我们可以理解到,任何一个构造函数都有一个prototype属性,该属性是一个object对象。

构造函数的prototype对象都有一个默认的constructor属性,指向prototype对象所在函数:

function F = (){};
console.log(F.prototype.constructor === F);  //true

通过构造函数得到的实例对象内部会包含一个指向构造函数的prototype对象的指针 _proto_ 。

var instance = new F()
console.log(instance.__proto__ === F.prototype) // => true

综上所述,我们对原型可以做出如下总结:

  • 任何函数都具有一个 prototype 属性,该属性是一个对象

  • 构造函数的 prototype 对象默认都有一个 constructor 属性,指向 prototype 对象所在函数

  • 通过构造函数得到的实例对象内部会包含一个指向构造函数的 prototype 对象的指针 __proto__

  • 所有实例都直接或间接继承了原型对象的成员

 

好了,说了这么多,我们来说一下重点,也就是原型链,结合前面对构造函数-实例-原型对象介绍,我们来说一下实例对象是如何访问原型对象中的成员:

每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性

  • 搜索首先从对象实例本身开始

  • 如果在实例中找到了具有给定名字的属性,则返回该属性的值

  • 如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性

  • 如果在原型对象中找到了这个属性,则返回该属性的值

也就是说,在我们调用 person1.sayName() 的时候,会先后执行两次搜索:

  • 首先,解析器会问:“实例 person1 有 sayName 属性吗?”答:“没有。

  • ”然后,它继续搜索,再问:“ person1 的原型有 sayName 属性吗?”答:“有。

  • ”于是,它就读取那个保存在原型对象中的函数。

  • 当我们调用 person2.sayName() 时,将会重现相同的搜索过程,得到相同的结果。

而这正是多个对象实例共享原型所保存的属性和方法的基本原理,这也就是所谓的原型链。

下面附上几张图示,方便大家理解:

 

 

(以上纯属个人意见,不足之处,还希望大家提出意见和建议)

 

 

 

 

 

 

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值