亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定

JS面向對象學習:從入門到初級教程

標簽:
JavaScript
概述

本文详细介绍了JS面向对象学习的基础概念,包括对象和类的定义、构造函数与实例化、属性与方法的定义等。文章还深入讲解了访问修饰符与封装、继承与多态等高级主题,并通过实战案例和常见问题解答帮助读者更好地理解和掌握JS面向对象编程的知识。

JS面向对象学习:从入门到初级教程
JS面向对象基础概念

对象和类的定义

在JavaScript中,对象是属性和方法的集合。属性是对象的状态或数据,方法是对象的行为或操作。类是创建对象的蓝图,定义了对象的属性和方法。

创建一个简单的对象

let person = {
    name: "张三",
    age: 25,
    introduce: function() {
        console.log(`我是${this.name},今年${this.age}岁`);
    }
};

person.introduce(); // 输出:我是张三,今年25岁

使用类来定义对象

从ES6开始,JavaScript引入了类的概念,使得面向对象编程更加简洁和直观。

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    introduce() {
        console.log(`我是${this.name},今年${this.age}岁`);
    }
}

let person = new Person("张三", 25);
person.introduce(); // 输出:我是张三,今年25岁

构造函数与实例化

构造函数是一种特殊的函数,用于创建和初始化对象。在使用new关键字调用构造函数时,会创建一个新的对象实例。

function Person(name, age) {
    this.name = name;
    this.age = age;
    this.introduce = function() {
        console.log(`我是${this.name},今年${this.age}岁`);
    };
}

let person = new Person("张三", 25);
person.introduce(); // 输出:我是张三,今年25岁

属性与方法的定义

属性是类或对象中的变量,表示对象的状态或数据。方法是类或对象中的函数,表示对象的行为或操作。

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    introduce() {
        console.log(`我是${this.name},今年${this.age}岁`);
    }
}

let person = new Person("张三", 25);
person.introduce(); // 输出:我是张三,今年25岁
访问修饰符与封装

公有、私有属性和方法

在面向对象编程中,封装指的是将对象的内部细节隐藏起来,只暴露必要的接口。通过控制属性和方法的访问权限,可以实现封装。

  • 公有属性和方法:可以在类的任何地方访问。
  • 私有属性和方法:只在类的内部访问,不能被外部访问。

公有访问

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    introduce() {
        console.log(`我是${this.name},今年${this.age}岁`);
    }
}

let person = new Person("张三", 25);
person.introduce(); // 输出:我是张三,今年25岁

私有访问

由于JavaScript本身没有内置的私有属性和方法,可以通过一些约定或技巧来实现。

class Person {
    constructor(name, age) {
        this._name = name; // 通过下划线前缀表示私有属性
        this._age = age;
    }

    get name() {
        return this._name;
    }

    set name(value) {
        this._name = value;
    }

    introduce() {
        console.log(`我是${this._name},今年${this._age}岁`);
    }
}

let person = new Person("张三", 25);
console.log(person._name); // 输出:张三
person.name = "李四";
console.log(person._name); // 输出:李四

封装类的内部实现

封装类的内部实现可以隐藏对象的内部状态和实现细节,只暴露必要的接口给外部使用。

class BankAccount {
    constructor(owner, balance) {
        this.owner = owner;
        this._balance = balance;
    }

    getBalance() {
        return this._balance;
    }

    deposit(amount) {
        this._balance += amount;
    }

    withdraw(amount) {
        if (amount > this._balance) {
            console.log("余额不足");
        } else {
            this._balance -= amount;
        }
    }
}

let account = new BankAccount("张三", 1000);
console.log(account.getBalance()); // 输出:1000
account.deposit(500);
console.log(account.getBalance()); // 输出:1500
account.withdraw(2000); // 输出:余额不足
account.withdraw(1000);
console.log(account.getBalance()); // 输出:500

使用this关键字

this关键字在JavaScript中用于引用当前对象。在方法内部使用this可以访问当前对象的属性和方法。

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    introduce() {
        console.log(`我是${this.name},今年${this.age}岁`);
    }
}

let person = new Person("张三", 25);
person.introduce(); // 输出:我是张三,今年25岁
继承与多态

继承的概念

继承是一种机制,允许一个类(子类或派生类)继承另一个类(父类或基类)的属性和方法。通过继承,可以实现代码的重用和扩展。

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    introduce() {
        console.log(`我是${this.name},今年${this.age}岁`);
    }
}

class Student extends Person {
    constructor(name, age, grade) {
        super(name, age); // 调用父类的构造函数
        this.grade = grade;
    }

    introduce() {
        super.introduce(); // 调用父类的方法
        console.log(`我在读${this.grade}年级`);
    }
}

let student = new Student("张三", 18, "大一");
student.introduce(); // 输出:我是张三,今年18岁\n我在读大一

使用原型链继承

原型链继承是JavaScript中最原始的继承方式,通过原型链实现属性和方法的继承。

function Person(name, age) {
    this.name = name;
    this.age = age;
}

Person.prototype.introduce = function() {
    console.log(`我是${this.name},今年${this.age}岁`);
};

function Student(name, age, grade) {
    Person.call(this, name, age); // 调用父类的构造函数
    this.grade = grade;
}

Student.prototype = Object.create(Person.prototype); // 设置原型
Student.prototype.constructor = Student; // 修复构造函数

Student.prototype.introduce = function() {
    Person.prototype.introduce.call(this); // 调用父类的方法
    console.log(`我在读${this.grade}年级`);
};

let student = new Student("张三", 18, "大一");
student.introduce(); // 输出:我是张三,今年18岁\n我在读大一

构造函数继承

构造函数继承是一种结合原型链和构造函数的方法,通过借用构造函数实现继承。

function Person(name, age) {
    this.name = name;
    this.age = age;
}

Person.prototype.introduce = function() {
    console.log(`我是${this.name},今年${this.age}岁`);
};

function Student(name, age, grade) {
    Person.call(this, name, age); // 调用父类的构造函数
    this.grade = grade;
}

Student.prototype = Object.create(Person.prototype); // 设置原型
Student.prototype.constructor = Student; // 修复构造函数

Student.prototype.introduce = function() {
    Person.prototype.introduce.call(this); // 调用父类的方法
    console.log(`我在读${this.grade}年级`);
};

let student = new Student("张三", 18, "大一");
student.introduce(); // 输出:我是张三,今年18岁\n我在读大一

实现多态

多态是一种机制,允许同一方法在派生类中具有不同的实现。通过多态,可以实现更灵活和动态的编程。

class Animal {
    speak() {
        console.log("动物在说话");
    }
}

class Dog extends Animal {
    speak() {
        console.log("汪汪汪");
    }
}

class Cat extends Animal {
    speak() {
        console.log("喵喵喵");
    }
}

let dog = new Dog();
let cat = new Cat();

dog.speak(); // 输出:汪汪汪
cat.speak(); // 输出:喵喵喵
面向对象设计原则

单一职责原则

单一职责原则意味着一个类应该只负责一个功能。这样可以提高代码的可维护性和可扩展性。

// 错误示例
class User {
    constructor(name) {
        this.name = name;
    }

    getName() {
        return this.name;
    }

    calculateAge() {
        // 计算年龄的逻辑
    }
}

// 正确示例
class User {
    constructor(name) {
        this.name = name;
    }

    getName() {
        return this.name;
    }
}

class AgeCalculator {
    calculateAge(user) {
        // 计算年龄的逻辑
    }
}

开闭原则

开闭原则意味着一个类应该对扩展开放,对修改关闭。即在不修改原有代码的情况下,可以扩展类的功能。

// 错误示例
class Calculator {
    add(a, b) {
        return a + b;
    }

    multiply(a, b) {
        return a * b;
    }
}

// 正确示例
class Calculator {
    add(a, b) {
        return a + b;
    }
}

class ExtendedCalculator extends Calculator {
    multiply(a, b) {
        return a * b;
    }
}

里氏替换原则

里氏替换原则意味着子类可以替换父类而不会影响程序的正确性。即子类可以扩展父类功能而不破坏父类的接口。

class Shape {
    area() {
        throw new Error("未实现面积计算");
    }
}

class Circle extends Shape {
    constructor(radius) {
        super();
        this.radius = radius;
    }

    area() {
        return Math.PI * this.radius * this.radius;
    }
}

class Rectangle extends Shape {
    constructor(width, height) {
        super();
        this.width = width;
        this.height = height;
    }

    area() {
        return this.width * this.height;
    }
}

let shapes = [new Circle(5), new Rectangle(4, 6)];
shapes.forEach(shape => {
    console.log(shape.area());
});

接口隔离原则

接口隔离原则意味着一个类不应该依赖于它不需要的接口。即多个特定接口比一个通用接口要好。

// 错误示例
interface Animal {
    eat();
    drink();
    speak();
}

class Dog implements Animal {
    eat() {
        console.log("狗在吃东西");
    }

    drink() {
        console.log("狗在喝水");
    }

    speak() {
        console.log("汪汪汪");
    }
}

class Bird implements Animal {
    eat() {
        console.log("鸟在吃东西");
    }

    drink() {
        console.log("鸟在喝水");
    }

    speak() {
        console.log("叽叽喳喳");
    }
}

// 正确示例
interface Eat {
    eat();
}

interface Drink {
    drink();
}

interface Speak {
    speak();
}

class Dog implements Eat, Speak {
    eat() {
        console.log("狗在吃东西");
    }

    speak() {
        console.log("汪汪汪");
    }
}

class Bird implements Eat, Drink, Speak {
    eat() {
        console.log("鸟在吃东西");
    }

    drink() {
        console.log("鸟在喝水");
    }

    speak() {
        console.log("叽叽喳喳");
    }
}
实践案例:模拟简单的银行账户系统

创建账户类

银行账户系统通常需要管理用户的账户信息,包括存款、取款等功能。

class BankAccount {
    constructor(owner, balance) {
        this.owner = owner;
        this._balance = balance;
    }

    getBalance() {
        return this._balance;
    }

    deposit(amount) {
        this._balance += amount;
    }

    withdraw(amount) {
        if (amount > this._balance) {
            console.log("余额不足");
        } else {
            this._balance -= amount;
        }
    }
}

实现存款、取款功能

通过实现存款和取款方法,可以管理账户的余额。

let account = new BankAccount("张三", 1000);
console.log(account.getBalance()); // 输出:1000
account.deposit(500);
console.log(account.getBalance()); // 输出:1500
account.withdraw(2000); // 输出:余额不足
account.withdraw(1000);
console.log(account.getBalance()); // 输出:500

展示多态的应用

多态允许在不同的账户类型中实现相同的方法,而具有不同的行为。

class SavingAccount extends BankAccount {
    constructor(owner, balance, interestRate) {
        super(owner, balance);
        this.interestRate = interestRate;
    }

    addInterest() {
        this.deposit(this._balance * this.interestRate);
    }
}

let savingAccount = new SavingAccount("张三", 1000, 0.05);
savingAccount.addInterest();
console.log(savingAccount.getBalance()); // 输出:1050

class CheckingAccount extends BankAccount {
    constructor(owner, balance) {
        super(owner, balance);
    }

    withdrawFee(amount) {
        this.withdraw(amount + 5); // 每次取款收取5元手续费
    }
}

let checkingAccount = new CheckingAccount("张三", 1000);
checkingAccount.withdrawFee(200); // 输出:余额不足
console.log(checkingAccount.getBalance()); // 输出:795
常见问题与解答

常见错误分析

  1. 忘记使用super调用父类的构造函数

    class Child extends Parent {
       constructor() {
           // 忘记使用super调用父类的构造函数
       }
    }

    正确做法:

    class Child extends Parent {
       constructor() {
           super(); // 调用父类构造函数
       }
    }
  2. 原型链继承可能导致原型方法被覆盖

    function Parent() {
       this.name = "Parent";
    }
    
    Parent.prototype.getName = function() {
       return this.name;
    };
    
    function Child() {
       Parent.call(this);
    }
    
    Child.prototype = new Parent(); // 这里可能导致原型方法被覆盖

    正确做法:

    function Parent() {
       this.name = "Parent";
    }
    
    Parent.prototype.getName = function() {
       return this.name;
    };
    
    function Child() {
       Parent.call(this);
    }
    
    Child.prototype = Object.create(Parent.prototype); // 设置原型
    Child.prototype.constructor = Child; // 修复构造函数

常见面试题解析

  1. JavaScript中的继承方式有哪些?

    • 构造函数继承
    • 原型链继承
    • 组合继承(同时使用构造函数和原型链)
    • 寄生组合继承(使用对象字面量创建中间类)
  2. 什么是this关键字?
    • this关键字在JavaScript中用于引用当前对象。在方法内部使用this可以访问当前对象的属性和方法。

实战经验分享

  1. 如何合理使用继承?

    • 继承可以实现代码的重用和扩展,但过度使用可能会导致代码结构复杂。建议在需要重复使用代码时使用继承。
  2. 如何避免JavaScript中的原型链问题?
    • 使用组合继承或寄生组合继承,可以避免原型链中的原型方法被覆盖。

通过以上案例和分析,希望读者能够更好地理解和掌握JavaScript面向对象编程的知识。更多学习资源可以参考慕课网(http://www.xianlaiwan.cn/)。

點擊查看更多內容
TA 點贊

若覺得本文不錯,就分享一下吧!

評論

作者其他優質文章

正在加載中
  • 推薦
  • 評論
  • 收藏
  • 共同學習,寫下你的評論
感謝您的支持,我會繼續努力的~
掃碼打賞,你說多少就多少
贊賞金額會直接到老師賬戶
支付方式
打開微信掃一掃,即可進行掃碼打賞哦
今天注冊有機會得

100積分直接送

付費專欄免費學

大額優惠券免費領

立即參與 放棄機會
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號

舉報

0/150
提交
取消