资讯专栏INFORMATION COLUMN

《JS高级程序设计》读书笔记----JS创建对象的七种模式

Dogee / 3354人阅读

摘要:用对象字面量形式创建的对象,直接赋值给函数的原型对象,本质上完全重写了其对象,因此属性也就变成了新对象的属性指向构造函数,不再指向函数。

【上一篇】:JavaScript对象内部属性及其特性总结

工厂模式(★★)
先在内部显示地创建一个临时对象,根据接收的参数来构建(赋值属性和方法)该对象,并返回该对象。
缺点:没有解决对象识别的问题(即无法确认一个对象的类型)。
function PersonFactory (name, age){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.sayName = function(){
        console.log(this.name)
    }
    return o;
}

var p1 = PersonFactory();
var p2 = PersonFactory();
p1 instanceof Object; // true
p2 instanceof Object; // true
p1 instanceof PersonFactory; // false  无法识别具体对象类型
p2 instanceof PersonFactory; // false
构造函数模式(★★★)

函数名首字母一般大写(如:Person,借鉴OO语言命名习惯)区别于其他函数;

构造函数本身也是函数,只不过可以用来参加对象而已;

每个新建的实例对象都有一个constructor属性,该属性指向构造函数自身Person

构造函数也可以被当做普通函数来调用;

在全局作用域中调用一个函数时,this对象总是指向Global对象(浏览器中即window对象)

【优点】自定义构造函数可以将它的实例标识为一种特定的类型;

【缺点】构造函数的主要问题是,每个方法都要在每个实例上重新创建一遍;

调用构造函数实际经历4个步骤

创建(隐式地)一个新对象;

将构造函数的新对象赋值给新对象(此时this指向这个新对象);

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

返回新对象(该构造函数的实例);

与工厂模式比较

没有显示地创建对象;

直接将属性和方法赋值给this对象;

没有return语句;

优点:可以确认具体对象类型;

function Person (name, age){
    this.name = name;
    this.age = age;
    this.sayName = function(){
        console.log(this.name);
    }
}
// 当做普通函数
var p1 = new Person("Tom", 20);
var p2 = new Person("Greg", 30);
p1 instanceof Object;         // true
p2 instanceof Object;         // true
p1 instanceof PersonFactory;  // true 可以确认具体对象类型
p2 instanceof PersonFactory;  // true

// 作为普通函数调用
Person("Green", 30);  // 属性和方法都被添加到window对象了
window.sayName();     // "Green"

// 在另一个对象的作用域中调用
var obj = new Object();
Person.call(obj , "Jim", 23);
obj .sayName(); // "Jim"
原型模式(★★★)

构造函数变为空函数;

将所有属性和方法都添加到了构造函数的prototype属性中;

与构造函数模式比较

不同:新对象的属性和方法由所有实例共享(p1和p2访问的都是同一组属性和同一个函数);

function Person(){}

Person.prototype.name = "Tom";
Person.prototype.age = 24;
Person.prototype.sayName = function(){
    console.log(this.name);
}

var p1 = new Person(), p2 = new Person();
p1.sayName();// "Tom"
p2.sayName();// "Tom"
console.log(p1.sayName === p2.sayName);// true
利用更简单的原型语法

每创建一个函数,就会同时创建它的prototype对象,该prototype对象也会自动获得constructor属性。

用对象字面量形式创建的对象,直接赋值给函数的原型对象(Person.prototype),本质上完全重写了其prototype对象,

因此constructor属性也就变成了新对象的constructor属性(指向Object构造函数),不再指向Person函数。

此时instanceof操作符可以返回正确结果,但通过constructor已经无法确定对象类型了。

function Person(){}
Person.prototype = {
    // construtor : Person, // 需要重新指设constructor属性
    name : "Tom",
    age : 24,
    sayName : function(){
        console.log(this.name);
    }
}

var p = new Person(); 
console.log(p instanceof Object); // ture
console.log(p instanceof Person); // ture
console.log(p.construtor == Object); // ture
console.log(p.construtor == Person); // false
安全地重设constructor
以上述方式重设constructor属性会导致它的[[Enumerable]]特性被设置为true;
默认情况下,原生的constructor属性是不可枚举的;
因此可利用Object.defineProperty()重设;
Object.defineProperty(Person.prototype, "constructor", {
    enumerable : false,
    value : Person
});
组合模式(构造函数模式和原型模式)(★★★★)

定义

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

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

优点:

每个实例都会有自己的一份实例属性的副本,同时又共享着对方方法的引用,最大限度节省了内存;

这种混成模式支持向构造函数传递参数;

目前使用最广泛、认同度最高的一种自定义类型的方法,可以说是用来定义引用类型的一种默认模式

// 实例属性均在构造函数中定义
function Person(name, age){
    this.name = name;
    this.age = age;
    this.friends = ["Nicholas", "Jim"];
}
// 由所有实例共享属性constructor和方法则是在原型中定义的
Person.prototype = {
    construtor: Person,
    sayName: function(){
        console.log(this.name);
    }
}

var p1 = new Person("Tom", 25);
var p2 = new Person("Greg", 26);
p1.friends.push("Tony");
console.log(p1.friends); // ["Nicholas", "Jim", "Tony"]
console.log(p2.friends); // ["Nicholas", "Jim"]
console.log(p1.friends === p2.friends); // false
console.log(p1.sayName === p2.sayName); // true
动态原型模式(★★★★★)

定义

动态原型模式把所有信息都封装在了构造函数中;

通过在构造函数中初始化原型(仅在必要的情况下,比如第一次新建实例时);

通过检查某个应该存在(原型中)的方法是否有效,来决定是否需要初始化原型;

优点:保持了同时使用构造函数和原型的特点;

注意点:

下段代码中只会在初次调用构造函数时才会执行,此后原型已经完成初始化,无需再做修改;

这里对原型所做对修改,能够立即在所有实例中得到反映;

if语句检查的可以是初始化之后应该存在的任何属性或方法,检查其中一个即可;

function Person(name, age){
    // 属性
    this.name = name;
    this.age = age;
    // 方法
    if(typeof this.sayName != "function"){
        Person.prototype.sayName = function(){
            console.log(this.name);
        }
    }
}
寄生构造函数模式(★★)

【定义】: 基本思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象

【特点】

返回的对象与构造函数或者与构造函数的原型属性之间没有关系;

寄生构造函数返回的对象与在寄生构造函数外部创建的对象没有什么不同;

不能依赖instanceof操作符来确定对象类型;

与工厂模式的比较

除了使用new操作符并把使用的包装函数叫做构造函数之外,该模式和工厂模式其实是一模一样的;

与构造函数模式的比较

从表面上看,很像典型的构造函数;

构造函数在不返回值的情况下,默认会返回新对象实例

寄生构造函数通过在构造函数的末尾加一个return语句,重写了调用构造函数时返回的值

此时返回的对象的__proto__属性指向Object;

function Person(name, age){
    // 创建临时新对象
    var o = new Object();
    // 初始化该对象
    o.name = name;
    o.age = age;
    o.sayName = function(){
        console.log(this.name);
    }
    // 返回该临时新对象
    return o;
}

var p1 = new Person("Tom", 25);
var p2 = new Person("Greg",30);
console.log(p1 instanceof Object); // true
console.log(p1 instanceof Person); // false
console.log(p1.sayName == p2.sayName); // false
console.log(p1.constructor == Object); //true
稳妥构造函数模式(★★)
所谓稳妥对象,就是指没有公共属性,而且其方法也不引用this的对象。
稳妥对象最适合在一些安全的环境中(这些环境会禁用thisnew),或者防止数据被其他应用程序改动时调用。
与寄生构造函数模式的比较

与寄生构造函数类似,但又不同:

一是新创建的对象实例方法不引用this;

二是不使用new操作符调用构造函数;

function Person (name, age){
    var o = new Object();
    // 可定义私有变量和函数
    // ...
    
    // 新创建的对象实例方法未引用this;
    o.sayName = function(){
        console.log(name)
    }
    return o;
}

var p = Person("Tom", 23);
p.sayName(); // "Tom"
console.log(p instanceof Person); // false

除了sayName()方法外,没有别的方法可以访问其数据成员;

即使有其他代码给这个对象添加方法或数据成员,也不可能有别的办法访问传入到构造函数中的原始数据

与寄生构造函数模式类似,使用稳妥构造函数模式创建的对象与构造函数之间也没有关系(故instanceof操作符对这种对象也无意义);

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

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

相关文章

  • 读书笔记】JavaScriptの类型

    摘要:函数类型检测是的子类型,其属性为参数个数,但是判断结果有内建函数原生函数常见的有,可能被当作构造函数来使用,创建出来的是封装了的基本类型值。构造函数可以不带关键字。建议使用和来进行显示强制转换。 前言 此篇小结来源与《你不知道的JavaScript》和《JavaScript高级程序设计》的结合??或许是的,龟速总结中... 七种内置类型 null undefined boolean ...

    cjie 评论0 收藏0
  • 前端最实用书签(持续更新)

    摘要:前言一直混迹社区突然发现自己收藏了不少好文但是管理起来有点混乱所以将前端主流技术做了一个书签整理不求最多最全但求最实用。 前言 一直混迹社区,突然发现自己收藏了不少好文但是管理起来有点混乱; 所以将前端主流技术做了一个书签整理,不求最多最全,但求最实用。 书签源码 书签导入浏览器效果截图showImg(https://segmentfault.com/img/bVbg41b?w=107...

    sshe 评论0 收藏0
  • 《javascript高级程序设计》第六章 读书笔记 之 javascript继承的6种方法

    摘要:继承的是超类型中构造函数中的属性,如上继承了属性,但没有继承原型中的方法。上述造成的结果是子类型实例中有两组超类型的构造函数中定义的属性,一组在子类型的实例中,一组在子类型实例的原型中。 ECMAScript只支持实现继承,主要依靠原型链来实现。与实现继承对应的是接口继承,由于script中函数没有签名,所以无法实现接口继承。 一、原型链 基本思想:利用原型让一个引用类型继承另一个引用...

    孙吉亮 评论0 收藏0
  • Javascript 设计模式读书笔记(二)——封装,简单的创建对象模式

    摘要:创建对象中,创建对象的基本模式有三种。因此,在设计构造函数时,需要进行慎重考虑。因此在中,这种问题被称作继承破坏封装。静态成员每个只有一份,直接通过类对象进行访问。 什么是封装 找工作时一些公司给了offer后我就想知道真正拿到手的是多少,毕竟赋税繁重。但各种税也好,五险一金也好我实在是弄不清楚,于是我就会在网上的一些税后收入计算器上进行计算,只需要填写一些基本信息,比如税前收入,所...

    lentrue 评论0 收藏0

发表评论

0条评论

Dogee

|高级讲师

TA的文章

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