原型和它的原型链

简介: 每个函数都有一个自己的prototype属性,默认是一个object空对象(即:函数的原型对象)


网络异常,图片无法展示
|

原型(prototype)


每个函数都有一个自己的prototype属性,默认是一个object空对象(即:函数的原型对象)

网络异常,图片无法展示
|


可以看出此时的object空对象是指内部不存在我们自己添加的属性和方法。原型对象中存在constructor、__proto__属性。


原型对象中constructor指向的是它的函数对象。原型对象、函数对象关系如下:

网络异常,图片无法展示
|

原型添加方法


我们可以给原型对象添加属性或方法,通过实例对象可以直接访问。

function Fun() { }
Fun.prototype.test = function () {
  console.log('test');
}
let f = new Fun()
f.test()

tip: new 一个对象背后做了什么?


创建一个空对象;给对象设置__proto__,值为构造函数对象的prototype属性值(this.__proto__ =Fun.prototype);执行构造函数体,给对象添加属性或方法。


如果直接给函数原型添加方法,函数只能通过prototype访问,调用prototype无意义。

function fun() { }
 fun.prototype.tick = function () {
   console.log('tick');
 }
fun.prototype.tick()


显式原型与隐式原型


显示原型:每个函数都有一个prototype属性。在定义函数时自动添加,默认为object空对象。


隐式原型:每个实例对象都有一个__proto__属性。在创建对象时自动添加,默认指向构造函数的prototype属性值。


所有函数的__proto__都是一样的(函数都是new Function的实例)。

function Fun() { }
let f1 = new Fun()
let f2 = new Fun()
console.log(f1.__proto__ === Fun.prototype); // true
console.log(f2.__proto__ === Fun.prototype); // true

Fun函数在定义时,会自动生成一个原型对象,f1、f2是Fun的实例。

网络异常,图片无法展示
|
Fun函数通过prototype指向自身的原型对象,f1、f2实例通过隐式调用__proto__,指向的是自己构造函数的原型对象。三者其实指向的是同一个地址(原型对象)。

原型对象:相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象。

显式原型与隐式原型关系:


实例.__proto__ === 构造函数.prototype


函数、实例、object关系


首先定义一个函数,函数的prototype属性,指向函数的原型对象。函数的原型对象中的__proto__指向Object的原型对象。


function Fun() { }
let f1 = new Fun()
let f2 = new Fun()


执行上面这段代码,首先会创建一个Object,Object指向自己的函数对象。Object函数对象的prototype指向Object的原型对象。Object的原型对象中包含一些属性方法。比如我们常用的:hasOwnProperty、valueOf、toString等。且Object的原型对象的原型为null。

网络异常,图片无法展示
|

其次,创建函数时,自动创建函数原型对象。函数原型的__proto__指向Object的函数对象。

网络异常,图片无法展示
|

可以通过以下语句可以验证:

console.log(Fun.prototype.__proto__ == Object.prototype);
console.log(f1.__proto__.__proto__ == Object.prototype);

如果上图不能够理解,可以转化成下图:

网络异常,图片无法展示
|


原型链


定义:访问一个对象的属性时,先在自身属性中查找,找到返回;否则再沿着__proto__这条链向上找,找到返回;如果最终没找到,返回undefined


本质:隐式原型链

作用:查找对象的属性(方法)


基础原型链

function Fun() { }
Fun.prototype.test = function () {
  console.log('Fun 中的 test');
}
let f1 = new Fun()
f1.test = function () {
  console.log('f1 实例中的 test');
}
let f2 = new Fun()
f1.test() // f1 实例中的 test
f2.test() // Fun 中的 test
console.log(f1.test2); // undefined
console.log(f1.test2()); // TypeError: f1.test2 is not a function

网络异常,图片无法展示
|

f1实例中存在test方法所以直接使用。


而f2实例中不存在test方法,所以通过原型链(__proto__)向上查找,f2的上一级Fun中存在test方法,所以输出:Fun 中的 test。

f1实例中不存在test2、test2(),对于test2属性输出undefined,对于test2()函数为TypeError。


构造函数、原型、实例对象关系


function fun(){}


上面一行代码就相等于先创建Object、Function,并实例化。再实例化fun函数。 开始加载:


引入Object


网络异常,图片无法展示
|

引入Function

网络异常,图片无法展示
|


至于为什么会多一条隐式原型?


那是因为:所有函数的隐式原型(__proto__)都是一样的(函数就相当于是Function的实例)。所以Function的函数对象的隐式原型指向自身的原型对象。就相当于

Function = new Function();
Function.__proto__ = Function.prototype


Object与Function


Object函数对象,本身就是一个函数。Object函数就相当于是Function的实例。

网络异常,图片无法展示
|

fun执行

网络异常,图片无法展示
|
fun函数就相当于是Function的实例。添加fun的原型指向Function的原型。

网络异常,图片无法展示
|

关系加强版

console.log(Function.prototype instanceof Object); // true


Function原型是一个Object对象,就相当于是Object函数的实例。所以Function原型的隐式原型等于Object的显式原型。


fun函数原型同样是Object对象。

得到关系如下:

网络异常,图片无法展示
|


总结:

  • 函数的显式原型指向的都是默认的Object空对象(原型内部只有constructor和__proto__)。函数的原型对象都是Object实例。但是Object的原型对象为null。Object的原型对象是原型链的尽头。

网络异常,图片无法展示
|

  • 所有函数的隐式原型(__proto__)都是一样的(函数都是new Function的实例),指向Function函数的原型。实例对象的隐式原型等于构造函数的显示原型。

网络异常,图片无法展示
|


原型链继承


基础原型链中讲解了,一个构造函数有多个实例,实例继承了原型上的方法属性。

下面是两个不同的构造函数之间的父子继承。

function Fun1() {
  this.tag = 'div1'
}
Fun1.prototype.test = function () {
  console.log('Fun1 中的 test');
}
function Fun2() { }
Fun2.prototype = new Fun1()
Fun2.prototype.constructor = Fun2
let f2 = new Fun2()
console.log(f2.tag);  // div1
f2.test();  // Fun1 中的 test


两个构造函数之间,Fun2构造函数将原型指向Fun1的实例。在调用Fun2时,Fun2构造函数会通过原型链找到Fun1的原型对象,从而获取原型中的属性或方法。

网络异常,图片无法展示
|


特别要注意的是:我们还需要将Fun2的原型对象指向自身构造函数。如果不指回自身,那么自身的原型属性或方法将失效

网络异常,图片无法展示
|


instanceof


定义:检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

A instanceof B


A:实例。看A的原型链(__proto__

B:构造函数。看B的 prototype 属性。 判断是否正确,只要跟据下图找A、B两个指向是否能够指向同一个。

网络异常,图片无法展示
|

function fun() { }
let f1 = new fun()
console.log(f1 instanceof Fun); // true
console.log(f1 instanceof Object); // true
console.log(Object instanceof Function); // true
console.log(Object instanceof Object); // true
console.log(Function instanceof Function); // true
console.log(Function instanceof Object); // true
console.log(Object instanceof fun); // false


Object instanceof Function。Object为实例,Function为构造函数

网络异常,图片无法展示
|

Object instanceof fun。Object为实例,fun为构造函数。

网络异常,图片无法展示
|

目录
相关文章
|
4月前
|
JavaScript 前端开发
原型,原型链
原型,原型链
|
前端开发
前端原型和原型链构造函数的使用
前端原型和原型链构造函数的使用
63 0
|
前端开发
前端原型和原型链构造函数的使用
前端原型和原型链构造函数的使用
73 0
|
4月前
|
设计模式 前端开发 JavaScript
【面试题】 对象、原型、原型链与继承?这次我懂了!
【面试题】 对象、原型、原型链与继承?这次我懂了!
|
4月前
|
设计模式 前端开发 JavaScript
【面试题】对象、原型、原型链与继承 ,你了解多少?
【面试题】对象、原型、原型链与继承 ,你了解多少?
|
10月前
|
JavaScript 前端开发
原型和原型链
原型和原型链
28 0
|
存储 JavaScript 前端开发
深入理解原型与原型链
当我们在 JavaScript 中创建一个对象时,它会自动继承一个称为“原型”的对象。如果该对象访问一个属性或方法,但在自己的属性列表中找不到,它会沿着原型链向上查找,直到找到该属性或方法为止。在本篇文章中,我们将深入探讨 JavaScript 中的原型与原型链。
|
JavaScript 前端开发
JavaScript 中的原型、对象原型、原型继承和原型链
在 JavaScript 中,原型(prototype)是一个对象,它用于实现对象之间的继承和共享属性。JavaScript 是一种基于原型的编程语言,每个对象都有一个原型,而原型又可以拥有自己的原型,形成一个原型链。
|
前端开发
前端原型和原型链constructor
前端原型和原型链constructor
81 0
|
JavaScript
关于原型、原型链我所知道的
关于原型、原型链我所知道的
70 0