原生JS知识点复习之面向对象概念及原型链

209 阅读5分钟

「这是我参与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的修改变成了数组