JS学习笔记(第六章)(面向对象之创建对象)

栏目: JavaScript · 发布时间: 5年前

内容简介:抽象了创建具体对象的过程。工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)构造函数模式与工厂模式存在以下几点不同:(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 0;
}
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");

构造函数模式与工厂模式存在以下几点不同:

(1)没有显示地创建对象;

(2)直接将属性和方法赋给了this对象;

(3)没有return语句

要创建Person的新实例,必须使用new操作符,以这种方式调用构造函数实际上会经历一下4个步骤:

(1)创建一个新得对象;

(2)将构造函数的作用域赋值给新对象(因此this就指向了这个新对象);

(3)执行构造函数中的代码(为这个新对象添加属性);

(4)返回新对象。

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

使用构造函数的主要问题就是每个方法都要在每个实例上重新创建一遍。

解决办法:通过吧函数定义转移到构造函数外部来解决这个问题。

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

JS学习笔记(第六章)(面向对象之创建对象)

JS学习笔记(第六章)(面向对象之创建对象)

三、原型模式

我们创建的每个函数都有一个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

1、理解原型对象

(1)只要创建了一个新的函数,就会根据一组特定的规则诶该函数创建一个prototype属性,这个属性指向函数的原型对象;

(2)在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性是一个指向prototype属性所在函数的指针;

(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();
var person2 = new Person();

下图展示了上述代码创建的各个对象之间的关系。

JS学习笔记(第六章)(面向对象之创建对象)

(1)可以通过 isPrototypeOf() 方法来确定对象之间是否存在[[Prototype]]关系,如果[[Prototype]]指向调用isPrototypeOf()方法的对象(Person.prototype),那么这个方法就返回true。

alter(Person.prototype.isprototypeOf(person1));  //true

Object.getPrototypeOf() ,在所有支持的实现中。这个方法返回[[Prototype]]的值。

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

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

(3)虽然可以意通过对象实例访问保存在原型中的值,但却不能够通过对象实例重写原型的值。党委对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性;也就是说,添加这个属性只会阻止我们访问原型中的那个属性,但不会修改那个属性。

(4)使用delete操作符则可以完全删除实例属性,从而使我们能够重新访问原型中的属性。

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"来自原型

delete(person1.name); //完全删除实例对象
alert(person1.name); //"Nicholas"来自原型

(5)使用 hasOwnProperty() 方法可以检测一个属性是存在于实例中,还是存在于原型中。这个方法只在给定属性存在于对象实例中时,才会返回true。

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

alert(person1.hasOwnPrototype("name")); //false

person1.name = "Greg";
alert(person1.name); //"Greg"来自实例
alert(person1.hasOwnPrototype("name")); //true

alert(person2.name); //"Nicholas"来自原型
alert(person1.hasOwnPrototype("name")); //false

delete(person1.name); //完全删除实例对象
alert(person1.name); //"Nicholas"来自原型
alert(person1.hasOwnPrototype("name")); //false

2、原型与in操作符

(1)有两方式使用in操作符:单独使用和在for-in循环使用。在单独使用时,in操作符会在通过对象能够访问给定属性时返回true,无论属性存在于实例还是原型中;在使用for-in循环时,返回的是所有能够通过对象访问的、可枚举的属性,其中既包括存在于实例中的属性,也包括存在于原型中的属性。

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

alert(person1.hasOwnPrototype("name")); //false
alert("name" in person1); //true

person1.name = "Greg";
alert(person1.name); //"Greg"来自实例
alert(person1.hasOwnPrototype("name")); //true
alert("name" in person1); //true

alert(person2.name); //"Nicholas"来自原型
alert(person1.hasOwnPrototype("name")); //false
alert("name" in person2); //true

delete(person1.name); //完全删除实例对象
alert(person1.name); //"Nicholas"来自原型
alert(person1.hasOwnPrototype("name")); //false
alert("name" in person1); //true

(2)同时使用hasOwnProperty()方法和in操作符,就可以确定该属性到底是存在于对象中,还是存在于原型中。

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

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

var person = new Person();
alert(person1.hasOwnPrototype(person,"name")); //true表示属性存在于原型中 

person.name = "Greg";
alert(person1.hasOwnPrototype(person,"name")); //false表示属性存在于实例中

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

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"

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

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.getOwnPropertyName(Person.prototype);
alert(keys);  //"constructor,name, age, job, sayName"

3、更简单的原型语法

JS学习笔记(第六章)(面向对象之创建对象)

//原型模式
function Person() {
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function() {
    alert(this.name);
};

//更简单的原型语法:用一个包含所有属性和方法的对象字面量来重写整个原型对象;
导致的问题:相当于重写了默认的prototype属性,constructor属性不再指向Person 
function Person() {
}
Person.prototype = {
    name : "Nicholas",
    age : 29,
    job : "Software Engineer",
    sayName : function () {
        alert(this.name);
    }
};

解决办法:特意将constructor属性设置回适当的值

function Person() {
}
Person.prototype = {
    constructor : Person, //将constructor设置为原来的值
    name : "Nicholas",
    age : 29,
    job : "Software Engineer",
    sayName : function () {
        alert(this.name);
    }
};

JS学习笔记(第六章)(面向对象之创建对象)

但是,以这种方式重设constructor属性导致它的[[Enumberable]]特性被设置为true。默认情况下,原生的constructor属性是不可枚举的。因此可以尝试用Object.defineProperty()重设构造函数。

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

4、原型的动态性

function Person() {
}
var friend = new Person();
//重写整个原型对象
Person.prototype = {
    name : "Nicholas",
    age : 29,
    job : "Software Engineer",
    sayName : function () {
        alert(this.name);
    }
};
friend.sayName();  //error

重写对象之前

JS学习笔记(第六章)(面向对象之创建对象)

重写原型对象之后

JS学习笔记(第六章)(面向对象之创建对象)

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

5、原型对象的问题

原型模式的最大问题是由其共享的本质所导致的,原型中的所有属性是被很多实例共享的。但是,实例一般都是要有属于自己的全部属性的。

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

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

创建自定义类型的最常见方式,就是组合使用构造函数模式和原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。

//构造函数
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); //"Shebly", "Court","Van"
alert (person2.friends); //"Shebly", "Court"
alert(person1.friends === person2.friends); //false
alert(person1.sayName === person2.sayName); //true

五、动态原型模式

可以通过检查某个应该存在的方法是否有效来决定是否需要初始化

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    //方法
    if (typeof this.name != "function") {
        Person.prototype.sayName = function() {
            alert(this.name);
        };
    }
}
var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName();

注意:如果在已经创建了实例的情况下重写原型,就会切断现有实例与新原型之间的联系。

六、寄生构造函数模式

P160封装创建对象的代码,然后再返回新创建的对象

function Person (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 friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName();  //"Nicholas"

七、稳妥构造函数模式

P161稳妥对象,指的是没有公共属性,而且其方法也不引用this的对象。稳构造函数遵循与寄生构造函数类似的模式,但有两点不同:(1)新创建对象的实例方法不引用this;(2)不使用new操作符点用构造函数。

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

    //添加函数
    o.sayName = function() {
        alert(name);
    };
    //返回对象
    return o;
}
var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName();  //"Nicholas"

注意,在以这种模式创建的对象中,除了使用sayName()方法之外,没有其他办法访问name的值。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

人月神话

人月神话

[美] 弗雷德里克·布鲁克斯 / 汪颖 / 清华大学出版社 / 2002-11 / 29.80元

作者为人们管理复杂项目提供了颇具洞察力的见解,既有很多发人深省的观点,也有大量的软件工程实践。书中的内容来自布鲁克斯在IBM公司System 360家族和OS 360中的项目管理经验。初版的20年后,布鲁克斯重新审视了他原先的观点,增加了一些新的想法和建议。新增加的章节包括:原著中一些核心观点的精华;在经过了一个时代以后,Brooks博士对原先观点新的认识;1986年的经典文章《没有银弹》;对19......一起来看看 《人月神话》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

在线进制转换器
在线进制转换器

各进制数互转换器

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具