构造函数和普通函数没有什么本质的区别,唯一的区别有两个:
函数首字母大写,这个区别只是约定俗成的,便于区分。你实在要小写定义构造函数也完全没问题,所以这个区别可以忽略。
构造函数的调用需要用new操作符,而普通函数的调用又分很多种,但是都不会用到new操作符。所以,构造函数和普通函数的区别就在这个new操作符里,现在让我们来好好研究一下这个new操作符。
用new操作符创建对象发生的事情:
- 创建一个新对象;
- 将构造函数的作用域赋给这个对象(因此this就指向了这个对象);
- 执行构造函数中的代码(为这个对象添加属性和方法,以及执行构造函数中其他的代码);
- 把这个新对象返回;
注意:原本的构造函数是window对象的方法,如果不用new操作符而直接调用,那么构造函数的执行对象就 是window,即this指向了window。现在用new操作符后,this就指向了新生成的对象。理解这一步至关重要。
执行构造函数中的代码
function Person() {
// this.age = 22;
window.age=23;
this.name='tom'
let name = 'tony';
console.log(name);
}
let p = new Person(); //自动执行构造函数中的代码,输出 tony
console.log(p.age) // undefined
console.log(window.age) // 23
console.log(p.name) // tom
将构造函数的执行对象赋给新生成的这个实例。再结合上一段里说的,自动执行构造函数里的this.name = "张三";就相当于是执行p.name = "张三";这里this就是指向新创建的对象p,并给p赋值属性和方法。
这是前面的三个步骤,最后一步是返回这个创建的对象,但是我们并没有看到return相关的代码。
如果被调用的函数没有显式的 return 表达式(仅限于返回对象),则隐式的会返回 this 对象 - 也就是新创建的对象。
function Person(){
this.name = "Tim";
return {};
}
var p = new Person();
console.log(p.name) // undefined;
实际上 new Person()相当于是如果没有返回任何东西,就默认返回return this,this即当前创建的对象。
function Person(){
this.name = "Tim";
return this;
}
var p = new Person();
console.log(p.name)// Tim;
那么最后实现一个简单的new也容易了。如下图代码:
// 创建构造函数
function Person(name,age){
this.name = name;
this.age = age;
}
// 定义自己实现的类似new方法,
function create(fn,...args){
let obj = {};
fn.call(obj,...args);
return obj;
}
// 使用自己定义的方法创建对象,第一参数是构造函数,相当于模板。从第二个参数起,为对象的属性值。
let obj = create(Person,'张三',18);
console.log(obj) // {name: "张三", age: 18}