JS中对象的定义:无序属性的集合,其属性值可以包含基本值,对象或者函数。我们可以把js的对象想象成散列表,无非就是一组键值对,其中的值可以是数据或函数。
属性有两种类型:数据属性和访问器属性。
数据属性:包含一个数据值的位置,在这个位置可以读取和写入值。数据属性有四个特性:
configurable 属性能否delete掉,属性特性能否修改,能否把数据属性变为访问器属性
enumerable 能否通过for in循环返回属性
writable 能否修改属性的值
value 属性的数据值
var person = {}; //person的name属性值为Jack 不可修改值 Object.defineProperty(person,"name",{ writable:false, value:"Jack" }); alert(person.name); person.name="Grey"; alert(person.name); Object.defineProperty(person,"age",{ configurable:false, value:22 }) alert(person.age); delete person.age;//configurable为false,不能修改 alert(person.age);
访问器属性:不包含数据值,它们包含一对getter和setter函数。
configurable 属性能否delete掉,属性特性能否修改,能否把数据属性变为访问器属性
enumerable 能否通过for in循环返回属性
get 读取属性时调用的函数
set 写入属性时调用的函数
访问器属性不能直接定义,需通过defineProperty来定义。在调用该方法时,如果不指定,configurable,enumerable,writable特性的默认值都是falsevar book={ _year:2004, edition:1 }; Object.defineProperty(book,"year",{ get:function(){ return this._year; }, set:function(newValue){ if(newValue>2004){ this._year=newValue; this.edition+=newValue-2004; } } }); book.year=2006; alert(book.edition); book._year=2007;//直接设置下划线属性的值也会起作用
_year前面的下划线是一种常用的记号,用于表示只能通过对象方法定义的属性。_year是数据属性,year是访问器属性
定义多个属性 通过描述符一次定义多个属性,第一个参数是要修改的对象,第二个对象的属性与第一个对象中要添加或修改的属性一一对应
var book={}; Object.defineProperties(book,{ _year:{ value:2004 }, edition:{ value:1 }, year:{ get:function(){ return this._year; }, set:function(){ if(arguments[0]>2004){ this._year=arguments[0]; this.edition+=arguments[0]-2004; } } } }) var des=Object.getOwnPropertyDescriptor(book,"_year"); console.log(des);//读取属性描述符
6.2 创建对象
工厂模式
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("jack",22,"worker"); person1.sayname(); console.log(person1); alert(person1 instanceof Object);//工厂模式的坏处,无法确定对象类型,都是Object
构造函数模式
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; this.sayname=function(){ alert(this.name); } } var person1 = new Person("jack",33,"workers") person1.sayname(); console.log(person1); alert(person1 instanceof Person);//构造函数可以确定对象类型 但是实例上每个方法都要在实例上创建一遍
原型模式
我们创建的每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,这个对象包含特定类型的所有实例共享的属性和方法。
function Person(){ } Person.prototype.name="jack"; Person.prototype.age=29; Person.prototype.job="coder"; Person.prototype.sayname=function(){ alert(this.name); } var person1 = new Person(); person1.sayname();//jack var person2 = new Person(); person2.name="lucy"; person2.sayname();//lucy person1.sayname();//jack
三者关系
无论什么时候,只要创建一个新函数,就会为该函数创建一个prototype属性,这个属性指向函数的原型对象。而原型对象里也有一个constructor指针指向该函数。当用函数创建一个新实例以后,这个实例的内部的proto指针将指向原型。
console.log(Object.getPrototypeOf(person1));//实例对应的原型
每当读取某个对象的某个属性时,搜索首先从对象实例本身开始,如果找到了则返回对应的值,找不到到原型对象里找。
我们在实例对象里添加一个属性,即使和原型里的某个属性重名,也不会覆盖原型里的属性。
person1.city="beijing"; alert(person1.hasOwnProperty('city'));//属性在实例中返回true truealert(person1.hasOwnProperty('name'));//false
in 只要属性能找到就返回true
alert("job" in person1);for(var p in person1){//遍历所有属性,包括原型中的 alert(p); } alert(Object.keys(person1));//返回所有属性组成的字符串,不包含原型
字面量重写原型对象
function Person(){}//字面量重写原型对象Person.prototype={ constructor:Person//重写设定指针 name:"jack", age:29, job:"coder", sayName:function(){ alert(this.name); } }
原型中查找值的过程是一次搜索,因此我们对原型对象所做的任何修改都能立即从实例上反应出来,即使先创建实例后修改原型也照样如此。
原型模式的缺点:如果属性是引用类型,一个实例的修改会导致所有实例的值都被修改。
创建自定义类型的最常见方式是组合使用构造函数模式和原型模式。(实例属性用构造,方法和共享属性用原型)
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; this.friends=["lucy","bob"]; } Person.prototype={ constructor:Person, sayName:function(){ alert(this.name); } }var person1=new Person("jack",22,"worker");
继承
主要依靠原型链来完成
让某个实例的原型对象等于另一类型的实例。
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; this.friends=["lucy","bob"]; } Person.prototype={ constructor:Person, sayName:function(){ alert(this.name); } }function Student(school){ this.school=school; } Student.prototype=new Person(); Student.prototype.saySchhol=function(){ alert(this.school); };var s1=new Student("ustc"); s1.name="jack"; s1.age=12; s1.sayName();console.log(s1);
原型链的问题:一 父类的引用属性,子类一但修改,所有子类实例的值都会变化,二 无法向父类传递构造参数。
借用构造函数
在子类的构造函数内部调用父类构造函数
function Student(school){ Person.call(this);//每个子类都会有父类属性的拷贝副本 this.school=school; }
组合继承
使用原型链实现对原型属性和方法的继续,而借用构造函数来实现对实例属性的继承。
function Person(name){ this.name=name; } Person.prototype.sayname=function(){ alert(this.name); }function Student(name,school,score){ Person.call(this,name); this.school=school; this.score=score; } Student.prototype=new Person("Jack"); Student.saySChool=function(){ alert(this.school); }var s1=new Student("lucy","ustc",99);var s2=new Student("mike","autc",80);console.log(s1);console.log(s2); s1.sayname(); s2.sayname();
作者:QJK
链接:https://www.jianshu.com/p/36422e538cdb