JavaScript 原型链和继承面试题

JavaScript 原型链和继承问题JavaScript 中没有类的概念的,主要通过原型链来实现继承。通常情况下,继承意味着复制操作,然而 JavaScript默认并不会复制对象的属性,相反,JavaScript只是在两个对象之间创建一个关联(原型对象指针),这样,一个对象就可以通过委托访问另一个对象的属性和函数,所以与其叫继承,委托的说法反而更准确些。原型当我们 new 了一个新的对象实例,明明什么都没有做,就直接可以访问 toString 、valueOf 等原生方法。那么这些方法是从哪里来的
摘要由CSDN通过智能技术生成

JavaScript 原型链和继承问题

JavaScript 中没有类的概念的,主要通过原型链来实现继承。通常情况下,继承意味着复制操作,然而 JavaScript默认并不会复制对象的属性,相反,JavaScript只是在两个对象之间创建一个关联(原型对象指针),这样,一个对象就可以通过委托访问另一个对象的属性和函数,所以与其叫继承,委托的说法反而更准确些。

原型

  • 当我们 new 了一个新的对象实例,明明什么都没有做,就直接可以访问 toString 、valueOf 等原生方法。那么这些方法是从哪里来的呢?答案就是原型。
    在这里插入图片描述
  • 在控制台打印一个空对象时,我们可以看到,有很多方法,已经“初始化”挂载在内置的 proto 对象上了。这个内置的 proto 是一个指向原型对象的指针,它会在创建一个新的引用类型对象时(显示或者隐式)自动创建,并挂载到新实例上。当我们尝试访问实例对象上的某一属性 / 方法时,如果实例对象上有该属性 / 方法时,就返回实例属性 / 方法,如果没有,就去 proto 指向的原型对象上查找对应的属性 / 方法。这就是为什么我们尝试访问空对象的 toString 和 valueOf 等方法依旧能访问到的原因,JavaScript 正式以这种方式为基础来实现继承的。

构造函数

如果说实例的 proto 只是一个指向原型对象的指针,那就说明在此之前原型对象就已经创建了,那么原型对象是什么时候被创建的呢?这就要引入构造函数的概念。
其实构造函数也就只是一个普通的函数而已,如果这个函数可以使用 new 关键字来创建它的实例对象,那么我们就把这种函数称为 构造函数

// 普通函数
function person () {
   }

// 构造函数,函数首字母通常大写
function Person () {
   }
const person = new Person();
  • 原型对象正是在构造函数被声明时一同创建的。构造函数被申明时,原型对象也一同完成创建,然后挂载到构造函数的 prototype 属性上:

在这里插入图片描述
原型对象被创建时,会自动生成一个 constructor 属性,指向创建它的构造函数。这样它俩的关系就被紧密地关联起来了。

细心的话,你可能会发现,原型对象也有自己的 proto ,这也不奇怪,毕竟万物皆对象嘛。原型对象的 proto 指向的是 Object.prototype。那么 Object.prototype.proto 存不存在呢?其实是不存在的,打印的话会发现是 null 。这也证明了 Object 是 JavaScript 中数据类型的起源。


分析到这里,我们大概了解原型及构造函数的大概关系了,我们可以用一张图来表示这个关系:
在这里插入图片描述

原型链

说完了原型,就可以来说说原型链了,如果理解了原型机制,原型链就很好解释了。其实上面一张图上,那条被 proto 链接起来的链式关系,就称为原型链

原型链的作用:原型链如此的重要的原因就在于它决定了 JavaScript 中继承的实现方式。当我们访问一个属性时,查找机制如下:

  • 访问对象实例属性,有则返回,没有就通过 proto 去它的原型对象查找。
  • 原型对象找到即返回,找不到,继续通过原型对象的 proto 查找。
  • 一层一层一直找到 Object.prototype ,如果找到目标属性即返回,找不到就返回 undefined,不会再往下找,因为在往下找 proto 就是 null 了。

通过上面的解释,对于构造函数生成的实例,我们应该能了解它的原型对象了。JavaScript 中万物皆对象,那么构造函数肯定也是个对象,是对象就有 proto ,那么构造函数的 proto 是什么?
在这里插入图片描述
现在才想起来所有的函数可以使用 new Function() 的方式创建,那么这个答案也就很自然了,有点意思,再来试试别的构造函数。
在这里插入图片描述
这也证明了,所有函数都是 Function 的实例。等一下,好像有哪里不对,那么 Function.proto 岂不是。。。
在这里插入图片描述
按照上面的逻辑,这样说的话,Function 岂不是自己生成了自己?其实,我们大可不必这样理解,因为作为一个 JS 内置对象,Function 对象在你脚本文件都还没生成的时候就已经存在了,哪里能自己调用自己,这个东西就类似于玄学中的“道”和“乾坤”,你能说明它们是谁生成的吗,天地同寿日月同庚不生不灭。。。算了,在往下扯就要写成修仙了=。=
至于为什么 Function.proto 等于 Function.prototype 有这么几种说法:

  • 为了保持与其他函数保持一致
  • 为了说明一种关系,比如证明所有的函数都是 Function 的实例。
  • 函数都是可以调用 call bind 这些内置 API 的,这么写可以很好的保证函数实例能够使用这些 API。

注意点:

关于原型、原型链和构造函数有几点需要注意:

  • proto 是非标准属性,如果要访问一个对象的原型,建议使用 ES6 新增的 Reflect.getPrototypeOf 或者 Object.getPrototypeOf() 方法,而不是直接 obj.proto,因为非标准属性意味着未来可能直接会修改或者移除该属性。同理,当改变一个对象的原型时,最好也使用 ES6 提供的 Reflect.setPrototypeOf 或 Object.setPrototypeOf。
let target = {
   };
let newProto = {
   };
Reflect.getPrototypeOf(target) === newProto; // false
Reflect.setPrototypeOf(target, newProto);
Reflect.getPrototypeOf(target) === newProto; // true
  • 函数都会有 prototype ,除了 - Function.prototype.bind() 之外。
    对象都会有 proto ,除了 Object.prototype 之外(其实它也是有的,之不过是 null)。
  • 所有函数都由 Function 创建而来,也就是说他们的 proto 都等于 Function.prototype。
  • Function.prototype 等于 Function.proto

原型污染

  • 原型污染是指:攻击者通过某种手段修改 JavaScript 对象的原型。
  • 什么意思呢,原理其实很简单。如果我们把 Object.prototype.toString 改成这样:
Object.prototype.toString = function () {
   alert('原型污染')};
let obj = {
   };
obj.toString();

那么当我们运行这段代码的时候浏览器就会弹出一个 alert,对象原生的 toString 方法被改写了,所有对象当调用 toString 时都会受到影响。
你可能会说,怎么可能有人傻到在源码里写这种代码,这不是搬起石头砸自己的脚么?没错,没人会在源码里这么写,但是攻击者可能会通过表单或者修改请求内容等方式使用原型污染发起攻击,来看下面一种情况:

'use strict';

const express = require('express');
const bodyParser = require('body-parser')
const cookieParser = require('cookie-parser');
const path = require('path');

const isObject =
  • 6
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,我来回答你的问题。以下是一些JS原型继承原型链面试题及其答案: 1. 什么是原型继承? 答:原型继承是通过将一个对象的原型作为另一个对象的原型来实现继承。在JS中,每个对象都有一个原型,它可以通过__proto__属性来访问。 2. 什么是原型链? 答:原型链是一种通过原型继承来实现对象间继承关系的机制。当我们访问一个对象的属性时,JS会首先在该对象自身查找,如果没有找到,就会在它的原型对象上查找,如果还没有找到,就会在原型对象的原型对象上查找,一直到Object.prototype对象为止,这个查找的过程就是原型链。 3. 如何实现一个简单的原型继承? 答: ```javascript function Person(name) { this.name = name; } Person.prototype.sayHi = function() { console.log('Hi, my name is ' + this.name); } function Student(name, grade) { Person.call(this, name); this.grade = grade; } Student.prototype = Object.create(Person.prototype); Student.prototype.constructor = Student; Student.prototype.sayGrade = function() { console.log('My grade is ' + this.grade); } var tom = new Student('Tom', 3); tom.sayHi(); tom.sayGrade(); ``` 4. 如何判断一个对象是否是另一个对象的实例? 答:可以使用instanceof运算符来判断,例如: ```javascript var obj = {}; console.log(obj instanceof Object); // true ``` 5. 如何判断一个属性是在对象自身定义的还是继承原型? 答:可以使用JS提供的hasOwnProperty方法来判断,例如: ```javascript var obj = {}; console.log(obj.hasOwnProperty('toString')); // false console.log(Object.prototype.hasOwnProperty('toString')); // true ``` 希望这些问题和答案能够帮助你更好地理解JS原型继承原型链

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值