《JavaScript高级程序设计》第6章 面向对象程序设计

6.1 对象属性

6.1.1 属性类型

1. 数据属性

我们一般所说的属性就是数据属性,它用来将一个字符串名称映射到某个值上

数据属性的4个特性: configurable, enumerable, writable, value
要修改属性默认的特性,必须使用Object.defineProperty()方法
Object.defineProperty(对象,属性名,描述符对象)

var person = {};
Object.defineProperty(person, ‘name‘, {
  writable: false,
  value: ‘natsu‘
});
console.log(person.name);
person.name = ‘natsu12‘;
console.log(person.name); // 非严格模式下被忽略,严格模式下抛出错误

把configurable设置为false后,不能再修改除writable之外的特性

var person = {};
Object.defineProperty(person, ‘name‘, {
  configurable: false
});
Object.defineProperty(person, ‘name‘, {   // 抛出错误
  configurable: true
});

2. 访问器属性

访问器属性不能直接定义,必须使用Object.defineProperty来定义,定义了get和set特性的属性即成为访问器属性。
在读取访问器属性的时候会调用getter函数,在写入访问器属性的时候会调用setter函数并传入新值。
访问器属性的4个特性: configurable, enumerable, get, set
只指定get函数意味着属性只能读不能写

/* 以下实例当修改访问器属性year时会导致数据属性_year和edition的变化 */
var book = {
  _year: 2014,
  edtion: 1
};

Object.defineProperty(book, ‘year‘, {
  get: function() {
    return this._year;
  },
  set: function(newValue) {
    if (newValue > 2014) {
      this._year = newValue;
      this.edtion = newValue - 2013;
    }
  }
});

console.log(book.year);   // 读year的时候调用get函数,2014
book.year = 2015;         // 写year的时候调用set函数
console.log(book.edtion); // 2

6.2 创建对象

6.2.1 工厂模式

这种模式抽象了创建具体对象的过程,虽然解决了创建多个相似对象的问题,却没有解决对象识别的问题(即怎样知道一个对象的类型)。

function createPerson (name, age, job) {
  var o = new Object();
  o.name = name;
  o.age = age;
  o.job = job;
  o.sayName = function () {
    console.log(this.name);
  };
  return o;
}

var person1 = createPerson(‘Nicholas‘, 29, ‘Software Engineer‘);
person1.sayName();  // Nicholas

6.2.2 构造函数模式

要创建Person的新实例,必须使用new操作符,这种调用构造函数方式实际上经历以下四个步骤:
1.创建一个新对象;
2.将构造函数的作用域赋给新对象(因此this指向这个新对象)
3.执行构造函数中的代码(为这个新对象添加属性)
4.返回新对象

function Person (name, age, job) {
  this.name = name;
  this.age = age;
  this.job = job;
  this.sayName = function () {
    console.log(this.name);
  };
}

var person1 = new Person(‘Nicholas‘, 29, ‘Software Engineer‘);
var person2 = new Person(‘Greg‘, 27, ‘Doctor‘);

/* 创造的对象都有一个constructor属性,指向构造函数 */
console.log(person1.constructor == Person); // true

/* 创建的对象既是Person的实例,也是Object的实例,因为所有对象均继承自Object */
console.log(person1 instanceof Person); // true
console.log(person1 instanceof Object); // true

创建自定义的构造函数意味这将来可以将它的实例标识为一种特定类型,而这正是构造函数模式胜过工厂模式的地方(工厂模式无法知道对象的类型)。

构造函数的的多重身份

构造函数也是函数,通过new操作符来调用,它就可以作为构造函数,如果不通过new来调用,则跟普通函数没有区别。

// 当作构造函数使用
var person = new Person(‘Nicholas‘, 29, ‘Software Engineer‘);
person.sayName();  // "Nicholas"
// 当作普通函数使用
Person(‘Greg‘, 27, ‘Doctor‘);   // 严格模式下会报错,非严格模式下this绑定到window对象
window.sayName();  // "Greg"
// 在另一个对象的作用域中调用
var o = new Object();
Person.call(o, ‘Kristen‘, 25, ‘Nurse‘);
o.sayName();      // "Kistten"

构造函数模式的问题

构造函数模式的每个方法都要在每个实例上重新创建一遍,然而创建两个完成同样任务的函数实例是没有必要的(浪费空间)。

console.log(person1.sayName == person2.sayName);  // false
// 说明不同实例上的同名函数是不相等的,但它们是完成同样任务的函数

6.2.3 原型模式

通过原型模式创建的实例,会共享原型对象中的所有属性和方法。

function Person() {}

Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function () {
  console.log(this.name);
};

var person1 = new Person();
var person2 = new Person();
console.log(person1.sayName == person2.sayName);  // true

只要创建了一个新函数,就会为该函数自动增加一个prototype属性,指向该函数的原型对象。
该原型对象拥有一个constructor属性,反指回这个新函数,初始时原型对象只取得constructor属性,其他方法从Object继承而来。
调用构造函数创建一个新实例后,会为该实例自动增加一个__proto__属性,指向构造函数的原型对象。
上面的例子中,两个person实例都不包含属性和方法,却可以调用sayName,是通过原型对象查找对象属性来实现的。

在读取某个对象的属性时,首先从对象实例本身开始搜索,然后继续搜索__proto__指向的原型对象

如果我们在实例中添加一个同名属性,则该属性会屏蔽原型中的那个属性。

function Person() {}

Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function () {
  console.log(this.name);
};

var person1 = new Person();
var person2 = new Person();
person1.name = "Greg";
person1.sayName();  // "Greg"——来自实例
person2.sayName();  // "Nicholas"——来自原型

使用hasOwnProperty()方法可以判断属性是不是实例属性,如果属性是对象自身的实例属性则返回true,如果是来自原型对象则返回false。

// 接上面的例子
console.log(person1.hasOwnProperty("name"));  // true
console.log(person2.hasOwnProperty("name"));  // false
delete person1.name;
person1.sayName();  // "Nicholas"——来自原型
console.log(person1.hasOwnProperty("name"));  // false

in操作符(propertyName in object):只要通过对象能够访问到某个属性就返回true,无论是直接在对向上访问到的,还是通过原型访问到的。

for in:可以遍历所有通过对象访问的(包括实例和原型)、可枚举的属性。

原型的动态性:
可以随时为原型添加属性和方法,对原型对象所做的任何修改都能够立即从实例上反映出来。

请注意,重写原型对象会导致切断现有原型与之前任何已经存在的对象实例之间的联系。

原型对象的问题:当包含引用类型值的属性时,所有实例都被迫共享。

例如某个属性friends是数组,当调用person1.friends.push增加一个元素时,person2.friends也会跟着修改。

6.2.4 组合使用构造函数模式和原型模式

最常见的方式,构造函数模式用于定义实例属性(每个实例自己独有,不共享),而原型模式用于定义方法和共享的属性。

6.2.5 动态原型模式

将所有信息都封装在了构造函数中,在构造函数中初始化原型。

function Person(name, age, job) {
  this.name = name;
  this.age = age;
  this.job = job;

  if (typeof this.sayName != "functin") {
    // 在sayName方法不存在的情况下,才将它添加到原型中
    Person.prototype.sayName = function () {
      console.log(this.name);
    }
  }
}

上面的代码中,给原型添加sayName方法只有在初次调用构造函数时才会执行。

时间: 2024-11-05 18:38:51

《JavaScript高级程序设计》第6章 面向对象程序设计的相关文章

JavaScript高级程序设计 第六章 面向对象程序设计

面向对象程序设计 ECMA-262将对象定义为:“无序属性的集合,其属性可以包含基本值.对象或者函数.”严格来讲,这就相当于说对象是一组没有特定顺序的值.对象的每个属性和方法都有一个名字,而每个名字都映射到一个值. 6.1理解对象 创建对象的最简单方式就是创造一个Object实例,然后再为它添加属性和方法,如下所示: var person = new Object(); person.name = 'yyg'; person.age = 23; person.job = 'student; pe

C++ Primer学习总结 第15章 面向对象程序设计

第15章 面向对象程序设计 1.    构造基类和派生类. 其中A类是基类,B类是派生类.派生类的构造函数必须重新写过,不能继承.(因为毕竟两个类的类名都不一样,不可能构造函数继承)只继承其他的成员函数和成员变量. 派生类可以覆盖基类的虚函数,但是也可以选择不覆盖(即直接使用父类的函数版本)比例A类的print_1()虚函数就没有被覆盖. 基类的静态成员:如果基类有一个静态成员,那么基类和所有派生类都共同拥有这仅有的一个静态成员. 2.    基类的虚函数默认实参最好与派生类的虚函数默认实参一致

第7章 面向对象程序设计

第7章 面向对象程序设计 7.1 面向对象概述 面向对象(Object Oriented)的英文缩写是OO,它是一种设计思想.从20世纪60年代提出面向对象的概念到现在,它已经发展成为一种比较成熟的编辑思想,并且逐步成为目前软件开发领域的主流计技术.如我们经常听说的面向对象编程(Object Oriented Programming,即OOP)就是主要针对大型软件设计而提出的,它可以使软件设计更加灵活,并且能更好地进行代码复用. 面向对象中的对象(Object),通常是指客观世界中存在的对象,具

Java程序设计 实验二 Java面向对象程序设计

北京电子科技学院(BESTI) 实     验    报     告 课程:Java程序设计 班级:1353  姓名:李海空  学号:20135329 成绩:             指导教师:娄嘉鹏      实验日期:2015.5.7 实验密级:         预习程度:         实验时间: 仪器组次:          必修/选修:            实验序号: 实验名称:       Java面向对象程序设计 实验目的与要求:实验内容 1. 初步掌握单元测试和TDD 2.

JavaScript高级程序设计学习笔记第六章--面向对象程序设计

1.ECMAScript没有类的概念,ECMA-262 把对象定义为:“无序属性的集合,其属性可以包含基本值.对象或者函数.”,有点类似于散列表 2.ECMAScript 中有两种属性:数据属性和访问器属性. 数据属性: [[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性. [[Enumerable]]:表示能否通过 for-in 循环返回属性. [[Writable]]:表示能否修改属性的值. [[Valu

JavaScript高级程序设计-第六章面向对象的程序设计

创建对象主要的两种形式,创建Object实例和创建对象字面量 对象包含属性和方法 数据 .属性有四个特性,特性是为了描述属性行为的,他们是: Configurable(可配置的)是否能删除或是否能修改为访问器属性 Enumerable(枚举)是否能够for-in Writeable(可写)能否修改属性值 Value(值)默认为undefined,从这个位置上读数据或把新值保存到这个位置上 eg: var person = {}'; Object.defineproperty(person,"na

《JavaScript高级程序设计第六章--面向对象》section_03

这一部分主要讲讲有关继承的问题. 6.3  继承 许多面向对象语言比如java都支持两种继承方式:接口继承(只继承方法签名)和实现继承(继承实际的方法):由于函数没有签名,在ECMAScript中只能支持实现继承.实现继承主要依靠原型链. 6.3.1 原型链 基本思想:利用原型让一个引用类型继承另一个引用类型的属性和方法. 回顾一下构造函数.原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针. 所以,试想一下,我们让原型对

读书笔记 - js高级程序设计 - 第六章 面向对象的程序设计

EcmaScript有两种属性 数据属性 和 访问器属性 数据属性有4个特性 Configurable Enumerable Writable Value 前三个值的默认值都为false 举例 Object.defineProperty( person, "name", { writable:false, value:"niko"} ) ; 一旦属性定义为不可配置的,就不能再把它变回可配置的了 读取属性 的特性 var descriptor  = Object.ge

第15章 面向对象程序设计

面向对象的核心是:数据抽象.继承.动态绑定 数据抽象:将类的接口与实现分离 继承:对类型的相似关系进行定义建模 动态绑定:忽略相似类的区别,统一使用它们的对象 15.2基类与派生类 class Base { public: Base() = default; Base(int value) :value(value){} virtual int get_value() { return value; }; virtual ~Base() = default; private: int value