第三章 对象高级
1. 创建对象的方式
① Object构造函数
new Object()
适用场景: 开始不确定对象的内部数据
缺点: 添加属性方法的时候,代码比较多
② 字面量/直接量方式
{属性:值}
适用场景: 一开始明确对象内部的数据
缺点: 如果创建多个对象,代码量大
③ 工厂模式
function factory() {
return {
}
}
var obj = factory();
适用场景: 需要创建的对象比较多
缺点: 对象的类型都是Object类型的,无法拥有自己的类型
④ 自定义构造函数
function Person(){
this.属性 = 值
}
⑤ 自定义构造函数与原型相结合
function Person(){
this.属性 = 值
}
Person.prototype,getName = function(){}
属性定义在构造函数内部,方法定义在构造函数的显示原型(实例的原型)
2 实现面向对象的继承
① 利用原型链继承
示例代码:
function Super() {}
function Sub() {
}
Sub.prototype = new Super();
Sub.prototype.constructor = Sub;
实现思路:
1. 让子类(Sub)的显示原型 = 父类(Super)的一个实例。
2. 设置子类(Sub)的显示原型的 constructor 属性指向子类。
缺点:
1. 无法在子类实例化的时候给父类的属性赋值。
// 定义父类
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.say = function(){
console.log('hello,My Name is '+this.name);
};
// 定义子类
function Man(name, age, info) {
this.info = info;
}
// 修改Man的显示原型(实例的原型)
Man.prototype = new Person();
Man.prototype.constructor = Man;
//创建一个 Man 的实例
var m = new Man('科比', 17, '我是男人');
m.say(); // hello,My Name is undefined
② 在子类中调用父类
示例代码:
function Super() {
}
function Sub() {
Supper.call(this, ...)
}
实现思路:
1. 在子类(Sub)中调用父类函数(Super),并改变 this 指向子类实例。
2. 父类(Super)中定义的方法会添加到子类(Sub)实例中。
缺点:
1. 无法继承到父类现实原型上的方法。
// 定义父类
function Person(name, age) {
this.name = name;
this.age = age;
this.say = function(){
console.log('hello,My Name is '+this.name);
};
}
Person.prototype.eat = function(){
console.log('我是'+this.name+',我能吃');
};
// 定义子类
function Man(name, age, info) {
//调用父类,把 this 指向 Man 的实例
Person.call(this, name, age);
//添加自己的属性
this.info = info;
}
//实例化
var m = new Man('Tom', 10, '我是男人');
console.log(m.name, m.age, m.info); // Tom 10 我是男人
m.say(); // hello,My Name isTom
m.eat(); // TypeError: m.eat is not a function
③ 组合方式
示例代码:
function Super(){
}
function Sub() {
Super.call(this)
}
Sub.prototype = new Super();
Sub.prototype.constructor = Sub;
实现思路:
1. 通过子类中调用父类函数,实现父类中属性和方法的继承。
2. 通过改变原型继承父类现实原型上的数据。
3 对象的高级特性
3.1 数据属性
数据属性包含一个数据值的位置,在我们之间所使用的对象属性一般都是数据属性。
数据属性有如下 4 个特性:
1)Configurable
:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。直接在对象上定义的属性,它们的这个特性默认值为 true。
2)Enumerable
:表示能否通过 for-in 循环返回属性。直接在对象上定义的属性,它们的这个特性默认值为 true。
3)Writable
:表示能否修改属性的值。直接在对象上定义的属性,它们的这个特性默认值为 true。
4)Value
:包含这个属性的数据值。读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置。这个特性的默认值为 undefined。
可以通过下面方式设置属性的特性:
var person = {};
Object.defineProperty(person, "name", {
writable: false,
value: "Nicholas"
});
3.2 访问器属性
访问器属性不包含数据值;它们包含一对儿 getter 和 setter 函数。在读取访问器属性时,会调用 getter 函数,这个函数负责返回有效的值;在写入访问器属性时,会调用setter 函数并传入新值,这个函数负责决定如何处理数据。
访问器属性有如下 4 个特性:
1)Configurable
:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为数据属性。
2)Enumerable
:表示能否通过 for-in 循环返回属性。
3)Get
:在读取属性时调用的函数。默认值为 undefined。
4)Set
:在写入属性时调用的函数。默认值为 undefined。
可以通过如下方式为对象设置访问器属性:
var 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;
}
}
});
3.3 定义多个属性
Object.defineProperties()
这个方法可以通过描述符一次定义多个属性。
var book = {};
Object.defineProperties(book, {
_year: {
value: 2004
},
edition: {
value: 1
},
year: {
get: function(){
return this._year;
},
set: function(newValue){
if (newValue > 2004) {
this._year = newValue;
this.edition += newValue - 2004;
}
}
}
});
3.4 读取属性特性
Object.getOwnPropertyDescriptor()
方法可以取得给定属性的描述符。
Object.getOwnPropertyDescriptor(book, "year");
4 安全的类型检测
在任何值上调用 Object 原生的 toString()方法,都会返回一个 [object NativeConstructorName] 格式的字符串。每个类在内部都有一个[[Class]] 属性,这个属性中就指定了上述字符串中的构造函数名。
console.log(Object.prototype.toString.call(value)); //"[object Array]"
Object 的 toString()方法不能检测非原生构造函数的构造函数名。因此,开发人员定义的任何构造函数都将返回[object Object]。