看完这篇,再也不害怕别人问我什么是原型了

2023-06-14 0 832

看完这篇,再也不害怕别人问我什么是原型了

序言

蓝本、蓝本链假如是被绝大多数后端er说烂的词,但假如除了许多人无法完备的说明这三个文本,总之也主要包括我自己。

最先一则蓝本链该文所写2019年07月,那个这时候也是费了大哥劲才认知到了下年,到那时大体上忘的相差无几了。阔别一年,浓厚兴趣所系再次已经开始TRAP呵呵蓝本和蓝本链的文本。

JavaScript中的第一类

在JavaScript中,第一类被称作是一连串特性的子集。

创建第一类的方式也有许多种不同,最常用的一类是洼瓣括弧的方式:

var obj = {}; obj.name = 紫菊; obj.age = 18;

此种方式事实上是上面此种方式的句法糖:

var obj = new Object(); obj.name = 紫菊; obj.age = 18;

除此以外,在JavaScript中也能透过构造表达式自订第一类。

function Cat(){} var catMimi = new Cat(); // 自订第一类

假如两个表达式采用newURL初始化,所以那个表达式就能称作是缺省,不然是一般表达式。

甚么是蓝本

一句话简单总结蓝本:蓝本是两个第一类。

在后面的总结中,蓝本可能会被描述为蓝本第一类,其等价于蓝本

蓝本从哪里来?蓝本那个第一类存在于哪里,需要透过代码去创建吗?

我们说第一类是一连串特性的子集,那蓝本那个第一类包含甚么特性呢?

如何操作和采用蓝本?

接下来我们两个两个问题去探究。

▣ 蓝本从哪里来

JavaScript会为所有的表达式创建两个蓝本。

function Cat(){} 

上面的代码中我们创建了两个Cat表达式,那那个Cat表达式就有两个蓝本,用代码表示是:Cat.prototype。

同样我们创建两个表达式Fn1,表达式Fn1就有两个蓝本,用代码表示是Fn1.prototype。

表达式名称的大写和小写本质上没有任何区别

▣ 蓝本包含哪些特性

前面我们说过以下这两点:

蓝本是两个第一类

第一类是一连串特性的子集

那蓝本都包含哪些特性呢?

前面我们已经知道蓝本用代码表示是:functionName.prototype,那我们在代码中console.log呵呵。

function Cat(){} console.log(“Cat.prototype:”); console.log(Cat.prototype); function Dog(){}console.log(“Dog.prototype:”); console.log(Dog.prototype);

Firefox浏览器中的输出结果如下:

看完这篇,再也不害怕别人问我什么是原型了

能看到表达式的蓝本默认有三个特性:constructor和 。

其中,表达式蓝本的constructor特性指向表达式本身。

看完这篇,再也不害怕别人问我什么是原型了

表达式蓝本的 特性称作隐式蓝本,后面我们会分出一节单独介绍隐式蓝本。

▣ 如何操作和采用蓝本

正常我们操作两个一般第一类的方式是上面这样的:

function Cat(){} console.log(“Cat.prototype:”); console.log(Cat.prototype); function Dog(){} console.log(“Dog.prototype:”); console.log(Dog.prototype);

蓝本既然也是两个第一类,所以操作蓝本的方式和上述的方式相同。

function Cat(){} Cat.prototype.type =cat; Cat.prototype.color = White; Cat.prototype.sayInfo = function(){ console.log(this.type + is + this.color); }

此时再次打印Cat.prototype就能看到我们添加到蓝本上的特性:

看完这篇,再也不害怕别人问我什么是原型了

访问蓝本第一类上的方法和特性:

看完这篇,再也不害怕别人问我什么是原型了

以上这些操作蓝本的方法,对于真正的项目开发并没有甚么参考价值,不过不用着急,后面我们会详细讲解

隐式蓝本

前面我们在总结表达式的蓝本第一类时提到过隐式蓝本。

看完这篇,再也不害怕别人问我什么是原型了

那事实上,JavaScript会为所有的第一类创建叫隐式蓝本的特性。我们一直说蓝本是两个第一类,所以在上面的截图中,蓝本也有两个隐式蓝本特性。

▣ 隐式蓝本的代码表示

隐式蓝本

是第一类的私有特性,在代码中能这样访问:obj.__proto__。

obj.__proto__此种写法是非标准的,一些低版本的浏览器并不支持这样的写法

我们在浏览器的控制台中实际访问呵呵:

看完这篇,再也不害怕别人问我什么是原型了

从打印的结果能看到隐式蓝本也是两个第一类,那隐式蓝本那个第一类里面又包含甚么特性呢?上面我们一起来看看。

▣ 隐式蓝本存在的意义

首先我们写两个简单的示例:

function Cat(){} var catMimi = new Cat(); var catJuju = new Cat();

在上面这段代码中,我们创建了两个Cat表达式,并且透过newURL创建了以Cat为缺省的三个实例第一类catMimi和catJuju。

接下来我们在浏览器的console工具中看看这三个实例第一类的隐式蓝本都包含了那些特性。

看完这篇,再也不害怕别人问我什么是原型了

能看到,catMimi.__proto__和catJuju._proto__的结果貌似是一样的,而且眼尖的同学假如也发现了那个打印结果似乎和前面一节【蓝本包含那些特性】中打印的Cat.prototype是一样的。

那话不多说,我们用==运算符判断一下即可:

看完这篇,再也不害怕别人问我什么是原型了

能看到所有的判断结果均为true。

由于第一类catMimi、catJuJu都是由Cat表达式创建出来的实例,所以总结出来结论是:第一类的隐式蓝本__proto__指向创建该第一类的表达式的蓝本第一类。

蓝本链:蓝本和隐式蓝本存在的意义

前面我们总结了蓝本、隐式蓝本的概念以及如何采用代码操作蓝本和隐式蓝本,总的看来蓝本和隐式蓝本好像也没有特别厉害的地方,它们到底有甚么用呢?

▣ 所有的实例第一类共享蓝本上定义的特性和方法

我们来看上面这样两个示例:

function Cat(name, age){ this.type = RagdollCat; //布偶猫 this.eyes = 2; this.name = name;this.age = age; this.sayInfo = function(){ console.log(this.type + + this.name + is + this.age + years old); } }

在那个示例中,我们创建了两个Cat表达式,同时Cat表达式有五个特性:type、eyes、name、age、sayInfo,其中type和eyes特性已经有了初始值,而name、age透过参数传递并赋值;sayInfo对应是两个表达式,打印出type、name和age的值。

接着我们创建Cat的三个实例第一类catMimi、catJuju,并传入不同的name和age参数。

var catMimi = new Cat(Mimi, 1); var catJuju = new Cat(Juju, 2);

控制台查看呵呵我们创建的第一类:

看完这篇,再也不害怕别人问我什么是原型了

能看到这三个第一类有着相同的特性,由于type、eyes是在Cat表达式创建时已经有了固定的初始值,所以这三个特性值是相同的;sayInfo表达式也都是相同的功能,打印出一些特性的信息;只有name、age是透过参数传递的,各自的值不相同。除此以外呢,catMimi和catJuju是三个不同的第一类,两者的特性值互相独立,修改其中任意两个的特性值并不会影响另外两个第一类的特性值。

假如之后我们有更多这样的第一类,JavaScript还是会为每两个第一类创建相同的特性,而这些所有的第一类都拥有着相同的type、eyes特性值和相同功能的sayInfo表达式。这无疑造成了内存浪费,那那个这时候我们就能将这些特性定义到表达式的蓝本第一类上:

function Cat(name, age){ this.name = name; this.age = age; } Cat.prototype.type = RagdollCat; //布偶猫 Cat.prototype.eyes =2; Cat.prototype.sayInfo = function(){ console.log(this.type + + this.name + is + this.age + years old); } var catMimi = new Cat(Mimi, 1); var catJuju = new Cat(Juju, 2);

然后我们再来看看这三个第一类:

看完这篇,再也不害怕别人问我什么是原型了

能看到这三个第一类那时只包含了三个特性,就是Cat缺省文本内部定义的三个特性:name、age。

接着我们在去访问第一类上的type、eyes和sayInfo:

看完这篇,再也不害怕别人问我什么是原型了

我们的实例第一类还是能正常访问到特性,方法也打印出来正确的信息。那到底是怎么访问到的呢?

▣ 蓝本链

在上两个示例代码中,我们将一些特性和方法定义到表达式的蓝本上,最后采用该表达式创建出来的实例第一类能正常访问蓝本上定义的特性和方法,这是怎么做到的呢?

前面我们说过:第一类的隐式蓝本指向创建该第一类的表达式的蓝本第一类,所以当实例第一类中没有某个特性时,JavaScript就会沿着该实例第一类的隐式蓝本去查找,这便是我们所说的蓝本链。

看完这篇,再也不害怕别人问我什么是原型了

那既然是链,我们想到的假如是两个连着两个的东西,所以假如不仅仅是当前实例第一类的隐式蓝本指向创建该第一类的表达式的蓝本第一类,所以我们在对catMimi第一类做点操作:

看完这篇,再也不害怕别人问我什么是原型了

在上面的操作,我们初始化了catMimi的hasOwnProperty方法,很明显我们并没有为那个第一类定义该方法,那那个方法从哪里来呢?

答案依然是蓝本链:

初始化catMimi.hasOwnProperty()方法在实例第一类catMimi中查找特性,发现没有该特性去catMimi.__proto__中查找,因为catMimi.__proto__=Cat.prototype(实例第一类的隐式蓝本指向创建该实例的表达式的蓝本),也是在Cat.prototype中查找hasOwnProperty特性,很明显Cat.prototype也没有该特性于是继续沿着Cat.prototype.__proto__查找,又因为Cat.prototype.__proto__ = Object.prototype(我们一直在强调原型是两个第一类,既然是第一类,是由Object表达式创建的,所以Cat.prototype的隐式蓝本指向Object表达式的蓝本)

我们打印呵呵Object.prototype的是否包含hasOwnProperty特性:

看完这篇,再也不害怕别人问我什么是原型了

能看到,Object.prototype中存在hasOwnProperty特性,所以catMimi.hasOwnPrototype事实上初始化的是

Object.prototype.hasOwnProperty。
看完这篇,再也不害怕别人问我什么是原型了

总结

本篇该文到此基本就基本结束了,相信大家假如对蓝本和蓝本链有了一定的了解。最后呢,我们在对本篇该文做两个总结。

看完这篇,再也不害怕别人问我什么是原型了

原文链接:

https://mp.weixin.qq.com/s/59p32Xe03YCGhP2uTBjTUg

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务