1.ECMAScript没有类的概念,ECMA-262 把对象定义为:“无序属性的集合,其属性可以包含基本值、对象或者函数。”,有点类似于散列表

2.ECMAScript 中有两种属性:数据属性和访问器属性。

  • 数据属性:

    • [[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特
      性,或者能否把属性修改为访问器属性。
    • [[Enumerable]]:表示能否通过 for-in 循环返回属性。
    • [[Writable]]:表示能否修改属性的值。
    • [[Value]]:包含这个属性的数据值。

修改默认属性:

要修改属性默认的特性,必须使用 ECMAScript 5 的 Object.defineProperty()方法。这个方法接收三个参数:属性所在的对象、属性的名字和一个描述符对象。其中,描述符(descriptor)对象的属性必须是: configurable、 enumerable、 writable 和 value。设置其中的一或多个值,可以修改对应的特性值。例如:

 var person = {};
Object.defineProperty(person, "name", {
writable: false,
value: "Nicholas"
});
alert(person.name); //"Nicholas"
person.name = "Greg";
alert(person.name); //"Nicholas"

如果通过defineProperty将configurable设置为false,那么就不可以在更改其他设置了。

  • 访问器属性:不包含数据值;它们包含一对儿 getter 和 setter 函数

    • [[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特
      性,或者能否把属性修改为数据属性。对于直接在对象上定义的属性,这个特性的默认值为
      true。
    • [[Enumerable]]:表示能否通过 for-in 循环返回属性。对于直接在对象上定义的属性,这
      个特性的默认值为 true。
    • [[Get]]:在读取属性时调用的函数。默认值为 undefined。
    • [[Set]]:在写入属性时调用的函数。默认值为 undefined。

访问器属性不能直接定义,必须使用 Object.defineProperty()来定义。例如:

 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;
}
}
});
book.year = 2005;
alert(book.edition); //

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;
}
}
}
});

p.s 需要注意的点

每个属性名后面是冒号

第二个参数的属性用花括号括起来,每个属性用逗号分隔

最后有一个分号

4.读取属性特性:Object.getOwnPropertyDescriptor()方法,可以取得给定属性的描述符。这个方法接收两个参数:属性所在的对象和要读取其描述符的属性名称。返回值是一个对象,如果是访问器属性,这个对象的属性有 configurable、 enumerable、 get 和 set;如果是数据属性,这个对象的属性有 configurable、 enumerable、 writable 和 value。例如:

 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;
}
}
}
});
var descriptor = Object.getOwnPropertyDescriptor(book, "_year");
alert(descriptor.value); //
alert(descriptor.configurable); //false
alert(typeof descriptor.get); //"undefined"
var descriptor = Object.getOwnPropertyDescriptor(book, "year");
alert(descriptor.value); //undefined
alert(descriptor.enumerable); //false
alert(typeof descriptor.get); //"function"

5.创造对象模式:

工厂模式:在函数内部新创建一个对象Object,并在函数内部定义其属性与方法,最后返回这个对象Object。例如:

 function createPerson(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
var person1 = createPerson("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");

优点:创建的对象都是相似的,用函数封装了具体创建的细节

缺点:不能确定创建的是怎样的对象类型

构造函数模式:

 function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
};
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");

与工厂模式相比,构造函数模式有以下几个不同:

  • 不用显示的创建对象
  • 不用返回对象
  • 将属性和方法直接交给this对象

优点:创建的函数可以当做普通函数用,也可以与new操作符结合当做构造函数使用,使用方法灵活

缺点:每个方法都要在每个实例上创建一遍,每个方法都是不同的实例。例如:alert(person1.sayName == person2.sayName); //false,person1的sayName与person2的sayName不是同一个实例。

原型模式:

每个函数都有一个 prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。那么 prototype 就是通过调用构造函数而创建的那个对象实例的原型对象。
优点:可以让所有对象实例共享它所包含的属性和方法。

 function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
person1.sayName(); //"Nicholas"
var person2 = new Person();
person2.sayName(); //"Nicholas"
alert(person1.sayName == person2.sayName); //true

上述代码中,person1中的sayName与person2中的sayName是一个函数。

原型对象:

只要创建了一个新函数(上例中是Person),就会根据一组特定的规则为该函数创建一个 prototype属性(Person中有一个属性prototype),这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个 constructor(构造函数)属性,这个属性包含一个指向 prototype 属性所在函数的指针。Person.prototype. constructor 指向 Person。而通过这个构造函数,我们还可继续为原型对象添加其他属性和方法。

根据Person创建实例时(上述代码中创建了person1和person2),该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。这个指针叫[[Prototype]],存在于实例与构造函数的原型对象之间。

虽然在脚本中没有标准的方式访问[[Prototype]],但 Firefox、 Safari 和 Chrome 在每个对象上都支持一个属性__proto__;而在其他实现中,这个属性对脚本则是完全不可见的。

虽然person1与person2中没有对应的属性和方法,但是可以根据[[prototype]]属性找到Person Prototype,里面有相应的方法。

虽然在所有实现中都无法访问到[[Prototype]],但可以通过 isPrototypeOf()方法来确定对象之间是否存在这种关系。从本质上讲,如果[[Prototype]]指向调用 isPrototypeOf()方法的对象(Person.prototype),那么这个方法就返回 true。例如:

 alert(Person.prototype.isPrototypeOf(person1)); //true
alert(Person.prototype.isPrototypeOf(person2)); //true

ECMAScript 5 增加了一个新方法,叫 Object.getPrototypeOf(),在所有支持的实现中,这个方法返回[[Prototype]]的值。例如:

 alert(Object.getPrototypeOf(person1) == Person.prototype); //true
alert(Object.getPrototypeOf(person1).name); //"Nicholas"

每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。搜索首先从对象实例本身开始。如果在实例中找到了具有给定名字的属性,则返回该属性的值;如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性。如果在原型对象中找到了这个属性,则返回该属性的值。

虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。如果在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,那我们就在实例中创建该属性,该属性将会屏蔽原型中的那个属性。例如:

 function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
var person2 = new Person();
person1.name = "Greg";
alert(person1.name); //"Greg"—— 来自实例
alert(person2.name); //"Nicholas"—— 来自原型

上述代码中重新定义了person1的name属性,这样就不会去访问原型中的属性。如果想要重新访问原型中的属性,就可以先delete(person1.name);,将person1的属性删除掉,在访问person1.name属性就是原型中的name属性。

使用 hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中。属性在实例对象中返回true,在原型中返回false。

原型与in操作符:

使用in操作符的两种方式:第一种,单独使用。只要是能通过实例访问到的属性时,无论该属性是在对象中还是在实例中,总是返回true。通过hasOwnProperty()方法与in方法连个使用可以确定该属性是在原型中还是实例中。

第二种,for-in循环中使用,返回的是所有能够通过对象访问的、可枚举的(enumerated)属性,无论该属性是在原型中还是实例中。

要取得对象上所有可枚举的实例属性,可以使用 ECMAScript 5 的 Object.keys()方法。这个方法接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。

要得到所有实例属性,无论它是否可枚举,都可以使用 Object.getOwnPropertyNames()方法。

如果将原型函数添加属性的代码改写成字面量形式。如下所示:

 function Person(){
}
Person.prototype = {
name : "Nicholas",
age : 29,
job: "Software Engineer",
sayName : function () {
alert(this.name);
}
};
PrototypePatternExample07.ht

结果相同,但是constructor 属性不再指向 Person ,而是指向了Object。

解决方法:

如果显示的将constructor指向Person,例如:

 function Person(){
}
Person.prototype = {
constructor : Person,
name : "Nicholas",
age : 29,
job: "Software Engineer",
sayName : function () {
alert(this.name);
}
};

那么constructor 就会成为可枚举的,原生constructor 确实不可枚举的。

在兼容 ECMAScript 5 的 JavaScript 引擎,可以试一试 Object.defineProperty()。如下所示:

 Object.defineProperty(Person.prototype, "constructor", {
enumerable: false,
value: Person
});

可以随时为原型添加属性和方法,并且修改能够立即在所有对象实例中反映出来,因为原型与实例之间是指针关系,不是副本。但是如果先创建实例,在重写整个原型对象的话,就会出错,因为该实例指向的仍然是原先的原型对象,无法访问到新的原型对象的属性与方法。

原型模式的缺点:

  • 首先,它省略了为构造函数传递初始化参数这一环节,结果所有实例在默认情况下都将取得相同的属性值。
  • 原型中所有属性是被很多实例共享的,这种共享对于函数非常合适。对于那些包含基本值的属性倒也说得过去,通过在实例上添加一个同名属性,可以隐藏原型中的对应属
    性。对于包含引用类型的状况,比如包含一个数组的时候,就会出现不能够包含特殊属性,都会成为共享属性。

4.构造函数模式与原型模式的组合:将公用的属性与方法添加到原型中,将有区别的属性添加到构造函数中。例如:

 function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ["Shelby", "Court"];
}
Person.prototype = {
constructor : Person,
sayName : function(){
alert(this.name);
}
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
person1.friends.push("Van");
alert(person1.friends); //"Shelby,Count,Van"
alert(person2.friends); //"Shelby,Count"
alert(person1.friends === person2.friends); //false
alert(person1.sayName === person2.sayName); //true

5.动态原型模式

6.寄生构造函数模式

7.稳妥构造函数模式

8.继承:将原型链作为实现继承的主要方法。其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。
将构造函数指向原型的指针,将指向的原型变成另一个类型的实例。其本质是重写了原型对象,依次类推,就会出现原型链。

使用原型链也是扩展了原型搜索机制的范围。

继承大概过程实现:

 function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
this.subproperty = false;
}
//继承了 SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function (){
return this.subproperty;
};
var instance = new SubType();
alert(instance.getSuperValue()); //true

确定原型与实例的关系:

有两种方法:第一种,使用 instanceof 操作符。只要用这个操作符来测试实例与原型链中出现过的构造函数,结果就会返回 true。例如:

 alert(instance instanceof Object); //true
alert(instance instanceof SuperType); //true
alert(instance instanceof SubType); //true

第二种,使用 isPrototypeOf()方法。只要是原型链上出现的原型,都可以说是该原型链所派生的实例的原型,因此 isPrototypeOf()方法也会返回 true。

 alert(Object.prototype.isPrototypeOf(instance)); //true
alert(SuperType.prototype.isPrototypeOf(instance)); //true
alert(SubType.prototype.isPrototypeOf(instance)); //true

在通过原型链实现继承时,不能使用对象字面量创建原型方法,会是的继承无效。

原型链实现继承的问题:

  • 在通过原型来实现继承时,原型实际上会变成另一个类型的实例。于是,原先的实例属性也就顺理成章地变成了现在的原型属性了。
  • 在创建子类型的实例时,不能向超类型的构造函数中传递参数。实际上,应该说是没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。

9.借用构造函数,解决传递参数的问题

10.组合继承:将借用构造函数与原型链进行组合的一种构造方式。

11.原型式继承

12.寄生式继承

13.寄生组合式继承

JavaScript高级程序设计学习笔记第六章--面向对象程序设计的更多相关文章

  1. JavaScript高级编程学习笔记(第三章之一)

    继续记笔记,JavaScript越来越有意思了. 继续... 第三章:JavaScript基础 ECMAScript语法在很大程度上借鉴了C和其它类似于C的语言,比如Java和Perl. 大小写敏感: ...

  2. JVM学习笔记-第六章-类文件结构

    JVM学习笔记-第六章-类文件结构 6.3 Class类文件的结构 本章中,笔者只是通俗地将任意一个有效的类或接口锁应当满足的格式称为"Class文件格式",实际上它完全不需要以磁 ...

  3. [Python学习笔记][第六章Python面向对象程序设计]

    1月29日学习内容 Python面向对象程序设计 类的定义与使用 类定义语法 使用class关键词 class Car: def infor(self): print("This is ca ...

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

    面向对象程序设计 ECMA-262将对象定义为:“无序属性的集合,其属性可以包含基本值.对象或者函数.”严格来讲,这就相当于说对象是一组没有特定顺序的值.对象的每个属性和方法都有一个名字,而每个名字都 ...

  5. C Primer Plus 学习笔记 -- 前六章

    记录自己学习C Primer Plus的学习笔记 第一章 C语言高效在于C语言通常是汇编语言才具有的微调控能力设计的一系列内部指令 C不是面向对象编程 编译器把源代码转化成中间代码,链接器把中间代码和 ...

  6. Effective Java 学习笔记----第7章 通用程序设计

    第7章 通用程序设计 第29条 将局部变量的作用域最小化     使一个局部变量的作用域最小化,最有力的技术室在第一次使用它的地方声明.   第30条 了解和使用库      效率提高.如果你不知道库 ...

  7. Linux学习笔记(第六章)

    第六章-档案权限与目录配置#chgrp:改变档案的所属群组#chown:改变档案的拥有者#chmod:改变档案的权限及属性 chown用法 chmod用法: r:4 w:2 x:1对于文档: 对于目录 ...

  8. o'Reill的SVG精髓(第二版)学习笔记——第六章

    第六章:坐标系统变换 想要旋转.缩放或者移动图片到新的位置.可以给对应的SVG元素添加transform属性. 6.1 translate变换 可以为<use>元素使用x和y属性,以在特性 ...

  9. 学习笔记 第六章 使用CSS美化图片

    第六章  使用CSS美化图片 6.1  在网页中插入图片 GIF图像 跨平台能力,无兼容性问题: 具有减少颜色显示数目而极度压缩文件的能力,不会降低图像的品质(无损压缩): 支持背景透明功能,便于图像 ...

随机推荐

  1. PowerBuilder -- 其他

    判断某键是否被按下 KeyDown ( keycode ) 继承问题 如果是 uf_1是函数呢   你在父类UO_1的uf_1里面 写了代码,只要在子类UO_2的uf_1写了代码,默认是覆盖(over ...

  2. C#多线程学习(六) 互斥对象

    如何控制好多个线程相互之间的联系,不产生冲突和重复,这需要用到互斥对象,即:System.Threading 命名空间中的 Mutex 类. 我们可以把Mutex看作一个出租车,乘客看作线程.乘客首先 ...

  3. Javascript模块化编程-规范[2]

    实现Javascript模块化,固然很重要,但是怎样才能实现国际上都能认可的模块化呢?模块化编程规范随应运而生. 目前Javascript模块化规范主要有两种:CommonJS和AMD. Common ...

  4. mybatis 各组件生命周期

    1.SqlSessionFactoryBuilder SqlSessionFactoryBuilder是通过利用XML或者java编码来获取Configuration配置来构建SqlSessionFa ...

  5. install_driver(mysql) failed

        安装好了mysql监控神器innotop,正得意,innotoop不可用,其错误提示为install_driver(mysql) failed: Can't load '/usr/lib64/ ...

  6. js日历学习

    <!DOCTYPE html><html><head><title>自己写的JS日历,适合学习</title><script src= ...

  7. 剑指Offer:栈的压入、弹出序列【31】

    剑指Offer:栈的压入.弹出序列[31] 题目描述 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序.假设压入栈的所有数字均不相等.例如序列1,2,3,4,5是某栈 ...

  8. Python中出现“TabError: inconsistent use of tabs and spaces in indentation”问题的解决

  9. BZOJ 3314 [Usaco2013 Nov]Crowded Cows:单调队列

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3314 题意: N头牛在一个坐标轴上,每头牛有个高度.现给出一个距离值D. 如果某头牛在它的 ...

  10. 搭建LoadRunner中的场景(三)场景的执行计划

    所谓场景操作,包括初始化用户组.启动用户组各用户以及停止虚拟用户的全过程.依据设置不同,执行过程中可以最多有5类操作,分别是启动用户组(start group).初始化(Initialize).启动虚 ...