楼主比较喜欢看一些很短但很有意思的题目,无意间又瞥到了一题,大家不妨可以一试。(原题链接猛戳这里)
function Fn1() { this.name = 'peter'; return { name: 'jack' }; } function Fn2() { this.name = 'peter'; return 'jack'; } var obj1 = new Fn1(); var obj2 = new Fn2(); console.log(obj1.name, obj2.name);
或许你经常写面向对象编程的代码,也熟悉以上代码中this的用法甚至是prototype或者原型链,但是还是无法解释上面代码的输出,这时我们不得不承认对new的了解还不是很透彻。
先看一段普通的使用new操作符创建对象的代码:
function Person(a, b) { this.name = a; this.age = b; } Person.prototype.show = function() { console.log(this.name, this.age); }; var p = new Person('hanzichi', 10); console.log(p);
将console的内容截图:
我们可以清楚看到,p是一个对象,并且拥有构造函数Person中创建的name和age属性,还有个__proto__属性(这货太特殊,反正不在p的hasOwnProperty内!),其值为构造函数的prototype属性值。
其实new操作符创建对象可以分为四个步骤:
- 创建一个空对象
- 将所创建对象的__proto__属性值设成构造函数的prototype属性值
- 执行构造函数中的代码,构造函数中的this指向该对象
- 返回该对象(除非构造函数中返回一个对象)
用代码表示如下:
function Person(a, b) { this.name = a; this.age = b; } Person.prototype.show = function() { console.log(this.name, this.age); }; // var p = new Person('hanzichi', 10); var p = {}; p.__proto__ = Person.prototype; Person.call(p, 'hanzichi', 10); console.log(p);
观察输出的p,我们看到获取的对象p和通过new操作符获取的对象属性以及属性值一致,甚至连p.__proto__通过hasOwnProperty都无法检测到!
通过以上的介绍,我们基本就可以解答本文开头的那个问题了,其实就是构造函数里,如果返回一个非null的对象(包括数组),则将该对象值赋值给新建的对象,其实上面的实例代码还少了一个是否返回对象的判断,整理后应该是:
function Person(a, b) { this.name = a; this.age = b; return { name: 'zichi' }; } Person.prototype.show = function() { console.log(this.name, this.age); }; function init() { var p = {}; p.__proto__ = Person.prototype; var temp = Person.call(p, 'hanzichi', 10); if(temp !== null && typeof temp === 'object') return temp; return p; } var p = init(); console.log(p);