初读ES6标准入门,
这本书也买了许久,一直放在家里睡觉,个人喜欢纸质书多一点,这算是一本开源书籍,免费阅读网址http://eses6.ruanyifeng.com
书讲的知识点还是挺细致的
这里先给大家试着总结下
菜鸟题目分享中,有一题说后面会给大家分享简单的 做法
for(let i = 0; i < 6; i++ ) {
setTimeout(function timer () {
console.log(i);
}, i*1000);
}
把var 换成 let 是不是简单很多,为什么这样写呢?
通过var定义的变量,作用域是整个封闭函数,在全局范围内都有效,所以全局只有一个变量i;通过let定义的变量,作用域是在块级或是子块中。
更进一步的说,每轮循环的时候let声明的i都是一个新的变量。for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域
(一)let和const命令
这里给大家分享下我总结的var 和 let 区别
- 1、通过var定义的变量,作用域是整个封闭函数,是全域的 。通过let定义的变量,作用域是在块级或是子块中。更进一步的说。每轮循环的时候let声明的i都是一个新的变量。var声明的i不会变,都是同一个变量。
for (let i = 0; i < 10; i++) {
// ...
}
console.log(i);
块级作用域
function f1() {
let n = 5;
if (true) {
let n = 10;
}
console.log(n); // 5 这表示外层代码块不受内层代码块的影响,如果两次都使用var定义变量n,最后输出的值才是 10
}
ES6 允许块级作用域的任意嵌套。
{{{{{let insane = 'Hello World'}}}}};//使用了一个五层的块级作用域。外层作用域无法读取内层作用域的变量
{{{{
{let insane = 'Hello World'}
console.log(insane); // 报错
}}}};
{{{{
let insane = 'Hello World';
{let insane = 'Hello World'}
}}}};//内层作用域可以定义外层作用域的同名变量。
- 2、 let声明变量不能被预解析,let命令所声明的变量一定要在声明后使用,否则报错。
console.log(a)
var a=5 =>undifined 变量a用var命令声明,会发生变量提升,即脚本开始运行时,变量a已经存在了,但是没有值,所以会输出undefined。
console.log(b)
let b=6 =>b is not defined let在声明它之前,变量bar是不存在的,这时如果用到它,就会抛出一个错误。
- 3、暂时性死区(变量let声明前都不能访问,为防止先调用在声明这个现象)只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
console.log(b)
let b=6
console.log(b) //在let声明变量前,对tmp赋值会报错
typeof x; // ReferenceError“暂时性死区”也意味着typeof不再是一个百分之百安全的操作。
let x; //变量x使用let命令声明,所以在声明之前,都属于x的“死区”,只要用到该变量就会报错。因此,typeof运行时就会抛出一个ReferenceError。
typeof undeclared_variable // "undefined" 作为比较,如果一个变量根本没有被声明,使用typeof反而不会报错。
- 4、在相同作用域内,let不能重复声明一个变量
var a=5
console.log(a)
var a=6
console.log(a)
let a=5
console.log(a)
let a=6
console.log(a) //报错:Identifier 'a' has already been declared(a已经存在)
const用法:const声明一个常量,一旦声明后就不能修改了
- 1、如果声明后再去修改的话就会报错
const PI = 3.1415;
PI // 3.1415
PI = 3; // TypeError: Assignment to constant variable.
- 2、只能先声明后使用,不会被提前解析
if (true) {
console.log(MAX); // ReferenceError 常量MAX声明之前就调用,结果报错。
const MAX = 5;
}
- 3、只声明不赋值也会报错
const foo;
// SyntaxError: Missing initializer in const declaration
- 4、不能重复声明一个常量
var message = "Hello!";
let age = 25;
// 以下两行都会报错
const message = "Goodbye!";
const age = 30;
注意:const声明的对象中属性是可以修改的
let和const区别
通过以上我们不难发现,let 和 const只在声明所在的块级作用域内有效,同时存在暂时性死区,只能在声明的位置后面使用,并且都不可重复声明,那么,他们之间有什么不同的地方呢?
- let声明的变量可以改变,值和类型都可以改变;const声明的常量不可以改变,这意味着,const一旦声明,就必须立即初始化,不能以后再赋值。但是如果const声明的常量是个对象或者数组,那么这个常量的属性还是能够改变的,因为这个常量保存的是内存地址,实际数据在堆内存里,因此const声明的对象还是能改变属性,如果不想被改变可以用object.freeze()进行冻结,这个我在前面菜鸟题目中也有分享
(二)变量的解构赋值
默认值
解构赋值允许指定默认值
let [foo = true] = [];
foo // true
let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
let [x = 1] = [undefined]; //只有当一个数组成员严格等于undefined,默认值才会生效
x // 1
let [x = 1] = [null]; //一个数组成员是null,默认值就不会生效,因为null不严格等于undefined
x // null
默认值可以引用解构赋值的其他变量,但该变量必须已经声明。
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还没有声明
不能使用圆括号的情况
1、变量声明语句
// 全部报错
let [(a)] = [1];
let {x: (c)} = {};
let ({x: c}) = {};
let {(x: c)} = {};
let {(x): c} = {};
let { o: ({ p: p }) } = { o: { p: 2 } };//它们都是变量声明语句,模式不能使用圆括号
2、函数参数
// 报错
function f([(z)]) { return z; }
// 报错
function f([z,(x)]) { return x; }
3、赋值语句的模式
// 全部报错
({ p: a }) = { p: 42 }; 将整个模式放在圆括号之中,导致报错。
([a]) = [5];
可以使用圆括号的情况
只有一种情况:赋值语句的非模式部分,可以使用圆括号
[(b)] = [3]; // 正确
({ p: (d) } = {}); // 正确
[(parseInt.prop)] = [3]; // 正确
解构赋值的用途
1、交换变量的值
let x = 1;
let y = 2;
[x, y] = [y, x];
2、从函数返回多个值。一般来说函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里返回。有了解构赋值,取出这些值就非常方便。
// 返回一个数组
function example() {
return [1, 2, 3];
}
let [a, b, c] = example();
// 返回一个对象
function example() {
return {
foo: 1,
bar: 2
};
}
let { foo, bar } = example();
3、函数参数的定义,解构赋值可以方便地将一组参数与变量名对应起来。
// 参数是一组有次序的值
function f([x, y, z]) { ... }
f([1, 2, 3]);
// 参数是一组无次序的值
function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1});
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]
5、函数参数的默认值
jQuery.ajax = function (url, {
async = true,
beforeSend = function () {},
cache = true,
complete = function () {},
crossDomain = false,
global = true,
// ... more config
} = {}) {
// ... do stuff
};
//指定参数的默认值,就避免了在函数体内部再写var foo = config.foo || 'default foo';这样的语句。
6、遍历 Map 结构
//任何部署了 Iterator 接口的对象,都可以用for...of循环遍历。Map 结构原生支持 Iterator 接口,配合变量的解构赋值,获取键名和键值就非常方便。遍历器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
const map = new Map();
map.set('first', 'hello');
map.set('second', 'world');
for (let [key, value] of map) {
console.log(key + " is " + value);
}
// first is hello
// second is world
//如果只想获取键名,或者只想获取键值,可以写成下面这样。
// 获取键名
for (let [key] of map) {
// ...
}
// 获取键值
for (let [,value] of map) {
// ...
}
7、输入模块的指定方法
const { SourceMapConsumer, SourceNode } = require("source-map");
(三)字符串的扩展
字符串的遍历器接口 (Iterator)
//遍历器我在上文也有简单介绍过
for (let codePoint of 'foo') {
console.log(codePoint)
}
// "f"
// "o"
// "o"
模板字符串
//es5中输出模版一般这样写:
$('#result').append(
'There are <b>' + basket.count + '</b> ' +
'items in your basket, ' +
'<em>' + basket.onSale +
'</em> are on sale!'
);
//es6 引入了模板字符串
$('#result').append(`
There are <b>${basket.count}</b> items
in your basket, <em>${basket.onSale}</em>
are on sale!
`);
模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
// 普通字符串
`In JavaScript '\n' is a line-feed.`
// 多行字符串
`In JavaScript this is
not legal.`
console.log(`string text line 1
string text line 2`);
// 字符串中嵌入变量
let name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
使用模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中
$('#list').html(`
<ul>
<li>first</li>
<li>second</li>
</ul>
`);
//如果你不想要这个换行,可以使用trim方法消除它。
$('#list').html(`
<ul>
<li>first</li>
<li>second</li>
</ul>
`.trim());
模板字符串中嵌入变量,需要将变量名写在${}之中。
function authorize(user, action) {
if (!user.hasPrivilege(action)) {
throw new Error(
// 传统写法为
// 'User '
// + user.name
// + ' is not authorized to do '
// + action
// + '.'
`User ${user.name} is not authorized to do ${action}.`);
}
}
大括号内部可以放入任意的 JavaScript 表达式,可以进行运算,以及引用对象属性。
let x = 1;
let y = 2;
`${x} + ${y} = ${x + y}`
// "1 + 2 = 3"
`${x} + ${y * 2} = ${x + y * 2}`
// "1 + 4 = 5"
let obj = {x: 1, y: 2};
`${obj.x + obj.y}`
// "3"
模板字符串之中还能调用函数。
function fn() {
return "Hello World";
}
`foo ${fn()} bar`
// foo Hello World bar
如果模板字符串中的变量没有声明,将报错。
// 变量place没有声明
let msg = `Hello, ${place}`;
// 报错
模板字符串的变量之中,嵌入了另一个模板字符串
const tmpl = addrs => `
<table>
${addrs.map(addr => `
<tr><td>${addr.first}</td></tr>
<tr><td>${addr.last}</td></tr>
`).join('')}
</table>//addrs 是函数tmpl的形参
`;
//使用方法:
const data = [
{ first: '<Jane>', last: 'Bond' },
{ first: 'Lars', last: '<Croft>' },
];
console.log(tmpl(data));
// <table>
//
// <tr><td><Jane></td></tr>
// <tr><td>Bond</td></tr>
//
// <tr><td>Lars</td></tr>
// <tr><td><Croft></td></tr>
//
// </table>
如需引用模板字符串本身,在需要时执行,可以这样写
// 写法一
let str = 'return ' + '`Hello ${name}!`';
let func = new Function('name', str);
func('Jack') // "Hello Jack!"
// 写法二
let str = '(name) => `Hello ${name}!`';
let func = eval.call(null, str);
func('Jack') // "Hello Jack!" eval是个特殊的语法解析器,它能把字符串解析成js代码,所以str明明是个字符串但是却能当函数执行。
includes(), startsWith(), endsWith()
这三个方法跟JavaScript中的indexOf方法相似
- 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
注意:endWith使用第二参数n时与其他两个方法有所不同,它针对前n个字符,而其他两个针对从第n个位置直到字符串结束
repeat方法返回一个新字符串,表示将原字符串重复n次。
'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
'na'.repeat(0) // ""
'na'.repeat(2.9) // "nana" 参数如果是小数,会被取整。
'na'.repeat(Infinity) // RangeError repeat的参数是负数或者Infinity,会报错。
'na'.repeat(-1) // RangeError
'na'.repeat(-0.9) // "" 参数是 0 到-1 之间的小数,则等同于 0,这是因为会先进行取整运算。0 到-1 之间的小数,取整以后等于-0,repeat视同为 0。
'na'.repeat(NaN) // "" 参数NaN等同于 0。
'na'.repeat('na') // ""
'na'.repeat('3') // "nanana" repeat的参数是字符串,则会先转换成数字。
padStart(),padEnd() padStart()用于头部补全,padEnd()用于尾部补全
'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'
'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'
padStart()和padEnd()一共接受两个参数,第一个参数是字符串补全生效的最大长度,第二个参数是用来补全的字符串。
'xxx'.padStart(2, 'ab') // 'xxx'
'xxx'.padEnd(2, 'ab') // 'xxx' 原字符串的长度,等于或大于最大长度,则字符串补全不生效,返回原字符串。
'abc'.padStart(10, '0123456789') // '0123456abc' 用来补全的字符串与原字符串,两者的长度之和超过了最大长度,则会截去超出位数的补全字符串。
'x'.padStart(4) // ' x'
'x'.padEnd(4) // 'x ' 省略第二个参数,默认使用空格补全长度。
'1'.padStart(10, '0') // "0000000001"
'12'.padStart(10, '0') // "0000000012"
'123456'.padStart(10, '0') // "0000123456" padStart()的常见用途是为数值补全指定位数。下面代码生成 10 位的数值字符串。
一个用途是提示字符串格式。
'12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12"
'09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"
今天有被师傅批评,因为好多例子都是从原书上搬过来的,自己也没有去尝试写,只是过一遍,也没有去深究,以后不会再这样了,很多东西想变成自己的就得付出努力,我很抱歉偷懒了,我会找时间把这篇文章修整下,保持本心,坚持不放弃,既然选择前端这条路,就别回头,愿所有在这条路上的人,能不被时光辜负😄