主旨:如何"封装"数据和方法,以及如何从原型对象生成实例。
创建对象的方法
工厂模式
var book={
"main title":"javaScript",
"for":"all audiences",
author:{
firstname:"David"
}
};
属性名可以是js标识符但是必须用引号引起来。如:“for”
对象直接量是一个表达式,每次运算都创建并初始化一个新对象。
构造函数模式
W:为了解决从原型生成实例的问题,有了构造函数。
H:关键字new后跟一个函数调用。如:function cat(){}; var cat=new Cat();
这里的Cat()被称为构造函数。
实际上并不存在所谓的“构造函数”,只有对于函数的“构造调用
其实构造函数就是一个内部使用了 this变量 普通的函数,因为使用了 new 运算符 生成了实例 且 this会绑定在实例对象上。
使用 new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。
- 创建(或者说构造)一个全新的对象。
- 这个新对象会被执行 [[ 原型 ]] 连接。
- 这个新对象会绑定到函数调用的 this。
- 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。
//这是原型对象
function Cat(name,color){
this.name=name;
this.color=color;
}
//这是实例对象
var cat1=new Cat('大白','白色');
var cat2=new Cat('大黄','黄色');
虽然这个模式简单好用,但是每次new一个实例出来都会有一个新的作用域链,这样很浪费,这么多都是同一个任务,所以有了下面的模式。
prototype模式
js规定我们创建的每个函数都有一个prototype 属性,这个属性指向一个对象,这个对象上所有的方法和属性都会被构造出的实例继承。
所以我们把一些不会变的属性和方法,直接定义在这个prototype对象上。
function Cat(name,color){
this.name=name;
this.color=color;
}
Cat.prototype.type="猫科动物";
Cat.prototype.eat=function(){alert("吃老鼠!")};
//生成实例
var cat1=new Cat('大黑','黑色');
cat1.eat();//吃老鼠
console.log(cat1.type);//猫科动物
这时所有实例的type属性和eat()方法,其实都是同一个内存地址,指向prototype对象,因此就提高了运行效率。
要理解原型模式,就让我们先理解原型对象。
原型对象:
每个被创建的新函数,都会有一个prototyoe属性,该属性指向函数的原型对象。
而原型对象自动获取一个constructor的属性,它指向了函数Cat。
Cat.prototype.constructor指向Cat。
没错他俩互相指。
在我们做了这个操作后:
//生成实例
var cat1=new Cat('大黑','黑色');
生成的cat1这个实例里就有了一个指针[[prototype]],看样子就知道是内部属性,指向它的原型对象Cat。虽然是内部属性,但是firefox,chrome,safari,在每个对象上支持一个__proto__属性。(注意:左右各是2个下划线)。
关于原型对象属性的操作方法
检测
isPrototypeOf():判断某个prototype对象和某个实例之间的关系
alert(Cat.prototype.isPrototypeOf(cat1)); //true
hasOwnProperty():判断一个属性到底是从原型继承来的还是自定义的本地属性。
alert(cat1.hasOwnProperty("name")); // true
alert(cat1.hasOwnProperty("type")); // false
枚举
in运算符,看某个实例里面有没有这个属性
alert("name" in cat1); // true
in运算符还可以用来遍历某个对象的所有属性。
for(var prop in cat1){
alert("cat1["+prop+"]="+cat1[prop]);
}
instanceof:验证原型对象和实例对象之间是否有关系
alert(cat1 instanceof Cat); //true
删除
删除属性的唯一方法是使用 delete 操作符;设置属性为 undefined 或者 null 并不能真正的删除属性, 而仅仅是移除了属性和值的关联。
var obj = {
bar: 1,
foo: 2,
baz: 3
};
obj.bar = undefined;
obj.foo = null;
delete obj.baz;
for(var i in obj) {
if (obj.hasOwnProperty(i)) {
console.log(i, '' + obj[i]); // bar undefined 和 foo null 只有 baz 被真正的删除了,
}
}
delete只能删除自有属性,不能删除继承属性。(想想也知道,你妈只是把房子给你住,这房子还是她的你有啥权卖房)
tips:
如果
var a={p:{x:1}};
b=a.p;
delete a.p;
//执行完上面后 b.x的值依然是1. delete不能删除属性中的属性。自己遍历属性中的属性,依此删除
组合使用构造函数模式和原型模式
function Cat(name,color,age){
this.name=name;
this.colre=color;
this.age=age;
};
Cat.prototype={
constructor:Cat,
say:function(){
alert('你好我是'+this.name);
}
};
var cat1=new Cat('小黑','黑色','10');
console.log(cat1.say());//你好我是小黑
因Cat这个函数对象一创建就有了prototype属性,就有了constructor属性指向object。但是为了实现这个模式,于是我们在定义Cat的prototype属性时,要让constructor属性指向Cat。
寄生构造函数模式
和构造函数模式很像,但是在结尾return 了,重写调用构造函数时返回的值。
function SpecialArray(){
var values=new Array();
values.push.apply(values,arguments);
values.toPipedString=function(){
return this.join("|");
};
return values;
}
var colors=new SpecialArray("red","blue","green");
alert(colors.toPipedString());
tips:返回的对象和构造函数或者构造函数的原型属性之间没有关系。所以不能依赖instanceof操作符来确定对象类型。
稳妥构造函数模式
function Person(name,age,job){
var o=new Object();
o.sayName=function(){
alert(name);
}
return o;
}
var friend=Person("xiaoming","20","清洁工");
friend.sayName();
undefined
friend里保存的是一个稳妥对象,除了调用sayname()外别无他法访问其数据成员。即使有其他代码会给这个对象添加方法或者数据成员,但也不可能有别的方法访问传入到构造函数中的原始数据。
参考资料:http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_encapsulation.html

被折叠的 条评论
为什么被折叠?



