JavaScript 并不严格地要求使用对象,甚至可以不使用函数,将代码堆积成简单的顺序代码流。但随着代码的增加,为了提供更好的软件复用,建议使用对象和函数。
面向对象的概念
JavaScript 并不是面向对象的程序设计语言,面向对象设计的基本特征:继承、多态等没有得到很好的实现。在纯粹的面向对象语言里,最基本的程序单位是类,类与类之间提供严格的继承关系。比如 Java 中的类,所有的类都可以通过 extends 显式继承父类,或者默认继承系统的 Object 类。而 JavaScript 并没有提供规范的语法让开发者定义类。
对象和关联数组
当需要访问某个 JavaScript 对象的属性时,不仅可以使用 obj.propName 的形式 , 也 可以采用 obj[propName]的形式, 有些时候甚至必须使用这种形式。
<script>
function Person(name,age){
this.name=name;
this.age=age;
this.info=function(){
alert("info");
}
}
var p = new Person('小明',19);
for(proName in p){
document.write('p 对象的'+proName+"属性值:"+p[proName]+'<br/>');
document.write('p 对象的'+proName+"属性值:"+p.proName+'<br/>');
}
</script>
运行结果:
p 对象的name属性值:小明
p 对象的name属性值:undefined
p 对象的age属性值:19
p 对象的age属性值:undefined
p 对象的info属性值:function(){ alert(“info”); }
p 对象的info属性值:undefined
原因分析:用p.proName 的形式, JavaScript 不会把 propName 当成变量处理, 它试图直接访问该对象的 propName 属性一但该属性实际并不存在。
继承和 prototype
问题导入:
function Person(name,age){
this.name=name;
this.age=age;
this.info=function(){
alert("info");
}
}
原来方法为 Person 类定义增加 info 方法相当不好,主要有如下两个原因。
- 性能低下: 因为每次创建 Person 实例时,程序依次向下执行,每次执行程序中都将创建一个新的 info 函数一当创建多个 Person 对象时,系统就会有很多个 info 函数-~这就会造成系统内存泄涌,从而引起性能下降。实际上, info 函数只需 要一个就够了 。
- 使得 info 函数中的局部变量产生闭包: 闭包会扩大局部变握的作用域,使得局部变量 一直存活到函数之外的地方
改善方法把(函数定义在类外):
伪继承机制:
JavaScript 的所有类(也就是函数)都有一个 prototype 属性, 如果为 JavaScript类的 prototype 属性增加属性、 方法,则可视为对原有类的扩展。 我们可理解为: 增加了 prototype 属性的类继承了原有的类-这就是 JavaScript 所提供的一种伪继承机制 。
代码示例:
<script>
function Person(name,age){
this.name = name;
this.age = age;
this.info = function(){
document.write("姓名"+this.name+'<br/>');
document.write("年龄"+this.age+'<br/>');
}
}
var p1 = new Person('li',19);
p1.info();
// 把work方法增加到Person的prototype属性上
// 动态增加,即使p1现在没有work方法,下面定义了之后,在后面也可以调用
Person.prototype.work = function(){
document.write(this.name+"玩呢。。。<br/>");
}
document.write('<hr/>');
var p2 = new Person('liu',20);
p2.info();
document.write('<hr/>');
p2.work();
// p1可以调用work方法
p1.work();
</script>
但这种“伪继承"的实质是修改了原有的类, 并不是 产生了一个新的子类
prototype 属性为该类创建对象
JavaScript 类的 prototype 属性代表了该类的原型对象。在默认情况下, JavaScript 类的 prototype 属性值是一个 Object 对象,将 JavaScript 类的 prototype 设为父类实例,可实现 JavaScript 语言的继承。
代码示例:
<script>
function Person(name,age){
this.name = name;
this.age = age;
}
//使用 prototype 为 Person 类添加 sayHello 方法
Person.prototype.sayHello=function(){
console.log(this.name+'向你打招呼');
}
var per = new Person('牛魔王',22);
per.sayHello();//输出,牛魔王向你打招呼
function Student(grade){
this.grade = grade;
}
//将 Student的 prototype 设为 Person 对象
Student.prototype = new Person('未命名',0);
Student.prototype.intro = function(){
console.log("%s是个学生,读%d年级",this.name,this.grade);
}
var stu = new Student(5);
alert(stu.name);
stu.name = "孙悟空";
console.log(stu instanceof Student);//true
console.log(stu instanceof Person);//true
stu.sayHello();//输出 : 孙悟空向您打招呼 !
stu.intro();//输出 : 孙悟空是个学生,读 5 年级
</script>
使用 apply 或 call 实现伪继承
<script>
function Person(name,age){
this.name = name;
this.age = age;
}
//使用 prototype 为 Person 类添加 sayHello 方法
Person.prototype.sayHello=function(){
console.log(this.name+'向你打招呼');
}
var per = new Person('牛魔王',22);
per.sayHello();//输出,牛魔王向你打招呼
function Student(name,age,grade){
// Person.call(this,name,age);
Person.apply(this,[name,age]);
this.grade = grade;
}
//使用 prototype 为 Student 类添加 intro 方法
Student.prototype.intro = function(){
console.log("%s是个学生,读%d年级",this.name,this.grade);
}
var stu = new Student('孙悟空',34,5);
console.log(stu instanceof Student);//true
console.log(stu instanceof Person);//伪继承,所以输出 false
stu.sayHello();//输出 : 孙悟空向您打招呼 !
stu.intro();//输出 : 孙悟空是个学生,读 5 年级
</script>