前言: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构造函数

  1. 对象原型( proto)和构造函数(prototype)原型对象里面都有一个属性 constructor 属性 ,constructor 我们称为构造函数,因为它指回构造函数本身
  2. constructor 主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。
  3. 一般情况下,对象的方法都在构造函数的原型对象中设置。如果有多个对象的方法,我们可以给原型对象采取对象形式赋值,但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象 constructor 就不再指向当前构造函数了。此时,我们可以在修改后的原型对象中,添加一个 constructor 指向原来的构造函数。

    instanceof

    a instanceof b
    判断是true还是false
    看a的constructor是什么
    当b为 a的constructor或a的proto的constructor……
    结果都是true

    原型链和成员查找机制

    image.png
    image.png

    原型体系中的继承

    call方法可以改变一个函数的指向

  4. 继承父构造函数里面的属性

    1. // 1. 父构造函数
    2. function Father(uname, age) {
    3. // this 指向父构造函数的对象实例
    4. this.uname = uname;
    5. this.age = age;
    6. }
    7. // 2 .子构造函数
    8. function Son(uname, age, score) {
    9. // this 指向子构造函数的对象实例
    10. // 3.使用call方式实现子继承父的属性
    11. Father.call(this, uname, age);
    12. this.score = score;
    13. }
    14. var son = new Son('刘德华', 18, 100);
    15. console.log(son);
  5. 继承方法

    1. // 1. 父构造函数
    2. function Father(uname, age) {
    3. // this 指向父构造函数的对象实例
    4. this.uname = uname;
    5. this.age = age;
    6. }
    7. Father.prototype.money = function() {
    8. console.log(100000);
    9. };
    10. // 2 .子构造函数
    11. function Son(uname, age, score) {
    12. // this 指向子构造函数的对象实例
    13. Father.call(this, uname, age);
    14. this.score = score;
    15. }
    16. // Son.prototype = Father.prototype; 这样直接赋值会有问题,如果修改了子原型对象,父原型对象也会跟着一起变化
    17. Son.prototype = new Father();
    18. // 如果利用对象的形式修改了原型对象,别忘了利用constructor 指回原来的构造函数
    19. Son.prototype.constructor = Son;
    20. // 这个是子构造函数专门的方法
    21. Son.prototype.exam = function() {
    22. console.log('孩子要考试');
    23. }
    24. var son = new Son('刘德华', 18, 100);
    25. console.log(son);

    体系二:类(class)【ES6】

    在 ES6 中新增加了类的概念,可以使用 class 关键字声明一个类,之后以这个类来实例化对象。类抽象了对象的公共部分,它泛指某一大类(class)对象特指某一个,通过类实例化一个具体的对象。其创建的方式和之前学过的语言很类似。
    在 ES6 中类没有变量提升,所以必须先定义类,才能通过类实例化对象

    创建+继承+使用

    ```javascript // 奥特曼类 class Aoteman { // 构造函数里面放共有属性、方法 constructor(name, age) {

    1. this.name = name;
    2. this.age = age;

    } Ability(abi) {

    1. 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();

  1. ![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)
  2. <a name="Pieyx"></a>
  3. ## 对象的各种创建方式
  4. <a name="zit4E"></a>
  5. ### 字面量
  6. 属性和方法的调用:<br />属性可以用"."和["属性名"]来调用<br />方法用"."来调用
  7. <a name="qieAE"></a>
  8. ### 模式工厂
  9. 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)
  10. <a name="6Kx17"></a>
  11. ### 构造函数
  12. > 构造函数:是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与 new 运算符一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。
  13. ```javascript
  14. function 构造函数名(形参1,形参2,形参3) {
  15. this.属性名1 = 参数1;
  16. this.属性名2 = 参数2;
  17. this.属性名3 = 参数3;
  18. this.方法名 = 函数体;
  19. }
  20. let obj = new 构造函数名(实参1,实参2,实参3)
  • 构造函数约定首字母大写
  • 函数内的属性和方法前面需要添加 this ,表示当前对象的属性和方法。
  • 构造函数中不需要 return 返回结果
  • 当我们创建对象的时候,必须用 new 来调用构造函数

    原型模式

    image.png
    下面详述什么是原型

    对象的内置方法

    参考教程:

  • https://zh.javascript.info/object-basics

    Object.defineProperty

    Object.defineProperty设置或修改对象中的属性

    1. Object.defineProperty(对象,修改或新增的属性名,{
    2. value:修改或新增的属性的值,
    3. writable:true/false,//如果值为false 不允许修改这个属性值
    4. enumerable: false,//enumerable 如果值为false 则不允许遍历
    5. configurable: false //configurable 如果为false 则不允许删除这个属性 属性是否可以被删除或是否可以再次修改特性
    6. })

    删除对象属性

  • 直接用delete,暴力简单

  • 也可以参考:如何优雅的删除对象中的指定属性?

    assign()

  • Object.assign后者覆盖前者,再返回前者

  • 常用于深拷贝浅拷贝数据

    对象的遍历

    for…in

    :::info 语句用于对数组或者对象的属性进行循环操作。 ::: 其语法如下:

    1. for (变量 in 对象名字) {
    2. // 在此执行代码
    3. }
  • 语法中的变量是自定义的,它需要符合命名规范,通常我们会将这个变量写为 k 或者 key。书里面建议用const来加持。

    1. for (let k in obj) {
    2. console.log(k); // 这里的 k 是属性名
    3. console.log(obj[k]); // 这里的 obj[k] 是属性值
    4. }

    Object.keys(obj)

    :::info 获取属性名 :::

    1. var obj = {
    2. id: 1,
    3. pname: '小米',
    4. price: 1999,
    5. num: 2000
    6. };
    7. var result = Object.keys(obj)
    8. console.log(result)//[id,pname,price,num]