7 个 JavaScript “特性”

原文链接:http://blog.scottlogic.com/2015/07/02/surprising-things-about-js.html

more

从任何一个代码块中 break

你应该已经知道你可以从任意循环中 break 和 continue —— 这是一个相当标准的程序设计语言结构。但你可能没有意识到,你可以给循环添加一个 label ,然后跳出任意层循环:

outer: for(var i = 0; i < 4; i++) {

while(true) {

continue outer;

}

}

label 特性同样适用于 break 和 continue。你在 switch 语句中肯定见过 break:

switch(i) {

case 1:

break;

}

顺便说一句,这是为什么 Crockford 建议你的 case 不应该缩进 —— 因为 break 跳出的是 switch 而不是 case,但是我认为缩进 case 的可读性更好。你也可以给 switch 语句添加 label:

myswitch: switch(i) {

case 1:

break myswitch;

}

你可以做的另一件事是创建任意块(我知道你可以在 C# 里面这么写,我期望其他语言也可以)。

{

{

console.log("I‘m in an abritrary block");

}

}

因此,我们可以把 label 和 break 放在一起,用来从任意代码块中跳出。

outer: {

inner: {

if (true) {

break outer;

}

}

console.log("I will never be executed");

}

注意到,这只适用于 break —— 因为你只能在一个循环中 continue。我从未见过 label 被使用在 JavaScript 中,我想知道为什么 —— 我想可能因为如果我需要 break 两层,说明把这个代码块放在一个函数里可能更好,这样我可以使用一个单层的 break 或者一个提前的 return 来达到同样的目的。

尽管如此,如果我想要保证每个函数只有一个 return 语句(这不是我的菜),那么我可以使用带 label 的 brock。例如,看下面这个多个 return 语句的函数:

function(a, b, c) {

if (a) {

if (b) {

return true;

}

doSomething();

if (c) {

return c;

}

}

return b;

}

而如果使用 label:

function(a, b, c) {

var returnValue = b;

myBlock: if (a) {

if (b) {

returnValue = true;

break myBlock;

}

doSomething();

if (c) {

returnValue = c;

}

}

return returnValue;

}

还有另一种选择,用更多代码块……

function(a, b, c) {

var returnValue = b;

if (a) {

if (b) {

returnValue = true;

} else {

doSomething();

if (c) {

returnValue = c;

}

}

}

return returnValue;

}

我最喜欢原版,然后是使用 else 的版本,最后才是使用 label 的版本 —— 但是,这可能是因为我的写码习惯?

解构一个已存在的变量

首先,有个怪异的写法我无法解释。貌似 ES3 中你可以添加一个小括号到一个简单的赋值语句左边的变量上,而这样写不会有问题:

var a;

(a) = 1;

assertTrue(a === 1);

如果你能想到为什么这样写可以,请在底下评论!

解构的过程是一个将变量从一个数组或者一个对象中拉取出来的过程。最常见的是以下例子:

function pullOutInParams({a}, [b]) {

console.log(a, b);

}

function pullOutInLet(obj, arr) {

let {a} = obj;

let [b] = arr;

console.log(a, b);

}

pullOutInParams({a: "Hello" }, ["World"]);

pullOutInLet({a: "Hello" }, ["World"]);

而你可以不使用 var 或 let 或 const。对数组你可以让下面的代码如你的期望运行:

var a;

[a] = array;

但是,对于对象,你必须将整个赋值语句用小括号括起来:

var a;

({a} = obj);

必须这样写的理由是,不加括号无法区分代码是解构赋值还是块级作用域,因为你可以使用匿名代码块而 ASI(automatic semi-colon insertion,自动插入括号)会将变量转成可以执行的表达式(如下面的例子所示,能够产生副作用……),这样就产生了歧义。

var a = {

get b() {

console.log("Hello!");

}

};

with(a) {

{

b

}

}

回到原始的例子,我们给我们的赋值语句里的变量加了圆括号 —— 你可能认为它也适用于解构,但它不是。

var a, b, c;

(a) = 1; //这句不是变量解构

[b] = [2];

({c} = { c : 3 });

对数值进行解构

解构的另一个方面你可能也没有意识到,属性名不是必须要是不带引号的字符串,它们也可以是数值:

`var {1 : a} = { 1: true };`

或者带引号的字符串:

`var {"1" : a} = { "1": true };`

或者你可能想要用一个计算的表达式作为名字:

var myProp = "1";

var {[myProp] : a} = { [myProp]: true };

这会很容易写出造成困惑的代码:

var a = "a";

var {[a] : [a]} = { a: [a] };

类声明是块级作用域的

函数声明会被提升,意味着你可以将函数声明写在函数调用之后:

func();

function func() {

console.log("Fine");

}

函数表达式与此相反,因为赋值一个变量的时候,变量声明被提升,但是具体赋值没有被提升。

func(); // func 被声明, 但是值为 undefined, 所以这里抛出异常: "func is not a function"

var func = function func() {

console.log("Fine");

};

类(Classes)成为 ES6 流行的部分,并且已被广泛吹捧为函数的语法糖。所以你可能会认为以下代码是可以工作的:

new func();

class func {

constructor() {

console.log("Fine");

}

}

然而,尽管它基本上是语法糖,但前面的代码是不能工作的。这实际上等价于:

new func();

let func = function func() {

console.log("Fine");

}

这意味着我们的 func 调用在暂时性死区(TDZ),这会导致引用错误。

同名参数

我认为不可能指定同名的参数,然而,却可以!

function func(a, a) {

console.log(a);

}

func("Hello", "World");

// 输出 "World"

在严格模式下不行:

function func(a, a) {

"use strict";

console.log(a);

}

func("Hello", "World");

// 在 chrome 下报错 - SyntaxError: Strict mode function may not have duplicate parameter names

typeof 不安全

好吧,我偷了这篇文章的结论,但是这值得强调。

在 ES6 之前,众所周知使用 typeof 总是能安全地找出某个变量的定义,不管它是否被声明:

if (typeof Symbol !== "undefined") {

// Symbol 可用

}

// 下面的代码抛异常,如果 Symbol 没有被声明

if (Symbol !== "undefined") {

}

但是,现在这个在不使用 let 或者 const 声明变量的时候才好使。因为有了 TDZ,会导致变量未声明时产生引用错误。从本质上讲,变量被提升到块级作用域的开始,但是在声明前的任何访问都会产生引用错误。在 JSHint 的作用域管理中,我必须记录一个变量的用法,如果它使用 let 或者 const 声明于当前块级作用域或者它的父级作用域,提前访问就会有引用错误。而如果是使用 var 语句声明的,那么它就是可用的,但是 JSHint 会给出一个警告,而如果它没有被声明,那么它使用全局作用域,JSHint 可能会有另外一种警告。

if (typeof Symbol !== "undefined") {

// Symbol 不可用,产生 reference error

}

let Symbol = true;

新数组

我总是避免使用 new Array 构造函数,一部分原因是因为它的参数既可以是一个长度又可以是一个元素列表:

new Array(1); // [undefined]

new Array(1, 2); // [1, 2]

但是,一个同事最近使用它遇到了一些我以前没有见过的东西:

var arr = new Array(10);

for(var i = 0; i < arr.length; i++) {

arr[i] = i;

}

console.dir(arr);

上面的代码产生一个 0 到 9 的数组。然而,如果将它重构为使用 map:

var arr = new Array(10);

arr = arr.map(function(item, index) { return index; });

console.dir(arr);

上面的代码运行后 arr 没有变化。似乎 new Array(length) 用指定长度创建了一个数组,但是没有设置任何值,所以引用它的长度可以工作,但是枚举元素不可以。如果我设置一个数值会怎么样?

var arr = new Array(10);

arr[8] = undefined;

arr = arr.map(function(item, index) { return index; });

console.dir(arr);

现在我得到了一个数组,第 8 个元素等于 8,但是其他所有的值依然是 undefined。看一下 map 的 polyfill 实现,它循环每一个元素(这是为什么 index 是正确的),但是它使用的是 in 来检查一个属性是否被设置。你如果使用数组直接量,也会得到同样的结果。

var arr = [];

arr[9] = undefined;

// or

var arr = [];

arr.length = 10;

其他

Mozilla 的开发者博客有一篇很棒的文章关于箭头函数,其中包含使用 <!-- 作为一种官方的 ES6 注释的细节。一整个系列的博客文章也都值得仔细阅读。

版权声明

本译文仅用于学习、研究和交流目的,欢迎非商业转载。转载请注明出处、译者和众成翻译的完整链接。要获取包含以上信息的本文Markdown源文本,请点击这里

时间: 2024-11-06 19:26:07

7 个 JavaScript “特性”的相关文章

javascript 特性

作用域: javascript的作用域称为静态作用域,在定义语法上就能确认了,而不是运行时. if (true) { var i = 'moersing' } console.log(i); //可以访问. 与C#,vb,java等语言不同,javascript没有所谓块级作用域概念,准确的说,就花括号之内定义的变量可以被外面访问到,但是,函数除外,因为函数本身是一个作用域. 另一个例子: var s= 'moersing'; var f= function () { console.log(s

深入理解JavaScript特性

最近在读<JavaScript高级程序设计>这一本书,里面提到了JavaScript的特征,倍感兴趣,于是结合自己的认识,在这里进行一下总结. 1.JavaScript的垃圾回收机制 javaScript中的5种数据类型存放在栈中(Undefined.Null.Boolean.Number.String),非基本数据类型存放在堆中,占用内存,堆不会被程序自动释放. 一张图看懂JS中数据类型的存放位置:基本数据类型存放在栈中,非基本数据类型存放在堆中(对象),变量obi1在栈中存储的只是堆中对象

JavaScript权威指南第13章 web浏览器中的javascript

13.1 客户端javascript window对象是所有的客户端javascript特性和api的主要接入点.表示浏览器的一个窗口,可以通过window对象来引用它. window 的方法 alert() prompt() confirm() 13.2 在html里嵌入javascript 4种方法: 内联:放置在<script></script>标签之中 外部引入:<script src="   "></script> html程序

JavaScript资源大全

目录 前端MVC 框架和库 包管理器 加载器 打包工具 测试框架 框架 断言 覆盖率 运行器 QA 工具 基于 Node 的 CMS 框架 模板引擎 数据可视化 编辑器 UI 输入 日历 选择 文件上传 其它 提示 模态框和弹出框 滚动 菜单 表格/栅格 框架 手势 地图 视频/音频 动画 图片处理 ECMAScript 6 软件开发工具包(SDK) 利器 前端MVC 框架和库 angular.js:为网络应用增强 HTML.官网 aurelia:一个适用于移动设备.桌面电脑和 web 的客户端

JavaScript BOM

JavaScript特性: 交互性 安全性(不可以直接访问本地硬盘) 跨平台性(只要是可以解析js的浏览器都可以执行,和平台无关) JavaScript三大核心: 1)核心(ECMAScript):描述了JS的语法和基本对象. 2)文档对象模型 ☆(DOM):处理网页内容的方法和接口 3)浏览器对象模型(BOM):与浏览器交互的方法和接口 BOM对象: 1. DOM 是 W3C 的标准: [所有浏览器公共遵守的标准] 2. BOM 是 各个浏览器厂商根据 DOM 在各自浏览器上的实现;[表现为不

JavaScript权威设计--Window对象(简要学习笔记十三)

1.Window对象是所有客户端JavaScript特性和API的主要接入点. Window对象中的一个重要属性是document,它引用Document对象. JavaScript程序可以通过Document对象和它包含的Element对象遍历和管理文档. 2.URL中的JavaScript 在URL后面跟一个JavaScript:协议限定符.里面的代码会作为JavaScript代码进行运行,需用分号分割. 如: <a href="javascript:alert('OK!')"

悟透JavaScript

From: 李战,http://www.cnblogs.com/leadzen/archive/2008/02/25/1073404.html 引子 编程世界里只存在两种基本元素,一个是数据,一个是代码.编程世界就是在数据和代码千丝万缕的纠缠中呈现出无限的生机和活力. 数据天生就是文静的,总想保持自己固有的本色:而代码却天生活泼,总想改变这个世界.    你看,数据代码间的关系与物质能量间的关系有着惊人的相似.数据也是有惯性的,如果没有代码来施加外力,她总保持自己原来的状态.而代码就象能量,他存

第十章:Javascript子集和扩展

本章讨论javascript的集和超集,其中子集的定义大部分处于安全考虑.只有使用这门语言的一个安全的子集编写脚本,才能让代码执行的更安全.更稳定.ECMScript3标准是1999年版本的,10年后,ECMAScript5规范的更新,由于ECMAScript标准规范是允许对其做任何扩充的,伴随着Mozilla项目的改进,Firefox1.0.1.5.2.3.和3.5版本中分别发布了javascript1.5.1.6.1.7.1.8.1.81版本,这些javascript的扩展版本已经融入到EC

JavaScript 资源大全中文版

包管理器 管理着 javascript 库,并提供读取和打包它们的工具. npm:npm 是 javascript 的包管理器.官网 Bower:一个 web 应用的包管理器.官网 component:能构建更好 web 应用的客户端包管理器.官网 spm:全新的静态包管理器.官网 jam:一个专注于浏览器端和兼容 RequireJS 的包管理器.官网 jspm:流畅的浏览器包管理器.官网 Ender:没有库文件的程序库.官网 volo:以项目模板.添加依赖项与自动化生成的方式创建前端项目.官网