资讯专栏INFORMATION COLUMN

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

tangr206 / 1673人阅读

摘要:就是通过调用构造函数而创建的那个对象实例的原型对象。创建要返回的对象可以在这里定义私有变量和函数添加函数注意寄生构造函数中是返回对象注意寄生构造函数中是注意,在以这种模式创建的对象中,除了使用方法之外,没有其他办法访问的值。

一、工厂模式
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");

三、原型模式

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

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

(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、更简单的原型语法

//原型模式
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);
    }
};


但是,以这种方式重设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

重写对象之前

重写原型对象之后


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

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.sayName != "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"

寄生构造函数模式除了使用new操作符并把使用的包装函数叫做构造函数之外,这个函数模式和工厂模式其实是一模一样的,下面附上两种模式的代码

七、稳妥构造函数模式

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

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

    //添加函数
    o.sayName = function() {
        alert(name);      `//注意寄生构造函数中是alert(this.name)`
    };
    //返回对象
    return o;
}
var friend = Person("Nicholas", 29, "Software Engineer");   
`//注意寄生构造函数中是var friend = newPerson("Nicholas", 29, "Software Engineer"); `
friend.sayName();  //"Nicholas"

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/109057.html

相关文章

  • JS学习笔记6)(面向对象的程序设计理解对象

    摘要:其中,描述符对象的属性必须是和。吧设置为,表示不能从对象中删除属性。这个方法接收两个对象参数要添加和修改其属性值的对象,第二个是与第一个对象中要添加和修改的属性值一一对应。 理解对象 1、创建自定义对象的两种方法: (1)创建一个Object实例,然后再为它添加属性和方法。 var person = new Object(); person.name = Nicholas; ...

    FingerLiu 评论0 收藏0
  • JS学习笔记6)(面向对象继承——JS继承的六大方式)

    摘要:除此之外,在超类型的原型中定义的方法,对子类型而言也是不可兼得,结果所有类型都只能用构造函数模式。创建对象增强对象指定对象继承属性这个例子的高效率体现在它只调用了一次构造函数。 1、原型链 原型链的基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。构造函数、原型和实例的关系:每个构造函数都有一个原型对象;原型对象都包含着一个指向构造函数的指针;实例都包含一个指向原型对象的...

    lscho 评论0 收藏0
  • JS学习笔记10)(DOM节点层次)

    摘要:节点层次是针对和文档的一个。每一段标记都可以通过一个节点来表示。用于表明节点的类型。返回新增的这个节点。将节点插入到节点列表指定的位置。节点有以下的特性为,为指向元素,指向元素。提供了对元素的标签名,子节点和特性的访问。   节点层次 DOM是针对HTML和XML文档的一个API。DOM描绘了一个层次化的节点树,允许开发人员添加、移除和修改页面的某一部分。 DOM可以将任何HTML或X...

    kamushin233 评论0 收藏0
  • 读《javaScript高级程序设计-6继承

    摘要:此时的原型对象包括一个指向另一个原型的指针,相应的,另一个原型中的指向另一个构造函数。这种关系层层递进,就通过一个原型对象链接另一个构造函数的原型对象的方式实现了继承。 读这篇之前,最好是已读过我前面的关于对象的理解和封装类的笔记。第6章我一共写了3篇总结,下面是相关链接:读《javaScript高级程序设计-第6章》之理解对象读《javaScript高级程序设计-第6章》之封装类 一...

    villainhr 评论0 收藏0
  • 什么是 Python 编程语言?

    摘要:由设计,作为编程语言的继承者,于年首次发布。表达式表达式是编程语言中的语法实体,可以对其进行评估以确定其值。它是编程语言解释和计算以产生值的常量变量函数和运算符的组合。它在年年年和年被评为年度编程语言,是唯一四次获得该奖项的语言。 ...

    1treeS 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<