原型和原型链

JS原型链

构造函数、实例对象

  • person是Person函数的实例对象
  • Person是函数person的构造函数
function Person()
let person = new Person()
person // Person {__proto__} 是一个对象,有一个__proto__属性
person.constructor === Person // true 指向Person的构造函数本身

实例对象的 constructor 属性指向构造函数本身

其实这么说不准确。后面会说明其实是 person 实例对象通过 __proto__属性指向了构造函数的原型对象 prototypeprototypeconstructor 属性才指向了构造函数 Person 本身。所以打印结果上来看 person 上本身是没有 constructor 的,但是却能打印 person.constructor)


JS内置对象Object、Function

  • Function 函数和 Object 函数是 JS 内置对象,也叫内部类。这些内置函数可以当作构造函数来使用。
  • 为什么 “函数即对象”?关于 Function 对象的特殊性。
functino Person(){}
Person.construcotr === Function // true 构造函数的constructor属性指向内置函数Function
  • 所有函数都是 Function 函数的实例对象,所以常说函数即对象,JS 中一切皆对象。
Function.constructor === Function // true
Object.constructor === Functino // true
  • 说明Function函数也是是自己的构造函数
  • 说明Function函数也是Object这类内置对象的构造函数
let obj = {}
obj.constructor === Object // true 普通对象的constructor 属性都是指向Object构造函数

整理一下 constructor 指向链

  • person.constructor -> Person构造函数,Person.constructor -> Function 函数, Function.constructor -> Function自己
  • obj.constructor -> Object,Object.constructor -> Function函数,Function.construtor -> Function自己

原型对象prototype

  • 为了让实例对象能够共享一些属性和方法,将他们全部放在了 构造函数的原型对象prototype 上。
Person.prototype.sayHello = function () {
  console.log("Hello!");
};
let person1 = new Person();
let person2 = new Person();

person1.sayHello === person2.sayHello; // true

  • 关于 Function 的特殊性:所有函数本身是 Function 函数的实例对象,所以 Function 函数中同样会有一个 prototype 对象放它自己实例对象的共享属性和方法。
  • constructor 也是一个共享属性,存放在原型对象 prototype 中,作用依然是指向自己的构造函数。
function Person(){}
Person.prototype.constructor === Person // true

从示意图中看出实例对象构造函数原型对象 prototypeconstructor 的存在关系。

在这里插入图片描述

实例对象的 constructor 属性指向该实例对象的构造函数,constructor属性作为共享属性存在于构造函数的prototype上。


__proto__ 属性

原型链的核心公式: person.__proto__ === Person.prototype即实例对象的 __proto__ 属性指向构造函数的原型对象 prototype

  • 通过在 persion 对象内部创建了一个属性 __proto__ 直接指向自己的原型对象 prototype,通过原型对象就可以找到共享属性 constructor ,此时 constructor 指向该实例对象的构造函数 Person 了。
function Person() {}
let person = new Person();
person.__proto__ == Person.prototype;
// constructor存在于prototype上,指向构造函数
Person.prototype.constructor == Person;
// 所以person.__proto__可以读取原型prototype上的constructor,指向构造函数
person.__proto__.constructor == Person;
person.constructor === Person; // true 也成立?
// 这就是因为原型链查找了,后面会详细说。因为person通过原型链查找到了constructor,并且指向了Person构造函数。
// 注意
Person.constructor == Function; // 因为Person的构造函数是Function

关于 Function 的特殊性:

  • 因为 JS 内所有函数都是 Function 函数的实例对象,所以 Person 函数也有个__proto__ 属性指向自己的原型对象,即 Function 函数的 prototype

  • Function 函数也有个__proto__ 属性指向自己,因为它拿自身作为自己的构造函数

由于原型对象 prototype 也是个对象,那它也有个__proto__属性,指向自己的原型对象。那它的构造函数是谁呢

Person.prototype.__proto__ === Object.prototype

所以函数内的原型对象 prototype 跟所有普通对象一样,也都是内置Object 函数的实例对象。

总结: 实例对象的__proto__ 属性指向构造函数的原型对象 prototype; 而所有原型对象 prototype 的 __proto__ 属性,都指向了Object的原型对象!


什么是原型链

  • 每个 实例对象 都有一个 私有属性__proto__ 指向它的 构造函数 的 原型对象prototype。该原型对象也有自己的私有属性 __proto__指向原型对象…层层向上直到一个对象(内置Object对象)的原型对象为 null,并作为这个原型链中的最后一个环节。`

  • 将一个个 实例对象原型对象 关联在一起,关联的原型对象也是别人的实例对象,所以就形成了串连的形式,也就形成了我们所说的原型链

  • Object 函数是所有对象通过原型链追溯到最根的构造函数。即 “最后一个 prototype 对象”便是 Object 函数内的 prototype 对象了。

  • Object 函数内的 prototype 对象的 __proto__ 指向 null。
    为什么 Object 函数不能像 Function 函数一样让 __proto__ 属性指向自己的 prototype?答案就是如果指向自己的 prototype,那当找不到某一属性时沿着原型链寻找的时候就会进入死循环,所以必须指向 null,这个 null 其实就是个跳出条件。


找出图中的原型链
在这里插入图片描述

  • 第一条: person.__proto__ -> Person.prototype -> Person.prototype.__proto__ -> Object.prototype -> Object.prototype.__proto__ -> null

  • 第二条: Person.__proto__ -> Function.prototype -> Functino.prototype.__proto__ -> Object.prototype -> Object.prototype.__proto__ -> null

  • 第三条: Function.__proto__ -> Function.prototype -> Function.prototype.__proto -> Object.prototype -> Object.prototype.__proto__ -> null

  • 第四条: Object.__proto__ -> Function.prototype -> Function.prototype.__proto__ -> Object.prototype -> Object.prototype.__proto__ -> null.


实例对象上都有什么
function P(){}
let p = new P();
// ↓↓↓
> P {}
  > __proto__: Object // 实例对象p上有 __proto__ 属性,指向构造函数的原型对象prototype
    > constructor: ƒ P() // prototype 上有公共方法 constructor
    > __proto__: // prototype 上还有 __proto__ 属性,指向内置Object的原型对象
      > constructor: ƒ Object() // 内置Object的原型对象上的constructor 以及hasOwnProperty等公共方法
      > hasOwnProperty: ƒ hasOwnProperty()
      ...

构造函数的属性和方法放在this上和放在prototype上有什么区别

Case1: 
function Person() {
  this.age = 0;
  this.list = [];
}
let p1 = new Person();
p1.list.push(1);
p1.list; // [1]

let p2 = new Person();
p2.list.push(2);
p2.list; // [2]

Case2:

function Person() {
  this.age = 0;
}
Person.prototype.list = [];

let p1 = new Person();
p1.list.push(1);
p1.list; // [1]

let p2 = new Person();
p2.list.push(2);
p2.list; // [1,2]

总结: this 定义的属性和方法是生成的实例自己独有的属性和方法;而定义在 prototype 上的属性和方法,是每个实例所共有的。那么定义在 prototype 上的属性和方法发生改变则每个实例对象都会拿到。


总结:函数/对象上都有什么?

  • 对象都有__proto__属性
  • 函数也有__proto__属性,还有原型对象prototype
  • 原型对象 prototype上也有 __proto__ 属性、还有 constructor 属性、以及一些共享属性和方法(挂在 prototype 上)
  • __proto__ 是浏览器实现的查看原型方案,也写成 [[prototype]]。__proto__属性的作用就是,当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__属性所指向的那个对象(父对象)里找,一直找,直到__proto__属性的终点 null,再往上找就相当于在 null 上取值,会报错。通过__proto__属性将对象连接起来的这条链路即我们所谓的原型链。
  • constructor 属性其实就是一个拿来保存自己构造函数引用的属性。实例对象的 constructor 属性指向构造函数本身。
  • 原型对象 prototype 的作用就是,让该函数所实例化的对象们都可以找到公用的属性和方法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值