ES6学习笔记
一、let 和const关键字
1.2 .0 let关键字的使用
-
let命令 基本语法
let a =10; { let b = 100;//b只在当前代码块中有效 }
-
不存在变量的提升
console.log(a)// a is not function let a=19; console.log(b);//undefined var b =10;
- a在未声明之前就是用了,var存在全局作用于,所以b只是没有被赋值,结果为undefined;但是let声明的变量,提前使用就会报错
-
暂时性死区( TDZ)
var tmp = 123; if (true) { //TDZ开始 tmp = 'abc'; // ReferenceError let tmp;//TDZ结束 }
- 虽然tmp在外层使用了var声明,但是if语句里,tmp使用了let声明绑定了tmp区域,但是tmp=“abc”,还未在let声明tmp是就进行赋值操作,所以报错了。
function bar(x = y, y = 2) { return [x, y]; } bar(); // 报错
- 调用
bar
函数之所以报错(某些实现可能不报错),是因为参数x
默认值等于另一个参数y
,而此时y
还没有声明,属于“死区”。如果y
的默认值是x
,就不会报错,因为此时x
已经声明了
-
不允许重复声明
// 报错 function func() { let a = 10; var a = 1; } // 报错 function func() { let a = 10; let a = 1; }
1.2.1 块级作用域
-
ES5存在全局作用域和函数作用域
var tmp = new Date(); function f() { console.log(tmp); if (false) { var tmp = 'hello world'; } } f(); // undefined
- (
内层变量覆盖外层变量
)外部全局tmp变量,但是又因为函数f()中,有声明了tmp,导致函数内部tmp覆盖了外部temp变量,至于输出undefined,是因为if 判断false不走输出语句,但是前面输出语句有tmp,声明了没赋值,所以值就是undefined啦
var s = 'hello'; for (var i = 0; i < s.length; i++) { console.log(s[i]); } console.log(i); // 5
- (
计数的循环变量泄露为全局变量
)循环结束了,但是i的值还存在。
- (
-
ES6块级作用域
function f1() { let n = 5; if (true) { let n = 10; } console.log(n); // 5 }
- n =5 和 n= 10是处于两块不同作用于的变量,n之所以输出5是因为这个n是和 let n= 5同处于一块作用域
-
ES6块级作用域和函数声明
- 块级作用域中声明函数,类似于用let声明变量,只在当前块中有效
function f() { console.log('I am outside!'); } (function () { if (false) { // 重复声明一次函数f function f() { console.log('I am inside!'); } } f();//ES5中 结果为:I am inside! }());
- ES5实际执行过程(f函数会被提升到自调用函数作用域的最前面)
// ES5 环境 function f() { console.log('I am outside!'); } (function () { //注意和上面代码if function位置 function f() { console.log('I am inside!'); } if (false) { } f(); }());
- ES6中执行(函数f存在if判断这个作用域中,所以仅限于此作用域有用,但是因为false所以,相当于没有声明函数)
// 浏览器的 ES6 环境 function f() { console.log('I am outside!'); } (function () { if (false) { // 重复声明一次函数f function f() { console.log('I am inside!'); } } f(); }()); // Uncaught TypeError: f is not a function
-
特别注意:(浏览器兼容性问题)—>为了减轻因此产生的不兼容问题;浏览器的实现可以不遵守上面的规定,有自己的行为。以下,只针对ES6有用
-
允许在块级作用域声明函数
-
函数声明类似于var ,即会将函数提升到作用域的头部
-
// 块级作用域内部的函数声明语句,建议不要使用 { let a = 'secret'; function f() { return a; } } // 块级作用域内部,优先使用函数表达式 { let a = 'secret'; let f = function () { return a; }; }
-
1.2.3 const关键字的使用
-
1.const基本语法(
const
声明一个只读的常量。一旦声明,常量的值就不能改变。)const PI = 3.1415; PI // 3.1415 PI = 3; // TypeError: Assignment to constant variable.
-
2、const本质(
const
实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。) -
3、const定义的数组,可以对其进行读写操作,但是如果将新数组赋值给它就会报错,可以push但是不可以给数组赋值
const a = []; a.push('Hello'); // 可执行 a.length = 0; // 可执行 a = ['Dave']; // 报错
1.2.4 顶层对象的属性
-
在浏览器中指的是window对象,在node中指的是global;ES5中顶层对象和全局属性都是等价的
window.a = 1;//顶层对象 a // 1 a = 2;//全局对象 window.a // 2
-
ES6改进
var a = 1; // 如果在 Node 的 REPL 环境,可以写成 global.a // 或者采用通用方法,写成 this.a window.a // 1 let b = 1; window.b // undefined
- 上面代码中,全局变量
a
由var
命令声明,所以它是顶层对象的属性;全局变量b
由let
命令声明,所以它不是顶层对象的属性,返回undefined
- 上面代码中,全局变量
二、变量的解构赋值
2.1.0 解构的含义和使用
2.1.1 解构的数组案例
-
从数组和对象中提取值,对变量进行赋值,这被称为解构
//以前写法 let a= 10; let b =20; //ES6写法 可以从数组中提取值,按照对应位置,对变量赋值 let [a,b] =[10,20]
-
解构本质:写法属于“模式匹配”;只要等号两边的模式相同,左边的变量就会被赋予对应的值(值)
<script> // 解构 let [a,b,c] = [1,2,4]; console.log(a); let [e,,d]=[,3,6]; console.log(e);//undefied 表示解构不成功 console.log(d);//6 let[head,hand,...all] = [1,2,3,4] console.log(head); console.log(hand); console.log(all);//前面解构完剩下的值 let [a,...b,c]= [1,2,3];//报错 ...b应该放在最后 let[h,k,...para] = [1] console.log(h);//1 console.log(k);//undefind console.log(para);//[] </script>
-
不完全解构
let [a,[b],c]=[3,[6,8],0]; console.log(a);//3 console.log(b);//6 console.log(c);//0
-
注意:如果等号右边的是不可遍历的对象,就会报错
let [a,r,y]='哈哈哈'; let [m,n,o]=false; //浏览器控制台报错 变量的解构和赋值.html:42 Uncaught TypeError: false is not iterable
-
解构的默认值
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中使用严格运算符
===
只有严格等于undefined默认值才会生效,后面有值就用后面的。let [x = 1] = [undefined]; x // 1 let [x = 1] = [null]; x // null
- 上面代码,一个数组等于null,是不严格等于
undefined
的,所以默认值不生效
- 上面代码,一个数组等于null,是不严格等于
2.1.3 解构对象案例
let {foo,bar} ={foo:'小明',bar:99};
console.log(foo);//小明
console.log(bar);//99
-
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。例如:
let {bar}={brr:'1000'}console.log(bar);//undefined
-
对象的解构赋值,可以很方便地将现有对象的方法,赋值到某个变量。
// 例一 let { log, sin, cos } = Math; sin(90)//0.89 // 例二 const { log } = console; log('hello') // hello
-
对象的解构其实是下面代码的简写(找到右边对应,直接赋值输出)
let { foo: foo, bar: bar } = { foo: 'aaa', bar: 'bbb' }; console.log(foo)//aaa console.log(bar)//bbb
let { foo: baz } = { foo: 'aaa', bar: 'bbb' }; baz // "aaa" foo // error: foo is not defined let {a =10,b=1} = {a=1,b=3} //a =10;可以看成a:10 a = 1 b = 3
上面代码中,
foo
是匹配的模式,baz
才是变量。真正被赋值的是变量baz
,而不是模式foo
。(直接看值)[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7IreVL96-1635732965572)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210928153614434.png)]上面代码有三次解构赋值,分别是对
loc
、start
、line
三个属性的解构赋值。注意,最后一次对line
属性的解构赋值之中,只有line
是变量,loc
和start
都是模式,不是变量 -
如果结构模式是嵌套对象。而且子对象的父属性不存在,则会报错
let {foo :{bar}} = {bar:"bar"}//报错
-
解构赋值可以取到被继承的对象
// 解构赋值可以取到继承的值 let obj1 = {}; let obj2 = { foo: 'bar' }; Object.setPrototypeOf(obj1, obj2); const { foo } = obj1; console.log(foo);//"bar"
- obj1的原型对象是obj2,而obj1自身是没有foo属性的,因为继承,所以拿到来自obj2的属性和值
-
对象解构的默认值,和数组解构一样,严格等于undefined时才生效
-
注意点
如果要将已经声明的变量解构赋值必须注意let x; {x} ={x:1}//报错 let y; ({y} ={y:3}) console.log(y);//3
-
字符串的解构
let [a,b,c,d]='nice'; a//n b//i c//c d//e
-
数值和布尔值的解构赋值
let {toString:s} =123; console.log(s);//12
-
用途
//1 变量的交换 let x =20; let y =30; [x,y] =[y,x] //2 函数返回多个值,然后取值 function example() { return [1, 2, 3]; } let [a, b, c] = example(); //3 函数定义参数(能够匹配任意位置对应的参数值) function fn({x,y,r}){....} fn({x:10,y:2,r:30}) //4 提取JSON字符串 let jsonData = { id: 42, status: "OK", data: [867, 5309] }; let { id, status, data: number } = jsonData; console.log(id, status, number); // 42, "OK", [867, 5309]
三、字符串、数值、数组、对象的拓展
2.1.1字符串拓展
-
字符串遍历for …of
// for..of() 创建一个可迭代对象 let str = 'codePonit'; for (let item of str) { console.log(item); }; //这个遍历器最大的优点是可以识别大于0xFFFF的码点 let text = String.fromCodePoint(0x20BB7); for (let i = 0; i < text.length; i++) { console.log(text[i]); } // " " // " " for (let i of text) { console.log(i); } // "𠮷"
-
模板字符串(可以放入js表达式、可以运算、引用对象属性,调用函数)
es5 var age = 10; innerText="年龄是"+age+"岁了"; es6 innerHTML=`年龄是${age}岁了` //也可以进行运算 innerHTML=`年龄是${2*10}岁了` //调用函数 function fn() { return "Hello World"; } `foo ${fn()} bar` // foo Hello World bar
-
模板标签(紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串,函数调用的一种新式,标签指的是函数,紧跟的是参数)
alert`hello`; 等同于 alert(['hello']) let a = 5; let b = 10; tag`Hello ${ a + b } world ${ a * b }`; // 等同于 tag(['Hello ', ' world ', ''], 15, 50);
-
模板标签的应用
let message = SaferHTML`<p>${sender} has sent you a message.</p>`; function SaferHTML(templateData) { let s = templateData[0]; for (let i = 1; i < arguments.length; i++) { let arg = String(arguments[i]); // Escape special characters in the substitution. s += arg.replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">"); // Don't escape special characters in the template. s += templateData[i]; } return s; } //应用 let sender = '<script>alert("abc")</script>'; // 恶意代码 let message = SaferHTML`<p>${sender} has sent you a message.</p>`; message // <p><script>alert("abc")</script> has sent you a message.</p>
2.1.2 字符串方法拓展
-
includes(parm1,param2)返回布尔值 判断是否包含指定的字符 参数1 :指定字符 参数2: 指定开始下标
-
startsWith(parm1,param2) 返回布尔值 判断是否以指定的字符开始
-
endsWith(parm1,param2) 返回布尔值 判断是否以指定的字符结尾
let s = 'Hello World'; console.log(s.includes('e'));//true
mplate.
s += templateData[i];
}
return s;
}
//应用
let sender = ‘’; // 恶意代码
let message = SaferHTML<p>${sender} has sent you a message.</p>
;
message
//
<script>alert(“abc”)</script> has sent you a message.
### 2.1.2 字符串方法拓展
- includes(parm1,param2)返回布尔值 判断是否包含指定的字符 参数1 :指定字符 参数2: 指定开始下标
- startsWith(parm1,param2) 返回布尔值 判断是否以指定的字符开始
- endsWith(parm1,param2) 返回布尔值 判断是否以指定的字符结尾
```js
let s = 'Hello World';
console.log(s.includes('e'));//true