施主!请留步,我给您讲讲原型链,您看如何? —— 一个例子搞懂原型链

对于前端初学者来说,JS 原型链一直是个比较难理解的点。前段时间,在学习原型链时,便写了例子。经过加工就有了这篇文章。为了让大家看明白,我把例子拆开一步步来看,相信多看几遍就懂了。

注意:一定要看注释!看注释!看注释!, 文章末尾有完整的原型链关系图和完整的demo

文章是前段时间写的,有些不足和错误的地方,正在修改中..

几个重要的概念

new 一个实例对象 (单身狗表示很好使)

为了弄清楚下面几个概念,我们先 new 个对象来说明一下

// person() 构造函数
function person(name, age) {
  this.name = name;
  this.age = age;
  this.country = "China";
}


// es6 写法
// class person {
//   constructor(name, age) {
//     this.name = name;
//     this.age = age;
//     this.country = "China";
//   }
// }


// kite 实例对象
const kite = new person("Kite", 23);

这个几个概念,不理解也没有关系,脑子里有这个概念,后面看了自然就懂了。

  • 实例对象: 首先你要有一个函数(方法), 用 new 关键字来创建一个对象, 而这个对象,就叫实例对象。例:const kite = new person("Kite", 23)。这里的 kite 就是实例对象
  • 构造函数: 其实就是 js 普通的函数,之所以叫构造函数,是相对实例对象来说的。主要用来初始化对象,即为对象成员变量赋初始值,它总与 new 一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面,例:person()
  • prototype 属性: 只有函数才有 prototype 属性,指向函数的原型对象,例:person.prototype
  • __proto__ 属性:函数和对象都有__proto__属性,指向这个对象的构造函数的prototype属性,也就是构造函数的原型。例:kite.__proto__

对象属性访问(调用)原理

默认情况下,对象的调用属性时,会先检查自己有没有这个属性,如果有则用自己的,如果没有,就会去构造函数中找,一层一层得往上找(本质就是沿着__proto__这对链找),直到找到为止,如果都没有,则返回undefind

例如:

// kite 对象本身的属性
console.log(kite.name); //Kite
// kite 对象本身的属性
console.log(kite.age); // 23
// kite 对象本身的属性
console.log(kite.country); //China

// 在原型对象中新增 color 属性
person.prototype.color = "yellow";  

// person 原型对象上的属性,在 Chrome 控制台中 color属性是放在 [[Prototype]]中的,用来区分是不是原型对象上的属性
console.log(kite.color);  // yellow

//既没在实例对象 kite 中找到,也没有在 kite 的原型对象上找到,所以返回 undefined
console.log(kite.a); // undefinde 

constructor 属性

// constructor属性指向该对象的构造函数(person)
console.log(kite.constructor === person); 

微信图片_202110141017541.png

proto 、 prototype 、 constructor 属性的关系

// 注意:对象只有 __proto__ 属性,方法(js中方法其实也是对象)既有 __proto__ 属性,也有 prototype 属性

console.log(kite.__proto__ === person.prototype); //true, 说明对象的`__proto__`属性,指向这个对象的构造函数的`prototype`属性,也就是原型对象

// 说明 name、age、country 是在实例对象上的,而不是在原型对象上的
console.log(kite.name === kite.__proto__.name); //false
console.log(kite.age === kite.__proto__.age); //false
console.log(kite.country === kite.__proto__.country); //false

kite.hasOwnProperty("color") //false,kite 上没有 color属性
// 说明 kite.color 访问的是原型链上的属性
console.log(kite.color === kite.__proto__.color); //true

// 构造函数的原型对象 contructor 属性,指向构造函数本身
console.log(person.prototype.constructor === person); //true

微信图片_20211014101754.png

Function

JS 新建一个函数一般有两种方式,funtion person(){}const person = new funtion()他们是一样的。从后者我们可以看出,创建一个函数也是 new 出来,它的构造函数其实就是 Function

//我们知道 javascript 中万物皆为对象,person 也是一个对象, person 可以理解成是 Function 的实例对象
//person 的 __proto__ 指向 Function 的原型也就是 Function.prototype

console.log(person.constructor === Function); // true,可以理解为 person 是从 Function 中 new 出来的(var person = new Function())
console.log(person.__proto__ === Function.prototype); //true

和普通的函数不同,Function__proto__属性指向它的原型本身

// Function比较特殊, Function.__proto__ 和 Function.prototype 都指向 Function 的原型对象
console.log(Function.__proto__ === Function.prototype); //true

微信图片_202110141017542.png

Object.prototype ———— 万物皆为对象

// kite.__proto__.__proto__  和 person.__proto__.__proto__ 都指向 Object.prototype 也就是 Object 的原型对象
// 这也印证了 javascript “万物皆为对象” 的说法
console.log(kite.__proto__.__proto__ === person.__proto__.__proto__); //true
console.log(kite.__proto__.__proto__ === Object.prototype); //true
console.log(person.__proto__.__proto__ === Object.prototype); //true

值得注意的是,原型链的尽头是 null

//Object.prototype __proto__ 是 null
console.log(kite.__proto__.__proto__.__proto__); //原型链的尽头就是null

//正常我们访问一个对象的属性时,就是沿着 __proto__一步步往上找的,直到找到为止,如果一直找不到这个属性那就返回 undefined  

微信图片_202110141017543.png

完整原型链关系图

完整代码demo :codesandbox.io/s/wonderful…

无标题-2021-08-16-0914.png

结语

文章如果有帮到你,也请施主多多点赞,鼓励一下小僧