Javascript中没有类,但可以把函数当类使,被用来当做类构造器的函数就叫构造函数。
一、特点
1、定义时,函数名首字母大写
为了与普通函数区分开,建议首字母大写。2、定义时,函数内部使用this定义属性或者方法。3、定义时,默认不用return返回值构造函数是不需要用return显式返回值的,默认会返回this,也就是新的实例对象。当然,也可以用return语句,返回值会根据return值的类型而有所不同。如果return的是五种简单数据类型:String,Number,Boolean,Null,Undefined,则忽视return值,依然返回this对象。如果return的是Object,那么不再返回this对象,而是返回return语句的返回值。4、调用时,用new关键字默认返回一个新对象实例,这个新对象具有构造函数定义的公有属性(特权属性)和公有方法(特权方法)。二、基本模型
//定义function Person(name,age){ this.age = age; this.getAge = function(){ console.log('实例方法,我的年龄是:'+ this.age); }}//调用var p1 = new Person('Camille',25);p1.getAge();//实例方法,我的年龄是:25。
等同于
function Person(name,age){ var obj = new Object(); obj.age = age; obj.getAge = function(){ console.log('实例方法,我的年龄是:'+ obj.age); } return obj;}var p1 = Person("Camille",25);p1.getAge();
三、深入分析
function Person(name,age){ //私有成员属性 var name = 'Camille'; var sex = 'female'; var self = this; //私有成员方法 var getName = function(){ console.log('我的名字是:'+ self.name); } //公有属性(特权属性) this.age = age; //公有方法(特权方法) this.getAge = function(){ console.log('实例方法,我的年龄是:'+ this.age); }}Person.name = 'HouYi';Person.getName2 = function(){ console.log('我的名字2是:'+ this.name);}/** * 测试Person调用 * 私有成员和公有成员都不能访问 * 可以访问新定义的属性或方法 */console.log(Person.sex);//undefined,无法访问私有成员。console.log(Person.name);//Person,也不是undefined,为啥不是HouYi,留个悬念。Person.getName();//Person.getName is not a function,无法访问私有成员。console.log(Person.age);//undefined,无法访问公有成员。Person.getAge();//Person.getAge is not a function,无法访问公有成员。Person.getName2();//我的名字2是:Person,可以访问新定义的getName2()/** * 测试实例调用 * 不能访问私有成员 * 可以访问公有成员 * 不能访问新定义的属性或方法 */var p1 = new Person('Camille',25);console.log(p1.name);//undefined,无法访问私有成员。console.log(p1.constructor.name);//Person,为啥不是HouYi,你猜。p1.getName();//p1.getName is not a function,无法访问私有成员。p1.constructor.getName();//p1.constructor.getName is not a function,无法访问私有成员。p1.getAge();//实例方法,我的年龄是:25,可以访问公有成员。p1.getName2();//p1.getName2 is not a function,不能访问新定义的getName2()
四、一些疑惑
/** * 1、Func()和new Func()的区别 * Func()是普通函数,指向window;new Func()是构造的新函数,指向当前函数的实例。 */function Func(n){ var name = n || 'Camille'; console.log(name);}var f1 = Func();//Camilleconsole.log(f1); var f2 = new Func();//Camilleconsole.log(f2); /** * 2、new Func和new Func()的区别 * new运算符本身就可以执行构造函数,所以后面的构造函数可以带括号,也可以不带括号。 * 都是Func类的实例化,是等价的,只不过后者可以向函数传参数。 */var f3 = new Func;//Camilleconsole.log(f3);var f4 = new Func('Houyi');//Houyiconsole.log(f4);
/** * 3、return getName和return getName()的区别 * 前者是引用,没有立即执行;后者会立即执行函数。 */function func(){ var name = 'Camille'; function getName(){ console.log('我的名字是:'+ name); } return getName;//只是一个变量,引用,没有立即执行}func()();//我的名字是:Camillefunction func(){ var name = 'Camille'; function getName(){ console.log('我的名字是:'+ name); } return getName();//立即执行了}func();//我的名字是:Camille
/** * 4、func()和return func()的区别 * 前者没有保存最后一次循环的值,后者保存了。 */var i = 0;function func(){ i++; if(i < 10){ func(); }else{ return i; }}var temp = func();console.log(temp); //undefinedvar i = 0;function func(){ i++; if(i < 10){ return func(); }else{ return i; }}var temp = func();console.log(temp);//10
五、new的用途
new的作用,就是执行构造函数,返回一个实例对象。
1、使用new命令时,它后面的函数调用就不是正常的调用,而是依次执行下面的步骤:
- 创建一个空对象,作为将要返回的对象实例
- 将这个空对象的原型,指向构造函数的prototype属性
- 将这个空对象赋值给函数内部的this关键字
- 开始执行构造函数内部的代码
function _new(/* 构造函数 */ constructor, /* 构造函数参数 */ param1) { // 将 arguments 对象转为数组 var args = [].slice.call(arguments); // 取出构造函数 var constructor = args.shift(); // 创建一个空对象,继承构造函数的 prototype 属性 var context = Object.create(constructor.prototype); // 执行构造函数 var result = constructor.apply(context, args); // 如果返回结果是对象,就直接返回,则返回 context 对象 return (typeof result === 'object' && result != null) ? result : context;}// 实例var p1 = _new(Person, 'Camille', 28);
2、为了保证构造函数必须与new命令一起使用,下面提供两种方案告诉开发者不要忘了new。
A、在构造函数内部使用严格模式,即第一行加上use strict。function Person(age){ 'use strict'; this.age = age; this.getAge = function(){ console.log('实例方法,我的年龄是:'+ this.age); }}Person(25);// TypeError: Cannot set property 'age' of undefined
上面代码的Person为构造函数,use strict命令保证了该函数在严格模式下运行。由于在严格模式中,函数内部的this不能指向全局对象,默认等于undefined,导致不加new调用会报错,因为JavaScript 不允许对undefined添加属性。
B、在构造函数内部判断是否使用new命令,如果发现没有使用,则直接返回一个实例对象。function Person(age){ if (!(this instanceof Person)) { return new Person(age); }/* if (!new.target) { throw new Error('请使用new命令调用!'); }*/ this.age = age; this.getAge = function(){ console.log('实例方法,我的年龄是:'+ this.age); }}Person(25).getAge() // 实例方法,我的年龄是:25//(new Person(25)).getAge() // 实例方法,我的年龄是:25