javascript面向对象系列———构造函数
构造函数
一般地,javascript使用构造函数和原型对象来进行面向对象编程,它们的表现与其他面向对象编程语言中的类相似又不同,本文将详细介绍如何用构造函数。
构造函数是用new创建对象时调用的函数,与普通唯一的区别是构造函数名应该首字母大写
function Person(){ this.age = 30; } var person1 = new Person(); console.log(person1.age);//30
根据需要,构造函数可以接受参数
function Person(age){ this.age = age; } var person1 = new Person(30); console.log(person1.age);//30
如果没有参数,可以省略括号
function Person(){ this.age = 30; } //等价于var person1 = new Person() var person1 = new Person; console.log(person1.age);//30
如果忘记使用new操作符,则this将代表全局对象window
function Person(){ this.age = 30; } var person1 = Person(); //Uncaught TypeError: Cannot read property 'age' of undefined console.log(person1.age);
instanceof
instanceof操作符可以用来鉴别对象的类型
function Person(){ // } var person1 = new Person; console.log(person1 instanceof Person);//true
constructor
每个对象在创建时都自动拥有一个构造函数属性constructor,其中包含了一个指向其构造函数的引用。而这个constructor属性实际上继承自原型对象,而constructor也是原型对象唯一的自有属性
function Person(){ // } var person1 = new Person; console.log(person1.constructor === Person);//true console.log(person1.__proto__.constructor === Person);//true
以下是person1的内部属性,发现constructor是继承属性
虽然对象实例及其构造函数之间存在这样的关系,但是还是建议使用instanceof来检查对象类型。这是因为构造函数属性可以被覆盖,并不一定完全准确
function Person(){ // } var person1 = new Person; Person.prototype.constructor = 123; console.log(person1.constructor);//123 console.log(person1.__proto__.constructor);//123
返回值
函数中的return语句用来返回函数调用后的返回值,而new构造函数的返回值有点特殊
如果构造函数使用return语句但没有指定返回值,或者返回一个原始值,那么这时将忽略返回值,同时使用这个新对象作为调用结果
function fn(){ this.a = 2; return; } var test = new fn(); console.log(test);//{a:2}
如果构造函数显式地使用return语句返回一个对象,那么调用表达式的值就是这个对象
var obj = {a:1}; function fn(){ this.a = 2; return obj; } var test = new fn(); console.log(test);//{a:1}
所以,针对丢失new的构造函数的解决办法是在构造函数内部使用instanceof判断是否使用new命令,如果发现没有使用,则直接使用return语句返回一个实例对象
function Person(){ if(!(this instanceof Person)){ return new Person(); } this.age = 30; } var person1 = Person(); console.log(person1.age);//30 var person2 = new Person(); console.log(person2.age);//30
使用构造函数的好处在于所有用同一个构造函数创建的对象都具有同样的属性和方法
function Person(name){ this.name = name; this.sayName = function(){ console.log(this.name); } } var person1 = new Person('bai'); var person2 = new Person('hu'); person1.sayName();//'bai'
构造函数允许给对象配置同样的属性,但是构造函数并没有消除代码冗余。使用构造函数的主要问题是每个方法都要在每个实例上重新创建一遍。在上面的例子中,每一个对象都有自己的sayName()方法。这意味着如果有100个对象实例,就有100个函数做相同的事情,只是使用的数据不同
function Person(name){ this.name = name; this.sayName = function(){ console.log(this.name); } } var person1 = new Person('bai'); var person2 = new Person('hu'); console.log(person1.sayName === person2.sayName);//false
可以通过把函数定义转换到构造函数外部来解决问题
function Person(name){ this.name = name; this.sayName = sayName; } function sayName(){ console.log(this.name); } var person1 = new Person('bai'); var person2 = new Person('hu'); console.log(person1.sayName === person2.sayName);//true
但是,在全局作用域中定义的函数实际上只能被某个对象调用,这让全局作用域有点名不副实。而且,如果对象需要定义很多方法,就要定义很多全局函数,严重污染全局空间,这个自定义的引用类型没有封装性可言了
时间是一个好东西,记录的是爱你的证据
没有终点,没有彼岸,坚持就好,愿岁月如初