JavaScript之 这次彻底搞懂new操作符!

JavaScript之 这次彻底搞懂new操作符!

前言

在学习javascript的过程中,不可避免的会遇到new操作符,这次就来好好刨根问底一下,也算是加深理解和记忆了。

什么是new操作符?

mdn中是这么定义new操作符的:

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

在这句话里我们来看一个关键词:具有构造函数。这是个什么意思呢?我们先通过几个例子来看一下:

//例1let Animal1=function(){this.name=1};let animal=new Animal1; //这里不带()相当于不传参数//=>Animal1 {name: 1}//例2let TestObj={}let t1=new TestObj;//=>Uncaught TypeError: TestObj is not a constructor复制代码

登录后复制

我们可以看到,例1成功的执行了new语句,创建出了实例。例2在new一个{}对象时报错TypeError: TestObj is not a constructor,指出目标不是一个constructor。为什么普通的对象就不能执行new操作符呢?在ECMA规范里有相关的介绍:

If Type(argument) is not Object, return false.
If argument has a [[Construct]] internal method, return true.Return false.

意思就是:

立即学习“Java免费学习笔记(深入)”;

构造函数首先得是一个对象,否则不满足条件其次,对象必须拥有[[Construct]]内部方法,才可以作为构造函数  

我们这里的{}就是一个对象,满足第一个条件,那么显然,肯定是因为{}没有[[Construct]]这个内部方法,所以无法使用new操作符进行构造了。

那么我们已经搞定了new操作符的可操作对象,是不是可以去看看它的作用了呢?答案是:NO!我们再来看一个例子:

//例3let testObj={    Fn(){        console.log("构造成功!")    }}let t3=new testObj.Fn;//=>Uncaught TypeError: testObj.Fn is not a constructor复制代码

登录后复制

what?为什么刚刚还能成功构造的函数,作为方法就不行了呢?其实在MDN中也有直接介绍:

Methods cannot be constructors! They will throw a TypeError if you try to instantiate them.

意思就是,方法不能是构造函数,如果尝试创建一个方法的实例,就会抛出类型错误。这样说就懂了,但是还没完,这个说法没有完全解释清楚原理,我们再看个例子:

//例4const example = {  Fn: function() { console.log(this); },  Arrow: () => { console.log(this); },  Shorthand() { console.log(this); }};new example.Fn();        // Fn {}new example.Arrow();     // Uncaught TypeError: example.Arrow is not a constructornew example.Shorthand(); // Uncaught TypeError: example.Shorthand is not a constructor复制代码

登录后复制

对照这个例子,我们在ECMA规范查阅,发现所有的函数在创建时都取决于FunctionCreate函数:

FunctionCreate (kind, ParameterList, Body, Scope, Strict, prototype)

If the prototype argument was not passed, then let prototype be the intrinsic object %FunctionPrototype%.If “kind” is not Normal, let allocKind be “non-constructor”.

这个函数的定义可以看出

只有当类型为Normal的函数被创建时,它才是可构造的函数,否则他就是不可构造的。

在我们这个例子中,Arrow的类型为Arrow,而ShortHand的类型是Method,因此都不属于可构造的函数,这也解释了例3所说的”方法不能作为构造函数”。搞清楚了new操作符可以操作的目标,终于可以神清气爽的来看看它的作用了(不容易呀TAT)。

new操作符实现了什么?

我们举一个简单的例子来具体看看它的作用:

function Animal(name){    this.name=name;    console.log("create animal");}let animal=new Animal("大黄");  //create animalconsole.log(animal.name);       //大黄Animal.prototype.say=function(){    console.log("myName is:"+this.name);}animal.say();                   //myName is:大黄复制代码

登录后复制

我们从这个例子来分析一下,首先我们看这一句:

let animal=new Animal("大黄");复制代码

登录后复制

可以看到,执行new操作符后,我们得到了一个animal对象,那么我们就知道,new操作符肯定要创建一个对象,并将这个对象返回。再看这段代码:

function Animal(name){    this.name=name;    console.log("create animal");}复制代码

登录后复制

同时我们看到结果,确实输出了create animal,我们就知道,Animal函数体在这个过程中被执行了,同时传入了参数,所以才执行了我们的输出语句。但我们的函数体里还有一句this.name=name体现在哪里呢?就是这一句:

console.log(animal.name);       //大黄复制代码

登录后复制

执行完函数体后,我们发现返回对象的name值就是我们赋值给this的值,那么不难判断,在这个过程中,this的值指向了新创建的对象。最后还有一段:

Animal.prototype.say=function(){    console.log("myName is:"+this.name);}animal.say();                   //myName is:大黄复制代码

登录后复制

animal对象调用的是Animal函数原型上的方法,说明Animal在animal对象的原型链上,那么在哪一层呢?我们验证一下:

animal.__proto__===Animal.prototype; //true复制代码

登录后复制

那我们就知道了,animal的__proto__直接指向了Animal的prototype。除此之外,如果我们在构造函数的函数体里返回一个值,看看会怎么样:

function Animal(name){    this.name=name;    return 1;}new Animal("test"); //Animal {name: "test"}复制代码

登录后复制

可以看到,直接无视了返回值,那我们返回一个对象试试:

function Animal(name){    this.name=name;    return {};}new Animal("test"); //{}复制代码

登录后复制

我们发现返回的实例对象被我们的返回值覆盖了,到这里大致了解了new操作符的核心功能,我们做一个小结。

小结

new操作符的作用:

创建一个新对象,将this绑定到新创建的对象使用传入的参数调用构造函数将创建的对象的_proto__指向构造函数的prototype如果构造函数没有显式返回一个对象,则返回创建的新对象,否则返回显式返回的对象(如上文的{})

模拟实现一个new操作符

说了这么多理论的,最后我们亲自动手来实现一个new操作符吧~

var _myNew = function (constructor, ...args) {    // 1. 创建一个新对象obj    const obj = {};    //2. 将this绑定到新对象上,并使用传入的参数调用函数    //这里是为了拿到第一个参数,就是传入的构造函数    // let constructor = Array.prototype.shift.call(arguments);    //绑定this的同时调用函数,...将参数展开传入    let res = constructor.call(obj, ...args)    //3. 将创建的对象的_proto__指向构造函数的prototype    obj.__proto__ = constructor.prototype    //4. 根据显示返回的值判断最终返回结果    return res instanceof Object ? res : obj;}复制代码

登录后复制

上面是比较好理解的版本,我们可以简化一下得到下面这个版本:

function _new(fn, ...arg) {    const obj = Object.create(fn.prototype);    const res = fn.apply(obj, arg);    return res instanceof Object ? res : obj;复制代码

登录后复制

大功告成!

总结

本文从定义出发,探索了new操作符的作用目标和原理,并模拟实现了核心功能。其实模拟实现一个new操作符不难,更重要的还是去理解这个过程,明白其中的原理。

更多相关免费学习推荐:javascript(视频)

以上就是JavaScript之 这次彻底搞懂new操作符!的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2725458.html

(0)
上一篇 2025年3月7日 23:23:21
下一篇 2025年3月7日 23:23:29

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • 杜绝这五个 Javascript 错误啦

    javascript栏目将列出五个让你的代码更加可读和易于维护小窍门。 在编辑旧项目的时候,有没有遇到过那种一加新逻辑就“粉碎”的迷惑代码?我们当然都遇到过。为了让世界上不可读的 Javascript 代码更少,我得给你们看看以下的五个例子…

    2025年3月7日
    000
  • js保留两位小数的函数有哪些

    js保留两位小数的函数有:1、【toFixed()】函数;2、【Math.floor()】函数不四舍五入 ,向下取整;3、使用字符串匹配方法;4、四舍五入保留2位小数;5、浮点数保留两位小数。 js保留两位小数的函数有: 一、四舍五入相关 …

    2025年3月7日
    200
  • 一纸搞懂JS系列(1)之编译原理,作用域,作用域链,变量提升,暂时性死区

    写在最前面:这是javascript栏目我即将开始写的一个系列,主要是在框架横行的时代,虽然上班用的是框架,但是对于面试,以及技术进阶,JS基础知识的铺垫是锦上添花,也是不得不学习的一块知识,虽然开汽车的不需要很懂汽车,只需要掌握汽车的常用…

    2025年3月7日
    200
  • 深入理解JavaScript中的事件

    在本文中,我们将讨论事件处理程序、事件监听器和事件对象。我们还将介绍三种不同的处理事件的方法,以及一些最常见的事件。通过了解事件,您将能够为用户提供更具交互性的web体验。 事件是发生在浏览器中的操作,可以由用户或浏览器本身发起。以下是一些…

    2025年3月7日
    200
  • 原生JavaScript实现弹幕组件de方法

    JavaScript栏目今天为大家介绍原生JavaScript实现弹幕组件的方法。 前言 如今几乎所有的视频网站都有弹幕功能,那么今天我们就自己用原生 javascript 封装一个弹幕类。这个类希望有如下属性和实例方法: 属性 el容器节…

    2025年3月7日
    200
  • JS中20个常用字符串方法及使用方式(总结)

    下面本篇文章给大家介绍JavaScript中20个常用字符串方法及使用方式。有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 1. charAt(x) charAt(x)返回字符串中x位置的字符,下标从 0 开始。     /…

    2025年3月7日
    200
  • 深入了解JS中的for…of循环

    本篇文章带大家深入了解一下javascript中的for…of循环。有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 for…of语句创建的循环可以遍历对象。在ES6中引入的for…of可以…

    2025年3月7日
    200
  • 深入了解JavaScript中的构造器

    对构造函数有很好的理解是你掌握JavaScript这门语言的重点。我们都知道JavaScript不像其他语言,它没有class关键字,但是它有跟function非常相似的构造函数。这篇文章我们一起来详细地了解JavaScript构造函数如何…

    2025年3月7日
    200
  • JS中的一些常用基础算法介绍

    一个算法只是一个把确定的数据结构的输入转化为一个确定的数据结构的输出的function。算法内在的逻辑决定了如何转换。 基础算法 一、排序 1、冒泡排序 //冒泡排序function bubbleSort(arr) {  for(var i…

    2025年3月7日
    200
  • 详解JavaScript中的变量、范围和提升

    变量是许多编程语言的基本组成部分,也是新手需要学习的第一个也是最重要的概念。JavaScript中有许多不同的变量属性,以及命名变量时必须遵循的一些规则。在JavaScript中,有三个关键字用于声明变量——var、let和const——每…

    2025年3月7日
    200

发表回复

登录后才能评论