1.面向对象的基本概念
1.1 面向对象思想
- 面向对象(Object Oriented,OO)是软件开发方法
- 面向对象是一种对现实世界抽象的理解,是计算机编程技术发展到一定阶段后的产物
- Object Oriented Programming-OOP ——面向对象编程
1.2 面向对象和面向过程区别
内容 | 强调 | 关注 |
---|---|---|
面向过程 | 强调的是功能行为 | 关注的是解决问题需要哪些步骤 |
面向对象 | 将功能封装进对象,强调具备了功能的对象 | 关注的是解决问题需要哪些对象 |
-
面向对象是基于面向过程而言
-
面向对象和面向过程都是一种思想
2. 类与对象的关系
2.1 使用默认类创建对象
2.1.1 通过 new Object() 创建对象
-
1.JavaScript中提供了一个默认的类Object, 我们可以通过这个类来创建对象
-
2.由于我们是使用系统默认的类创建的对象, 所以系统不知道我们想要什么属性和行为, 所以我们必须
手动的添加
我们想要的属性和行为 -
3.如何给一个对象添加属性
对象名称.属性名称 = 值;
-
4.如何给一个对象添加行为
对象名称.行为名称 = 函数;
// 创建对象的第一种方式
let obj = new Object();
obj.name = "sss";
obj.age = 33;
obj.say = function () {
console.log("hello world");
}
console.log(obj.name);
console.log(obj.age);
obj.say();
2.1.2 使用字面量创建对象
let obj = {}; // 相当于 let obj = new Object();
obj.name = "lnj";
obj.age = 33;
obj.say = function () {
console.log("hello world");
}
console.log(obj.name);
console.log(obj.age);
obj.say();
// 注意点: 属性名称和取值之间用冒号隔开, 属性和属性之间用逗号隔开
let obj = {
name: "sss",
age: 33,
say: function () {
console.log("hello world");
}
};
console.log(obj.name);
console.log(obj.age);
obj.say();
2.1.3 工厂函数创建对象
上面的创建方式, 每多创建一个人都需要将代码再写一遍, 冗余代码太多, 所以可以创建创建对象的代码封装到一个函数中
专门用于创建对象的函数我们称之为工厂函数
function createPerson(myName, myAge) {
let obj = new Object();
obj.name = myName;
obj.age = myAge;
obj.say = function () {
console.log("hello world");
}
return obj;
}
let obj1 = createPerson("lnj", 34);
let obj2 = createPerson("zs", 44);
console.log(obj1);
console.log(obj2);
2.1.4 构造函数创建对象
因为工厂函数创建对象并不专业,所以又引申出了构造函数
1.什么是构造函数
- 构造函数和工厂函数一样, 都是
专门用于创建对象的
- 构造函数
本质上
是工厂函数的简写
2.构造函数和工厂函数的区别
- 2.1 构造函数的函数名称
首字母必须大写
- 2.2 构造函数
只能
够通过new来调用
function Person(myName, myAge) {
// let obj = new Object(); // 系统自动添加的
// let this = obj; // 系统自动添加的
this.name = myName;
this.age = myAge;
this.say = function () {
console.log("hello world");
}
// return this; // 系统自动添加的
}
let obj1 = new Person("ls", 34);
let obj2 = new Person("zs", 44);
console.log(obj1); // Person
console.log(obj2); // Person
/*
1.当我们new Person("ls", 34);系统做了什么事情
1.1会在构造函数中自动创建一个对象
1.2会自动将刚才创建的对象赋值给this
1.3会在构造函数的最后自动添加return this;
*/
3.构造函数的方法中的this谁调用就是谁
function Person(myName, myAge) {
this.name = myName;
this.age = myAge;
this.say = function () {
// 方法中的this谁调用就是谁
console.log(this, this.name, this.age);
}
}
let obj1 = new Person("sss", 34);
obj1.say();
let obj2 = new Person("zs", 44);
obj2.say();
4.构造函数的性能问题
创建一个对象就会在内存开辟一块存储空间
由于两个对象中的say方法的实现都是一样的, 但是保存到了不同的存储空间中,所以有性能问题
5.构造函数的优化
1.prototype
- JavaScript 规定,每一个
构造函数
都有一个prototype 属性
,指向另一个对象 - 这个对象的所有属性和方法,都会被构造函数的所拥有
- 也就意味着,可以把所有对象实例需要共享的属性和方法直接定义在 prototype 对象上
function Person(myName, myAge) {
this.name = myName;
this.age = myAge;
}
Person.prototype = {
say: function () {
console.log("hello world");
}
}
let obj1 = new Person("sss", 34);
obj1.say();
let obj2 = new Person("zs", 44);
obj2.say();
console.log(obj1.say === obj2.say); // true
3. 面向对象三大特性
封装性
1.什么是封装?
封装性就是隐藏实现细节,仅对外公开接口
2.为什么要封装?
- 不封装的缺点:当一个类把自己的成员变量暴露给外部的时候,那么该类就失去对属性的管理权,别人可以任意的修改你的属性
- 封装就是将数据隐藏起来,只能用此类的方法才可以读取或者设置数据,不可被外部任意修改. 封装是面向对象设计本质(将变化隔离)。这样降低了数据被误用的可能 (提高安全性和灵活性)
function Person() {
this.name = "lnj";
// this.age = 34;
let age = 34;
this.setAge = function (myAge) {
if (myAge >= 0) {
age = myAge;
}
}
this.getAge = function () {
return age;
}
this.say = function () {
console.log("hello world");
}
/*
// 由于构造函数也是一个函数, 所以也会开启一个新的作用域
// 所以在构造函数中通过var/let定义的变量也是局部变量
// 所以在构造函数中定义的函数也是局部函数
var num = 123;
let value = 456;
function test() {
console.log("test");
}
*/
}
let obj = new Person();
// 结论: 默认情况下对象的属性和方法都是公开的, 只要拿到对象就可以操作对象的属性和方法
// console.log(obj.name);
// obj.age = -3;
// console.log(obj.age);
// obj.say();
// console.log(age); // 无法直接获取以及修改
obj.setAge(-3); // 提供给修改的接口以及控制输入的内容
console.log(obj.getAge()); // 提供获取的接口
3.私有属性变量和函数
默认情况下对象中的属性和方法都是公有的, 只要拿到对象就能操作对象的属性和方法
外界不能直接访问的变量和函数就是私有变量和私有函数
3.1 私有属性注意点
在给一个对象不存在的属性设置值的时候, 不会去原型对象中查找, 如果当前对象没有就会给当前对象新增一个不存在的属性
由于
私有属性的本质就是一个局部变量
, 并不是真正的属性, 所以如果通过 对象.xxx的方式是找不到私有属性的, 所以会给当前对象新增一个不存在的属性
function Person() {
this.name = "lnj";
let age = 34;
this.setAge = function (myAge) {
if (myAge >= 0) {
age = myAge;
}
}
this.getAge = function () {
return age;
}
this.say = function () {
console.log("hello world");
}
}
let obj = new Person();
// 1.操作的是私有属性(局部变量)
obj.setAge(-3);
console.log(obj.getAge());
// 2.操作的是公有属性
obj.age = -3;
console.log(obj.age);
4.属性和方法分类
1.实例属性/实例方法
在企业开发中通过实例对象
访问的属性, 我们就称之为实例属性
在企业开发中通过实例对象
调用的方法, 我们就称之为实例方法
function Person() {
this.name = "lnj";
this.say = function () {
console.log("hello world");
}
}
// 通过构造函数创建的对象, 我们称之为"实例对象"
let obj = new Person();
console.log(obj.name);
obj.say();
obj.age = 34;
console.log(obj.age);
obj.eat = function () {
console.log("eat");
}
obj.eat();
2.静态属性/静态方法
在企业开发中通过构造函数
访问的属性, 我们就称之为静态属性
在企业开发中通过构造函数
调用的方法, 我们就称之为静态方法
function Person() {
this.name = "lnj";
this.say = function () {
console.log("hello world");
}
}
// 构造函数也是一个"对象", 所以我们也可以给构造函数动态添加属性和方法
Person.num = 666;
Person.run = function () {
console.log("run");
}
console.log(Person.num);
Person.run();
继承性
- js中继承目的: 把子类型中共同的属性和方法提取到父类型中
- 较少代码的冗余度, 提升代码的复用性
实现继承的方法
4. 终极方案
- 1在子类的构造函数中通过call借助父类的构造函数
- 2将子类的原型对象修改为父类的实例对象
Student.prototype = new Person();
Student.prototype.constructor = Student;
function Person(myName, myAge) {
// let per = new Object();
// let this = per;
// this = stu;
this.name = myName; // stu.name = myName;
this.age = myAge; // stu.age = myAge;
// this.say = function () { // stu.say = function () {}
// console.log(this.name, this.age);
// }
// return this;
}
Person.prototype.say = function () {
console.log(this.name, this.age);
}
function Student(myName, myAge, myScore) {
Person.call(this, myName, myAge);
this.score = myScore;
this.study = function () {
console.log("day day up");
}
}
Student.prototype = new Person();
Student.prototype.constructor = Student;
Student.prototype.run = function(){
console.log("run");
}
let per = new Person();
per.run(); // 报错
多态
1.什么是多态?
多态是指事物的多种状态
例如:
按下 F1 键这个动作,
如果当前在 webstorm 界面下弹出的就是 webstorm 的帮助文档;
如果当前在 Word 下弹出的就是 Word 帮助;
同一个事件发生在不同的对象上会产生不同的结果。
2.多态在编程语言中的体现
父类型变量保存子类型对象, 父类型变量当前保存的对象不同, 产生的结果也不同
4.ES6类和对象
1.在ES6之前如果定义一个类?
通过构造函数来定义一个类
2.从ES6开始系统提供了一个名称叫做class的关键字, 这个关键字就是专门用于定义类的
class Person{
// 当我们通过new创建对象的时候, 系统会自动调用constructor
// constructor我们称之为构造函数
constructor(myName, myAge){
this.name = myName;
this.age = myAge;
}
// 实例属性
// name = "lnj";
// age = 34;
// 实例方法
say(){
console.log(this.name, this.age);
}
// 静态属性
static num = 666;
// 静态方法
static run() {
console.log("run");
}
}
// let p = new Person();
let p = new Person("zs", 18);
p.say();
console.log(Person.num);
Person.run();
1. 以下定义"实例属性"的方式并不是ES6正式版标准中的写法, 大部分的浏览器不支持
在ES6标准中添加实例属性都需要在
constructor
中添加
// 实例属性
// name = "lnj";
// age = 34;
constructor(){
this.name = "lnj";
this.age = 34;
}
2. 以下定义"静态属性"的方式并不是ES6正式版标准中的写法, 大部分的浏览器不支持
在ES标准中
static只支持定义静态方法不支持定义静态变量
class Person {
// 静态属性
// static num = 666;
// 静态方法
static run() {
console.log("run");
}
}
// 静态属性
Person.num = 666;
let p = new Person();
console.log(p);
3.constructor 内写内容相当于之前在构造函数中编写,而在constructor以外编写的相当于在prototype原型上
function Person(myName, myAge) {
// 实例属性
this.name = myName;
this.age = myAge;
// 实例方法
this.hi = function () {
console.log("hi");
}
}
Person.prototype.say = function () {
console.log(this.name, this.age);
}
let p = new Person("lnj", 34);
console.log(p);
class Person{
constructor(myName, myAge){
this.name = myName;
this.age = myAge;
this.hi = function () {
console.log("hi");
}
}
say(){
console.log("hi");
}
}
let p = new Person("lnj", 34);
console.log(p);
4.在prototype上添加属性和方法
注意点:
如果通过
class定义类
, 那么不能自定义
这个类的原型对象
如果想将属性和方法保存到原型中, 只能动态给原型对象添加属性和方法
- ES6之前,构造函数
function Person(myName, myAge) {
// 实例属性
this.name = myName;
this.age = myAge;
// 实例方法
this.hi = function () {
console.log("hi");
}
}
// 原型上的方法
// Person.prototype.type = "人";
// Person.prototype.say = function () {
// console.log(this.name, this.age);
// };
Person.prototype = {
constructor: Person,
type: "人",
say: function () {
console.log(this.name, this.age);
}
};
- ES6
class Person{
constructor(myName, myAge){
this.name = myName;
this.age = myAge;
this.hi = function () {
console.log("hi");
}
}
run(){
console.log("run");
}
}
// 方法一:
// Person.prototype.type = "人";
// Person.prototype.say = function () {
// console.log(this.name, this.age);
// };
// 方法二:
let obj = {
constructor: Person,
type: "人",
say: function () {
console.log(this.name, this.age);
}
};
Person.prototype = obj;
let p = new Person("lnj", 34);
console.log(p);
2.ES6继承
1.ES6之前的继承
- 1.在子类中通过call/apply方法
借助父类的构造函数
- 2.将子类的原型对象设置为
父类的实例对象
2.在ES6中如何继承
- 1在子类后面添加
extends
并指定父类的名称 - 2在子类的constructor构造函数中通过
super
方法借助父类的构造函数
class Person{
constructor(myName, myAge){
// this = stu;
this.name = myName; // stu.name = myName;
this.age = myAge; // stu.age = myAge;
}
say(){
console.log(this.name, this.age);
}
}
/*
1.在ES6中如何继承
1.1在子类后面添加extends并指定父类的名称
1.2在子类的constructor构造函数中通过super方法借助父类的构造函数
*/
// 以下代码的含义: 告诉浏览器将来Student这个类需要继承于Person这个类
class Student extends Person{
constructor(myName, myAge, myScore){
// 1.在子类中通过call/apply方法借助父类的构造函数
// Person.call(this, myName, myAge);
super(myName, myAge);
this.score = myScore;
}
study(){
console.log("day day up");
}
}
let stu = new Student("zs", 18, 98);
stu.say();
学习笔记❥(^_-),版权归Jonathan