完全搞懂js 中的new()到底做了什么?

您所在的位置:网站首页 vue初始化发生了什么 完全搞懂js 中的new()到底做了什么?

完全搞懂js 中的new()到底做了什么?

2024-01-03 19:38| 来源: 网络整理| 查看: 265

今天面试,总体还可以,其中有一个问题我回答的很支支吾吾,就是js在new()过程中到底做了什么?(我居然从堆和栈的角度来回答的,面试官想听的根本不是这个,错的很离谱啊)下面便来总结一下吧: 要创建 Person 的新实例,必须使用 new 操作符。以这种方式调用构造函数实际上会经历以下 4个步骤: (1) 创建一个新对象; (2) 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象) ; (3) 执行构造函数中的代码(为这个新对象添加属性) ; (4) 返回新对象。

new 操作符 在有上面的基础概念的介绍之后,在加上new操作符,我们就能完成传统面向对象的class + new的方式创建对象,在JavaScript中,我们将这类方式成为Pseudoclassical。 基于上面的例子,我们执行如下代码

var obj = new Base();

这样代码的结果是什么,我们在Javascript引擎中看到的对象模型是: 在这里插入图片描述

new操作符具体干了什么呢?其实很简单,就干了三件事情。

var obj = {};

obj.proto = Base.prototype;

Base.call(obj);

第一行,我们创建了一个空对象obj 第二行,我们将这个空对象的__proto__成员指向了Base函数对象prototype成员对象 第三行,我们将Base函数对象的this指针替换成obj,然后再调用Base函数,于是我们就给obj对象赋值了一个id成员变量,这个成员变量的值是”base”,关于call函数的用法。

如果我们给Base.prototype的对象添加一些函数会有什么效果呢? 例如代码如下:

Base.prototype.toString = function() { return this.id; }

那么当我们使用new创建一个新对象的时候,根据__proto__的特性,toString这个方法也可以做新对象的方法被访问到。于是我们看到了: 构造子中,我们来设置‘类’的成员变量(例如:例子中的id),构造子对象prototype中我们来设置‘类’的公共方法。于是通过函数对象和Javascript特有的__proto__与prototype成员及new操作符,模拟出类和类实例化的效果。

--------------------------------------------------手动分割线------------------------------------------------------------------------------------------------------

对于大部分前端开发者而言,new一个构造函数或类得到对应实例,是非常普遍的操作了。下面的例子中分别通过构造函数与class类实现了一个简单的创建实例的过程。

// ES5构造函数 let Parent = function (name, age) { this.name = name; this.age = age; }; Parent.prototype.sayName = function () { console.log(this.name); }; const child = new Parent('一晌贪欢', 25); child.sayName() //'一晌贪欢' //ES6 class类 class Parent { constructor(name, age) { this.name = name; this.age = age; } sayName() { console.log(this.name); } }; const child = new Parent('echo', 25); child.sayName() //echo

但new不应该像一个黑盒,我们除了知道结果,更应该明白过程究竟如何。那么那么这篇文章主要围绕两点展开,第一,js中new一个对象时到底发生了什么,第二,知道了原理后我们通过js来实现一个简单的new方法。

一、new操作中发生了什么?

比较直观的感觉,当我们new一个构造函数,得到的实例继承了构造器的构造属性(this.name这些)以及原型上的属性。

在《JavaScript模式》这本书中,new的过程说的比较直白,当我们new一个构造器,主要有三步:

• 创建一个空对象,将它的引用赋给 this,继承函数的原型。 • 通过 this 将属性和方法添加至这个对象 • 最后返回 this 指向的新对象,也就是实例(如果没有手动返回其他的对象)

改写上面的例子,大概就是这样:

// ES5构造函数 let Parent = function (name, age) { //1.创建一个新对象,赋予this,这一步是隐性的, // let this = {}; //2.给this指向的对象赋予构造属性 this.name = name; this.age = age; //3.如果没有手动返回对象,则默认返回this指向的这个对象,也是隐性的 // return this; }; const child = new Parent();

这应该不难理解,你应该在工作中看过类似下述代码中的操作,将this赋予一个新的变量(例如that),最后返回这个变量:

// ES5构造函数 let Parent = function (name, age) { let that = this; that.name = name; that.age = age; return that; }; const child = new Parent('一晌贪欢', '25');

为什么要这么写呢?我在前面说this的创建与返回是隐性的,但在工作中为了让构造过程更易可见与更易维护,所以才有了上述使用that代替this,同时手动返回that的做法;这也验证了隐性的这两步确实是存在的。

但上述这个解释我觉得不够完美,它只描述了构造器属性是如何塞给实例,没说原型上的属性是如何给实例继承的。

我在winter大神的重学前端专栏中,看到了比较符合我心意的,同时也是符合原理的描述:

• 以构造器的prototype属性为原型,创建新对象; • 将this(也就是上一句中的新对象)和调用参数传给构造器,执行; • 如果构造器没有手动返回对象,则返回第一步创建的对象 到这里不管怎么说,你都应该大概知道了new过程中会新建对象,此对象会继承构造器的原型与原型上的属性,最后它会被作为实例返回这样一个过程。知道了原理,我们来手动实现一个简单的new方法。

二、实现一个简单的new方法

// 构造器函数 let Parent = function (name, age) { this.name = name; this.age = age; }; Parent.prototype.sayName = function () { console.log(this.name); }; //自己定义的new方法 let newMethod = function (Parent, ...rest) { // 1.以构造器的prototype属性为原型,创建新对象; let child = Object.create(Parent.prototype); // 2.将this和调用参数传给构造器执行 Parent.apply(child, rest); // 3.返回第一步的对象 return child; }; //创建实例,将构造函数Parent与形参作为参数传入 const child = newMethod(Parent, 'echo', 26); child.sayName() //'echo'; //最后检验,与使用new的效果相同 child instanceof Parent//true child.hasOwnProperty('name')//true child.hasOwnProperty('age')//true child.hasOwnProperty('sayName')//false

希望看到这里我能理解完全理解~~~



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3