很久没有写点东西了,总以为本身应该写点牛逼的,却又不知道如何下笔。既然如此,仍是回归最基本的吧,今天就来讲一说这个new
。关于javascript的new
关键字的内容上网搜一搜还真很多,你们都说new
干了3件事:javascript
__proto__
指向构造函数的prototype
文字比较难懂,翻译成javascript:html
javascriptfunction Base() { this.str = "aa"; } // new Base()干了下面的事 var obj = {}; obj.__proto__ = Base.prototype; Base.call(obj);
想一想是这么回事哈,那就赶快试试:java
javascriptvar b = new Base(); console.dir(b); // Base {str: 'aa', __proto__: Base}
好像是正确的,可是真的正确吗???shell
每一个对象都有一个constructor
属性,那么咱们来试试看new
出来的实例的constructor
是什么吧。编程
javascriptconsole.dir(b.constructor); // [Function: Base]
能够看出实例b的constructor
属性就是Base
,那么咱们能够猜想new
是否是至少还作了第4件事:segmentfault
javascriptb.constructor = Base;
以上结果看似正确,下面咱们进行一点修改,这里咱们修改掉原型的constructor
属性:app
javascriptBase.prototype.constructor = function Other(){ }; var b = new Base(); console.dir(b.constructor); // [Function: Other]
状况就不同了,能够看出,以前的猜想是错误的,第4件事应该是这样的:函数
javascriptb.constructor = Base.prototype.constructor;
这里犯了一个错误,那就是没有理解好这个constructor
的实质:当咱们建立一个函数时,会自动生成对应的原型,这个原型包含一个constructor
属性,使用new
构造的实例,能够经过原型链查找到constructor
。以下图所示:this
这里很是感谢zonxin同窗指出个人错误。spa
通常状况下构造函数没有返回值,可是咱们依旧能够获得该对象的实例;若是构造函数有返回值,凭直觉来讲状况应该会不同。咱们对于以前的构造函数进行一点点修改:
javascriptfunction Base() { this.str = "aa"; return 1; // return "a"; // return true; } var b = new Base(); console.dir(b); // { str: 'aa'}
咱们在构造函数里设置的返回值好像没什么用,返回的仍是原来对象的实例,换一些例子试试:
javascriptfunction Base() { this.str = "aa"; return [1]; // return {a:1}; } var b = new Base(); console.dir(b); // [1] or {a: 1}
此时结果就不同了,从上面的例子能够看出,若是构造函数返回的是原始值,那么这个返回值会被忽略,若是返回的是对象,就会覆盖构造的实例。
总结一下,new
至少作了4件事:
javascript// new Base(); // 1.建立一个空对象 obj var obj = {}; // 2.设置obj的__proto__为原型 obj.__proto__ = Base.prototype; // 3.使用obj做为上下文调用Base函数 var ret = Base.call(obj); // 4.若是构造函数返回的是原始值,那么这个返回值会被忽略,若是返回的是对象,就会覆盖构造的实例 if(typeof ret == 'object'){ return ret; } else { return obj; }
在《Javascript语言精粹》(Javascript: The Good Parts)中,道格拉斯认为应该避免使用new
关键字:
If you forget to include the new prefix when calling a constructor function, then this will not be bound to the new object. Sadly, this will be bound to the global object, so instead of augmenting your new object, you will be clobbering global variables. That is really bad. There is no compile warning, and there is no runtime warning.
大意是说在应该使用new
的时候若是忘了new
关键字,会引起一些问题。最重要的问题就是影响了原型查找,原型查找是沿着__proto__
进行的,而任何函数都是Function
的实例,一旦没用使用new
,你就会发现什么属性都查找不到了,由于至关于直接短路了。以下面例子所示,没有使用new
来建立对象的话,就没法找到原型上的fa1属性了:
javascriptfunction F(){ } F.prototype.fa1 = "fa1"; console.log(F.fa1); // undefined console.log(new F().fa1); // fa1
这里我配合一张图来讲明其中原理,黄色的线为原型链,使用new
构造的对象能够正常查找到属性fa1
,没有使用new
则彻底走向了另一条查找路径:
以上的问题对于有继承的状况表现得更为明显,沿着原型链的方法和属性全都找不到,你能使用的只有短路以后的Function.prototype
的属性和方法了。
固然了,遗忘使用任何关键字都会引发一系列的问题。再退一步说,这个问题是彻底能够避免的:
javascriptfunction foo() { // 若是忘了使用关键字,这一步骤会悄悄帮你修复这个问题 if ( !(this instanceof foo) ) return new foo(); // 构造函数的逻辑继续…… }
能够看出new
并非一个很好的实践,道格拉斯将这个问题描述为:
This indirection was intended to make the language seem more familiar to classically trained programmers, but failed to do that, as we can see from the very low opinion Java programmers have of JavaScript. JavaScript’s constructor pattern did not appeal to the classical crowd. It also obscured JavaScript’s true prototypal nature. As a result, there are very few programmers who know how to use the language effectively.
简单来讲,JavaScript是一种prototypical类型语言,在建立之初,是为了迎合市场的须要,让人们以为它和Java是相似的,才引入了new
关键字。Javascript本应经过它的Prototypical特性来实现实例化和继承,但new
关键字让它变得不三不四。
虽然使用new
建立新对象的时候用讨论了这个constructor
属性,可是这个属性彷佛并无什么用,也许设置这个属性就是一种习惯,可以让其余人直观理解对象之间的关系。
欢迎光临小弟博客:Superlin's Blog
个人博客原文:你真的弄明白new了吗