书籍 在线阅读
第一章 块级绑定
块级声明
块级声明:块级声明指的是该声明的变量无法被代码块外部访问,块作用域又被称为此法作用域
创建条件
- 函数内部
- 在代码块 (即{ }内部)
let 声明
禁止重复声明
就是不可以对声明的标识符进行覆盖,会报错。
var count = 30; => let count = 30 也不会报错
// 不会抛出错误
if (condition) {
let count = 40;
// 其它代码
}
因为 let声明的变量在if中,并非与var声明的处于同一级
const 声明
该种声明的变量被视为常量,意味着他们不能被再次赋值,所以const 声明都必须在声明处初始化
const 变量的值如果是个对象,那么这个对象本身可以被修改
都是块级声明,都不会进行变量提示
- 不可以对已存在的标识符重新定义(赋值)
let | const | |
---|---|---|
声明时 | 可以不赋值 | 必须赋值 |
将对象赋给const 变量
const声明只是阻止变量和值的再次绑定而不是值本身的修改。
const person ={
name:"xioaming"
}
//正确
person.name = "chu"
//错误
person = {
name:"chu"
}
const 阻止的是绑定的修改而不是绑定值的修改
暂存性死区
let 或 const 声明的变量在声明之前不能被访问
循环中的块级绑定
例子:不想让循环外部访问到内部的索引计数器
for(var i=0;i<10;i++){
console.log("over")
}
//这里仍然可以访问到i
console.log(i) //10
for(let i=0;i<10;i++){
console.log("over")
}
//这里会报错
console.log(i)
循环中的函数
因为每次迭代的过程中i是被共享的,意味着循环中创建的函数都保持这对相同变量的引用,当循环结束后i的值为10
var funcs = [];
for (var i = 0; i < 10; i++) {
funcs.push(function() { console.log(i); });
}
funcs.forEach(function(func) {
func(); // 输出 "10" 共10次
});
解决方法:循环内部使用即时调用函数表达式,来迫使每一次迭代时创建一份当前索引值的拷贝
for(var i=0;i<10;i++){
funcs.push((function(value){
return function(){
console.log(value)
}
}(i)))
}
循环中的let声明
在每次迭代中,一个新的同名变量会被创建并初始化
var funcs = [];
for (let i = 0; i < 10; i++) {
funcs.push(function() {
console.log(i);
});
}
funcs.forEach(function(func) {
func(); // 输出 0,1,2 ... 9
})
for-in与 for-of 。每次循环的开始都会创建一个新的变量key的绑定,所以每个函数都会有各自key变量值的备份。
var funcs = [],
object = {
a: true,
b: true,
c: true
};
for (let key in object) {
funcs.push(function() {
console.log(key);
});
}
funcs.forEach(function(func) {
func(); // 输出 "a","b" 和 "c"
});
全局块级绑定
当在全局作用域内使用var声明时会创建一个全局变量,同时也是全局对象(浏览器环境下是window)的一个属性。
意味着全局对象的属性可能会被重写覆盖
// 在浏览器中运行
var RegExp = "Hello!";
console.log(window.RegExp); // "Hello!"
如果你在全局作用域中使用let或者const,那么绑定就会发生在全局作用域内,但是不会向全局对象内部添加任何属性,意味着不能使用 let 或 const 重写全局变量,仅能屏蔽他们。
// 在浏览器中运行
let RegExp = "Hello!";
console.log(RegExp); // "Hello!"
console.log(window.RegExp === RegExp); // false
总结
目前关于块级绑定的最佳实践是使用 const 作为默认的声明方式,当变量需要更改时切换为 let 声明。保证代码中最基本的不可变性能防止错误的发生。
第二章 字符串与正则表达式
更佳的Unicode支持
第三章 函数
带默认参数的函数
ES5默认参数模拟
原理:都是可选参数因为未传入实参给他们的情况下会使用各自的默认值
缺点:如果给timeout 传入的值为 0 ,那么timeout 的值会被替换为2000,因为0被默认为假值
function makeRequest(url, timeout, callback) {
timeout = timeout || 2000;
callback = callback || function() {};
// 其它部分
}
=> 更加健壮的写法
function makeRequest(url, timeout, callback) {
timeout = (typeof timeout !== "undefined") ? timeout : 2000;
callback = (typeof callback !== "undefined") ? callback : function() {};
// 其它部分
}
ES6默认参数
function makeRequest(url, timeout = 2000, callback = function() {}) {
// 其余代码
}
可以指定任意一个函数参数的默认值
function makeRequest(url, timeout = 2000, callback) {
// 其余代码
}
// 使用 timeout 的默认值
makeRequest("/foo", undefined, function(body) {
doSomething(body);
});
// 使用 timeout 的默认值
makeRequest("/foo");
// 不使用 timeout 的默认值
makeRequest("/foo", null, function(body) {
doSomething(body);
});
注意:在提供参数值的时候,null是有效的
默认参数对arguments对象的影响
function mixArgs(first, second) {
console.log(first === arguments[0]);
console.log(second === arguments[1]);
first = "c";
second = "d";
console.log(first === arguments[0]);
console.log(second === arguments[1]);
}
mixArgs("a", "b");
true
true
true
true
当时不理解,但是调试代码之后会发现 first 就是参数 a(也就是arguments[0])
arguments 对象在非严格模式下总是实行更新反映出命名参数的变化,因此当first和second变量获得新值之后,arguments[0] 和 arguments[1] 也同步更新,使得 === 比较的值为 true 。
ES5的严格模式下,上述不成立。
当使用ES6默认参数时,表现和ES5严格模式一致。
// 非严格模式
function mixArgs(first, second = "b") {
console.log(arguments.length);
console.log(first === arguments[0]);
console.log(second === arguments[1]);
first = "c";
second = "d"
console.log(first === arguments[0]);
console.log(second === arguments[1]);
}
mixArgs("a");
1
true
false
false
false