一、工厂模式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>工厂模式</title>
</head>
<body>
<script type="text/javascript">
function createPerson(name,age,job){
var o=new Object();
o.name=name;
o.age=age;
o.job=job;
o.sayName=function(){
console.log(this.name);
};
return o;
}
var person1=createPerson("liujie",13,"student");
var person2=createPerson("lisi",14,"doctor");
person1.sayName();
person2.sayName();
</script>
</body>
</html>
可以无数次的调用这个函数,每次它都会返回一个包含3个属性和一个方法的对象。 工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题即怎样知道一个对象的类型。
二、构造函数模式
构造函数模式与工厂模式区别:
1、没有显示地创建对象
2、直接将属性和方法赋给了this对象
3、没有return语句
特别注意:构造函数始终都应该以一个大写字母开头,非构造函数应该以小写字母开头
构造函数本身也是函数,只不过用来创建对象而已
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>构造函数模式</title>
</head>
<body>
<script type="text/javascript">
function Person(name,age,job){
/*
要创建Person的新实例,必须使用new操作符
1、创建一个新对象
2、将构造函数的作用域赋值给新对象(因此this就指向了这个新对象)
3、执行这个构造函数中的代码,为这个新对象添加属性
4、返回新对象
*/
this.name=name;
this.age=age;
this.job=job;
this.sayName=function(){
console.log(this.name);
};
}
var person1=new Person("liujie",13,"student");
var person2=new Person("lisi",15,"doctor");
person2.sayName();
person1.sayName();
//constructor属性指向构造函数
console.log(person1.constructor==Person);//true
console.log(person2.constructor==Person);//true
//创建的所有对象既是Object的实例,也是Person的实例
//之所以是Object的实例,是因为所有对象均继承自Object
console.log(person1 instanceof Object);//true
console.log(person1 instanceof Person);//true
console.log(person2 instanceof Object);//true
console.log(person2 instanceof Person);//true
//构造函数问题:每个方法都要在每个实例上重新创建一遍。每个Person实例都有一个不同的Function实例
console.log(person1.sayName==person2.sayName);//false
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>构造函数模式2</title>
</head>
<body>
<script type="text/javascript">
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.sayName=function(){
console.log(this.name);
};
}
//当作构造函数使用
var person=new Person("liujie",14,"student");
person.sayName();//liujie
//当作普通函数调用
Person("lisi",16,"doctor");
window.sayName();//lis
//在另一个对象的作用域中调用
var o=new Object();
Person.call(o,"wangwu",15,"master");//这里是在对象o的作用域中调用Person构造函数,调用后o就拥有了所有属性和sayName()方法
o.sayName();//wangwu
</script>
</body>
</html>
三、原型模式
prototype就是原型对象
使用原型对象的好处就是让所有实例共享它所包含的属性和方法
每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。
所有原型对象都会自动获得一个constructor属性,这个属性包含一个指向prototype属性所在函数的指针。
这里Person.prototype.constructor指向Person
当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。换句话说,这个指针与构造函数没有关系。
每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。
搜索首先从实例本身开始。如果实例中找到了具有给定名字的属性,则返回该属性的值;如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找具有指定名字的属性。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>原型模式</title>
</head>
<body>
<script type="text/javascript">
function Person(){
}
Person.prototype.name="liujie";
Person.prototype.age=13;
Person.prototype.job="student";
Person.prototype.sayName=function(){
console.log(this.name);
};
//这里person1和person2访问的是同一组属性和同一个sayName()方法
var person1=new Person();
person1.sayName();//liujie
var person2=new Person();
person2.sayName();//liujie
console.log(person1.sayName==person2.sayName);//true
console.log(Person.prototype.isPrototypeOf(person1));//true
console.log(Person.prototype.isPrototypeOf(person2));//true
//Object.getPrototypeOf()返回的对象就是原型对象
console.log(Object.getPrototypeOf(person1)==Person.prototype);//true
console.log(Object.getPrototypeOf(person2)==Person.prototype);//true
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>原型模式2</title>
</head>
<body>
<script type="text/javascript">
function Person(){
}
Person.prototype.name="liujie";
Person.prototype.age=13;
Person.prototype.job="student";
Person.prototype.sayName=function(){
console.log(this.name);
};
//这里person1和person2访问的是同一组属性和同一个sayName()方法
var person1=new Person();
person1.name="liujiejie";
//这里说明了可以通过对象实例访问保存在原型中的值,但是不能通过对象实例重写原型中的值
//如果对象实例中的属性与实例原型中的一个属性同名,实例中的属性会屏蔽原型中的那个属性
//person1.name=null;//即使将这个属性设置为null,也只会在实例中设置这个属性,而不会恢复其指向原型的链接
delete person1.name;//如果使用delete操作符则可以完全删除实例属性,从而让我们能够重新访问原型中的属性
person1.sayName();//liujiejie
var person2=new Person();
person2.sayName();//liujie
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>原型模式3</title>
</head>
<body>
<script type="text/javascript">
function Person(){
}
Person.prototype.name="liujie";
Person.prototype.age=13;
Person.prototype.job="student";
Person.prototype.sayName=function(){
console.log(this.name);
};
//这里person1和person2访问的是同一组属性和同一个sayName()方法
//使用hasOwnProperty()方法,当给定属性存在于实例中时,返回true
var person1=new Person();
var person2=new Person();
console.log(person1.hasOwnProperty("name"));//false
person1.name="liujiejie";
console.log(person1.name);//liujiejie 来自实例
console.log(person1.hasOwnProperty("name"));//true
console.log(person2.name);//liujie 来自原型
console.log(person2.hasOwnProperty("name"));//false
delete person1.name;
console.log(person1.name);//liujie 来自原型
console.log(person1.hasOwnProperty("name"));
</script>
</body>
</html>
四、原型的动态性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>原型的动态性</title>
</head>
<body>
<script type="text/javascript">
function Person(){
}
Person.prototype = {
constructor: Person,
name : "Nicholas",
age : 29,
job : "Software Engineer",
sayName : function () {
alert(this.name);
}
};
var friend = new Person();//即使先创建了实例,后修改了原型也没有问题
Person.prototype.sayHi = function(){//向原型中添加方法
alert("hi");
};
friend.sayHi(); //"hi" – works! 先在实例中搜索sayHi方法,找不到的话再搜索原型
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>原型的动态性2</title>
</head>
<body>
<script type="text/javascript">
function Person(){
}
var friend = new Person();//调用构造函数创建实例,并向实例中添加一个指向最初原型的[[Prototype]]指针,而把原型重写修改后就等于切断了构造函数与最初原型之间的联系。
Person.prototype = {
//这里重写原型对象切断了现有的原型与任何之前已经存在的对象实例之间的联系,之前的实例引用的仍然是最初的原型
constructor: Person,
name : "Nicholas",
age : 29,
job : "Software Engineer",
sayName : function () {
alert(this.name);
}
};
friend.sayName(); //error
</script>
</body>
</html>
五、原型与in操作符
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>原型与in操作符</title>
</head>
<body>
<script type="text/javascript">
/*
只要能够访问到给定属性,in操作符就返回true。不管属性存在于原型上还是实例上。
*/
function Person(){
}
Person.prototype.name="liujie";
Person.prototype.age=13;
Person.prototype.job="student";
Person.prototype.sayName=function(){
console.log(this.name);
};
//这里person1和person2访问的是同一组属性和同一个sayName()方法
//使用hasOwnProperty()方法,当给定属性存在于实例中时,返回true
var person1=new Person();
var person2=new Person();
console.log(person1.hasOwnProperty("name"));//false
console.log("name" in person1);//true
person1.name="liujiejie";
console.log(person1.name);//liujiejie 来自实例
console.log(person1.hasOwnProperty("name"));//true
console.log("name" in person1);//true
console.log(person2.name);//liujie 来自原型
console.log(person2.hasOwnProperty("name"));//false
console.log("name" in person2);//true
delete person1.name;
console.log(person1.name);//liujie 来自原型
console.log(person1.hasOwnProperty("name"));//false
console.log("name" in person1);//true
</script>
</body>
</html>
在使用for-in循环时,返回的是所有能够通过对象访问的、可枚举的属性,其中既包括存在于实例中的属性,也包括存在于原型中的属性。屏蔽了原型中不可枚举属性的实例属性也会存在for-in循环中返回,因为根据规定,所有开发人员定义的属性都是可枚举的,但是在IE8及更早版本中例外。
<script type="text/javascript">
var obj = {
toString : function(){
return "myvalue";
}
}
for(var prop in obj){
if(prop == "toString"){
alert("toString found");
}
}
</script>
要取得对象上所有可枚举的实例属性,可以使用ECMAScript5的Object.keys()方法。这个方法接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组
<script type="text/javascript">
function Person(){}
Person.prototype.name = "lisi";
Person.prototype.age = 23;
Person.prototype.sayName = function(){
alert(this.name+"--"+this.age);
}
var keys = Object.keys(Person.prototype);
console.log(keys);//["name", "age", "sayName"]
var person1 = new Person();
person1.name = "wangwu";
person1.age = 23;
console.log(Object.keys(person1));//["name", "age"]
</script>
如果想要得到所有的实例属性,无论它是否可枚举,可以使用Object.getOwnPropertyNames()方法
<script type="text/javascript">
function Person(){}
Person.prototype.name = "lisi";
Person.prototype.age = 23;
Person.prototype.sayName = function(){
alert(this.name+"--"+this.age);
}
var keys2 = Object.getOwnPropertyNames(Person.prototype);
console.log(keys2);//["constructor", "name", "age", "sayName"]
</script>
注意:这里的结果中包含了不可枚举的constructor属性。Object.getOwnPropertyNames()和Object.keys()方法都可以用来代替for-in循环
六、简单的原型语法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>更简单的原型语法</title>
</head>
<body>
<script type="text/javascript">
/*
这里将Person.prototype设置为等于一个对象字面量形式创建的新对象,最终结果相同
但是有一个例外:constructor属性不再指向Person了
因为,每创建一个函数,就会同时创建它的prototype对象,这个对象也会自动获得constructor属性
而我们在这里本质上完全重写了默认的prototype对象,因此constructor属性也就变成了新对象的constructor属性(指向Object构造函数),不再指向Person函数。
*/
function Person(){
}
Person.prototype = {
//constructor:Person,//显式地设置构造函数为Person
//这样重设constructor属性会导致它的[[Enumerable]]特性被设置为true。默认情况下,原生的constructor属性是不可枚举的。因此,
考虑使用Object.defineProperty()方法。
name : "Nicholas",
age : 29,
job: "Software Engineer",
sayName : function () {
alert(this.name);
}
};
var friend = new Person();
alert(friend instanceof Object); //true
alert(friend instanceof Person); //true
alert(friend.constructor == Person); //false
alert(friend.constructor == Object); //true
</script>
</body>
</html>