对象的定义
在ECMAScript-262中,对象被定义为**“无序属性的集合,其属性可以包含基本值,对象或者函数”**。
也就是说,在JavaScript中,对象无非就是由一些列无序的key-value对组成。其中value可以是基本值,对象或者函数。
对象的原型
对象的创建
1、简单对象的创建
通过new操作符,new的过程参考 Js高级---new
var obj = new Object();
通过对象字面量
var obj = {};
Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
Object.create(proto[, propertiesObject])
2、复杂对象的创建
对象的属性
在ECMAScript5中,对每个属性都添加了几个属性类型,来描述这些属性的特点。他们分别是:
configurable: 表示该属性是否能被delete删除。当其值为false时,其他的特性也不能被改变。默认值为trueenumerable: 是否能枚举。也就是是否能被for-in遍历。默认值为truewritable: 是否能修改值。默认为truevalue: 该属性的具体值是多少。默认为undefinedget: 当我们通过person.name访问name的值时,get将被调用。该方法可以自定义返回的具体值是多少。get默认值为undefinedset: 当我们通过person.name = 'Jake'设置name的值时,set方法将被调用。该方法可以自定义设置值的具体方式。set默认值为undefined
需要注意的是,不能同时设置value、writable 与 get、set的值。
我们可以通过Object.defineProperty方法来修改这些属性类型。
下面我们用一些简单的例子来演示一下这些属性类型的具体表现。
configurable
// 用普通的方式给person对象添加一个name属性,值为TOM
var person = {
name: 'TOM'
}
// 使用delete删除该属性
delete person.name; // 返回true 表示删除成功
// 通过Object.defineProperty重新添加name属性
// 并设置name的属性类型的configurable为false,表示不能再用delete删除
Object.defineProperty(person, 'name', {
configurable: false,
value: 'Jake' // 设置name属性的值
})
// 再次delete,已经不能删除了
delete person.name // false
console.log(person.name) // 值为Jake
// 试图改变value
person.name = "alex";
console.log(person.name) // Jake 改变失败
enumerable
var person = {
name: 'TOM',
age: 20
}
// 使用for-in枚举person的属性
var params = [];
for(var key in person) {
params.push(key);
}
// 查看枚举结果
console.log(params); // ['name', 'age']
// 重新设置name属性的类型,让其不可被枚举
Object.defineProperty(person, 'name', {
enumerable: false
})
var params_ = [];
for(var key in person) {
params_.push(key)
}
// 再次查看枚举结果
console.log(params_); // ['age']
writable
var person = {
name: 'TOM'
}
// 修改name的值
person.name = 'Jake';
// 查看修改结果
console.log(person.name); // Jake 修改成功
// 设置name的值不能被修改
Object.defineProperty(person, 'name', {
writable: false
})
// 再次试图修改name的值
person.name = 'alex';
console.log(person.name); // Jake 修改失败
value
var person = {}
// 添加一个name属性
Object.defineProperty(person, 'name', {
value: 'TOM'
})
console.log(person.name) // TOM
get/set
var person = {}
// 通过get与set自定义访问与设置name属性的方式
Object.defineProperty(person, 'name', {
get: function() {
// 一直返回TOM
return 'TOM'
},
set: function(value) {
// 设置name属性时,返回该字符串,value为新值
console.log(value + ' in set');
}
})
// 第一次访问name,调用get
console.log(person.name) // TOM
// 尝试修改name值,此时set方法被调用
person.name = 'alex' // alex in set
// 第二次访问name,还是调用get
console.log(person.name) // TOM
请尽量同时设置get、set。如果仅仅只设置了get,那么我们将无法设置该属性值。如果仅仅只设置了set,我们也无法读取该属性的值。
Object.defineProperty只能设置一个属性的属性特性。当我们想要同时设置多个属性的特性时,需要使用我们之前提到过的Object.defineProperties
var person = {}
Object.defineProperties(person, {
name: {
value: 'Jake',
configurable: true
},
age: {
get: function() {
return this.value || 22
},
set: function(value) {
this.value = value
}
}
})
person.name // Jake
person.age // 22
读取属性的特性值
我们可以使用Object.getOwnPropertyDescriptor方法读取某一个属性的特性值。
var person = {}
Object.defineProperty(person, 'name', {
value: 'alex',
writable: false,
configurable: false
})
var descripter = Object.getOwnPropertyDescriptor(person, 'name');
console.log(descripter); // 返回结果如下
descripter = {
configurable: false,
enumerable: false,
value: 'alex',
writable: false
}
对象的方法
1.内置方法
Object.is() , Object.assign() 等
参考:es6对象的新增方法
2.实例方法
function Person(name,age){
// 实例属性
this.name = name
this.age = age
// 实例方法
this.eat = function(){
console.log(this.name + '吃饭')
}
}
// 通过构造函数创建出实例p
let p = new Person("123",28)
// 通过实例p去访问实例属性
console.log(p.name)
// 通过实例p去访问实例方法
p.eat()
3.静态方法
function foo(){
this.show = function(){
return this;
}
}
foo.test = 123; //静态属性
foo.say = function(){//静态方法
return this;
}
foo.say();
var fn = new foo(); //实例化的新的对象,this指向这个新的对象,不能访问类的静态方法
fn.say(); //Noname1.html:45 Uncaught TypeError: fn.say is not a function
console.log(foo.say() == fn.say());
4.原型方法
function Person(name,age){
this.name = name
this.age = age
}
Person.prototype.eat = function(){ // 使用prototype找到该Person的原型对象
console.log(this.name + '吃饭')
}
let p1 = new Person("123",28)
let p2 = new Person("swr",28)
console.log(p1.eat === p2.eat) // true
p1.eat()
对象的访问
假如我们有一个简单的对象如下:
var person = {
name: 'TOM',
age: '20',
getName: function() {
return this.name
}
}
当我们想要访问他的name属性时,可以用如下两种方式访问。
person.name
// 或者
person['name']
如果想要访问的属性名是一个变量时,常常会使用第二种方式。例如我们要同时访问person的name与age,可以这样写:
['name', 'age'].forEach(function(item) {
console.log(person[item]);
})
这种方式一定要重视,记住它以后在我们处理复杂数据的时候会有很大的帮助。
对象的拷贝
浅拷贝和深拷贝
封装
封装的定义:
- 把对象的状态和行为看成一个统一的整体,将二者抽象在一个独立的模块中,比如:类;
- 细节隐藏, 把不想对外公开的实现细节隐藏起来,保证调用者安全访问,不会出现因为越界导致本不应该出现的问题出现;
封装的好处:
- 调用者能够正确、方便地使用系统功能,有效地防止调用者随意修改系统属性,提高安全性。
- 把特定的功能封装起来,提高功能的重用性。
- 降低功能组件之间的耦合性,即使一个模块的实现细节发生改变,只要保证对外暴露的接口或者方法保持不变,就不会影响到其他调用者。
封装的方式:
继承
继承的定义:
从面向对象的角度上说,继承是一种从一般到特殊的关系,是一种“is a”的关系,即子类是对父类的拓展,是一种特殊的父类,所以可以基于父类并对其加以拓展,产生新的子类定义,这就是继承。
继承的好处:
子类可以通过继承获得父类原有的字段和方法,也可以增加父类所没有的字段和方法,更是可以覆写父类中的允许被子类覆盖的字段或者方法。
继承的方式:
参考 Js高级---继承
多态
多态的定义:
允许将子类类型的指针赋值给父类类型的指针。
多态的表现:
方法重载:重载是指不同的函数使用相同的函数名,但是函数的参数个数或类型不同。调用的时候根据函数的参数来区别不同的函数。
方法重写:重写(也叫覆盖)是指在派生类中重新对基类中的虚函数(注意是虚函数)重新实现。即函数名和参数都一样,只是函数的实现体不一样。
参考文章
轻松理解JS中的面向对象,顺便搞懂prototype和__proto__