JavaScript面向对象编程——构造函数
写在开头,本篇是学习阮一峰的JavaScript教材后,为加深理解和记忆所写的博客,本文中基本全部引用阮一峰的JavaScript教材。详情请看https://wangdoc.com/javascript/oop/index.html
-
构造函数
面向对象第一步就是要生成对象,对象的生成需要一个模板,在Java中这个模板是“类”,但JavaScript中,面向对象的模板是构造函数和原型链。
构造函数作为对象的模板,用来生成实例对象的函数,描述了实例对象的基本结构,一个构造函数可以生成多个实例对象,这些实例对象都有相同的结构。
构造函数也是一个普通的函数,但是有自己的特征和用法。var Animal=function(){ this.name='老虎'; };
上面代码中,animal就是构造函数。为了与普通函数有区别,构造函数第一个字幕通常大写。
构造函数有两个特点-
函数内部使用了this关键字,代表了所要生成的对象实例。
-
在生成对象的时候,必须使用new命令。
-
-
new命令
new命令的作用,执行构造函数时返回一个实例对象
var Animal=function(){ this.name='老虎'; } var a=new Animal(); a.name;//老虎
以上代码中,构造函数Animal生成一个实例对象 保存在a中。这个对象从构造函数Animal中得到了name属性。new命令执行过程中,构造函数内部的this,就代表了新生成的实例对象。
使用new命令时,也可以接受参数var Animal=function(p){ this.name=p; }; var a=new Animal('狮子');
实例对象生成的写法
//推荐写法 var a=new Animal(); //不推荐的写法 var a=new Animal;
注:如果忘记使用new命令,此时的构造函数就变成普通函数,不会生成实例对象。此时函数内的this就代表全局对象。
var Animal = function (){ this.name = '老虎'; }; var a= Animal(); a // undefined name // 老虎
为了避免忘记使用new命令,可以在构造函数内部加上 use strict,利用严格模式,直接调用时就会报错。
另一个解决办法是,在内部判断是否使用new命令,如果没有使用,则直接返回一个实例对象function Fubar(foo,bar){ if(!(this instanceof Fubar)){ return new Fubar(foo,bar); } this._foo=foo; this._bar=bar; Fubar(1, 2)._foo // 1 (new Fubar(1, 2))._foo // 1
-
new命令原理
使用new命令时,它后面的函数依次执行下面的步骤。
-
创建一个空对象,作为将要返回的对象实例。
-
将这个空对象的原型,指向构造函数的prototype属性。
-
将这个空对象赋值给函数内部的this关键字。
-
开始执行构造函数内部的代码。
之所以构造函数叫构造函数,意思是此函数的目的是操作一个空对象(this对象),将其构造成需要的样子,如果构造函数内部有return语句,而且后面跟着一个对象,new 命令就会返回return语句指定的对象;否则,就不会管return语句,返回this对象
-
-
原型
由于构造函数生成新对象,再通过为构造函数为实例定义属性,虽然方便,但有弊端,一个构造函数的多个实例之间无法共享属性,造成资源的浪费,因此可以利用原型,将实例中属性放在原型上,不仅节省了空间,还体现了实例对象之间的联系。function Animal(name) { this.name = name; } Animal.prototype.color = 'white'; var cat1 = new Animal('大毛'); var cat2 = new Animal('二毛'); cat1.color // 'white' cat2.color // 'white'
注:在定义原型对象的属性时,当实例对象本身没有某个属性或方法的时候,他会到原型对象上去寻找该属性或方法。如果实例中有某个属性或方法,就不会去原型对象上寻找。
-
原型链
JavaScript定义了,每个对象都有自己的原型对象,一方面,任何一个对象,都可以充当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。因此,就会形成一个“原型链”(prototype chain):对象到原型,再到原型的原型……,最终可以到Object构造函数的prototype属性,也就是所有对象都继承了Object.prototype的属性,而Object.prototype的原型是null。 -
constructor 属性
prototype对象有一个constructor属性,默认指向prototype对象所在的构造函数,因此可以根据此原理,判断某个实例对象是由哪一个构造函数产生的。function F() {}; var f = new F(); f.constructor === F // true f.constructor === RegExp // false
上面代码中f就是F的实例对象,不是RegExp的。
同时constructor属性表示原型对象与构造函数之间的关联关系,如果修改了原型对象,一般会同时修改constructor属性,防止引用的时候出错。
// 坏的写法 C.prototype = { method1: function (...) { ... }, // ... }; // 好的写法 C.prototype = { constructor: C, method1: function (...) { ... }, // ... }; // 更好的写法 C.prototype.method1 = function (...) { ... }; 会出现的问题 function Person(name) { this.name = name; } Person.prototype.constructor === Person // true Person.prototype = { method: function () {} }; Person.prototype.constructor === Person // false Person.prototype.constructor === Object // true