第三章 对象高级

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]。

results matching ""

    No results matching ""