ES6 基本语法1(变量、解构赋值、字符串、函数)

一、变量的声明:
1.let(限制在当前作用域使用)
ES6 新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效

例1:下面的代码如果使用var,最后输出的是10

//es5
var a=[];
for(var i=0;i<10;i++){
   a[i]=function(){
        console.log(i);  //10
    }
}
a[6]();

说明变量i是var命令声明的,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i。也就是说,所有数组a的成员里面的i,指向的都是同一个i,导致运行时输出的是最后一轮的i的值,也就是 10。

例2:若使用let,声明的变量仅在块级作用域内有效,最后输出的是 6

//es6
let a=[];
for(let i=0;i<10;i++){
     a[i]=function(){
         console.log(i);  //6
     }
}
a[6]();

说明变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。
Q:如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?
A:因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。

例3:for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。

for(let i=0;i<3;i++){
     let i='abc';
     console.log(i);
}
//abc
//abc
//abc

说明:输出了 3 次abc。这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域。

2.const(引入了常量,常量即只读、不可修改的值)

var a=10;
console.log(a);  //10
a=20;
console.log(a);  //20

const PI=3.14;
console.log(PI);  //3.14
PI=3.15;
console.log(PI);  //Uncaught TypeError: Assignment to constant variable.

补充:
代码块:相当于函数,自动执行,将同类的代码利用代码块封装

{

}

二、变量的解构赋值
1.数组的解构赋值
本质:“模式匹配”,即只要等号两边的模式相同,左边的变量就会被赋予对应的值

  • 基本用法

(1)es6中的写法

let [a, b, c] = [1, 2, 3];

说明:可以从数组中提取值,按照对应位置,对变量赋值。

(2)使用嵌套数组进行解构

let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3

let [ , , third] = ["foo", "bar", "baz"];
third // "baz"

let [x, , y] = [1, 2, 3];
x // 1
y // 3

let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]

let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []

(3)解构不成功,变量的值就等于undefined

let [foo] = [];
let [bar, foo] = [1];

说明:以上两种情况都属于解构不成功,foo的值都会等于undefined。

(4)不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功

let [x, y] = [1, 2, 3];
x // 1
y // 2

let [a, [b], d] = [1, [2, 3], 4];
a // 1
b // 2
d // 4

说明:上面两个例子,都属于不完全解构,但是可以成功。

(5)若等号右边不是数组,则将会报错

// 报错
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};
  • 默认值

(6)解构赋值允许指定默认值

let [foo = true] = [];
foo // true
let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'

:ES6 内部使用严格相等运算符(===),判断一个位置是否有值

(7)只有当一个数组成员严格等于undefined,默认值才会生效

let [x = 1] = [undefined];
x // 1
let [x = 1] = [null];
x // null

说明:若一个数组成员是null,默认值就不会生效,因为null不严格等于undefined。

(8)默认值可以引用解构赋值的其他变量,但该变量必须已经声明

let [x = 1, y = x] = [];     // x=1; y=1
let [x = 1, y = x] = [2];    // x=2; y=2
let [x = 1, y = x] = [1, 2]; // x=1; y=2
let [x = y, y = 1] = [];     // ReferenceError: y is not defined

说明:最后一个表达式之所以会报错,是因为x用y做默认值时,y还没有声明。

三、字符串的扩展
es5:给字符串绑值 直接是拼接(++)
es6:使用模板字符串来动态绑值 ( ``)

let a=10;
let str=`我的年龄:${a}岁!`;
console.log(str);

四、函数的扩展
1.函数的默认值
es5

function log(x, y) {
  y = y || 'World';
  console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello World

说明:检查函数log的参数y有没有赋值,如果没有,则指定默认值为World。这种写法的缺点是若参数y赋值了,但对应的布尔值为false,则该赋值不起作用。就像上面代码的最后一行,参数y等于空字符,结果被改为默认值。

为了避免这个问题,通常需要先判断一下参数y是否被赋值,如果没有,再等于默认值

if (typeof y === 'undefined') {
  y = 'World';
}

es6

function log(x, y = 'World') {
  console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello

说明:允许为函数的参数设置默认值,即直接写在参数定义的后面。


(1)参数变量是默认声明的,所以不能用let或const再次声明

function foo(x = 5) {
  let x = 1; // error
  const x = 2; // error
}

说明:参数变量x是默认声明的,在函数体中,不能用let或const再次声明,否则会报错。
(2)使用参数默认值时,函数不能有同名参数

// 不报错
function foo(x, x, y) {
  // ...
}
// 报错
function foo(x, x, y = 1) {
  // ...
}
// SyntaxError: Duplicate parameter name not allowed in this context

(3)参数默认值不是传值的,而是每次都重新计算默认值表达式的值。也就是说,参数默认值是惰性求值的

let x = 99;
function foo(p = x + 1) {
  console.log(p);
}
foo() // 100
x = 100;
foo() // 101

说明:参数p的默认值是x + 1。这时,每次调用函数foo,都会重新计算x + 1,而不是默认p等于 100。

2.箭头函数

  • 基本用法

(1)ES6 允许使用“箭头”(=>)定义函数

var f = v => v;

// 等同于
var f = function (v) {
  return v;
};

(2)若箭头函数不需要或需要多个参数时,则使用一个圆括号代表参数部分

var f = () => 5;
// 等同于
var f = function () { return 5 };

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

(3)若箭头函数的代码块部分多于一条语句,就要用大括号将它们括起来,并且使用return语句返回

var sum = (num1, num2) => { return num1 + num2; }

(4)由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错

// 报错
let getTempItem = id => { id: id, name: "Temp" };

// 不报错
let getTempItem = id => ({ id: id, name: "Temp" });

(5)特殊情况,虽然可以运行,但会得到错误的结果

let foo = () => { a: 1 };
foo() // undefined

说明:原始意图是返回一个对象{ a: 1 },但是由于引擎认为大括号是代码块,所以执行了一行语句a: 1。这时,a可以被解释为语句的标签,因此实际执行的语句是1;,然后函数就结束了,没有返回值。
解决方法:见(4)

(6)若箭头函数只有一行语句,且不需要返回值,可以采用下面的写法,就不用写大括号了

let fn = () => void doesNotReturn();

(7)箭头函数可以与变量解构结合使用

const full = ({ first, last }) => first + ' ' + last;

// 等同于
function full(person) {
  return person.first + ' ' + person.last;
}
  • 使用注意点:

(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象;
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误;
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替;
(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数

  • 关于this对象:指向是可变的,但是在箭头函数中,它是固定的

例1:计时器中的this

function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

var id = 21;

foo.call({ id: 42 });
// id: 42

说明:setTimeout的参数是一个箭头函数,这个箭头函数的定义生效是在foo函数生成时,而它的真正执行要等到 100 毫秒后。如果是普通函数,执行时this应该指向全局对象window,这时应该输出21。但是,箭头函数导致this总是指向函数定义生效时所在的对象(本例是{id: 42}),所以输出的是42。

例2:计时器中的this
箭头函数可以让setTimeout里面的this,绑定定义时所在的作用域,而不是指向运行时所在的作用域

function Timer() {
  this.s1 = 0;
  this.s2 = 0;
  // 箭头函数
  setInterval(() => this.s1++, 1000);
  // 普通函数
  setInterval(function () {
    this.s2++;
  }, 1000);
}

var timer = new Timer();

setTimeout(() => console.log('s1: ', timer.s1), 3100);
setTimeout(() => console.log('s2: ', timer.s2), 3100);
// s1: 3
// s2: 0

说明:Timer函数内部设置了两个定时器,分别使用了箭头函数和普通函数。前者的this绑定定义时所在的作用域(即Timer函数),后者的this指向运行时所在的作用域(即全局对象)。所以,3100 毫秒之后,timer.s1被更新了 3 次,而timer.s2一次都没更新。

例3:构造函数中的this

let a=[1,2,5,7,3,8,4];
Array.prototype.mysort=function(){
    console.log(this);
}
var n=new Array(1,2,3,4,5);
n.mysort();

说明:此时的this指当前函数前面的对象Array,输出了整个数组集合。

以防在方法里使用外面的对象:
法一:箭头函数保持上下文对象一致

let a=[1,2,5,7,3,8,4];
Array.prototype.mysort=()=>{
    console.log(this);  //window
}
var n=new Array(1,2,3,4,5);
n.mysort();

法二:

let a=[1,2,5,7,3,8,4];
let that=this;
Array.prototype.mysort=function(){
    console.log(that);
}
var n=new Array(1,2,3,4,5);
n.mysort();

例4:枚举对象中的this

let student={
    init(){
         document.addEventListener("click",function(){
         console.log(this);  //this指向当前document对象
       });
    }
}
student.init();
let student={
    init(){
         document.addEventListener("click",()=>{
         console.log(this);  //this指向init对象
       });
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_43675447/article/details/88632395
今日推荐