原型和原型链相关知识点

174 阅读5分钟

参考链接:juejin.cn/post/687073…

参考链接:zhuanlan.zhihu.com/p/62903507

是什么

JavaScript是基于原型的,我们创建的每个函数都有一个 prototype(原型) 属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。

简单来说,就是当我们创建一个函数的时候,系统就会自动分配一个 prototype属性,可以用来存储可以让所有实例共享的属性和方法。 js 中每个对象都有一个“原型”,原型一般可以通过 __ proto__访问到: 可以这么理解:原型,也是一个对象。

image

  • 每一个构造函数都拥有一个 prototype 属性,这个属性指向一个对象,也就是原型对象
  • 原型对象默认拥有一个 constructor 属性,指向它的构造函数
  • 每个对象都拥有一个隐藏的属性 __ proto__,指向它的构造函数的原型对象

下面先介绍几个概念

1、对象

分为两种,一种叫普通对象,一种叫函数对象。

//下面三个都是普通对象
var o1 = {}; 
var o2 =new Object();
var o3 = new f1();

//下面三个都是函数对象
function f1(){}; 
var f2 = function(){};
var f3 = new Function('str','console.log(str)');

2、构造函数

通过new关键字调用的函数就是构造函数,构造函数首字母建议大写 常见的有:Object,Array,Date,Function等。

function Person(){
    
}
var person1 = new Person();
person1.name = 'Kevin';
console.log(person1.name) // Kevin

上面例子中,Person 就是一个构造函数,person1 就是 Person 的实例对象。 实例对象可以有一个 constructor(构造函数)属性,这个属性是一个指针,指向的是 Person。 image

下面正式进入介绍原型和原型链:

3、原型对象

要点:

  • 原型对象,当 Person 构造函数被定义出来的时候,它就存在了,是一个有特殊地位的普通对象。
  • 在JavaScript中,每一个已定义的对象都有一些预定义的属性。
  • 在这些预定义的属性中,所有的对象都含有一个__proto__属性,其中所有的函数对象都含有额外的一个prototype属性这个属性指向函数的原型对象。即:Person.prototype就是Person的原型对象。
  • 每一个原型对象都含有一个 constructor (构造函数)属性这个属性指向的又是它对应的那个构造函数本身。即:Person.prototype.constructor == Person
  • 原型对象是普通对象但是 Function.prototype 除外,这个是一个函数对象,并且没有 prototype 属性(之前提到所有的函数对象都有一个 prototype 属性,但是这个特殊的也得除外)。

4、原型链

含义: 当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾

function Person(name) {
    this.name = name;
    this.age = 18;
    this.sayName = function() {
        console.log(this.name);
    }
}
// 第二步 创建实例
var person = new Person('person')

根据代码,我们可以得到下图: (要记牢!) image

五个问题

  1. person.__ proto__ 是什么?
  2. Person.__ proto__ 是什么?
  3. Person.prototype.__ proto__ 是什么?
  4. Object.__ proto__ 是什么?
  5. Object.prototype.__ proto__ 是什么?

1、每个对象的__proto__都是指向它的构造函数的原型对象的。所以:

person.__proto__ === Person.prototype

2、Person这个构造函数是一个函数对象,是通过 Function 构造器产生的。所以:

Person.__proto__ === Function.prototype

3、Person 的原型对象本身是一个普通对象,而普通对象的构造函数都是Object。所以:

Person.prototype.__proto__ === Object.prototype

4、刚刚上面说了,所有的构造器都是函数对象,函数对象都是 Function 构造产生的。所以:

Object.__proto__ === Function.prototype

5、Object 的原型对象也有__proto__属性,但是比较特殊,是null。因为null是处于原型链的顶端。

Object.prototype.__proto__ === null

Function的原型链是:Function → Function.prototype → Object.prototype → null Object的原型链是:Object → Function.prototype → Object.prototype → null 和上面的结论其实不冲突。只是在 Function 这里绕了一圈。

下面来解释为什么实例对象没有 constructor 属性,但是 person.constructor == Person依然成立? 其实是因为 __ proto__ 属性,这个是关于原型链的东西,下面会讲,person 没有 constructor 属性,但是它可以顺着原型链往原型对象上寻找,所以找到了 Person.prototype 上面的 constructor 属性,又因为:

Person.prototype.constructor == Person

所以

person1.constructor == Person

再举个例子

function Person() {

}

Person.prototype.name = 'Kevin';

var person = new Person();
person.name = 'Daisy';
console.log(person.name) // Daisy

delete person.name;
console.log(person.name) // Kevin
  1. 我们先在构造函数Person的原型对象中添加了一个name属性为 Kevin,接着实例化了 Person 为 person。
  2. 在 person 中添加了name属性为 Daisy。
  3. 当我们输出 person 的name时,因为这时 person 中有这个属性,自然就返回了 Daisy。
  4. 后面我们删除了 person 中的这个属性,找不到了,于是js顺着 person 的原型链往上找,找到了 person 的构造函数 Person 的原型对象中有name属性,于是返回了Kevin。

假如原型对象上也没有这个属性,那么会顺着原型对象的__proto__继续往上找,最终会找到 Object.prototype. __ proto __ ,这个是原型链的起点,为 null。如果还是没有就会返回 undefined。

__ proto __

前面提到每个对象都会有一个 proto 的预定义属性,这个是用来指向创建它的构造函数的原型对象的image

作用

原型对象的作用是用来继承。

  var Person = function(name){
    this.name = name;
  };
  Person.prototype.getName = function(){
    return this.name;
  }
  var person1 = new person('Mick');
  person1.getName(); //Mick

通过给 Person 的原型对象设置了一个函数对象的属性 getName,使得 Person 的实例 person1 继承了这个属性。实现方法是通过原型链。

如何判断原型关系

1、instanceof instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。 而不能判断一个对象实例具体属于哪种类型

function Person(){}
var person = new Person();

person instanceof Person // true
person instanceof Object // true

2、hasOwnProperty 通过使用 hasOwnProperty 可以确定访问的属性是来自于实例还是原型对象

function Person() {}
Person.prototype = {
    name: 'tt'
}
var person = new Person();
person.age = 15;

person.hasOwnProperty('age') // true
person.hasOwnProperty('name') // false