(JavaScript)原型与原型对象

第一次记录自己学习的脚步,我选择了JavaScript中自认为比较熟悉的一小部分来说,诚挚的希望能够得到各位前辈的批评与指正。而对于看到我这篇笔记希望从这篇笔记中收获知识的读者,我希望你们可以参考权威,拥有自己的真知灼见而不听我一家之言,以免有不正确的地方误导了读者。

1.原型,原型对象是什么?

**1.原型(prototype)是函数的一个属性,这个属性是个指针指向原型对象。
2.原型对象(prototype object)是一个属于其所在函数的空对象,可以通过它给函数添加属性和方法。**
值得注意的是原型对象也拥有一个属性——constructor指向其函数。

通过一张图我们可以更好的理解这几者的关系:

(JavaScript)原型与原型对象

上图给我们传达了几个信息:
**1.实例拥有一个属性[[prototype]],这个属性指向其构造函数的原型对象。
2.原型对象也是构造函数的实例**

2.那么,这个东西有什么用?

我们知道,JavaScript是一个基于对象的语言,而与Java等语言不同的是JavaScript没有类的概念。而要实现类的功能我们则需要模拟类。在模拟类的实现中使用原型和原型对象我们就可以更好的创建具有封装性,共享性的类(对象),而这种创建类(对象)的模式就叫原型模式。

这也解释了为什么在《JavaScript高级程序设计》中描述原型以及原型对象用于创建对象,而在《JavaScript权威指南》中描述原型以及原型对象用于创建类。(因为在JavaScript中没有类,有类也只是对象模拟出来的,包括ES6中的class关键字)

3.该如何使用它?

原型模式模拟类十分简单:

var Foo = function(){}
Foo.prototype.username = 'ec'
console.log(Foo.prototype)  // --> a{username = 'ec'}
var f = new Foo()
console.log(f.username)     //--> 'ec'
console.log(Foo.username)   //--> underfined

我们可以发现两个信息:
**1.原型对象与它所在函数同名
2.属性已经被添加进了原型对象中
3.使用时必须实例化构造器函数。**

需要注意的是原型模式模拟类是有缺点的,例如以下代码:

function Person(){}
Person.prototype = {
  constructor: Person,
  name: "李小山",
  age: 20,
  family: [
    "李大山",
    "张晓梅",
  ],
}
var person1 = new Person()
var person2 = new Person()
person1.family.push("李巨山")
console.log(person1.family)  //--> ["李大山", "张晓梅", "李巨山"]
console.log(person2.family)  //--> ["李大山", "张晓梅", "李巨山"]

从上面这个例子我们可以得到几个信息:
**1.因为原型模式规定我们在原型对象上添加属性与方法,所以无法传递初始化参数
2.因为过度的“共享”以至于当一个实例改变了引用类型的值,所有实例的该值都会被改变。**

以上的原因就造成了很少有使用纯的原型模式创建对象,而其他混合使用原型模式的创建对象模式就不在这里展开说了。

4.还是不太明白原型对象、对象、函数

终于讲到了这部分,这个部分我们可以提一个更加具体的问题:
原型对象与对象、函数、构造函数、实例的关系是什么?
首先是函数与构造函数的区别:

  • 构造函数需要new操作符实例化才能使用
  • 构造函数没有return语句
  • 构造函数this指向调用者往往是调用构造函数的实例本身,而函数使用this则会指向window全局对象
  • 构造函数名首字母大写(非强制性的)

实例就是构造函数创建出来的对象,拥有构造函数的属性与方法

知道了这些后我们就可以通过这张图来明确它们之间的关系了:

(JavaScript)原型与原型对象

这个图看似唬人,其实只需要知道三点就可以秒懂了:
1.JavaScript中一切皆是对象
2.所有对象有[[prototype]]属性,指向其构造函数的原型对象
3.所有函数都有prototype属性,指向其原型对象
4.所有实例都有constructor属性,指向其构造函数

图中有两个地方可能比较难以理解:

  • 为什么内置对象Function()的原型对象function()是个函数对象?

因为内置对象Function()也是函数,而函数就是function,这就造成了一种鸡生蛋、蛋生鸡的问题,而让Function()的原型对象为函数对象就可以添加函数方法给Function(),这也解释了为什么Function()的[[prototype]]属性也指向其原型对象。

  • 内置对象的原型对象的[[prototype]]属性指向谁?

这也是一个鸡生蛋、蛋生鸡的问题,是对象创建了对象,那么追根溯源谁真正创建了对象呢?答案就是Null空。(图中这个地方箭头指向错误,望见谅)

至此,我们算是大致了解了JavaScript中有关于原型的基本知识了,其实还有很多问题我们没有解决,比如比原型模式更好的创建对象模式,还有关系图中有关于继承的部分都还没用详细说明。篇幅有限,下次再聊。

相关推荐