构造函数详解
- 铺垫:面向对象编程
- 一、构造函数是什么?
- 二、构造函数的作用?
- 三、构造函数的执行过程?
- 四、构造函数的返回值?
- 五、构造函数为什么要用new关键字调用?
- 六、构造函数的实例成员和静态成员?
- 七、内置构造函数?
铺垫:面向对象编程
1、面向对象编程
的第一步,就是要生成对象
;
2、例如典型的面向对象编程语言C++、Java,存在“类”(class)这个概念:“类”就是“对象”的模板
,“对象”就是“类”的实例
;
3、在js语言的对象体系中,不是基于“类”的,而是基于构造函数(constructor)
和原型链(prototype)
的;
4、“对象”是单个实物的抽象,通常需要一个模板,表示某一类实物的共同特征,然后“对象”根据这个模板生成。
5、js语言中使用构造函数(constructor)作为一个生成对象的模板,可以以此生成多个对象,每个对象都有相同的结构
。
6、举例:生产10000辆颜色不同的汽车,就会先造一个汽车模子,然后再按照这个模子造出汽车基本雏形。那这个例子里面的汽车模子就是构造函数,每一辆汽车就是实例,构造函数生成的实例都有相同的结构,颜色可能有所不同。
一、构造函数是什么?
在js中,任何用new关键字来调用的函数,都叫做构造函数,一般首字母大写。(强调new关键字,见目录五)
/* 构造函数 */function Person (name, age) {this.name = name // 属性、方法前必须加this,this表示当前运行时的对象this.age = agethis.say = function (word) {console.log('我是人')return word}}/* 实例对象 */var per = new Person('Jack', 16)console.log(per) // Person {name: 'Jack', age: 10, say: f}console.log(per.constructor) // f Person() {} // 构造函数原型console.log(per.name) // Jackconsole.log(per.age) // 10console.log(per.say('我是高中生')) // 我是高中生
二、构造函数的作用?
1、使用对象字面量创建一系列同一类型的对象时,这些对象可能具有一些相似的特征(属性)和行为(行为),降低代码冗余,提高代码复用率。
2、构造新对象,设置对象的属性和方法。
三、构造函数的执行过程?
示例代码
function Person(name, gender, hobby) {this.name = name;this.gender = gender;this.hobby = hobby;this.age = 6;} var p1 = new Person('Jack', 'male', 'basketball'); // 创建一个新的内存 #f1 var p2 = new Person('Mary', 'female', 'dancing'); // 创建一个新的内存 #f2 var p3 = new Person('Jane', 'female', 'singing'); // 创建一个新的内存 #f3
代码分析:构造函数执行过程
1、一开始,Person函数还不叫作构造函数,当使用 new
关键字调用Person函数时,Person函数才叫做 构造函数
。
2、以 new
关键字调用时,会创建一个新的 内存空间#f1
,标记为 Person 的 实例
;
2、函数体内部的 this
指向该内存空间#f1
;
3、执行函数体内的代码:给 this
添加属性,就相当于给实例添加属性name、gender、hobby、age;
4、默认返回this
:就相当于默认返回了该内存空间,也就是上图中的 #f1
等。此时,#f1
的内存空间被变量p1
所接受。也就是说 p1
这个变量,保存的内存地址就是 #f1
,同时被标记为 Person 的实例。
四、构造函数的返回值?
1、没有return,默认返回this – 具体原因见目录三:执行过程
function Person1 (name) {this.name = name}var p1 = new Person1('Tom')console.log(p1) // Person1 {name: 'Tom'}
2、return 基本数据类型,最终返回this
function Person2 (name) {this.name = namereturn 'zhangsan'}var p2 = new Person2('Jack')console.log(p2) // Person2 {name: 'Jack'}
3、return 复杂数据类型(对象),返回该that对象,this对象被丢失
function Person3 (name) {this.name = namevar that = [1, 2, 3]return that}var p3 = new Person3('Jack')console.log(p3) // [1, 2, 3]
五、构造函数为什么要用new关键字调用?
1、使用new关键字调用this对象指向构造函数生成的对象实例
function Person (name) {this.name = namethis.say = function () {return `I am ${this.name}`}}var p1 = new Person('Jack')console.log(p1.say()) // I am Jack
伪代码:同目录三:执行过程
1、使用new关键字调用Person函数时,Person函数才被称为构造函数;
2、创建一个this变量,该变量指向一个空对象/#f1内存空间,并且该对象继承Person函数的原型;
3、属性喝方法被加入到this引用的对象中;
4、隐式返回this对象(如果没有显性返回其他对象,见目录四构造函数的返回值)
// 伪代码:function Person (name) {// 使用new关键字时,创建this变量,指向空对象var this = {} // 空对象/#f1内存空间// 属性喝方法被加入到this引用的对象中this.name = namethis.say = function () {return `I am ${this.name}`}// 返回this对象return this}
2、直接调用:this对象指向window,不会默认返回任何对象
var p2 = Person('Tom')console.log(p2) // undefinedconsole.log(window.name) // Tomconsole.log(window.say()) // I am Tom
六、构造函数的实例成员和静态成员?
/* 构造函数 *//* 构造函数中,实例成员就是构造函数内部通过this添加的成员,name、age、say就是实例成员,实例化后可以访问的成员 *//* 通过prototype添加的成员是实例成员,也就是只要是实例化的对象都可以访问到 */function Person(name) {this.name = name // 实例成员this.say = function (word) { // 实例成员return word}}Person.height = '188' // 静态成员 - 在构造函数上添加的成员Person.prototype.weight = '50kg' // 实例成员 - 在构造函数原型链上添加的成员var p1 = new Person('Tom') // 实例化对象Person.age = 20 // 静态成员Person.prototype.married = true // 实例成员/* 静态成员只能通过构造函数进行访问 */console.log(p1.height) // undefinedconsole.log(p1.age) // undefinedconsole.log(Person.height) // '188'console.log(Person.age) // 20/* 实例成员只能通过实例对象进行访问 */console.log(p1.name) // 'Tom'console.log(p1.weight) // '50kg'console.log(p1.married) // trueconsole.log(p1.say('我是大学生')) // '我是大学生'console.log(Person.name) // Personconsole.log(Person.weight) // undefinedconsole.log(Person.married) // undefinedconsole.log(Person.say('我是大学生')) // Uncaught TypeError: Person.say is not a function
七、内置构造函数?
Object、Array、String、Boolean、Number、Date等等
他们都可以使用new关键字生成实例:var xxx = new XXX();