JavaScript基础捡漏之原型与原型链

JavaScript基础捡漏之原型与原型链

前言

​ 关于JavaScript的原型与原型链的知识点很多,本文仅是作者在阅读很多大牛的文章后,做出的自己的理解。方便与日后对基础知识的回忆和巩固。

当然如果对读者有帮助,也是笔者非常乐意看到的一件事情。

构造函数

​ 用new关键字来调用的函数,首字母一般大写。用来初始化和创建实例对象的函数。

Code:

functin Person() {}
var person = new Person();
console.log(person); // Person {}

在上述code中Person就是一个构造函数,创建了一个实例对象person;

实例与原型

​ 原型为所有实例对象的“基础”,它定义并实现了一个新创建对象所必须包含的成员列表,为所有实例对象所共享!

当读取实例的属性时,如果查找失败就会继续沿着与实例对象关联的原型中去查找,一直到找最顶层为止。

Code:

function Person() {
}
Person.prototype.name = '张三';

var person = new Person();

person.name = '老王';
console.log(person.name); // 老王

delete person.name;
console.log(person.name); // 张三

第一次打印name结果为老王,是因为我们给实例对象person添加了name属性。打印时他会优先查找实例成员,找到老王后直接返回打印结果

第二次打印name结果为张三,因为我们对实例对象的name进行了删除,打印时,他在实例成员中并未找到name,而后去person实例相关的原型中去找,找到张三后返回打印结果。

实例成员与原型成员

​ 实例成员在对象创建后就存在与实例对象中。原型成员则从对象原型“继承”而来。

当访问对象的某属性时,会优先访问该对象的实例成员,如果查询未果,则会从原型成员中查找。

prototype

​ 翻译过来就是原型的意思。每个函数都有一个prototype属性,提供给构造函数所创建的实例对象关联其原型的能力。

Code:

function Person() {}
Person.prototype.name = '张三';
var person1 = new Person();
var person2 = new Person();

Person.prototype其实就是指向了一个对象。

这个对象正是通过调用该构造函数Person()方法所创建的实例的原型。

得出对应代码就是Person.prototype就是person1和person2原型对象。

_proto_

​ 每个对象都有一个__proto__属性,提供给该对象关联原型的能力。

它指向该对象的原型。

Code:

console.log(Person.prototype === person1.__proto__); // true

constructor

​ 翻译过来就是构造函数,主要用于初始化和创建实例对象。每个原型都有一个constructor,指向与原型关联的构造函数

Code:

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

综上所述,总结图谱如下:

构造函数与实例原型的关系.jpg

Code:

function Person() {};
var person = new Person();
console.log(person.__proto__ === Person.prototype); // true
console.log(Person.prototype.constructor === Person); // true
// Es5提供的查询对象原型的方法
console.log(Object.getPrototypeOf(person) === Person.prototype); // true
console.log(person instanceof Person); // true
console.log(person instanceof Object); // true
console.log(Object instanceof Function); // true
console.log(Function instanceof Object); // true
console.log(Function instanceof Function); // true

Instanceof: 沿着A的__proto__这条线来找,同时沿着B的prototype这条线来找,如果两条线能找到同一个引用,即同一个对象,那么就返回true。如果找到终点还未重合,则返回false

原型的原型

来看这样一段代码:

var obj = new Object();
obj.name = '张三';
console.log(obj.name); // 张三
console.log(obj.__proto__);

对象的创建也可以通过Object来创建,既然创建的也是对象,那么,我们看看他的__proto__属性

JavaScript基础检漏1.png

我们可以发现通过Object创建的实例对象的原型和前面实例对象person._proto_.__proto__访问的属性得到的是一样的结果。可以访问到原始基本Object对象的所有属性和方法。

当我们再次添加一个__proto__属性时,便会发现得到的是null。说明至此已经完成了所有原型的访问。

更新得到图谱:

JavaScript原型链.png

前面的Person还是很好理解的。起初笔者到Object()和Function()这里还是蒙蔽了一会。不是说只有对象才有__proto__属性嘛?为什么最后的所有构造函数都有__proto__属性了?

其实JavaScript中万物皆对象。包括函数也可以理解为对象。不过这些对象都是有Function()创建的实例。

所以也可以说所有的函数都是Function的实例对象。

既然如此,那所有函数的.__proto__其实也就指向了Function.prototype

Object也就可以理解为是一个构造函数,所以就有了

Object.prototype === Function.prototype

这里可能就涉及到了JavaScript中蛋生鸡,鸡生蛋的问题了。后续在知识得到补充后,将会作出补充!

补充

_proto_

​ 该属性并不是某实例对象的属性,可以理解为当使用__proto__时,执行了Object.getPrototypeOf()方法,只不过现在主流浏览器也都支持这种非标准的方法访问原型。

如果对本文有任何疑问可留言,很希望大家能帮我指出错误。

参考文献

讶羽:《JavaScript深入之从原型到原型链》

王福朋:深入理解javascript原型和闭包(5) ——instanceof


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!