MST

星途 面试题库

面试题:JavaScript 赋值表达式与原型链及作用域的关联陷阱

在 JavaScript 中,结合原型链和作用域的知识,阐述在不同作用域下对具有原型链关系的对象进行赋值操作时可能出现的陷阱。要求从原理层面深入分析,并给出能够全面避免这些陷阱的通用策略及示例代码。
21.9万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试

原理分析

  1. 原型链:JavaScript 中每个对象都有一个 [[Prototype]] 内部属性(可通过 __proto__ 访问,标准方式是 Object.getPrototypeOf),指向其原型对象。当访问对象的属性或方法时,如果对象本身没有该属性或方法,就会沿着原型链向上查找,直到找到或到达原型链顶端(null)。
  2. 作用域:JavaScript 有函数作用域(ES6 引入块级作用域)。作用域决定了变量的可访问范围。当在某个作用域内查找变量时,会先在当前作用域查找,若未找到则向上级作用域查找,直到全局作用域。
  3. 赋值操作陷阱
    • 意外覆盖原型属性:如果在对象实例上直接对某个属性赋值,而该属性恰好存在于原型链上,就会在实例上创建一个新的属性,从而遮蔽原型上的同名属性。例如:
function Animal() {}
Animal.prototype.sound = 'generic sound';
let dog = new Animal();
dog.sound = 'woof'; // 这里在 dog 实例上创建了 sound 属性,遮蔽了原型上的 sound 属性
  • 作用域链与原型链混淆:在函数内部,如果对对象属性赋值时,当前作用域存在同名变量,可能会导致误解。比如:
function test() {
    let obj = {name: 'initial'};
    function inner() {
        let name = 'local';
        obj.name = name; // 这里本意可能是修改 obj 的 name 属性,但容易因作用域问题导致错误
    }
    inner();
    console.log(obj.name); // 输出 'local',可能与预期不符
}

通用策略

  1. 明确赋值目标:在对对象属性赋值前,先确认是要操作实例属性还是原型属性。如果要操作原型属性,可直接在原型对象上进行修改,避免在实例上意外创建同名属性。例如:
function Shape() {}
Shape.prototype.color = 'white';
// 修改原型属性
Shape.prototype.color = 'black';
let square = new Shape();
console.log(square.color); // 输出 'black'
  1. 使用 hasOwnProperty 检查:在对属性赋值前,使用 hasOwnProperty 方法检查对象本身是否已经有该属性,避免意外遮蔽原型属性。例如:
function Person() {}
Person.prototype.age = 0;
let john = new Person();
if (!john.hasOwnProperty('age')) {
    john.age = 30;
}
console.log(john.age); // 输出 30
  1. 注意作用域:在函数内部对对象属性赋值时,确保变量名不会与作用域内其他变量冲突。尽量使用更具描述性的变量名,并且在必要时使用 this 来明确对象。例如:
function Car() {
    this.model = 'default';
    function setModel(newModel) {
        this.model = newModel; // 使用 this 明确是 Car 实例的 model 属性
    }
    setModel.bind(this)('sports car');
    console.log(this.model); // 输出'sports car'
}
let myCar = new Car();