ES6所改良的javascript“缺陷”问题

块级作用域

ES5没有块级作用域,只有全局作用域和函数作用域,由于这一点,变量的作用域甚广,所以一进入函数就要马上将它创建出来。这就造成了所谓的变量提升。

ES5的“变量提升”这一特性往往一不小心就会造成一下错误:

1.内层变量覆盖外层变量

var tmp = new Date();
function f() {
console.log(tmp);
if (false) { //执行则undefined
var tmp = "hello world";
}
}

2.变量泄露,成为全局变量

var s = 'hello';
for (var i = 0; i < s.length; i++) {
console.log(s[i]);
}
console.log(i); // 5

往常我们往往是使用闭包来解决这一问题的(比如自执行函数)。现在,基于这一问题,ES6增加了块级作用域,所以不再需要自执行函数了。

let 和 const

ES6是是向后兼容的,而保持向后兼容性意味着永不改变JS代码在Web平台上的行为,所以var创建的变量其作用域依旧将会是全局作用域和函数作用域。这样以来,即使拥有了块级作用域,也无法解决ES5的“变量提升”问题。所以,这里ES6新增了俩个新关键词:let和const。

1.let

“let是更完美的var”,它有着更好的作用域规则。

2.const

const声明一个只读的常量。一旦声明,常量的值就不能改变,但const声明的对象可以有属性变化(对象冻结Object.freeze)

const a = [];
a.push('Hello'); // 可执行
a = ['Dave']; // 报错

也可以使用Object.freeze将对象冻结

const foo = Object.freeze({});
// 常规模式时,下面一行不起作用;
// 严格模式时,该行会报错
foo.prop = 123;//

使用let和const:

•变量只在声明所在的块级作用域内有效

•变量声明后方可使用(暂时性死区)

•不能重复定义变量

•声明的全局变量,不属于全局对象的属性

var a = 1;
window.a // 1
let b = 1;
window.b // undefined

this关键字

我们知道,ES5函数中的this指向的是运行时所在的作用域。比如

function foo() {
setTimeout(function(){
console.log('id:', this.id);
}, 100);
}
var id = 21;
foo.call({id:42});//id: 21

在这里,我声明了一个函数foo,其内部为一个延迟函数setTimeout,每隔100ms打印一个this.id。我们通过foo.call({id:42})来调用它,并且为这个函数设定作用域。它真正执行要等到100毫秒后,由于this指向的是运行时所在的作用域,所以这里的this就指向了全局对象window,而不是函数foo。这里:

•使用call来改变foo的执行上下文,使函数的执行上下文不再是window,从而来辨别setTimeout中的this指向

•setTimeout方法挂在window对象下,所以其this指向执行时所在的作用域——window对象。

超时调用的代码都是在全局作用域中执行的,因此函数中this 的值在非严格模式下指向window 对象,在严格模式下是undefined --《javascript高级程序设计》

为了解决这一问题,我们往常的做法往往是将this赋值给其他变量:

function foo() {var that = this;
setTimeout(function(){
console.log('id:', that.id);
}, 100);
}
var id = 21;
foo.call({id:42});//id: 42

而现在ES6推出了箭头函数解决了这一问题。

箭头函数

标识符=> 表达式

var sum = (num1, num2) => { return num1 + num2; }
// 等同于
var sum = function(num1, num2) {
return num1 + num2;
};

•如果函数只有一个参数,则可以省略圆括号

•如果函数只有一条返回语句,则可以省略大括号和return

•如果函数直接返回一个对象,必须在对象外面加上括号。(因为一个空对象{}和一个空的块 {} 看起来完全一样。所以需要用小括号包裹对象字面量。)

针对this关键字的问题,ES6规定箭头函数中的this绑定定义时所在的作用域,而不是指向运行时所在的作用域。这一以来,this指向固定化了,从而有利于封装回调函数。

function foo() {var that = this;
setTimeout(()=>{
console.log('id:', that.id);
}, 100);
}
var id = 21;
foo.call({id:42});//id: 42

注意:箭头函数this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this。而箭头函数根本没有自己的this,其内部的this也就是外层代码块的this。这就导致了其:

•不能用作构造函数

•不能用call()、apply()、bind()这些方法去改变this的指向

类与继承

传统ECMAScript没类的概念,它描述了原型链的概念,并将原型链作为实现继承的主要方法。其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。而实现这一行为的传统方法便是通过构造函数:

function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';
};
var p = new Point(1, 2);

在这里,构造函数Point会有一个原型对象(prototype),这个原型对象包含一个指向Point的指针(constructor),而实例p包含一个指向原型对象的内部指针(prop)。所以整个的继承是通过原型链来实现的。详情可见我的这篇文章:javascript中的prototype和constructor

class

ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。但是类只是基于原型的面向对象模式的语法糖。对于class的引入,褒贬不一,很多人认为它反而是一大缺陷,但对我来说,这是一个好的语法糖,因为往常的原型链继承的方式往往能把我绕那么一会儿。

//定义类
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
var p = new Point(1, 2);

•类里面有一个constructor方法,它是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。

•constructor方法中的this关键字代表实例对象,

•定义“类”的方法(如上例的toString)的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。另外,方法之间不需要逗号分隔,加了会报错。

•使用的时候,也是直接对类使用new命令,跟构造函数的用法完全一致

•类的所有方法都定义在类的prototype属性上面

class的继承——extend

Class之间可以通过extends关键字实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多。

class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 调用父类的constructor(x, y)
this.color = color;
}
toString() {
return this.color + ' ' + super.toString(); // 调用父类的toString()
}
}

•super关键字,作为函数调用时(即super(...args)),它代表父类的构造函数;作为对象调用时(即super.prop或super.method()),它代表父类。在这里,它表示父类的构造函数,用来新建父类的this对象。

•子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。

模块化

历史上,JavaScript一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来,这对开发大型的、复杂的项目形成了巨大障碍。为了适应大型模块的开发,社区制定了一些模块加载方案,比如CMD和AMD。

ES6的模块化写法:

import { stat, exists, readFile } from 'fs';

上面代码的实质是从fs模块加载3个方法,其他方法不加载。这种加载称为“编译时加载”,即ES6可以在编译时就完成模块加载,效率要比CommonJS模块的加载方式高。当然,这也导致了没法引用ES6模块本身,因为它不是对象。

模块功能主要由两个命令构成:

•export

用于规定模块的对外接口,对外的接口,必须与模块内部的变量建立一一对应关系。

// 写法一
export var m = 1;
//错误
export 1;
// 写法二
var m = 1;
export {m};
//错误
export m;
// 写法三 重命名
var n = 1;
export {n as m};

•import

用于输入其他模块提供的功能,它接受一个对象(用大括号表示),里面指定要从其他模块导入的变量名(也可以使用*号整体加载)

字符串插值

在javascript的开发中,我们常常需要这样来输出模板:

function sayHello(name){
return "hello,my name is "+name+" I am "+getAge(18);
}
function getAge(age){
return age;
}
sayHello("brand") //"hello,my name is brand I am 18"

我们需要使用+来连接字符串和变量(或者表达式)。例子比较简单,所以看上去无伤大雅,但是一旦在比较复杂的情况下,就会显得相当繁琐不方便,这一用法也让我们不厌其烦。对此,ES6引入了模板字符串,可以方便优雅地将 JS 的值插入到字符串中。

模板字符串

对于模板字符串,它:

•使用反引号``包裹;

•使用${}来输出值;

•${}里的内容可以是任何 JavaScript 表达式,所以函数调用和算数运算等都是合法的;

•如果一个值不是字符串,它将被转换为字符串;

•保留所有的空格、换行和缩进,并输出到结果字符串中(可以书写多行字符串)

•内部使用反引号和大括号需要转义,转义使用反斜杠\

对于上面的例子,模板字符串的写法是:

function sayHello(name){
return `hello,my name is ${name} I am ${getAge(18)}`;
}
function getAge(age){
return age;
}
sayHello("brand") //"hello,my name is brandI am 18"

严格模式

严格模式的目标之一是允许更快地调试错误。帮助开发者调试的最佳途径是当确定的问题发生时抛出相应的错误(throw errors when certain patterns occur),而不是悄无声息地失败或者表现出奇怪的行为(非严格模式下经常发生)。严格模式下的代码会抛出更多的错误信息,能帮助开发者很快注意到一些必须立即解决的问题。在 ES5 中, 严格模式是可选项,但是在 ES6 中,许多特性要求必须使用严格模式,这个习惯有助于我们书写更好的 JavaScript。

以上所述是小编给大家介绍的ES6所改良的javascript“缺陷”问题,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • 简单谈谈ES6的六个小特性

    前言 本文主要针对ES6做一个简要介绍,也许你还不知道ES6是什么, 实际上, 它是一种新的JavaScript规范.在这个大家都很忙碌的时代,如果你想对ES6有一个快速的了解,那么请继续往下读,去了解当今最流行的编程语言JavaScript最新一代的六大特性. 过去一年ES6带来了十足的进步,下面是6个我最喜欢的JS新增特性. 一.Object[key] 有时候不能在对象变量声明时设置所有的key/value,所以得再声明之后添加key/value. let myKey = 'key3'; l

  • Es6 写的文件import 起来解决方案详解

    这段时间,学习了一点关于es6新规范的知识,然后心血来潮,想尝试一下用ES6编写的代码在浏览器中跑起来. 说干就干,先说下我的实现步骤(没想到有坑!) 把ES6代码转译成ES5; html文件引入转译后的ES5; 然后在浏览器环境中运行; 在node环境中运行; 然后下面是我的一些目录结构,大致预览一下. src,es6开发目录 dist,es5生产目录 test,一个测试目录 然后,看一下我的ES6开发的一些js是什么样子. file        file2      app   test

  • 详解JavaScript ES6中的模板字符串

    在 ES6 中引入了一种新的字符串字面量 - 模板字符串,除了使用反引号 (`) 表示,它们看上去和普通的字符串没有什么区别.在最简单的情况下,他们就是普通的字符串: context.fillText(`Ceci n'est pas une cha?ne.`, x, y); context.fillText(`Ceci n'est pas une cha?ne.`, x, y); 之所以被称为模板字符串,是因为模板字符串为 JS 引入了简单的字符串插值特性,也就是说,可以方便优雅地将 JS 的值

  • 深入浅析react native es6语法

    react native是直接使用es6来编写代码,许多新语法能提高我们的工作效率 解构赋值 var { StyleSheet,Text,View } = React; 这句代码是ES6 中新增的解构(Destructuring)赋值语句.准许你获取对象的多个属性并且使用一条语句将它们赋给多个变量. 上面的代码等价于: var StyleSheet = React.StyleSheet; var Text = React.Text; var View = React.View 再看几个例子,以前

  • 解析JavaScript的ES6版本中的解构赋值

    什么是解构赋值? 解构赋值允许你使用类似数组或对象字面量的语法将数组和对象的属性值赋给一系列变量.这个语法非常简洁,而且比传统的属性访问更加清晰. 在不使用解构赋值的情况下,访问数组的前三项: var first = someArray[0]; var second = someArray[1]; var third = someArray[2]; var first = someArray[0]; var second = someArray[1]; var third = someArray

  • 详解Javascript ES6中的箭头函数(Arrow Functions)

    ES6可以使用"箭头"(=>)定义函数,注意是函数,不要使用这种方式定义类(构造器). 一.语法 1. 具有一个参数的简单函数 var single = a => a single('hello, world') // 'hello, world' 2. 没有参数的需要用在箭头前加上小括号 var log = () => { alert('no param') } 3. 多个参数需要用到小括号,参数间逗号间隔,例如两个数字相加 var add = (a, b) =&g

  • ES6中如何使用Set和WeakSet

    ES6中提供了两新数据结构-Set和WeakSet.Set是类似于数组,但是成员变量的值都是唯一的,没有重复的值.WeakSet也是不重复的值的集合,但是只能用来存放对象. 一.Set使用 (1)Set本身提供了一个构造函数,用来生成Set数据结构. var s = new Set(); [2,2,2,5,8,16,2,1].map(x => s.add(x)) for(i of s){console.log(i)} //2,5,8,16,1 (2)Set()函数可以接受一个数组,作为构造参数,

  • JavaScript中ES6 Babel正确安装过程

    本文介绍Babel6.x的安装过程~首先呢,可以使用Babel在线转换 https://babeljs.io/repl/ 然后进入主题:安装Babel(命令行环境,针对Babel6.x版本) 1.首先安装babel-cli(用于在终端使用babel) npm install -g babel-cli 2.然后安装babel-preset-es2015插件 npm install --save babel-preset-es2015 注:Babel5版本默认包含各种转换插件,然而Babel6.x相

  • ES6生成器用法实例分析

    本文实例讲述了ES6生成器用法.分享给大家供大家参考,具体如下: 语法 何为生成器?让我们先看看以下代码: function* quips(name) { yield "hello " + name + "!"; yield "i hope you are enjoying the blog posts"; if (name.startsWith("X")) { yield "it's cool how your n

  • 深入理解React中es6创建组件this的方法

    首发于:https://mingjiezhang.github.io/. 在JavaScript中,this对象是运行时基于函数的执行环境(也就是上下文)绑定的. 从react中的demo说起 Facebook最近一次更新react时,将es6中的class加入了组件的创建方式当中.Facebook也推荐组件创建使用通过定义一个继承自 React.Component 的class来定义一个组件类.官方的demo: class LikeButton extends React.Component

  • JavaScript ES6的新特性使用新方法定义Class

    ES6(ECMAScript 6)是即将到来的新版本JavaScript语言的标准,代号harmony(和谐之意,显然没有跟上我国的步伐,我们已经进入中国梦版本了).上一次标准的制订还是2009年出台的ES5.目前ES6的标准化工作正在进行中,预计会在14年12月份放出正式敲定的版本.但大部分标准已经就绪,且各浏览器对ES6的支持也正在实现中. ES6中定义类的方式, 就是ES3和ES5中定义类的语法糖,虽然也有些区别,但是整体定义类的方式更加简洁,类的继承更加方便, 如果想对ES6中的继承更加

  • JavaScript ES6中CLASS的使用详解

    前言 对于javascript来说,类是一种可选(而不是必须)的设计模式,而且在JavaScript这样的[[Prototype]] 语言中实现类是很蹩脚的. 这种蹩脚的感觉不只是来源于语法,虽然语法是很重要的原因.js里面有许多语法的缺点:繁琐杂乱的.prototype 引用.试图调用原型链上层同名函数时的显式伪多态以及不可靠.不美观而且容易被误解成"构造函数"的.constructor. 除此之外,类设计其实还存在更进一步的问题.传统面向类的语言中父类和子类.子类和实例之间其实是复

  • JavaScript学习笔记之ES6数组方法

    ES6(ECMAScript 6)是即将到来的新版本JavaScript语言的标准,代号harmony(和谐之意,显然没有跟上我国的步伐,我们已经进入中国梦版本了).上一次标准的制订还是2009年出台的ES5.目前ES6的标准化工作正在进行中,预计会在14年12月份放出正式敲定的版本.但大部分标准已经就绪,且各浏览器对ES6的支持也正在实现中. ES6给数组添加了一些新特性,而这些新特性到目前为止完全可以运用到自己的业务层.在这一节中将总结有关于ES6给数组提供一些新特性的使用方法. ES6提供

随机推荐