js之面向对象编程
1、JS对象与其它对象的区别:
像JAVA与C++语言中的对象,是先构造一个类,再通过类实例化对一个对象
而javascript却不一样:
JavaScript中不区分类和实例的概念,而是通过原型(prototype)来实现面向对象编程。
2、什么是JS中的原型(prototype):
首先来与电影类比一下:有一个人的生活很精彩,所以可以把他的一生搬上大荧幕,我们就叫以这个人为原型,制作成的电影。
由于JS没有【类】这个概念,所以我们要得到一个对象时,可以找一个类似的对象为原型,通过属性的修改来得到新的对象。代码示例如下:
假设想生成一个名叫【小明】的对象,但我之前有一个叫【小红】的对象,我就可以以【小红】对象为原型,来生成【小明】。
// 定义小红
var xh =
{
name: 'xiaohong',
height: 1.2,
run: function () {
console.log(this.name + ' is running...');
}
};
// 定义小明
var xm = {
name: '小明'
};
// 把小明的原型设定为小红,这时除了在小明对象中重新定义过的name属性
// 其它属于和方法都会继承小红的内容。
xm.__proto__ = xh ;
// 如果有一天【小明】想飞了,那可以把他的原型设定不一只鸟:
var Bird =
{
fly: function () {
console.log(this.name + ' is flying...');
}
};
xiaoming.__proto__ = Bird;
注意:JavaScript没有“Class”的概念,它是用原型这个概念来实现面向对象的封装,继承和多态的。
3、用create()方法创建对象:
不要直接用obj.__proto__去改变一个对象的原型,不符合面向对象的思想,有些浏览器也不支持。用create()方法的代码示例如下:
// 原型对象
var Student = {
name: 'nobody',
height: 1.6,
run: function () {
console.log(this.name + ' is running...');
}
};
function createStudent(name) {
// 以Student为原型创建一个新对象:
var stu = Object.create(Student);
// 初始化新对象:
stu.name = name;
return stu;
}
// 创建名叫小明的对象
var xiaoming = createStudent('小明');
xiaoming.run();
// xiaoming.__proto__ === Student; xiaoming对象的原型就是Student。
4、原型链说明:
原型是可以不断系继承的,如上例中小明的原型是学生,那学生的原型还可以是人,如下:
xiaoming.__proto__ === Student
Student.__proto__ === Person
在查找一个属性时,如果当前对象有,就直接返回,如当前对象没有,就在上一级原型中去查找。到最终一级原型上都没找到的话,就只能返回undefined了。
如数据的原型链如下:
var arr = [1, 2];
其原型链是:
arr ----> Array.prototype ----> Object.prototype ----> null
函数的原型链是:
function foo() { return 0; }
foo ----> Function.prototype ----> Object.prototype ----> null
5、JS中的构造函数:
以下是一个普通函数,它返回undefined:
function Student(name) {
this.name = name;
this.hello = function () {
alert('Hello, ' + this.name + '!');
}
}
// 如果以new关键字来调用,Student就变成了一个构造函数,它绑定的this指向新创建的对象,并默认返回this,也就是说,不需要在最后写return this;
var xiaoming = new Student('小明');
新创建的xiaoming的原型链是:
xiaoming ----> Student.prototype ----> Object.prototype ----> null
6、内存浪费的问题:
以上function Student(name) 函数为例:
var s1 = new Student('小明');
var s2 = new Student('小红');
var s3 = new Student('小天');
其中s1,s2,s3都包含不同的name与hello属性。有不同name是合理的,因为大家的名字都不相同。但不同的hello却不合理了,因为s1,s2,s3中hello的代码与功能都是相同的,所以只应该存在一份就行了。这时就需要把hello从Student中移动到Student的原型中。原因如下:
当s1,s2,s3不存在在hello属性时,就会自动去搜索它所在的原型是否提供了该属性。这样一来就只需要一个hello代码就行了,内存也就不会造成浪费了。
7、不要忘记写new:
如果生成对象时忘记写new了,如下:
var xiaoming = Student('小明');
在strict模式下,this.name = name将报错,因为this绑定为undefined。
在非strict模式下,this.name = name不报错,因为this绑定为window,于是无意间创建了全局变量name,并且返回undefined。
所以生成对象时千万不要忘记了写new。