当前位置: 首页 > news >正文

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 特性同样适用于 breakcontinue。你在 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"]);

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

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源文本,请点击这里

转载于:https://www.cnblogs.com/Jasonellen/p/5769071.html

相关文章:

  • linux 下 ant 安装配置
  • 后台——使用maven时出现Failure to transfer 错误的解决方法
  • 吾爱论坛浏览器分享
  • java 极光推送
  • Plsql连接不上64位oracle数据库问题解决方案
  • 【面试系列】之二:关于js原型
  • 基于nexus的maven私服配置
  • 设计模式之Adapter模式
  • 关于KMP算法理解(快速字符串匹配)
  • uva 10370 Above Average
  • linux下部署tomcat指定JDK版本编译并运行javaWEB应用
  • 个人视频发布汇总——教育改变人生
  • springmvc项目提交post表单参数乱码解决办法
  • MongoDB常用操作命令大全
  • I.MX6 android 4.2 源码下载
  • 《网管员必读——网络组建》(第2版)电子课件下载
  • 【159天】尚学堂高琪Java300集视频精华笔记(128)
  • 【腾讯Bugly干货分享】从0到1打造直播 App
  • 【许晓笛】 EOS 智能合约案例解析(3)
  • 78. Subsets
  • LeetCode29.两数相除 JavaScript
  • UMLCHINA 首席专家潘加宇鼎力推荐
  • vue从创建到完整的饿了么(11)组件的使用(svg图标及watch的简单使用)
  • Yii源码解读-服务定位器(Service Locator)
  • 欢迎参加第二届中国游戏开发者大会
  • 基于axios的vue插件,让http请求更简单
  • 开源地图数据可视化库——mapnik
  • 开源中国专访:Chameleon原理首发,其它跨多端统一框架都是假的?
  • 名企6年Java程序员的工作总结,写给在迷茫中的你!
  • 如何解决微信端直接跳WAP端
  • 山寨一个 Promise
  • 使用common-codec进行md5加密
  • 微信公众号开发小记——5.python微信红包
  • 详解移动APP与web APP的区别
  • 学习ES6 变量的解构赋值
  • - 语言经验 - 《c++的高性能内存管理库tcmalloc和jemalloc》
  • 阿里云服务器购买完整流程
  • 蚂蚁金服CTO程立:真正的技术革命才刚刚开始
  • ​configparser --- 配置文件解析器​
  • #14vue3生成表单并跳转到外部地址的方式
  • #WEB前端(HTML属性)
  • (C语言)逆序输出字符串
  • (NO.00004)iOS实现打砖块游戏(十二):伸缩自如,我是如意金箍棒(上)!
  • (附源码)springboot 校园学生兼职系统 毕业设计 742122
  • (一)u-boot-nand.bin的下载
  • (转)清华学霸演讲稿:永远不要说你已经尽力了
  • .L0CK3D来袭:如何保护您的数据免受致命攻击
  • .net反混淆脱壳工具de4dot的使用
  • /var/log/cvslog 太大
  • @Async注解的坑,小心
  • @Data注解的作用
  • [ solr入门 ] - 利用solrJ进行检索
  • [AIR] NativeExtension在IOS下的开发实例 --- IOS项目的创建 (一)
  • [C#]winform部署yolov5-onnx模型
  • [CSS]中子元素在父元素中居中