通俗易懂的原型和原型链

前言

不要为了面试而去背题,匆匆忙忙的,不仅学不进去,背完了几天后马上会忘记。

图片

你可能会说,“没办法,这不是为了能找份工作嘛!”。我想说的是,“那你没开始找工作的时候,咋不好好学习呢。”

好了,上述扯的这些,意思就是让大家不要做收藏家,不要把好文收藏了,就放在收藏夹里吃灰!

下面为大家简单阐述我对原型和原型链的理解,若是觉得有说的不对的地方,还望直接把页面关闭了,别在我这篇文章上继续浪费时间。(逃)

图片

四个规则

我们先来了解下面引用类型的四个规则:

1、引用类型,都具有对象特性,即可自由扩展属性。

2、引用类型,都有一个隐式原型 __proto__ 属性,属性值是一个普通的对象。

3、引用类型,隐式原型 __proto__ 的属性值指向它的构造函数的显式原型 prototype 属性值。

4、当你试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么它会去它的隐式原型 __proto__(也就是它的构造函数的显式原型 prototype)中寻找。

引用类型:Object、Array、Function、Date、RegExp。这里我姑且称 proto 为隐式原型,没有官方中文叫法,大家都瞎叫居多。

下面我们逐一验证上面几个规则,就会慢慢地理解原型和原型链。

规则一

引用类型,都具有对象特性,即可自由扩展属性:

const obj = {}
const arr = []
const fn = function () {}

obj.a = 1
arr.a = 1
fn.a = 1

console.log(obj.a) // 1
console.log(arr.a) // 1
console.log(fn.a) // 1

这个规则应该比较好理解,Date 和 RegExp 也一样,就不赘述了。

规则二

引用类型,都有一个隐式原型 __proto__ 属性,属性值是一个普通的对象:

const obj = {};
const arr = [];
const fn = function() {}

console.log('obj.__proto__', obj.__proto__);
console.log('arr.__proto__', arr.__proto__);
console.log('fn.__proto__', fn.__proto__);

图片

规则三

引用类型,隐式原型 __proto__ 的属性值指向它的构造函数的显式原型 prototype 属性值:

const obj = {};
const arr = [];
const fn = function() {}

obj.__proto__ == Object.prototype // true
arr.__proto__ === Array.prototype // true
fn.__proto__ == Function.prototype // true

规则四

当你试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么它会去它的隐式原型 __proto__(也就是它的构造函数的显式原型 prototype)中寻找:

const obj = { a:1 }
obj.toString
// ƒ toString() { [native code] }

首先, obj 对象并没有 toString 属性,之所以能获取到 toString 属性,是遵循了第四条规则,从它的构造函数 Object 的 prototype 里去获取。

一个特例

我试图想推翻上面的规则,看下面这段代码:

function Person(name) {
  this.name = name
  return this // 其实这行可以不写,默认返回 this 对象
}

var nick = new Person("nick")
nick.toString
// ƒ toString() { [native code] }

按理说, nick 是 Person 构造函数生成的实例,而 Person 的 prototype 并没有 toString 方法,那么为什么, nick 能获取到 toString 方法?

这里就引出 原型链 的概念了, nick 实例先从自身出发检讨自己,发现并没有 toString 方法。找不到,就往上走,找 Person 构造函数的 prototype 属性,还是没找到。构造函数的 prototype 也是一个对象嘛,那对象的构造函数是 Object ,所以就找到了 Object.prototype 下的 toString 方法。

图片

上述寻找的过程就形成了原型链的概念,我理解的原型链就是这样一个过程。也不知道哪个人说过一句,JavaScript 里万物皆对象。从上述情况看来,好像是这么个理。🤔

一张图片

用图片描述原型链:

图片

最后一个 null,设计上是为了避免死循环而设置的, Object.prototype 的隐式原型指向 null

一个方法

instanceof 运算符用于测试构造函数的 prototype 属性是否出现在对象原型链中的任何位置。 instanceof 的简易手写版,如下所示:

// 变量R的原型 存在于 变量L的原型链上
function instance_of (L, R) {    
  // 验证如果为基本数据类型,就直接返回 false
  const baseType = ['string', 'number', 'boolean', 'undefined', 'symbol']
  if(baseType.includes(typeof(L))) { return false }

  let RP = R.prototype;  // 取 R 的显示原型
  L = L.__proto__; // 取 L 的隐式原型
  while (true) {
    if (L === null) { // 找到最顶层
      return false;
    }
    if (L === RP) { // 严格相等
      return true;
    }
    L = L.__proto__;  // 没找到继续向上一层原型链查找
  }
}

我们再来看下面这段代码:

function Foo(name) {
  this.name = name;
}
var f = new Foo('nick')

f instanceof Foo // true
f instanceof Object // true

上述代码判断流程大致如下:

1、 f instanceof Foo: f 的隐式原型 __proto__ 和 Foo.prototype ,是相等的,所以返回 true 。

2、 f instanceof Object: f 的隐式原型 __proto__ ,和 Object.prototype 不等,所以继续往上走。 f 的隐式原型 __proto__ 指向 Foo.prototype ,所以继续用 Foo.prototype.__proto__ 去对比 Object.prototype ,这会儿就相等了,因为 Foo.prototype 就是一个普通的对象。

<think>嗯,用户问的是关于JavaScript原型原型链的面试题,我需要详细介绍下这个知识点。首先,我应该从基本概念开始,解释什么是原型原型链,然后逐步深入,可能还要举些例子来帮助理解。 首先,原型是什么?每个JavaScript对象都有原型对象,对象会继承原型的属性方法。构造函数通过prototype属性指向原型,而实例对象通过__proto__属性访问原型。这点可能需要详细说明,比如构造函数、实例、原型之间的关系。 然后原型链是什么?当访问个对象的属性时,如果对象自身没有,就会去原型上找,原型又有自己的原型,这样层层往上,直到找到或者到达null,形成链条。这个过程就是原型链。这里可能需要画个示意图或者用代码示例来说明。 接下来,常见的面试题可能涉及如何实现继承,instanceof的原理,或者如何判断属性是对象自身还是继承来的。比如,hasOwnProperty方法的作用,或者手写个instanceof函数。这些都需要准备例子解释。 还有可能问到ES6的class语法原型继承的关系,毕竟class只是语法糖,底层还是基于原型链的。这部分需要对比传统原型继承class的区别,说明其原理。 另外,可能会涉及些陷阱题,比如修改原型的影响,或者原型链污染的问题。例如,给Array原型添加方法会影响所有数组实例,这可能引发的问题需要注意。 在回答结构上,应该分点清晰,先讲原型,再讲原型链,然后举例说明,接着讨论相关方法应用,最后总结常见考点。同时,要确保术语准确,比如__proto__prototype的区别,constructor属性的指向等。 需要检查自己是否遗漏了重要概念,比如Object.create的作用,或者new操作符的内部步骤。这些都可能出现在面试题中。比如,new个构造函数时,发生了什么,如创建空对象、绑定原型、执行构造函数、返回对象等步骤。 可能还需要解释为什么函数也是对象,因为函数有prototype属性,而函数本身又是Function的实例,这涉及到FunctionObject的关系,这部分比较绕,需要用简单明了的例子说明。 最后,确保语言通俗易懂,避免过于晦涩,同时保持专业性。可能需要用代码片段来辅助解释,比如构造函数、实例、原型之间的关系,以及原型链的查找过程。</think>### JavaScript 原型原型链核心知识点(面试题解析) --- #### 、基础概念 1. **原型(Prototype)** - 每个 JavaScript **对象**都有个隐藏属性 `[[Prototype]]`(通过 `__proto__` 访问),指向它的原型对象。 - **构造函数**通过 `prototype` 属性指向其原型对象,例如: ```javascript function Person() {} console.log(Person.prototype); // 原型对象 ``` $$ \text{构造函数} \xrightarrow{\text{prototype}} \text{原型对象} $$ 2. **原型链(Prototype Chain)** - 对象查找属性时,若自身不存在,会通过 `__proto__` 逐级向上查找原型链,直到 `Object.prototype.__proto__`(即 `null`): $$ \text{实例} \xrightarrow{\text{__proto__}} \text{构造函数原型} \xrightarrow{\text{__proto__}} \text{Object.prototype} \xrightarrow{\text{__proto__}} \text{null} $$ --- #### 二、高频面试题解析 **1. 如何实现继承?** ```javascript // 父类 function Animal(name) { this.name = name; } Animal.prototype.say = function() { console.log(this.name); }; // 子类继承 function Dog(name) { Animal.call(this, name); // 继承属性 } Dog.prototype = Object.create(Animal.prototype); // 继承方法 Dog.prototype.constructor = Dog; // 修复构造函数指向 ``` **2. `instanceof` 的原理是什么?** - 检查右侧构造函数的 `prototype` 是否在左侧对象的原型链上: ```javascript function myInstanceof(obj, Constructor) { let proto = obj.__proto__; while (proto) { if (proto === Constructor.prototype) return true; proto = proto.__proto__; } return false; } ``` **3. `Object.create(null)` `{}` 的区别?** - `Object.create(null)` 创建的对象无原型链(`__proto__` 为 `undefined`),适合纯粹的数据存储。 --- #### 三、关键方法 1. **`hasOwnProperty`** 判断属性是否为对象自身所有(非继承): ```javascript const obj = { a: 1 }; obj.hasOwnProperty('a'); // true ``` 2. **`Object.getPrototypeOf(obj)`** 标准方法获取对象的原型(替代 `obj.__proto__`)。 --- #### 四、常见陷阱题 **题目:以下代码输出什么?** ```javascript function Foo() {} const f1 = new Foo(); console.log(f1.constructor === Foo); // true console.log(f1.constructor === Object); // false ``` **解析**: - `f1` 自身无 `constructor`,通过原型链找到 `Foo.prototype.constructor`(指向 `Foo`)。 - 若修改 `Foo.prototype = {}`,则 `f1.constructor` 会指向 `Object`。 --- #### 五、总结高频考点 1. 原型链的查找机制 2. 继承的多种实现方式(组合继承、寄生组合继承) 3. `new` 操作符的底层步骤(创建对象、绑定原型、初始化属性) 4. ES6 `class` 与原型的关系(语法糖本质) 理解这些核心概念后,可结合手写代码(如实现 `new`、继承)巩固知识。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值