javascript的面向对象开发(一)

javascript的面向对象开发(一)


在我刚来到这个公司实习的时候,看到的javascript代码全部都是内嵌在标签中,一竖排的function,各种$(“element”).html(), $.ajax把页面功能给堆出来。如果不是自己写的根本别想一眼看懂,就算是自己写的过一段时间也糊涂了,可读性几乎为0。而且如果要改别人的代码,更是难度超级大,当时一度看得我想吐。js代码行数超级多,然而仔细一看后发现很多代码实现的是同一个功能或者相似的功能,然而它们却因为属于不同的页面或者业务流程而被完全隔离。。

解决这些问题的方法很简单,就是用javascript的面向对象开发方法。(原来javascript也能写面向对象哦!之前真是什么都不懂)页面上的视图组件,数据模型包括其他控制逻辑等,都可以用面向对象的方式来开发。好处是:

  • 提高代码的复用率
  • 更抽象,容易理解,可读性高
  • 封装对象的内部逻辑,功能仅以接口的形式暴露,易于使用

我们来看一个例子。这里我们实现一个Car类,有setSpeed方法和run方法。

var Car = function(name) {
    this.name = name;
    this.distance = 0;
};
Car.prototype = {
    setSpeed: function(speed) {
        this.speed = speed;
    },
    run: function(time) {
        this.distance = this.distance + this.speed * time;
        console.log(this.name + " has runned for " + this.distance + " meters.");
    }
}
var car = new Car("bmw");
car.setSpeed(10);
car.run(3);
//console: bmw has runned for 30 meters.

在上面的代码中,首先定义的function(name) 其实就是Car类型的构造函数,其中this指向的就是新创建的那个变量。构造函数中为car实例设置了name和distance两个属性。随后的Car.prototype为Car类型定义了setSpeed()和run()两个方法。
这里有两个要点:一个是prototype的含义,一个是this这个东西在js里到底是怎么一回事。

一、prototype

在javascript中,任意一个构造函数(构造函数和普通function的区别就是构造函数体中有对this的操作)都会有一个prototype属性。这个属性指向一个对象,任何由该构造函数创建的实例都会继承这个对象的所有属性。
在上文的例子中,car实例继承了prototype中的setSpeed()和run()两个属性。其实也可以不写定义prototype,而把所有继承关系都在构造函数体中实现,比如:

var Car = function(name) {
    this.name = name;
    this.distance = 0;
    this.setSpeed = function(speed) {
        this.speed = speed;
    }
    this.run = function(time) {
        this.distance = this.distance + this.speed * time;
        console.log(this.name + " has runned for " + this.distance + " meters.");
    }
};

问:然而还是用prototype的模式更加的好,为什么?
答:这样的话,所有实例的setSpeed()和run()方法,其实都是同一个内存地址,指向prototype对象,在创建新的实例的时候不需要重新分配内存,所以能提高运行效率也节省空间。

与prototype相似的还有一个叫做proto的属性。与prototype不同,proto是属于具体的实例的属性。指向创造该实例的构造函数的prototype所指向的对象。 这两者的区别就是一个属于构造函数,一个属于实例。

prototype或__proto__链:

一个对象能够继承别的对象的属性,那么那个被继承的对象的属性也有可能继承自其他对象。和Java一样,一切对象的祖先是原生的Object{}类型。那么这里有一个问题,如果一个类型ClassA有methodA()属性,同时其构造函数的prototype中也有一个名为methodA()的属性,在var a = new ClassA()后,a的methodA到底是指向前者,还是后者呢?答案是前者,因为在寻找属性的时候,javascript永远先从本地的属性中寻找,如果找不到才会再找prototype中有没有该属性。
代码如下:

var Car = function(name) {
    this.name = name;
    this.distance = 0;
    this.run = function(time) {
        this.distance = this.distance + this.speed * time;
        console.log(this.name + " has runned for " + this.distance + " kilometers.");
    }
};
Car.prototype = {
    setSpeed: function(speed) {
        this.speed = speed;
    },
    run: function(time) {
        this.distance = this.distance + this.speed * time;
        console.log(this.name + " has runned for " + this.distance + " meters.");
    }
}
var car = new Car("bmw");
car.setSpeed(10);
car.run(3);
//console: bmw has runned for 30 kilometers.
二、this的指向

this的指向是js的一个麻烦的地方,经常会有因为搞错了this的作用域导致程序bug的问题。总的来说,this的指向有这么几条规则。

In JavaScript, as in most object-oriented programming languages, this is a special keyword that is used within methods to refer to the object on which a method is being invoked.
也就是说,this指向当前函数执行时的当前对象。

问:所有的函数都有它所属的对象么?js中的匿名函数呢?
答:事实上,匿名函数也是有他所属的对象的。这个对象就是全局对象--在前端开发中,这个全局对象就是window对象。(在node环境中就不是了)这就是为什么在定义了window.a后,直接访问a也是可以的,反之亦然。

在上面的例子中,构造函数中的this指向就是函数执行时候其所属的对象,也就是新建的实例了。

这里顺带一提:所有用var声明的变量的作用域都是当前作用域。比如在for循环内用var声明的变量外部不可访问,而不使用var声明的变量,其作用域都是全局变量,也就是window。当要使用到一个变量时,首先访问当前作用域,如果没有则一层层向上寻找。

call和apply:

看这样一段代码:

function log() {
    console.log(this.name);
}
window.name = "window";
var obj = { name: "obj"}
log();//console: window
log.call(obj);//console: obj

函数的call方法能够强行更改函数中this的指向,其实就是更改了log函数在执行似的当前对象(原本是window,现在被更改成了obj)。
apply和call方法相似,区别是传递参数的格式。

以上这些就是javascript面向对象开发的最最基础。在这里推荐一本书:雅虎大牛Douglas Crockford的《JavaScirpt语言精粹》,电子工业出版社。书如其名,助你写出最精粹的js代码。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值