JS基础 - 类和对象
创建对象
- 对象直接量
对象直接量就是名/值对数据格式,见下面代码 -
var obj = { prop1: 1, prop2: '属性' }
- new关键字
"new [类名]"的方式能返回类的实例化对象, 类可以是构造函数名,也可以类名(ES2015+)
-
var Example = function() { this.prop1 = 1; this.prop2 = '属性'; }; var obj = new Example();
class Example { consturctor() { this.prop1 = 1; this.prop2 = '属性'; } } var obj = new Example();
- create关键字
这种方式可以指定属性的特性(property attribute),见下面代码(ES5+)
-
var Example = function() {}; Object.create(Example.prototype, { prop1: { value: 1 }, prop2: { value: '属性' } }
原型(prototype)
概念
- 函数对象都有prototype属性。
- prototype是一个对象,函数用于实例化后,每个实例化的对象,都从这个prototype对象继承属性。
- prototype对象的属性的变化,会影响已经实例化的继承这个prototype的对象。
- prototype有一个不可枚举的constructor属性,为当前prototype对应的构造函数,但并不意味着prototype对象是由这个constructor构造函数实例化而来。
ES2015之前的版本,类的继承就是通过prototype的继承特性来实现的,下面是一个类继承的例子:
注:SuperClass为父类,SubClass为子类。
-
var SuperClass = function() {}; var inherit = function(superClass) { var EmptyClass = function() {}; EmptyClass.prototype = superClass.prototype; var SubClass = function() {} SubClass.prototype = new EmptyClass(); SubClass.prototype.constructor = SubClass; } var SubClass = inherit(SuperClass);
原型链(prototype chain)
因每个对象都有对应的原型对象,而原型对象也是个对象,这样它也有自己的原型对象,这种链式关系就形成了一个原型链。
有两种方式获取一个实例化对象obj的prototype对象。
方式一:
-
Object.getPrototypeOf(obj)
方式二:
-
obj.__proto__
确定对象所关联的原型对象的规则是:
1) 先看对象是由哪个构造函数实例化而来,构造函数的prototype属性就是这个原型对象了。
2) 如果对象是一个函数(类)对象,找到这个函数(类)从哪个函数(类)继承而来,被继承的函数(类)对象的prototype属性就是这个原型对象。
3) 如果对象是一个构造函数的prototype属性,先按第一条规则来确定,如果无法确定这个对象是由哪个构造函数实例化而来,再看是否能确定prototype对应的构造函数是从哪个构造函数所继承而来,如能确定,这个被继承的构造函数对象的prototype属性就是这个原型对象。
4) 默认的构造函数是Object,按构造函数的继承关系,最终也会找到最上层的构造函数Object,那么原型对象也就是Object.prototype。
5) Object.prototype对象所对应的原型对象是null。
根据上面的规则,看几个原型链的例子:
注:Example是一个自定义类,obj是Example的实例对象。
1) obj –> Example.prototype –> Object.prototype –> null
2) Example–> Function.prototype –> Object.prototype –> null
3) Object –> Function.prototype –> Object.prototype –> null
4) Function –> Object.prototype –> null
对象属性
属性特性(property attribute)
从ES5开始,对象属性除了名字和值之外,还有一些其他的特性(property attribute),可以通过Object.getOwnPropertyDescriptor和Object.defineProperty这两个方法来读取或设置对象的属性特性:
- 可枚举(enumerable):是否能通过for/in遍历对象,访问到该属性。
- 可写(writable):是否能修改属性的值。当一个原型对象的属性a的writable为false,且这个原型对象的实例对象也有属性a时,只有原型对象的属性a受writable为false的影响而不能修改,但当继承这个原型对象的实例对象没有属性a,则会受原型对象writable为false的影响,而无法为实例对象赋值a属性。
- 可配置(configurable):是否能删除属性,或是否能修改属性的特性。一旦将configurable设置为false,将不能再调用Object.defineProperty对属性的行为进行修改(将enumerable/writable修改为false除外)。
属性定义
- 直接设置
-
obj.prop1 = 1; obj['prop2'] = '属性';
- set/get
-
obj = { get prop1() { return this.prop1; }, set prop1(val) { this.prop1 = val; } }
- defineProperty
-
Object.defineProperty(obj, 'prop1', { value: 1, enumerable: true, writable: true, configurable: true } Object.defineProperties(obj, { prop1: { value: 1 }, prop2: { value: '属性' } }
属性访问
-
检测属性
in:检测自有和继承属性
hasOwnProperty:检测自有属性
propertyIsEnumerable:检测自有可枚举属性 -
遍历属性
for - in:遍历自有和继承的可枚举属性
Object.getOwnPropertyNames:获取自有全部属性
Object.keys:获取自有的可枚举属性
属性删除
delete obj.prop1,可以解除属性prop1与对象obj的关联。
delete关键字,只能删除自有属性(要删除继承属性需要在prototype上操作)。