《Javascript高级程序设计》第六章笔记整理

一、创建对象的方法(6种)

  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(‘Lily‘,17,‘Teacher‘);
var person2 = createPerson(‘Simon‘,22,‘Doctor‘);

  弊端:无法解决对象识别的问题(即怎么知道一个对象的类型)。

  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(‘Lily‘,17,‘Teacher‘);
    var person2 = new Person(‘Simon‘,22,‘Doctor‘);

  构造器函数始终都应该以大写字母开头;要创建新的实例,必须使用new操作符。

  与工厂模式的区别:

    A、没有显式地创建对象

    B、直接将属性和方法赋给了this对象

    C、没有return语句

  创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型,这正是构造函数胜于工厂模式的地方。

  检测对象类型可使用 instanceof 操作符:

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

  构造函数的弊端: 每个方法都要在每个实例上重新创建一遍。

  在之前的例子中,person1和person2都有一个名为sayName()的方法,但两个方法不是同一个Function的实例。(因为,ECMAScript中的函数是对象,因此每定义一个函数,实际上就是实例化一个对象)从逻辑上讲,此时的构造函数也可以这样定义:

function Person(name,age,job){
        this.name = name;
        this.age = age;
        this.job = job;
        this.sayName = new Function(‘alert(this.name)‘);//与声明函数在逻辑上是等价的。
    }

 以这种方式创建函数,会导致不同的作用域和标识符解析。

  

  3.原型模式

    function Person(){};
    Person.prototype.name = ‘Lily‘;
    Person.prototype.age = 17;
    Person.prototype.job = ‘Teacher‘;
    Person.prototype.sayName = function(){
        alert(this.name);
    }

    var person1 = new Person();
    alert(person1.sayName);//‘Lily‘

    var person2 = new Person();
    alert(person2.sayName);//‘Lily‘

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

  

  通过 isPrptotypeOf() 方法可以确定实例与某对象原型之间的关系:

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

  因为person1和person2都有一个指向Person.prototype的的指针,因而都返回了true。

  

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

    function Person(){};
    Person.prototype.name = ‘Lily‘;
    Person.prototype.age = 17;
    Person.prototype.job = ‘Teacher‘;
    Person.prototype.sayName = function(){
        alert(this.name);
    }

    var person1 = new Person();
    var person2 = new Person();

    person1.name = ‘Simon‘;
    alert(person1.name);//‘Simon‘   来自实例
    alert(person2.name);//‘Lily‘  来自原型

  即,为对象实力添加一个属性时,该属性就会屏蔽原型对象中的同名属性;换句话说,添加这个属性只会阻止我们访问原型中的那个同名属性,而不会修改那个属性。即使将这个属性设置为null,也无法恢复其指向原型的链接。

  想完全删除实例属性,可使用 delete 操作符:

    function Person(){};
    Person.prototype.name = ‘Lily‘;
    Person.prototype.age = 17;
    Person.prototype.job = ‘Teacher‘;
    Person.prototype.sayName = function(){
        alert(this.name);
    }

    var person1 = new Person();
    var person2 = new Person();

    person1.name = ‘Simon‘;
    alert(person1.name);//‘Simon‘   来自实例
    alert(person2.name);//‘Lily‘  来自原型

    delete person1.name;
    alert(person1.name);//‘Lily‘   来自原型

  

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

   function Person(){};
    Person.prototype.name = ‘Lily‘;
    Person.prototype.age = 17;
    Person.prototype.job = ‘Teacher‘;
    Person.prototype.sayName = function(){
        alert(this.name);
    }

    var person1 = new Person();

    alert(person.hasOwnProperty(‘name‘));//false

    person1.name = ‘Simon‘;
    alert(person.hasOwnProperty(‘name‘));//true

  

  使用 in 操作符能够检测某对象是否具有某个属性(无论该属性是存在于实例中还是原型中):

function Person(){};
    Person.prototype.name = ‘Lily‘;
    Person.prototype.age = 17;
    Person.prototype.job = ‘Teacher‘;
    Person.prototype.sayName = function(){
        alert(this.name);
    }

    var person1 = new Person();

    alert(person.hasOwnProperty(‘name‘));//false   alert(‘name‘ in person1); //true
    person1.name = ‘Simon‘;
    alert(person.hasOwnProperty(‘name‘));//true   alert(‘name‘ in person1); //true

  

  故而,检测某属性是否存在于原型中可使用函数↓

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

  只要in操作符返回true而hasOwnProperty()返回false,即可确定属性是原型中的属性。

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

   function Person(){};
    Person.prototype.name = ‘Lily‘;
    Person.prototype.age = 17;
    Person.prototype.job = ‘Teacher‘;
    Person.prototype.sayName = function(){
        alert(this.name);
    }

    var keys = Object.keys(Person.prototype);
    alert(keys);    //"name,age,job,sayName";
    alert(Array.isArray(keys)); //true
    alert(keys.length);     //4

    var person1 = new Person();
    person1.name = "Candy";
    person1.job = "Singer";
    var keys2 = Object.keys(person1);
    alert(keys2);   //"name,job";

  

  constructor 属性不可枚举。若想获得所有实例属性(无论是否可枚举),则可使用 Object.getOwnPropertyName()  方法:

    function Person(){};
    Person.prototype.name = ‘Lily‘;
    Person.prototype.age = 17;
    Person.prototype.job = ‘Teacher‘;
    Person.prototype.sayName = function(){
        alert(this.name);
    }

    var keys = Object.getOwnPropertyName(Person.prototype);
    alert(keys);    //"constructor,name,age,job,sayName";

  

  将 Person.prototype 设置为等于一个以对象字面量创建的新对象,是更为简单的原型语法:

function Person(){};

    Person.prototype = {
        name: ‘Lily‘,
        age: 17,
        job: ‘Teacher‘,
        sayName: function(){
            alert(this.name);
        }
    };

  但这样的语法,本质上是完全重写了默认的 prototype 对象,使得 constructor 属性不再指向Person了(指向Object构造函数)。

  可通过以下方式将constructor属性重新指向Person:

function Person(){};

    Person.prototype = {
        constructor: Person,     //重设constructor属性
        name: ‘Lily‘,
        age: 17,
        job: ‘Teacher‘,
        sayName: function(){
            alert(this.name);
        }
    };    

  但这种方式会导致它的[[Enumerable]](可枚举)特性被设置为true。详见高程P155.

  对原型对象所做的任何修改都能立即从实例上反映出来,即便是先创建了实例后修改原型:

   var friend = new Person();

    Person.prototype.sayHi = function(){
        alert(‘Hi!‘);
    };

    friend.sayHi(); //"Hi!"  

  但若是重写整个原型对象,情况就不一样了:

    var friend = new Person();

    Person.prototype = {
        construcor: Person,
        name: ‘Candy‘,
        age: 22,
        job: ‘Dancer‘,
        sayName: function(){
            alert(this.name);
        }
    };

    friend.sayName(); //error

  因为,调用构造函数时会为实例添加一个指向最初原型的指针,而把原型修改为另外一个对象就等同于切断了实例与最初原型之间的联系。

  实例中的指针仅指向原型,而不指向构造函数。

  重写原型对象切断了现有原型与任何之前已经存在的对象实例之间的联系,他们引用的仍然是最初的原型。

  原型对象的弊端:当属性中包含引用类型值时,修改实例中相应的引用类型值将会间接修改原型中的引用类型值:

function Person(){}
Person.prototype = {
        construcor: Person,
        name: ‘Candy‘,
        age: 22,
        job: ‘Dancer‘,
        friends: [‘Mary‘,‘June‘],
        sayName: function(){
            alert(this.name);
        }
    };

    var person1 = new Person();
    var person2 = new Person();

    person1.friends.push(‘Van‘);

    alert(person1.friends); //"Mary,June,Van"
    alert(person2.friends); //"Mary,June,Van"
    alert(person1.friends === person2.friends); //true

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

  构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性:

function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = [‘Lily‘,‘Candy‘];
}

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(‘Simon‘);
alert(person1.friends); //"Lily,Candy,Simon"
alert(person2.friends); //"Lily,Candy"
alert(person1.friends === person2.friends); //false
alert(person1.sayName === person2.sayName); //true

  5.动态原型模式

  6.寄生构造函数模式(和工厂模式一样)

  7.稳妥构造函数模式

  稳妥对象:没有公共属性,其方法也不引用this的对象。

  稳妥构造函数遵循与寄生构造函数类似的模式,不同之处在于:新创建的实例方法不引用this;不使用new操作符调用构造函数。 

function Person(name,age,job){
    //创建要返回的对象
    var o = new Object();
    //可以在这里定义私有变量和函数

    //添加方法
    o.sayName = function(){
        alert(name);
    };
    //返回对象
    return o;
}

var friend = Person(‘Nicholas‘,22,‘Software Engineer‘);
friend.sayName();   //"Nicholas"

二、继承

  1.原型链继承

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

  每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。故而,使原型对象等于另一个类型的实例,就实现继承:

A.prototype = new B();    //继承了B

  原型链继承的实质:重写原型对象。

  当以读取模式访问一个实例属性时,首先会在实例中搜索该属性,若没有找到该属性,则会继续搜索实例的原型。在通过原型链继承的情况下,搜索过程就得以沿着原型链继承向上。

  所以引用类型都默认继承了Object,这个继承也是通过原型链实现的。所以函数的1默认原型都是Object的实例。P164

  确定原型和实例之间的关系:使用 instanceofisPrototypeOf() 方法。

alert(A instanceof B);  //boolean  

// A是B的实例,则返回true,反之返回false
alert(A.prototype.isPrototypeOf(B));    //boolean
 //A的原型也是B的原型

  给原型添加方法的代码一定要放在替换原型的语句之后。

  在通过原型链实现继承时,不能使用对象字面量创建原型方法,这样会重写原型方法。P166

  原型链继承的弊端:问题源于包含引用类型值的原型,包含引用类型值的原型属性会被所有实例共享。

  

  2.借用构造函数继承

  即,在子类型构造函数的内部调用超类型构造函数,通过 apply() call() 实现:

function Sub(){
    Sup.call(this);  //Sub继承了Sup
}

  弊端:方法都在构造函数中定义,函数复用无从说起。

  

  3.组合继承

  将借用构造函数和原型链组合起来。

  思路:通过原型链实现对原型属性和方法的继承,通过借用构造函数实现对实例属性的继承。

  

  4.原型式继承

  5.寄生式继承

  6.寄生组合式继承

  

  还是直接看书更好理解啊朋友们~

时间: 2024-10-05 20:01:24

《Javascript高级程序设计》第六章笔记整理的相关文章

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

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

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 原型链 基本思想:利用原型让一个引用类型继承另一个引用类型的属性和方法. 回顾一下构造函数.原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针. 所以,试想一下,我们让原型对

javascript高级程序设计 第十三章--事件

javascript高级程序设计 第十三章--事件js与HTML的交互就是通过事件实现的,事件就是文档或浏览器窗口中发生的一些特定的交互瞬间. 事件流:事件流描述的是从页面中接收事件的顺序,IE的是事件冒泡流,Netscape的是事件捕获流,这个两个是完全相反的事件流概念. 事件冒泡:由最具体的元素接收,然后逐级向上传播到更高级的节点,即事件沿DOM树向上传播,直到document对象. 事件捕获:不大具体的节点应该更早接收到事件,相当于沿DOM节点树向下级传播直到事件的实际目标,在浏览器中,是

javascript高级程序设计 第十一章--DOM扩展

javascript高级程序设计 第十一章--DOM扩展DOM最主要的扩展就是选择符API.HTML5和Element Traversal Selectors API:定义了两个方法 querySelector() 和 querySelectorAll(),能够基于CSS选择符从DOM中取得元素.querySelector()方法接收一个CSS选择符,返回该模式匹配的第一个元素,querySelectorAll()接收的参数一样,但是返回NodeList实例: matchesSelector()

Javascript高级程序设计——第三章:基本概念

javascript高级程序设计——第三章:基本概念 一.语法 EMCA-262通过叫做ECMAScript的“伪语言”为我们描述了javascript实现的基本概念 javascript借鉴了C的语法,区分大小写,标示符以字母.下划线.或美元符号($)开头,注释可以用 // 或者/* */ 严格模式: ECMAScript 5引入了严格模式,在严格模式下不确定的行为将得到处理,通过在顶部添加 “use strict”来启用严格模式: function fuc(){ "use strict&qu

《JavaScript高级程序设计第五章--引用类型》之Object对象和array对象

这一章主要就是介绍各种内置对象的用法,认识其经常用到的属性和方法. 5.1Object类型 创建objec的方式,一种是new Object(),一种是对象字面量(简化创建包含大量属性的对象的过程) var person = { name = "Nicholas"; age = 27 };//最后一个属性不必添加逗号,ie7等会导致错误 //在使用对象字面量语法时,属性名也可以使用字符串.这里的数值属性会自动转字符串. var person = { "name" :

JavaScript高级程序设计第20章JSON 笔记 (学习笔记)

第二十章 JSON 1.Json 可以表示三种类型的值: 1.简单值: 表示数值:5  表示字符串:“hello wrold”注表示字符串时必须使用双引号 2.对象: {“name”:“mi”,”age”:24}与JavaScript对象有两处不同一是没有声明变量 二是没有末尾的分号json对象中属性必须带引号,对象里的值可以嵌套对象. 3.数组 Json数组表示:[25,”hi”,true]和json对象一样json里也没有分号和变量 2.json对象 1.Stringify()方法用于把ja

JavaScript高级程序设计第14章表单脚本 (学习笔记)

第十四章 表单脚本 1.阻止默认表单提交 1.提交表单数据 1.使用type=submit提交按钮 2.使用submit():方法 注意:当用户点击提交按钮时,会触发submit事件,从而在这里我们有机会验证数据并决定要不要提交表单 1.preventnDefault(event):可以用在表单数据无效时不发给服务器,对于动态绑定的onclick 事件. 2.Submit()方法也可以提交表单,但是不会触发submit事件,所以在使用时须先验证表单数据, 如:<%response.write(t