Javascript是一种基于对象的语言,但是它又不是一种真正的面向对象编程语言,因为它的语法中没有类这一概念。要了解javascript中的基于对象原理,需要了解的javascript中的对象和原型。这篇文章就带大家逐一了解,看完之后你会发现深入了解这一原理的重要性,在开发过程中节省时间和空间。
对象
ECMA-262将对象(object)定义为”属性的无序集合,每个属性存放一个原始值、对象或函数”(unordered collection of properties each of which contains a primitive value, object, or function)。这意味着对象是无特定顺序的值的数组。说的通俗一些,对象其实就是一种引用类型,在javascript中引用类型是一种数据结构,将数据和功能组织在一起。
1. 对象的创建的基本方法
使用new运算符
<script>
var user = new Object(); //使用new运算符创建一个对象
user.name = 'java-tree'; //给对象添加属性
user.age = 1;
user.address = '湖北武汉';
alert(user.name + " " +user.address);//返回 'java-tree 湖北武汉'
</script>
JSON法创建
var user = {
name:'java-tree',
age:1,
address:'湖北武汉'
};
alert(user.name + " " +user.address);//返回 'java-tree 湖北武汉'
u 传统赋值
var user = {};
user.name = 'java-tree'; //给对象添加属性
user.age = 1;
user.address = '湖北武汉';
alert(user.name + " " +user.address);//返回 'java-tree 湖北武汉'
2. 基于对象的操作
属性的调用
上述对象创建中已经使用了属性调用的一种方式,即’.’运算符,方法如下:
alert(user.name + " " +user.address);//返回 'java-tree 湖北武汉'
另一种方法:
alert(user['name'] + " " +user['address']);//返回 'java-tree 湖北武汉
给对象添加方法(直接内部增加和外部调用)
var user = {
name:'java-tree', //给对象添加属性
age:1,
address:'湖北武汉',
showInfo:function(){//添加一个方法
alert(this.name+" "+this.age+" "+this.address);//返回 'java-tree 1湖北武汉'
},
showHello:showHello//将对象外部的方法添加到对象
};
function showHello(){
alert("Hello!");
}
删除对象属性
var user = {
name:'java-tree',
age:1,
address:'湖北武汉'
};
alert(user.name);//返回‘java-tree’
delete user.name;//删除user的name属性
alert(user.name);//返回‘undefined’
对象的创建的常用方法
前面我们也有介绍对象的基本的创建方法,之所以称之为基本的创建方法,是因为这种方法虽然直观,但是如果需要我们创建多个引用类型相同的对象,那么就需要写很多重复的代码,这在实际开发过程中是不合适的,大大增加了代码量。下面介绍工厂模式和构造函数的方法。
工厂模式
function create(name, age) {
var obj = new Object();
obj.name = name;
obj.age = age;
obj.run = function () {
return this.name +' '+ this.age;
};
return obj;
}
var obj1= create('A', 1); //第一个实例
var obj2= create('B', 2); //第二个实例
alert(obj1.run());
alert(obj1.run());
工厂模式虽然解决了大量重复代码的问题,但是也存在弊端——在识别实例的时候无法识别具体是哪一个对象创建的。
alert(typeof obj1); //Object
构造函数
function User(name, age) {
this.name = name;
this.age = age;
this.run = function () {
return this.name + ' '+this.age;
};
}
//创建对象
var user1= new User('A', 1);
var user2= new User('B', 2);
//对象识别
alert(user1 instanceof User);//true
构造函数的方法解决了重复实例化和对象识别的问题,但是也存在一定的问题,观察如下代码:
alert(user1.run==user2.run);//结果返回的是false
结果返回的是false,这就说明方法其实也是一种引用地址。如果我们同样重复创建了多个对象,那么每个对象中的方法都会在内存中开辟新的空间,这样浪费的空间就比较多。为解决这一问题,下面将引入原型对象的概念。
原型对象
构造函数方法
原型对象方法
从上述示意图中,可以看出所有User创建出的对象都共享了方法show(),因此“user1.show == user2.show;”在此处返回值为true。
function User(name,age){//构造方法
this.name = name;//对象属性
this.age = age;
}
User.prototype.addr = '湖北武汉';//在原型中添加属性
User.prototype.show = function(){//在原型中添加方法
alert(this.name+'|'+this.age);
};
var user1 = new User('A',1);//创建实例
var user2 = new User('B',2);
user1.show();//调用show()方法
user2.show();
alert(user1.show == user2.show);//返回 true 说明show方法是共享的
但是存在一个问题,如果既在构造方法中添加了一个属性,又在原型中添加了该属性,还在实例中添加了该属性,那么我们访问的究竟是哪一个属性呢?
此处属性调用的优先级服从就近原则:实例属性>对象属性>原型属性,大家可以自己动手尝试一下。
关于原型对象的创建,还有另外两种方法,一个是动态原型对象的创建方法,再有就是字面方法创建。
动态原型对象
function User(name,age){//构造方法
this.name = name;//属性
this.age = age;
this.addr = '湖北恩施';
User.prototype.addr = '湖北武汉';//在原型中添加属性
User.prototype.show = function(){//在原型中添加方法
alert(this.name+'|'+this.age+'|'+this.addr);
};
}
var user1 = new User('ZXC',22);//创建实例
var user2 = new User('CXZ',21);
动态原型对象相比之前的其实没有什么太大的差别,只是将方法和属性封装在一块看起来更加直观。但是如果细心去研究这一段代码的话,也会发现其存在一定的问题,在每次创建对象的时候,都会再创建一次原型,虽然控件没有额外增加,但是时间上确实增加了,因为每次都要重新创建,要解决这个问题只需要增加一个if判断即可:
if(this.show==undefined){//如果run方法还没有被创建
User.prototype.show = function(){//在原型中添加方法
alert(this.name+'|'+this.age+'|'+this.addr);
};
}
这样就减少了不必要的开销。
字面量方式创建原型
function User(name,age){
this.name = name;
this.age = age;
}
User.prototype = {
addr : '湖北武汉',
show : function(){
alert(this.name+'|'+this.age+'|'+this.addr);
}
};
//重写了原型
User.prototype = {
other : '重写原型',
show : function(){
alert(this.addr);
}
};
字面量方式需要注意的点在上述代码中已经体现,使用字面量方式创建后,不能再使用字面量的方式重写原型,一旦重写了原型,原来的原型中定义的所有属性和方法都将被清除。