了解一下javaScript构造函数

简介: 了解一下javaScript构造函数

JavaScript构造函数是指通过new关键字调用的函数且不能有返回值,他和普通函数有什么区别呢

  1. 构造函数可以通过new和实例进行调用,普通函数就只能通过函数名和实例进行调用
  2. 构造函数不能有返回值,普通函数可以有返回值
  3. 构造函数里面可以使用this定义变量,指向的当前使用new关键字调用出来的实例对象,普通函数的this是指向的调用者
  4. 构造函数是用于初始化对象的,而普通函数不能用于初始化

new 运算符


构造函数必须使用new关键字,那么new关键字都替我们做了那些事情

  1. 执行函数;
  2. 自动创建一个空对象;
  3. 把创建的对象指向另外一个对象;
  4. 把空对象和函数里的this 衔接起来;(this指向实例化对象)
  5. 隐式返还this;

手写new 运算符


// 构造函数
        function Fn() {
            this.name = '张三';
            this.age = function() {
                console.log('11111');
            }
        };
        Fn.prototype.hello = function() {
                console.log('hello');
            }
            //new 运算符:
            /* new实例化之后创建一个空对象,mynew的时候由于需要知道mynew的哪一个构造函数所以传递 */
            /**
             * @description: 仿写new
             * @param {*} constructor new的哪一个构造函数
             * @param {*} ...data 里面还会去传递参数但是具体传递的那些不知道,所以采用剩余参数
             * @return {object} obj
             */
        function mynew(constructor, ...data) {
            // 1.先创建一个空对象
            let obj = {};
            // 2.改变当前的this指向,把this指向指向到空对象
            constructor.call(obj, ...data);
            // 3.把当前的constructor原型赋值给obj原型
            obj.__proto__ = constructor.prototype;
            // 4.返回
            return obj;
        };
        // 使用
        let tab = mynew(Fn);
        tab.age();
        tab.hello();
    </script>

构造函数继承


其实在构造函数中还有一个更为重要的东西,就是构造函数继承,如果一个构造函数需要使用到另一个构造函数上的属性和方法,重新编写,少的属性还好,但是如果属性一多,修改就会增加我们的工作量,这个时候我们可以使用到继承。

构造函数的方法,写在构造函数内部,每次new都会在内存中开辟出一块内存存放该方法,我们直接把他挂载到原型上,这样只会开辟出一块内存,实现多实例对象共用一块,极大的减少了内存的消耗

构造函数属性的继承


// 父
        function Dod(name, age) {
            // 这些带this的都会变成实例化对象
            this.name = name;
            this.age = age;
            // 这么做虽然会继承方法,但是极其消耗性能,每一次调用都会创建一个内存来执行方法(在堆区存储)
            this.num = function() {
                console.log(121);
            }
        }
        Dod.prototype.hobby = function() {
                alert(`我叫${this.name},今年${this.age}`);
            }
            // 子
        function Son(name, age) {
            // ES5中三种方法继承其他的构造函数内容,但是继承不了prototype上的方法
            Dod.call(this, name, age);
            // Dod.apply(this,[name,age]);
            // Dod.bind(this)(name,age);
        }
        let obj1 = new Dod('若水', '20');
        let obj2 = new Son('biubiubiu', '99');
        // 调用构造函数方法
        obj1.hobby();
        console.log(obj2, obj1);
        // 给构造函数添加默认属性
        Dod.height = '175cm';
        // 使用
        console.log(Dod.height);

上面这种方法只能继承属性不能继承原型上的方法,下面我们来讲如何实现方法的继承

构造函数方法的继承


// 父
        function Dad(name, age) {
            this.name = name;
            this.age = age;
        }
        Dad.prototype.hobby = function () {
            alert(`我今年${this.age}`);
        };
        // 子
        function Son(name, age) {
            // 继承父级
            Dad.call(this, name, age);
        };
        Son.prototype = Dad.prototype;
        console.log(Dad.prototype == Son.prototype);
        // 创建方法
        let obj = new Dad('啾啾', '23');
        let obj1 = new Son('若水', '22');
        // 修改Son的原型方法
        Son.prototype.hobby = () => {
            alert(`啦啦啦,我修改了,你抓不到我呀!`);
        };
        // 使用 这时会发现他们俩个弹出的是一样的,因为共用了一个原型链的原因
        obj.hobby();
        obj1.hobby();

上面子的构造函数继承父级的prototype上面的方法,也就是说子的构造函数的原型等于父级的原型,但是这样会引发一个问题,就是你修改子的方法,父级方法也会修改,因为他们俩个现在已经共用了一个原型,下面就讲如何解决

// 父
        function Dad(name, age) {
            this.name = name;
            this.age = age;
        }
        Dad.prototype.hobby = function () {
            alert(`我今年${this.age}`);
        };
        // 子
        function Son(name, age) {
            // 继承父级
            Dad.call(this, name, age);
        };
        // 共用原型链会出现问题,所以这里使用了一个解决方法,也叫组合继承
        // 组合继承
        // 第一步:创建一个中转站,然后用于父和子之间的原型链中转,切断子类和父类的原型联系
        function Link() { };
        // 第二步:把当前父的构造函数原型赋值给Link原型
        Link.prototype = Dad.prototype;
        // 第三步:创建一个实例化对象赋值给当前的子构造函数原型
        /* 原理: 实例化后的对象会新开辟一个内存地址,切断实例化对象和构造函数的联系*/
        Son.prototype = new Link();
        // 第四步:prototype原型上有一个预定义属性constructor,constructor属性用于返回创建该对象的函数,也就是我们常说的构造函数,我们把子的prototype指向了创建的构造函数为Link的函数,我们找不到的属性都会去Link原型上找,现在我们的子prototype的constructor是Link,所以我们要更改过来
        Son.prototype.constructor = Son;
        // 创建方法
        let obj = new Dad('啾啾', '23');
        let obj1 = new Son('若水', '22');
        console.log(obj);
        // 利用中转的方法进行继承,在次修改无问题
        Son.prototype.hobby = () => {
            alert(`啦啦啦,我修改了,你抓不到我呀!`);
        };
        // 使用 完美
        obj.hobby();
        obj1.hobby();

上述我们利用了构造函数的特性实现了组合继承,完美解决原型链共用的问题

坚持努力,无惧未来!

相关文章
|
4月前
|
JavaScript
js开发:请解释什么是ES6的类(class),并说明它与传统构造函数的区别。
ES6的类提供了一种更简洁的面向对象编程方式,对比传统的构造函数,具有更好的可读性和可维护性。类使用`class`定义,`constructor`定义构造方法,`extends`实现继承,并可直接定义静态方法。示例展示了如何创建`Person`类、`Student`子类以及它们的方法调用。
41 2
|
4月前
|
JavaScript 前端开发
JavaScript中的正则表达式构造函数和正则表达式字面量
JavaScript中的正则表达式构造函数和正则表达式字面量
|
4月前
|
存储 JavaScript 前端开发
构造函数和原型的结合应用:轻松搞定JS的面向对象编程(三)
构造函数和原型的结合应用:轻松搞定JS的面向对象编程
|
4月前
|
设计模式 JavaScript 前端开发
构造函数和原型的结合应用:轻松搞定JS的面向对象编程(一)
构造函数和原型的结合应用:轻松搞定JS的面向对象编程
|
3月前
|
设计模式 JavaScript 前端开发
在JavaScript中,继承是一个重要的概念,它允许我们基于现有的类(或构造函数)创建新的类
【6月更文挑战第15天】JavaScript继承促进代码复用与扩展,创建类层次结构,但过深的继承链导致复杂性增加,紧密耦合增加维护成本,单继承限制灵活性,方法覆盖可能隐藏父类功能,且可能影响性能。设计时需谨慎权衡并考虑使用组合等替代方案。
41 7
|
4天前
|
JavaScript 前端开发
JavaScript基础知识-构造函数(也称为"类")定义
本文介绍了JavaScript中构造函数(也称为“类”)的定义和使用方法。
11 1
JavaScript基础知识-构造函数(也称为"类")定义
|
4月前
|
存储 JavaScript 前端开发
构造函数和原型的结合应用:轻松搞定JS的面向对象编程(二)
构造函数和原型的结合应用:轻松搞定JS的面向对象编程
|
1月前
|
存储 JavaScript 前端开发
JavaScript引用数据类型和构造函数的秘密
JavaScript引用数据类型和构造函数的秘密
|
2月前
|
设计模式 JavaScript 前端开发
js设计模式【详解】—— 构造函数模式
js设计模式【详解】—— 构造函数模式
31 6
|
4月前
|
JavaScript
js中如何使用工厂方式和构造函数创建对象,web开发项目实例
js中如何使用工厂方式和构造函数创建对象,web开发项目实例