ES6个人学习笔记(一)

一、es5与es6【es9】的区别?

(1)var会存在变量提升现象,但是let和const则不会有这种情况
(2)暂时性死区 简称 TDZ :只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。

var tmp = 123;

if (true) {
    
    
  tmp = 'abc'; // ReferenceError
  let tmp;
}

ES6 明确规定,如果区块中存在letconst命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。

暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

为什么需要块级作用域?
ES5 只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。
第一种场景,内层变量可能会覆盖外层变量。
第二种场景,用来计数的循环变量泄露为全局变量。

ES6 的块级作用域

function f1() {
    
    
  let n = 5;
  if (true) {
    
    
    let n = 10;
  }
  console.log(n); // 5
}

ES6 允许块级作用域的任意嵌套。块级作用域的出现,实际上使得获得广泛应用的立即执行函数表达式(IIFE)不再必要了。

let和var有区别?
(1)let是ES6新增的一个命令,用来声明局部变量,用法和var类似,但是声明的变量只在let命令所在的代码内容有效,而且有暂时性死区的约束(前面赋值报错,后面赋值不报错);
(2)let不允许在相同作用域内,重复声明同一个变量;
(3)var声明在方法外是全局变量、声明在方法内是局部变量;
(4)记住重要的一点:var声明的变量会挂载在window上,而let和const声明的变量不会;

二、const声明一个只读的常量

const除了以下两点与let不同外,其他特性均与let相同:
1、const一旦声明变量,就必须立即初始化,不能留到以后赋值。
2、一旦声明,常量的值就不能改变。

本质:const限定的是赋值行为。

const a = 1;
a = 2;//报错

const arr = [];
arr.push(1) //[1] 
//在声明引用型数据为常量时,const保存的是变量的指针,只要保证指针不变就不会保存。下面的行为就会报错

arr = [];//报错 因为是赋值行为。变量arr保存的指针改变了。

三、解构赋值

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)
1、es5一次声明多个变量:

var a = 1,
    b = 2,
    c = 3,
    ...;

2、es6一次声明多个变量

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

本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。

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
4、另一种情况是不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功。
5、如果等号的右边不是数组,那么将会报错。
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'

7、注意,ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,如果一个数组成员不严格等于undefined,默认值是不会生效的。
8、如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。
9、默认值可以引用解构赋值的其他变量,但该变量必须已经声明。

对象的解构赋值

解构不仅可以用于数组,还可以用于对象:

let {
    
     foo, bar } = {
    
     foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"

(1)对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。

let {
    
     bar, foo } = {
    
     foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"

let { baz } = { foo: “aaa”, bar: “bbb” };
baz // undefined
(2)如果变量名与属性名不一致,必须写成下面这样。

let {
    
     foo: baz } = {
    
     foo: 'aaa', bar: 'bbb' };
baz // "aaa"

let obj = {
    
     first: 'hello', last: 'world' };
let {
    
     first: f, last: l } = obj;
f // 'hello'
l // 'world'

字符串的解构赋值

const [a, b, c, d, e] = 'hello';

类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值。

let {
    
    length : len} = 'hello';
len // 5

函数参数的解构赋值

function add([a,b]){
    
    
  return a+b;
}
add([2,3])//5

函数参数的解构也可以使用默认值。

function move({
    
    x = 0, y = 0} = {
    
    }) {
    
    
  return [x, y];
}

move({
    
    x: 3, y: 8}); // [3, 8]
move({
    
    x: 3}); // [3, 0]
move({
    
    }); // [0, 0]
move(); // [0, 0]

用途

  1. 除了可以一次定义多个变量
  2. 还可以让函数返回多个值
  3. 可以方便地让函数的参数跟值对应起来
  4. 提取json数据
  5. 函数参数的默认值

如果将var x = 3var去除,函数foo的内部变量x就指向第一个参数x,与匿名函数内部的x是一致的,所以最后输出的就是2,而外层的全局变量x依然不受影响。

var x = 1;
function foo(x, y = function() {
    
     x = 2; }) {
    
    
  x = 3;
  y();
  console.log(x);
}

foo() // 2
x //

1

四、rest参数

ES6引入rest参数(形式为…变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了,rest参数搭配的遍历是一个数组,该变量将多余的参数放入数组中。

function add(...values) {
    
    
  let sum = 0;

  for (var val of values) {
    
    
    sum += val;
  }

  return sum;
}

add(2, 5, 3) // 10

arguments对象不是数组,而是一个类似数组的对象。所以为了使用数组的方法,必须使用Array.prototype.slice.call先将其转为数组。rest 参数就不存在这个问题,它就是一个真正的数组,数组特有的方法都可以使用。下面是一个利用 rest 参数改写数组push方法的例子。

function push(array, ...items) {
    
    
  items.forEach(function(item) {
    
    
    array.push(item);
    console.log(item);
  });
}

var a = [];
push(a, 1, 2, 3)

注意,rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。

// 报错
function f(a, ...b, c) {
    
    
  // ...
}

函数的length属性,不包括 rest 参数。

(function(a) {
    
    }).length  // 1
(function(...a) {
    
    }).length  // 0
(function(a, ...b) {
    
    }).length  // 1

五、严格模式

ES5 开始,函数内部可以设定为严格模式。

ES2016 做了一点修改,规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错

六、箭头函数

ES6 允许使用“箭头”(=>)定义函数。
如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。

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

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

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

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

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

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

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

箭头函数有几个使用注意点:

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

this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。正是因为它没有this,所以也就不能用作构造函数。

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

// ES5
function foo() {
    
    
  var _this = this;

  setTimeout(function () {
    
    
    console.log('id:', _this.id);
  }, 100);
}
//转换后的 ES5 版本清楚地说明了,箭头函数里面根本没有自己的this,而是引用外层的this。
//由于箭头函数没有自己的`this`,所以当然也就不能用`call()`、`apply()`、`bind()`这些方法去改变`this`的指向。

七、字符串扩展(includes()、startWith()、endsWith())

传统上,JavaScript 只有indexOf方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6 又提供了三种新方法。

  • includes():返回布尔值,表示是否找到了参数字符串。
  • startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
  • endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
let s = 'Hello world!';
s.startsWith('Hello') // true
s.endsWith('!') // true
s.includes('o') // true

注: 这三个方法都支持第二个参数

let s = 'Hello world!';
s.startsWith('world', 6) // true
s.endsWith('Hello', 5) // true
s.includes('Hello', 6) // false

上面代码表示,使用第二个参数n时,endsWith的行为与其他两个方法有所不同。它针对前n个字符,而其他两个方法针对从第n个位置直到字符串结束。

repeat方法返回一个新字符串,表示将原字符串重复n次

'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
'na'.repeat(0) // ""
  • 参数如果是小数,会被向下取整。
  • 如果repeat的参数是负数或者Infinity,会报错。
  • 参数NaN等同于 0。
  • 如果repeat的参数是字符串,则会先转换成数字。

padStart()、padEnd()

ES2017 引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全。

'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'

'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'

模板字符串 ``

es5的字符串模板输出通常是使用+拼接。

这样的缺点显然易见:字符串拼接内容多的时候,过于混乱,易出错。

而ES6 引入了模板字符串解决这个问题。

var name = "风屿",trait = "帅气";
//es5
var str = "他叫"+name+",人非常"+trait+",说话又好听";

//es6
var str2 = `他叫 ${
    
    name} ,人非常 ${
    
    trait} ,说话又好听`;

模板字符串是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。

  • 如果在模板字符串中需要使用反引号,则前面要用反斜杠转义。
  • 如果使用模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中。
  • 模板字符串中嵌入变量,需要将变量名写在${}之中。
  • 大括号内部可以放入任意的 JavaScript 表达式,可以进行运算,以及引用对象属性。
  • 模板字符串之中还能调用函数。
  • 如果大括号中的值不是字符串,将按照一般的规则转为字符串。比如,大括号中是一个对象,将默认调用对象的toString方法。
  • 如果模板字符串中的变量没有声明,将报错。

标签模板

模板字符串可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串。这被称为“标签模板”功能。

alert`123`
// 等同于
alert(123)

如果模板字符里面有变量,就不是简单的调用了,而是会将模板字符串先处理成多个参数,再调用函数。

let a = 5;
let b = 10;

tag`Hello ${
    
     a + b } world ${
    
     a * b }`;
// 等同于
tag(['Hello ', ' world ', ''], 15, 50);

猜你喜欢

转载自blog.csdn.net/javaScript1997/article/details/109744333
今日推荐