前言:JavaScript首先在ES6没有出来之前,利用一个叫原型的一系列机制来用一段很长的代码来实现类的继承,说白了就是在函数里面默认给你加个一个叫原型的对象属性,再利用一系列指向来完成继承。在ES6之后,才有了形式上的类class及其对象,以及一个单词extends就搞定的继承,虽说搞定,但这里面的机制还是原型相关知识,记录学习一下。
不管是原型也好,新加的class也罢,就一个目的——为了实现面向对象。从两个体系来展开详述。
体系一:原型+原型链
- 讲解的很透彻:https://www.bilibili.com/video/BV1Q64y1v7fW
隐式原型proto
每个对象里面都有proto属性,这个叫对象原型,这个玩意指向构造函数的prototype对象proto对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象 prototype
比如说,有一个Person类,有一个Student类,Student类继承自Person类,Student有一个实例student1,那么student1会有proto对象属性,并且展开该__proto,会发现里面是Person类的方法和属性
显式原型prototype
每个构造函数里面都有一个属性,这个属性叫prototype,指向另一个对象(有什么用?在后面原型链就会发现有用了),并且这个属性是一个对象,叫做构造函数原型
这样可以解决一个问题,就是创建不同实例,这些事例所用的方法都是同一个内存下的方法,实现共享
constructor构造函数
- 对象原型( proto)和构造函数(prototype)原型对象里面都有一个属性 constructor 属性 ,constructor 我们称为构造函数,因为它指回构造函数本身。
- constructor 主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。
一般情况下,对象的方法都在构造函数的原型对象中设置。如果有多个对象的方法,我们可以给原型对象采取对象形式赋值,但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象 constructor 就不再指向当前构造函数了。此时,我们可以在修改后的原型对象中,添加一个 constructor 指向原来的构造函数。
instanceof
a instanceof b
判断是true还是false
看a的constructor是什么
当b为 a的constructor或a的proto的constructor……
结果都是true原型链和成员查找机制
原型体系中的继承
call方法可以改变一个函数的指向
继承父构造函数里面的属性
// 1. 父构造函数
function Father(uname, age) {
// this 指向父构造函数的对象实例
this.uname = uname;
this.age = age;
}
// 2 .子构造函数
function Son(uname, age, score) {
// this 指向子构造函数的对象实例
// 3.使用call方式实现子继承父的属性
Father.call(this, uname, age);
this.score = score;
}
var son = new Son('刘德华', 18, 100);
console.log(son);
继承方法
// 1. 父构造函数
function Father(uname, age) {
// this 指向父构造函数的对象实例
this.uname = uname;
this.age = age;
}
Father.prototype.money = function() {
console.log(100000);
};
// 2 .子构造函数
function Son(uname, age, score) {
// this 指向子构造函数的对象实例
Father.call(this, uname, age);
this.score = score;
}
// Son.prototype = Father.prototype; 这样直接赋值会有问题,如果修改了子原型对象,父原型对象也会跟着一起变化
Son.prototype = new Father();
// 如果利用对象的形式修改了原型对象,别忘了利用constructor 指回原来的构造函数
Son.prototype.constructor = Son;
// 这个是子构造函数专门的方法
Son.prototype.exam = function() {
console.log('孩子要考试');
}
var son = new Son('刘德华', 18, 100);
console.log(son);
体系二:类(class)【ES6】
在 ES6 中新增加了类的概念,可以使用 class 关键字声明一个类,之后以这个类来实例化对象。类抽象了对象的公共部分,它泛指某一大类(class)对象特指某一个,通过类实例化一个具体的对象。其创建的方式和之前学过的语言很类似。
在 ES6 中类没有变量提升,所以必须先定义类,才能通过类实例化对象创建+继承+使用
```javascript // 奥特曼类 class Aoteman { // 构造函数里面放共有属性、方法 constructor(name, age) {
this.name = name;
this.age = age;
} Ability(abi) {
console.log(this.name + " can " + abi);
} }
// 假奥特曼类 继承 奥特曼 class Jia extends Aoteman { constructor (x, y) { // 继承父类的构造函数 super(x,y); // 方便写自己的函数 this.x = x; this.y = y; } // 自己内部的函数 Chuiniu() { console.log(this.x + “ chuiniu”); }
}
// 创建迪迦对象 let dijia = new Aoteman(‘dijia’, 100); console.log(dijia); dijia.Ability(“X-ray”); let dijia_jia = new Jia(“dijia_jia”, 50); dijia_jia.Ability(“xxx”); dijia_jia.Chuiniu();
![image.png](https://cdn.nlark.com/yuque/0/2021/png/1484158/1622622728600-59f47873-c895-4686-b771-f1f538198f26.png#crop=0&crop=0&crop=1&crop=1&height=77&id=JN882&margin=%5Bobject%20Object%5D&name=image.png&originHeight=153&originWidth=882&originalType=binary&ratio=1&rotation=0&showTitle=false&size=14744&status=done&style=none&title=&width=441)
<a name="Pieyx"></a>
## 对象的各种创建方式
<a name="zit4E"></a>
### 字面量
属性和方法的调用:<br />属性可以用"."和["属性名"]来调用<br />方法用"."来调用
<a name="qieAE"></a>
### 模式工厂
new一个空对象,`let obj = new Object();`<br />然后利用"."的方式给其添加属性和方法<br />内置的Object()构造函数<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/1484158/1622623002064-1297005b-76a6-4d94-9b12-a83cf709e3a1.png#crop=0&crop=0&crop=1&crop=1&height=215&id=hDM2I&margin=%5Bobject%20Object%5D&name=image.png&originHeight=429&originWidth=1059&originalType=binary&ratio=1&rotation=0&showTitle=false&size=99843&status=done&style=shadow&title=&width=529.5)
<a name="6Kx17"></a>
### 构造函数
> 构造函数:是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与 new 运算符一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。
```javascript
function 构造函数名(形参1,形参2,形参3) {
this.属性名1 = 参数1;
this.属性名2 = 参数2;
this.属性名3 = 参数3;
this.方法名 = 函数体;
}
let obj = new 构造函数名(实参1,实参2,实参3)
- 构造函数约定首字母大写。
- 函数内的属性和方法前面需要添加 this ,表示当前对象的属性和方法。
- 构造函数中不需要 return 返回结果。
-
原型模式
对象的内置方法
参考教程:
https://zh.javascript.info/object-basics
Object.defineProperty
Object.defineProperty设置或修改对象中的属性
Object.defineProperty(对象,修改或新增的属性名,{
value:修改或新增的属性的值,
writable:true/false,//如果值为false 不允许修改这个属性值
enumerable: false,//enumerable 如果值为false 则不允许遍历
configurable: false //configurable 如果为false 则不允许删除这个属性 属性是否可以被删除或是否可以再次修改特性
})
删除对象属性
直接用delete,暴力简单
也可以参考:如何优雅的删除对象中的指定属性?
assign()
Object.assign后者覆盖前者,再返回前者
-
对象的遍历
for…in
:::info 语句用于对数组或者对象的属性进行循环操作。 ::: 其语法如下:
for (变量 in 对象名字) {
// 在此执行代码
}
语法中的变量是自定义的,它需要符合命名规范,通常我们会将这个变量写为 k 或者 key。书里面建议用const来加持。
for (let k in obj) {
console.log(k); // 这里的 k 是属性名
console.log(obj[k]); // 这里的 obj[k] 是属性值
}
Object.keys(obj)
:::info 获取属性名 :::
var obj = {
id: 1,
pname: '小米',
price: 1999,
num: 2000
};
var result = Object.keys(obj)
console.log(result)//[id,pname,price,num]