1. 对象
在JavaScript中,一个对象可以理解为一组无序属性的集合。每个对象都是基于一个引用类型创建的,引用类型的值(对象)是引用类型的一个实例。
2. 类
JavaScript并不具备传统OO语言所支持的类和接口等基本结构。ECMAScript中与类类似的是引用类型。《JavaScript高级程序设计》对引用类型是这样描述的:
引用类型是一种数据结构,用于将数据和功能组织在一起。它也常被称为类,但这种称呼并不妥当。虽然引用类型与类看起来相似,但他们并不是相同的概念。
创建对象的几种模式:
补充:记住这些模式名称可以大大提高沟通效率、加深对各种模式的优劣理解
- 工厂模式——创建工厂函数,封装了以特定接口创建对象的具体细节;但是没有解决对象识别问题
- 构造函数模式——解决了对象识别问题;但是每个方法都要在实例上重新创建一遍
- 原型模式——原型包含了实例共享的方法属性;
- 构造+原型——取长避短:)
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype = {
constructor: Person,
talk: function () {
console.log('talking。。。')
}
}
↓Attention↓
function Person(name, age) {
this.name = name;
this.age = age;
//其实这种方式也不推荐,毕竟每次调用构造函数都会执行一次
Person.prototype.speak = function(){}
//不要在构造函数内部重置原型,此次创建的新实例已
//经指向到‘类’的原型,而下列语句会重置'类'的原型。
Person.prototype = {
constructor: Person,
talk: function () {
console.log('talking。。。')
}
}
}
- 动态原型——把所有信息都封装在构造函数里(个人不太喜欢),不再在构造函数外设置原型;
function Person(){
if(typeof this.smMtd != 'function'){
Person.prototype.smMtd = function(){}
}
}
- 寄生构造函数——创建一个函数用来封装创建对象的代码,然后返回新创建的对象;除了使用new操作符,和工厂模式是一毛一样的;同样存在对象识别问题
如果不能修改现有构造函数,可以考虑这个模式:
function SpecialArray(){
var a = new Array()
a.push.apply(a, arguments)
a.newMethod = function(){return ''}
return a
}
var a = new SecialArray('a', 'b', 'c')
- 稳妥构造函数——创建稳妥对象(没有公共属性、不使用this;不使用new操作符);同样存在对象识别问题
/**
* 这种模式创建的对象,除了使用sayName方法,没有其他方式可以访问数据成员
* 传入构造函数的原始数据是安全的
*/
function Person(name){
var o = new Object()
o.sayName = function(){console.log(name)}
return o;
}
var p = Person('Frank')
- Object构造函数/对象字面量——使用同一个接口创建很多对象会产生大量的重复代码。
var ob = new Object();
终极构造方案( ̄▽ ̄)/
var MyCls = (function(){
//静态私有变量
var i = 0
//静态私有方法
var l = function(){console.log('0.1')}
//构造函数
function _(p1, p2, p3){
if(!(this instanceof _)){
return new _(p1, p2, p3)
}
//私有变量
var a, b, c
//公有属性
this.p1 = p1
}
_.prototype = {
constructor: _,
//静态公有变量
v: 0.1
}
//对象无法访问的静态公有变量
_.t = 0
return _
}());
扩展
属性类型
ES5描述了属性的各种特征,这些特性只有内部才使用。对象的属性(property)在创建时都带有一些特征值(characteristic),JavaScript通过这些特征值来定义它们的行为。ES5定义这些特性是为了实现JavaScript引擎用的,因此在JavaScript中不能直接访问它们。
ES中有两种属性:数据属性和访问器属性;
-
要修改属性默认的特性,必须使用ES5的
Object.defineProperty(object, property, descriptor)
方法。在调用此方法 创建新的属性时,如果不指定特征值,configurable、enumerable和writable特性的默认值都是false
-
读取给定属性的描述符可以使用
Object.getOwnPropertyDescriptor(object, property)
-
访问器属性不能直接定义,必须使用
defineProperty()
来定义。
数据属性:数据属性有4个特性,包含一个数据值的位置:
[[Configurable]]:能否 delete、修改属性特性、把属性修改为访问器属性。直接定义在对象上的属性此特征值默认为true
//注意 此特性标识 在对象上执行 for-in循环时,该属性是否会被返回
[[Enumerable]]:能否通过for-in 循环返回属性()。直接定义在对象上的属性此特征值默认为true
[[Writable]]:能否修改属性的值。直接定义在对象上的属性此特征值默认为true
[[Value]]:包含这个属性的数据值,读写都在这个位置。默认为undefined
访问器属性:访问器属性不包含数据值,但包含getter和setter函数(非必需)。其有4个特性:
[[Configurable]]:能否 delete、修改属性特性、把属性修改为数据属性。直接定义在对象上的属性此特征值默认为true
[[Enumerable]]:能否通过for-in 循环返回属性。直接定义在对象上的属性此特征值默认为true
[[Get]]:在读取属性时调用的函数,默认值为undefined
[[Set]]:在写入属性时调用的函数,默认值为undefined
eg.
var frank = {
name: 'frank',
_age: 25, //下划线前缀是一种常用记号, 表示该变量应通过对象方法访问
year: 2018
}
Object.defineProperty(frank, 'age', {
get: function(){
return this._age;
},
set: function(newValue) {
this.year += newValue - this._age
this._age = newValue
}
});
Object.defineProperties
//定义/修改多个属性
var person = {};
Object.defineProperties(person,{
_name: {
writable: true,
value: 'frank'
},
age: {
}
});
Object.getOwnPropertyDescriptor
//取得给定属性的描述符对象
var propObj = Object.getOwnPropertyDescriptor(frank, 'name')
//propObj://{value: "frank", writable: true, enumerable: true, configurable: true}
Object.getOwnPropertyDescriptors
//取得对象所有属性的描述符
var propObj = Object.getOwnPropertyDescriptors(person)
//{name:{value: "frank", writable: true, enumerable: true, configurable: true}}
网友评论