• 与原型、原型链、构造函数相关的面试题


    1.什么是构造函数?如何定义它?

    构造函数是一个用于创建和初始化一个对象的特殊函数。在 JavaScript 中,几乎每个对象都是由某个构造函数创建的。构造函数的约定是,其名称首字母大写。

    如何定义它?

    你可以像定义常规函数一样定义构造函数,但是为了创建新的实例,你需要使用 new 关键字。

    以下是一个简单的例子:

    // 定义一个构造函数
    function Person(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    
        this.getFullName = function() {
            return this.firstName + " " + this.lastName;
        };
    }
    
    // 使用构造函数创建新的对象
    var john = new Person("John", "Doe");
    var jane = new Person("Jane", "Smith");
    
    console.log(john.getFullName()); // 输出:John Doe
    console.log(jane.getFullName()); // 输出:Jane Smith
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这个例子中:

    1. Person 是一个构造函数,它接受两个参数,firstNamelastName
    2. 使用 this 关键字,我们可以给创建的对象分配属性和方法。
    3. 通过使用 new 关键字,我们创建了 Person 的新实例。

    注意:更好的做法是使用原型链来给所有对象共享方法,如:Person.prototype.getFullName = function() {...}。只在原型链上有一个方法,但是所有对象可用。在构造函数里面创建getFullName会导致所有的对象都有,占用大量内存

    2.什么是原型 (prototype)? 每个 JavaScript 对象都有一个原型吗?

    在 JavaScript 中,原型 (prototype) 是一个对象的内部链接,通过这个链接,对象可以继承其他对象的属性和方法。原型是 JavaScript 的核心特性,支撑了它的原型继承机制。

    每个 JavaScript 对象都有一个原型吗?

    是的,几乎每个 JavaScript 对象都有一个原型。唯一没有原型的对象是通过 Object.create(null) 创建的对象。

    当你试图访问一个对象的属性或方法,而该对象本身没有这个属性或方法,JavaScript 会查找该对象的原型(以及原型的原型,以此类推)直到找到该属性或方法或达到原型链的尽头(即 null)。

    3.原型和原型链有什么区别?

    原型(prototype)是一个对象,它包含共享属性和方法的对象。

    原型链(prototype chain)是由一系列原型对象组成的链式结构。

    通过原型链,JavaScript可以实现对象之间的继承和共享属性和方法

    4.解释 proto 和 prototype 的区别。

    __proto__prototype 是 JavaScript 中经常被提及,但同时也是经常被混淆的两个属性。它们之间的关系和用途非常核心,对于理解 JavaScript 的原型继承机制至关重要。

    1. prototype属性:
      • prototype 是 JavaScript 中的函数对象特有的属性
    2. __proto__属性:
    • __proto__ 是**每个 JavaScript 对象(**除了 null)都具有的一个属性,它是一个访问器属性(一个 getter 函数和一个 setter 函数)。

    关系与区别:

    1. 对于自定义构造函数,其创建的实例的 __proto__ 属性指向构造函数的 prototype 属性。例如:

      function MyConstructor() {}
      let obj = new MyConstructor();
      console.log(obj.__proto__ === MyConstructor.prototype); // true
      
      • 1
      • 2
      • 3
    2. 只有函数对象才有 prototype 属性,而所有的对象都有 __proto__ 属性。

    5.如何检查一个对象的原型?给定两个对象,如何检查它们是否具有相同的原型?

    1. 使用 Object.getPrototypeOf(obj) 方法:这个方法返回对象 obj 的原型。例如:
    var person = {
      name: "John",
      age: 30,
    };
    
    var student = Object.create(person);
    
    console.log(Object.getPrototypeOf(student)); // 输出 person 对象
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. 使用 obj.__proto__ 属性:__proto__ 是一个非标准的属性,但在大多数现代浏览器中支持。它返回对象 obj 的原型。例如:
    var person = {
      name: "John",
      age: 30,
    };
    
    var student = Object.create(person);
    
    console.log(student.__proto__); // 输出 person 对象
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    需要注意的是,虽然 __proto__ 属性在大多数情况下是有效的,但它不是 JavaScript 标准的一部分,因此不建议在生产环境中广泛使用。

    1. 使用 instanceof 操作符:instanceof 操作符可用于检查一个对象是否是某个构造函数的实例,其实现原理是通过原型链进行判断。例如:
    function Person(name) {
      this.name = name;
    }
    
    var john = new Person("John");
    
    console.log(john instanceof Person); // 输出 true
    console.log(john instanceof Object); // 输出 true(所有对象都是 Object 的实例)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    instanceof 操作符可以判断对象是否是某个构造函数的实例,间接地可以判断对象的原型是否是某个对象。

    这些方法都可以用来检查一个对象的原型,具体选择哪种方法取决于个人偏好和项目需求。通常情况下,推荐使用 Object.getPrototypeOf(obj) 方法来获取对象的原型,因为它是标准的方法且具有广泛的兼容性。

    6.为什么最好不要修改对象的原型?与解决方法

    潜在的覆盖问题:如果原型上已经存在了一个同名的方法或属性,你的修改可能会意外地覆盖它,或者在未来的版本中被新的原型方法覆盖。

    解决方法:

    如果你确实需要修改或扩展原型(虽然这通常不推荐),以下是一些建议:

    1. 不修改全局对象的原型:特别是不要修改原生对象,如ArrayObjectString等的原型。如果你想为原生对象添加功能,可以考虑使用工具函数或类/对象扩展。

    2. 使用Object.defineProperty:使用Object.defineProperty可以让你更加细致地定义原型上的属性或方法,例如设置为不可枚举,这样它就不会在for...in循环中出现。

      Object.defineProperty(Array.prototype, 'myCustomFunction', {
          value: function() {
              // ...
          },
          writable: true,
          configurable: true,
          enumerable: false
      });
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    3. 检查方法是否已存在:在添加新的方法或属性前,检查它是否已存在,以避免意外的覆盖。

      if (!Array.prototype.myCustomFunction) {
          Array.prototype.myCustomFunction = function() {
              // ...
          };
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5

    7.解释原型继承是如何工作的。

    回答属性查找:当试图访问一个对象的属性时,JavaScript 首先在该对象本身上查找该属性。如果没有找到,它会在对象的原型上查找,然后是原型的原型,依此类推,直到找到属性或达到原型链的末尾。

    原型链继承:4种方式,class的extend继承,在原型上new一个父类,function + call,原型链+call

    8.hasOwnProperty 方法是从哪里来的?为什么所有的普通对象都可以访问它?

    hasOwnProperty 方法用于检查一个对象是否具有指定名称的自身属性(即非继承而来的属性)。它接受一个参数,即属性的名称,如果对象拥有该属性,则返回 true,否则返回 false。这个方法对于遍历对象自身的属性非常有用,可以用来确定属性的来源是否是对象本身而不是继承的。

    总结来说,hasOwnProperty 方法是所有普通对象通过原型链继承自 Object.prototype 的方法,用于判断一个对象是否具有指定名称的自身属性。

    9.什么是原型污染?为什么它可能是危险的?

    原型污染(Prototype Pollution)是指通过修改对象的原型链,对对象的原型进行恶意或意外的更改,从而影响到对象及其相关属性和方法的行为。这种污染可以导致安全漏洞和意外的行为。

    原型污染之所以可能是危险的,有以下几个原因:

    1. 污染全局对象:通过污染全局对象的原型,可以影响到所有基于该原型创建的对象。这可能导致全局范围内的意外行为和冲突,影响到整个应用程序。

    2. 覆盖或篡改原始对象的属性和方法:通过修改对象原型链上的方法或属性,可以改变对象的行为。这可能导致不可预料的结果和潜在的安全风险。例如,攻击者可以修改内置对象的原型,改变其方法的行为,从而实现代码执行、信息泄露或其他攻击。

    3. 绕过安全检查和访问控制:某些安全机制和访问控制是基于对象的原型链的。通过修改原型链,攻击者可以绕过这些机制,获取未授权的访问权限,从而导致数据泄露、越权访问和其他安全问题。

    原型污染可以通过多种方式发生,其中常见的一种是通过用户输入直接或间接地修改对象的原型链。例如,当应用程序使用不可信的用户输入直接作为属性名或方法名时,攻击者可以通过构造特定的输入来修改原型链。

    为了防止原型污染,可以采取以下措施:

    1. 避免使用不可信的用户输入作为属性名或方法名。
    2. 在使用第三方库时,仔细审查该库是否存在原型污染的安全问题,并及时更新到最新版本。
    3. 限制或禁用对全局对象的修改,避免对全局对象的原型进行篡改。
    4. 使用严格的输入验证和过滤,确保接受的用户输入符合预期的格式和范围。

    总之,原型污染是一种潜在的安全问题,攻击者可以通过修改对象的原型链来实现不当行为和攻击。为了保护应用程序的安全性,开发人员应了解原型污染的概念,并采取适当的措施来防止和缓解该问题。

  • 相关阅读:
    从零开始:使用Python创建GUI驱动的简易国际象棋游戏
    SSM+教学网站 毕业设计-附源码211611
    蚂蚁三面滑铁卢!遭分布式截胡,靠这些笔记潜修 30 天,挺进京东
    从物理转AI、战数据库,95后程序员的职业选择
    Oracle EBS Interface/API(50)-创建员工API
    zadig libusb-win32 驱动回退至 FTDIBUS
    【三维目标检测】VoteNet(一)
    Libuv的安装及运行使用
    kubernetes学习笔记-集群搭建篇
    扫码登录认证技术原理介绍及实践
  • 原文地址:https://blog.csdn.net/weixin_43850639/article/details/132872258