前端JavaScript-对象-同Python及C++对比
目录
JS 和 Python 的 OOP 差异
1.类与对象的定义
2.继承机制
3.封装和访问控制
4.多态与this/self的绑定
类和对象字面量
JS运行环境
对象基础
点表示法
设置成员
this 含义
实例化对象
Python的面向对象编程(OOP)详情可以看备战菊厂笔试1-CSDN博客
面向对象的三大特性
封装 将数据和方法放在一起,对外只暴露接口 class + def 组合,通过__init__构造
继承 子类拥有父类的属性和方法,代码复用 class B( A ) 表示B继承A
多态 不同对象调用同样的方法,但是同一个函数在不同对象上表现的行为不同,实现松耦合
(松耦合 Loose Coupling 是指系统或模块间的依赖性尽量降低,做到各自独立,如果模块化做的好可以为松耦合打下基础)
JS 和 Python 的 OOP 差异
然而 JS 的 OOP 和 Python 的 OOP 有很多差异
1.类与对象的定义
类实例化得到对象
Python:直接通过 class 定义,并且使用 __init__ 进行初始化和封装
无需 new ,直接通过类名创建实例,如
class Person:def __init__(self, name, age):self.name = nameself.age = agedef greet(self): return f"Hello, I'm {self.name}."
person = Person("Alice", 30)
JS:早期使用构造函数+原型链实现类(ES5之前),ES6引入 class 语法糖,本质仍然是原型链
必须使用 new 关键词进行实例化对象
class Person {constructor(name, age) { this.name = name; this.age = age; }greet() { return `Hello, I'm ${this.name}.`; }
}
const person = new Person("Alice", 30);
2.继承机制
Python 是基于类的继承,支持多态和显式的方式重写
直接 class A(B)
class Animal:def __init__(self, name): self.name = namedef speak(self): print(f"{self.name} makes a sound.")
class Dog(Animal):def speak(self): print(f"{self.name} barks.")
JS 基于原型链的继承,每个对象通过原型与其他对象相关联,继承属性和方法,例如通过构造函数和 prototype 属性拓展功能
(Q:JS中的继承是如何继承的?A:通过原型链实现,子对象可以通过原型链访问父对象的属性和方法)
function Animal(name) { this.name = name; }
Animal.prototype.speak = function() { console.log(`${this.name} makes a sound.`); };
class Dog extends Animal { // ES6语法糖,底层仍是原型链speak() { console.log(`${this.name} barks.`); }
}
(ES6语法糖,是指 ECMAScript 6(也称 ES2015)中新增的一些写法更简洁、可读性更强的语法形式,它们本质上是对已有功能的“包装”,并没有引入全新能力,但让代码更优雅易懂。)
(语法糖:对已有功能语法优化,不改变本质,只是让代码更好写)
3.封装和访问控制
Python:通过命名约定,本质是伪私有(通过名称改写)
JS:没有原生的私有成员语法,需通过闭包、Symbol 或命名约定来模拟私有属性
4.多态与this/self的绑定
Python:显示地传递 self 作为方法的第一个参数
多态通过方法实现和鸭子类型自然实现
(什么是鸭子类型?
“如果它像鸭子、会鸭子叫、走路像鸭子,那它就是鸭子。”
即:不关心对象的真实类型,只关心它有没有某个“行为”或“特征”)
JS:this 的指向动态绑定,取决于调用上下文,可能会导致意外(比如回调的时候丢失 this )
需使用 bind() 、箭头函数(匿名函数)或显示保存 this 来确保正确绑定
在下面的 this含义 篇我会详细介绍(“调用方式决定this”)
总之,面向对象的核心概念差不多,但是实现过程中有差异,JS更适合动态、事件驱动的场景(比如前端交互),python在复杂层和科学计算中更直观
类和对象字面量
上面介绍的是对象的模板,通过 new 实例化制造很多对象字面量
而我们下面要介绍的是对象字面量,是单个实例,JS的对象自变量有点像动态的C++结构体:
他们在数据聚合的用途上有相似之处,但是在底层设计、灵活性和功能上有明显差异
C++结构体是先定义结构体有什么类型的成员,而JS的对象字面量是直接通过键值对的方式进行创建对象
struct Point {int x;int y;
};
Point p1 = {10, 20}; // 创建结构体实例
const point = { x: 10, y: 20 }; // 直接创建对象
C++结构体是强类型的、固定的,JS是弱类型的、对象的属性可以增删
若想要更接近C++结构体的行为,可以使用 TypeScript 的接口或类,通过静态类型约束增强安全性
JS运行环境
1.在浏览器开发者工具中运行
打开浏览器 -> 按F12(或者shift+Ctrl+i) -> 切换到 Console 控制台 -> 输入JS代码回车即可
2.在Node.js运行环境中进行
先创建 JS 文件
然后在终端输入:node 文件名.js
3.在线运行
JSFiddle
对象基础
对象是一个包含相关数据和方法的集合
现在我们用一个例子来理解
<!DOCTYPE html>
<html lang="en-US"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>Object-oriented JavaScript example</title></head><body><p>This example requires you to enter commands in your browser's JavaScript console (see <a href="https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_are_browser_developer_tools">What are browser developer tools</a> for more information).</p><script src="oojs.js"></script></body></html>
const person = {};
用浏览器打开 html 文件后,按F12选择console打开浏览器控制台,然后输入 person ,会看到
{}
这是一个空对象,现在我们要对其进行赋属性、方法,更新.js 文件
const person = {name: ["Bob", "Smith"],age: 32,bio: function () {console.log(`${this.name[0]} ${this.name[1]} 现在 ${this.age} 岁了。`);},introduceSelf: function () {console.log(`你好!我是 ${this.name[0]}。`);},
};
现在调用属性和方法会看到
person.bio()person.introduceSelf()
Bob Smith 现在 32 岁了。你好!我是 Bob。
像前面的年龄这种数据项就是这个对象的属性,后面的函数这种就是对象的方法
像在服务器发起请求以存储一些数据到数据库时,常见方法就是使用字面量创建对象,这比单独发送这些数据更加有效率而且更加直观、容易维护
点表示法
像上面的 对象名.属性名 / .方法名() 就是点表示法,像 内置API函数(比如 .split(',') ) 就是对象方法
我们也可以在里面再声明一个对象,称之为子命名空间,比如
const person = {name: ["Bob", "Smith"],
};
这时候我们只需要链式的访问即可,和其他语言都是一样的
person.name.first;
person.name.last;
我们也可以通过 关联数组 的方式(但是这样就相比之下比较麻烦)
person["age"];
person["name"]["first"];
设置成员
上面我说过 JS对象字面量 是动态的、灵活的
我们可以直接通过 赋值 语句进行更新、添加成员(有点像python的字典)
this 含义
还记得我在上面说过 JS的this 和 python的self 相比是动态绑定
这是因为 python的self 是谁调用了方法就代表谁,也就是当前实例,必须写在__init__参数中
但在 JS中,调用方式决定 this
1.作为对象方法调用
指向当前代码运行时的对象
obj.method() // this 指向 obj
2.单独调用函数(非严格模式)
function f() {console.log(this);
}
f(); // this 指向全局对象(浏览器中是 window)
3.箭头函数(匿名函数)中,this永远继承自外层作用域
const obj = {name: "Tom",say: function() {const inner = () => console.log(this.name);inner(); // 箭头函数不绑定自己的 this,继承外层 obj 的 this}
};
obj.say(); // 输出 "Tom"
4.显式强制绑定
function hello() {console.log(this.name);
}
const p = { name: "Jerry" };
hello.call(p); // this 被强制绑定为 p
大量同质对象
回收前面的铺垫,想要批量制造大量类似的实例有两种方法
1.自定义函数(ES5以及之前)
function Person(name, age) {this.name = name;this.age = age;this.sayHello = function() {console.log("Hi, I'm " + this.name);};
}const p1 = new Person("Tom", 18);
p1.sayHello(); // 输出:Hi, I'm Tom
2.类(ES6引入)
new
class Person {constructor(name, age) {this.name = name;this.age = age;}sayHello() {console.log("Hi, I'm " + this.name);}
}const p2 = new Person("Jerry", 20);
p2.sayHello(); // 输出:Hi, I'm Jerry