JavaScript中的原型详解

123 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第11天,点击查看活动详情

原型 prototype

        function Person(name, age) {
            this.name = name;
            this.age = age;
            this.sayHi = function() {
                console.log('hi, 我是' + this.name + ', 我' + this.age + '了');
            }
        }

        Person.prototype.sayHello = function() {
            console.log('hello, 我是' + this.name + ', 我' + this.age + '了');
        }

        console.log(Person.prototype);

        let p1 = new Person('shaosiming', 18);
        let p2 = new Person('dasiming', 19);

        console.log(p1.sayHi == p2.sayHi); // false
        console.log(p1.sayHello == p2.sayHello); // true

可以看到, 如果不使用原型那么每个对象都有自己的方法, 这样会很大的浪费内存资源 像sayHi这样的方法, 是属性每个对象的

而有了原型之后, 我们可以在原型上添加一个公用的方法, 这样就节省了内存资源

原型链的使用流程

  1. 当一个对象调用方法和访问属性时, 首先会在自己本身查找, 如果找到则调用或访问
  2. 如果在自身找不到属性和方法时, 会去自身的原型上查找, 如果找到则调用或访问
  3. 如果在自身的原型上找不到属性和方法时, 则去原型的原型上查找, 如果找到则调用或访问
  4. 按照这个规则, 在原型链上一路向上
  5. 直到Object这个根原型, 如果还没有找到, 则返回undefin

作用

  • 使用原型后, 我们可以把公有的属性和方法放到原型对象上去, 这样节省内存资源
  • 在使用原型后, 在给原型添加一个新方法或属性时, 拥有该原型的所有对象也自动获得了该属性和方法, 哪怕这些对象的创建是在添加这个方法和属性之前

this

在方法中this指向调用这个方法的对象.

而在如果在对象中没有找到要调用的方法, 而在原型中找到了要调用的方法, this还是指向调用方法的对象.

        Person.prototype.isSleeping = false;

        Person.prototype.sleep = function() {
            this.isSleeping = true;
        }

        console.log(p1.isSleeping); // false
        console.log(p2.isSleeping); // false
        console.log(p1.hasOwnProperty('isSleeping')); // false
        console.log(p2.hasOwnProperty('isSleeping')); // false

        p1.sleep();

        console.log(p1.isSleeping); // true
        console.log(p2.isSleeping); // false
        console.log(p1.hasOwnProperty('isSleeping')); // true
        console.log(p2.hasOwnProperty('isSleeping')); // false

hasOwnProperty方法: 查看对象自身有没有某个属性

注意: 这里一开始的时候p1和p2都没有isSleeping属性, isSleeping属性都是在它们的原型上

而当p1调用了sleep方法之后, 在该方法中this指向调用者, 也就是p1, 因此向p1添加了属性isSleeping并设置为true

而p2没有调用sleep方法, 因此也就没有向p2对象中添加isSleeping属性, 这个属性在原型上

原型链

在js中可以通过原型这种方式来实现常见面向对象中的继承这个特性

比如我们要让Student继承自Person, 在js中想要实现这种效果, 要按照如下步骤进行

  1. 有一个Peron构造函数, 有一个Student构造函数
  2. 设置Student的prototype为new Person()的空对象
  3. 如果想要实现的完全一点还要设置Student的prototype的constructor为Student构造函数

注意: 在Student的构造函数中, 我们可以请求Person构造函数来帮我们初始化实例, 这里要使用Person.call()这种调用方式

原型链的终点永远都是Object这个对象

uml图

代码

        function Person(name, age) {
            this.name = name;
            this.age = age;
        }

        Person.prototype.sayHi = function() {
            console.log('Person sayHi');
        }

        function Student(name, age, classNo) {
            // 要使用这种调用方式, 原指定Person构造函数中的this
            Person.call(this, name, age);
            this.classNo = classNo;
        }

        // 在在这里设置Student的原型为通过Person构造的空对象
        Student.prototype = new Person();

        // 这里要设置Student的原型的构造函数为Student, 否则它是指向的Person这个构造函数
        Student.prototype.constructor = Student;

        Student.prototype.study = function() {
            console.log(`${this.name}正在${this.classNo}班学习`);
        }

        let stu = new Student('shaosiming', 18, 100);
        console.log(stu);
        console.log('stu constructor: ', stu.constructor);
        stu.sayHi();
        stu.study();

给内置对象添加属性或者方法

有了原型, 我们可以给内置的对象添加属性或者方法, 简直不要太方便

        // 给内置对象添加方法
        let arr = ['wow', 'haha', 'xixi', 'abcdcba']
        String.prototype.palindrome = function() {
            let lastIdx = this.length - 1;
            for (let i = 0; i <= lastIdx; i++) {
                if (this[i] !== this[lastIdx - i]) {
                    return false;
                }
                return true;
            }
        }

        String.prototype.palindrome2 = function() {
            let reverseStr = this.split('').reverse().join('');
            return reverseStr === this.valueOf()
        }

        for (let i = 0; i < arr.length; i++) {
            const element = arr[i];
            if (element.palindrome2()) {
                console.log(`${arr[i]} is a palidrome`);
            } else {
                console.log(`${arr[i]} is not a palidrome`);
            }
        }