做家务的男人网站,特种工建设网站,找建设网站公司哪家好,网站seo顾问个人主页#xff1a;学习前端的小z 个人专栏#xff1a;JavaScript 精粹 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结#xff0c;欢迎大家在评论区交流讨论#xff01; ES5、ES6介绍 文章目录 #x1f4af;声明命令 let、const#x1f35f;1 let声明符学习前端的小z 个人专栏JavaScript 精粹 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结欢迎大家在评论区交流讨论 ES5、ES6介绍 文章目录 声明命令 let、const1 let声明符1.1 基础特性1.2 不进行变量提升1.3 致死域(暂时性死区) 1.4 无法重复声明1.5 块作用域 (block scope)1.6 块作用域下的函数声明 2 const声明符2.1 声明时必须赋值2.2 值为对象 解构赋值1 数组解构1.1 基础变量赋值1.2 声明分别赋值1.3 解构默认值1.4 交换变量值1.5 解析函数返回的数组1.6 忽略返回值/跳过某项1.7 赋值数组剩余值给一个变量1.8 嵌套数组解构 2 对象解构2.1 基础对象解构2.2 无声明解构2.3 赋值给新变量名2.4 解构默认值2.5 赋值给新变量名的同时提供默认值2.6 嵌套对象和数组解构2.7 可计算对象属性名与解构2.8 同时使用数组和对象解构2.9 注意点 3 函数参数的解构赋值4 解构的用途4.1 交换变量的值4.2 从函数返回多个值4.3 函数参数的定义4.4 提取 JSON 数据4.5 函数参数的默认值 声明命令 let、const
1 let声明符 ES6 新增了let命令用来声明变量。它的用法类似于var但是所声明的变量只在let命令所在的代码块内有效。 1.1 基础特性
if(true){let a let;var b var;
}console.log(b); //var
console.log(a); //Uncaught ReferenceError: a is not defined---------------------------------------------------------//使用let声明的变量仅在块级作用域内({ })有效最后输出的是 6。
for(let i 0; i 10; i){}
console.log(i); //Uncaught ReferenceError: i is not defined
上面代码在代码块之中分别用let和var声明了两个变量。然后在代码块之外调用这两个变量结果let声明的变量报错var声明的变量返回了正确的值。这表明let声明的变量只在它所在的代码块有效。
// 在循环体表达式中let声明迭代变量i是let声明的当前的i只在本轮循环有效所以每一次循环的i其实都是一个新的变量
var a [];
for (let i 0; i 10; i) {a.push(function () {console.log(i);});
}
a[5](); // 5// 在循环体表达式中let声明 和循环体中let 声明同名变量是不冲突的 设置循环变量的那部分是一个父作用域而循环体内部是一个单独的子作用域。相当于每次循环{}内部都会单独开一个局部作用域
for (let i 0; i 3; i) {let i abc;console.log(i);
}1.2 不进行变量提升 var命令会发生“变量提升”现象即变量可以在声明之前使用值为undefined。 let命令改变了语法行为它所声明的变量一定要在声明后使用否则报错。 这一点需要特殊注意 // var 的情况
console.log(num); // 输出undefined
var num 2;-------------------------// let 的情况
console.log(age); // 报错ReferenceError
let age 16;1.3 致死域(暂时性死区) 只要块级作用域内存在let命令它所声明的变量就“绑定”binding这个区域不再受外部的影响。 //正常调用
let num 10;
if (true) {console.log(num); //10
}------------------//if { }内部let为独立局部作用域 访问为 20
let num 10;
if (true) {let num 20;console.log(num); //20
}------------------//发生致死 因为fn局部作用域中有let声明num 所以在作用域内 不能先调用再使用let声明。
let num 10;
function fn() {console.log(num); //Uncaught ReferenceError: Cannot access num before initializationlet num 20;
}
fn();------------------//typeof操作影响typeof num;
let num; //Uncaught ReferenceError: Cannot access num before initialization当程序的控制流程在新的作用域module function 或 block 作用域进行实例化时在此作用域中用let/const声明的变量会先在作用域中被创建出来但因此时还未进行词法绑定所以是不能被访问的如果访问就会抛出错误。因此在这运行流程进入作用域创建变量到变量可以被访问之间的这一段时间就称之为暂时死区。
es6规定 let/const 会使区域形成封闭的作用域 若在声明之前使用变量就会发生错误, 在代码块内使用let命令声明变量之前 该变量都无法使用称为“暂时性死区”temporal dead zone简称 TDZ。总之暂时性死区的本质就是只要一进入当前作用域所要使用的变量就已经存在了但是不可获取只有等到声明变量的那一行代码出现才可以获取和使用该变量。使用let 确保先 声明 再 使用 变量 不要混用let和var在同一个作用域链上 避免死区
1.4 无法重复声明 let不允许在相同作用域内重复声明同一个变量。 //发生致死 因为var会变量提升。
let num 10;
if (true) {var num 20; //Uncaught SyntaxError: Identifier num has already been declaredconsole.log(num);
}1.5 块作用域 (block scope) 在ES5中只全局作用域和函数作用域。这会导致函数作用域覆盖了全局作用域亦或者循环中的变量泄露为全局变量。 EcmaScript 6引入了块级作用域(block scope)块级作用域只能在块中被访问以下两种情况可以创建块级作用域的变量。 在函数中 在被{和}包裹的块中
{var x 10;
}
console.log(x); //10------------------{let x 10;
}
console.log(x);//x is not defined------------------//函数有两个代码块都声明了变量num运行后输出 3。这表示外层代码块不受内层代码块的影响。如果两次都使用var定义变量num最后输出的值才是 6。
function fn() {let num 3;if (true) {let num 6;}console.log(num); // 3
}1.6 块作用域下的函数声明 ES5 规定函数只能在顶层作用域和函数作用域之中声明不能在块级作用域声明。 ES6 的块级作用域必须有大括号如果没有大括号JavaScript 引擎就认为不存在块级作用域。 function a() {console.log(我是全局作用域内声明的函数a);
}(function () {if (false) {function a() {console.log(我是块级作用域内声明的函数a);}}a();
})();----------------------function a() {console.log(我是全局作用域内声明的函数a);
}
(function () {var a;if (false) {a function () {console.log(我是块级作用域内声明的函数a);}}a();
})();
为了减轻因此产生的不兼容问题ES6 在附录 B里面规定浏览器的实现可以不遵守上面的规定有自己的行为方式。
允许在块级作用域内声明函数。函数声明类似于var即会提升到全局作用域或函数作用域的头部。同时函数声明还会提升到所在的块级作用域的头部。
2 const声明符 const声明一个只读的常量。一旦声明常量的值就不能改变。 const与let在 块作用域 重复声明 致死域的问题上是一致的 const的作用域与let命令相同只在声明所在的块级作用域内有效。 const DATA 我是常量 不能改变哦;
DATA // 我是常量 不能改变哦DATA 改一个试试看;
// TypeError: Assignment to constant variable.const声明的变量不得改变值这意味着const一旦声明变量就必须立即初始化不能留到以后赋值。
2.1 声明时必须赋值
const X;
//Uncaught SyntaxError: Missing initializer in const declaration2.2 值为对象
const KEY_MAP {a:1,b:2
}KEY_MAP[a] 2; //不报错-----------const ARRAY [];
ARRAY.push(something); // 可执行
ARRAY.length 0; // 可执行
ARRAY [something]; // 报错解构赋值 ES6提供了更简洁的赋值模式, 从数组和对象中提取值. 这被称为解构Destructuring。 [a, b] [50, 100];console.log(a);
// expected output: 50console.log(b);
// expected output: 100[a, b, ...rest] [10, 20, 30, 40, 50];console.log(rest);
// expected output: [30, 40, 50]我们有很多种不同的方式使用 JavaScript 解构。
1 数组解构
数组解构是极为简单整洁的在复制表达式的左侧使用数组字面量。数组字面量中的每个变量名称映射为解构数组的相同索引项。
1.1 基础变量赋值
let foo [one, two, three];let [red, yellow, green] foo;
console.log(red); // one
console.log(yellow); // two
console.log(green); // three1.2 声明分别赋值
你可以通过变量声明分别解构赋值。举例首先声明变量然后分别赋值。
// 声明变量
let a, b;
// 然后分别赋值
[a, b] [1, 2];
console.log(a); // 1
console.log(b); // 21.3 解构默认值
如果解构取出的值是 undefined可以设置默认值
let a, b;
// 设置默认值
[a 5, b 7] [1];
console.log(a); // 1
console.log(b); // 7在上面的例子中我们给 a 和 b 设置了默认值。这种情况下如果 a 或 b 的值是 undefined它将赋值默认值 5 给 a 7 给 b
1.4 交换变量值
解构可以在一个解构表达式中交换两个变量值
let a 1;
let b 3;[a, b] [b, a];
console.log(a); // 3
console.log(b); // 1如果你想不使用解构交换变量值将必须提供一个缓存变量或者同解构一起使用
1.5 解析函数返回的数组
可以解构函数返回的数组。
function c() {return [10, 20];
}let a, b;
[a, b] c();
console.log(a); // 10
console.log(b); // 20在上面的例子中c() 的返回值 [10, 20] 可以在单独的一行代码中使用解构解析。
1.6 忽略返回值/跳过某项
你也可以跳过一些没有用的返回值。
function c() {return [1, 2, 3];
}let [a, , b] c();
console.log(a); // 1
console.log(b); // 3在罕见的情况下你想忽略所有的值。
[, ,] c();当然这是不会发生的。
1.7 赋值数组剩余值给一个变量
当你使用数组解构你可以赋值数组剩余部分给一个单独的变量。
let [a, ...b] [1, 2, 3];
console.log(a); // 1
console.log(b); // [2, 3]小心结尾的逗号语法错误它将爆发如果在剩余元素的左侧使用结尾逗号
let [a, ...b,] [1, 2, 3];
// SyntaxError: rest element may not have a trailing comma1.8 嵌套数组解构
像对象一样你也可以使用数组嵌套解构。这里有一个例子
const color [#FF00FF, [255, 0, 255], rgb(255, 0, 255)];// Use nested destructuring to assign red, green and blue
// 使用嵌套解构赋值 red, green, blue
const [hex, [red, green, blue]] color;console.log(hex, red, green, blue); // #FF00FF 255 0 2552 对象解构
2.1 基础对象解构
let x { y: 22, z: true };
let { y, z } x; // let {y:y,z:z} x;的简写console.log(y); // 22
console.log(z); // true2.2 无声明解构
你可以使用解构分别从它的声明赋值变量。这意味着在上面的例子中不需要创建变量 x。
let y, z;({ y, z } { y: 1, z: 2 });注意圆括号 (...) 包裹赋值声明是必须的当使用对象字面量解构赋值无声明变量。 {a, b} {a: 1, b: 2} 不是有效的独立语法左侧的 {a, b} 被考虑为代码块而不是一个对象字面量。 因此({a, b} {a: 1, b: 2}) 是有效的, 等价于 var {a, b} {a: 1, b: 2}。 (...) 表达式需要前置分号或者它可能用于在前一行执行函数。 2.3 赋值给新变量名
当使用对象解构时你也可以改变变量的名称如下例子
let o { p: 22, q: true };
let { p: foo, q: bar } o;console.log(foo); // 22
console.log(bar); // true例子中var {p: foo} o 获取对象 o 的属性名 p然后赋值给一个名称为 foo 的变量。
2.4 解构默认值
如果解构取出的对象值是 undefined 你也可以设置默认值。
let { a 10, b 5 } { a: 3 };console.log(a); // 3
console.log(b); // 52.5 赋值给新变量名的同时提供默认值
let { a: aa 10, b: bb 5 } { a: 3 };console.log(aa); // 3
console.log(bb); // 52.6 嵌套对象和数组解构
const metadata {title: Scratchpad,translations: [{locale: de,localization_tags: [],last_edit: 2014-04-14T08:43:37,url: /de/docs/Tools/Scratchpad,title: JavaScript-Umgebung,},],url: /en-US/docs/Tools/Scratchpad,
};let {title: englishTitle, // 重命名translations: [{title: localeTitle, // 重命名},],
} metadata;console.log(englishTitle); // Scratchpad
console.log(localeTitle); // JavaScript-Umgebung2.7 可计算对象属性名与解构
当使用解构改变对象属性的名称时可以使用对象计算属性名。
let key z;
let { [key]: foo } { z: bar };console.log(foo); // bar在上面的例子中我们计算变量键值并改变它的名称为 foo。
2.8 同时使用数组和对象解构
在解构中数组和对象可以联合使用
const props [{ id: 1, name: Fizz },{ id: 2, name: Buzz },{ id: 3, name: FizzBuzz },
];const [, , { name }] props;console.log(name); // FizzBuzz所有的解构赋值语法是相同的是在赋值符号左侧声明从源变量取出的值。举例来说
let x [1, 2, 3, 4, 5];
let [y, z] x;
console.log(y); // 1
console.log(z); // 22.9 注意点
1如果要将一个已经声明的变量用于解构赋值必须非常小心。
// 错误的写法
let x;
{x} {x: 1};
// SyntaxError: syntax error上面代码的写法会报错因为 JavaScript 引擎会将{x}理解成一个代码块从而发生语法错误。只有不将大括号写在行首避免 JavaScript 将其解释为代码块才能解决这个问题。
// 正确的写法
let x;
({x} {x: 1});上面代码将整个解构赋值语句放在一个圆括号里面就可以正确执行。
2解构赋值允许等号左边的模式之中不放置任何变量名。因此可以写出非常古怪的赋值表达式。
({} [true, false]);
({} abc);
({} []);上面的表达式虽然毫无意义但是语法是合法的可以执行。
3由于数组本质是特殊的对象因此可以对数组进行对象属性的解构。
let arr [1, 2, 3];
let {0 : first, [arr.length - 1] : last} arr;
first // 1
last // 3上面代码对数组进行对象解构。数组arr的0键对应的值是1[arr.length - 1]就是2键对应的值是3。方括号这种写法属于“属性名表达式”
3 函数参数的解构赋值
函数的参数也可以使用解构赋值。
function add([x, y]){return x y;
}add([1, 2]); // 3上面代码中函数add的参数表面上是一个数组但在传入参数的那一刻数组参数就被解构成变量x和y。对于函数内部的代码来说它们能感受到的参数就是x和y。
下面是另一个例子。
[[1, 2], [3, 4]].map(([a, b]) a b);
// [ 3, 7 ]函数参数的解构也可以使用默认值。
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]上面代码中函数move的参数是一个对象通过对这个对象进行解构得到变量x和y的值。如果解构失败x和y等于默认值。
注意下面的写法会得到不一样的结果。
function move({x, y} { x: 0, y: 0 }) {return [x, y];
}move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]上面代码是为函数move的参数指定默认值而不是为变量x和y指定默认值所以会得到与前一种写法不同的结果。
undefined就会触发函数参数的默认值。
[1, undefined, 3].map((x yes) x);
// [ 1, yes, 3 ]4 解构的用途
变量的解构赋值用途很多。
4.1 交换变量的值
let x 1;
let y 2;[x, y] [y, x];上面代码交换变量x和y的值这样的写法不仅简洁而且易读语义非常清晰。
4.2 从函数返回多个值
函数只能返回一个值如果要返回多个值只能将它们放在数组或对象里返回。有了解构赋值取出这些值就非常方便。
// 返回一个数组function example() {return [1, 2, 3];
}
let [a, b, c] example();// 返回一个对象function example() {return {foo: 1,bar: 2};
}
let { foo, bar } example();4.3 函数参数的定义
解构赋值可以方便地将一组参数与变量名对应起来。
// 参数是一组有次序的值
function f([x, y, z]) { ... }
f([1, 2, 3]);// 参数是一组无次序的值
function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1});4.4 提取 JSON 数据
解构赋值对提取 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]上面代码可以快速提取 JSON 数据的值。
4.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;这样的语句。 JavaScript: https://developer.mozilla.org/en-US/docs/Web/JavaScript