new操作符做了这些事:
- 它创建了一个全新的对象
- 它会被执行[[Prototype]](也就是__proto__)链接 (设置对象的__proto__属性为构造函数的原型:调用原型方法)
- 它使this指向新创建的对象 (使用apply,将构造函数中的this指向新对象:调用构造函数上的属性)
- 通过new创建的每个对象将最终被[[Prototype]]链接到这个函数的prototype对象上
- 如果函数没有返回对象类型Object(包含Functoin, Array, Date, RegExg, Error),那么new表达式中的函数调用将返回该对象引用
// objectFactory(Father, 'cxk', '18')
function objectFactory() {
const obj = new Object();
const Constructor = [].shift.call(arguments);
obj.__proto__ = Constructor.prototype;
const ret = Constructor.apply(obj, arguments);
return typeof ret === "object" ? ret : obj;
}
new概念:
【MDN】new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例
(1)new常用用来生成构造函数的实例
function Person(name){
this.name = name
}
Person.prototype.say = function(){
console.log(`i'm ${this.name}`) //模板字符串,解决字符串连接“+”问题
}
let person = new Person('千玺')
console.log(person.name) // 千玺
student.say() // i'm 千玺
这里我们可以看出 通过new 创建的实例 student 可以访问 Person的 属性以及Person 定义在原型链上的方法;
总结在使用new运算符调用构造函数时的执行过程:
- 创建一个新对象
- 将构造函数中的this指向该对象
- 执行构造函数代码(给新对象添加属性和方法)
- 返回者着新对象
据此,我们初步实现模拟new的objectFactory
方法:
function objectFactory(){
var obj=new Object();//创建一个新对象
var Constructor=[].shift.call(arguments);//取得该方法的第一个参数(并删除第一个参数),该参数是构造函数
obj.__proto__=Constructor.prototype;//将新对象的内部属性__proto__指向构造函数的原型,这样新对象就可以访问原型中的属性和方法
Constructor.apply(obj,arguments);//使用apply,将构造函数中的this指向新对象,这样新对象就可以访问构造函数中的属性和方法
return obj;//返回一个对象
}
(2)一般情况下,构造函数是没有返回值的。当有返回值时:
- 如果返回值是基本类型,
function Person(name) {
this.name = name
return '金高'
}
Person.prototype.say = function () {
console.log(`i'm ${this.name}`)
}
let person = new Person('千玺')
console.log(person.name) // 千玺
student.say() // i'm 千玺
这里可以看出当返回值为基本类型时,并没有什么区别,返回的还是内部创建的对象;
- 返回值是一个对象
function Person(name) {
this.name = name
return {actor:'高银'}
}
Person.prototype.say = function () {
console.log(`i'm ${this.name}`)
}
let person = new Person('千玺')
console.log(person) // {actor:'高银'}
console.log(person.name) // undefined
这里我们可以看出,当构造函数内部返回一个对象时;函数内部的对象会直接覆盖我们内部创建的那个函数。
【MDN】如果构造函数返回了一个“对象”,那么这个对象会取代整个new出来的结果。如果构造函数没有返回对象,那么new出来的结果为步骤1创建的对象。(一般情况下构造函数不返回任何值,不过用户如果想覆盖这个返回值,可以自己选择返回一个普通对象来覆盖。当然,返回数组也会覆盖,因为数组也是对象。
据此,我们对之前的objectFactory
方法进行改进:
//最终版
function objectFactory(){
var obj=new Object();
var Constructor=[].shift.apply(arguments);
obj.__proto__=Constructor.prototype;
var ret=Constructor.apply(obj,arguments);//取得构造函数的返回值
return typeof ret=="object"? ret : obj;//如果返回值是一个对象就返回该对象,否则返回构造函数的一个实例对象
}