阿里云主机折上折
  • 微信号
Current Site:Index > Overriding object methods

Overriding object methods

Author:Chuan Chen 阅读数:3933人阅读 分类: JavaScript

Basic Concepts of Method Overriding

In JavaScript, method overriding refers to a subclass replacing a method already defined in the parent class to implement behavior specific to the subclass. This mechanism is an important manifestation of polymorphism in object-oriented programming, allowing different objects to respond differently to the same message.

class Animal {
  makeSound() {
    console.log('Some generic animal sound');
  }
}

class Dog extends Animal {
  makeSound() {
    console.log('Bark! Bark!');
  }
}

const myDog = new Dog();
myDog.makeSound(); // Output: Bark! Bark!

Prototype Chain and Method Overriding

JavaScript implements inheritance through the prototype chain. Method overriding essentially creates a method with the same name on the subclass's prototype, thereby shadowing the method on the parent class's prototype:

function Animal() {}
Animal.prototype.makeSound = function() {
  console.log('Generic sound');
};

function Dog() {}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

// Method overriding
Dog.prototype.makeSound = function() {
  console.log('Woof!');
};

const dog = new Dog();
dog.makeSound(); // Output: Woof!

Using the super Keyword

In ES6 class syntax, the super keyword can be used to call the overridden method of the parent class:

class Bird {
  fly() {
    console.log('Flying high');
  }
}

class Penguin extends Bird {
  fly() {
    if (this.canFly) {
      super.fly(); // Call the parent class's fly method
    } else {
      console.log('Sorry, I can\'t fly');
    }
  }
  
  constructor() {
    super();
    this.canFly = false;
  }
}

const penguin = new Penguin();
penguin.fly(); // Output: Sorry, I can't fly

Practical Application Scenarios of Method Overriding

Method overriding is widely used in framework development and everyday programming:

  1. UI Component Customization:
class BaseButton {
  onClick() {
    console.log('Default button click');
  }
}

class SubmitButton extends BaseButton {
  onClick() {
    super.onClick();
    this.submitForm();
  }
  
  submitForm() {
    console.log('Form submitted');
  }
}

const submitBtn = new SubmitButton();
submitBtn.onClick();
// Output:
// Default button click
// Form submitted
  1. Character Behavior in Game Development:
class Character {
  attack() {
    return 10; // Base attack power
  }
}

class Warrior extends Character {
  attack() {
    return super.attack() * 1.5; // Warrior attack bonus
  }
}

class Mage extends Character {
  attack() {
    return super.attack() + 15; // Mage magic attack
  }
}

Difference Between Method Overriding and Property Shadowing

Method overriding is different from property shadowing, where a new value simply overwrites the property:

class Parent {
  value = 1;
  getValue() {
    return this.value;
  }
}

class Child extends Parent {
  value = 2; // Property shadowing
  
  getValue() {
    return this.value * 2; // Method overriding
  }
}

const child = new Child();
console.log(child.value); // 2 (Property shadowing)
console.log(child.getValue()); // 4 (Method overriding)

Dynamic Method Overriding

JavaScript allows dynamic method overriding at runtime:

class DynamicExample {
  logMessage() {
    console.log('Original message');
  }
}

const instance = new DynamicExample();

// Original method
instance.logMessage(); // Output: Original message

// Dynamically override the method
instance.logMessage = function() {
  console.log('Dynamically replaced message');
};

instance.logMessage(); // Output: Dynamically replaced message

Considerations for Method Overriding

  1. Maintain Consistent Method Signatures:
class ApiClient {
  fetchData(url, options = {}) {
    // Base implementation
  }
}

class AuthApiClient extends ApiClient {
  // Incorrect overriding - parameter mismatch
  fetchData(url) {
    // Missing options parameter
  }
  
  // Correct overriding
  fetchData(url, options = {}) {
    options.headers = options.headers || {};
    options.headers.Authorization = 'Bearer token';
    return super.fetchData(url, options);
  }
}
  1. Avoid Excessive Overriding:
class OverrideExample {
  // Too many small method overrides can make maintenance difficult
  method1() { /*...*/ }
  method2() { /*...*/ }
  method3() { /*...*/ }
}

class BadPractice extends OverrideExample {
  method1() { /* Completely different implementation */ }
  method2() { /* Completely different implementation */ }
  method3() { /* Completely different implementation */ }
  // In such cases, consider refactoring instead of inheritance
}

Advanced Method Overriding Patterns

  1. Decorator Pattern:
function withLogging(target, name, descriptor) {
  const original = descriptor.value;
  descriptor.value = function(...args) {
    console.log(`Calling ${name} with`, args);
    const result = original.apply(this, args);
    console.log(`Called ${name}, returned`, result);
    return result;
  };
  return descriptor;
}

class Calculator {
  @withLogging
  add(a, b) {
    return a + b;
  }
}

const calc = new Calculator();
calc.add(2, 3);
// Output:
// Calling add with [2, 3]
// Called add, returned 5
  1. Strategy Pattern:
class PaymentProcessor {
  constructor(strategy) {
    this.strategy = strategy;
  }
  
  process(amount) {
    return this.strategy.process(amount);
  }
}

class CreditCardStrategy {
  process(amount) {
    console.log(`Processing $${amount} via Credit Card`);
  }
}

class PayPalStrategy {
  process(amount) {
    console.log(`Processing $${amount} via PayPal`);
  }
}

const processor = new PaymentProcessor(new CreditCardStrategy());
processor.process(100); // Processing $100 via Credit Card

processor.strategy = new PayPalStrategy();
processor.process(200); // Processing $200 via PayPal

Method Overriding and Performance Considerations

Frequent method overriding can impact performance, especially in hot code paths:

class PerformanceExample {
  heavyCalculation() {
    let result = 0;
    for (let i = 0; i < 1e6; i++) {
      result += Math.sqrt(i);
    }
    return result;
  }
}

class OptimizedExample extends PerformanceExample {
  heavyCalculation() {
    if (this.useCache && this.cachedResult) {
      return this.cachedResult;
    }
    
    const result = super.heavyCalculation();
    
    if (this.useCache) {
      this.cachedResult = result;
    }
    
    return result;
  }
  
  constructor() {
    super();
    this.useCache = true;
    this.cachedResult = null;
  }
}

Browser Compatibility and Method Overriding

Compatibility issues to note in older JavaScript environments:

// Compatibility writing for ES5 and below
function OldWay() {}
OldWay.prototype.method = function() {
  console.log('Old way');
};

function NewWay() {}
NewWay.prototype = Object.create(OldWay.prototype);
NewWay.prototype.method = function() {
  OldWay.prototype.method.call(this); // Explicitly call the parent class method
  console.log('New way');
};

var instance = new NewWay();
instance.method();
// Output:
// Old way
// New way

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.