javascript高级程序设计笔记(第6章 面向对象的程序设计)

1、访问器属性:

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); //2

非标准方法:

var book = {
    _year : 2004,
    edition : 1
};
//定义访问器的旧有方法
book.__defineGetter__("year", function () {
    return this._year;
});
book.__defineSetter__("year", function (newValue) {
    if (newValue > 2004) {
        this._year = newValue;
        this.edition += newValue - 2004;
    }
});
book.year = 2005;
alert(book.edition); //2


2、创建对象

  2.1 工厂模式(没有解决对象识别的问题(即怎样知道一个对象的类型))

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

2.2 构造函数模式

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

alert(person1.constructor == Person); //true
alert(person2.constructor == Person); //true

alert(person1 instanceof Object); //true
alert(person1 instanceof Person); //true
alert(person2 instanceof Object); //true
alert(person2 instanceof Person); //true

把上面的构造函数当做函数

// 当作构造函数使用
var person = new Person("Nicholas", 29, "Software Engineer");
person.sayName(); //"Nicholas"
// 作为普通函数调用
Person("Greg", 27, "Doctor"); // 添加到window
window.sayName(); //"Greg"
// 在另一个对象的作用域中调用
var o = new Object();
Person.call(o, "Kristen", 25, "Nurse");
o.sayName(); //"Kristen"

构造函数的问题:就是每个方法都要在每个实例上重新创建一遍。在前面的例子中,person1 和person2 都有一个名为sayName()的方法,但那两个方法不是同一个Function 的实例

可以把函数定义转移到构造函数外部来解决这个问题:

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

        function sayName(){
            alert(this.name);
        }

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

        person1.sayName();   //"Nicholas"
        person2.sayName();   //"Greg"

        alert(person1 instanceof Object);  //true
        alert(person1 instanceof Person);  //true
        alert(person2 instanceof Object);  //true
        alert(person2 instanceof Person);  //true

        alert(person1.constructor == Person);  //true
        alert(person2.constructor == Person);  //true

        alert(person1.sayName == person2.sayName);  //true 

但是新的问题又来了:在全局作用域中定义的函数实际上只能被某个对象调用,这让全局作用域有点名不副实。而更让人无法接受的是:如果对象需要定义很多方法,那么就要定义很多个全局函数,于是我们这个自定义的引用类型就丝毫没有封装性可言了。好在,这些问题可以通过使用原型模式来解决。

2.3 原型模式

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

可以通过画图分析:非常重要图见书中

下面这个函数可以用于断属性到底是存在对象中还是存在于原型中

function hasPrototypeProperty(object, name) {
    return !object.hasOwnProperty(name) && (name in object);
}

ie BUG :ie不会枚举默认不可枚举的所有属性和方法包括:hasOwnProperty()、propertyIsEnumerable()、toLocaleString()、toString()和valueOf()。

var o = {
    toString : function () {
        return "My Object";
    }
};
for (var prop in o) {
    if (prop == "toString") {
        alert("Found toString"); //在IE 中不会显示
    }
}

枚举对象上的可枚举属性:keys/Object.getOwnPropertyNames

function Person() {}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function () {
    alert(this.name);
};
var keys = Object.keys(Person.prototype);
alert(keys);     //"name,age,job,sayName"
var p1 = new Person();
p1.name = "Rob";
p1.age = 31;
var p1keys = Object.keys(p1);
alert(p1keys);       //"name,age"
var keys = Object.getOwnPropertyNames(Person.prototype);
alert(keys);       //"constructor,name,age,job,sayName"

使用对象直接量,不用每次都打一遍Person.prototype

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

但是这样上面的代码constructor 属性不再指向Person 了。

解决方法:

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

原型的动态性:

比较下面两段代码的不同:图表分析见书中

var friend = new Person();
Person.prototype.sayHi = function(){
alert("hi");
};
friend.sayHi(); //"hi"(没有问题!)

//注意这里的顺序:

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

修改原生对象的原型:如给所有的字符串类型添加一个方法

原型对象的问题:

即两个实例的属性不可能完全相同

如这个例子中两个人的朋友不可能完全相同,

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

2.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

2.5 动态原型模式

可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型。来看一个例子。

function Person(name, age, job) {
    //属性
    this.name = name;
    this.age = age;
    this.job = job;
    if (typeof this.sayName != "function") {
        Person.prototype.sayName = function () {
            alert(this.name);
        };
    }
}
var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName();
这里只在sayName()方法不存在的情况下,才会将它添加到原型中。这段代码只会在初次调用构造函数时才会执行。此后,原型已经完成初始化,不需要再做什么修改了。不过要记住,这里对原型所做的修改,能够立即在所有实例中得到反映。因此,这种方法确实可以说非常完美。其中,if 语句检查的可以是初始化之后应该存在的任何属性或方法——不必用一大堆if 语句检查每个属性和每个方法;只要检查其中一个即可。对于采用这种模式创建的对象,还可以使用instanceof 操作符确定它的类型。但是记住,使用动态原型模式时,不能使用对象字面量,

2.6 寄生构造函数模式

2.7 稳妥构造函数模式

时间: 2024-10-08 07:32:30

javascript高级程序设计笔记(第6章 面向对象的程序设计)的相关文章

javascript高级编程笔记01(基本概念)

1.在html中使用JavaScript 1.  <script> 元素 <script>定义了下列6个属性: async:可选,异步下载外部脚本文件. charset:可选,通过src属性指定代码的字符集,大多浏览器会忽略这个值,所以很少人使用 language:已放弃 src:可选,外部脚本的地址 type:可选,现在不推荐用“text/javascript”,考虑到约定俗成和最大限度的浏览器兼容,目前type属性的值还是text/javascript,不过,这个属性并不是必需

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

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

【javascript高级程序设计笔记】第一章与第三章

第1章 javascript简介 1.2Javascript实现 一个完整的javascript实现由下列三个不同的部分组成 核心(ECMAScript) 提供核心语言功能 文档对象模型(DOM) 提供访问和操作网页内容的方法和接口 浏览器对象模型(BOM)提供与浏览器交互的方法和接口 ECMAScript 它规定了这门语言的下列组成部分: 语法  类型  语句  关键字 保留字 操作符 对象 ECMA-262第5版,发布于2009年. 文档对象模型(DOM) Document Object M

javascript对象[第6章-面向对象的程序设计 笔记1]

ECMA-262 把对象定义为: “无序属性的集合, 其属性可以包含基本值. 对象或者函数. ” 严格来讲,这就相当于说对象是一组没有特定顺序的值.对象的每个属性或方法都有一个名字,而每个名字都映射到一个值.正因为这样(以及其他将要讨论的原因) ,我们可以把 ECMAScript 的对象想象成散列表:无非就是一组名值对,其中值可以是数据或函数. 对象 创建自定义对象的最简单方式就是创建一个 Object 的实例,然后再为它添加属性和方法,如下所示. var person = new Object

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

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

JavaScript高级程序设计(第三版)第六章 面向对象的程序设计

6.1 理解对象 var person = new Object(); person.name = "Nicholas"; person.age = 29; person.job = "Software Engineer"; person.sayName = function(){ alert(this.name); }; person.sayName(); 6.1.1 属性类型 1.数据属性 [[Configurable]]:表示能否通过delete删除属性从而重

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

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

Javascript高级程序设计笔记 &lt;第五章&gt; 引用类型

一.object类型 创建object实例的方式有两种: //第一种使用new操作符跟构造函数 var person= new Object(); person.name="小王"; person.age=29; //第二种使用对象字面量 var person={ name:"小王", age:29 }; 二.Array类型 创建数组有两种基本方式: 1 //第一种使用array构造函数(可以省略new) 2 var colors=new Array(); 3 va

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

6.1 对象属性 6.1.1 属性类型 1. 数据属性 我们一般所说的属性就是数据属性,它用来将一个字符串名称映射到某个值上 数据属性的4个特性: configurable, enumerable, writable, value要修改属性默认的特性,必须使用Object.defineProperty()方法Object.defineProperty(对象,属性名,描述符对象) var person = {}; Object.defineProperty(person, 'name', { wr