上一篇啊,我们聊了聊字面量对象和自定义构造函数。这一篇,我们继续,来聊聊new和数组字面量。

三、强制使用new的模式

  要知道,构造函数,只是一个普通的函数,只不过它却是以new的方式调用。如果在调用构造函数时忘记制定new操作符会发生什么?这并不会导致语法或运行时错误,但可能导致逻辑错误或意外的行为发生。发生这类问题是因为您忘记使用new操作符,从而导致结构函数中的this指向了全局对象(在浏览器中,this会指向window)。

// 构造函数
function Waffle() {
this.tastes = 'yummy';
} // 定义一个新对象
var good_morning = new Waffle();
console.log(typeof good_morning); // "object"
console.log(good_morning.tastes); //"yummy" // 反模式
// 忘记使用new操作符
var good_morning_1 = Waffle();
console.log(typeof good_morning_1); // "undefined"
console.log(window.tastes); //"yummy"
console.log(good_morning_1.tastes); //"Error"

  上面的代码,在没有使用new操作符的情况下,没有改变this的指向,导致污染了全局,并得到了不符合预期的结果。

命名约定

  最简单的方法是使用命名约定,使构造函数的名称中的首字母变成大写(MyConstructor),并且使“普通”函数和方法的名称中的首字母变成小写(MyFunction)。

使用that

  遵循命名约定一定程度上有助于避免忘记使用new所带来的问题,但是命名约定只是一种建议,不具有强制保证正确的行为。下面的模式可以确保构造函数的行为总是表现出应有的预期。

// 构造函数
function Waffle() {
var that = {};
that.tastes = 'yummy';
return that;
}
// 对于简单的对象,甚至不需要类似that这样的局部变量
function Waffle1() {
return {
tastes:"yummy"
};
} // 使用上面任何一种Waffle()的实现方式都总是会返回一个对象,而无论它是如何被调用的:
var first = new Waffle(),
second = Waffle();
console.log(first.tastes);
console.log(second.tastes);

  需要注意的是,上面代码中的that,只是一个命名公约,你可以使用任意名称。

  但是,上面的解决方法有个问题,就是会丢失原型的连接,因为,您添加到Waffle()原型的成员,对于对象来说都是不可用的。我们看代码:

// 构造函数
function Waffle() {
this.tastes = 'yummy';
} var first = new Waffle();
console.log(first.tastes); Waffle.prototype.getName = function (){console.log(this.tastes + '------')};
first.getName();

  这是正常的模式,构造函数中隐式的返回this,并且我们可以获取到原型链上的方法。但是:

// 构造函数
function Waffle() {
var that = {};
that.tastes = 'yummy';
return that;
} // 使用上面任何一种Waffle()的实现方式都总是会返回一个对象,而无论它是如何被调用的:
var first = new Waffle(),
second = Waffle();
console.log(first.tastes);
console.log(second.tastes); Waffle.prototype.getName = function (){console.log(this.tastes + '------')}
first.getName()
second.getName()

  好吧,实际上我只是在最开始的代码里,给构造函数的原型上加了个方法,我们发现,无论是first.getName()还是second.getName()都会报错。这是为什么呢?区别就在于,你在构造函数内部返回的是的对象,是否继承了构造函数本身的原型链。

  那么,还是上面的代码,我把this赋值给that是不是就可以了?

function Waffle() {
//注意这里细微的区别
var that = this;
that.tastes = 'yummy';
return that;
} // 使用上面任何一种Waffle()的实现方式都总是会返回一个对象,而无论它是如何被调用的:
var first = new Waffle(),
second = Waffle();
console.log(first.tastes);
console.log(second.tastes); Waffle.prototype.getName = function (){console.log(this.tastes + '------')}
first.getName()
second.getName()

  但是,我们发现,second.getName()报错了。这是因为没有new运算符所做的内部逻辑,前面的章节说过。new操作符到底做了什么:创建一个空对象并且this变量引用了该对象,同时还继承了该函数的原型。这是最重要的一句,所以,你没有用new,没有继承该函数的原型。那你说我自己手动继承行不行。当然可以,这里我就不演示了,自己去尝试一下。我们继续。

自调用构造函数

  为了解决前面模式的缺点,并使得原型(prototype)属性可在实例对象中使用,那么可以考虑下面的方法。具体来说,可以在构造函数中检查this是否为构造函数的一个实例,如果为否,构造函数可以再次调用自身,并且在这次调用中正确地使用new操作符:

// 构造函数
function Waffle() {
if(!(this instanceof Waffle)){
return new Waffle();
}
this.tastes = 'yummy';
} Waffle.prototype.getName = function (){console.log(this.tastes + '------')} var first = new Waffle(),
second = Waffle();
console.log(first.tastes);
console.log(second.tastes); first.getName()
second.getName()

  实际上,上面的代码就是判断生成的实例是否是由该构造函数所创建的,如果不是,就重新通过new运算符创建一下。

  另一种用于检测实力对象的通用方法是将其与arguments.callee进行比较,而不是在代码中硬编码构造函数名称:

if(!(this instanceof arguments.callee)){
return new arguments.callee();
}

  使用这种模式是基于这样一个事实:即在每个函数内部,当该函数被调用时,将会创建一个名为arguments的对象,其中包含了传递给该函数的所有参数。同时,arguments对象中又一个名为callee的属性,该属性会指向被调用的函数。

  需要注意的是,在ES5的严格模式中,并不支持arguments.callee属性,因此,最好限制在将来才使用该属性。

四、数组字面量

  JavaScript中的数组与语言中的绝大多数事物比较相似,即都是对象。当然,数组也同样可以通过内置的构造函数Array()来创建,但是极其不推荐这种做法。请使用数组字面量来创建一个数组!

// 具有三个元素的数组
// 反模式
var a = new Array("itsy","bitsy","spider"); // 完全相同的数组
var a = ["itsy","bitsy","spider"]; console.log(typeof a); //输出“object”,这是由于数组本身也是对象类型
console.log(a.constructor === Array); //输出true

  

数组字面量语法

  数组字面量表示法并没有太多的内容:它只是一个逗号分隔的元素列表,并且整个列表包装在方括号中。可以给数组元素制定任意类型的值,包括对象或者其它数组。

  数组字面量语法是非常简单、明确,并且优美的。毕竟,一个数组仅是一个零值缩阴列表。为此,也没有必要通过引入构造函数以及使用new操作符使得事情变得复杂。

数组构造函数的特殊性

  避开new Array()的另一个理由是用于避免构造函数中可能产生的陷阱。

// 具有一个元素的数组
var a = [3];
console.log(a.length); //
console.log(a[0]); // // 具有三个元素的数组
var a = new Array(3);
console.log(a.length); //
console.log(typeof a[0]); // "undefined"

  上面的例子,当向数组构造函数传递一个整数时,它并不会作为数组的第一个值。相反,它却设定了数组的长度。这意味着new Array(3)这个语句创建了一个长度为3的数组,但是该数组中并没有实际的元素。

  假如,你像数组构造函数中传递了一个浮点数,那么情况会变得更糟:

// 具有一个元素的数组
var a = [3.14];
console.log(a[0]); // 3.14 // 具有三个元素的数组
var a = new Array(3.14);
console.log(typeof a); // "undefined"

  为了避免您在运行时创建动态数组可能产生的潜在错误,坚持使用数组字面量表示法。程序将会更加安全。

  tips:虽然有一些使用Array()构造函数的灵巧方法,比如重复字符串。下面的代码片段返回了一个具有255个空白字符的字符串(为什么不是256个呢?)。

var white = new Array(256).join(' ');

检查数组性质

  以数组作为操作数并且使用typeof操作符,其结果会返回“object”。

  虽然这种行为是有意义的(数组也是对象),但对于排除错误却没有什么帮助。通常,需要知道某个值是否是一个数组。有时候,可以检查代码是否存在length属性或者一些数组方法,比如slice()方法,以此来确定该值是否具有“数组性质”。

  但是这些检查机制并不健壮,因为没有任何理由确定一个非数组对象就不能具有同样名称的属性和方法。另外一些人使用instanceof Array进行检查,但是这种检查机制在某些IE浏览器版本中的不同框架中运行并不正确。

  ECMAScript 5定义了一个新方法,Array.isArray(),该函数在参数为数组时返回true:

console.log(Array.isArray([]));// true

// 试图以一个类似数组的对象欺骗检查
console.log(Array.isArray({
length:1,
"0":1,
slice:function() {}
}));// false

  但是,假如,在您的环境中无法使用这种新方法,可以通过调用Object.prototype.toString()方法对其进行检查。如果在数组上、下文中调用了toString的call()方法,他应该返回字符串“[object Array]”。如果该上、下文是一个对象,则它应该返回字符串“[object Object]”。因此,可以这样:

if(typeof Array.isArray === 'undefined'){
Array.isArray = function(arg) {
return Object.prototype.toString.call(arg) === "[object Array]";
};
}

  本来,想要把JSON的部分也写在这里,但是这篇的内容和重点已经足够多了。还是放在下一篇文章吧。

JavaScript 模式》读书笔记(3)— 字面量和构造函数2的更多相关文章

  1. JavaScript模式读书笔记 文章3章 文字和构造

    1.对象字面量     -1.Javascript中所创建的自己定义对象在任务时候都是可变的.能够从一个空对象開始,依据须要添加函数.对象字面量模式能够使我们在创建对象的时候向其加入函数.       ...

  2. JavaScript模式读书笔记 第4章 函数

    2014年11月10日 1.JavaScript函数具有两个特点: 函数是第一类对象    函数能够提供作用域         函数即对象,表现为:         -1,函数能够在执行时动态创建,也 ...

  3. JavaScript 模式》读书笔记(3)— 字面量和构造函数3

    这是字面量和构造函数的最后一篇内容,其中包括了JSON.正则表达式字面量,基本值类型包装器等知识点.也是十分重要的哦. 五.JSON JSON是指JavaScript对象表示以及数据传输格式.它是一种 ...

  4. 《JavaScript 模式》读书笔记(3)— 字面量和构造函数1

    新的篇章开始了,本章开始,所有的内容都是十分有价值和意义的.本章主要的内容包括对象字面量.构造函数.数组字面量.正则字面量.基本值类型字面量以及JSON等.在大家的工作和实际应用中也有一定的指导意义. ...

  5. 《JavaScript模式》第3章 字面量和构造函数

    @by Ruth92(转载请注明出处) 第3章:字面量和构造函数 一.创建对象的三种方式 // 对象字面量 var car = {goes: "far"}; // 内置构造函数(反 ...

  6. 《Javascript模式》之对象创建模式读书笔记

    引言: 在javascript中创建对象是很容易的,可以使用对象字面量或者构造函数或者object.creat.在接下来的介绍中,我们将越过这些方法去寻求一些其他的对象创建模式. 我们知道js是一种简 ...

  7. 《javascript模式--by Stoyan Stefanov》书摘--字面量和构造函数

    二.字面量和构造函数 1,能够使用对象字面量时,就没理由使用new Object构造函数 // 一个空对象var 0 = new Object();console.log( o.constructor ...

  8. 《编写可维护的javascript》读书笔记(中)——编程实践

    上篇读书笔记系列之:<编写可维护的javascript>读书笔记(上) 上篇说的是编程风格,记录的都是最重要的点,不讲废话,写的比较简洁,而本篇将加入一些实例,因为那样比较容易说明问题. ...

  9. 《你不知道的javascript》读书笔记2

    概述 放假读完了<你不知道的javascript>上篇,学到了很多东西,记录下来,供以后开发时参考,相信对其他人也有用. 这篇笔记是这本书的下半部分,上半部分请见<你不知道的java ...

随机推荐

  1. 使用apktool反编译apk文件

    Apktool https://ibotpeaches.github.io/Apktool/install/ 下载地址:Apktool https://bitbucket.org/iBotPeache ...

  2. R的基础数据结构

  3. 使用 KM 处理 HHKB 方向键

    对于上了 HHKB 这条贼船的人来说,刚开始使用起来最大的别扭可能就是没有方向键的问题了. 最早的我使用 Karabiner 来解决,里边有一些内置的组合可以替代方向键,我用 control + hj ...

  4. Microsoft Translator:打破语言障碍 拓展全球沟通新机遇

    Translator:打破语言障碍 拓展全球沟通新机遇"> 作者:Olivier Fontana, 微软研究院Microsoft Translator产品战略总监 世界越来越小,全球协 ...

  5. 一条SQL在内存结构与后台进程工作机制

    oracle服务器由数据库以及实例组成,数据库由数据文件,控制文件等物理文件组成,实例是由内存结构+后台进程组成,实例又可以看做连接数据库的方式,在我看来就好比一家公司,实例就是一个决策的办公室,大大 ...

  6. JMeter接口测试-计数器

    前言 在测试注册接口的时候,需要批量注册账号时,每注册一个并且需要随时去修改数据,比较繁琐,除了使用随机函数生成账号,我们还可以使用计数器来进行批量注册. 一:添加配置元件-计数器 二:注册10个账号 ...

  7. Vue-API之全局配置

    API 全局配置 Vue.config 是一个对象,包含 Vue 的全局配置. 源码位置:util/config.js 搜索config 可以找到其源码地址,其中声明了config的类型和默认参数 下 ...

  8. AndroidStudio3.x中api、compile和implementation的区别

    首先在AndroidStudio3.x中compile已经过时 由implementation和api来代替 其次compile与api完全等同 3.x中可以完全将compile换成api mplem ...

  9. 一文看懂js中的clientX,clientY,pageX,pageY,screenX,screenY

    一. 客户区坐标位置(clientX,clientY) 鼠标事件都是在浏览器视口中的特定位置发生的.这个位置信息保存在事件对象的clientX和clientY属性中,所有浏览器都支持这两个属性. 我们 ...

  10. CSS+JS相应式导航菜单

    响应式导航菜单 响应式导航菜单就是当网页在其他不同视口的样式,不同的设备需要不同的样式 需要掌握的知识 - 掌握媒体查询,如果你不是很懂那就看我写的CSS响应式布局 掌握CSS重的display:no ...