面向对象的语言有一个类的概念,通过类可以创建任意多个具有相同属性和方法的对象。即三大特点:继承,多态,封装。ECMAScript没有类的概念,因此它的对象也与基于类的语言中的对象有所不同,可以理解为JS的对象是一组无序的值,其中的属性或方法都有一个key,根据这个key可以访问相映射的值/对象/方法。
1.对象:
简单创建一个对象如下:
<span style="font-size:14px;">var person = new Object(); //创建一个object对象
person.name = "lly"; //创建一个 name属性并赋值
person.age = 24;
person.goBack = function() { //建一个goBack()方法并返回值
alert("姓名:"+this.name+",年龄:"+this.age); //this表示当前作用域下的变量和方法;需要注意作用域的问题
}
person.goBack();</span>
这种创建对象的方法,很大的一个弊端就是每创建一个类似对象都会产生大量的冗余代码,这个问题可以通过定义一个集中实例化的函数来规避处理,如下:
<span style="font-size:14px;">function creatObj(name,age){
var obj = new Object();
obj.name = name;
obj.age = age;
obj.goBack = function(){
alert("姓名:"+this.name+",年龄:"+this.age);
};
return obj;
}
var person = creatObj("lly",24);
var person1 = creatObj("lvly",25);
alert(person.goBack());
alert(person1.goBack());</span><span style="font-size: 14px;">
</span>
这种称为工厂模式的创建对象方法,虽然解决了重复实例化的问题,但person和person1之间没有内在的联系,没法搞清是哪个对象或是同一个原型对象的实例,如下面:
<span style="font-size:14px;">alert(typeof person); //object
alert(typeof person1); //object
alert(person1 instanceof creatObj); //false
</span>
鉴于这个从原型对象生成实例的问题,Javascript提供了一个构造函数(Constructor)模式,可以采用构造函数(构造方法)的方式来创建特定的对象;所谓的构造函数,其实与普通函数不同的就是里面用了this这个变量,【关于this的使用,this其实就是代表当前作用域对象的引用。如果在全局范围this就代表window对象,如果在构造函数体内,就代表当前的构造函数所声明的对象】直接对构造函数使用new运算符,就可以生成特定实例,当然这个实例已经绑定了this变量。
<span style="font-size:14px;">function ConstructorFn(name,age){ //构造函数也是函数,函数名的第一个字母需要大写;必须new
this.name = name;
this.age = age;
this.goBack = function(){
alert("姓名:"+this.name+",年龄:"+this.age);
};
}
var person = new ConstructorFn("lly",24);
var person1 = new ConstructorFn("lvly",25);
person1.goBack();
alert(typeof person); //object
alert(typeof person1); //object
alert(person1 instanceof ConstructorFn); //true</span>
上面通过JavaScript提供的instanceof函数可以很好的验证当前实例对象所属的原型对象;
<span style="font-size:14px;">alert(person.constructor == ConstructorFn);//true
alert(person1.constructor == ConstructorFn);//true
</span>
通过new运算符得到实例对象后,他们自动含有一个constructor属性,指向它们的构造函数,如上。
归纳下:
可以从上面看到使用构造函数的方法,解决了重复实例化的问题和对象识别的问题,但这里并没有new Object(),为什么可以实例化ConstructorFn()?
构造函数的方法和使用工厂模式的方法不同之处有:
1.构造函数方法没有显示的创建对象【new Object()】;
2.直接将属性和方法赋值给this对象;
3.没有renturn语句。
构造函数的方法规范:
1.函数名和实例化构造名相同且大写,这么写有助于区分构造函数和普通函数;
2.通过构造函数创建对象,必须使用new运算符。
通过构造函数来创建对象执行的过程如下:
1.当使用了构造函数,并且new构造函数(),那么就后台执行了new Object();
2.将构造函数的作用域给新对象,(即new Object()创建出的对象),函数体内的this就代表new Object()出来的对象;
3.执行构造函数内的代码;
4.后台直接返回新对象;
构造函数方法的问题所在:
1.给上面的构造函数再添加一个方法job();就变成了:
<span style="font-size:14px;">function ConstructorFn(name,age){
this.name = name;
this.age = age;
this.goBack = function(){
alert("姓名:"+this.name+",年龄:"+this.age);
};
}
var person = new ConstructorFn("lly",24);
var person1 = new ConstructorFn("lvly",25);
alert(person.goBack == person1.goBack );//false
</span>
这种很明显就可以看出弊端了,goBack()方法在内存中生成不止一次了,每次new一个对象调一次就生成一次,这样的方法内容一模一样的,完全没必要多次,既不环保效率也不高…
为了加深下了解,我们可以采用在函数外面绑定一个函数的方法来保证引用地址的唯一性,如下面这种写法:
<span style="font-size:14px;">function ConstructorFn(name,age){
this.name = name;
this.age = age;
this.goBack = goBack;
}
function goBack (){
alert("姓名:"+this.name+",年龄:"+this.age);
}
alert(person.goBack == person1.goBack );//true
</span>
很明显的通过这种方式解决了引用地址一致性的问题,但又会导致新的问题全局中的this在对象调用的时候是ConstructorFn本身,但在普通的函数中调用的时候很明显又代表了window。其实没必要用上面说的这种外部函数的方法处理引用地址一致的问题,仅作为深入了解一下,可以通过原型Prototype模式来处理一下!
2.原型:
Javascript规定,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。也就不必在构造函数中定义对象信息,而是可以直接将这些信息添加到原型中,即定义到prototype对象上。
<span style="font-size:14px;"><span style="font-size:14px;">function ConstructorFn(){};
ConstructorFn.prototype.name = "lly";
ConstructorFn.prototype.age = 24;
ConstructorFn.prototype.goBack = function(){
alert("姓名:"+this.name+",年龄:"+this.age);
}
var person = new ConstructorFn();
var person1 = new ConstructorFn();
alert(person.goBack == person1.goBack); //true </span><strong style="font-size:18px;">
</strong></span>
在这个原型模式图中可以看到, 多了__proto__和constructor两个属性, 这两个属性都是创建对象时自动生成的。 __proto__属性是实例指向原型对象的一个指针,它的作用就是指向构造函数的原型属性constructor。
通过这两个属性,就可以访问到原型里的属性和方法了。也可以是说是每个对象都会在其内部初始化一个属性,就是__proto__,当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么他就会去__proto__里找这个属性,这个__proto__又会有自己的__proto__,于是就这样一直找下去,也就是我们平时所说的原型链的概念。
通过以下几种方法来进行Prototype模式的验证:
1.isPrototypeOf这个方法用来判断某个proptotype对象和某个实例之间的关系。
<span style="font-size:14px;">alert(ConstructorFn.prototype.isPrototypeOf(person));//true</span>
原型模式的执行流程:
先查找构造函数实例里的属性或方法,如果有,立刻返回;如果构造函数实例里没有,则去它的原型对象里找,如果有,就返回;但是必须明确的一点是我们只能通过实例对象访问原型中的值但却不能通过对象实例来修改原型中的值;如:
<span style="font-size:14px;">var person = new ConstructorFn();
alert(person.name);//lly
person.name = “lvly”;
alert(person.name);
var person1 = new ConstructorFn();
alert(person1.name);//lly 可以看出原型中的值并没有被修改掉
</span>
这种如果后面也想让person访问到原型中值的话,就需要先做一把delete,删掉之后再去请求对应的原型值就可以了,如:
<span style="font-size:14px;"> delete person.name;//删除属性
alert(person.name); //lly
</span>
2 .hasOwnProperty()这个方法用来判断某一个属性到底是本地属性,还是继承自prototype对象的属性。
<span style="font-size:14px;">alert(person.hasOwnProperty("name")); //false 实例里有返回true,否则返回 false</span>
3.in操作符会在通过对象能够访问给定属性时返回 true,无论该属性存在于实例中还是原型中;通过结合这两点可以判断原型中是否存在属性:
<span style="font-size:14px;">function isProperty(object,property){
return !object.hasOwnProperty(property)&&(property in object);
}
var person = new ConstructorFn();
alert(person,”name”); //原型中有的话就是true
</span>
未完待续…最后贴张stackoverflow上的图理解一下了,后续再详细了解原型链的东西吧!