Bootstrap

JS中的类与对象

面向对象是使用最广泛的一种编程范式,最具代表性的面向对象语言就是Java和C++,在它们的理念中,面向对象的三大特性:封装,继承,多态。类,对象,公有/私有方法/属性,各种继承就是在语法层面实现这些理念的工具。这种面向对象的理念取得了巨大的成功,以至于我们说到面向对象时,认为这是面向对象的标准。

其实以JS为代表的面向对象理念跟它们完全不同,JS才可以说是真正的面向对象语言,因为在JS中一切皆为对象。在Java/C++语言中,面向对象的最基础的两个概念是对象通过类来抽象事物,通过对象来模拟具体事物。

类是静态的,类定义后,就是变成了一个"印刷模板",属性和方法都不能再变动,通过"印刷模板" 产生的对象,属性和方法也不能变动。要添加新的属性和方法,得产生新的"模板"。

在JS中没有类的概念,只有一个行为类似于"类"的对象,就是构造器函数。因为通过new表达式它也可以创建对象。但它本质上不是类,它也无法表达类的含义。

JS中的对象的属性和方法可以任意添加,删除,完全是动态化。构造器函数也是如此,因为它也是一个对象。那么从这个特性上理解就与类的概念相隔十万八千里了。

所以不要把Java/C++这类面向对象的概念往JS上套,这样很容易绕晕。要明白JS的核心一切皆对象。

通过构造器函数创建对象

构造器函数

它是JS中的"类",可以批量产生对象的函数,如下代码:

function Foo(){
  ...
}

var a = new Foo();

函数Foo的行为与类很类似,通过new表达式产生一个对象a。如果通过这种表面的方式去理解,可以认为它是"类"。

但是Foo函数它也是对象,只是通过new表达式,产生一个新的对象a,所以特殊的是new表达式,它在背后做了些事情,让函数Foo像类

new表达式

函数Foo通过new表达式来调用,会执行下面的操作:

  • 创建一个全新的对象。
  • 这个新对象会被执行Prototype链接,与构造器函数的原型相连。
  • 这个新对象会绑定到函数调用的this。
  • 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。

函数本身就是对象,通过new表达式调用后,函数调用变成"构造函数调用"。这个函数也变成了构造器函数。


new表达式产生的新的对象其实是与Foo对象产生了关联,并且是通过原型对象产生关联**,新对象的原型属性指向了Foo的原型对象。

所以所谓的"类"创建"对象",其实只是改变各自的原型属性的指向,产生一个链接,从而建立关系。

(这里要注意了构造器函数产生的对象,通过 proto([[Prototype]])属性访问原型对象。
构造器函数对象通过Prototype属性访问原型)

构造器函数创建对象,如下代码:

function Foo(){
}

var a = new Foo();
//输出true
console.log(Object.getPrototypeOf(a) === Foo.prototype);
//输出true
console.log(a.__proto__ == Foo.prototype);

相互之间的链接

function Foo(name,age){
  this.name = name;
  this.age = age;
};

var a = new Foo("hhh",32);
//a与Foo的原型相同
console.log(Object.getPrototypeOf(a) === Foo.prototype);//输出为true
//a是否是Foo的实例
console.log(a instanceof Foo);//输出为true
//a的构造函数
console.log(a.constructor);//输出为[Function: Foo]

**通过console.log(Object.getPrototypeOf(a) === Foo.prototype)可以知道a的原型对象与Foo的原型对象相同,这就是它们之间的联系。**后面两条语句,从表面上看就是对象与类的关系即a 是Foo的实例,a的构造函数为Foo。

instanceofconstructor

instanceof操作符是判断一个对象是否另一个对象的实例,constructor属性指向对象的构造器,它们是检查对象间是否有建立关系。

instanceof操作符判断a的整条原型链中是否有指向Foo.prototype的对象,而constructor也并非是永远指向Foo。如果把这两个属性的指向一换,那建立的"类"和"对象"的关系就没了。在Jave/C++这种行为是完全被禁止。如下代码:

function Foo(name,age){
  this.name = name;
  this.age = age;
};

var a = new Foo("hhh",32);
//将a的原型设置为空对象(构造器函数产生的对象a要用__proto__访问原型)
a.__proto__ = {};
console.log(Object.getPrototypeOf(a) === Foo.prototype);//输出为false
//a是否是Foo的实例
console.log(a instanceof Foo);//输出为false
//a的构造函数
console.log(a.constructor);//输出为[Function: object]

加入语句a.__proto__={}即将a的原型对象设置为空,切断了a与Foo的关联。

那么结果就不同了 instanceof返回false, a.constructor指向object,而object并非a的构造函数。

function person(name,age) {
    this.name = 'mmm';
    this.age = 18;
    this.msg = "123456";
}

let p = new person('cc',19);
console.log("对象p的构造对象:"+p.__proto__.constructor);


console.log("person是否是p的原型:"+person.isPrototypeOf(p));
console.log("person的原型是否p的原型:"+person.prototype.isPrototypeOf(p));

;