18.1 基础
new的作用是通过构造函数来创建一个实例对象,该实例与原型和构造函数之间的关系如下图所示:
18.2 new过程中发生了什么
当一个构造函数new的过程到底发生了什么?简要概述主要分为以下几个步骤:
- 一个新对象被创建;
- 该对象的__ proto __属性指向该构造函数的原型,即Fn.prototype;
- 将执行上下文(this)绑定到新创建的对象中;
- 如果构造函数有返回值(对象或函数),那么这个返回值将取代第一步中新创建的对象。
new真的做了这几步吗?秉承着“实践是检验真理的唯一标准”的原则,下面将这几个关键点进行逐一验证。
- function Fun() {
- this.a = 10;
- this.b = 20;
- this.method1 = () => {
- return this.a + this.b;
- }
- this.method2 = () => {
- return this;
- }
- }
- Fun.prototype = {
- method2: () => {
- console.log('原型上的method1被访问');
- }
- }
18.2.1 验证点1——新对象被创建
验证点1是新对象被创建,其实这个里面有两层含义:
new之后返回的内容是一个对象
- const fun = new Fun();
- console.log(fun); // { a: 10, b: 20, method1: [Function] }
- console.log(typeof(fun)); // object
通过打印其内容,并通过typeof进行验证,其返回内容确实是一个对象。
每次返回的都是一个新创建的对象
- const fun1 = new Fun();
- const fun2 = new Fun();
- console.log(fun1 === fun2); // false
通过创建两个实例,通过判断两个实例不相等,则证明确实每次返回的是一个新的对象。
18.2.2 验证点2——该对象可访问原型上的属性和方法
验证点2是新创建的实例可访问原型上的属性和方法,验证该关键点只需要访问原型上的方法即可实现,若原型上的方法能够被正常访问,则表示该验证点通过,负责不通过。
- const fun3 = new Fun();
- fun3.method3(); // 原型上的method3被访问
通过验证,原型上的方法确实能够被访问。
18.2.3 验证点3——this指向
验证this指向只需要将this指向打印出来即可。
- const fun4 = new Fun();
- console.log(fun4.method2()); // { a: 10, b: 20, method1: [Function], method2: [Function] }
- console.log(fun4.method2() === fun4); // true
18.2.4 验证点4——构造函数有返回值的处理逻辑
一个函数的返回值可以有多种,例如:string、boolean、number、Object、function等,下面我们验证一些内容,看构造函数有不同的返回值,其实例为何值。
返回值为string
- function Fun() {
- this.a = 10;
- this.b = 20;
- return 'test';
- }
- Fun.prototype = {
- method: () => {
- console.log('原型上的method被访问');
- }
- }
- const fun = new Fun();
- console.log(fun); // { a: 10, b: 20 }
观察其最终结果,字符串没有没正常返回,返回值是一个新的实例。
返回值为Object
- function Fun() {
- this.a = 10;
- this.b = 20;
- return {
- c: 30
- };
- }
- Fun.prototype = {
- method: () => {
- console.log('原型上的method被访问');
- }
- }
- const fun = new Fun();
- console.log(fun); // { c: 30 }
观察其结果,返回值是函数中返回的对象,则表征当构造函数返回值为对象时,会返回其对象,不返回实例化后的内容。
返回值为function
- function Fun() {
- this.a = 10;
- this.b = 20;
- return function() {
- this.d = 40;
- };
- }
- Fun.prototype = {
- method: () => {
- console.log('原型上的method被访问');
- }
- }
- const fun = new Fun();
- console.log(fun); // [Function]
返回函数的效果和返回对象的效果一致。
通过不断尝试总结,可以得出以下结论:
构造函数的返回值为基本类型,其返回值是实例化后的对象,不受返回值的影响;
构造函数的返回值是引用类型,其返回值即为new之后的返回值。
18.3 实现一个new
介绍了这么多,已经理解了new时发生的事情并经过了验证,下面就手动实现一个自己的new函数。
- function myNew(Fn, ...args) {
- // 一个新的对象被创建
- const result = {};
- // 该对象的__proto__属性指向该构造函数的原型
- if (Fn.prototype !== null) {
- Object.setPrototypeOf(result, Fn.prototype);
- }
- // 将执行上下文(this)绑定到新创建的对象中
- const returnResult = Fn.apply(result, args);
- // 如果构造函数有返回值(对象或函数),那么这个返回值将取代第一步中新创建的对象。
- if ((typeof returnResult === 'object' || typeof returnResult === 'function') && returnResult !== null) {
- return returnResult;
- }
- return result;
- }
小试牛刀
- function Fun() {
- this.a = 10;
- this.b = 20;
- }
- Fun.prototype = {
- method: () => {
- console.log('原型上的method被访问');
- }
- }
- const fun1 = new Fun();
- console.log(fun1); // { a: 10, b: 20 }
- const fun2 = myNew(Fun);
- console.log(fun2); // { a: 10, b: 20 }