new做了哪些操作?手写一个new方法!

简介: 前言在程序员的世界里怎么可能没有对象,没有对象我们new一个不就完事儿了吗?在Java这样的面向对象的语言里面,new的操作可以说是随处可见。在我们前端程序员的世界里,可能很多小伙伴还没有感受到new的魅力,但是随着TS、ES6等等应用广泛,new的操作也逐渐被应用起来了!我们都说new一个对象,顾名思义new操作就是创建一个新对象,那么它是如何创建的?创建的对象有什么特点?创建的过程是什么?今天就来理一理!

1.代码分析


我们口说一千遍一万遍都不如一行代码来得实在!想要new的过程做了些什么,那么我们很有必要先搞清楚new的用法,都不会用new,那还何谈实现它呢?

示例代码:

<script>
  // 定义构造函数
  function Person(name, age) {
    this.name = name;
    this.age = age;
  }
  // 定义原型方法
  Person.prototype.say = function () {
    console.log("你好:", this.name)
  }
  // new一个对象
  let obj = new Person("小猪课堂", 26);
  obj.say(); // 你好:小猪课堂
  console.log(obj);
</script>


输出结果:135.png


上段代码是一个标准的使用new创建对象的过程,相信大家也非常熟悉。我们的Person就是大家常说的构造函数,其实它就是一个函数而已,只不过我们让他的作用和其它函数有一点不一样。其实我们平常声明class类也就是一个函数而已。


我们重点关注控制台的输出结果,我们有两点重大发现

  1. 我们new出来的对象可以调用Person的原型方法
  2. 我们new出来的对象里面包含Person定义的属性


从上面不难看出,new操作无非就是新创建了一个对象,有些小伙伴可能会觉得会是Person返回的对象,那么我们是不是可以直接在Person里面return一个对象呢?我们实际操作一下。


修改代码如下:

<script>
  // 定义构造函数
  function Person(name, age) {
    this.name = name;
    this.age = age;
    return {
      name: this.name,
      age: this.age
    }
  }
  // 定义原型方法
  Person.prototype.say = function () {
    console.log("你好:", this.name)
  }
  // new一个对象
  let obj = new Person("小猪课堂", 26);
  console.log(obj);
  obj.say();
</script>

输出结果:136.png

上段代码我们也没做什么修改,就在构造函数内部return了一个对象而已,可以结果却发生了巨大的变化。我们new出来的对象没有继承Person的原型方法了,new出来的对象也只是一个普通的对象了。


那么我们继续探索,如果在Person中返回一个值类型会是什么样的呢?


修改后代码如下:

<script>
  // 定义构造函数
  function Person(name, age) {
    this.name = name;
    this.age = age;
    return this.name + ':' + this.age
  }
  // 定义原型方法
  Person.prototype.say = function () {
    console.log("你好:", this.name)
  }
  // new一个对象
  let obj = new Person("小猪课堂", 26);
  console.log(obj);
  obj.say();
</script>

输出结果:

136.png

我们到这里又会发现一切都回来了!这说明我们返回非对象类型时,不会对new操作造成任何影响。


2.总结new做了啥?


看了上面的代码分析,我相信你对new的操作过程有了一定的感悟了吧!虽然你可能还有点模糊,但是不用着急,我们把new的这些特定以此总结出来,我相信你一定就不会感到模糊了!可能这就是只可意会不可言传吧!


老规矩,我们先来看看官方是如何解释new的。


官方解释:

new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。


其中官网还对new关键字所作的操作做出了如下的解释:

  1. 创建一个空的简单JavaScript对象(即{});
  2. 为步骤1新创建的对象添加属性__proto__,将该属性链接至构造函数的原型对象 ;
  3. 将步骤1新创建的对象作为this的上下文 ;
  4. 如果该函数没有返回对象,则返回this


官网的解释其实还是比较通透明了了,但是为了让小伙伴们更加容易理解,我们可以结合我们上面的代码在总结一下。


咱们的总结:

  1. 首先会创建一个新的空对象,如我们上面的obj
  2. obj对象会继承构造函数即Person的原型,即obj.__proto__ === Person.prototype,这样obj就可以调用Person上的原型方法了。
  3. Personthis指向obj,也就是将Person上的属性添加到新的obj对象上去,obj则可以调用Person中定义的属性了。
  4. 如果Person构造函数没有返回对象,则返回新创建的对象(即this),否则返回return的对象。


到这儿我们应该就大概明白了一new的操作过程主要做了哪些步骤,如果你还有点云里雾里,这里在给大家说一下另一种理解:new String()大家应该都知道是在做什么吧,它返回了一个新的string实例对象,我们的new Person()也是返回一个新的person实例对象,只不过String是内置的罢了,所以我们new操作就是创建一个用户自己定义的对象类型的实例,只有如下两步:


  1. 通过编写函数来定义对象的类型,比如new String()定义string类型。
  2. 通过new来创建对象的实例。


3.实现一个new方法


核心原理我们都已经了解了,那还有什么理由不去实现它呢?我们根据前面总结的特点一步一步去实现它。

代码框架:

<script>
  // 手动实现new方法
  function myNew(constructor) {
    if (typeof constructor !== "function") {
      throw "myNew方法的第一个参数必须是一个方法";
    }
    // 返回新对象
    return newObj;
  }
  let obj = myNew(Person, '小猪课堂', 26);
  console.log(obj);
  obj.say();
</script>


管它三七二十一,我们先把架子搭起来,我们的myNew方法最终肯定是要返回一个对象,因为new操作就是返回一个新的对象。


代码填充后(完整代码)

<script>
  // 定义构造函数
  function Person(name, age) {
    this.name = name;
    this.age = age;
    return this.name + ':' + this.age
  }
  // 定义原型方法
  Person.prototype.say = function () {
    console.log("你好:", this.name)
  }
  // 手动实现new方法
  function myNew(constructor) {
    if (typeof constructor !== "function") {
      throw "myNew方法的第一个参数必须是一个方法";
    }
    // 基于constructor的原型创建一个全新的对象
    let newObj = Object.create(constructor.prototype);
    // 获取传入的参数
    let args = Array.from(arguments).slice(1);
    // 执行constructor函数,获取结果,并将属性添加到新对象newObj上
    let result = constructor.apply(newObj, args); // 将this指向newObj
    // 判断result类型,如果是object或者function类型,则直接返回结果
    let originType = Object.prototype.toString.call(result); // 获取内部属性值
    let isObject = originType === '[object Object]';
    let isFunction = originType === '[object Function]';
    if (isObject || isFunction) {
      return result;
    } else {
      // 返回新对象
      return newObj;
    }
  }
  let obj = myNew(Person, '小猪课堂', 26);
  console.log(obj);
  obj.say();
</script>

输出结果:

137.png

可以看到上面的输出结果和我们使用new关键字输出的结果一致了。想要理解上面代码,我们需要将new操作的步骤和我们的代码一一对应,确定我们每一步都在做什么。


这里有几点难理解或者需要注意的地方:

  1. Object.create()方法:该方法会创建一个新的对象,传入的参数是我们需要绑定的原型,上段代码中的这步操作其实就是实现了new关键词的两步操作:首先创建新对象,然后赋值原型给新对象。
  2. constructor.apply()方法:该方法用来改变this指向,也就是我们将this指向了我们新创建的对象,这样Person方法中的this其实就是newObj,这样newObj就会拥有Person方法的属性了。
  3. Object.prototype.toString.call()方法:这个方法主要是用来判断Person方法执行后返回的结果是什么,我们在前面实验过,new操作的时候,如果构造函数返回的是一个对象,则直接返回return的那个对象,初次之外,function也是一种特殊的对象。所以我们这一步就是为了解决这个问题。


总结

new了这么多次对象了,我们今天总算搞明白它的原理了。从我们实现结果来看,稍微难理解的就是原型和this指向这两块问题。


所以说强烈建议小伙伴们好好学一学原型、原型链和this指向。


如果觉得文章太繁琐或者没看懂,可以观看视频: 小猪课堂

相关文章
|
8月前
|
安全 Linux 数据安全/隐私保护
掌握文件权限:理解Linux chomod
文件权限是Linux系统安全管理的核心,chmod命令作为调整文件权限的关键工具,帮助管理员灵活设置用户对文件的读取、写入和执行权限。通过数字或符号表示权限,如“755”分别赋予所有者完全访问及组用户和其他人仅读与执行权。此外,chmod支持递归修改(-R)、添加(+)、删除(-)或替换(=)权限,确保系统资源的安全访问。掌握chmod的最佳实践对于维护系统安全至关重要,例如定期审核权限和谨慎使用递归选项。
229 5
|
Java Maven
IDEA打包maven项目同时带上依赖
IDEA打包maven项目同时带上依赖
1307 0
IDEA打包maven项目同时带上依赖
|
10月前
|
数据处理 语音技术 项目管理
人人都是音乐家!中科大&科大讯飞重磅开源OpenMusic:音乐生成更高质量,更有乐感
提出了一种质量感知训练范式,使模型在训练过程中能够感知数据集的质量,从而在音乐性(美学角度)和音频质量方面实现卓越的音乐生成效果。
416 9
人人都是音乐家!中科大&科大讯飞重磅开源OpenMusic:音乐生成更高质量,更有乐感
|
4月前
|
缓存 小程序 视频直播
基于uni-app+vite5+vue3实战短视频+直播+聊天app应用
基于uniapp+vue3+vite5从0-1实战搭建仿抖音/微信直播带货商城。集短视频+聊天+直播功能于一体。实现全屏沉浸式切换短视频/直播,支持编译运行到h5+小程序端+app端。
336 4
|
运维 Kubernetes Cloud Native
Kubernetes云原生架构深度解析与实践指南####
本文深入探讨了Kubernetes作为领先的云原生应用编排平台,其设计理念、核心组件及高级特性。通过剖析Kubernetes的工作原理,结合具体案例分析,为读者呈现如何在实际项目中高效部署、管理和扩展容器化应用的策略与技巧。文章还涵盖了服务发现、负载均衡、配置管理、自动化伸缩等关键议题,旨在帮助开发者和运维人员掌握利用Kubernetes构建健壮、可伸缩的云原生生态系统的能力。 ####
|
Java API 开发者
Java 注释规范
Java中的注释规范包括单行注释(`//`)、多行注释(`/* ... */`)和文档注释(`/** ... */`)。单行注释适用于简短说明,多行注释用于较长描述,文档注释则专为自动生成API文档设计。注释应清晰明了、及时更新,避免冗余,并详细说明参数和返回值。遵循这些规范有助于提高代码的可读性和可维护性。
773 5
|
Java 测试技术 开发者
Java零基础-indexOf(String str)详解!
【10月更文挑战第13天】Java零基础教学篇,手把手实践教学!
323 1
|
移动开发 小程序 API
uniapp中uview组件库的丰富Upload 上传上午用法
uniapp中uview组件库的丰富Upload 上传上午用法
719 0
|
算法 Java
Java中常用hash算法总结
Java中常用hash算法总结
232 0
|
定位技术
uniapp使用map标签
uniapp使用map标签
366 2