一、let、const与 block 作用域
block块级作用域
ES6引入了块级作用域,块内部定义的变量,在外部是不可以访问的.
例子1
if(true){ // var flag = 123; let flag = 123; } // console.log(flag); //当使用var声明时可以打印, // 但使用let声明就不能打印了,{}相当于一个块级作用域
直接使用{}也可以形成一个块级作用域
{ // 这里是块级作用域 let flag = 111; console.log(flag); } console.log(flag);//这里会报错,不能获取到块级作用域里声明的变量
在块级作用域内部,变量也是只能先声明再使用
if(true){ console.log(flag); let flag = 123; }
let
ES6新增了let命令,用来生命变量。它的用法类似于var,但是所声明的变量,只在let
命令所在的代码块内有效。
for (let i = 0; i < 10; i++) { } console.log(i) //ReferenceError: i is not defined<br> for(var i=0;i<10;i++){ } console.log(i) //10
上面代码中,计数器i
只在for
循环体内有效,在循环体外引用就会报错。let具有块级作用域的。var不存在块级作用域问题,具有全局变量提示的问题存在.
let声明的变量不允许重复(在同一个作用域内)
let flag = 123; let flag = 456; console.log(flag);//会报错
let声明的变量不存在预解析
console.log(flag); var flag = 123; let flag = 123; //不再预解析,直接报错
for循环中的块级作用域使用
for (var i = 0; i < 3; i++) { console.log(i); } console.log(i); //这里不会有问题,i变成了3,可以打印,var的作用域是全局的,所以不会报错
而使用let
for (let i = 0; i < 3; i++) { // for循环括号中声明的变量只能在循环体中使用 console.log(i); } console.log(i);//会报错,无法获取i
const
const声明一个只读的常量,一旦声明,常量的值就不能改变。
const a = 2; a = 20; console.log(a) //TypeError: Assignment to constant variable. 上面代码表明改变常量的值会报错。
const
声明的变量不得改变值,这意味着,const
一旦声明变量,就必须立即初始化,不能留到以后赋值。
const a; console.log(a)
上面代码表示,对于const
来说,只声明不赋值,就会报错。
let与const
1.都不能重复声明
2.都存在块级作用域问题
3.只在声明所在的块级作用域内有效。
二、模板字符串
模板字符串就是一种字符串的新的表现形式
(1)基本用法
let s1 = ` hello ` let s2 = ' hello '
(2)字符串和变量拼接
let s3 =" a " + s1 + " b " + s2; let s4 = ` a ${s1} b ${s2}`;
(3)字符串换行
var box =`<div> <p> <span>123</span> </p> <p>${a1}</p> </div>`;
模板字符串的出现,极大的改变传统字符串的拼接方法,减少代码出现错误的几率。提高开发效率
三、箭头函数(Arrow Functions)
ES6 中,箭头函数就是函数的一种简写形式,使用括号包裹参数,跟随一个 =>,紧接着是函数体:
基本用法
//匿名函数 div.onclick=function(){ console.log("你好") } //箭头函数 div.onclick=()=>{ console.log("你好") }
有一个参数的箭头函数
var fn=(a)=>{ console.log("abc"); } //等价于: var fn=a=>{ console.log("abc"); }
有2个及更多参数的箭头函数
var f=(a,b,c)=>{ console.log("abc") }
即
//无形参 var f = () => 5; // 等同于 var f = function () { return 5 }; //多个形参 var sum = (num1, num2) => num1 + num2; // 等同于 var sum = function(num1, num2) { return num1 + num2; };
箭头函数有几个使用注意点。
1、函数体内的this
对象,就是使用时所在的对象。
// ES3、ES5中普通函数 var name = '张三'; var person = { name:'nicholas', age:18, fav:function(){ console.log(this); console.log(this.name) } }; person.fav();
打印结果为
此时this指向的是使用它的对象,也就是person对象,即调用它的对象。
而使用箭头函数
//使用箭头函数 var person = { name: 'nicholas', age: 18, fav: () => { // 当前this对象(window) console.log(this); console.log('this'); } }; person.fav();
打印结果
此时因为箭头函数是指向全局的,即window对象
箭头函数的this永远指向其父作用域,任何方法都改变不了,包括call,apply,bind。 普通函数的this指向调用它的那个对象。
Javascript定时器中的this指向
var name = 'my name is window'; var obj = { name: 'my name is obj', fn: function () { var timer = null; clearInterval(timer); timer = setInterval(function () { console.log(this.name); //my name is window }, 1000) } }
在这里,从this.name可以看出this的指向是window。
如果没有特殊指向,setInterval和setTimeout的回调函数中this的指向都是window。这是因为JS的定时器方法是定义在window下的。但是平时很多场景下,都需要修改this的指向。这里总结了几种:
(1)最常用的方法:在外部函数中将this存为一个变量,回调函数中使用该变量,而不是直接使用this。
var name = 'my name is window'; var obj = { name: 'my name is obj', fn: function () { var that = this; var timer = null; clearInterval(timer); timer = setInterval(function () { console.log(that.name); //my name is obj }, 1000) } }
在fn中加了var that = this; 回调函数中使用that代替this即可。这种方法最常见,使用也最广泛。
(2)使用es6的箭头函数:箭头函数的最大作用就是this指向。
var name = 'my name is window'; var obj = { name: 'my name is obj', fn: function () { var timer = null; clearInterval(timer); timer = setInterval(() => { console.log(this.name); //my name is obj }, 1000) } }
箭头函数没有自己的this,它的this继承自外部函数的作用域。所以,在该例中,定时器回调函数中的this,是继承了fn的this。当然箭头函数也有兼容问题,要是兼容低版本ie,需要使用babel编译,并且引入es5-shim.js才可以。
2、不可以使用arguments
对象,该对象在函数体内不存在。
let foo = (a,b) => { console.log(a,b); // console.log(arguments);//这种方式获取不到实参列表 }; foo(123,456);
四、对象的简洁表示法
属性简写
ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。
const foo = 'bar'; const baz = {foo}; baz // {foo: "bar"} // 等同于 const baz = {foo: foo};
上面代码中,变量foo
直接写在大括号里面。这时,属性名就是变量名, 属性值就是变量值。下面是另一个例子。
function f(x, y) { return {x, y}; } // 等同于 function f(x, y) { return {x: x, y: y}; } f(1, 2) // Object {x: 1, y: 2}
方法简写
const o = { method() { return "Hello!"; } }; // 等同于 const o = { method: function() { return "Hello!"; } };
例子2
let birth = '2000/01/01'; const Person = { name: '张三', //等同于birth: birth birth, // 等同于hello: function ()... hello() { console.log('我的名字是', this.name); } };
例子3
function getPoint() { const x = 1; const y = 10; return {x, y}; } getPoint() // {x:1, y:10}
五、es6中的解构赋值
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
变量的解构赋值
以前,为变量赋值,只能直接指定值。
let a = 1; let b = 2; let c = 3;
ES6 允许写成下面这样。
let [a, b, c] = [1, 2, 3];
上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。
例子
let [a,b,c] = [,123,]; //与前面的数组是一一对应的关系,没有值就是undefined console.log(a,b,c) //输出:undefined 123 undefined let [a=111,b,c] = [,123,];//可以在前面设置默认值 console.log(a,b,c); //输出:111 123 undefined
对象的解构赋值
解构不仅可以用于数组,还可以用于对象。
let {foo,bar} = {foo : 'hello',bar : 'hi'}; let {foo1,bar1} = {bar1 : 'hi',foo1 : 'hello'};//改变key的顺序无影响 console.log(foo,bar);//打印的value console.log(foo1,bar1);//打印的value
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
变量没有对应的同名属性,导致取不到值,最后等于undefined
。
let {foo} = {bar: 'baz'}; foo // undefined
变量没有对应的同名属性,导致取不到值,最后等于undefined
。
对象的解构赋值是下面形式的简写
let { foo: foo, bar: bar } = { foo: 'aaa', bar: 'bbb' };
也就是说,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。
对象属性别名(如果有了别名,那么原来的名字就无效了)
let {foo:abc,bar} = {bar : 'hi',foo : 'nihao'}; //给foo这个key设置别名,可以用别名,但原来的key就不能用了 console.log(abc,bar); console.log(foo,bar);//会报错 如果解构失败,变量的值等于undefined。
对象的解构赋值指定默认值
let {foo:abc='hello',bar} = {bar : 'hi'}; console.log(abc,bar);
六、Class 的基本语法
JavaScript 语言中,生成实例对象的传统方法是通过构造函数。下面是一个例子。
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);
上面这种写法跟传统的面向对象语言(比如 C++ 和 Java)差异很大,很容易让新学习这门语言的程序员感到困惑。
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class
关键字,可以定义类。
基本上,ES6 的class
可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class
写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用 ES6 的class
改写,就是下面这样
class Animal{ // 构造器 当你创建实例之后 constructor()方法会立刻调用 通常这个方法初始化对象的属性 constructor(name,age){ this.name = name; this.age = age; } showName(){ console.log(this.name); } } var a2 = new Animal('点点',3);
上面代码定义了一个“类”,可以看到里面有一个constructor
方法,这就是构造方法,而this
关键字则代表实例对象。也就是说,ES5 的构造函数Animal,对应 ES6 的Animal类的构造方法。
Animal类除了构造方法,还定义了一个showName方法。注意,定义“类”的方法的时候,前面不需要加上function
这个关键字,直接把函数定义放进去了就可以了。另外,方法之间不需要逗号分隔,加了会报错。