JavaScript new 关键词解析及原生实现 new
Java里面,new 运算符是用来实例化一个类,从而在内存中分配一个实例对象。 但在 javascript 中,原型语言没类,只有对象与原型链继承
JavaScript 中 new 表达式的作用是生成一个对象。
new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。
new 关键字会进行如下的操作:
-
创建一个空对象(即{});
-
链接该对象(即设置该对象的构造函数)到另一个对象 ;
-
将步骤1新创建的对象作为this的上下文 ;
-
如果该函数没有返回对象,则返回this。
根据这个步骤,手工实现new
function create(){ //创建一个空对象 let obj = new Object(); //获取构造函数 // let args = [].slice.call(arguments); let Fun = args.shift(); let Constructor = [].shift.call(arguments); //链接到原型 obj.__proto__ = Constructor.prototype; //绑定this值,使用apply,将构造函数中的this指向新对象,这样新对象就可以访问构造函数中的属性和方法 let result = Constructor.apply(obj,arguments); //返回新对象 return typeof result === "object" ? result : obj;//如果返回值是一个对象就返回该对象,否则返回构造函数的一个实例对象 }
在javascript中, 通过new可以产生原对象的一个实例对象,而这个实例对象继承了原对象的属性和方法。因此,new存在的意义在于它实现了javascript中的继承
在《JavaScript模式》这本书中,new的过程说的比较直白,当我们new一个构造器,主要有三步:
• 创建一个空对象,将它的引用赋给 this,继承函数的原型。
• 通过 this 将属性和方法添加至这个对象
• 最后返回 this 指向的新对象,也就是实例(如果没有手动返回其他的对象)
当我们new一个构造函数,得到的实例继承了构造器的构造属性(this.name这些)以及原型上的属性。
// ES5构造函数 let Parent = function (name, age) { //1.创建一个新对象,赋予this,这一步是隐性的, // let this = {}; //2.给this指向的对象赋予构造属性 this.name = name; this.age = age; //3.如果没有手动返回对象,则默认返回this指向的这个对象,也是隐性的 // return this; //this的创建与返回是隐性的 }; const child = new Parent();
但上述这个解释我觉得不够完美,它只描述了构造器属性是如何塞给实例,没说原型上的属性是如何给实例继承的。
我在winter大神的重学前端专栏中,看到了比较符合我心意的,同时也是符合原理的描述:
• 以构造器的prototype属性为原型,创建新对象;
• 将this(也就是上一句中的新对象)和调用参数传给构造器,执行;
• 如果构造器没有手动返回对象,则返回第一步创建的新对象,如果有,则舍弃掉第一步创建的新对象,返回手动return的对象。
new过程中会新建对象,此对象会继承构造器的原型与原型上的属性,最后它会被作为实例返回这样一个过程。
以直观的方式来理解的话,关键词 new 之后所写的是类名。不过正如此前说明,JavaScript 中没有类的概念,所以,根据 JavaScript 的语法规则,new 之后所写的是函数名。在 new 之后写函数名的话,就会把该函数作为构造函数来进行调用。
在 JavaScript 的语言特性中没有“类”的概念,为了便于理解,将用类 这个词来称呼那些可以被视作“类”的概念。来称呼那些实际上将会调 用构造函数的 Function 对象。此外,在强调对象是通过调用构造函数而生成的时候,会将这些被生成的 对象称作对象实例以示区别。
虽然在 JavaScript 中没有类的概念,但将 new 之后所写的标识符(函数名)看作是类名, 也并没有什么概念上的问题。也就是说,完全可以认为,上文中代码 new Object() 的作用是生成一个 Object 类的实例。
对类的功能的整理
接口 | 说明 |
---|---|
函数或是构造函数的调用 | - |
类的属性 | 相当于Java 中的static 方法或是static 域 |
prototype 对象的属性 | 相当于Java 中的实例方法 |
实例属性 | 相当于Java 中的实例域 |
类的属性是一个类自身的属性,例如,String 类的属性是 String 类的对象自身的属性。如果是函数的话,则可以像 String.fromCharCode(0x41) 这样来使用。如果用更加直观一些的说法来讲,这就相当于 Java 或 C++ 中的 static 方法。
prototype 对象的属性和实例属性,都是以对象实例的形式来进行访问的。以 String 类为例,可以以 str.trim() 或是 str.length 的方式,来使用引用了 String 对象(对象实例)的变量 str。
prototype 对象的属性与实例属性之间的不同点在于是否进行了继承。例如,String 对象的 trim 方法,其实是 String.prototype 对象的属性。这种以实例来继承属性的方式被称为原型继承。
实现一个简单的new方法
//自己定义的new方法 let newMethod = function (Parent, ...rest) { // 1.以构造器的prototype属性为原型,创建新对象; let child = Object.create(Parent.prototype); // 2.将this和调用参数传给构造器执行 let result = Parent.apply(child, rest); // 3.如果构造器没有手动返回对象,则返回第一步的对象 return typeof result === 'object' ? result : child; };
参考文章:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/new
JavaScript深入之new的模拟实现 https://github.com/mqyqingfeng/Blog/issues/13
js new一个对象的过程,实现一个简单的new方法 https://www.cnblogs.com/echolun/p/10903290.html
js手动实现new方法 https://www.jianshu.com/p/9cee6a703e01
转载本站文章《JavaScript new 关键词解析及原生实现 new 》,
请注明出处:https://www.zhoulujun.cn/html/webfront/ECMAScript/js/2020_0630_8498.html