小知识,大挑战!本文正在参与“程序员必备小知识”创作活动
前言
在js里,继承机制是原型继承。继承的起点是对象的原型(Object prototype)...
一切皆为对象,只要是对象,就会有proto属性,该属性存储了指向其构造的指针。
原型链⼜是什么呢?
我们都知道当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还 查不到,就去找原型的原型,⼀直找到最顶层为⽌。
举个例子:
const person = function(){};
const father = function(){};
person.eat = '米饭';
father.prototype = person;
const son = new father();
console.log(father.eat) // undefined
console.log(son.eat) // 米饭
为什么 father.price === undefined
- 原型链是依赖于
__proto__,而不是prototype。 - 首先我们先看这句话
const father = function(){} ;这个语句实际上就是var father = new Function(); 由此father.__proto__ === Function.prototype是成立的;而Function.prototype并没有eat属性,所以输出的情况是 undefined;但是father.prototype.eat === '米饭'
为什么 son.eat === 米饭
- 还是那句话原型链是依赖于
__proto__,而不是prototype。 - 我们在代码中
father.prototype = person曾有过这样的赋值操作;还有这一步操作:const son = new father(); 大家可能很懵逼,这两个有什么关系呢?别急,我们看一下下面这个,简单版new操作符你就懂了
function new(obj, ...arg){
let newObj = {} // 创建一个空对象
newObj.__proto__ = obj.prototype
let res = obj.apply(new, arg)
return res instanceof Object ? res : new;
}
- 我们在new的过程中,会将
prototype复制给创建的__proto__,而我们又有了如下的操作father.prototype = person,那么son__proto__ == father.prototype == person,所以当son上没有eat属性就会从__proto__上寻找方法,找到person有eat属性,返回。
这样⼀条通过proto和 prototype 去连接的对象的链条,就是原型链
怎么样使father.eat希望不等于undefined?
就加一句:
Function.prototype.eat = '干饭';
那么:
console.log(father.eat) // 输出 干饭
外部理解链接 最详尽的 JS 原型与原型链终极详解,没有「可能是」
由于原型链涉及构造函数、函数Function、引用类型Object及特定的两个属性prototype和_proto_,因此在谈原型链前先搞清楚他们之间的关系;
- 函数必然有prototype和_proto_两个属性,所有的函数(包括自定义函数)都是Function实例的对象;
- 对象必然有_proto_属性,但不一定有prototype;实例的对象通过_proto_属性连接到构造函数的prototype属性上。而原型链就是从这两者的关系开始一层一层往下找的关系;
- JS 在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做__proto__ 的内置属性,用于指向创建它的构造函数的原型对象。
假设,我们目前有一个对象 person1; person1有一个 proto 属性,创建它的构造函数是 Person,且Person构造函数的原型对象是 Person.prototype ,那么我们就可以大胆的下一个这样的结论
person1.__proto__ === Person.prototype
function person() {}
const person1 = new person()
那么 =>
person1.__proto__ === person.prototype // true
person.prototype.constructor=== person // true
Object.getPrototypeOf(person1) === person.prototype // true
小小的延伸一下
因为绝大部分浏览器都支持__proto__属性,所以它才被加入了 ES6 里(ES5 部分浏览器也支持,但还不是标准)
总结一下:
每一个对象数据类型(普通的对象、实例、
prototype......)也天生自带一个属性__proto__,属性值是当前实例所属类的原型(prototype)。原型对象中有一个属性constructor, 它指向函数对象。