javascript的面向对象

1. 原始类型和对象类型

javascript的数据类型分为两类,原始类型(primitive type)和对象类型(object type)。原始类型包括javascript已定义的数值、字符串、布尔值。对象类型包括用户定义的对象以及函数(对,函数在javascript中其实也是一种对象)。原始类型的赋值是传值赋值,对象类型的赋值是引用赋值。在下面的例子可以看出来。
var n = 10;
var m = n;								// 传值赋值,m的值等于n
m = 13;
console.log('n = %d, m = %d.', n, m);	// n = 10, m = 13.

var onePerson = {}; 					// 新建一个空对象
onePerson.fullname = 'HarryPotter';		// 为对象新建属性fullname
onePerson.age = 21;						// 为对象新建属性age
onePerson.talk = function () {
	console.log('Hi, my name is %s. My age is %d.', this.fullname, this.age);
}										// 为对象新建方法talk,在对象的方法中,this指向该对象

var otherPerson = onePerson;			// 对象赋值是引用赋值,otherPerson就是onePerson,二者共同指向一块内存地址。
otherPerson.fullname = 'Kate';			// 等同于修改了onePerson.fullname		
onePerson.talk();						// Hi, my name is Kate. My age is 21.

 

2. 将函数作为构造函数

javascript的函数主要有三种调用方式。作为函数直接调用,作为对象的方法调用,作为对象的构造函数调用。

function Person() {
	global_firstname = 'funway';			// 定义一个全局变量,无法被对象继承
	var local_lastname = 'wang';			// 定义一个局部变量,无法被对象继承
	this.fullname = 'funway.wang';			// 定义一个继承属性,可以被对象继承
	console.log('In Person function as a constructor.');
}

Person();			         // 直接作为函数调用,打印 "In Person function as a constructor."
AA = new Person();			 // 作为构造函数调用, 同样会打印 "In Person function as a constructor."
console.log(global_firstname);		 // "funway"
console.log(AA.global_firstname);	 // undefined
console.log(AA.local_lastname);		 // undefined
console.log(AA.fullname);		 // "funway.wang"

上面的代码中,我们通过将Person函数作为构造函数创建了一个新的对象AA,用C++的面向对象来看,函数名Person就相当于一个类名。

实际上,Person作为一个javascript函数,默认有一个prototype属性。prototype属性值是一个对象,我们叫做原型对象。当函数作为构造函数被调用时候,该prototype原型对象就会被当作新对象的原型,所有新创建的对象均有一个__proto__对象,指向该原型对象。另外,prototype原型对象内部有一个constructor属性,指向构造函数Person,这个constructor负责调用构造函数对新对象进行初始化。可以这么说,AA = new Person() 相当于执行了以下三步:

AA = {};    // 创建空对象
AA.__ptoto__ = Person.prototype;
执行AA.__proto__.constructor指向的函数对AA进行初始化,创建AA对象的属性

屏幕快照 2015-04-14 上午2.04.48

上图中的Object与Function这俩个底层的概念这里就不多解释了,javascript的Object与Function的关系看到哭。。。=。=#

3. prototype原型,原型链与动态继承

从上面可以看出,由Person构造函数构造的对象,都有一个默认的__proto__属性指向Person.prototype,那么我对Person.prototype的修改都会实时的传递到所有Person对象上。另外,对象可以直接通过.属性名/.方法名访问到其原型对象的属性或者方法,前提是该对象本地没有重名的属性/方法。这就是javascript动态继承的基础。而原型链的意义就是,要访问一个对象x的属性/方法的时候,会先查找对象本地空间,没有,则查找x.__proto__空间,然后再x.__proto__.__proto__。这样递归直到找到所要的属性/方法或到达底层的Object基本对象。

function Person(name) {
  this.name = name? name : 'noname';
}

bill = new Person('bill');   // 创建新对象
kate = new Person('kate');   // 创建新对象

console.log(bill.name);    // "bill"
console.log(bill.age);     // undefined,bill对象没有该属性

Person.prototype.age = 18; // 增加age属性,所有的Person对象都自动拥有该属性。
Person.prototype.sayHi = function() {
  console.log('hi, my name is %s, i am %d yeas old.', this.name, this.age);
} // 增加sayHi方法,所有的Person对象都自动拥有该方法

bill.sayHi(); // "hi, my name is bill, i am 18 yeas old."
kate.sayHi(); // "hi, my name is kate, i am 18 yeas old."

bill.age = 25; // 给bill对象添加了一个本地属性age,之后bill.age将不再访问bill.__proto__.age
console.log(bill.age); // 25,访问的是bill.age(不会再跳进去访问bill.__proto__.age了)
console.log(kate.age); // 18,访问的是kate.__proto__.age(因为kate.age不存在)

var Programer = function(name) {
  this.name = name? name : 'noname';
}
Programer.prototype.__proto__ = Person.prototype;
Programer.prototype.skill = function() {
  console.log('coding');
}

var jim = new Programer('jim');
jim.sayHi(); // 调用的是jim.__proto__.__proto__.sayHi()
jim.skill(); // 调用的是jim.__proto__.skill
我们用直观的图来表示上面代码中的“类”与对象的关系:
屏幕快照 2015-04-23 下午11.06.54
从上面的例子,我们注意到,构造函数内定义的属性/方法对于每个对象来说是独享的,而在prototype原型对象上定义的属性/方法对于每个继承的对象来说是共享的。
因此,我们通常将对象的属性用this.定义在构造函数中,将对象的方法定义在prototype上。

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top