原型与原型链的学习理解

目录

1.对象和函数的原型

1.1认识对象的原型

1.2函数的原型

1.3 将方法放到原型上

 1.4总结:

 1.5思考:

2.显式原型的属性

2.1默认属性

 2.2重写原型对象

3.面向对象的特性——继承

那么继承能做什么呢?

原型链实现继承

原型式继承的渊源


ES5:

1.对象和函数的原型

1.1认识对象的原型

每一个对象都有自己的原型。

如何获取我们的原型?

疑问:这个原型有什么用???

当我们通过[[get]]方式获取一个属性对应的value时,

1)它会优先在自己的对象中查找,如果找到直接返回

2)如果没有找到,那么会在原型对象中查找

可以看到,如果对象中不存在但是原型链中存在的话,返回值不会是undefined。如果都没有则返回undefined。

那么我们尝试通过对原型添加属性,再通过对象获取,可以看到原型里面添加了这个属性,也能获取到该属性。

1.2函数的原型

1.将函数看作一个普通对象(隐式原型)

 

2.将函数看成一个函数,其具备prototype(注意:对象是没有prototype!!)(显式原型)

 

 

 prototype:只有函数才具备显式原型

回顾new操作new Person()

1.创建一个空对象

var obj = {}

2.将this指向该对象

this = obj

2.5.将函数的显式原型赋值给这个对象作为隐式原型(可作为第二步/第三步)

obj.__proto__ = Person.prototype

3.执行函数中的代码

4.将这个对象返回

1.3 将方法放到原型上

当我们创建一个构造函数的时候,如果创建对象,调用构造函数里面的方法时,每创建一个对象调用相应的方法,就会创建一个新的方法,其实这是没有必要当,当我们创建非常多的对象时,这样就会造成空间资源的浪费,是非常没有必要的。

 由上面两张图可以看出来:每个对象调用构造函数里面的方法时,都会创建新的方法。所以我们可以用prototype来把方法创建在原型上。

如下:

 当把方法放在原型上面,调用的方法都是同一个方法。

但是,为什么能够调用呢?

stu1的隐式原型就是Student的显式原型;

怎么查找自己的原型?(stu1举例)

*现在自己身上查找,没有找到;

*去原型上查找。

stu2的隐式原型也是Student的显示原型。

所以:当我们多个对象拥有共同的值时,可以将其放在构造函数的显式原型上,由构造函数创建出来的所有对象,都会共享这些属性。

 1.4总结:

1.什么是函数的显式原型?

区分和对象原型的区别。

2.函数原型的作用?

再通过new操作创建对象时,将这个显式原型赋值给创建出来的对象的隐式原型。

3.案例Person,将所有的函数定义放在显式原型上,所有的对象都可以共享这些方法。

 1.5思考:

属性为什么不放在原型上?

每个对象都有自己独有的属性。

2.显式原型的属性

2.1默认属性

事实上,原型对象都有一个属性:constructor

默认情况下原型上都会添加一个属性叫做constructor,这个constructor指向当前的函数对象。

 打印后发现原型的数据类型是:object

刷新后发现,其中有constructor(构造器)属性。

 首先提取出Person的prototype属性,对属性内的constructor进行打印,发现constructor指向了Person函数对象。所以这之间存在循环引用的。

 实例对象举例:

可以看到,constructor属性都是同一个。

 内存指向:

 验证如下:

 2.2重写原型对象

 

 从上图可以看到有个缺点:

 在未重写原型时,我们发现原型里面并不能枚举出来constructor属性,但是在重写原型后,是可以枚举出来constructor属性的,这就与浏览器的默认方法不一致,那么如何解决这个问题?

从上图可以看到:仍然添加了constructor属性,但是枚举时不显示。

3.面向对象的特性——继承

面向对象:一种编程方式。

 面向对象有三大特性:封装、继承、多态

  • 封装:我们将属性和方法封装到一个类中,可以称之为封装的过程;

例如:

var name = 'lu';
var age = 26;
function running () {}

function info = {
    name: 'lu',
    age: 26,
    running: function() {}
}
// 如果有很多类似的东西,我们也可以把其放到类里,这也叫封装
  • 继承是面向对象中非常重要的,不仅仅可以减少重复代码的数量,也是多态前提(纯面向对象中);
  • 多态:不同的对象在执行时表现出不同的形态;

那么继承能做什么呢?

  • 继承可以帮助我们将重复的代码和逻辑抽取到父类中,子类只需要直接继承过来使用即可;
  • 在很多编程语言中,继承也是多态的前提

 

 向上面这样,我们的学生和老师类都有很多共同的属性和方法,这个时候,只需要把共同的属性和方法放到父类里面,子类只需要继承。

所以这个时候讲解一下原型链的概念。

查找顺序:

1.obj上查找

2.obj.__proto__上查找

3.obj.__proto__.__proto__ -> null上查找(undefined)

 

对现有代码进行改造:

 假设前两个原型添加message属性:

所以得到了如下结论:

 这样一个链式查找过程就叫做:原型链。

所以继承类似,当我们在子类中查找不到时,就可以去父类中查找,所以就可以把所有的子类中相同的属性都放到父类中,以此类推。

原型链实现继承

什么地方是原型的尽头呢?

当我们一直打印原型__proto__属性会发现在最后返回null。

事实上,这个原型就是我们最顶层的原型了。

从Object上直接创建出来的对象原型都是null。

那么我们思考,该原型有什么特殊的吗?

1.该对象有原型属性,但是其原型属性已经指向null,已经是顶层原型了;

2.该对象上有很多默认的属性和方法;

我们先不使用继承创建一个Person类和一个Student类。

(下图的Student的s应该大写,后面改了,下面这张图懒得改了)

一个错误示例:

 首先考虑方法的继承:

原型链的弊端:

1.我们通过直接打印对象是看不到这个属性的;

2.这个属性会被多个对象共享,如果这个对象是一个引用类型,那么就会造成问题;

3.不能给Person传递参数(让每个stu有自己的属性),因为这个对象是一次性创建的,没办法定制化;

 考虑属性的继承:

 

 组合继承:

方法继承+属性继承。

组合继承存在什么问题?

1.无论在什么情况下,都会调用两次父类构造函数;

  • 一次在创建子类原型的时候;
  • 一次在子类构造函数内部(就是每次创建子类实例的时候);

2.所有子类实例事实上都会拥有两份父类属性

  • 一份在当前的实例自己里面(也就是Person本身),另一份在子类对应的原型对象中(也就是person.__proto__里面);
  • 默认访问实例本身;

原型式继承的渊源

那么我们如何解决这个问题?

满足条件:

1.必须创建出来一个对象

2.这个对象的隐式原型必须指向父类的显式原型

3.将这个对象赋值给子类的显式原型

在真实的开发中,一般都会对其进行封装成函数,并且因为创建对象里面不存在constructor属性,也会给其添加该属性。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值