面向对象
一、理解对象
每当new一个person对象都有相同的属性和方法。这些属性在创建时都带有一些特征值
let p = { name: "nihao"; age : "26"; sayName : function () { alert ( this.name ); } }
ECMAScript中有两种属性数据属性和访问器属性。
1、数据属性
- [[Configurable]]:表示能否通过delete删除属性,能否修改属性,默认true
- [[Emumerable]]:是否可枚举,遍历 for-in
- [[Writable]]:修改属性的值
- [[Value]]::包含这个数据属性的值
修改默认特性, Object.defineProperty( obj,"属性名","描述符对象" )
var person = {};
Object.defineProperty(person, "name", {
writable: false,
value: "aa"
});
console.log(person.name);//aa
person.name="bb";
2、访问器属性
- [[Configurable]]:表示能否通过delete删除属性,能否修改属性,默认true
- [[Emumerable]]:是否可枚举,遍历 for-in
- [[Get]]:在读取的时候调用的函数,默认undefined
- [[Set]]:在写入的时候调用的函数,默认undefined
访问器属性不能直接定义,必须使用Object.defineProperty()来定义
var book = { _year: 2004, edition: 1 } Object.defineProperty(book,"year",{ get: function () { return this._year; }, set: function ( newYear ) { if ( newYear > 2004 ) { this._year = newYear; this.edition += newYear - 2004; } } }) book.year = 2005; alert(book.edition) //2 //定义多个属性,区别同一时间创建 var book = {}; Object.defineProperties( book, { _year:{ value:2004, } edition: { value: 1, } year: { get: function () { return this._year; }, set: function ( newYear ) { if ( newYear > 2004 ) { this._year = newYear; this.edition += newYear - 2004; } } } })
读取属性特性:Object.getOwnPropertyDescriptor( obj,"属性名" )
二、创建对象
Object构造函数或对象字面量都可以创建单个对象,他们的缺点:使用同一个接口创建很多对象,会产生大量的重复代码;于是有了很多设计模式来结局以上问题;
设计模式
1、工厂模式:在函数内创建一个对象,给对象赋予属性及方法再将对象返回
function Person() {
var People = new Object();
People.name = 'nihao';
People.age = '25';
People.sayName= function(){
return this.name;
};
return People;
}
var a = Person();
console.log(a.name);//nihao
优点:同一类型同一属性可以重复调用;
缺点:同一类型不同属性值的不能调用;
2、构造函数模式:无需在函数内部重新创建对象,在函数内使用 this 关键字,创建属性和方法。再使用 new 运算符创建实例,通过传参生成不同的实例。
function Person() {
this.name = 'nihao';
this.age = '25';
this.sayName = function(){
return this.name;
};
}
var a = new Person();
console.log(a.name);//nihao
console.log(a.sayName());//nihao
缺点:每个方法都要在每个实例上重新创建一遍,因js中函数是对象,因此每定义一个函数,也就实例化了一个对象。不同实例上的同名函数不相等
function Person() {
this.name = 'nihao';
this.age = '25';
this.sayName = sayName ; //将sayName 属性设置等于全局的sayName函数,此时sayName 包含了指向函数的指针,因而不同实例共享全局作用域中定义的同一个sayName()函数
}
function sayName () {
alert(this.name )
}
缺点:需要全局定义函数,破坏了封装性
优点:构造函数模式解决了工厂设计模式所遇到的问题
3、原型模式:创建一个构造函数,再函数外为通过prototype方式添加属性和方法,最后通过new 运算符生成实例,可以让所有对象实例共享它所包含的属性及方法
function Person() {
}
Person.prototype.name= "nihao";
Person.prototype.age= 25;
Person.prototype.sayName = function() {
alert(this.name);
};
var p1= new Person();
var p2= new Person();
优点:语义上,看起来所有属性都是同一个对象,解决了上两种方式所遇到的问题;
缺点:不能通过给构造函数传递参数来初始化属性的值,对于包含引用类型来说,如果其中某个实例属性重新赋值,会导致其他的实例同一属性也会发生变化
function Person() {
}
Person.prototype.name= "nihao";
Person.prototype.age= 25;
Person.prototype.friends = ["张三","李四"]
Person.prototype.sayName = function() {
alert(this.name);
};
var p1= new Person();
var p2= new Person();
p1.friends.push("王老五");
console.log(p1.friends) //"张三","李四","王老五"
console.log(p2.friends) //"张三","李四","王老五"
4、混合的构造函数/原型方式:利用构造函数定义实例属性,利用原型方式来定义共享的方法和属性
function Person() { this.name = 'nihao'; this.age = '25'; this.friends = ["张三","李四"]; } Person.prototype = { constructor: Person, sayName: function () { alert(this.name) } }