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

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

继承、原型、原型链概念

首先要知道两个概念:

  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 指向构造函数

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

// 证明一下 F.prototype.constructor 指向 F 并且 它是一个函数
function F() {}
console.log(F.prototype.constructor === F); // true
console.log(F.constructor === Function); // true

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

function F() {}
var f = new F;
console.log(f.__proto__.constructor.name); // F ,表示 f 的 原型对象是 F
所有构造函数的 constructor 指向 Function
function F() {}
console.log(F.constructor === Function) // true
console.log(Object.constructor === Function) // true
console.log(Array.constructor === Function) // true
// 这里要注意 原型对象的 constructor 并不指向 Function
console.log(F.prototype.constructor === Function) // false
// 因为原型对象的 constructor 指向 构造函数 F ,构造函数的 constructor 指向 Function
console.log(F.prototype.constructor.constructor === Function) // true

接下来是原型链

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

那么,原型链有什么用呢

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

看一段基于原型链查找属性的代码
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) // 小王
原型链图解

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值