构造函数是用来创建一个对象,所有的对象都是被new构造函数()出来的;
构造函数与普通函数的区别就是调用的方式不同,而且构造函数也是函数,任何函数只要使用new操作符就是构造函数,不适用new操作符调用的函数就是普通函数。
例:
function Create(name,age){
this.name = name;
this.age = age;
this.sayName = function() {
console.log(this.name);
}
}
作为构造函数:
var obj = new Create("张三", 18) obj.sayName() //输出张三
作为函数调用:
Create('李四', 19)//属性会添加到window中
window.sayName()//输出李四
在另一个对象调用:
var demo = new Object();
Create.call(demo, '王五', 20)//改变this指向
demo.sayName()//输出王五
上面使用new创建了一个对象,然后是普通函数的调用方式,结果普通的函数调用方式会将属性和方法添加到window对象。
总结:在调用函数时,没有用对象去调用,或者没有用call()或者apply()改变this指向调用,
此时函数里this指向window对象
但是这三个创建的对象上面的sayName方法虽然同名但是不相等,实际上是每次创建对象是都实例化了一个Function对象,来显示这个对象的name;因为这个方法做的事情都是一样的,我们没必要定义非常多不同的方法来做相同的事情,所以我们可以方法写在外面:
function Create(name, age) {
this.name = name;
this.age = age;
this.sayName = sayName
}
function sayName() {
console.log(this.name);
}
这样虽然解决了相同逻辑的函数重复定义的问题,但是把全局的作用域搞混乱了,所以我们可以把这个方法定义到构造函数的原型上:
function Create(name, age) {
this.name = name;
this.age = age;
}
Create.prototype.sayName = function() {
console.log(this.name);
}
这样只有可以访问到构造函数的原型的对象才能调用sayName;上面直接调用函数的方式,window上是没有sayName这个方法的
new操作符帮我们做的一系列事:
1.在函数内部创建了一个空对象 { }
2.将函数里this指向这个空对象,并且将这个对象的隐式原型指向这个构造函数的原型
3.逐行执行函数中的代码
4.隐式返回这个对象
在构造函数中如果出现了返回值,如果返回的是原始(基本)类型,会直接忽略;如果返回的是引用类型,则使用返回的结果;(如果没有返回值,则返回new关键字隐式返回的对象)
new.Target
为什么内置的构造函数不通过new也能返回一个对象?
因为内置的构造函数在封装时会判断当前函数的new.Target
new.Target:在函数中用于判断,判断带函数是如何被调用的(new或者直接调用)
1.new调用:new.Target值为该函数体
2.直接调用:new.Target值为undefined
所以我们可以这样判断:
function myObject(){
if(new.target == undefined){
return new myObject();
}
}
var obj1 = new myObject(); // {}
var obj2 = myObject(); // {}
包装类
在js中基本数据类型是不能调用属性的,因为js为了增强(Number、String、Boolean)代码的功能,创建了三个构造函数。
在js中如果用基本数据类型去调用了属性(123.属性),则该代码的原地使用该类型数据的构造函数创建对象,用新创建的对象去调用属性;
num.abc = 123; // => new Number(num).abc = 123
console.log(num.abc); // new Number(num).abc : undefined