前端百题斩——从验证点到手撕New操作符

开发 前端
new真的做了这几步吗?秉承着“实践是检验真理的唯一标准”的原则,下面将这几个关键点进行逐一验证。

[[405765]]

18.1 基础

new的作用是通过构造函数来创建一个实例对象,该实例与原型和构造函数之间的关系如下图所示:

18.2 new过程中发生了什么

当一个构造函数new的过程到底发生了什么?简要概述主要分为以下几个步骤:

  • 一个新对象被创建;
  • 该对象的__ proto __属性指向该构造函数的原型,即Fn.prototype;
  • 将执行上下文(this)绑定到新创建的对象中;
  • 如果构造函数有返回值(对象或函数),那么这个返回值将取代第一步中新创建的对象。

new真的做了这几步吗?秉承着“实践是检验真理的唯一标准”的原则,下面将这几个关键点进行逐一验证。

  1. function Fun() { 
  2.     this.a = 10; 
  3.     this.b = 20; 
  4.     this.method1 = () => { 
  5.         return this.a + this.b; 
  6.     } 
  7.     this.method2 = () => { 
  8.         return this; 
  9.     } 
  10.  
  11. Fun.prototype = { 
  12.     method2: () => { 
  13.         console.log('原型上的method1被访问'); 
  14.     } 

18.2.1 验证点1——新对象被创建

验证点1是新对象被创建,其实这个里面有两层含义:

new之后返回的内容是一个对象

  1. const fun = new Fun(); 
  2. console.log(fun); //  { a: 10, b: 20, method1: [Function] } 
  3. console.log(typeof(fun)); // object 

通过打印其内容,并通过typeof进行验证,其返回内容确实是一个对象。

每次返回的都是一个新创建的对象

  1. const fun1 = new Fun(); 
  2. const fun2 = new Fun(); 
  3. console.log(fun1 === fun2); // false 

通过创建两个实例,通过判断两个实例不相等,则证明确实每次返回的是一个新的对象。

18.2.2 验证点2——该对象可访问原型上的属性和方法

验证点2是新创建的实例可访问原型上的属性和方法,验证该关键点只需要访问原型上的方法即可实现,若原型上的方法能够被正常访问,则表示该验证点通过,负责不通过。

  1. const fun3 = new Fun(); 
  2. fun3.method3(); // 原型上的method3被访问 

通过验证,原型上的方法确实能够被访问。

18.2.3 验证点3——this指向

验证this指向只需要将this指向打印出来即可。

  1. const fun4 = new Fun(); 
  2. console.log(fun4.method2()); // { a: 10, b: 20, method1: [Function], method2: [Function] } 
  3. console.log(fun4.method2() === fun4); // true 

18.2.4 验证点4——构造函数有返回值的处理逻辑

一个函数的返回值可以有多种,例如:string、boolean、number、Object、function等,下面我们验证一些内容,看构造函数有不同的返回值,其实例为何值。

返回值为string

  1. function Fun() { 
  2.     this.a = 10; 
  3.     this.b = 20; 
  4.     return 'test'
  5. Fun.prototype = { 
  6.     method: () => { 
  7.         console.log('原型上的method被访问'); 
  8.     } 
  9.  
  10. const fun = new Fun(); 
  11. console.log(fun); // { a: 10, b: 20 } 

观察其最终结果,字符串没有没正常返回,返回值是一个新的实例。

返回值为Object

  1. function Fun() { 
  2.     this.a = 10; 
  3.     this.b = 20; 
  4.     return { 
  5.         c: 30 
  6.     }; 
  7. Fun.prototype = { 
  8.     method: () => { 
  9.         console.log('原型上的method被访问'); 
  10.     } 
  11.  
  12. const fun = new Fun(); 
  13. console.log(fun); // { c: 30 } 

观察其结果,返回值是函数中返回的对象,则表征当构造函数返回值为对象时,会返回其对象,不返回实例化后的内容。

返回值为function

  1. function Fun() { 
  2.     this.a = 10; 
  3.     this.b = 20; 
  4.     return function() { 
  5.         this.d = 40; 
  6.     }; 
  7. Fun.prototype = { 
  8.     method: () => { 
  9.         console.log('原型上的method被访问'); 
  10.     } 
  11.  
  12. const fun = new Fun(); 
  13. console.log(fun); // [Function

返回函数的效果和返回对象的效果一致。

通过不断尝试总结,可以得出以下结论:

构造函数的返回值为基本类型,其返回值是实例化后的对象,不受返回值的影响;

构造函数的返回值是引用类型,其返回值即为new之后的返回值。

18.3 实现一个new

介绍了这么多,已经理解了new时发生的事情并经过了验证,下面就手动实现一个自己的new函数。

  1. function myNew(Fn, ...args) { 
  2.     // 一个新的对象被创建 
  3.     const result = {}; 
  4.     // 该对象的__proto__属性指向该构造函数的原型 
  5.     if (Fn.prototype !== null) { 
  6.         Object.setPrototypeOf(result, Fn.prototype); 
  7.     } 
  8.  
  9.     // 将执行上下文(this)绑定到新创建的对象中 
  10.     const returnResult = Fn.apply(result, args); 
  11.     // 如果构造函数有返回值(对象或函数),那么这个返回值将取代第一步中新创建的对象。 
  12.     if ((typeof returnResult === 'object' || typeof returnResult === 'function') && returnResult !== null) { 
  13.         return returnResult; 
  14.     } 
  15.     return result; 

小试牛刀

  1. function Fun() { 
  2.     this.a = 10; 
  3.     this.b = 20; 
  4. Fun.prototype = { 
  5.     method: () => { 
  6.         console.log('原型上的method被访问'); 
  7.     } 
  8. const fun1 = new Fun(); 
  9. console.log(fun1); // { a: 10, b: 20 } 
  10. const fun2 = myNew(Fun); 
  11. console.log(fun2); // { a: 10, b: 20 } 

 

责任编辑:武晓燕 来源: 执鸢者
相关推荐

2021-12-03 06:59:23

操作符验证点属性

2021-06-09 07:01:30

前端CallApply

2022-10-08 07:49:55

New操作符函数

2021-10-19 22:23:05

typeof方式Instanceof

2021-05-09 22:00:59

TypeofInstanceof运算符

2021-07-26 06:57:58

重绘回流前端

2021-05-16 19:23:11

引用类型包装

2021-05-30 19:02:59

变量对象上下文

2021-07-14 07:00:53

浏览器技巧前端

2021-10-18 09:01:01

前端赋值浅拷贝

2021-08-04 06:56:49

HTTP缓存前端

2021-05-12 07:04:55

Js变量方式

2021-11-19 09:01:09

防抖节流前端

2021-06-04 07:04:29

闭包JavaScript函数

2009-08-19 17:26:28

C# 操作符

2010-07-14 14:55:07

Perl操作符

2021-06-28 07:12:28

赋值浅拷贝深拷贝

2021-07-26 05:01:55

浏览器渲染流程

2021-10-31 18:59:55

Python操作符用法

2021-05-19 07:02:42

JS对象方法
点赞
收藏

51CTO技术栈公众号