目录
1.JavaScript面向对象编程的理解
面向对象:无序属性的集合,其属性可以包含基本值、对象和函数。
每个对象的创建都是基于一个引用类型创建的,也可以是开发人员自己定义的类型
1.1对象的创建
// 早期开发人员创建对象的方式
var person = new Object()
person.name = "zhang"
person.age = 29
person.job = "engineer"
person.sayName = function(){
console.log(this.name)
}
// 使用对象字面量语法创建
var person = {
name = "zhang"
age = 23
job = "engineer"
sayName: function(){
console.log(this.name)
}
}
1.2 工厂模式创建对象
// 将对象的构建封装到函数中,可以构建任意多个preson对象
function createPerson(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
var person1 = createPerson("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");
这个中工厂模式虽然可以根据必要信息,创建任意多个对象,避免了一些手动创建代码的麻烦,但是这种构建对象无法对对象的类型进行识别。构造函数模式,可以解决这个问题,构造函数可以创建特定类型的对象。
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
};
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
变化:
1 构造函数模式中使用Person()函数取代了createperson函数,没有显式的创建对象,直接将属性和方法赋值给了this
2 createPerson中有return o, Person中没有
3 Person是new出来的
通过new操作符创建Person对象经历了一下步骤:
(1) 创建一个新对象;
(2) 将构造函数的作用域赋给新对象(因此this 就指向了这个新对象);
(3) 执行构造函数中的代码(为这个新对象添加属性);
(4) 返回新对象。person1和person2都有一个constructor属性,该属性指向Person
通过instanceof验证对象的属性
alert(person1 instanceof Object); //true
alert(person1 instanceof Person); //true
alert(person2 instanceof Object); //true
alert(person2 instanceof Person); //true通过此特性,可以应用到对象的类型辨别上,用来标识对象的类型。
任何函数,只要通过new 操作符来调用,那它就可以作为构造函数;而任何函数,如果不通过new 操作符来调用,那它跟普通函数也不会有什么两样
1.2 原型模式
使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。不必在构造函数中定义对象实例的信息,而是
可以将这些信息直接添加到原型对象中。function Person(){ } Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function(){ alert(this.name); }; var person1 = new Person(); person1.sayName(); //"Nicholas" var person2 = new Person(); person2.sayName(); //"Nicholas" alert(person1.sayName == person2.sayName); //true
当person1调用sayName()时,会先后执行两次搜索,首先去person1的实例中寻找,如果没有再去person1的原型中取找,如果实例中有,则调用实例中的此方法。
虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。如果我们在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,那我们就在实例中创建该属性,该属性将会屏蔽原型中的那个属性。
虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。如果我们在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,那我们就在实例中创建该属性,该属性将会屏蔽原型中的那个属性。
function Person(){ } Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function(){ alert(this.name); }; var person1 = new Person(); var person2 = new Person(); person1.name = "Greg"; alert(person1.name); //"Greg"——来自实例 alert(person2.name); //"Nicholas"——来自原型 delete person1.name; alert(person1.name); //"Nicholas"——来自原型
使用hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中。
function Person(){ } Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function(){ alert(this.name); }; var person1 = new Person(); var person2 = new Person(); alert(person1.hasOwnProperty("name")); //false person1.name = "Greg"; alert(person1.name); //"Greg"——来自实例 alert(person1.hasOwnProperty("name")); //true alert(person2.name); //"Nicholas"——来自原型 alert(person2.hasOwnProperty("name")); //false delete person1.name; alert(person1.name); //"Nicholas"——来自原型 alert(person1.hasOwnProperty("name")); //false
无论该属性存在于实例中还是原型中,in 操作符会在通过对象能够访问给定属性时返回true,
function Person(){ } Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function(){ alert(this.name); }; var person1 = new Person(); var person2 = new Person(); alert(person1.hasOwnProperty("name")); //false alert("name" in person1); //true person1.name = "Greg"; alert(person1.name); //"Greg" ——来自实例 alert(person1.hasOwnProperty("name")); //true alert("name" in person1); //true alert(person2.name); //"Nicholas" ——来自原型 alert(person2.hasOwnProperty("name")); //false alert("name" in person2); //true delete person1.name; alert(person1.name); //"Nicholas" ——来自原型 alert(person1.hasOwnProperty("name")); //false alert("name" in person1); //true
in 操作符只要通过对象能够访问到属性就返回true,
hasOwnProperty()只在属性存在于实例中时才返回true,
// 同时使用hasOwnProperty()方法和in 操作符,就可以确定该属性到底是存在于对象中,还是存在于原型中 function hasPrototypeProperty(object, name){ return !object.hasOwnProperty(name) && (name in object); } function Person(){ } Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function(){ alert(this.name); }; var person = new Person(); alert(hasPrototypeProperty(person, "name")); //true person.name = "Greg"; alert(hasPrototypeProperty(person, "name")); //false // name 属性先是存在于原型中,因此hasPrototypeProperty()返回true。当在实例中重写name 属性后,该 // 属性就存在于实例中了,因此hasPrototypeProperty()返回false
1.3实例枚举
Object.keys()。这个方法接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。只获取可枚举的实例属性。
function Person(){ } Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function(){ alert(this.name); }; var keys = Object.keys(Person.prototype); alert(keys); //"name,age,job,sayName" var p1 = new Person(); p1.name = "Rob"; p1.age = 31; var p1keys = Object.keys(p1); alert(p1keys); //"name,age"
如果你想要得到所有实例属性,无论它是否可枚举,使用Object.getOwnPropertyNames()方法。
function Person(){ } Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function(){ alert(this.name); }; var keys = Object.getOwnPropertyNames(Person.prototype); alert(keys); //"constructor,name,age,job,sayName"
原型的另一种封装写法
function Person(){ } Person.prototype = { name : "Nicholas", age : 29, job: "Software Engineer", sayName : function () { alert(this.name); } };
此方法完全重写了Person.prototype,constructor 属性不再指向Person 了,而是指向了Object,通过constructor已经无法确定对象的类型了。
如下写法就可解决上面constructor属性指向的问题
function Person(){ } Person.prototype = { constructor : Person, name : "Nicholas", age : 29, job: "Software Engineer", sayName : function () { alert(this.name); } };