「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」。
面向对象的概念
1- 对象: 万事万物都是对象,每个对象都有自己的属性(共有属性,私有属性)
2- 类: 抽象对象的特性和功能,形成描述一类事物的抽象概念; 在js中分为内内置类和自定义类;
3- 实例: 类中的一个具体的个体.只要是类中的个体就会拥有这个类型的全部属性和特性
- js中的面向对象的范畴:封装,累的继承,多态(重写,重载)
- 类: js 中的类都是一个函数的数据类型,都天生自带一个 prototype(原型) 属性,它的值是一个对象
- prototype: prototype 对象都天生自带一个 constructor, 这个属性的值指向的是当前构造函数本身;
- 对象: (实例对象,prototype) 都有一个 __proto__的属性,这属性指向当前实例所属类的 prototype.
内置类: Array, String, Number, Function, Data 等等
- 内置类的原型: 每个类都有自己的原型,用来存储这个类型的属性以及方法
console.log(Array.prototype); // [constructor: ƒ, concat: ƒ, copyWithin: ƒ, fill: ƒ, find: ƒ, …]
console.log(String.prototype); // String {'', constructor: ƒ, anchor: ƒ, big: ƒ, blink: ƒ, …}
console.log(Function.prototype); // ƒ () { [native code] }
console.log(Number.prototype); // Number {0, constructor: ƒ, toExponential: ƒ, toFixed: ƒ, toPrecision: ƒ, …}
console.log(Date.prototype); // {constructor: ƒ, toString: ƒ, toDateString: ƒ, toTimeString: ƒ, toISOString: ƒ, …}
这里面都储存 属于这个类的方法以及属性
- 类的实例:
let ary = new Array(1,2,3);
ary.push(4);
console.log(ary); // [1, 2, 3, 4]
上面说到了对于类的实例是可以拥有这个类的所有属性和方法 push就是数组这个类的方法之一
思考? 实例 ary 是如何查找到自己的类并使用其类型上的方法
如何自定义类型
- 定义一个构造函数就是创建一个类
// 定义构造函数
function People(name, age, occupation) {
// 在构造函数中给实例添加私有属性
// 在构造函数中 this 的指向是当前实例
this.name = name;
this.age = age;
this.occupation = occupation;
};
// 向这个类的原型上添加共有属性以及方法
People.prototype.gender = "男";
People.prototype.work = function () {
console.log(`${this.name}是一名${this.occupation},年龄${this.age},性别${this.gender}`)
}
// 创建这个类的实例
let my = new People("大飞", "18", "前端");
let aa = new People("张三", "29", "法外狂徒")
console.log(my.name); // 大飞
console.log(my.age); // 18
my.work(); // 大飞是一名前端,年龄18, 性别男
aa.work(); // 张三是一名法外狂徒,年龄29, 性别男
如何检测私有属性
- hasOwnProperty() 检测一个属性是否是一个对象的私有属性;
// 定义构造函数
function People(name, age, occupation) {
// 在构造函数中给实例添加私有属性
// 在构造函数中 this 的指向是当前实例
this.name = name;
this.age = age;
this.occupation = occupation;
};
// 向这个类的原型上添加共有属性以及方法
People.prototype.gender = "男";
People.prototype.work = function () {
console.log(`${this.name}是一名${this.occupation},年龄${this.age},性别${this.gender}`)
}
// 创建这个类的实例
let my = new People("大飞", "18", "前端");
let aa = new People("张三", "29", "法外狂徒")
console.log(my.name); // 大飞
console.log(my.age); // 18
my.work(); // 大飞是一名前端,年龄18, 性别男
aa.work(); // 张三是一名法外狂徒,年龄29, 性别男
console.log(my.hasOwnProperty("name")); // true
console.log(my.hasOwnProperty("gender")); // false
原型链: 属性点饿查找机制
实例 ary 是如何查找到自己的类并使用其类型上的方法?
- 每个对象(实例对象,普通对象,函数对象), 自身都有一个__propo__ 的属性,这个属性指向所有属性的原型
- 当我们对象.属性名的时候,浏览器会在自己的私有属性中寻找这个属性,
- 如果有就使用,如果没有就会根据自身的__propo__属性去找到对象所属类的原型(prototype),
- 对象所属类的原型上有就使用.如果还没有就通过该所属类的原型对象(prototype)的__proto__继续向上查找;
- 直到找到基类Object的原型对象(prototype)有的话就使用,如果还没有就返回 undefined;
重写: 子类改写父类上的属性或者方法
重载: 根据不同的函数签名(函数的参数) 自动调用不同的方法; js 中是没有真正意义上的重载的
真正意义上的重载 案例 java 语言 网上抄的
function sum(int a, int b) {
return a + b;
}
function sum(char a, char b) {
return Number(a) + Number(b)
}
sum(1, 2)
sum('2', '3')
- 但是因为 js 中同名变量会相互覆盖,同名函数只会代表最后一个函数,所以不会有正式意义上的重载;
- 但是可以模拟: 因为重载要的效果就是根据函数的形参变量的不同做出相应的处理
function add(a, b) {
if (typeof a === "number" && typeof b === "number") {
console.log(a + b);
} else {
console.log(Number(a) + Number(b));
}
}
add("1","2"); // 3
add(1,2); // 3
深入原型,原型链
- 对象的 proto 属性
// 定义构造函数
function People(name, age, occupation) {
// 在构造函数中给实例添加私有属性
// 在构造函数中 this 的指向是当前实例
this.name = name;
this.age = age;
this.occupation = occupation;
};
// 向这个类的原型上添加共有属性以及方法
People.prototype.gender = "男";
People.prototype.work = function () {
console.log(`${this.name}是一名${this.occupation},年龄${this.age},性别${this.gender}`)
}
// 创建这个类的实例
let my = new People("大飞", "18", "前端");
my.work(); // 大飞是一名前端,年龄18, 性别男
my.__proto__.name = "张三";
my.__proto__.age = 28
my.__proto__.occupation = "打铁的";
my.__proto__.gender = "女"; // 原型上的属性
my.work(); // 大飞是一名前端,年龄18,性别女 只有性别改了?
// __proto__ 只可以找到所属类原型上的属性而不能找到实例上的私有属性
// 所以我们要明确修改实例上的私有属性只能通过实例自己去修改
my.name = "张三";
my.age = 28;
my.occupation = "打铁的";
my.work(); // 张三是一名打铁的,年龄28,性别女
// 修改原型上的方法
my.__proto__.work = () => {
console.log("哪有女的打铁?像话吗?")
}
my.work(); // 哪有女的打铁?像话吗?
- 修改原型对象的指向
// 定义构造函数
function People(name, age, occupation) {
// 在构造函数中给实例添加私有属性
// 在构造函数中 this 的指向是当前实例
this.name = name;
this.age = age;
this.occupation = occupation;
};
// 向这个类的原型上添加共有属性以及方法
People.prototype.gender = "男";
People.prototype.work = function () {
console.log(`${this.name}是一名${this.occupation},年龄${this.age},性别${this.gender}`)
}
// 创建这个类的实例
let my = new People("大飞", "18", "前端");
console.log(my.constructor); // ƒ People(name, age, occupation) {} 此时还是People的实例
my.work(); // 大飞是一名前端,年龄18, 性别男
People.prototype.print = function () {
console.log(this.life.text, this.life.money);
};
// 批量添加属性
People.prototype.life = {
text: "好累啊! 不想写了",
money: "不你想写!"
};
console.log(People.prototype.constructor) // 当前实例 People(name, age, occupation) {// 在构造函数中给实例添加私有属性 // 在构造函数中 this 的指向是当前实例this.name = name;this.age = age;this.occupation = occupation;}
// 修改了原本的constructor指向
People.prototype.constructor = Array;
console.log(People.prototype.constructor); // ƒ Array() { [native code] }
console.log(People.prototype); // ƒ Array() { [native code] }
my.print();
// my 的实例指向了数组原型 所有就可以使用push 方法
console.log(my.constructor.prototype.push(1)); // 1
console.log(my.constructor); // ƒ Array() { [native code] } 由于上面的更改constructor的修改变成了数组