js中创建对象方式----原型模式

一、什么是原型模式

在js中,创建对象的方式有工厂模式和构造函数模式等; 而构造函数模式最大的问题在于:构造函数中的每个方法都需要在实例对象中重新创建一遍,不能复用,所以为了解决这一个问题,就需要使用原型模式来创建对象。
原型模式是把所有实例共享的方法和属性放在一个叫做prototype(原型)的属性中 ,在创建一个函数时都会有个prototype属性, 这个属性是一个指针,指向一个对象,是通过调用构造函数而创建的那个对象实例的原型对象

 // 构造函数
    function Person() {};

    // 原型属性prototype
    Person.prototype.name = ‘张三‘;
    Person.prototype.sayName = function() {
        console.log(this.name);
    };

    let person1 = new Person();
    person1.sayName(); //张三
    let person2 = new Person();
    person2.sayName(); // 张三

    console.log(person1.sayName == person2.sayName); //true
  1. 理解原型对象

    无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象,在默认的情况下,所有的原型对象都自动获得一个constructor(构造函数)属性,这是一个指针,指向prototype属性所在的函数。创建了自定义的构造函数之后,其原型对象默认只会取得constructor属性;其他的方法则是从Object继承来的。
    当调用构造函数创建一个新实例对象后,该实例的内部将包含一个指针[[Prototype]],指向构造函数的原型对象。这个连接存在于实例和构造函数的原型对象之间,而不是存在实例和构造函数之间。
    每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。搜索首先从对象实例本身开始。如果在实例中找到了就返回该属性的值,没有找到,则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性,如果在原型对象中找到了这个属性,就返回该属性的值。
    虽然可以通过实例访问保存在原型中的值,但不能通过实例对象重写原型中的值,如果在实例中添加一个在原型中的同名属性,该属性会自动屏蔽原型中的属性,但是不会修改原型中的属性,只会阻止访问原型中的属性,通过delete操作符则可以完全删除实例属性,使得可以重新访问原型中的属性。

  2. 原型与in操作符

    hasOwnProperty()方法可以检测一个属性是否存在于实例对象中,
    // 构造函数
    function Person() {
    this.age = 16;
    };
    Person.prototype.name = "张三";
    let person1 = new Person();
    console.log(person1.hasOwnProperty(‘name‘)); // false
    console.log(person1.hasOwnProperty(‘age‘)); // true

    in操作符的使用可以分为两类,单独使用和在for-in循环使用,在单独使用时,in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例中还是原型中。
    // 构造函数
    function Person() {}
    Person.prototype.name = ‘zhang‘;
    let person1 = new Person();
    console.log(‘name‘ in person1); // true
    person1.age = 14;
    console.log(‘age‘ in person1); // true
    同时使用hasOwnProperty()方法和in操作符,可以确定该属性时在原型上还是在存在于对象中。
    // 构造函数
    function Person() {}
    function hasPrototypeProperty(object, name) {
    return !object.hasOwnProperty(name) && (name in object);
    }
    Person.prototype.name = "张三";
    let person = new Person();
    console.log(hasPrototypeProperty(person, ‘name‘)); // true
    console.log(hasPrototypeProperty(person, ‘age‘)); // false
    使用for-in循环时,返回的是所有能够通过对象访问的、可枚举的属性,其中即包含存在于实例中的属性,也包含与存在原型中的属性。
    let o = {
    name: ‘san‘,
    age: 14,
    };
    for(let key in o) {
    console.log(key);
    }
    要取得对象上所有可枚举的实例属性,可以使用Object.keys()方法,接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。
    如果想得到所有实例属性。无论是否可枚举,都可以使用Object.getOwnPropertyNames()方法。

  3. 更简单的原型语法

    为了减少不必要的输入和从视觉上更好的封装原型的功能,常见的做法是用一个包含所有属性和方法的对象字面量来重写整个原型对象。
    // 构造函数
    function Person() {};
    Person.prototype = {
    sayHi: function() {
    console.log(hi);
    },
    name: ‘张三‘,
    };
    通过这个方式会导致原型对象中的constructor属性不在指向Person了。如果constructor的值真的很重要,可以像下面这样特意将它设置回适当的值。
    // 构造函数
    function Person() {};
    Person.prototype = {
    constructor: Person,
    sayHi: function() {
    console.log(hi);
    },
    name: ‘张三‘,
    };
    但是通过这种方式会导致对象的[[Enumerable]]特性被设置为ture,默认情况下,constructor属性时不可枚举的,可以通过Object.defineProperty()解决这个问题。
    // 构造函数
    function Person() {};
    Person.prototype = {
    sayHi: function() {
    console.log(hi);
    },
    name: ‘张三‘,
    };
    Object.defineProperty(Person.prototype, "constructor", {
    enumerable: false,
    value: Person
    }

  4. 原型的动态性

    当对原型对象所做的任何修改都能够立即从实例上反应出来。
    function Person() {};
    var friend = new Person();
    Person.prototype.sayHi = function() {
    console.log(‘hi‘);
    };
    friend.sayHi(); // hi

    但是如果是重写整个原型对象,那么情况就不一样了。调用构造函数时会为实例添加一个指向最初原型的[[prototype]]指针,而把原型修改为另外一个对象 就相当于切断了构造函数与最初原型之间的联系。 实例中的指针仅指向原型,而不是指向构造函数。
    // 构造函数
    function Person() {};
    var friend = new Person();
    Person.prototype = {
    constructor: Person,
    sayHi: function() {
    console.log(hi);
    }
    };
    friend.sayHi(); // Uncaught TypeError: friend.sayHi is not a function

    创建了一个Person的实例,然后又重写了其原型对象。但是在使用sayHi()时发生了错误,这个时候实例所指向的原型对象是一个新的对象。重写原型对象切断了现有原型与之前已经存在的对象实例直接的联系。所以报错了。

  5. 原生对象的原型

    原型模式的重要性不仅体现在创建自定义类型方面,就连所有原生的引用类型,都采用这种模式,所有的原生引用类型(Object、Array、String)等,都在其构造函数的原型上定义了方法。可以像修改自定义对象的原型一样修改原生对象的原型。

二、原型模式的缺点

对于包含引用类型值的属性来说,所有实例在默认的情况下都会取得相同的属性值。
// 构造函数
function Person() {};
// 原型属性prototype
Person.prototype = {
constructor: Person,
friends: [‘张三‘, ‘李四‘],
}
let person1 = new Person();
let person2 = new Person();
person1.friends.push(‘王五‘);
console.log(person1.friends); // ["张三", "李四", "王五"]
console.log(person2.friends); // ["张三", "李四", "王五"]

由于friends存在于Person的原型对象中,所以person1对friends的修改也会通过person2反应出来,但是实例对象一般都是要有属于自己的全部属性,正因为如此,很少有人单独使用原型模式来创建对象。

原文地址:https://blog.51cto.com/9195095/2400210

时间: 2024-08-28 16:14:15

js中创建对象方式----原型模式的相关文章

js中创建对象方式

1.基本的三种方式 <script> //1.方法一 var per1={ name:"卡卡西", age:"15", eat:function(){ console.log("及哈哈"); } }; //方法二 var per2=new Object(); per2.name="大蛇丸"; per2.age=14; per2.eat=function(){ console.log("吃阿布")

js中创建对象的几种方式

js中的几种创建对象的方式. 一共有5种: 一 , 工厂方式 var lev = function() {    return this.age;};function Parent() {    var child = new Object();    child.name = '小芳';    child.age = 30;    child.lev = lev;    return child;}var x = Parent();alert(x.name);alert(x.lev()); 说

js中构造函数的原型添加成员的两种方式

首先,js中给原型对象添加属性和方法. 方式一:对象的动态特效 给原型对象添加成员 语法:构造函数.prototype.方法名=function (){ } 方式二:替换原型对象(不是覆盖,而是替换,把原先的同名的直接替换成现在的) 语法:构造函数.prototype.方法名={ } tips:方式二不常用,因为这样会修改了原型本身 搜索:JS中通过构造函数添加成员方法和通过原型法添加成员方法的区别 参考网址  http://blog.csdn.net/xxmzumeng/article/det

JS中创建对象的方法

最近手头一个项目刚完成,下一个显目还在准备中,趁这个空档期,拿起尘封多年的JS书, 重温一遍JS面向对象程序设计,然后就得出下文,算是一个总结吧. 也许,你会说 "创建对象不就是一对花括号的事吗?",是的,目前我们最常用, 也是最便捷的方式就是所谓的一对花括号的事,也就是我们常说的JSON对象(严格意义上,这其实不算JSON对象,具体我们这里不做深入),如下: let obj = { name:'xiaohong', age: 17, gender: 'female' } 这是就是我们

谈谈 js中的几种模式 (一)

今天看了<JavaScript 高级程序设计>(第三版)这本书,颇有收获,总想写点什么,只恨自己菜鸟一只写不出什么真知灼见,只能......好了废话不多说,开篇了. 大家都知道在js中可以用Object构造函数和对象字面量这 //利用Object构造函数创建对象 var person=new Object(); person.name="DJL"; person.age=22; //利用对象字面量创建对象 var person2={ name:"DJL"

js中创建对象的常用方法

[转自e良师益友网]其中对对象的创建做了具体的阐述,综合起来,总结了下: 第一种模式:工厂方式 代码如下: var lev=function(){ return "e良师益友网"; }; function Parent(){ var Child = new Object(); Child.name="交流学习"; Child.age="4"; Child.lev=lev; return Child; }; var x = Parent(); al

[工作中的设计模式]原型模式prototype

一.模式解析 提起prototype,最近看多了js相关的内容,第一印象首先是js的原型 var Person=function(name){ this.name=name; } Person.prototype.run=function(){ alert(this.name+" is running"; } 此处的原型是js的特殊定义,在原型上定义的属性和方法所有的类进行共享. 不过设计模式中的原型模式指的是:将已有的对象作为原型,拷贝出一份具有相同属性的新的对象. 模式定义为:原型

js中函数的原型

js中每一个构造函数都有一个prototype的属性,prototype指向一个对象,而这个对象的属性和方法都会被构造函数的实例所继承,因此,需要一些共享的属性和方法可以写在构造函数的原型中 1  用prototype属性可以向构造函数添加可继承的属性和方法,注意constructor属性指向prototype对象所在的函数 <script> function Person(){ } Person.prototype = { constructor: Person, name: 'xxx',

设计模式在游戏中的应用--原型模式(六)

Prototype原型模式是一种创建型设计模式,Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建. 上面是原型模式的UML结构图. 下面是原型模式的代码: #include "stdafx.h" #include <iostream> #include <string> using namespace s