js对象–自定义构造函数、原型链、继承、拷贝
js是一门基于对象的语言,对象是一个很重要的知识点,本文是我个人对于js对象的理解。
类(class)—js中构造函数就可以模拟class
类其实是人类对事物进行分类而得出来的类别,比如:老虎,人类,学生,这些都是类,那么 ,在代码中,我们把拥有共同特征的东西,并把这些特征抽取出来设为一个类,在js中,并没有类的概念,因为它不是面向对象语言,但他可以模拟类;
对象(object)
对象其实就是具体的事物,比如:你家的猫,某某某人。
他有自己的特征(属性)和行为(方法);
创建对象
js创建对象有下面三种方式:
- 通过系统构造函数创建:
var obj = new Object();
- 通过字面量的方式创建:
var obj1 = {};
- 通过自定义构造函数创建(Person就是自定义的构造函数):
var obj2 = new Person();
对象使用方法
- 通过.的方式赋值、取值:
var obj.name="小羊";
var name = obj.name;//name="小羊"
- 通过[]的方式赋值、取值:
var obj["name"]="小羊";
var name = obj["name"];//name="小羊"
- 对象在内存中的存储方式:
对象是引用数据类型的具体数据储存在堆里,地址储存在栈里,我们获取的是地址,然后通过地址来取值、赋值,具体如下图:
自定义构造函数
构造函数名一般首字母为大写,便于区分,函数内部通过this来给属性和方法赋值;
通过new来创建实例对象
var Person =function(name,age){
this.name = name;
this.age = age;
this.eat = function(){
console.log(this.name+"在吃饭");
}
};
var per = new Person("小羊",20);
console.log(per.name);//小羊
console.log(per.age);//20
per.eat();//小羊在吃饭
也可以通过.或[]方法给实例对象添加额外的属性和方法:
per.sex = "女";
per.sleep = function(){
console.log(this.name+"在睡觉");
};
console.log(per.sex);//女
per.sleep();//小羊在睡觉
创建实例对象图解
原型和原型链
构造函数中有一个原型(prototype)属性,原型是一个对象,
由于实例对象是根据构造函数创建的,所以实例对象也有一个原型(proto)属性,prototype和proto指向同一个原型对象(内存空间);
目的
为什么需要原型属性呢?—为了节省内存空间
根据构造函数创建多个实例对象时,每个实例对象都会把根据构造函数生成相应的属性和方法放在自己的内存空间中,每个实例对象属性的值是不一样的,但他们的方法都是一样的,比如:我们每个人都吃饭,吃饭这个行为是一样的;所以我们没必要为每个实例对象都复制一份方法,所以就需要原型(prototype)这个属性了,原型是个对象,是引用类型,所以他是prototype里面储存的是地址,这个地址指向存储原型具体数据的空间,那么,我们把相同的属性和方法存储在原型(prototype)中,创建实例对象时就不用为这些属性和方法开辟空间了,这样就节省内存空间实现数据共享;
原型和实例对象、构造函数的关系如图
constructor–构造器指向相应的构造函数
作用:节省内存空间,数据共享
使用:
- 构造函数.prototype.名字=值或方法;
Person.prototype.eat = function(){
console.log(this.name+"在吃饭");
}
- 构造函数.prototype=对象;
Person.prototype={
constructor:Person,
eat:function(){
console.log(this.name+"在吃饭");
}
}
手动添加构造器---constructor;
Student.prototype = new Person();
改变prototype的指向
当把Person的实例对象复制个Student的原型(prototype)会怎样呢?—会使Student的原型(prototype)指向Person的原型(prototype)
代码如下:
var Person=function (name,age) {
this.name=name,
this.age=age;
};
var Studen=function (id, score) {
this.id=id;
this. score= score;
};
Person.prototype.eat=function () {
console.log(this.name+"在吃饭");
};
Studen.prototype.scoreShow=function () {
console.log(this.score);
}
Studen.prototype=new Person("小羊",20);
var stu = new Studen(1,80);
console.log(stu.id);//1
console.log(stu.score);//80
console.log(stu.name);//小羊
console.log(stu.age);//20
stu.eat();//小羊在吃饭
stu.scoreShow();//报错:stu.scoreShow is not a function
关系图:
原型链
因为每个对象都有一个都一个proto属性,原型(prototype)对象也有proto属性指向Objcet的prototype,所以通过proto 属性可以一直追寻下去,直到Objcet.prototype,Objcet.prototype没有proto,Objcet.prototype===null;
改变prototype的指向可使这条原型链无限延长。
注意
- 实例对象中有一个属性叫proto,也是对象,叫原型,不是标准的属性,浏览器使用的(ie8没有改属性)
- 构造函数中有一个属性叫prototype,也是对象,叫原型,是标准的属性,程序员使用 原型中的方法和属性,是可以互相调用的;
- 实例对象先在自身寻找,如果不存在则去原型对象里找;
- 实例对象可以直接访问原型对象
- 构造函数中的this是实例对象===原型对象中的this是实例对象;
继承—子类与父类的关系
现在有下面三个个类:
Person:属性—>name,age;方法—>eat;
Student:属性—>name,age,id,score;方法—>eat,scoreShow;
Personnel:属性—>name,age,jobNumber,score;方法—>eat,job;
Student与Personnel都有Person的属性与方法,除此之外,它们还有自己独特的属性和方法,这种情况下使用继承,就能节省内存空间、解决代码的冗余、数据共享。
作用—节省内存空间、解决代码的冗余、数据共享
实现
因为js不是面向对象语言没有继承,需要模拟继承;
1.原型继承
通过改变原型的指向实现继承。
代码:
var Person=function (name,age) {
this.name=name,
this.age=age;
};
var Studen=function (id, score) {
this.id=id;
this. score= score;
};
Person.prototype.eat=function () {
console.log(this.name+"在吃饭");
};
Studen.prototype=new Person("小羊",20);
//改变原型指向后再进行添加子类共有的方法和属性
Studen.prototype.scoreShow=function () {
console.log(this.score);
};
//实例化对象
var stu = new Studen(1,80);
var stu1 = new Studen(2,90);
//stu=====================
console.log(stu.id);//1
console.log(stu.score);//80
console.log(stu.name);//小羊
console.log(stu.age);//20
stu.eat();//小羊在吃饭
stu.scoreShow();//80
//stu1========================
console.log(stu1.id);//2
console.log(stu1.score);//90
console.log(stu1.name);//小羊
console.log(stu1.age);//20
stu1.eat();//小羊在吃饭
stu1.scoreShow();//90
缺陷:两个不同的Student实例对象的name,age是一样的,需要重新赋值;
借用构造函数
代码:
var Person=function (name,age) {
this.name=name,
this.age=age;
};
var Studen=function (name,age,id, score) {
Person.call(this,name,age)
this.id=id;
this. score= score;
};
Person.prototype.eat=function () {
console.log(this.name+"在吃饭");
};
Studen.prototype.scoreShow=function () {
console.log(this.score);
};
//实例化对象
var stu = new Studen("小羊",20,1,80);
var stu1 = new Studen("小和",21,2,90);
//stu=====================
console.log(stu.id);//1
console.log(stu.score);//80
console.log(stu.name);//小羊
console.log(stu.age);//20
stu.scoreShow();//80
stu.eat();//报错:stu.eat is not a function
//stu1========================
console.log(stu1.id);//2
console.log(stu1.score);//90
console.log(stu1.name);//小和
console.log(stu1.age);//21
stu1.scoreShow();//90
stu1.eat();//报错:stu.eat is not a function
缺陷:无法原型中的公用属性和方法
组合继承
将原型方法和借用函数方法组合起来
var Person=function (name,age) {
this.name=name,
this.age=age;
};
var Studen=function (name,age,id, score) {
Person.call(this,name,age);
this.id=id;
this. score= score;
};
Person.prototype.eat=function () {
console.log(this.name+"在吃饭");
};
Studen.prototype=new Person();
//改变原型指向后再进行添加子类共有的方法和属性
Studen.prototype.scoreShow=function () {
console.log(this.score);
};
//实例化对象
var stu = new Studen("小羊",20,1,80);
var stu1 = new Studen("小和",21,2,90);
//stu=====================
console.log(stu.id);//1
console.log(stu.score);//80
console.log(stu.name);//小羊
console.log(stu.age);//20
stu.scoreShow();//80
stu.eat();//小羊在吃饭
//stu1========================
console.log(stu1.id);//2
console.log(stu1.score);//90
console.log(stu1.name);//小和
console.log(stu1.age);//21
stu1.scoreShow();//90
stu1.eat();//小和在吃饭