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

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. 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
    
  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. 使用 obj.__proto__ 属性:__proto__ 是一个非标准的属性,但在大多数现代浏览器中支持。它返回对象 obj 的原型。例如:
var person = {
  name: "John",
  age: 30,
};

var student = Object.create(person);

console.log(student.__proto__); // 输出 person 对象

需要注意的是,虽然 __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 的实例)

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
    });
    
  3. 检查方法是否已存在:在添加新的方法或属性前,检查它是否已存在,以避免意外的覆盖。

    if (!Array.prototype.myCustomFunction) {
        Array.prototype.myCustomFunction = function() {
            // ...
        };
    }
    

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. 使用严格的输入验证和过滤,确保接受的用户输入符合预期的格式和范围。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值