理解对象
- 内部属性特征:数据属性特征和访问器属性特征
- 数据属性特征
- [[Configurable]]:能否通过delete删除属性;能够修改属性的特征;能够把数据属性特征修改为访问器属性特征。默认为true
- [[Enumerable]]:能否通过for-in循环返回属性。默认为true
- [[Writable]]:能否修改属性的值。默认为true
- [[Value]]:这个属性的数据值。默认为undefined
- 访问器属性特征
- [[Configurable]]:能否通过delete删除属性;能够修改属性的特征;能够把访问器属性特征修改为数据属性特征。默认为true
- [[Get]]:读取属性时的行为。默认为undefined -[[Set]]:写入属性时的行为。默认为undefined
- 修改或定义属性的特征:Object.defineProperty()
var person={};
Object.defineProperty(person,"name",{
writable:false,
value:"Chris"
});
复制代码
var book={
_year:2014,
edition:1
};
Object.defineProperty(book,"year",{
get:function(){
return this._year;
},
set:function(newValue){
if(newValue>2014){
this._year=newValue;
this.edition+=newValue-2014;
}
}
})
复制代码
- 只能通过对象方法访问的属性:前置下划线记号。
- 定义多个属性:Object.defineProperties()
- Object.getOwnPropertyDescriptor()
创建对象
- 使用Object构造函数或对象字面量:使用同一个接口创建大量对象会产生许多重复的代码
- 使用工厂模式
- 用函数来封装以特定接口创建对象的细节
- 创建新对象
- 将属性和方法赋予新对象
- 使用return返回新对象
- 无法解决对象识别的问题
- 对象都是object
function createPerson(name,age,job){ //创建新对象Object var o=new Object(); //对象属性 o.name=name; o.age=age; o.job=job; //对象方法 o.sayName()=function(){ alert(this.name); } } 复制代码
- 构造函数模式
- 创建自定义构造函数意味着能够将它的实例标识为一种新的类型
- 每一个通过构造函数创建的实例,同名函数实际上是不相等的
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.sayName=function(){
alert(this.name);
}
}
var o = new Object();
Person.call(o,"Kris',25,"Doctor");
Person.sayName();//"Kris"
复制代码
- 原型模式
- 通过调用构造函数创建实例对象的原型对象
- 所有实例对象共享原型对象的属性和方法
- 任何函数都有prototype属性,这个属性指向函数的原型对象
- 原型对象会有一个constructor属性,这个属性指向拥有该prototype属性的函数
- 对象的实例拥有属性[[Prototype]],指向原型对象
- 所有实现都无法访问[[Prototype]],但是可以通过isPrototypeOf()方法来确定对象之间的关系。如果实例内部存在指向prototype的指针,那么isPrototypeOf()返回true。
Person.prototype.isPrototypeOf(person1)
(确定实例与原型之间的关系) - 通过
Object.getPrototypeOf()
可以方便地取得一个对象的原型 - 不能通过实例对象重写原型中的值,实例对象与原型对象重名的属性会屏蔽掉原型对象中的属性
- hasOwnProperty()判断属性是否是实例中的,当给定属性存在于对象实例中时才返回true
- in操作符,单独使用时,无论属性存在于实例还是原型中时都返回true。只有当属性在实例和原型中都搜索不到时才会返回false。
- for-in循环会返回的是能通过对象访问的,可枚举的属性。屏蔽了原型中不可枚举的实例属性也会在for-in循环中返回。
- Object.keys()返回所有可枚举属性的字符串
- Object.getOwnPropertyNames()返回所有属性
function Person(){ Person.prototype.name="Kris"; Person.prototype.age=25; Person.prototype.job="Doctor"; Person.prototype.sayName=function(){ alert(this.name); } } 复制代码
- 原型模式简化语法
function Person(){ } Person.prototype={ name:"Kris", age:29, job:"Doctor", sayName:function(){ alert(this.name); } } //上面的操作改变了Person Prototype中constructor的指向(指向Object)。 Object.defineProperty(Person.prototype,"constructor",{ enumerable:false,//默认情况下,constructor是不可枚举的 value:Person//简化语法会导致constructor指向Object }) 复制代码
- 原型模式的问题,省略了通过构造函数传递初始化参数这一环节。并且对于引用类型的属性,共享成为了问题。
- 组合使用构造函数模式和原型模式
- 定义引用类型的一种默认模式
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; this.friend=["Bob","Kevin"]//使用构造函数定义实例属性 } Person.prototype={ constructor:Person, sayName:function(){ alert(this.name); }//使用原型模式定义方法以及需要共享的属性 } 复制代码
- 动态原型模式
- 通过在构造函数中初始化原型
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; //初始化原型方法 if(typeof this.sayName!="function"){ Person.prototype.sayName=function(){ alert(this.name); }; } } 复制代码
- 寄生构造函数模式
- 稳妥构造函数模式
继承
- js只支持实现继承,即extends。依靠原型链实现。
- 让原型对象等于另一个类型的实例。关键代码
SubType.prototype=new SuperType()
- SubType的原型指向SuperType的原型,SuperType原型的constructor指向SuperType。所以所有SubType实例的constructor都指向SuperType
- 确定原型与实例的关系
- instanceof
instance instanceof Object
- isPrototypeOf
Object.prototype.isPrototypeOf(instance)
- instanceof
- 子类重写父类的方法或者子类添加父类中不存在的方法,这些语句需要放在继承语句(即替换原型的语句之后)。并且不能使用对象字面量创建原型方法
- 原型链继承的问题
- 创建子类实例不支持传递参数
- 原型中的引用类型
- 借用构造函数
function SubType(){ //支持传递参数 SuperType.call(this,"Kris"); //添加子类属性 this.age=29; } 复制代码
- 不支持方法复用
- 组合继承
- 使用原型链完成方法继承
- 使用借用构造函数完成属性和引用类型的继承
function SubType(name,age){ //继承属性 SuperType.call(this,name); this.age=29; } SubType.prototype=new SuperType(); SubType.prototype.constructor=SubType; SubType.prototype.sayName()=function(){ alert(this.name); } 复制代码
- 原型式继承:Object.create()
- 本质上是对传入参数对象进行一次浅复制,因此引用类型属性全实例共享
- 接受参数:一个用作新对象原型的对象和(可选的)一个为新对象定义额外属性
- 返回值:一个新对象,带着指定的原型对象和属性。
o = Object.create(Object.prototype, { // foo会成为所创建对象的数据属性 foo: { writable:true, configurable:true, value: "hello" }, // bar会成为所创建对象的访问器属性 bar: { configurable: false, get: function() { return 10 }, set: function(value) { console.log("Setting `o.bar` to", value); } } }); 复制代码
- 寄生式继承
- 对父对象进行一次浅复制
- 在封装函数中对复制后的对象进行方法增强
- 无法做到函数复用
function createAnother(original){ var clone=Object.create(original); clone.sayHi=function(){ alert("hi"); }; return clone; } 复制代码
- 寄生组合式继承
- 原有组合继承,无论什么情况下都会调用两次超类的构造函数
- 使用构造函数来继承属性,使用原型链的混成形式来继承方法
function inheritPrototype(subType,superType){ //获取父类的原型副本 var prototype=Object.create(superType.prototype); //为创建的副本添加constructor prototype.constructor=subType; //将新创建的原型副本指定为子类的原型 subType.prototype=prototype; } function SubType(name,age){ SuperType.call(this,name); this.age=age; } inheritPrototype(SubType,SuperType); 复制代码
- 只调用一次父类的构造函数,避免在子类的原型上创建多余的属性。是引用类型最理想的继承范式
转载于:https://juejin.im/post/5d08db3be51d45109725fe89