JavaScript 是面向对象语言
它以自己独特的方式实现了面向对象语言中的封装,抽象,继承,多态等特性;
JavaScript 没有类的概念,只有对象;
现实世界也是面向对象的,也没有类,到处都是“人”的实例,但并没有“人类”这个实体;工厂里生产各式各样的汽车,汽车是实例,并没有所谓的“汽车类”;类,从本质上讲是“知识”。
编程的本质
程序的本质是信息处理(存储,传输,转换,检索,呈现…)。汇编语言,基于“数据”和“指令”编程;面向过程语言(c语言),是基于“变量”和“函数”编程;面向对象语言以“面向对象”的思想去思考和处理“数据”和“行为”,基于对象编程。
数据和行为
从编程的角度看,数据就是特定的内存空间(栈/堆),行为是可执行的代码指令。
JavaScript的对象可以直接对数据和函数进行封装:
var s = {
name: "s",
say: function() {
console.log("Hello!")
}
}
s.name
s.say()
从面向对象的角度来看,同一类型的不同对象之间的公共行为是一样,也就是他们能够共享对象方法。在Java中,所有的对象方法都定义在类信息里,对象实例只包含数据成员。
那么JavaScript是如何把行为抽象出来的呢?
函数是对可执行语句的封装。在JavaScript中,函数也是对象。
以Java为例,抽象本质上就做了两件事——确定对象空间的大小和关联对象方法。Java通过类定义来实现抽象,可是JavaScript并没有类的概念,于是JavaScript只能通过函数来实现对象的抽象。
function Person(name){
this.name=name;
this.say = function(){
console.log("I'm "+this.name)
}
}
var p = new Person("da bao")
在JavaScript中,任何函数都可以作为构造函数!如上例,该构造函数很好的完成了封装,指定了对象p的大小{name,say},也实现了创建对象过程的复用。
但是该方式也存在问题:每个对象的存储空间都包含了独立的函数对象 s。如果对象有50个方法呢?将造成极大的空间浪费,Java类的对象可不是这样的哦!
另外一个问题是如何实现类变量(静态变量),也就是所有对象都共享的成员变量,在Java中通过 static 实现!
这就到了 prototype 大显身手的时候了!JavaScript 为每个函数设计了一个 prototype 属性,
prototype
每个函数都有一个prototype属性,是一个指针,指向该函数的原型对象。
__proto__
是一个对象拥有的内置属性。在JS内部使用寻找原型链。
function Person(name){
this.name=name;
Person.prototype.count++;
}
Person.prototype.say = function(){
console.log("I'm "+this.name)
}
Person.prototyp.count = 0
var p1 = new Person("da bao")
var p2 = new Person("er bao")
p1.say() // I'm da bao
p2.say() // I'm er bao
p1.count // 2
p2.count // 2
面向对象的另一大特性——继承,Java 通过 extends和implements,标识类之间的继承关系,编译器会根据标识隐式的进行对象存储空间的分配和类信息的关联。
JavaScript 是如何实现的呢?
function Worker(name, type){
Person.call(this, name); // (1)
this.type = type;
Worker.prototype.num++;
}
Worker.prototype = Object.create(Person.prototype) // (2)
Worker.prototype.num = 0
var w1 = new Worker("da bao", "Teacher")
var w2 = new Worker("er bao", "Coder")
w1.say() // I'm da bao
w2.say() // I'm er bao
w1.__proto__ == Worker.prototype // True
Worker.prototype.__proto__ == Person.prototype // True
例子中,(1)处代码保证为父类数据成员分配内存空间并初始化。(2)处代码保证对象能够调用父类中的成员方法。
JavaScript 是基于原型的面向对象语言,每一个js 对象都有一个原型链,这个原型链是一个原型对象之链;当修改构造函数的原型对象时,并不会影响已经存在的对象的原型链。
特别注意:
prototype 是个指针,当对它重新赋值时,已经生成的对象的原型链并不会被改变。
本例的原型链:
Person.prototype.__proto__ == Object.prototype
Worker.prototype.__proto__ == Person.prototype
w1.__proto__ == Work.prototype
…