JavaScript里new语句做了什么
首先,JavaScript并没有其他基于类的语言所定义的“方法”,只有函数(function)。在JavaScript里,任何函数都可以添加到对象上作为对象的属性。当继承的函数被调用时,this指向的是当前继承的对象,而不是继承的函数所在的原型对象。
在JavaScript中,构造器其实就是一个普通的函数。当使用 new 操作符来作用这个函数时,它就可以被称为构造方法(构造函数)。
要创建一个新实例,需要使用new操作符。以这种方式调用构造函数实际上会经历以下4个步骤:
(1)创建一个新对象;
(2)将构造函数的作用域赋给新对象(因此this就指向了这个对象);
(3)执行构造函数中的代码(为这个新对象添加属性);
(4)返回新对象。《JavaScript高级程序设计(第三版)》P145
这里有篇很好的文章解释了new的处理过程以及原型继承的相关内容:Javascript – How Prototypal Inheritance really works
1 2 3 4 5 6 7 |
function New (f) { var n = { '__proto__': f.prototype }; //步骤1 return function () { f.apply(n, arguments); //步骤2&3 return n; //步骤4 }; } |
可以创建对象来证明以上代码有效
1 2 3 4 5 6 7 8 9 |
function Point(x, y){ this.x = x; this.y = y; } var p = New(Point)(10, 20); Point.prototype.print = function(){ console.log(this.x, this.y); } p.print(); // 10 20 |
看起来,new语句基本就是以上函数的一个语法糖。
构造函数的返回值
当使用new关键字时,构造函数一定会返回一个对象,默认返回的是指向this
的对象。如果不给this
添加属性,则返回一个“空”的对象。
注意,即使不写return
,也会隐式返回指向this
的对象。当然,也可以返回任意的其他对象。如果写了return false;
或者return '123';
之类的非对象,会被忽略,依然返回指向this的对象。
1 2 3 4 5 6 7 8 9 |
var ObjectCreator = function(){ this.name = 'This name'; var that = {}; that.name = 'That name'; return that; } var o = new ObjectCreator(); console.log(o.name); // That name |
潜在问题
在《Javascript语言精粹》B.11(鸡肋)里,作者Douglas Crockford提倡更好的策略是不使用new,原因是因为如果忘记使用new,会把this绑定到全局对象,造成全局污染,并且没有警告。但是不使用new,就需要手动指定prototype,并完成步骤1~4,如果因为某个语法一旦写错会出问题而弃用它,有些违背语法创立的初衷。诚然,造个轮子肯定能解决一些问题,并且由于只使用基础语法,看起来更强壮,但是程序员那么懒,谁喜欢多写代码呢?
prototype和__proto__
每一个函数都有一个prototype的属性,每一个由该函数实例化的对象都包含一个隐式指针(__proto__)指向该函数的prototype属性。所以,prototype属性体现的是构造函数和实例之间的关系。
而__proto__,在ES6之前至少在规范中是没有这个属性的,但是浏览器厂商自行实现了,在ES6规范中,它其实是Object.prototype对外暴露的一个访问器属性,具有[[Get]]和[[Set]]的方法,用来操作对象的原型。我们在使用对象字面量创建对象的时候,__proto__属性能够设置对象的prototype,可以用来作为Object.create()的替代方案。
1 2 3 |
function A(){} var a = new A(); a.__proto__ === A.prototype //true |
但是,函数的prototype和__proto__属性没有关系,因为
1 |
A.__proto__ === Function.prototype //true |
而Object.create(proto[, propertiesObject])
可以理解为不执行构造函数的new。
Comments: 0