当前位置: 首页 > article >正文

JS面向对象及继承

目录

一、面向对象

1.面向对象特征

2.创建对象的方法

(1)通过new关键词

(2)对象字面量

(3)构造函数创建

(4)Object.create()创建

(5)ES6中的类

(6)工厂函数

(7)函数表达式与对象字面量的结合

(8)使用模板字面量创建对象(ES6+)

(9)通过解构赋值和剩余参数创建对象(ES6)

二、对象继承的方式

1.原型链继承

2.对象属性继承

3.ES6类继承

4.构造函数继承

5.组合继承


一、面向对象

1.面向对象特征

封装、继承、多态、抽象

封装:把客观事物封装成抽象的类,且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的类或者对象隐藏信息。

继承:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。

多态:一个类实例的相同方法在不同情形下有不同的表现形式。

2.创建对象的方法

(1)通过new关键词

console.dir(Object)方法:可以显示指定的JavaScript对象的属性列表

let obj3 = new Object();
console.log(obj3);
console.dir(obj3);

(2)对象字面量

这是最简单和直接的方式,通过直接量语法创建对象。

var obj1 = {
    name: 'jack',
    age: 20,
    sayHello: function(){
        console.log('Hello, I am'+ this.name);
    }
};
console.log(obj1);

(3)构造函数创建

构造函数是一个特殊的函数,用于初始化新创建的对象。使用new关键字与构造函数一起调用,可以创建一个新的对象实例。

function Person(name, age) {
    this.name = name;
    this.age = age;
    this.sayHello = function() {
        console.log('Hello, I am'+ this.name);
    }
}
var obj2 = new Person('Tom', 25);
console.log(obj2);

(4)Object.create()创建

Object.create() 创建一个新对象,并指定一个原型对象

const proto = {
    greet: function() {
        console.log('Hello!');
    }
};
let obj4 = Object.create(proto);
console.log('obj4',obj4);

(5)ES6中的类

ES6引入了类(Class)语法,使得对象创建和继承更加直观和易于理解。

语法:class xxx{

               constructor(参数){}

               函数名(){}

           }

class Animal {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    sayHello() {
        console.log('Hello, I am'+ this.name);
    }
}
let obj5 = new Animal('Lion', 10);
console.log(obj5);
obj5.sayHello(); // Hello, I am Lion
console.log(obj5.__proto__ === Animal.prototype); // true
console.log(obj5 instanceof Animal); // true

(6)工厂函数

工厂函数是一种创建对象的方法,它不需要使用new关键字,而是返回一个新对象。

可与构造函数作对比

function Person(name, age) {
    return {
        name:name,
        age:age,
        sayHello:function() {
            console.log('Hello, I am'+ this.name);
        }
    }
}
var obj6 = Person('Tom', 25);
console.log(obj6);

(7)函数表达式与对象字面量的结合

通常用于创建模块或私有变量。

const obj7=(function(){
    let num=10;
    return {
        getNum: function() {
            return num;
        }
    }
})();
console.log(obj7.getNum()); // 10

(8)使用模板字面量创建对象(ES6+)

ES6 对象的简写和计算属性:模板字面量主要用于字符串插值,但结合计算属性名和函数,也可以用于动态创建对象。

const key = 'name';
const value = 'haha';
const obj8 = {
    [key]: value
};

console.log(obj8.name); // haha

(9)通过解构赋值和剩余参数创建对象(ES6)

解构赋值和剩余参数可以与对象字面量结合使用,以更灵活的方式创建和修改对象。

const { a, b, ...object } = { a: 1, b: 2, c: 3, d: 4 };
const newObj = { ...object, e: 5 };

console.log(newObj); // { c: 3, d: 4, e: 5 }

二、对象继承的方式

在前端领域,继承通常是指JavaScript中的继承机制。JavaScript利用原型对象的链式查找机制实现继承。每个对象都有一个原型对象,当访问一个对象的属性或方法时,如果在该对象本身找不到,就会在其原型对象中查找,依次类推,直到找到或者到达原型链的顶(Object.prototype)。

原型与原型链:原型与原型链-CSDN博客

1.原型链继承

*共享引用类型属性

原理:将子构造函数的原型对象设置为父构造函数的实例,从而实现继承。

优点:可以实现函数复用。

缺点:引用类型属性会被所有实例共享,修改一个实例的属性会影响其他实例。在创建子类型实例时,不能向父类型构造函数传递参数。

function Parent(name) {
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
}

Parent.prototype.sayName = function() {
    console.log(this.name);
};

function Child(age) {
    this.age = age;
}

// 将子构造函数的原型对象设置为父构造函数的实例
Child.prototype = new Parent();
Child.prototype.constructor = Child;

const child1 = new Child(10);
const child2 = new Child(12);

child1.colors.push('black');

console.log(child1.name); // undefined,因为name属性是在Parent构造函数中定义的,未传递参数
console.log(child1.colors); // ['red', 'blue', 'green', 'black']
console.log(child2.colors); // ['red', 'blue', 'green', 'black'](共享引用类型属性)
child1.sayName(); // undefined,因为name属性是undefined

2.对象属性继承

*共享引用类型属性

通过拷贝属性实现

const parent = {
    name: 'Parent',
    colors: ['red', 'blue', 'green'],
    sayName: function() {
        console.log(this.name);
    }
};
const child = Object.create(parent); // 创建一个以parent为原型的对象
child.age = 10;
console.log(child.name); // 'Parent'
child.colors.push('black');
console.log(parent.colors); // ['red', 'blue', 'green', 'black'](共享引用类型属性)

3.ES6类继承

*不共享引用类型属性

真正意义上的继承

原理:使用class关键字定义类,通过extends关键字实现继承。

优点:语法更加简洁明了,易于理解和维护。

缺点:相对于原型链继承和构造函数继承来说,是ES6引入的新特性。

class Parent {
    constructor(name) {
        this.name = name;
        this.colors = ['red', 'blue', 'green'];
    }
    sayName() {
        console.log(this.name);
    }
}

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

const child1 = new Child('Child1', 10);
// ...

4.构造函数继承

*不共享引用类型属性

原理:在子构造函数中调用父构造函数,并使用call或apply方法将父构造函数的作用域绑定到子构造函数中,从而继承父构造函数的属性。

优点:可以向父构造函数传递参数。可以解决引用类型属性共享的问题。

缺点:方法不能复用,每次创建实例时都会创建一遍方法。

function Parent(name) {
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
}

Parent.prototype.sayName = function() {
    console.log(this.name);
};

function Child(name, age) {
    Parent.call(this, name); // 调用父构造函数
    this.age = age;
}
// ...

5.组合继承

*不共享引用类型属性

原理:结合原型链继承和构造函数继承的优点,通过在子构造函数中调用父构造函数来继承属性,同时将子构造函数的原型设置为父构造函数的实例,从而继承方法。

优点:可以向父构造函数传递参数。既可以实现属性继承,又可以实现方法复用。

缺点:调用了两次父构造函数,一次是在设置子构造函数的原型时,一次是在子构造函数内部。

function Parent(name) {
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
}

Parent.prototype.sayName = function() {
    console.log(this.name);
};

function Child(name, age) {
    Parent.call(this, name); // 继承父构造函数的属性
    this.age = age;
}

// 继承父构造函数的原型链
Child.prototype = new Parent();
Child.prototype.constructor = Child;

const child1 = new Child('Child1', 10);
const child2 = new Child('Child2', 12);

child1.colors.push('black');

console.log(child1.name); // 'Child1'
console.log(child1.colors); // ['red', 'blue', 'green', 'black']
console.log(child2.colors); // ['red', 'blue', 'green'](不共享)
child1.sayName(); // 'Child1'

注意:在组合继承中,虽然Child.prototype = new Parent();会调用一次Parent构造函数,但这是为了设置原型链,属性是通过Parent.call(this, name)Child构造函数中传递的,所以不会造成属性共享问题,同时方法也能复用。

若文章对你有帮助,点赞、收藏加关注吧!


http://www.kler.cn/a/445942.html

相关文章:

  • [创业之路-198]:华为的成立发展与新中国的建立与发展路径的相似性比较
  • WordPress 去除?v= 动态后缀
  • EasyExcel 导出文件
  • 基于SpringBoot+Vue实现的个人备忘录系统
  • 【进程篇】操作系统
  • 整合 Knife4j 于 Spring Cloud 网关:实现跨服务的 API 文档统一展示
  • 【第六节】Git Flow:分支管理模型与工作流程
  • nano编辑器怎么退出并保存
  • DeepFaceLab技术浅析(六):后处理过程
  • Golang中什么是协程泄露(Goroutine Leak)
  • autok3s管理k3s单节点集群
  • [Unity] 【VR】【游戏开发】在VR中使用New Input System获取按键值的完整教程
  • 【Jenkins】pipeline 的基础语法以及快速构建一个 jenkinsfile
  • sql server索引优化语句
  • Tomcat10安装报错Unknown module: java.rmi specified to --add-opens
  • nginx-虚拟主机配置笔记
  • Python TXT文件按条件批量删除行工具
  • 静态socks5代理ip 帮助您找到最合适的ip代理服务
  • 通过阿里云 Milvus 与 PAI 搭建高效的检索增强对话系统
  • JVM基本机制
  • Java 网络编程 ①-TCP || UDP || Socket
  • [搜广推]王树森推荐系统——矩阵补充最近邻查找
  • 深度解析 HarmonyOS 中的 RichEditor:实现图文混排与交互式编辑的利器
  • GO环境安装和配置
  • Linux Systemd基础教程
  • 【Linux】磁盘空间莫名消失,找不到具体原因的思路