乐趣区

js面向对象编程

== 什么是对象?==
就是无序 属性的集合 ,通过属性或者方法名与值的一一映射。
== 对象的属性类型 ==
对象中还定义了一些 js 中不能直接访问的属性,是为了实现 js 引擎用的。有 数据属性 访问器属性 两种。
1. 数据属性
数据属性包含一个数据值的位置。在这个位置可以读取和写入值。数据属性有 4 个描述其行为的
特性。

把新值保存在这个位置。这个特性的默认值为 undefined。

要修改属性默认的特性,必须使用 ECMAScript 5 的 Object.defineProperty()方法。


注意:
1》configurable 设置为 false,表示不能从对象中删除属性。如果对这个属性调用 delete,则 在非严格模式下什么也不会发生,而在严格模式下会导致错误。而且,一旦把属性定义为不可配置的,就不能再把它变回可配置。
2》在调用 Object.defineProperty()方法时,如果不指定,configurable、enumerable 和 writable 特性的默认值都是 false。
2. 访问器属性
访问器属性不包含数据值; 包含一对儿 getter 和 setter 函数(不过,这两个函数都不是必需的)。在读取访问器属性时,会调用 getter 函数,这个函数负责返回有效的值; 在写入访问器属性时,会调用 setter 函数并传入新值,这个函数负责决定如何处理数据。访问器属性有如下 4 个特性。

 [[Get]]: 在读取属性时调用的函数。默认值为 undefined。
 [[Set]]: 在写入属性时调用的函数。默认值为 undefined。
访问器属性不能直接定义,必须使用 Object.defineProperty()来定义。

可以通过 Object.definePro- perties()定义多个属性;
使用 ECMAScript 5 的 Object.getOwnPropertyDescriptor()方法,可以取得给定属性的描述符。

== 创建对象的方式 ==
通过 字面量 的方式可以创建对象,但是创建有类似属性的对象时会产生大量重复代码。
1. 工厂模式
用函数来封装以特定接口创建对象。


缺点就是无法知道一个对象的类型。
2. 构造函数模式


通过构造函数创建的对象可以通过 constructor 标识对象类型,通过 instanceof 检测对象类型。
构造函数 模式与 工厂模式 对比:
1》没有显式地创建对象;
2》直接将属性和方法赋给了 this 对象;
3》没有 return 语句。
通过 new 操作符调用构造函数会经历 4 个步骤:
(1) 创建一个新对象;
(2) 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);
(3) 执行构造函数中的代码(为这个新对象添加属性);
(4) 返回新对象。


构造函数也可作为 普通函数 调用
Person(“Greg”, 27, “Doctor”); // 添加到 window window.sayName(); //”Greg”
也可以在另一个 对象的作用域 中调用
var o = new Object();
Person.call(o, “Kristen”, 25, “Nurse”); o.sayName(); //”Kristen”

构造函数模式的 缺点 就是每个方法都要在每个 实例上重新创建一遍

function Person(name, age, job){

    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = new Function("alert(this.name)"); 
    // 与声明函数在逻辑上是等价的 

}
alert(person1.sayName == person2.sayName); //false
3. 原型模式


通过 prototype 为对象添加属性和方法。这样实例就能共享原型的属性和方法。
1》原型具有 动态性。在实例化后定义的原型方法,实例也能调用。

2》原型的简单写法。

可以将原型简写如上,但是这种写法实际上是重新写了原型,新原型的 constructor 已经改变,指向 object。所以可以重新设置 constructor。
而且如果在实例化后重写原型,则已经实例的对象依旧访问原来原型对象,不能访问新原型的方法和属性。

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

=== 继承 ===

继承:利用原型让一个引用类型继承另一个引用类型的属性和方法。

1. 原型链继承

包含引用类型值的 原型属性 会被所有 实例共享 ; 而 这也正是为什么要在构造函数中,而不是在原型对象中定义属性的原因。在通过原型来实现继承时,原型实际上会变成另一个类型的实例。于是,原先的实例属性也就顺理成章地变成了现在的原型属性了。
原型链的问题是: 由于原型中包含引用类型值所带来的问题。和在创建子类型的实例时,不能向超类型的构造函数中传递参数。实际上,应该说是没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。实践中很少会单独使用原型链。

2. 构造函数继承

借用构造函数:别忘了,函数只不过是在特定环境中执行代码的对象,因此通过使用 apply()和 call()方法也可以在 (将来) 新创建的对象上执行构造函数。

借用构造函数的问题
如果仅仅是借用构造函数,那么也将无法避免构造函数模式存在的问题——方法都在构造函数中定 义,因此函数复用就无从谈起了。而且,在超类型的原型中定义的方法,对子类型而言也是不可见的,结 果所有类型都只能使用构造函数模式。考虑到这些问题,借用构造函数的技术也是很少单独使用的。

组合继承

组合继承避免了原型链和借用构造函数的缺陷,融合了它们的优点,成为 JavaScript 中最常用的继 承模式。

退出移动版