- 用函数代替 class
- 函数本身就是构造函数
- 实例方法用 prototype
- 实例方法查找用 __proto__
- constructor
- 静态方法
- 继承
- 自己实现一个 new
- 自己实现一个 instanceof
- ES6 的 class
- 总结
JS 中面向对象以及 __proto__,ptototype和constructor,说难不难,说简单也总是让人搞糊涂,而这几个概念又都是相关的,所以一起讲了。在讲这个之前我们先来说说类,了解面向对象的朋友应该都知道,如果我要定义一个通用的类型我可以使用类(class)。比如在java中我们可以这样定义一个类:
public class Puppy{
int puppyAge;
public Puppy(age){
puppyAge = age;
}
public void say() {
System.out.println("汪汪汪");
}
}
上述代码我们定义了一个 Puppy 类,这个类有一个属性是 puppyAge,也就是小狗的年龄,然后有一个构造函数 Puppy(),这个构造函数接收一个参数,可以设置小狗的年龄,另外还有一个说话的函数 say。这是一个通用的类,当我们需要一个两岁的小狗实例是直接这样写,这个实例同时具有父类的方法:
Puppy myPuppy = new Puppy( 2 ); myPuppy.say(); // 汪汪汪
但是早期的 JS 没有class关键字啊(以下说 JS 没有class关键字都是指 ES6 之前的 JS,主要帮助大家理解概念),JS 为了支持面向对象,使用了一种比较曲折的方式,这也是导致大家迷惑的地方,其实我们将这种方式跟一般的面向对象类比起来就很清晰了。下面我们来看看 JS 为了支持面向对象需要解决哪些问题,都用了什么曲折的方式来解决。
用函数代替 class
首先 JS 连class关键字都没有,怎么办呢?用函数代替,JS 中最不缺的就是函数,函数不仅能够执行普通功能,还能当class使用。比如我们要用 JS 建一个小猫的类怎么写呢?直接写一个函数就行:
function Puppy() {}
这个函数可以直接用new关键字生成实例:
const myPuppy = new Puppy();
这样我们也有了一个小狗实例,但是我们没有构造函数,不能设置小狗年龄啊。
函数本身就是构造函数
当做类用的函数本身也是一个函数,而且他就是默认的构造函数。我们想让 Puppy 函数能够设置实例的年龄,只要让他接收参数就行了。
function Puppy(age) {
this.puppyAge = age;
}
// 实例化时可以传年龄参数了
const myPuppy = new Puppy(2);
注意上面代码的this,被作为类使用的函数里面this总是指向实例化对象,也就是 myPuppy。这么设计的目的就是让使用者可以通过构造函数给实例对象设置属性,这时候 console 出来看myPuppy.puppyAge 就是 2。
console.log(myPuppy.puppyAge); // 输出是 2
实例方法用 prototype
上面我们实现了类和构造函数,但是类方法呢?Java 版小狗还可以“汪汪汪”叫呢,JS 版怎么办呢?JS 给出的解决方案是给方法添加一个prototype属性,挂载在这上面的方法,在实例化的时候会给到实例对象。我们想要 myPuppy 能说话,就需要往Puppy.prototype添加说话的方法。
Puppy.prototype.say = function() {
console.log("汪汪汪");
}
使用 new 关键字产生的实例都有类的prototype上的属性和方法,我们在Puppy.prototype上添加了 say 方法,myPuppy 就可以说话了,我们来试一下:
myPuppy.say(); // 汪汪汪
实例方法查找用 __proto__
那 myPuppy 怎么就能够调用say方法了呢,我们把他打印出来看下,这个对象上并没有 say 啊,这是从哪里来的呢?
