原型链
Javascript 修炼之路必须越过的一座大山
- 理解 JS 面向对象
 - 理解
 prototype和__proto__;- 理解原型链和原型继承;
 
普通对象 & 函数对象
function fn1() {}
const fn2 = function () {};
const fn3 = new Function("language", "console.log(language)");
// fn1 fn2 fn3 都是函数对象 可以通过 typeof 查看
 
const ob1 = {};
const ob2 = new Object();
const ob3 = new fn1();
// ob1 ob2 ob3 都是普通对象所有 Function 的实例都是函数对象,而其他的都是普通对象。
普通对象也可以通过任何的函数对象实例化(new)生成
prototype & __proto__
| 对象类型 | prototype | __proto__ | 
|---|---|---|
| 普通对象 | × | √ | 
| 函数对象 | √ | √ | 
结论:
- 只有函数对象具有 prototype 属性
 prototype和__proto__都是 JavaScript 在定义一个函数或对象时自动创建的预定义属性(自带的)prototype被实例的__proto__所指向(构造器视角)__proto__指向构造器的prototype(实例视角)
所以也就是说
function fn() {}
const ob = {};
fn.__proto__ === Function.prototype; // true
ob.__proto__ === Object.prototype; // true那么问题来了:既然 fn 是一个函数对象,那么 fn.prototype.__proto__ 到底等于什么?
console.log(fn.prototype.__proto__ === Object.prototype); // true上面所说,函数对象的 prototype 是在创建时自动生成的,所以我们可以猜测:
// 实际代码
function fn1() {}
 
// JavaScript 自动执行
fn1.prototype = {
  constructor: fn1,
  __proto__: Object.prototype,
};
 
fn1.__proto__ = Function.prototype;普通对象为什么没有 prototype,因为他不可能再次作为一个普通对象的构造器,也没有实例的 __proto__ 指向他的 prototype。
同样,Function 也是 Function 的实例
Function.__proto__ === Function.prototype; // true原型链
同时可以带着这个问题“为什么只有函数对象有 prototype?”
上面讲解的 prototype 和 __proto__ 主要就是为了构造原型链
const Person = function (name, age) {
  this.name = name;
  this.age = age;
};
 
Person.prototype.getName = function () {
  return this.name;
};
 
const coyote = new Person("coyote", 24);
 
console.log(coyote);
console.log(coyote.getName());可以通过打印看到 coyote 实际上只有 age 和 name 这两个 “own property”,那么 getName 是怎么调用成功的?
- 会向上查找 
__proto__,找到Person.prototype那他有getName方法,于是调用成功 
所以在访问对象某个属性/方法时,在当前对象找不到时,就会不断的访问 __proto__ 查找,直到找不到为止(返回 undefined)
coyote["something"];
coyote.__proto__;
coyote.__proto__["something"];
coyote.__proto__.proto__;
coyote.__proto__.__proto__["something"];
coyote.__proto__.proto__.__proto__; // null 停止万物皆空——原型链的终点,Object.prototype.__proto__ -> null
检验下理解程度
以下代码的输出结果是?
console.log(coyote.__proto__ === Function.prototype);- false
 
Person.__proto__ 和 Person.prototype.__proto__ 分别指向何处?
- 首先 Person 是函数对象,他的 
__proto__指向Function.prototype Person.prototype是普通对象,他的__proto__指向Object.prototype
prototype 中的 constructor 指向构造器自己(循环引用)
为什么只有函数对象有 prototype?
- 函数对象可以作为普通对象的构造器,而实例化之后的对象需要通过 
__proto__去链式访问查找属性 
小结
对象属性的访问,会通过原型链向上查找,JS 中原型链的入口通常是 __proto__(当然也可以是其他属性名,只是一种约定),指向构造器(OOP 中 Class)的样子(prototype),最终会找到 Object.prototype.__proto__ 就到终点了。
题外话
instanceOf(obj, Constr) 的原理?一路从 obj.__proto__ 向上找有没有指向 Constr.prototype
Object.create 生成出来的对象的原型链?对象的 __proto__ 指向的就是 create 接受的参数对象
中断一个实例的继承?修改 coyote.__proto__ = null(瞎搞可以,没事别瞎搞)