前言
当我们对函数进行实例化时,需要用new操作符来实现。那么,对于它的底层实现原理你是否清楚呢?本文就跟大家分享下它的原理并用一个函数来模拟实现它,欢迎各位感兴趣的开发者阅读本文。
原理分析
我们通过一个具体的例子来看下一个函数在new之后都能做些什么,如下所示:
function Person(name, age) { this.name = name; this.age = age; this.height = "175cm"; this.bodyWeight = "65kg"; return "some anything"; } // 每日运动量 Person.prototype.dailyExercise = "300 kcal"; Person.prototype.printBodyWeight = function() { console.log(this.name + "体重为: " + this.bodyWeight); };
接下来,我们用new关键字将Person
函数进行实例化,我们发现实例化后,可以访问到:
- 函数内部的属性
- 函数原型上的属性
const person = new Person("神奇的程序员", "22"); console.log(person.age); console.log(person.bodyWeight); console.log(person.dailyExercise); person.printBodyWeight();
image-20221005120945478
眼尖的开发者可能已经发现我们的构造函数中返回了一个字符串,它是属于基本类型,如果我们返回一个对象会发生什么?
function Person(name, age) { //... 其他代码省略 ...// return { bodyWeight: this.bodyWeight }; }
再次运行代码后,我们发现:
- 只能访问我们在构造函数中所返回的属性
- 构造函数中声明的其它属性以及挂载在原型上的属性均无法访问
image-20221005160504245
实现思路
经过前面的分析,我们知道了函数在new完之后会返回一个新的对象,这个对象上挂载了构造函数内的所有属性以及函数原型上的所有属性。
我们在实现的时候,也需要建立一个新的对象,这个对象上需要包含构造函数里的属性,因此我们可以使用apply
方法来给此对象添加新属性。
在深入理解原型链与继承文章中,我们知道实例的 __proto__
属性会指向构造函数的prototype
,建立起这样的关系后,实例才可以访问原型上的属性。
有了这些知识点作为铺垫后,我们就可以写出这个模拟函数了,如下所示:
- 创建一个对象用来存储构造函数的属性
- 从
arguments
中取出第一个参数,这个参数便是调用时的构造函数 - 将新对象的原型通过
__proto__
指向构造函数的prototype
- 通过
apply
方法改变构造函数的this指向,从而实现将构造函数内部的属性添加进新创建的对象中 - 判断构造函数是否有返回值
- 有返回值且其类型为一个对象或者一个函数,则返回构造函数的返回值
- 否则就返回我们新创建的对象
function instantiateFactory() { const obj = {}; // 取出第一个参数: 函数的构造函数 const Constructor = [].shift.call(arguments); // 将新对象的原型指向构造函数 obj.__proto__ = Constructor.prototype; // 改变构造函数的this指向至新创建的对象 const result = Constructor.apply(obj, arguments); // 如果构造函数有返回值且它的值为对象或者函数则将其返回 if (result && (typeof result == "object" || typeof result == "function")) { return result; } // 否则返回此对象 return obj; }
测试用例
我们用原理分析中的例子来验证下我们实现的这个工厂函数能否正确执行。
const factory = instantiateFactory(Person, "神奇的程序员", "22"); console.log(factory.age); console.log(factory.bodyWeight); console.log(factory.dailyExercise); factory.printBodyWeight();
假设函数没有返回值或者返回值是一个字符串类型时,执行结果如下所示:
image-20221005222833333
当函数的返回值是一个对象时,执行结果如下所示:
image-20221005222930129
代码地址
本文为《JS原理学习》系列的第7篇文章,本系列的完整路线请移步:《JS原理学习 (1) 》学习路线规划
本系列文章的所有示例代码,请移步:js-learning
写在最后
至此,文章就分享完毕了。
我是神奇的程序员,一位前端开发工程师。
如果你对我感兴趣,请移步我的个人网站,进一步了解。
- 公众号无法外链,如果文中有链接,可点击下方阅读原文查看😊