本分围绕以下几个方面展开:
- 构造函数是什么?
- 关键字new的作用是什么?
一 什么是构造函数?
用通俗的话解释,构造函数就一张产品设计图,通过设计图,就可以不断制造出相同的产品。
JS中,构造函数的函数名首字母需要大写(其实小写也没问题,约定俗成罢了),通过new 构造函数,得到实例对象。 ES6出的class,是语法糖,这里不去讨论它,继续基于原生进行理解。
//函数名大小写不影响构造函数的功能
function factory() {
this.name = '这是factory构造函数';
}
function Factory() {
this.name = '这是Factory构造函数';
}
let a = new factory();
let b = new Factory();
console.log(a);//factory {name: '这是Factory构造函数的实例'}
console.log(b);//Factory {name: '这是Factory构造函数的实例'}
二 关键字new的作用是什么?
从结构上看,普通函数与构造函数,相差的就是一个 new 关键字,我们先看看去掉了new,会产生什么影响。
function Factory() {
this.name = '这是Factory构造函数';
}
let a = Factory();
console.log(a);//undefined
a的值为undefined,说明关键字new给函数Factory隐式return了一个值。那这个值到底是什么,继续往下探究。
function Test1() {}
var a = new Test1();
console.log(a); //Test1 {}
function Test2() {
return '这是test2构造函数'
}
var b = new Test2();
console.log(b); //Test2 {}
new关键字 会隐式返回一个值,这个值必须是对象。
function Test1() {
this.name = "这是Test1构造函数"
}
var a = new Test1();
console.log(a); //Test1 {name: '这是Test1构造函数'}
function Test2() {
var obj = {
name: "这是Test2构造函数"
}
}
var b = new Test2();
console.log(b); //Test2 {}
function Test3() {
var obj = {
name: "这是Test3构造函数"
}
return obj;
}
var c = new Test3();
console.log(c); //{name: '这是Test3构造函数'}
function Test4() {
this.name = 'this的name:这是Test4构造函数'
var obj = {
name: "这是Test4构造函数"
}
return obj;
}
var d = new Test4();
console.log(d); //{name: '这是Test4构造函数'}
以上列子说明:
在不更改return的情况下,new关键字会返回一个对象,这个对象是this。
当更改return,new关键字,会对return所返回的值,进行校验,如果不是引用值,则依旧返回this对象,是引用值,则返回引用值
那么问题来了,既然能直接return,那为什么我们平时使用构造函数,还需要往this上挂载数据呢?retrun this和retrun 新对象又有什么区别呢?我们继续分析。
function Test1() {
this.name = '这是Test1构造函数'
return this;
}
var a = new Test1();
console.log(a); //Test1 {name: '这是Test1构造函数'}
function Test2() {
var obj = {
name:'这是Test2构造函数'
}
return obj;
}
var b = new Test2();
console.log(b); //{name: '这是Test2构造函数'}
对比发现,retrun this 获得的实例对象,会携带着实例化它的构造函数的原型对象。
return obj 获得的实例对象,并没有携带着实例化它的构造函数的原型对象。
也就意味着,实例b无法继承实例化它的构造函数的原型对象上的方法和属性,那实例b对象存在的意义就不大了,需要改造一下。
Test1.prototype.fn = function () {
console.log('调用Test1原型对象方法', this.name)
}
function Test1() {
this.name = '这是Test1构造函数'
}
var a = new Test1();
console.log('输出Test1实例----------------------', a);
a.fn();
Test2.prototype.fn = function () {
console.log('调用Test2原型对象方法', this.name)
}
function Test2() {
var obj = Object.create(Test2.prototype);
obj.name = '这是Test2的构造函数'
return obj
}
var b = new Test2();
console.log('输出Test2实例----------------------', b);
b.fn();
通过对比属性,可以看到两个实例的数据结构基本一致了。其原因就是,通过
Object.create()
方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__,使得新创建的对象可以继承Test2构造函数原型对象上的属性和方法。