首先要明确的是构造函数也是函数
我经常使用构造函数来创建实例对象,例如对象和数组的实例化可以通过相应的构造函数Object()和Array()完成。
构造函数与普通函数在语法的定义上没有任何区别,主要的区别体现在以下3点。
(1)构造函数的函数名的第一个字母通常会大写。按照惯例,构造函数名称的首字母都是要大写的,非构造函数则以小写字母开头。这是从面向对象编程语言那里借鉴的,有助于在ECMAScript中区分构造函数和普通函数。
(2)在函数体内部使用this关键字,表示要生成的对象实例,构造函数并不会显式地返回任何值,而是默认返回“this”。在下面的代码段中,Person()函数并没有使用return关键字返回任何信息,但是输出的变量person1是一个具有name、age属性值的Person实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <!DOCTYPE html> <html lang= "en" > <head> <meta charset= "UTF-8" > <meta name= "viewport" content= "width=device-width, initial-scale=1.0" > <title>Document</title> </head> <body> <script> //1.定义构造函数 function Person(name,age){ this .age = age; this .name = name; this .sayName = function (){ console.log( this .name); } } // 2.生成实例对象 var person1 = new Person( '小苏' , '18' ); // 3.打印实例对象 console.log(person1); </script> </body> </html> |
显示效果如下
(3)作为构造函数调用时,必须与new操作符配合使用。这一点非常重要,任何函数只要使用new操作符调用就是构造函数,而不使用new操作符调用的函数就是普通函数。
1、构造函数的定义与调用
构造函数又称为自定义函数,以函数的形式为自己的对象类型定义属性和方法。
举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <!DOCTYPE html> <html lang= "en" > <head> <meta charset= "UTF-8" > <meta name= "viewport" content= "width=device-width, initial-scale=1.0" > <title>Document</title> </head> <body> <script> //1.定义构造函数 function Person(name,age){ this .age = age; this .name = name; this .sayName = function (){ console.log( this .name); } } // 2.生成实例对象 var person1 = new Person( '小苏' , '18' ); // 3.调用方法 person1.sayName(); </script> </body> </html> |
2、new关键字的用途
构造函数在执行时,会执行以下4步:
- 通过new关键字(操作符)创建实例对象,会在内存中创建一个新的地址。
- 为构造函数中的this确定指向,指向实例的本身。
- 执行构造函数代码,为实例添加属性。
- 返回这个新创建的对象。
以前面生成person1实例的代码为例。
第一步:为person1实例在内存中创建一个新的地址。
第二步:确定person1实例的this指向,指向person本身。
第三步:为person1实例添加name、age和sayName属性,其中sayName属性值是一个函数。
第四步:返回这个person1实例。
举个实例
1 | var person1 = new Person( "Bob" , 18); |
这行代码,等价于
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // 演示 new 的功能 function Person(name,age) { // 1.创建一个新对象 var instance = new Object(); // 2.将函数内部的 this 指向了这个新对象 this = instance; // 3.执行构造函数内部的代码 this .name = name; this .age = age; this .sayName = function () { console.log( this .name); }; // 4.将新对象作为返回值 return instance; } |
这就意味着,将instance 赋值给 person1,即person1 = instance。实际上又是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // 演示 new 的功能 function Person(name,age) { // 1.创建一个新对象 var person1 = new Object(); // 2.将函数内部的 this 指向了这个新对象 this = person1; // 3.执行构造函数内部的代码 this .name = name; this .age = age; this .sayName = function () { console.log( this .name); }; // 4.将新对象作为返回值 return person1; } |
3、构造函数的问题:浪费内存
基本事实:不同实例中的sayName属性是不同的。举个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | <!DOCTYPE html> <html lang= "en" > <head> <meta charset= "UTF-8" > <meta name= "viewport" content= "width=device-width, initial-scale=1.0" > <title>Document</title> </head> <body> <script> // 1.自定义构造函数 function Person(name,age) { this .name = name; this .age = age; // this 内部的 type 属性值是不变的 this .type = "human" ; // 每个对象的 sayName 方法也是一样的 this .sayName = function () { console.log( this .name); }; } var person1 = new Person( 'Bob' ,18); var person2 = new Person( 'Mike' ,20); // 2,判断各自的方法是否是同一个函数 console.log(person1.sayName === person2.sayName); // false </script> </body> </html> |
解释:通过console.log(person1.sayName === person2.sayName)方法可判断两个函数是否是是同一个,显然这不是同一个函数,那么两个函数就占用了两个内存空间,就会造成内存的浪费。
那如何解决函数占用内存的办法呢?答案是将对象内部函数提取出来作为公共函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <!DOCTYPE html> <html lang= "en" > <head> <meta charset= "UTF-8" > <meta name= "viewport" content= "width=device-width, initial-scale=1.0" > <title>Document</title> </head> <body> <script> // 解决方法1: 将公共的函数提取到构造函数之外 function sayName() { console.log( this .name); } function sayAge() { console.log( this .age); } //1.自定义对象 function Person(name,age) { this .name = name; this .age = age; // this 内部的 type 属性值是不变的 this .type = "human" ; // 每个对象的 sayName 方法也是一样的 this .sayName = sayName; this .sayAge = sayAge; } //2.实例化对象 var person1 = new Person( 'Bob' ,18); var person2 = new Person( 'Mike' ,20); console.log(person1.sayName === person2.sayName); // true </script> </body> </html> |
此时,也存在一个问题,如果有多个公共函数,需要在外部创建多个函数,可能会造成命名冲突
解决办法,将多个公共的函数封装到一个对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | <!DOCTYPE html> <html lang= "en" > <head> <meta charset= "UTF-8" > <meta name= "viewport" content= "width=device-width, initial-scale=1.0" > <title>Document</title> </head> <body> <script> // 3.将多个公共的函数封装到一个对象 var fns = { sayName : function () { console.log( this .name); }, sayAge : function () { console.log( this .age); } }; // 1.自定义对象 function Person(name,age) { this .name = name; this .age = age; this .type = "human" ; this .sayName = fns.sayName; this .sayAge = fns.sayAge; } // 2.生成对象实例 var person1 = new Person( "Bob" ,18); var person2 = new Person( "Mike" ,20); // person1.sayName(); console.log(person1.sayName === person2.sayName); console.log(person1.sayAge === person2.sayAge); </script> </body> </html> |
解释:将多个函数封装成一个对象即可解决函数名可能冲突的问题。
通过这种设置全局访问的函数,这样就可以被所有实例访问到,而不用重复创建。
但是这样也会存在一个问题,如果为一个对象添加的所有函数都处理成全局函数,这样并无法完成对一个自定义类型对象的属性封装(言外之意。指的是全局函数只可以封装函数,但无法封装属性,因为属性封装在函数外面,要定义为全局变量,才可以去调用,这反而会污染全局变量),因此这不是一个好的解决办法。
此时,便引用原型的概念,使用原型概念可以很好解决这个问题,可以很好解决这个问题。
总结
本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注自学编程网的更多内容!
- 本文固定链接: https://zxbcw.cn/post/220238/
- 转载请注明:必须在正文中标注并保留原文链接
- QQ群: PHP高手阵营官方总群(344148542)
- QQ群: Yii2.0开发(304864863)