JavaScript语言与传统的面向对象语言(如Java)有点不一样,js语言设计的简单灵活,没有class、namespace等相关概念,而是万物皆对象。虽然js不是一个纯正的面向对象语言,但依然可以对js面向对象编程。java语言面向对象编程的基础是类,而js语言面向对象编程的基础是原型。
原型是学习js的基础之一,由它衍生出许多像原型链、this指向、继承等问题。所以深入掌握js原型,才能对其衍生的问题有很好的理解。网上有很多文章解释原型里的等式关系,那样有些晦涩难懂,这里笔者从js设计历史来逐步解释js原型。
在ES6前,js语法是没有class的。倒不是js语言作者Brendan Eich忘记引入class语法,而是因为当初设计js语言时,只想解决表单验证等简单问题(估计js作者没想到后来js成为最流行的语言之一),没必要引入class这种重型武器,不然就跟Java这种基于class的面向对象语言一样了。具体可以看下阮一峰老师的Javascript继承机制的设计思想 (opens new window)。
虽然设计js语言时,更多的考虑轻量级灵活,但依然要在语言层面考虑对象封装以及多个对象之间复用的问题。先看下使用传统方式进行封装:
`function Person(name) { return { name: name, sleep: function() { console.log( 'go to sleep' ) } } }
var person1 = Person('tom') var person2 = Person('lucy') ... personN
Copied!`
传统方式有以下两个弊端:
既然无意引入class语法,同时需要满足对象的封装以及复用问题,那就需要在js语言层面引入一种机制来处理class问题。
js作者使用了原型概念来解决class问题。那什么是原型?原型是如何在语法层实现的?会涉及到哪些概念?
js原型概念通俗讲有点像Java中的类概念,多个实例是基于共同的类型定义,就像tom、lucy这些真实的人(占据空间)基于Person概念(不占空间,只是定义)。java中类是基于class关键字的,但js中没有class关键字,有的是function。而java类定义中都有个构造函数,实例化对象时会执行该构造函数,所以js作者简化把构造函数constructor作为原型(代替class)的定义。同时规定构造函数需要满足以下条件:
`// java定义类 class Person { // java类中都有构造函数 constructor(name) { this.name = name }
public void sleep() { .... } }
Copied!`
`// js使用构造函数代替类的作用 function Person(name) { this.name = name this.sleep = function() { ... } }
Copied!`