欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

js原型、原型链、原型链继承

程序员文章站 2022-06-15 16:00:19
...

每次面试都要被原型虐一次,写个帖子加深一下记忆。

继承、原型、原型链概念

首先要知道两个概念:

  1. 几乎所有函数有 prototype 属性,几乎所有对象有 __proto__ 属性。(注意几乎所有这个词,例如:Obejct.create(null) 创建的对象没有任何属性)
  2. 实例对象的 __proto__ 指向创建它构造函数的原型对象。
console.log(Obejct.create(null).__proto__); // undefined 

我们先解释什么是继承

继承是面向对象中的一个概念,与多态、封装为面向对象的三个基本特征。继承可以是子类具有父类的属性和方法或者重新定义、追加属性或方法。

然后了解什么是原型

每个函数创建后,都会有一个默认 prototype 属性,这个属性指向一个由 Object 创建的对象,这个对象为函数的原型。换句话说,prototype 就是原型对象。

看一下代码

function F() {}
console.log(F.prototype);
// 打印结果
// {
//		constructor: f F()
//		__proto__: Obejct
// }

每个原型对象有两个属性: __proto__ 和 constructor。

先看一下 __proto__ 属性(隐式原型)

几乎所有对象都有 __proto__ 属性,它指向创建它构造函数的原型对象。

// 证明一下实例的 __proto__ 指向构造函数的原型对象 
function F() {}
var f = new F();
console.log(f.__proto__ === F.prototype) // true
构造函数创建对象
function Person() {}
var p = new Person();
console.log(p.__proto__);
// 打印结果
// {
// 		constructor: ƒ Person()
//		__proto__: Object
// }

// p.__proto__ 指向的是 Person.prototype
console.log(p.__proto__ === Person.prototype) // true
字面量创建对象
var o = {};
console.log(o.__proto__);
// 打印结果
// {
//		constructor: ƒ Object() // 字面量对象是由 Object 创建的
//		hasOwnProperty: ƒ hasOwnProperty()
//		isPrototypeOf: ƒ isPrototypeOf()
//		propertyIsEnumerable: ƒ propertyIsEnumerable()
//		toLocaleString: ƒ toLocaleString()
//		toString: ƒ toString()
//		valueOf: ƒ valueOf()
//		__defineGetter__: ƒ __defineGetter__()
//		__defineSetter__: ƒ __defineSetter__()
//		__lookupGetter__: ƒ __lookupGetter__()
//		__lookupSetter__: ƒ __lookupSetter__()
//		get __proto__: ƒ __proto__()
//		set __proto__: ƒ __proto__()
// }

// 所以 o.__proto__ 指向的是 Object.prototype
console.log(o.__proto__ === Object.prototype); // true

由此可知道,字面量创建对象也是由 Object 构造函数创建的。

constructor 属性又是什么

constructor 属性是专门为 Function 设计的。创建一个 Function 时,js 内部首先会创建一个 prototype 对象,然后在原型对象中会创建一个 constructor 属性,指向的是函数本身(指向同一块内存)。

// 证明一下
function F() {}
console.log(F.prototype.constructor === F); // true

因为实例的隐式原型(__proto__)指向创建它构造函数的原型对象,constructor 属性可以用来进行对象类型判断的,但是不安全,有更好的方法。 类型判断请阅读

function F() {}
var f = new F;
console.log(f.__proto__.constructor.name); // F ,表示 f 的 原型对象是 F

接下来是原型链

因为几乎所有对象的都有 __proto__ 属性,指向创建它构造函数的原型对象。同样的,原型对象本质也是对象,它也具有 __proto__ 属性,指向另一个原型对象,以此类推,实例对象通过隐式原型和所有的原型对象会串联成一个链式结构,这个链式结构就称作原型链。

那么,原型链有什么用呢

对象在查找某个属性时,会先从自身查找,如果没有找到,会依次从原型链上进行查找,直到找到或者查找到顶层。

看一段基于原型链查找属性的代码
function Person() {}
var p = new Person();

// 直接查找 name,查找到顶层,没有此属性返回 undefined
console.log(p.name) // undefined

// 原型对象添加属性 name,自身查找不到,继续向上层查找
Person.prototype.name = '小马';
console.log(p.name) // 小马

// 对象 p 增加自身属性 name,自身有属性,停止查找
p.name = '小王';
console.log(p.name) // 小王

未完待续。。。