js原型、原型链、原型链继承
程序员文章站
2022-06-15 16:00:19
...
每次面试都要被原型虐一次,写个帖子加深一下记忆。
继承、原型、原型链概念
首先要知道两个概念:
- 几乎所有函数有 prototype 属性,几乎所有对象有 __proto__ 属性。(注意几乎所有这个词,例如:Obejct.create(null) 创建的对象没有任何属性)
- 实例对象的 __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) // 小王
未完待续。。。