构造函数
1.什么是构造函数?
a:构造函数的首字母必须大写,用来区分于普通函数
b:内部使用的this对象,来指向即将要生成的实例对象
c:使用New来创建实例对象
1 function Person() { 2 //默认隐含的操作,把刚才用new新创建出来的对象赋值给this 3 this.name = "尼古拉斯凯奇"; 4 this.age = 50; 5 this.sayHello = function () { 6 console.log("Hey man"); 7 } 8 9 return null; //只要返回的不是object类型的值,其他随便写都默认返回新创建出来的对象 10 } 11 12 var p = new Person();//实例化 新创建的对象 P 对象是构造函数的一个实例,创建这个对象的过程叫做实例化 此处对象p就是Person构造函数的一个实例。 13 console.log(p); 14 p.sayHello();
2.构造函数的执行过程
1.使用new关键字创建对象
2.调用构造函数,把新创建出来的对象赋值给构造函数内的this
3.在构造函数内使用this为新创建出来的对象新增成员
4.默认返回新创建的这个对象 (普通的函数,如果不写返回语句,会返回undefined)
1 //对象是无序的键值对儿的集合 在Javascript中,每一个函数实际上都是一个函数对象,它们的类型是function 详细介绍见 js-基本数据类型 2 function Animal(name, type, barkWay) { 3 this.name = name; 4 this.type = type; 5 this.bark = barkWay; 6 } 7 8 //注意:如果像使用正常的函数一样使用构造函数 9 //构造函数中的this将不再指向新创建出来的对象(因为根本就没有创建对象) 10 //构造函数中的this这个时候指向的就是window全局对象 11 //当使用this给对象添加成员的时候,全部都添加到了window上 12 13 var cat = new Animal("小花","BSM",function () { // 新创建的对象cat 14 console.log("喵喵喵"); 15 }); 16 17 console.log(cat); 18 console.log(typeof cat);//object
3.构造函数的返回值
1.如果不写返回值,默认返回的是新创建出来的对象 (一般都不会去写这个return语句)
2.如果我们自己写return语句,比如:return的是空值(return;),或者是基本数据类型的值或者null,undefined等,都会默认返回新创建出来的对象
3.如果返回的是object类型的值,将不会返回刚才新创建的对象,取而代之的是return后面的值
4.构造函数存在的问题
1 function studyMethod(){ 2 console.log("我叫"+ this.name +"Good Good Study Day Day Up"); 3 } 4 5 function Student(stuName) { //写一个构造函数,用来创建学生对象 6 this.name = stuName; 7 this.study = studyMethod; 8 //方法* 8*10^20 9 } 10 11 var stu = new Student("高金彪"); 12 stu.study(); 13 14 var stu1 = new Student("李嘉欣"); 15 stu1.study();
每个学生对象的study函数都是同一句代码,那同样的代码在内存里面占两份就不合适。因为封装函数是为了复用,所以这个情况就要考虑封装函数
传统构造函数存在的问题;
如果在构造函数中定义函数,那么每次创建对象,都会重新创建该函数,但是函数内部代码完全相同,这带来的坏处就是占用内存,造成了资源浪费。
传统解决办法
将构造函数内的方法进行提取,放在构造函数外面,在构造函数内部进行引用赋值,那么创建出来的对象都会指向构造函数外面的这个函数,达到共享的目的。
使用这种方式写好的方法中,this指向的就是调用该方法的对象。如果没有对象调用则默认是window调用,即this默认指向window
使用这种方式存在的问题:1.全局变量增多,造成污染 2.代码结构混乱,不易维护 至于解决办法会用到原型,下面详细介绍
原型
1.什么是原型
在构造函数创建出来的时候,系统会默认的帮构造函数创建并关联一个神秘的对象,这个对象就是原型。js中万物皆对象,因此原型也是对象,而且原型默认的是一个空的对象。
“我们创建的每个函数都有一个prototype(原型)属性(除了Function.bind()返回的函数),这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。”
2.原型的作用及如何访问
作用:原型中的属性和方法 可以被使用该构造函数创建出来的对象 使用
如何访问构造函数的原型 :1.通过构造函数访问原型:使用构造函数.prototype, prototype是构造函数的属性,跟对象没有关系。
2.通过对象访问原型; 对象可以通过 .__proto__ 属性 去访问原型.
原型对象在创建出来的时候,会默认的有一个constructor属性 指向对应的构造函数。
按照javascript的说法,function定义的这个Person就是一个Object(对象),而且还是一个很特殊的对象,这个使用function定义的对象与使用new操作符生成的对象之间有一个重要的区别。这个区别就是function定义的对象有一个prototype属性,使用new生成的对象就没有这个prototype属性,我们一般称为普通对象!
1 function Person(name, status) { 2 this.name = name; 3 this.status = status; 4 this.act = function () { 5 console.log("演戏"); 6 }; 7 this.exercise = function () { 8 console.log("就不强身健体,就要保卫祖国"); 9 } 10 } 11 var p = new Person("xyz","single"); 12 13 console.log(Person); 14 console.log(p); 15 console.log(Person.prototype);// 打印出一个空的object对象 16 console.log(p.prototype); //undefined //注意 prototype是构造函数的属性,跟对象没有关系 17 18 //如何给原型对象添加属性和方法? 使用 对象的动态特性 19 20 Person.prototype.exercise = function () { 21 console.log("强身健体,保卫祖国"); 22 } 23 24 p.exercise();
1 function Person() { 2 3 } 4 //1.通过构造函数访问原型 5 // Person.prototype 6 7 Person.prototype.msg = "在不在!"; 8 var p = new Person(); 9 10 //2.通过对象访问原型 11 //__proto__属性的用途 12 //__proto__是一个非标准的属性,通常开发人员调试的时候使用。 13 console.log(p.__proto__); //object 14 15 p.__proto__.sayHello = function () { 16 console.log("你好") 17 } 18 19 p.sayHello(); //你好
3.使用原型解决构造函数的问题
将构造函数中需要创建的函数,放到原型对象中存储,这样就解决了“全局变量污染”和“代码结构混乱”的问题。
这里利用的原理是:构造函数的原型对象中的成员和方法,可以被该构造函数创建出来的对象访问,而且,所有的对象共享该对象。
1 function Student(stuName) { 2 this.name = stuName; 3 // this.sayHello = function () { 4 // console.log("你好我是" + this.name); 5 // } 6 } 7 8 var p = new Student("张三"); 9 var p1 = new Student("李四"); 10 11 Person.prototype.sayHello = function () { 12 console.log("你好我是" + this.name); 13 } 14 15 Person.prototype["sing"] = function () { 16 console.log("一千个伤心的母牛"); 17 } 18 19 p.sayHello(); 20 p1.sayHello(); 21 22 p.sing(); 23 p1.sing(); 24 25
4.原型的使用方法
1 //原型的使用方法 2 //1.利用对象的动态特性给原型对象添加成员 3 //2.直接替换原型对象 4 5 //如果使用第二种方式使用原型,那么会出现如下问题: 6 //在替换原型之前创建的对象的原型 和 在替换原型对象之后的创建的对象的原型 不是同一个! 7 function Person(name, age, gender) { 8 this.name = name; 9 this.age = age; 10 this.gender = gender; 11 } 12 13 Person.prototype.sayHello = function () { 14 console.log("Nice to meet you all"); 15 } 16 17 var p = new Person("刘能", 18, "male"); 18 p.sayHello(); 19 20 //替换了原型对象 21 Person.prototype = { 22 msg : "你猜我在不在" 23 }; 24 25 var p1 = new Person("xzy",18,"male"); 26 27 console.log(p1.msg); //你猜我在不在 28 // p1.sayHello(); //会报错,因为之前的原型对象被替换了,新的原型对象没有该方法。 29 p.sayHello(); //Nice to meet you all
5.使用原型的注意事项
1.使用对象访问属性的时候,会首先在对象自己内部进行查找,如果找到了,就直接使用。如果没有找到,就去原型中查找,找到后使用。如果原型中还没有, 如果是属性,就是Undefined,如果是方法,就报错。
但是使用点语法进行属性赋值的时候,并不会去原型中进行查找
使用点语法赋值的时候,如果对象中不存在该属性,就会给该对象新增该属性,而不会去修改原型中的属性。
2.如果在原型中的属性是引用类型的属性,那么所有的对象共享该属性,并且一个对象修改了该引用类型属性中的成员,其他对象也都会受影响
3.一般情况下不会将属性放到原型对象中,一般情况下原型中只会放需要共享的方法。
1 function Person(){ 2 3 } 4 //给原型对象中添加 name,age属性 5 Person.prototype.name = "张三"; 6 Person.prototype.age = 18; 7 8 var p = new Person(); 9 console.log(p.name); // 张三 10 11 p.name = "李四"; 12 console.log(p.name); //李四 13 14 var p1 = new Person(); 15 console.log(p1.name); //张三 16 17 var x = { //这是一个x对象 18 brand:"laosilaisi", 19 type:"huanying" 20 }; 21 22 Person.prototype.car = x; //给原型对象中添加了一个car属性并且指向了x对象。 23 24 var p = new Person(); 25 26 console.log(p.car.brand); //laosilaisi 27 28 Person.prototype.car = { //原型对象中的car属性指向了一个新的对象。 29 brand:"BYD" 30 }; 31 32 var p1 =new Person(); 33 console.log(p1.car.brand); //BYD
6.构造函数 原型 对象 之间的三角关系
构造函数和原型是成对出现的,构造函数通过 .prototype 属性访问原型。 原型.constructor就访问到了构造函数。
对象是构造函数的一个实例,创建这个对象的过程叫做实例化
对象可以通过 .__proto__ 属性 去访问原型。