JavaScript笔记——面向对象与原型
JavaScript也是一门面向对象的语言。面向对象的语言有一个标志,那就是类的概念,而通过类可以创建任意多个具有相同属性和方法的对象。但是,JavaScript竟然没有class,因此它的面向对象也与其他的OOP语言有所不同,创建对象和继承关系都显得很怪异很复杂难用
创建对象
JavaScript也采用了构造函数的概念:
function Box(name, age) { //构造函数模式 this.name = name; this.age = age; this.run = function () { return this.name + this.age + '运行中...'; }; } var box1 = new Box('Lee', 100); var box2 = new Box('Jack', 200); alert(box1.run()); alert(box1 instanceof Box); //很清晰的识别他从属于 Box
构造函数的方法有一些规范:
1.函数名和实例化构造名相同且大写,(非强制,但这么写有助于区分构造函数和普通函数)
2.通过构造函数创建对象,必须使用 new 运算符(构造函数和普通函数的唯一区别,就是他们调用的方式不同。构造函数也是函 数,必须用 new 运算符来调用,否则就是普通函数了)
构造函数示意图:
原型的概念
在创建类之前,先要有原型的概念,我们创建的每个函数都有一个 prototype(原型)属性,这个属性是一个对象,它的用途是 包含可以由特定类型的所有实例共享的属性和方法。简单地说原型就是实现共享的功能:比如说类里的方法和一些需要共享的属性(较少见)
function Box() {} //声明一个构造函数 Box.prototype.name = 'Lee'; //在原型里添加属性 Box.prototype.age = 100; Box.prototype.run = function () { //在原型里添加方法 return this.name + this.age + '运行中...'; }; var box1 = new Box(); var box2 = new Box(); alert(box1.run== box2.run); //true,方法的引用地址保持一致
在上面的run方法和name,age属性不论声明多少个对象都是相等的(共享了)
原型示意图:
在原型模式声明中,多了两个属性,这两个属性都是创建对象时自动生成的。__proto__ 属性是实例指向原型对象的一个指针,它的作用就是指向构造函数的原型属性 constructor。 通过这两个属性,就可以访问到原型里的属性和方法了。浏览器中无法获取其内部信息
原型的声明是有先后顺序的,所以,重写的原型会切断之前的原型
function Box() {}; Box.prototype = { //使用原型创建属性和方法 constructor : Box, name : 'Lee', age : 100, run : function () { return this.name + this.age + '运行中...'; } }; Box.prototype = { //原型被重写了 age :200 }; var box = new Box(); alert(box.run()); //错误TypeError: box.run is not a function ,因为重写了原型,原来的原型方法,属性失效
创建类与对象
使用动态原型模式创建对象可以使方法成为共享的,而属性各是各的
//动态原型模式创建对象 function Box(name,age){ this.name=name; this.age=age; if(typeof this.run!='function'){ Box.prototype.run=function(){ //使用原型来实现方法共享 return this.name+this.age+'运行时'; }; } } var box1=new Box('lz',21); var box2=new Box('Jack',20); alert(box1.run()); alert(box2.run());
继承
继承是面向对象中一个比较核心的概念。其他正统面向对象语言都会用两种方式实现继承:一个是接口实现,一个是继承。而JavaScript只支持继承,不支持接口实现
推荐使用寄生组合继承的方式:
function obj(o) { //传递一个字面量函数 function F() {} //创建一个构造函数 F.prototype = o; //把字面量函数赋值给构造函数的原型 return new F(); //最终返回出实例化的构造函数 } function create(box,desk){ //将来传入父类和子类 var f=obj(box.prototype); //将父类的原型传入 f.constructor=desk; //改变子类的constructor desk.prototype=f; //又将f传给子类的原型 } function Box(name,age){ //父类 this.name=name; this.age=age; this.arr=['爸爸','妈妈','儿子']; if(typeof this.run!='function'){ Box.prototype.run=function(){ return this.name+this.age+'运行时'; } } } function Desk(name,age){ //子类 Box.call(this,name,age); //对象冒充 } create(Box,Desk); //通过这里实现继承 //下面是证明不同子类对象互不影响(不同空间) var desk=new Desk('lz',21); desk.arr.push('女儿'); alert(desk.arr); var desk2=new Desk('jack',20); alert(desk2.arr);