构造函数 和原型
以前的创建对象的三种方式:
1.对象字面量
2.new object()
3.自定义构造函数
new 在执行时会做的四件事:
1.在内存中创建一个空的内存对象
2.让this指向这个对象
3.执行构造函数中的代码,给这个新对象添加属性和方法
4.返回这个新对象(不需要return)
function star(uname,age){
this.uname=uname;
this.age=age;
this.sing=function(){
}
}
var ldh=new star();
star.sex='男'; 这个就是静态成员
静态成员和实例成员:
实例成员:
就是构造函数内部通过this添加的成员
实例成员只能通过实例化的对象来访问。
ldh.uname;
静态成员:
在构造函数本身上添加的成员
静态成员只能通过构造函数来访问
star.sex;
构造函数存在浪费内存的问题:因为构造函数中的方法属于复杂数据类型,每一个方法都需要开辟一个新的内存空间。
构造函数原型 prototype
构造函数通过原型分配的函数是所有对象所共享的,每一个构造函数都又一个原型对象。
我们可以把那些不变的方法,直接定义在prototype 对象上,这样所有对象的实例就可以共享这些方法。所以写法变为:
function star(uname,age){
this.uname=uname;
this.age=age;
}
star.prototype.sing=function(){}
一般情况下,我们公共属性定义到构造函数里面,公共的方法我们都放到原型对象身上。
对象身上系统自己添加一个_proto_属性,指向我们构造函数的原型对象。
代码方法的找寻:首先先看实例化对象上是否有这个方法,如果有就执行,如果没有因为_proto_的存在就去构造函数原型对象prototype身上去查找方法。
constructor构造函数:
对象原型(__proto__)和构造函数(prototype)原型对象里面都有一个属性 constructor 属性,constructor我们称为构造函数,因为它指回构造函数本身。
constructor主要用于记录该对象引用哪个构造函数,它可以让原型对象重新指向原来的构造函数。
小问题:当我们给原型对象赋值时:
star.protopyte={
sing:function(){},
movic:function(){}
}
当我们这样操作时,和上面的star.prototype.sing=function(){}大不相同
第二个操作是给star的原型对象添加属性,所以原型对象中默认的constructor不会被覆盖。第一个操作是重新创建了一个对象然后将star的原型对象全部给覆盖掉了,所以constructor属性也没有了,当然它就不能指回原来的构造函数。所以我们就需要进行手动指回:
star.protopyte={
constructor:star, //手动指回star构造函数
sing:function(){},
movic:function(){}
}
总结:每一个构造函数中都存在一个原型对象prototype,构造函数可以通过prototype指向它的原型对象,然后在原型对象中有一个constructor属性可以指回构造函数,构造函数实例化出的对象中
包含了一个原型对象__proto__它里面也有一个constructor属性指向prototype。
成员的查找机制:
先在自身上(ldh)找有没有这个成员,有就返回 ,没有就去prototype上找。如果还没有就去Object上去找。
原型对象中this指向
1.在构造函数中,里面的this指向的是对象实例
2.原型对象函数中(prototype)里面的this 指向的是 对象实例
this只有被调用的时候才知道指向的是谁
可以通过原型对象扩展内置对象:
//原型对象的应用,扩展内置对象方法
Array.prototype.sum = function() {
var sum = 0;
for (var i = 0; i < this.length; i++) {
sum += this[i];
}
return sum;
}
var arr = [1, 2, 3];
console.log(arr.sum());
继承:
call() 调用这个函数,并且修改函数运行时的this指向
fun.call(thisArg,arg1,arg2) thisArg:当前调用函数this的指向对象,arg1,arg2:传递的其他参数
function fu(){}
var o={}
fu.call(o)这样fu这个函数this指向的对象就改为了o这个对象
利用父构造函数继承属性:
function Father(uname ,age){
this.uname=uname;
this.age=age;
}
function Son(uname ,age){
Father.call(this,uname,age) //这个this指向的是son的实例对象
}
var son =new Son('ldh',18);这样子构造函数就可以继承父构造函数的属性
如果属性或者方法在原型对象上,Son就不会得到。Father.call(this,uname,age)这个得到的只是父亲的构造函数
利用原型对象继承方法:
Son.prototype=Father.prototype 不行,因为相当于把地址值赋值给了Son.prototype 如果儿子的原型对象发生改变那么父亲的原型对象也会发生改变
Son.prototype =new Father();
通过实例对象的__proto__可以得到Father原型对象中的方法,这时候Son.prototype 被Father实例覆盖了 所以constructor指向的是Father构造函数。
Son.prototype.constructor=Son; 让Son原型对象指回Son的构造函数
类的本质:
类的本质其实还是一个函数,也可以理解为构造函数的另一种写法。