对象、类和原型
在JS中,万物皆对象,对象因为你的“关注”而产生。每个对象都有自己的属性特征和行为方法,函数、数组、对象等都可以被视为对象,那么js中的对象又是怎么产生的呢?
其他大型语言里,对象都和“类”这个概念紧密相连,“类”是对象的一个抽象概念,每个对象都是通过个“类”的构造方法创建的。而js中抛弃了“类”的概念,引入另外一个抽象概念——“原型”。js中每个对象都有一个原型,原型也是对象,所以原型也有自己的原型对象,如果一路找上去,最终会找到Object。也就是说js中所有对象都是由Object克隆出来的,Object是所有对象的起始。
创建对象的方法
1.构造函数
let Construct=function(name,age){
this.name=name;
this.age=age;
};
let c1=new Construct("123",123);
通过函数Construct,来创建一个实例对象c1,函数Construct也可称为构造函数。构造函数就是普通函数,只不过通过这个函数new了一个实例对象。在new的过程中通过构造函数的函数体,给对象c1增加了属性。
2.构造函数加原型
let Construct=function(name,age){
this.name=name;
this.age=age;
};
Construct.prototype.fn=function(){
console.log("hello world");
};
let c1=new Construct("123",123);
c1.fn();
在构造函数的基础上,给构造函数的原型(Constructor.prototype)加一个方法fn,将该方法从构造函数的原型(Constructor.prototype)复制到新实例对象c1的原型(c1.__ prototype __ )上。很多人会认为这两个是一个东西,我不这么认为,我理解为每一个对象都有一个自己的原型,下文会详细解释。这样构造函数原型中的方法就会被复制到c1的原型上,c1在自身中找不到fn这个方法,就会去自己的原型:c1.__ prototype __ 上去找。
3.JSON
let c1={
"name":"123",
"age":123,
"fn":function(){
console.log("hello world");
},
};
c1.fn();
JSON是js创建对象的一个简便算法,但也有它的缺点,在需要创建的对象数量很多时就不适用。构造函数创建的c1也有一个原型,这个原型为空,可以通过.__ prototype __ 给它的原型添加属性和方法。
4.class
class Construct{
constructor(name,age){
this.name=name;
this.age=age;
}
fn(){
console.log("hello world");
}
}
let c1=new Construct();
先创建一个Construct的类,在通过new创建一个新的实例对象c1。在Construct这个类中,有一个名为constructor的函数,它可以把里面的属性通过new的过程初始化给c1;还有一个名为fn的方法,这个方法被直接添加到Construct的原型(Construct.prototype)上,在new的过程中又被复制到了c1的原型(c1.__ prototype __)上,所以c1本身没有这个方法,却可以通过原型链调用这个方法。
js的class是es6新增的一个概念,class的提出让我们在创建对象时的逻辑和操作更偏向于面向对象的语言。但它不是一个“真类”,是用函数模拟出来的一个“类”,底层和java等语言还是有很大的差别。
原型链
对象可以通过 .__ proto __ 这个方法找到该对象的原型,有一个特殊的对象——函数,函数通过.prototype找到该函数的原型对象,.prototype这个方法只有函数才可以调用,而找到的是原型对象,所以不能继续用.prototype这个方法。而.__ proto __ 这个方法可以重复调用,通过.__ proto . __ proto __ 来找到最顶层的原型对象Object,这条线就称为原型链。
不管是函数的原型还是实例对象的原型. proto __后都找到Object,也就是{}
原型对象
let Construct=function(name,age){
this.name=name;
this.age=age;
};
Construct.prototype.fn=function(){
console.log("hello world");
};
let c1=new Construct("123",123);
Construct这个构造函数(也可视为对象)在我们声明的时候,就已经产生了一个原型对象
(Construct.prototype),通过原型对象添加的方法给上面加入方法fn,然后我们通过Construct,new一个新对象c1,要理解原型对象,首先我们要理解new的这个过程,new的过程一共分为3步:
- 先通过构造函数Construct来新建一个实例c1,并新建一个c1的原型对象:c1.__ prototype __ (此时,c1和c1.__ prototype __ 都是空的)。
- c1.__ prototype __ = Construct.prototype(把Construct.prototype上的方法fn赋到c1.__ prototype __ 上,此时c1还是空的,c1.__ prototype __ 上有了一个方法fn)
- 通过函数Construct的函数体,对c1进行初始化设置,将属性和属性值赋给c1。
到此三步结束,c1中具有了属性和属性值,c1的原型(c1.__ prototype __ )上有了fn这个方法,函数Construct的原型(Construct.prototype)没有改变,并且每次new对象的时候,都会重复上述的三个动作。可以通过分别查看c1、 c1. __ prototype __、Construct.prototype来验证刚才的证明。
并且我们在使用原型添加方法的时候,如果通过c1. __ prototype __ 来添加的属性和方法,并不会影响Construct.prototype里的内容,也不会影响其他新new出来的实例对象,只会改变c1自己的原型。而通过Construct.prototype去修改的原型,会影响其他新new出来的实例对象,这更好地解释了刚才的想法。