因为项目需要用到javascript,boss说我以前是写Java的,写javascript上手快...只怪当时too young,于是误打误撞走进了javascript的世界。殊不知一入js深似海,更不知js比之Java就像雷峰塔比之雷锋、印度尼西亚比之印度、老婆饼比之老婆...
提起面向对象编程语言,往往想起的是C++、Java等强类型静态语言,以及Python等脚本语言。其中尤以Java为甚,Java is pure object oriented ,相较于C++,Java的main函数甚至都在一个类里面。这些语言都有一个共同点——他们都是基于类的面向对象。而提到javascript,很多人会怀疑它的面向对象特性,甚至认为javascript不是一门面向对象的语言,因为javascript没有类。事实上,javascript确实没有类,但javascript有对象,甚至只有对象,javascript的对象不是类的实例。大部分程序设计语言的面向对象都是基于类的,以至于人们形成一种惯性思维:对象都是类的实例。在javascript中这样的经验却是不适用的,所以我们更愿意称javascript是一门基于对象的语言。
I 创建
javascript对象实际上是一个由属性和值组成的关联数组。什么是关联数组呢?相较于平常意义上用数字作为索引的数组,关联数组可以用字符串作为索引。类似于键值对,对象的属性就是“键”,对象的值就是“值”咯。对象的值可以是任何数据类型,或者函数和其他对象。为什么对象的值可以是函数呢?说来话长,50多年前诞生了一种叫做函数式编程的东东(区别于命令式编程,与面向对象式编程更是风马牛不相及),函数式编程有很坚固的数学基础,跟什么lambda演算有关啦,一般人都不会懂的啦。我们javascript具有函数式编程的特点,所以函数是"第一等公民" !所谓"第一等公民",指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值,这些特性在C++和纯正的Java(之所以说纯正,是为了区别于scala等)里面是不可以想象的吧。好像扯远了,总之就是表达一个意思:函数可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。
我们可以用以下代码创建一个简单的对象:
var obj = {};//创建一个空对象
obj.prop1 = 'hey';//给第一个属性赋值
obj.prop2 = true;
obj.prop3 = function (str) { //函数也可以赋值给一个变量
return str;
};
console.log(obj.prop3('hello world'));//输出hello world
以上代码中,我们通过var obj = { };创建出一个对象并将其引用赋值给obj,随后通过obj.prop1 来获取其成员并赋值。除了通过var obj = { };创建一个对象,还可以用显式地用var obj=new Object();来创建一个对象。
既然上面已经提到javascript对象实质上就是一个关联数组,我们就来看看怎么用关联数组的方式创建对象,修改以上代码:
var obj = {};//创建一个空对象
obj['prop1'] = 'hey';//使用关联数组引用赋值
obj['prop2'] = true;
obj['prop3'] = function (str) { //函数也可以赋值给一个变量
return str;
};
console.log(obj.prop3('hello world'));//输出hello world
在javascript中,使用句点运算符和关联数组引用是等价的,使用关联数组的的一个好处是,在我们不知道对象的属性名称时,可以用变量作为关联数组的索引。例如:
var someProp = 'prop4';
obj[prop4] = 100;
通过以上代码你应该已经了解了如何去创建一个对象,但是在实际使用时,难免不会觉得繁琐冗杂。更加紧凑明了的方式如下:
var obj = {
prop1: 'hey',
prop2: true,
prop3: function (str) {
return str;
}
};
II 构造函数
前一节介绍的创建对象的方法可能让写惯了C++、Java的你大开眼界,对象竟然还能不靠构造函数来生成。仔细想想,我们可以察觉出前面方法的弱点:如果我们想批量地创建对象,能初始化若干固定好的属性、方法呢?不用担心,javascript为我们提供了构造函数,让我们看看如何用构造函数创建一个对象出来:
function Person(name, age) {
this.name = name;
this.age = age;
this.introduceMyself = function () {
console.log(this.name);
};
}
以上就是一个简单的构造函数,我们就可以用new语句来创建对象了:
var me = new Person("chendotjs", 21);
接着就可以通过me访问该对象的方法和属性了。
III this指针
首先需要更正的是:在javascript里面并没有像C++一样的指针概念,这里所谓的this指针只是沿用一种传统的称法。javascript里的this“指针”和Java里面的this“指针”是很类似的。javascript里面任何函数的运行一定是被某个对象(包括全局变量)调用,而this指针就指向该对象,或者准确点说,是指向该对象的一个引用。来看一个例子:
var user1 = {
name: 'user1',
display: function () {
console.log('I\'m ' + this.name);
}
};
var user2 = {
name: 'user2',
func: user1.display
};
user1.display();//输出I'm user1
user2.func();//输出I'm user2
name = 'global';
func = user1.display;
func();//输出I'm global
j
avascript的函数式编程特性使得函数可以像一般的变量一样赋值、传递。在上面的代码中,user2的func属性和global的func属性都是user1.display。当调用user1.display();、user2.func();和func();时,虽然调用的是同一个函数,但是this指针不属于任何一个函数,而取决于函数被调用时所属的对象。
事实上,在以上代码中,user1.display、user2.func和func是指向同一个函数实体的3个引用,引用之间的赋值不会复制出新的对象。这涉及到了“深拷贝和浅拷贝”的内容,将放在下一篇详细讨论。
关于this指针还有3个有趣的函数:call、apply以及bind,下面就介绍一下这3个函数:
1.call和apply
fun.call(thisArg[, arg1[, arg2[, ...]]])
fun.apply(thisArg[,argsArray])
其中,fun是函数的引用,thisArg是fun调用时的this指针指向的对象,arg1、arg2是argsArray是传给fun的参数。我们来看一个例子:
var user1 = {
name: 'user1',
display: function (food) {
console.log(this.name + ' eats ' + food);
}
};
var user2 = {
name: 'user2'
};
user1.display('apples');//输出user1 eats apples
user1.display.call(user2, 'pears');//输出user2 eats pears
user1.display.apply(user2, ['pears']);//输出user2 eats pears
2.bind
fun.bind(thisArg[, arg1[, arg2[, ...]]])
其中,fun是待绑定的函数,thisArg是fun调用时的this指针指向的对象,arg1、arg2是argsArray是传给fun的参数。bind方法的返回值是绑定了thisArg的fun,这点和call、apply是不同的。看一个例子:
var user1 = {
name: 'user1',
display: function () {
console.log('I\'m ' + this.name);
}
};
var user2 = {
name: 'user2'
};
user2.func1 = user1.display;
user2.func1();//输出I'm user2
user2.func2 = user1.display.bind(user1);
user2.func2();//输出I'm user1
name = 'global';
func = user1.display.bind(user2);
func();//输出I'm user2
在直接将user1.display赋值给user2.func1后,调用user2.func1()时,this指针指向user2,所以输出结果为“I'm user2”。user2.func2 使用了bind方法,将user1作为this指针绑定到user2.func2 ,调用user2.func2 ()时,this指针指向user1,所以输出为“I'm user1”。全局函数func也是同样的道理,这里省略具体分析。
var user1 = {
name: 'user1',
display: function (act, sth) {
console.log(this.name + ' ' + act + ' ' + sth);
}
};
var user2 = {
name: 'user2'
};
user1.display('eats', 'food');//输出user1 eats food
var func = user1.display.bind(user2, 'eats');
func('apples');//输出user2 eats apples
以上代码中,func函数将this指针绑定到user2,并将第一个参数绑定为‘eats’,之后在调用func时,只需要传入第三个参数即可。仔细想想,这个特性有很大的用处,通过这个特性可以在多次调用函数时,省略重复输入相同的参数。
3.bind的原理
var user1 = {
name: 'user1',
display: function () {
console.log('I\'m ' + this.name);
}
};
var user2 = {
name: 'user2'
};
func1 = user1.display.bind(user2);
func1();
func2 = func1.bind(user1);
func2();
I'm user2
fun.bind=function(obj){
var method=this;
return function(){
method.call(obj);
}
}
var user1 = {
name: 'user1',
display: function () {
console.log('I\'m ' + this.name);
}
};
var user2 = {
name: 'user2'
};
bind_byme = function (obj) {
var method = this;
return function () {
method.call(obj);
}
};
user1.display.bind_byme = bind_byme;//给user1.display加上自己写的bind
func1 = user1.display.bind_byme(user2);
func1();
func1.bind_byme = bind_byme;//给func1加上自己写的bind
func2 = func1.bind_byme(user1);
func2();
将自己作为编译器去解释以上代码
![大笑](http://static.blog.csdn.net/xheditor/xheditor_emot/default/laugh.gif)
func1=function(){
user1.display.call(user2);
};
func2=function(){
func1.call(user1);
};
以上结果就很明了了,因为func1根本没有使用this指针,所以func2的bind函数根本不起作用,所以两次绑定是没有意义的。